Unity基础实践小项目

项目流程:

需求分析

开始界面

选择角色面板

排行榜面板

设置面板

游戏面板

确定退出面板

死亡面板

UML类图

准备工作

1.导入资源

2.创建需要的文件夹

3.创建好面板基类

开始场景

开始界面

1.拼面板

2.写脚本

注意事项:注意先设置NGUI的分辨率大小,注意控件的分辨率自适应,注意 Draw Calls!

设置界面 —— 拼面板和基础逻辑

1.拼面板

2.逻辑

BegingPael 里打开设置面板

设置界面——音效数据

1.创建音效数据类

2.创建游戏数据管理类

3.SettingPanel 关联逻辑

实现了音效数据的在设置面板中的获取和修改

设置界面——背景音乐对象

1.创建音乐对象和音乐控制脚本

音乐对象

音乐控制脚本

2.设置面板 调用

排行榜界面——排行榜数据

1.排行榜数据类

2.GameDataMgr 中调用

排行榜界面——拼面板

排行榜界面——逻辑处理

1.单个排行榜控件类——RankItem

2.创建排行榜面板类

选择角色界面 —— 拼面板

选择角色界面——数据准备

1.创建数据集合

2.创建Xml配置文件

3.GameDataMgr 中调用初始化

选择角色界面——逻辑处理

1.创建 ChoosePanel 类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ChoosePanel : BasePanel<ChoosePanel>
{
    //各按钮
    public UIButton btnClose;
    public UIButton btnLeft;
    public UIButton btnRight;
    public UIButton btnStart;

    //模型父对象
    public Transform heroPos;

    //下方属性相关对象
    public List<GameObject> hpObjs;
    public List<GameObject> speedObjs;
    public List<GameObject> volumeObjs;

    //当前显示的飞机模型对象
    private GameObject airPlaneObj;

    public override void Init()
    {
        //选择角色后 点击开始 切换场景
        btnStart.onClick.Add(new EventDelegate(() =>
        {
            SceneManager.LoadScene("GameScene");
        }));

        btnLeft.onClick.Add(new EventDelegate(() =>
        {
            //左按钮 减我们设定的索引
            --GameDataMgr.Instance.nowSelHeroIndex;
            //如果 小于最小的索引 直接让其等于 最后一个索引
            if (GameDataMgr.Instance.nowSelHeroIndex < 0)
                GameDataMgr.Instance.nowSelHeroIndex = GameDataMgr.Instance.roleData.roleList.Count - 1;

            ChangeNowHero();
        }));

        btnRight.onClick.Add(new EventDelegate(() =>
        {
            //右按钮 加我们设定的索引
            ++GameDataMgr.Instance.nowSelHeroIndex;
            //如果大于了最大索引 就置0
            if (GameDataMgr.Instance.nowSelHeroIndex > GameDataMgr.Instance.roleData.roleList.Count - 1)
                GameDataMgr.Instance.nowSelHeroIndex = 0;
            ChangeNowHero();
        }));

        btnClose.onClick.Add(new EventDelegate(() =>
        {
            //关闭自己
            HideMe();
            //显示开始面板
            BeginPanel.Instance.ShowMe();
        }));

        HideMe();

    }

    public override void ShowMe()
    {
        base.ShowMe();
        //每次显示的时候都从第一个开始
        GameDataMgr.Instance.nowSelHeroIndex = 0;
        ChangeNowHero();
    }

    public override void HideMe()
    {
        base.HideMe();
        //删除当前模型
        DestroyObj();
    }

    //切换当前的选择
    private void ChangeNowHero()
    {
        //得到当前选择的 玩家游戏数据
        RoleInfo info = GameDataMgr.Instance.GetNowSelHeroInfo();

        //更新模型
        //先删除上一次的飞机模型
        DestroyObj();
        //再创建当前的飞机模型
        airPlaneObj = Instantiate(Resources.Load<GameObject>(info.resName));
        //设置父对象
        airPlaneObj.transform.SetParent(heroPos);
        //设置角度和位置 缩放
        airPlaneObj.transform.localPosition = Vector3.zero;
        airPlaneObj.transform.localRotation = Quaternion.identity;
        airPlaneObj.transform.localScale = Vector3.one * info.scale;
        //修改层级
        airPlaneObj.layer = LayerMask.NameToLayer("UI");

        //更新属性
        for (int i = 0; i < 10; i++)
        {
            hpObjs[i].SetActive(i < info.hp);
            speedObjs[i].SetActive(i < info.speed);
            volumeObjs[i].SetActive(i < info.volume);
        }

    }

    /// <summary>
    /// 用于删除上一次显示的模型对象
    /// </summary>
    private void DestroyObj()
    {
        if(airPlaneObj != null)
        {
            //移除场景的模型
            Destroy(airPlaneObj);
            //置空
            airPlaneObj = null;
        }
    }

    private float time;
    //是否鼠标选中 模型
    private bool isSel;
    // Update is called once per frame
    void Update()
    {
        //让飞机 上下浮动
        time += Time.deltaTime;
        heroPos.Translate(Vector3.up * Mathf.Sin(time) * 0.0001f, Space.World);

        //射线检测 让飞机 可以左右转动
        if (Input.GetMouseButtonDown(0))
        {
            //如果点击了 UI层碰撞器 认为需要开始 拖动 飞机了
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),
                                1000,
                                1 << LayerMask.NameToLayer("UI")))
            {
                isSel = true;
            }
        }

        //抬起 取消选中
        if (Input.GetMouseButtonUp(0))
            isSel = false;

        //旋转对象
        if (Input.GetMouseButton(0) && isSel)
        {
            heroPos.rotation *= Quaternion.AngleAxis(Input.GetAxis("Mouse X") * 20, Vector3.up);
        }

    }
}

