前言
小游戏Konster在测试的时候有热心玩家反馈左右移动手感不是很好,最主要的问题是:比如手指一旦按在手机屏幕的右方向按钮上,向左滑动到另一个左移按钮上是不会改变玩家移动方向。
具体如下图:
刚开始我按下了→按钮,小怪兽行走到一半的时候我将拇指滑动到了←按钮,但是小怪兽并没有向左移动,然后就继续向右直到撞上尖刺死亡。
原因就是:我写的脚本只监听了按钮的按下和抬起事件。对于拇指在按钮上的滑动没有做响应处理。
所以我决定重写一个JoyStick脚本来控制小怪兽的移动方向。
实现思路
参考了这篇博客【Unity3d基础】Unity3d中利用NGUI实现虚拟摇杆。它实现的是一个360度摇杆。
我要实现的只需要左右感应就行,所以我先将左右按钮的事件监听删掉,保留图片即可。然后在按钮下方添加一个新的滑动条,挂上BoxCollider,将滑动条的左右anchor设置为leftBtn的最左和rightBtn的最右,这样自适应的时候,中间区域会随着按钮的适应和变长变短。上下anchor保持和按钮的设置一样------离屏幕底部一定距离即可。
然后在滑动条物体下面创建一个指示拇指位置的摇杆按钮。
然后我们需要写一个脚本来监听这个条状区域的按下和滑动事件。代码如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class JoyStick :MonoBehaviour{ public static float direction; //决定玩家的移动方向 public Transform target; //摇杆按钮 public float sensibility = 1; //摇杆灵敏度 public float DragDistance; //摇杆平移的最大距离 private void FixedUpdate() { if (target.localPosition.x > 0) direction = 1; else if (target.localPosition.x == 0) direction = 0; else direction = -1; } void OnDrag(Vector2 delta) //NGUI组件的拖动事件回调 { float targetPosition = target.localPosition.x + delta.x * sensibility; //下一帧的位置 if(Mathf.Abs(targetPosition) >= DragDistance ) { if (targetPosition > 0) targetPosition = DragDistance; else targetPosition = -DragDistance; } target.localPosition = new Vector2(targetPosition, target.localPosition.y); } void OnPress(bool isPressed) //NGUI组件的按住事件回调 { if (isPressed) { Debug.Log(UICamera.currentTouch.pos); if (UICamera.currentTouch.pos.x > 7 * Screen.width/32) //如果点击摇杆区域,判断点击是在中心点右边还是左边 target.localPosition = new Vector2(DragDistance , target.localPosition.y); else target.localPosition = new Vector2(-DragDistance, target.localPosition.y); } else target.localPosition = Vector2.zero; } }
在这部分代码中,我们用了OnDrag()函数来处理滑动事件,OnPress()来处理抬起和按下事件。需要特别说明的是,这两个函数的调用机制类似unity的OnGUI(),NGUI的相机会向每一个在NGUI相机渲染下可见的NGUI组件发送触发的对应事件,然后回调对应的函数。以下为NGUI的基础事件系统:
UIEvents-事件系统
void OnHover (bool isOver):当鼠标移出或者悬停在某个碰撞器上的时候返回布尔值.在触摸设备上不会有作用.
void OnPress (bool isDown):当鼠标或者触摸到碰撞器发生布尔值返回.
void OnSelect (bool selected):当鼠标或者触摸从OnPress发生后的释放将会返回这个布尔值.
void OnClick():和OnSelect的产生条件相同,当点击或触摸碰撞器并且没有发生拖拽时候触发.
void OnDrag (Vector2 delta):当移动鼠标或者触摸按下时候位移超过特定阀值时触发.
void OnDrop (GameObject drag):当鼠标或触摸释放于从发生OnDrag的不同碰撞器伤触发.传递的参数是产生OnDrag的游戏对象.
void OnInput (string text):当一个OnSelect发生后在同一个碰撞器上触发输入.一般只有UIInput用它.
void OnTooltip (bool show):当鼠标悬停超过tooltipDelay时间后触发该命令.触摸设备上不会有作用.
回到正题
在OnDrag函数里,我们直接根据每帧滑动的距离delta来更新target(也就是上面的摇杆按钮Thumb)在滑动条坐标系下的位置(localPosition),然后用一个变量distance限制左右滑动的最大距离,sensibility来控制摇杆按钮的速度,摇杆按钮的初始位置为Vector2.Zero(也就是(0,0))。当localPositon的横坐标大于0时,表示向右滑动,怪兽向右移动;反之向左。
在OnPress函数里,我们根据手指触摸的区域来判断是触摸了滑动条的左半部分还是右半部分,所以我们需要确定滑动条中间位置在屏幕上的坐标。
UICamera.currentTouch.pos这个值是以屏幕的左下角为原点来计算触摸点的坐标,如下图:
所以只要确定了滑动条的中心位置(图中的绿色分割线)在屏幕上的横坐标,就可以根据这个横坐标和触摸点的坐标来判断手指按下的区域是在滑动条左边还是右边了。最后附上实现效果图: 滑动Drag
按下Press
如果觉得摇杆按钮有点影响美观,可以将它的Sprite精灵的alpha值设置为0,这样效果变成:
这样,一个基本的左右摇杆就完成了(^▽^)。
参考资料
Unity3D NGUI事件 UIEvents 作者:脱莫柔
【Unity3d基础】Unity3d中利用NGUI实现虚拟摇杆 作者:coffeecao