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 + "只");
原文:https://www.cnblogs.com/zpy1993-09/p/11750322.html