2.GameDataMgr 中逻辑关联

3.BeginPanel 中显隐设置

注意:模型旋转时,发射射线检测的摄像机要是主摄像机,或者就自己指定摄像机发射。

游戏场景

游戏界面

1.拼面板

2.逻辑面板

确定退出界面

1.拼面板

2.写逻辑

3.调用

GamePanel 中调用

结束界面

1.拼面板

2.写逻辑

游戏主逻辑——主玩家相关

1.加一个相加来渲染

一个专门渲染特效

一个专门渲染UI

一个专门渲染其他

2.玩家逻辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerObject : MonoBehaviour
{
    //血量
    public int nowHp;
    public int maxHp;

    //速度
    public int speed;
    //旋转速度
    public int roundSpeed;
    //目标四元数角度
    private Quaternion targetQ;

    //是否死亡
    public bool isDead;

    //当前世界坐标系转屏幕上的点
    private Vector3 nowPos;
    //上一次玩家的位置 就是在位移前 玩家的位置
    private Vector3 frontPos;
    
    public void Dead()
    {
        isDead = true;
        //显示游戏结束面板
        GameOverPanel.Instance.ShowMe();
    }

    public void Wound()
    {
        if (isDead)
            return;
        //减血
        this.nowHp -= 1;
        //更新游戏面板上的血量显示
        GamePanel.Instance.ChangeHp(this.nowHp);
        //是否死亡了
        if (this.nowHp <= 0)
            this.Dead();
    }

    private float hValue;
    private float vValue;
    // Update is called once per frame
    void Update()
    {
        //如果死亡了 就没有必要移动了
        if (isDead)
            return;

        //移动 旋转逻辑

        //旋转
        hValue = Input.GetAxisRaw("Horizontal");
        vValue = Input.GetAxisRaw("Vertical");
        //如果没有按我们的AD键 那么目标角度 就是(0,0,0)度
        if (hValue == 0)
            targetQ = Quaternion.identity;
        //如果按AD键 就是(0,0,20)或者 (0,0,-20) 根据按的左右决定
        else
            targetQ = hValue < 0 ? Quaternion.AngleAxis(20, Vector3.forward) : Quaternion.AngleAxis(-20, Vector3.forward);

        //让飞机朝着 这个目标四元数 去旋转
        this.transform.rotation = Quaternion.Slerp(this.transform.rotation, targetQ, roundSpeed * Time.deltaTime);

        //在位移之前 记录 之前的位置
        frontPos = this.transform.position;

        //移动
        this.transform.Translate(Vector3.forward * vValue * speed * Time.deltaTime);
        this.transform.Translate(Vector3.right * hValue * speed * Time.deltaTime, Space.World);

        //进行极限判断
        nowPos = Camera.main.WorldToScreenPoint(this.transform.position);
        //左右 溢出判断
        if(nowPos.x < 0 || nowPos.x >= Screen.width)
        {
            this.transform.position = new Vector3(frontPos.x, this.transform.position.y, this.transform.position.z);
        }
        //上下 溢出判断
        if(nowPos.y < 0 || nowPos.y >= Screen.height)
        {
            this.transform.position = new Vector3(this.transform.position.x, this.transform.position.y, frontPos.z);
        }

    }
}

游戏主逻辑——子弹相关

数据准备

1.制作子弹 和 子弹爆炸特效

2.子弹数据类

3.Xml文件配置

4. GameDataMgr 管理初始化

写逻辑

1.创建 BullteObject 脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletObject : MonoBehaviour
{
    //子弹使用的数据
    private BulletInfo info;

    //用于曲线移动的 计时变量
    private float time;

    

    //初始化子弹数据的方法
    public void InitInfo(BulletInfo info)
    {
        this.info = info;
        //根据生命周期函数 决定自己什么时候 延迟移除
        //Destroy(this.gameObject, info.lifeTime);
        //两种方式来移除 Destroy和延迟函数 看看哪种方法更合适
        Invoke("DealyDestroy", info.lifeTime);
    }

    private void DealyDestroy()
    {
        Destroy(this.gameObject);
    }

    //销毁场景上的子弹
    public void Dead()
    {
        //创建死亡特效
        GameObject eff = Instantiate(Resources.Load<GameObject>(this.info.deadEffRes));
        //设置特效的位置 创建在当前子弹的位置
        eff.transform.position = this.transform.position;
        //1秒后延迟移除特效
        Destroy(eff, 1f);

        //销毁子弹对象
        Destroy(this.gameObject);
    }

    //和对象碰撞时 (触发)
    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Player"))
        {
            //得到玩家脚本
            PlayerObject obj = other.GetComponent<PlayerObject>();
            //玩家受伤减血
            obj.Wound();

            //销毁自己 就是直接调用 Dead方法
            Dead();
        }
    }

    // Update is called once per frame
    void Update()
    {
        //所有移动的共同特点 都是朝自己的面朝向动
        this.transform.Translate(Vector3.forward * info.forwardSpeed * Time.deltaTime);
        //接着再来处理 其他的移动逻辑
        //1 代表 只朝自己面朝向移动 直线移动
        //2 代表 曲线运动
        //3 代表 右抛物线
        //4 代表 左抛物线
        //5 代表 跟踪移动
        switch (info.type)
        {
            case 2:
                time += Time.deltaTime;
                //sin里面值变化的快慢 决定了 左右变化的频率
                //乘以的速度 变化的大小 决定了 左右位移的多少
                this.transform.Translate(Vector3.right * info.rightSpeed * Time.deltaTime * Mathf.Sin(time * info.roundSpeed));
                break;
            case 3:
                //右抛物线 无非 就是 去改变 旋转角度
                this.transform.rotation *= Quaternion.AngleAxis(info.roundSpeed * Time.deltaTime, Vector3.up);
                break;
            case 4:
                //左抛物线 无非 就是 去改变 旋转角度
                this.transform.rotation *= Quaternion.AngleAxis(-info.roundSpeed * Time.deltaTime, Vector3.up);
                break;
            case 5:
                //跟踪移动 不停的计算 玩家和我之间的方向向量 得到四元数 然后自己的角度 不停的 变化为这个目标四元数
                this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
                                                            Quaternion.LookRotation(PlayerObject.Instance.transform.position - this.transform.position), info.roundSpeed * Time.deltaTime);
                break;
        }

    }
}

2.测试

游戏主逻辑——开火点相关

数据准备

1.创建开火点数据 —— FireData

2.配置Xml文件

3. GameDataMgr 中初始化

逻辑处理一

主要实现了绘制开火点位置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 表示 开火点位置的 类型
/// </summary>
public enum E_Pos_Type
{
    TopLeft,
    Top,
    TopRight,

    Left,
    Right,

    BottonLeft,
    Botton,
    BottonRight,
}

public class FireObject : MonoBehaviour
{
    public E_Pos_Type type;

    //表示屏幕上的点
    private Vector3 screenPos;
    //初始发射子弹的方向 主要用于作为散弹的初始方向 用于计算
    private Vector3 initDir;

    // Update is called once per frame
    void Update()
    {
        //用于测试玩家转屏幕坐标后 横截面的 z轴值
        //print(Camera.main.WorldToScreenPoint(PlayerObject.Instance.transform.position));
        //更新 开火点位置 达到分辨率自适应
        UpdatePos();

    }

    //根据点的类型 来更新它的位置
    private void UpdatePos()
    {
        //这里设置z轴 是为了和主玩家位置转屏幕坐标后的 z位置一样 目的是 让点和玩家 所在的 横截面是一致的
        screenPos.z = 150.6f;
        switch (type)
        {
            case E_Pos_Type.TopLeft:
                screenPos.x = 0;
                screenPos.y = Screen.height;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.Top:
                screenPos.x = Screen.width / 2;
                screenPos.y = Screen.height;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.TopRight:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height;

                initDir = Vector3.left;
                break;
            case E_Pos_Type.Left:
                screenPos.x = 0;
                screenPos.y = Screen.height / 2;

                initDir = Vector3.up;
                break;
            case E_Pos_Type.Right:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height / 2;

                initDir = Vector3.up;
                break;
            case E_Pos_Type.BottonLeft:
                screenPos.x = 0;
                screenPos.y = 0;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.Botton:
                screenPos.x = Screen.width / 2;
                screenPos.y = 0;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.BottonRight:
                screenPos.x = Screen.width;
                screenPos.y = 0;

                initDir = Vector3.left;
                break;
        }

        //再把屏幕点 转换为 世界坐标点 那得到的 就是我们想要的坐标点
        this.transform.position = Camera.main.ScreenToWorldPoint(screenPos);
    }
}

逻辑处理二

1.实现了重置开火点数据 和 检测开火逻辑 (代码量较多 要好好了解)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 表示 开火点位置的 类型
/// </summary>
public enum E_Pos_Type
{
    TopLeft,
    Top,
    TopRight,

    Left,
    Right,

    BottonLeft,
    Botton,
    BottonRight,
}

public class FireObject : MonoBehaviour
{
    public E_Pos_Type type;

    //表示屏幕上的点
    private Vector3 screenPos;
    //初始发射子弹的方向 主要用于作为散弹的初始方向 用于计算
    private Vector3 initDir;

    //当前开火点的数据信息
    private FireInfo fireInfo;
    private int nowNum;
    private float nowCD;
    private float nowDelay;
    //当前组开火点 使用的子弹信息
    private BulletInfo nowBulletInfo;

    //散弹时 每颗子弹的间隔角度
    private float changeAngle;

    //用于发射散弹时 记录上一次的方向
    private Vector3 nowDir;

    // Update is called once per frame
    void Update()
    {
        //用于测试玩家转屏幕坐标后 横截面的 z轴值
        //print(Camera.main.WorldToScreenPoint(PlayerObject.Instance.transform.position));
        //更新 开火点位置 达到分辨率自适应
        UpdatePos();
        //每次 都检测 是否需要 重置 开火点数据
        ResetFireInfo();
        //发射子弹
        UpdateFire();
    }

    //根据点的类型 来更新它的位置
    private void UpdatePos()
    {
        //这里设置z轴 是为了和主玩家位置转屏幕坐标后的 z位置一样 目的是 让点和玩家 所在的 横截面是一致的
        screenPos.z = 150.6f;
        switch (type)
        {
            case E_Pos_Type.TopLeft:
                screenPos.x = 0;
                screenPos.y = Screen.height;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.Top:
                screenPos.x = Screen.width / 2;
                screenPos.y = Screen.height;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.TopRight:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height;

                initDir = Vector3.left;
                break;
            case E_Pos_Type.Left:
                screenPos.x = 0;
                screenPos.y = Screen.height / 2;

                initDir = Vector3.up;
                break;
            case E_Pos_Type.Right:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height / 2;

                initDir = Vector3.up;
                break;
            case E_Pos_Type.BottonLeft:
                screenPos.x = 0;
                screenPos.y = 0;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.Botton:
                screenPos.x = Screen.width / 2;
                screenPos.y = 0;

                initDir = Vector3.right;
                break;
            case E_Pos_Type.BottonRight:
                screenPos.x = Screen.width;
                screenPos.y = 0;

                initDir = Vector3.left;
                break;
        }

        //再把屏幕点 转换为 世界坐标点 那得到的 就是我们想要的坐标点
        this.transform.position = Camera.main.ScreenToWorldPoint(screenPos);
    }

