二.Unity中使用虚拟摇杆来控制角色移动
上一篇中我们完成了不借助第三方插件实现手游的虚拟摇杆,现在借助这个虚拟摇杆来实现控制角色的移动。
虚拟摇杆实际上就给角色输出方向,类似于键盘的WSAD,也是一个二维坐标,也就是(-1,1)的范围,将摇杆的方向进行归一化传递给角色即可,创建一个名为PlayerManager的脚本,通过Character组件来控制角色的移动。这里在上一篇文章在一. Unity实现虚拟摇杆及屏幕自适应功能,讲述过了,这里拖拽的时候将位移的向量归一化后传递给角色,松手的时候归0.
mUIEvtListener.OnPointerUpEvent += (eventData =>
{
mPointImg.transform.localPosition = mDefaultPos;
mPointImg.SetActiveState(false);
mDirImg.transform.localPosition = mDefaultPos;
PlayerManager.Instance.SetPlayerDir(Vector2.zero);
});
mUIEvtListener.OnDragFunc += (eventData =>
{
Vector2 dir = eventData.position - mStartPos;
float length = dir.magnitude;
PlayerManager.Instance.SetPlayerDir(dir.normalized);
if (length > mTouchMaxDir)
{
Vector2 clampDir = Vector2.ClampMagnitude(dir, mTouchMaxDir);
mPointImg.transform.position = mStartPos + clampDir;
}
else
{
mPointImg.transform.position = eventData.position;
}
});
在PlayerManager中就是一些常规操作,设置方向,这里用了一个Vector2.SignedAngle的API,其作用就是返回传入向量和目标向量之间的角度,我们设定的目标向量是(0,1)也就是二维向量的正前方,下面画了一张图,应该会很清楚。其中为什么要+mCamTrans.eulerAngles.y,是因为相机是倾斜的照着角色,摄像机于角色之间有一个相对角度,是为了能够确保玩家的移动方向与摄像机视角一致,不论摄像机朝哪个方向,玩家的输入都能按照当前摄像机的视角调整角色的朝向。
public class PlayerManager : Component<PlayerManager>
{
private const float TouchSpeed = 5;
private CharacterController mCC;
private Animator mAnim;
private Vector2 mDir;
private Transform mCamTrans;
private Vector3 mOffset;
private float mCurAnimSpeed;
private float mTargerAnimSpeed;
private const float AccelerSpeed = 5;
public override void IStart()
{
base.IStart();
mCC = GetComponent<CharacterController>();
mAnim = GetComponentInChildren<Animator>();
mCamTrans = Camera.main.transform;
mOffset = transform.position - mCamTrans.transform.position;
}
public override void IUpdate()
{
base.IUpdate();
if (mDir != Vector2.zero)
{
SetDir();
SetMove();
SetCamera();
}
}
/// <summary>
/// 通过摇杆控制玩家方向
/// </summary>
/// <param name="dir"></param>
public void SetPlayerDir(Vector2 dir)
{
if (dir != Vector2.zero)
{
mDir = dir;
SetAnimSpeed(1);
}
else
{
mDir = Vector2.zero;
SetAnimSpeed(0);
}
}
private void SetDir()
{
float angle = Vector2.SignedAngle(mDir, new Vector2(0, 1)) + mCamTrans.eulerAngles.y;
transform.eulerAngles = new Vector3(0, angle, 0);
}
private void SetMove()
{
mCC.Move(transform.forward * Time.deltaTime * TouchSpeed);
}
private void SetCamera()
{
mCamTrans.transform.position = transform.position - mOffset;
}
}
}
最后简单设置一下角色的动画,将Idle和Run进行一个简单的混合,通过插值的形式,让当前的Speed以平缓的过渡到目标值。
public class PlayerManager : Component<PlayerManager>
{
private float mCurAnimSpeed;
private float mTargerAnimSpeed;
private const float AccelerSpeed = 5;
public override void IUpdate()
{
base.IUpdate();
if (mDir != Vector2.zero)
{
SetDir();
SetMove();
SetCamera();
}
if (mCurAnimSpeed != mTargerAnimSpeed)
{
UpdateAnimFixed();
}
}
private void SetAnimSpeed(float speed)
{
mTargerAnimSpeed = speed;
}
private void UpdateAnimFixed()
{
if (Mathf.Abs(mCurAnimSpeed - mTargerAnimSpeed) > AccelerSpeed)
{
mCurAnimSpeed = AccelerSpeed;
}
else if(mCurAnimSpeed > mTargerAnimSpeed)
{
mCurAnimSpeed -= Time.deltaTime * AccelerSpeed;
}
else if(mCurAnimSpeed< mTargerAnimSpeed)
{
mCurAnimSpeed += Time.deltaTime * AccelerSpeed;
}
mAnim.SetFloat("Speed",mCurAnimSpeed);
}
}
测试运行结果如下: