基于PF规则的CRPG制作尝试(三)实现通过点击地面完成角色的移动
前次将几个基础的动作动画导入了模型并设计了animator。本次准备完成角色移动。
作为crpg,每回合是有基于速度和负重的移动距离限制的,所以比起WASD式的移动控制,我认为鼠标右键点击的移动方式更为合适。
这样一来,就有几个问题点。
1.确定角色当前位置。
2.确定鼠标点击的位置
3.进行转向和移动
4.绑定动画
一、确定角色的当前位置
这一步并不难,直接用 AcotorTrans = GetComponent();获取即可。
二、确定鼠标点击的位置
本方法主要由以下几个部分组成:
1.点击鼠标右键,获取鼠标在屏幕上的坐标信息。
2.将该坐标转化为世界坐标。
3.通过角色当前位置的世界坐标和鼠标点击的世界坐标,获取移动的向量。
4.实现转向。
5.实现移动。
1.点击鼠标右键,获取鼠标在屏幕上的坐标信息
if (Input.GetMouseButtonDown(1))
{
Vector3 mousepostion = Input.mousePosition;
}
2.将该坐标转化为世界坐标
此处使用的是ray方法,即:发射一条起点为摄像头,沿鼠标点击方向的射线ray,并求出该射线与地面的碰撞交点hit:
Ray ray = Camera.main.ScreenPointToRay(mousepostion);
RaycastHit hitInfo;
3.通过角色当前位置坐标和鼠标点击的世界坐标,获取移动向量
endPoints = new List<Vector3>();
Vector3 v = endPoints[0] - AcotorTrans.position;
4.实现转向
//获取forward 与位移向量的夹角
float angle = Vector3.Angle(v, AcotorTrans.forward);
var dot = Vector3.Dot(v, AcotorTrans.right);
if (Vector3.SqrMagnitude(v) > 1f)
{ //通过转速变量来控制转向速度
float minAngle = Mathf.Min(angle, rotatePower * Time.deltaTime);
//点乘(便于辨认左右)
if (angle > 1f)
{
if (dot > 0)
{
AcotorTrans.Rotate(new Vector3(0, minAngle, 0));
}
else
{
AcotorTrans.Rotate(new Vector3(0, -minAngle, 0));
}
}
}
5.实现移动
Vector3 next = v.normalized * moveSpeed * Time.deltaTime;
AcotorTrans.position += next;
三、完整代码
public class ActorController : MonoBehaviour
{
public Animator anim;
public ActorData adata;
public enum STATE
{
IDLE,
SWORDIDLE
}
public STATE state;
Transform MyTrans;
List<Vector3> endPoints;
// moveSpeed 是一个浮点数, 为预设的角色移动速度
public static float moveSpeed = 1.0f;
// rotatePower 是一个浮点数, 为与角色转向速度相关的预设值
public static float rotatePower = 300.0f;
// Start is called before the first frame update
void Start()
{
MyTrans = GetComponent<Transform>();
endPoints = new List<Vector3>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.J))
{
if (state == STATE.IDLE)
{
anim.SetTrigger("drawsword");
state = STATE.SWORDIDLE;
}
}
if (Input.GetKeyDown(KeyCode.K))
{
if (state == STATE.SWORDIDLE)
{
anim.SetTrigger("attack");
}
}
if (Input.GetKeyDown(KeyCode.L))
{
if (state == STATE.SWORDIDLE)
{
anim.SetTrigger("clear");
state = STATE.IDLE;
}
}
if (Input.GetMouseButtonDown(1))
{
if (state == STATE.IDLE)
{
anim.SetBool("movewithoutsword", true);
UpdateControl();
}
if (state == STATE.SWORDIDLE)
{
anim.SetBool("movewithsword", true);
UpdateControl();
}
}
print(endPoints.Count);
//实时检查,如果终点数组非空,就继续走
//if (endPoints[0]!= MyTrans.position)
//print("终点数组中终点的个数为:"+endPoints.Count);
if (endPoints.Count > 0)
{
print("检测到终点数组非空");
print("终点坐标是:" + endPoints[0]);//经验证,终点坐标识别的没问题
Vector3 v = endPoints[0] - MyTrans.position;
var dot = Vector3.Dot(v, MyTrans.right);
Vector3 next = v.normalized * moveSpeed * Time.deltaTime;
//print("v的值:" + "x:" + v.x + "y:" + v.y + "z:" + v.z);
//print("next的值:" + "x:" + next.x + "y:" + next.y + "z:" + next.z);
// print(Time.deltaTime);
//计算转头角度
float angle = Vector3.Angle(v, MyTrans.forward);
if (Vector3.SqrMagnitude(v) > 1f)
{
float minAngle = Mathf.Min(angle, rotatePower * Time.deltaTime);
//点乘
if (angle > 1f)
{
//transform.Rotate(Vector3.Cross(tank.forward, v.normalized), minAngle);
if (dot > 0)
{
MyTrans.Rotate(new Vector3(0, minAngle, 0));
}
else
{
MyTrans.Rotate(new Vector3(0, -minAngle, 0));
}
}
else
{
MyTrans.LookAt(endPoints[0]);
MyTrans.position += next;
MyTrans.position += next;
print("走完了一小步");
}
}
else
{
//清空endpoints
endPoints.RemoveAt(0);
}
}
else
{
if (state == STATE.IDLE)
{
anim.SetBool("movewithoutsword", false);
}
if (state == STATE.SWORDIDLE)
{
anim.SetBool("movewithsword", false);
}
}
void UpdateControl()
{
//获取屏幕坐标
Vector3 mousepostion = Input.mousePosition;
//定义从屏幕
Ray ray = Camera.main.ScreenPointToRay(mousepostion);
RaycastHit hitInfo;
//如果点击并有交点
if (Physics.Raycast(ray, out hitInfo))
{
print("有人点鼠标?");
//按了shift的情况,直接加入表单,实现预设移动
if (Input.GetKey(KeyCode.LeftShift))
{
//获取交点并加入endpoints列表
AddEndPoint(hitInfo.point);
}
//没按shift单独点击的话,就表单里只有这一个目的地
else
{
ReSetEndPoint(hitInfo.point);
}
//end = hitInfo.point;
//print("x为:"+hitInfo.point.x);
//print("y为:" + hitInfo.point.y);
//print("z为:" + hitInfo.point.z);
}
}
//加入终点列表的函数,切仅保留x和z
void AddEndPoint(Vector3 endPoint)
{
endPoint.y = MyTrans.position.y;
endPoints.Add(endPoint);
print("新的点加入了坐标");
}
//重置endpoints,终点列表里仅保留一个终点
void ReSetEndPoint(Vector3 endPoint)
{
//傻瓜修正,在当前移动方向上增加一个单位的长度
Vector3 v2 = endPoint - MyTrans.position;
endPoint += v2.normalized;
endPoint.y = MyTrans.position.y;//y不需要
endPoints.Clear();
endPoints.Add(endPoint);
print("导航终点已经重置到:(" + endPoints[0].x + "," + endPoints[0].z + ")");
}
}
}
遇到的问题
在移动过程中,发生了目标点与鼠标点击点视觉位置不一致的问题,原因不明,最后发现相差的距离正好为1,于是进行傻瓜修正进行了调整,视觉效果上是一致了,可是移动后角色的世界坐标与点击位置的世界坐标就会产生1的差值。
目前暂且观望,等到之后做攻击判定的时候再考虑是否需要解决。