    //重置当前要发射的炮台数据
    private void ResetFireInfo()
    {
        //自己定一个规则 只有当cd和数量都为0时 才认为需要重新获取 发射点数据
        if (nowCD != 0 && nowNum != 0)
            return;
        //组间休息时间判断
        if (fireInfo != null)
        {
            nowDelay -= Time.deltaTime;
            //还在组间休息
            if (nowDelay > 0)
                return;
        }

        //从数据中随机取出一条 来按照规则 发射子弹
        List<FireInfo> list = GameDataMgr.Instance.fireData.fireInfoList;
        fireInfo = list[Random.Range(0, list.Count)];
        //我们不能直接改变数据当中的内容 我们应该拿变量 临时存储下来 这样就不会影响我们数据本身
        nowNum = fireInfo.num;
        nowCD = fireInfo.cd;
        nowDelay = fireInfo.delay;

        //通过 开火点数据 取出 当前要使用的子弹数据信息
        //得到开始id 和 结束id 用于随机取子弹信息
        string[] strs = fireInfo.ids.Split(',');
        int beginID = int.Parse(strs[0]);
        int endID = int.Parse(strs[1]);
        int randomBulletID = Random.Range(beginID, endID + 1);
        nowBulletInfo = GameDataMgr.Instance.bulletData.bulletInfoList[randomBulletID - 1];

        //如果是散弹 就需要计算 间隔角度
        if (fireInfo.type == 2)
        {
            switch (type)
            {
                case E_Pos_Type.TopLeft:
                case E_Pos_Type.TopRight:
                case E_Pos_Type.BottonLeft:
                case E_Pos_Type.BottonRight:
                    changeAngle = 90f / (nowNum + 1);
                    break;
                case E_Pos_Type.Top:
                case E_Pos_Type.Left:
                case E_Pos_Type.Right:
                case E_Pos_Type.Botton:
                    changeAngle = 180f / (nowNum + 1);
                    break;
            }
        }

    }

    //检测开火
    private void UpdateFire()
    {
        //当前状态 是不需要发射子弹的
        if (nowCD == 0 && nowNum == 0)
            return;

        //cd更新
        nowCD -= Time.deltaTime;
        if (nowCD > 0)
            return;

        GameObject bullet;
        BulletObject bulletObj;

        switch (fireInfo.type)
        {
            //一颗一颗的发射子弹 朝向玩家
            case 1:
                //动态创建 子弹对象
                bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                //动态添加 子弹脚本
                bulletObj = bullet.AddComponent<BulletObject>();
                //把当前的子弹数据传入子弹脚本 进行初始化
                bulletObj.InitInfo(nowBulletInfo);

                //设置子弹的位置 和朝向
                bullet.transform.position = this.transform.position;
                bullet.transform.rotation = Quaternion.LookRotation(PlayerObject.Instance.transform.position - bullet.transform.position);

                //表示已经发射一颗子弹
                --nowNum;
                //重置cd
                nowCD = nowNum == 0 ? 0 : fireInfo.cd;


                break;
                //发射散弹
            case 2:
                //无CD 一瞬间 发射所有的散弹
                if(nowCD == 0)
                {
                    for (int i = 0; i < nowNum; i++)
                    {
                        //动态创建 子弹对象
                        bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                        //动态添加 子弹脚本
                        bulletObj = bullet.AddComponent<BulletObject>();
                        //把当前的子弹数据传入子弹脚本 进行初始化
                        bulletObj.InitInfo(nowBulletInfo);

                        //设置子弹的位置 和朝向
                        bullet.transform.position = this.transform.position;
                        //每次都会旋转一个角度 得到一个新的方向
                        nowDir = Quaternion.AngleAxis(changeAngle * i, Vector3.up) * initDir;
                        bullet.transform.rotation = Quaternion.LookRotation(nowDir);
                    }

                    //因为是瞬间创建完所有子弹 所以 重置数据
                    nowCD = nowNum = 0;
                }
                else
                {
                    //动态创建 子弹对象
                    bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                    //动态添加 子弹脚本
                    bulletObj = bullet.AddComponent<BulletObject>();
                    //把当前的子弹数据传入子弹脚本 进行初始化
                    bulletObj.InitInfo(nowBulletInfo);

                    //设置子弹的位置 和朝向
                    bullet.transform.position = this.transform.position;
                    //每次都会旋转一个角度 得到一个新的方向
                    nowDir = Quaternion.AngleAxis(changeAngle * (fireInfo.num - nowNum), Vector3.up) * initDir;
                    bullet.transform.rotation = Quaternion.LookRotation(nowDir);

                    //表示已经发射一颗子弹
                    --nowNum;
                    //重置cd
                    nowCD = nowNum == 0 ? 0 : fireInfo.cd;
                }
                break;
        }
    }

}

