zoukankan      html  css  js  c++  java
  • U3D 案例练习

      UGUI 实现摇杆控制3D世界物体移动

    实现效果:使用UGUI实现类似于 王者荣耀,或其他的RPG类型的游戏中的屏幕摇杆功能

    step1:搭建UI界面,摇杆的样式,一大一小两个圆盘的图案,将小圆设置为大圆的子物体。将摇杆放置于屏幕左下角。如图:

        

    step2:实现小圆在大圆上拖拽,并获得小圆相对于初始位置拖拽的方向,脚本实现
    由于在这里要将小圆被拖拽移动的数据传递出去,给3D世界的游戏物体Cube,所以可将摇杆的脚本写成单例脚本,既方便传值,又保证数据不会发生冲突。在由于要不停的在两个脚本之间传值,使用委托

    //定义委托,用来将摇杆的Image的移动方向传递给对应的Cube
    public delegate void YGImagechangeDirectionDelegate (Vector3 dir);
    //声明委托对象
    public YGImagechangeDirectionDelegate ygImgDele;
    
    //定义记录摇杆初始位置的字段
    Vector3 startPos;
    //移动的方向,摇杆产生的数据
    Vector3 moveDir;
    //判断当前是否有拖拽
    bool isDrag = false;
    
    //单例脚本
    public static YGImageScript Instance;
    void Awake ()
    {
    Instance = this;
    }
    
    void Update ()
    { 由于摇杆的值在实时的发生变化,实时传值,方法在Update内实现
    if (isDrag) {
    //调用委托实现摇杆数据传递给Cube移动
    ygImgDele (moveDir);
    }
    }
    
    //开始拖拽
    public void BeginDragAction (BaseEventData sender)
    {当鼠标开始拖拽时,记下摇杆的初始位置;并将isDrag设为True
    isDrag = true;
    startPos = transform.position;
    }
    //拖拽中
    public void DragAction (BaseEventData sender)
    {当用户拖拽摇杆的时候,要想实现摇杆的位置随着鼠标点的拖拽移动,并且不超出背景图片(大圆),要首先判断鼠标点位置和摇杆起始位置(即同心圆圆心)之间的距离,保证摇杆不会超出背景范围
    if (Vector3.Distance (startPos, Input.mousePosition) < 50f) {
    transform.position = Input.mousePosition;
    } else {
    //当鼠标拖出大圆时,获取鼠标和初始位置之间的方向,根据方向移动小球位置
    Vector3 dir = (Input.mousePosition - startPos).normalized;
    得到从起始位置到鼠标点位置方向的一条标准化向量
    transform.position = dir * 50f + startPos;
    }
    //将摇杆移动的方向传递出去
    moveDir = (Input.mousePosition - startPos).normalized;
    }
    
    //结束拖拽
    public void EndDragAction (BaseEventData sender)
    {结束拖拽将摇杆归回起始位置
    transform.position = startPos;
    isDrag = false;
    }
    

      

    step3:Cube接收摇杆的传值,运动

    float hor, ver = 0;
    //实现让Cube移动的方法
    void Start ()
    {
    将Cube移动的方法传递给委托对象绑定起来
    当摇杆移动产生方向时,调用委托对象时就会执行让Cube移动的方法
    YGImageScript.Instance.ygImgDele += MoveToPosition;
    }
    void MoveToPosition (Vector3 pos)
    {
    hor = pos.x; ver = pos.y;
    Vector3 dir = new Vector3 (hor, 0, ver);
    transform.position += dir * Time.deltaTime * 5f;
    transform.forward = dir;
    }


    UGUI实现背包系统
    实现效果:在背包中拖拽背包内的物体,拖入空的背包格子内放进去,拖入已有物品的格子内两个物品交换位置。

    实现上述效果,要实现当鼠标拖拽图片时,图片随着鼠标点移动,放下时设置新的父物体,如果是交换位置,要和交换的物品交换父物体

    public void OnBeginDrag (PointerEventData eventData)
    {
    //1.记录原始父物体
    originParent = transform.parent;
    //2.设置当前Item拖拽的临时父物体
    transform.SetParent (tempParent);
    //3.关闭当前Item身上的射线检测,因为要检测的是Item下方的UI控件,来决定当前Item落在哪一个格子里
    GetComponent<Image> ().raycastTarget = false;
    }
    public void OnDrag (PointerEventData eventData)
    {
    //让图片跟随鼠标点移动
    transform.position = Input.mousePosition;
    }
    public void OnEndDrag (PointerEventData eventData)
    {
    //拖拽结束
    if (eventData.pointerEnter == null) {
    transform.SetParent (originParent);
    transform.localPosition = Vector3.zero;
    } else if (eventData.pointerEnter.tag == "Cell" && eventData.pointerEnter.transform.childCount == 0) {
    //表示当前Item落在了空的装备栏上,直接将该装备栏设置为装备的父物体
    transform.SetParent (eventData.pointerEnter.transform);
    //保证当前拖拽的装备图片和装备栏对齐
    transform.localPosition = Vector3.zero;
    } else if (eventData.pointerEnter.tag == "Item") {
    //如果当前装备落在的已经有装备的装备栏上,实现当前装备和将要落下的装备交换装备栏
    transform.SetParent (eventData.pointerEnter.transform.parent);
    eventData.pointerEnter.transform.SetParent (originParent);
    transform.localPosition = Vector3.zero;
    eventData.pointerEnter.transform.localPosition = Vector3.zero;
    } else {
    transform.SetParent (originParent);
    transform.localPosition = Vector3.zero;
    }
    //打开事件监测
    GetComponent<Image> ().raycastTarget = true;
    }
    #endregion
    
    摄像机跟随
    为摄像机设定一个相对于目标物体的固定位置,让摄像机始终位于目标之外的某一个物体
    //表示摄像机应该距离物体的水平距离
    public float distanceAway = 5f;
    //表示摄像机应该距离物体的垂直距离
    public float distanceUp = 3f;
    //摄像机应该在的目标位置
    Vector3 targetPosition;
    //摄像机要跟随的目标位置
    Transform followTarget;
    //摄像机移动的平滑系数
    public float smooth = 1f;
    void Start ()
    {
    followTarget = GameObject.Find ("Player").transform;
    }
    void Update ()
    {
    //得到摄像机要一定到的目标位置
    targetPosition = followTarget.position + Vector3.up * distanceUp - followTarget.forward * distanceAway;
    //设置摄像机的位置到这里
    transform.position = Vector3.Lerp (transform.position, targetPosition, Time.deltaTime * smooth);
    //相机始终看向目标物体
    transform.LookAt (followTarget);
    }

    小地图
    1.在层级视图中新建一个相机将相机设为艾希的子物体
    2.在Project中创建一个Render Texture,拖入新建的摄像机对应的Render Texture中
    3.在场景中创建一个Raw Image将Render Texture拖给它

    4.创建一个Image,将RawImage拖成Image的子物体,为Image添加不同形状的材质,可改变地图显示的形状

    UGUI实现关卡选择
    实现效果:每一关卡都会点亮不同数量的星星,当最高关卡通关后会自动解锁下一关卡;使用鼠标点击任意关卡都能选中该关卡,选中效果是除了这一个关卡的红线框被点亮,其余的所有关卡的红线框都灭掉;每次通关场景切换回来后,都可以继续之前的关卡通关。

     

    在场景的切换的时候,每次加载一个新的场景前一个场景的所有游戏物体都会被释放掉。为了实现在场景与场景之间传值,可使用单例类来存储每次通关后的数据,以便于切换场景后重新加载关卡数据。
    step1:先写实现每一关卡中的功能方法
    //1.设置星星的数量
        public void SetStarNumber (int num)
        {
            //a.隐藏锁
            locked.gameObject.SetActive (false);
            //b.显示星星背景
            stars.gameObject.SetActive (true);
            //c.设置星星的数量(注意先后顺序)
            GameObject child0 = stars.transform.GetChild (0).gameObject;
            GameObject child1 = stars.transform.GetChild (2).gameObject;
            GameObject child2 = stars.transform.GetChild (1).gameObject;
            //将这三个子物体存储在数组中
            GameObject[] childs = { child0, child1, child2 };
            //将三颗星星初始化为隐藏
            for (int i = 0; i < childs.Length; i++) {
                childs [i].SetActive (false);
            }
            //根据传进来数值显示星星
            for (int i = 0; i < num; i++) {
                childs [i].SetActive (true);
            }
        }
        //2.红线的关闭和开启
        public void SetRedLineImage (bool flag)
        {
            redLineImage.gameObject.SetActive (flag);
        }
        //3.每一个关卡上的数字
        public void SetPassNumText (int num)
        {
            passNum.text = num.ToString ();
        }
        //4.获取当前正在游戏的关卡数
        public int GetCurrentPassnum ()
        {
            return int.Parse (passNum.text);
        }
        //5.显示锁,隐藏星星,关闭红线,关闭星星背景
        public void SetLockedImg ()
        {
            //隐藏星星
            SetStarNumber (0);
            //显示锁
            locked.gameObject.SetActive (true);
            //关闭红线
            SetRedLineImage (false);
            //关闭背景
            stars.gameObject.SetActive (false);
        }
        //6.开启新关卡
        public void SetNewCellBtn ()
        {
            //没有星星等级
            SetStarNumber (0);
            // 显示红线
            SetRedLineImage (true);
            //修改当前关卡是谁
            LevelManger.Instance.curPlayingLevel = GetCurrentPassnum ();
        }
        //7.选中一个关卡时,关闭其他红线,显示自己的红线
        public void SelectCellBtnAction ()
        {
            //如果没有关卡被选中
            if (locked.gameObject.activeSelf) {
                return;
            }
            //通过遍历某一个Button的父物体,将所有的子物体的红线隐藏
            foreach (Transform cellBtn in transform.parent.GetComponentInChildren<Transform>()) {
                cellBtn.gameObject.GetComponent<CellButtonScripts> ().SetRedLineImage (false);
            }
            //将自己的红线打开
            SetRedLineImage (true);
            //修改当前关卡是谁
            LevelManger.Instance.curPlayingLevel = GetCurrentPassnum ();
        }
    
    step2 :通过代码控制在场景中创建关卡,并初始化。由于每一次加载场景,都会更新创建。所以在创建关卡时要获取到存储在单例类中关卡信息。
    //关卡按钮的预制体
        public GameObject cellButtonPrefab;
        //用一个cell产生之后的父物体
        public Transform content;
        //设置游戏的总关卡数
        public int totalLevelNum = 12;
    
        void Start ()
        {
            for (int i = 1; i <= totalLevelNum; i++) {
                //获取到创建的游戏物体
                GameObject cellBtn = Instantiate (cellButtonPrefab);
                cellBtn.transform.SetParent (content);
                //设置关卡数
                cellBtn.GetComponent<CellButtonScripts> ().SetPassNumText (i);
                //设置没有开启的关卡
                cellBtn.GetComponent<CellButtonScripts> ().SetLockedImg ();
                //如果单例中包含当前关卡的数据,赋值
                if (LevelManger.Instance.scoreInfoDic.ContainsKey (i)) {
                    //从单例类中去除对应关卡数据
                    int num = LevelManger.Instance.scoreInfoDic [i];
                    //设置给cellBtn
                    cellBtn.GetComponent<CellButtonScripts> ().SetStarNumber (num);
                }
                //判断当前关卡是否是最高关卡,如果是,就解锁下一关
                if (LevelManger.Instance.maxPassLevel == i) {
                    //解锁关卡
                    cellBtn.GetComponent<CellButtonScripts> ().SetNewCellBtn ();
                }
            }
        }
        //点击开始游戏按钮的响应事件
        public void StartGameAction ()
        {
            //加载修改数据的场景
            SceneManager.LoadScene (1);
        }
    
    step3:创建单例类保存每次重新加载的关卡数据,并实现两个场景之间的传值
    单例类不需要继承于MonoBehaviour类,没有脚本的声明周期,不用挂载在游戏物体上。所以不用担心每次重新加载场景是被释放掉。
    public class LevelManger
    {
        //定义字典来存储每一管对应的星星数量
        public Dictionary<int,int> scoreInfoDic;
        //当前玩的是哪一关卡
        public int curPlayingLevel;
        //检测当前完了多少关
        public int maxPassLevel = 3;
    a.私有化构造函数,使该类不能在外部被实例化
        private LevelManger ()
        {
            //初始化存储数据的字典
            scoreInfoDic = new Dictionary<int, int> ();
            scoreInfoDic [1] = 2;
            scoreInfoDic [2] = 3;
    
        }
    b.私有化一个静态单例类的对象
        private static LevelManger level = null;
    c.在一个公开的静态属性中实现单例类的初始化
        public static LevelManger Instance {
            get {
                if (level == null) {
                    level = new LevelManger ();
                }
                return level;
            }
        }
    
    step4:数据修改场景,完成关卡数据的修改,并返回上一场景
    //分数的输入框
        public InputField scoreInputField;
        //实现返回上个场景的方法
        public void ReturnUISceneAction ()
        {
            //拿到当前正在玩的关卡
            int level = LevelManger.Instance.curPlayingLevel;
            int score = int.Parse (scoreInputField.text);
            if (score <= 0 || score > 3) {
                Debug.Log ("输入有误!");
                return;
            }
    
            //修改当前关卡对应的星星
            LevelManger.Instance.scoreInfoDic [level] = score;
            // 如果修改的关卡是当前存储的最大关卡,那么要将单例中最大关卡数加1
            if (LevelManger.Instance.maxPassLevel == level) {
                LevelManger.Instance.maxPassLevel++;
            }
            //返回之前的场景
            SceneManager.LoadScene (0);
        }
    
    Application类:
    //返回程序的数据文件所在的文件夹的相对路径,(只读)
            //Debug.Log (Application.dataPath);
            //返回当前程序运行的平台
            //Debug.Log (Application.platform.ToString ());
            //判断当前程序是否支持在后台运行
            //Debug.Log (Application.runInBackground.ToString ());
            //获取当前场景的索引值
            //Debug.Log (Application.loadedLevel.ToString ());
            //Debug.Log (Application.loadedLevelName.ToString ());
            Debug.Log (SceneManager.GetActiveScene ().buildIndex);
            Debug.Log (SceneManager.GetActiveScene ().name);
            Debug.Log (SceneManager.sceneCountInBuildSettings + "");
  • 相关阅读:
    Sublime Text 3 格式化HTML CSS JS 代码
    CSS 温故而知新
    JQuery Mobile 学习笔记
    Eclipse ADT 代码注释模版
    Eclipse ADT 与VS 常用的快捷键 对比学习
    元组、文件、以及其他
    类型与运算
    python
    python 内置函数 lamda表达式。 open 文件方法
    python 基础 基本数据类型
  • 原文地址:https://www.cnblogs.com/zpy1993-09/p/11750322.html
Copyright © 2011-2022 走看看