• 系列目录 【Unity3D基础】让物体动起来①--基于UGUI的鼠标点击移动Unity3D基础】让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoTween...时光煮雨 Unity3D实现2D人物动画② Un...

    系列目录

    【Unity3D基础】让物体动起来①--基于UGUI的鼠标点击移动

    【Unity3D基础】让物体动起来②--UGUI鼠标点击逐帧移动

    时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现

    时光煮雨 Unity3D实现2D人物动画① UGUI&Native2D序列帧动画

    时光煮雨 Unity3D实现2D人物动画② Unity2D 动画系统&资源效率

    背景

    最近研究Unity3d,2d寻路的实现。所以又一次涉及到了角色坐标位移的问题。系统的对于这个简单问题进行整理和总结。本来就是一个简单的几何问题,结果发现已经有两个小坑,顺便填上,这里做下总结。

    move1

    实现

    需求:通过鼠标点击,控制2d角色移动,就是点哪里,角色向移动到哪里

    问题分解:按照时间进行动画分解,鼠标输入(动画开始)、平移(动画进行)、移动结束(动画结束)

    前提:这里前面的文章基本解决了一些基础的知识,比如IO获取(鼠标输入),移动的基本方式(Unity中的位置系统transform)

    坑:1、平移中的平滑移动,2、如何确定移动了目标点,并使物体停止下来

    坑1:平移中的平滑移动

    补充知识,关于角色的平移和位置更新,Unity无非就几种方式

    A、transform.Translate(new Vector3(1, 1, 1) * moveSpeed * Time.deltaTime); // Translate方法移动不会考虑刚体等碰撞(会直接穿过物体)

    // 确保我们的速度不会超过maxDistanceDelta
    B、Vector3.MoveTowards(transform.position, targetPos.position, speed * Time.deltaTime);

    // 速度会超过移动速度,像弹簧一样
    C、Vector3.Lerp(transform.position, targetPos.position, speed * Time.deltaTime);

    D、直接设置transform.Positon,最简单的方式

    这个坑,真是坑了很多很多人,目前网上一半以上的教程,从严格意义上都是错误的,这里真的想吐槽一下(太他妈不负责了),这个问题我在群里问过一次,结果还被怀疑是菜鸟,其实焦点还是 我用红色标出的这个线性插值函数,其实简单的不得了,就是个直线方程。这里可以参考,以下这两篇文章

    unity3d问题集 <2> 对Vector3.Lerp 插值的理解

    unity3d Vector3.Lerp解析 http://www.cnblogs.com/shenggege/p/5658650.html

    分析为什么“速度会超过移动速度,像弹簧一样”和 线性插值的函数,后来我仔细想了想,其实还是自己知识掌握的不够透彻,具体我们了解以后分析下,经典教程中的函数

        public float moveSpeed;
        public float turnSpeed;

        private Vector3 moveDirection;
        // Use this for initialization
        void Start () {
            moveDirection = Vector3.right;
        }
       
        // Update is called once per frame
        void Update () {

            // 1
            Vector3 currentPosition = transform.position;
            // 2
            if( Input.GetButton("Fire1") ) {
                // 3
                Vector3 moveToward = Camera.main.ScreenToWorldPoint( Input.mousePosition );
                // 4
                moveDirection = moveToward - currentPosition;
                moveDirection.z = 0;
                moveDirection.Normalize();
            }

            Vector3 target = moveDirection * moveSpeed + currentPosition;
            transform.position = Vector3.Lerp( currentPosition, target, Time.deltaTime );

        }
    }

    这里我们看红色部分的文字,这里之所以不会出现弹簧移动的效果,主要是每次插值都是当前点和这帧将要移动点的位置的插值,其实这里根本没有必要 ,直接设置 transform.position = moveDirection * moveSpeed*Time.deltaTime + currentPosition;(其实本身就是一个 基于时间的线性移动)

    还有 本身 Vector3.Lerp(transform.position, targetPos.position, speed * Time.deltaTime); 这么用就有很大的问题

    A、speed * Time.deltaTime 当speed设置很大而帧率很低的时候这个系数可能全是1,这样根本就是不插值,

    B、当用UGUI时坐标系统是屏幕坐标值很大,这样插值会很不准(这也是我曾经问过的问题,不过没有人回答我)

    至此第一个坑填上了,下面我列出使用不同方式来进行移动的相关代码

    第一种,改进型插值移动

    /// <summary>
    /// 使用Vector3的插值进行更新位置
    /// </summary>
    private void MoveByVector3Lerp()
    {
        //1、获得当前位置
        Vector3 curenPosition = this.transform.position;
        //2、获得方向
        if (Input.GetButton("Fire1"))
        {
            Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            moveTowardPosition = moveToward;
            moveTowardPosition.z = 0;
    
            moveDirection = moveToward - curenPosition;
            moveDirection.z = 0;
            moveDirection.Normalize();
        }
    
        var distance = Vector3.Distance(curenPosition, moveTowardPosition);
       // Debug.Log(string.Format("curenPosition:{0}, moveTowardPosition{1},distance:{2},speed:{3}", curenPosition, moveTowardPosition, distance, speed * Time.deltaTime));
        if (distance < 0.01f)
        {
            transform.position = moveTowardPosition;
        }
        else
        {
            //3、插值移动
            //目标位置方向加上速度移动
            Vector3 target = moveDirection*speed*Time.deltaTime + curenPosition;
            target.z = 0;
            transform.position = target;
        }
    }
    

      

    第二种,MoveTowards进行移动更新

    /// <summary>
    /// 使用Vector3的MoveTowards 直接进行位置更新 
    /// </summary>
    private void MoveByVector3MoveTowards()
    {
        //1、获得当前位置
        Vector3 curenPosition = this.transform.position;
        //2、获得方向
        if (Input.GetButton("Fire1"))
        {
            moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            moveTowardPosition.z =0;
        }
        if (Vector3.Distance(curenPosition, moveTowardPosition) < 0.01f)
        {
            transform.position = moveTowardPosition;
        }
        else
        {
            //3、插值移动
            //距离就等于 间隔时间乘以速度即可
            float maxDistanceDelta = Time.deltaTime * speed;
            transform.position = Vector3.MoveTowards(curenPosition, moveTowardPosition, maxDistanceDelta);
        }
    }
    

      

    第三种,transform.Translate

    /// <summary>
    /// 使用Vector3的Translate 直接进行位置更新 
    /// </summary>
    private void MoveByTransformTranslate()
    {
        //1、获得当前位置
        Vector3 curenPosition = this.transform.position;
        //2、获得方向
        if (Input.GetButton("Fire1"))
        {
            moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            moveTowardPosition.z = 0;
    
            moveDirection = moveTowardPosition - curenPosition;
            moveDirection.z = 0;
            moveDirection.Normalize();
        }
        //3、插值移动
        Vector3 target = moveDirection * speed * Time.deltaTime + curenPosition;
        target.z = 0;
        if (Vector3.Distance(curenPosition, moveTowardPosition) < 0.01f)
        {
            transform.position = moveTowardPosition;
        }
        else
        {
            transform.Translate(target - curenPosition);
        }
    }
    

      

    坑2:如何确定移动了目标点,并使物体停止下来

    补充知识:其实坑1中列出的三种平移方法,其实并不是什么套路,不是什么标准的动画移动方式,虽然他们也是基于时间的,只能归纳成一种简单的顺序帧移动,这里我查了很多资料还有一种基于时间线的移动方式。

    问题描述:这里先说下坑2是怎么回事,就是我们希望角色移动到鼠标点击的点以后停下来,结果发现停不下来,通过调试日志主要的问题在这一行(这也是我以前提出过的一个问题,但无人解答)

    if (Vector3.Distance(curenPosition, moveTowardPosition) < 0.01f)

    实际上这行代码非常不靠谱,至少有两点

    A、单位差异,UGUI中是屏幕坐标也是localPositon像素,Native中是Unit两个单位不同判断的这个距离常量不一样

    B、由于speed * Time.deltaTime 每帧移动的距离是与速度和帧率有关的,这个常量(0.01)必须与之匹配需要设置合理的值

    C、使用插值计算3维坐标误差会扩大,这里我用“第一种,改进型插值移动”,“第三种,transform.Translate”都出现了误差较大的情况,而“第二种,MoveTowards进行移动更新”,就很准确。

    所以系统给出的函数

    Vector3.MoveTowards(curenPosition, moveTowardPosition, maxDistanceDelta);

    不是白给的,这也是很多人推荐使用这个函数的原因(但不告诉我们为什么)

    最后给出我自己写的基于时间线的位移实现

    /// <summary>
        /// 鼠标点击移动,目标点
        /// </summary>
        private Vector3 moveTowardPosition = Vector3.zero;
        private Vector3 moveStartPosition = Vector3.zero;
        private float totalTime = 0.0f;
        private float costTime = 0.0f;
        private float timePrecent = 0.0f;
    
        private bool _isRuning = false;
    
        /// <summary>
        /// 是否正在移动
        /// </summary>
        public bool IsRuning
        {
            get { return _isRuning; }
            set { _isRuning = value; }
        }
    
        private void MoveByTimeline()
        {
            /*
             * 获得移动的最终目标位置,根据移动速度获得一共需要移动的时间 totalTime
             * 每一帧,
             *   1、累加 已经逝去的时间,并得到costTime,并获得移动的百分比 precent = costTime/totalTime
             *   2、获得当前精灵的位置,根据precent 进行位置插值,得到这一帧应该移动的位置
             *   3、使用设置移动
             *   4、通过precent判断是否<1 来判断是否移动到了目标位置
             *   5、如果完成,则调用最后一次移动实现,终点移动误差,并置为一些标志位
             */
            //获得当前位置
            Vector3 curenPosition = this.transform.position;
            if (Input.GetButton("Fire1"))
            {
                moveStartPosition = curenPosition;
                //获得移动终点位置
                moveTowardPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                moveTowardPosition.z = 0;
    
                costTime = 0.0f;
                //计算记录
                var subVector3 = moveTowardPosition - curenPosition;
                //计算需要移动的总时间
                totalTime = subVector3.magnitude / speed;
    
                _isRuning = true;
            }
            //如果已经移动
            if (_isRuning)
            {
                //如果时间百分比小于1 说明还没有移动到终点
                if (timePrecent < 1)
                {
                    //累加时间
                    costTime += Time.deltaTime;
                    timePrecent = costTime/totalTime;
    
                    Vector3 target = Vector3.Lerp(moveStartPosition, moveTowardPosition, timePrecent);
                    transform.position = target;
      
                }
                else //大于或者等于1 了说明是最后一次移动
                {
                    transform.position = moveTowardPosition;
                    _isRuning = false;
                    moveTowardPosition = Vector3.zero;
                    timePrecent = 0.0f;
                    costTime = 0.0f;
                }
            }
        }
    

      

    这种方法基本排除了,移动到终点的位移误差问题,缺点是使用的临时变量较多(我不喜欢),而“第二种,MoveTowards进行移动更新”可以基本不使用临时变量。时间线动画实际上这也是一些小的平移组件及itween的核心原理(为什么,还需要进一步探索,也许扩展性更强)

    总结

    反正被坑很不爽,不过也怪不了别人,还是自己才疏学浅(不是天才,就使劲干)。下一篇 继续探索角色的系列目标点的移动

    转载于:https://www.cnblogs.com/IlidanStormRage/p/6126362.html

    展开全文
  • Unity3D 角色转向和自动跟随通过挂载脚本控制3D人物的移动和转向,先上一个官方的例子:这里只有转向而没有移动的方法,看了下原工程,移动是做在动画中的,只要执行了动画就会跟着移动,而且每迈出一步的距离是固定...
  • 1.简介 在Unity3D中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position。常用API的脚本,会增强代码能力。2. 通过Transform组件移动物体2.1 Transform.Translate(键盘输入,最最最...
  • 在Unity4.3版本以后,Unity3D推出了基于Box2D2D组件,使得Unity3D成为一个可以支持2D游戏开发的游戏引擎。在Unity3D推出这一功能之前,大家对使用Unity3D开发2D游戏已经进行了大量的研究。所以,Unity3D推出2D组件...
  • using UnityEngine; using System.Collections; public class HeroColtrol : MonoBehaviour { private Rigidbody2D HeroRd; public float MoveSpeed; pu...
  • 简单Unity2D角色控制示例 2019-06-25 22:03:23
    之前我做2D游戏,需求是控制一个3D...项目使用的2D地图是使用Ferr2D制作的 然后只需要加上BoxCollider即可在2D地图上面运行3D角色 脚本可以辨别斜坡的角度 解决了使用transform控制人物的情况下 下陡坡抖动的问题...
  • 首先是在俯视角摄像机下的键盘控制角色移动(如下图), 先写一个最简单的移动脚本吧(这个脚本也可以用于平常的小测试) 在这之前先得为角色挂上character controller组件,然后获取它。 void move(){ //...
  • Unity移动某个游戏对象有以下两种方式:·更改Transform组件的Position属性;·利用物理引擎(PhyX, Box2D移动游戏对象。首先我们试着通过调整Transform组件的属性实现游戏对象的移动。在Unity的左手坐标系中,+...
  • 整理截图自官方视频教程,unity2D角色控制器教程(基于untiy5修改)
  • Unity 2D角色跳跃 2019-08-01 11:57:42
    一,在角色下添加一个空物体 using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { private Rigidbody2D m_rg; ...
  • 先上一个gif看看效果。因为图片大小限制。... 1:摄像机跟着角色移动,上篇博客说的是。把摄像机变成角色的子对象。发现没有我想要的效果。跟着角色移动代码是我自己弄出来的。不知道实际项目中是否也是这样。
  • 1.改变对象速度 rb = GetComponent(); if (Input.GetKey(KeyCode.LeftArrow)) { rb.velocity = new Vector2(-movespeed, rb.velocity.y); } if (Input.GetKey(KeyCode.RightArrow)) { rb.velocity = new Vector
  • Unity3D 2D角色看向鼠标方向 本篇文章是通过动画控制器 (Animator) 中的混合树 (Blend Tree) 来实现2D角色看向鼠标方向,希望能对您有帮助。 还记得我曾经的毕设,是一款2D解谜游戏,游戏中的小人会根据鼠标位置看...
  • 转载请注明出处:http://blog.csdn.net/u010019717更全的内容请看我的游戏蛮牛地址:http://www.unitymanual.com/forum.php?mod=guide&view=my开发Cocos2d的程序员都知道,Cocostudio其中一个动画编辑组件中的。...
  • Unity3D 物体移动方式总结 2016-09-01 10:29:33
     在unity3d中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position。 2. 通过Transform组件移动物体  Transform 组件用于描述物体在空间中的状态,它包括 位置(position), ...
  • Unity 2D 终结手册 (1) - 2D 项目_Unity3d 开发技术:这篇系列文章主要介绍 Unity 2D 开发技术。包括 2D/3D 项目的区别、如何设置 2D/3D 模式、2D 开发中使用到的组件等。
  •  一般在做鼠标选择时是从摄像机向目标点发送一条射线,然后取得射线与对象相交的点来计算3D目标点。后来在开发中发现了一个问题(射线被别的对象挡住了),就是如果主角的前面有别的游戏对象挡着。此时如果使用射线...
  • 本文主要讨论,在Unity中使用物理引擎Physics2D的三个方面的内容: 如何让Rigidbody2D物理模拟看起来更加的顺滑。 介绍几个造成运动卡顿的原因和解决方法。 针对Rigidbody2D的重心旋转模拟。 让运动更加顺滑最直接的...
  • 欢迎使用Markdown编辑器 public class PlayerAct : MonoBehaviour { private float speed =5; private Transform m_Transform; void Start() { m_Transform = this.transform;... void Up...
1 2 3 4 5 ... 20
收藏数 2,235
精华内容 894