基于PF规则的CRPG制作尝试(三)实现通过点击地面完成角色的移动

基于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的差值。
目前暂且观望,等到之后做攻击判定的时候再考虑是否需要解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值