2.串联 开始面板和游戏面板

创建 Main 类 用于创建玩家飞机

逻辑功能完善

1.子弹自动销毁

2.不隐藏光标

3.射线检测销毁子弹

游戏展示:

暂时无法上传!

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/686970.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL数据库数据恢复方案应对没有where误操作导致的大量数据更新或删除

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

医疗保健的未来——Kompas.ai如何助力精准医疗

引言 在现代医学中&#xff0c;精准医疗逐渐成为提高诊疗效果的重要手段。随着科技的进步&#xff0c;人工智能&#xff08;AI&#xff09;在医疗领域的应用越来越广泛。本文将探讨精准医疗的发展趋势&#xff0c;并介绍Kompas.ai如何通过AI技术助力精准医疗的发展。 精准医疗…

@Validated 前端表单数据校验

1. 整合 1.1 依赖引入 <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId></dependency>1.2 控制层 /*** 新增胎架计划** param subsectionPlanVo* return*/PostMapping("/sched…

充电桩,未来出行的绿色引擎

随着新能源时代的到来&#xff0c;充电桩早已不再是一个陌生的词汇&#xff0c;它正在成为我们生活中不可或缺的一部分。 它不仅仅是一个简单的充电设备&#xff0c;更是未来出行的绿色引擎&#xff0c;驱动着我们的汽车&#xff0c;也驱动着这个时代的绿色梦想。 想象一下&am…

重庆公司记账代理,打造专业财务管理解决方案的领先企业

重庆公司记账代理&#xff0c;作为专业的财务管理服务提供商&#xff0c;我们的目标是为公司的经营管理和决策提供科学、准确的财务数据支持&#xff0c;我们通过长期的专业经验和对市场的深入理解&#xff0c;为您提供一站式的记账服务和财务咨询。 专业团队 我们拥有一支由经…

企业物料主数据管理的重要性

物料主数据包含了对所有企业所采购、生产和存储在库存中物料的集中描述。它是企业中有关物料信息的物料数据代码库。将所有的物料数据集成在单一的物料数据库中&#xff0c;消除了数据冗余的问题&#xff0c;而且不仅允许采购部门使用这些数据&#xff0c;其他应用部门也可以使…

我们如何用npm发布自己的插件包?详细的教程来了

一、什么是npm插件&#xff1f; npm&#xff08;“Node 包管理器”&#xff09;是 JavaScript 运行时 Node.js 的默认程序包管理器。npm插件是指通过npm安装的第三方包&#xff0c;可以在Node.js项目中直接使用。这些插件涵盖了各种领域&#xff0c;包括Web开发、数据测试、构建…

什么是专业的倾斜摄影轻量化?

眸瑞科技是一家专业从事自研3D可视化技术底层、提供三维模型轻量化服务的高新技术公司&#xff0c;从事该行业近10年&#xff0c;有着丰富的三维模型处理及开发经验。目前已向许多企事业单位提供过工厂厂区、城市地貌、铁路桥梁、高速公路、旅游景区等倾斜摄影模型轻量化处理、…

什么是捷云等保一体机?解密等保一体机

捷云等保一体机的优点&#xff1f; 一台设备即可满足等级保护所有安全要求&#xff0c;避免了企业购买不同厂商的安全设备的问题。 可以降低等保整改成本&#xff0c;提高整改效率&#xff0c;可以帮助企业快速实现等保合规。 2 捷云等保一体机的安全能力&#xff1f; 捷云等保…

深入了解物流运输管理系统的重要性与实践

在当今竞争激烈的商业环境中&#xff0c;物流运输管理系统正变得越发重要。随着全球化贸易的迅速发展&#xff0c;企业需要更高效、更智能的方式来管理供应链运作&#xff0c;以确保产品能够准时以最优质的方式送达客户手中。物流运输管理系统作为一个关键的工具&#xff0c;为…

高德车道级安全预警再升级 10大场景全方位守护行车安全

今日起&#xff0c;2024年高考、端午节小长假相继到来&#xff0c;正值新一波出行高峰到来&#xff0c;高德地图车道级安全预警功能再次升级。升级后的车道级安全预警功能覆盖了10大安全风险较高的行车场景&#xff0c;全方位、超视距地为用户实时探测、预警行车风险&#xff0…

工厂生产计划难以执行的真正原因及对策

在制造业中&#xff0c;生产计划的执行对于企业的运营至关重要。然而&#xff0c;许多工厂在生产计划执行过程中面临着诸多挑战&#xff0c;尤其是物料齐套率低的问题。本文将探讨工厂生产计划难以执行的真正原因&#xff0c;并提出相应的解决对策。 一、生产计划难以执行的真…

005.FashionMNIST数据集简介

一、FashionMNIST数据集简介 FashionMNIST数据集&#xff0c;是一款作为经典的MNIST数据集的现代替代品的数据集&#xff0c;用来做衣物分类问题&#xff0c;由Zalando&#xff08;一家德国的在线时尚零售商&#xff09;发布。 该数据集含有10种类别&#xff0c;共70000个灰度图…

[Cesium学习]

Popup弹窗 Cesium点位弹窗_cesium popup弹窗-CSDN博客 Cesium构造popup弹窗函数_cesium popup-CSDN博客 开发之家 - Cesium构造popup弹窗函数 GitHub - cesium-plugin/cesium-popup-es6: 气泡弹窗 热力图分析 // 创建Cesium Viewer实例 const viewer new Cesium.Viewer(c…

java:reactor的Mono和Reactor的简单例子

【pom.xml】 <dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId><version>3.3.17.RELEASE</version> </dependency>【MyReactorTest2.java】 package com.chz.myReactor;import react…

擎创动态 | 擎创科技受邀参与电信数智“走进地市“专项行动启动会

为贯彻落实电信集团公司云改数转战略&#xff0c;切实推进数智公司"三化"战略落地&#xff0c;强化全国数智体系协同建设&#xff0c;5月24日由中电信数智科技有限公司&#xff08;以下简称“电信数智”&#xff09;、中国电信党校&#xff08;以下简称“人才发展中心…

SITNE24V2BNQ-3/TR一种瞬态电压抑制器,对标PESD1CAN

SITNE24V2BNQ是一种瞬态电压抑制器&#xff0c;设计用于保护两个汽车控制器区域 网络(CAN)母线不受ESD等瞬变造成的损坏。 SITNE24V2BNQ采用SOT-23封装。标准产品不含铅和卤素。 产品参数 方向&#xff1a;双向通道数&#xff1a;2VRWM(V)(Max)&#xff1a;24IPP8/20μS(A)(M…

风丘新加坡全资子公司正式成立!

在风丘科技&#xff08;WINDHILL&#xff09;成立20周年之际&#xff0c;风丘全资子公司——WINDHILL TECHNOLOGIES PTE. LTD.于2024年3月初正式在新加坡注册成立。 随着出海4.0时代的到来&#xff0c;为更快地响应海外区域客户的需求&#xff0c;风丘加大了区域市场的投入&…

stm32中如何实现EXTI线 0 ~ 15与对应IO口的配置呢?

STM32的EXTI控制器支持19 个外部中断/ 事件请求。每个中断设有状态位&#xff0c;每个中断/ 事件都有独立的触发和屏蔽设置。 STM32的19个外部中断对应着19路中断线&#xff0c;分别是EXTI_Line0-EXTI_Line18&#xff1a; 线0~15&#xff1a;对应外部 IO口的输入中断。 线16&…

Angular17版本集成Quill富文本编辑器

Angular17版本集成Quill富文本编辑器 前言:网上找了好多富文本资源,对应Angular17版本的且兼容的太少了,且找到不到对应的版本 自己就去网上找个兼容的免费的富文本组件 1.兼容Angular17版本的quill包 "types/quill": "^1.3.10","ngx-quill": …