基于unity物体定点移动与模拟刹车的细节 GIF 图文详解——线性差值函数以及平滑阻尼的运用和实践(Lerp AND SmoothDamp)

Posted 秩沅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于unity物体定点移动与模拟刹车的细节 GIF 图文详解——线性差值函数以及平滑阻尼的运用和实践(Lerp AND SmoothDamp)相关的知识,希望对你有一定的参考价值。



👨‍💻个人主页@元宇宙-秩沅

hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅!

本文由 秩沅 原创

收录于专栏 [unity常用API]


⭐相关文章:基础不牢,地动山摇系列 ------ 软硬通吃 unity常用API

⭐相关文章:关于游戏剧情模式中用到的基础简单API

⭐相关文章:控制游戏人物移动的细节到底有多少?

⭐相关文章:坦克炮管旋转发射炮弹(向量基础,射线碰撞,物体实例化)


⭐基于unity实现物体移动与模拟刹车的细节处理⭐

文章目录


所有线性有关的差值函数都运用到物体移动的情况,以下四种是常见且重要的API,他们的区别和用法详解如下。这里主要以二维向量为例子,三维以此类推。

如图: a点物体到b点物体用差值函数进行移动


👨‍💻Lerp(a,b,t) 限制性差值


文档:Lerp(a,b,t) 限制性差值

gameObject.transform.position = Vector2.Lerp(a.position,b.position ,speed);
让物体A到B进行差值移动

  • 场景应用实现

可以看出使用lerp()用到物体运动身上并不是匀速的,因为此时每一次差值完之后,都是用差值完后的两点距离再进行差值、


  • 向量含义图:

图中的黑点就是又无数个任意取差值的点 ,范围为(0,1)


  • 应用示意图

按照差值进行移动,但只只进行一次,所以将其放在update函数里面,实现作用于a向b点移动

如果此时要让其变成匀速只需要让其加上前一次差值过的数值,即可实现匀速移动

  speed += persent * Time.deltaTime;
  gameObject.transform.position =  Vector2.Lerp(a.position,b.position ,speed);
  • 修改代码后的效果图:


👨‍💻MoveTowards(a,b,t) 匀速步频


文档:MoveTowards(a,b,t)

  • 相比上面MoveToward 也是匀速运动用到较多的函数,并且相较于上面大家可以直接使用movetowards 进行物体的匀速运动
  • 此时的t 作为步频,也可以理解为移动的频率

  • 效果图:

在这里当然我将速度从0.01改成了0.05,但是匀速效果还是显而易见的,只是由于电脑原因导致了一下卡帧


👨‍💻LerpUnclamped(a,b,t)非限制差值


文档:LerpUnclamped(a,b,t)

  • 此时就是非限制性差值了,也就是说我们的t可以随便取值,不再是0到1范围内取值,这就意味着它的差值会超过两点本身的距离

  • 向量图:

  • 效果图

gameObject.transform.position = Vector2.LerpUnclamped (a.position,b.position ,speed);

此时我将 t 变为1.3 ,这时可以看到物体A 超出了B,原因是差值超过两点本身的距离,之后又和B重合


👨‍💻smoothDamp(a,b,v,t)平滑阻尼


文档:smoothDamp(a,b,v,t)

  • 平滑阻尼,应用于汽车刹车,想象一下汽车刹车,特别是速度快的是不是还要向前摩擦行驶才停下来,效果就是如此
  • v 是速度 ,t 是平滑时间

Vector2 vority = new Vector2(0.1f, 0);
gameObject.transform.position = Vector2.SmoothDamp(a.position,b.position, ref vority ,speed); 、


  • 我将阻尼时间 t = 设为0.1 的效果图

可以看到阻尼效果


  • 我将阻尼时间 t = 设为0.5 的效果图

可以看到阻尼时间越长越慢


  • 我将阻尼时间 t = 设为0 的效果图

可以看到丝毫没有阻尼效果


👨‍💻结论


  • 👍Lerp () 先慢后快
  • 👍moveTowards ()匀速
  • 👍smoothDamp() 模拟制动系统


⭐相关文章:基础不牢,地动山摇系列 ------ 软硬通吃 unity常用API

⭐相关文章:关于游戏剧情模式中用到的基础简单API

⭐相关文章:控制游戏人物移动的细节到底有多少?

⭐相关文章:坦克炮管旋转发射炮弹(向量基础,射线碰撞,物体实例化)


你们的点赞👍 收藏⭐ 留言📝 关注✅是我持续创作,输出优质内容的最大动力!

Unity中基于网格/平铺移动+碰撞?

【中文标题】Unity中基于网格/平铺移动+碰撞?【英文标题】:Grid-based/Tile Movement + Collisions in Unity? 【发布时间】:2018-10-09 06:40:06 【问题描述】:

在弄清楚如何进行基于网格的移动或基于图块的移动时遇到了很大的问题。有点类似于 Nitrome 的 Redungeon:不能真正发布图像:所以这是一个 gif。 http://www.eneminds.com/redungeon/img/gifs/gif_1.gif

我想我应该创建一个网格系统。我认为这很容易,就像旧游戏(口袋妖怪等)一样。虽然不知道该怎么做。但我也希望运动快。

【问题讨论】:

你尝试过做什么?您面临的具体问题是什么?您的研究向您展示了什么? 【参考方案1】:

Unity 有自己的 tilemaps 实现,它应该允许您构建背景,也就是您行走的地面: https://docs.unity3d.com/Manual/Tilemap.html

此外,Unity 提供了更多 2d 内容来帮助您在这里构建东西: https://github.com/Unity-Technologies/2d-extras

在此处找到的众多教程中概述了碰撞: https://unity3d.com/learn/tutorials/s/2d-game-creation

要制作一款像 GIF 动画那样的游戏,不容易。如果您是游戏开发新手,情况尤其如此,所以不要指望简单的解决方案。

您可能想查看 Unity 资源商店以快速获取基于图块的移动方式,但这总是要付出代价,并且不会帮助您学习。 https://assetstore.unity.com/

关于移动速度,这始终只是游戏中的一个变量,可以根据您的喜好进行调整。在基于图块的游戏的情况下,它将对应于角色在视觉上从一个图块转移到另一个图块所花费的时间。尝试阅读作为 Unity 引擎一部分的 Lerping。 https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

【讨论】:

谢谢。另外...我的意思是移动速度是...因为我已经尝试过使用 (pos += Vector3.DIRECTION) 进行 MoveTowards。但仅在 (pos == transform.position) 时才存储在 if 语句中)。 where pos = transform.position..这意味着你只有在你的目标位置时才能移动..它确实使移动不平滑并且没有碰撞。【参考方案2】:

我能够成功。 我刚刚修改了 Unity 学习部分的 RogueLike 教程中的输入。

所以这是负责移动的代码

    void Move(string direction_string)
    
        ChangeDirection(direction_string);

        Vector2 start = transform.position;
        Vector2 end = start + direction;

        boxCollider.enabled = false;
        hit = Physics2D.Linecast(start, end, blockingLayer);
        boxCollider.enabled = true;

        if (hit.transform == null) 
            StartCoroutine(Movement(end));
            animator.SetTrigger(direction_string);
            return;
         else 
            moveSequence.RemoveAt(0);
            animator.SetTrigger(direction_string);
            return;
        
    

    void ChangeDirection(string direction_string)
    
        switch (direction_string) 
            case "up":
                direction = dir_Up;
                break;
            case "down":
                direction = dir_Down;
                break;
            case "left":
                direction = dir_Left;
                break;
            case "right":
                direction = dir_Right;
                break;
        
    

    IEnumerator Movement(Vector3 end)
    
        moving = true;
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemainingDistance > float.Epsilon) 
            Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, moveSpeed * Time.deltaTime);
            rb2D.MovePosition(newPosition);
            sqrRemainingDistance = (transform.position - end).sqrMagnitude;
            yield return null;
        

        currentPos = end;
        moveSequence.RemoveAt(0);
        moving = false;
    

这是负责输入的内容


    void FixedUpdate ()
    
        if (moveSequence.Count > 0 && !moving) 
            Move(moveSequence[0]);
        
    

然后只需将它连接到一个 Update() 函数,该函数会监听按钮按下并将一个列表项添加到 moveSequence List 中

moveSequence.Add("up");

【讨论】:

【参考方案3】:

刚刚在 reddit 上为某人做了一个这样的代码,来这里看看它之前是否被问过。我知道我迟到了 2 年,但如果有人需要,它就在这里。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
     
    public class GameController : MonoBehaviour
    
     
        // a script created so that a player always moves one direction at a time on a grid.
        // It uses a plane with even width and height to make a grid
        //     - if needed, turn off the mesh renderer so that you dont see the tiled grid.
     
     
     
     
        // the game object we use to create a grid. Should be a plane with an even width and height.
        public  GameObject tile_object;
     
        // grid x and y sizes, set them to odd numbers
        public float x_grid_size, y_grid_size;
     
        // List of all our floor tiles
        Dictionary<(float x, float y), GameObject> floor_list = new Dictionary<(float x, float y), GameObject>();
     
        // Reference to the player prefab
        public GameObject player;
     
        // reference to player character
        private GameObject player_character;
     
        // a tool used for debugging
        bool debugging = true;
     
        //check if player is moving
        bool player_is_moving = false;
     
        (float x, float y) players_current_tile;
     
        // players speed
        public float player_speed = 35f;
     
        // used in calculating movements
        (float x, float y) to_tile;
     
        //used to calculate movement
        Vector3 to_pos;
     
     
        // Start is called before the first frame update
        void Start()
        
            // put the player into the scene
           player_character = Instantiate(player, Vector3.zero, player.transform.rotation);
     
            // Sets the Middle Tile and checks grid is odd
            SetMiddleTile();
     
            // create a floor grid to help us visualize our play field.
            CreateGrid();
     
        
     
        // Update is called once per frame
        void Update()
        
            PlayerMovement();
            MovePlayer();
        
     
        // gets inputs on A S W D keys for movement
        void PlayerMovement() 
            if (Input.GetKeyDown(KeyCode.W))
                PlayerInput((0, 1));
            
            if (Input.GetKeyDown(KeyCode.A))
            
                PlayerInput((-1, 0));
            
            if (Input.GetKeyDown(KeyCode.S))
            
                PlayerInput((0, -1));
            
            if (Input.GetKeyDown(KeyCode.D))
            
                PlayerInput((1, 0));
            
        
     
     
        // simplifies player entries, and gets input directional data
        void PlayerInput((float x, float y) input)
        
            if (!player_is_moving)
            
                (float x, float y) to_tile_check = (players_current_tile.x + input.x, players_current_tile.y + input.y);
                if (floor_list[to_tile_check] != null)
                
                    player_is_moving = true;
     
                    to_tile = (players_current_tile.x + input.x, players_current_tile.y + input.y);
                    to_pos = floor_list[to_tile].transform.position;
                
            
     
        
     
       // used to move the player
        void MovePlayer()
        
     
            if (player_is_moving)
            
                player_character.transform.position = Vector3.MoveTowards(player_character.transform.position, to_pos, Time.deltaTime * player_speed);
     
                if (player_character.transform.position == to_pos)
                
                    players_current_tile = to_tile;
                    player_is_moving = false;
                
            
        
     
        // used to create the floor grid
        void CreateGrid()
        
            // Create an empty to hold our floor grid
            GameObject grid_empty = new GameObject();
            grid_empty.name = "grid_floor";
     
            // get the size of our floor tile so we know how far to space them.
            float tile_size = tile_object.GetComponent<MeshRenderer>().bounds.size.x;
     
            // create an offset so the grid is always centered in game view
            Vector3 offSet = new Vector3((x_grid_size * tile_size) / 2 - (tile_size / 2), 0, (y_grid_size * tile_size)/2 - (tile_size / 2));
     
            // iterate to create tiles on x axis
            for (int x = 0; x < x_grid_size; x++)
            
                // iterate to create tiles on y axis
                for (int y = 0; y < y_grid_size; y++)
                
                    // instantiate new tile
                    GameObject floor_tile = Instantiate(tile_object, (new Vector3(x * tile_size, 0, y * tile_size) - offSet), tile_object.transform.rotation);
                    floor_tile.name = "flr_x: " + (x  + 1) + " y:" + (y + 1);
     
                    // set the parent to grid empty so the scene isnt filled with objects and all floor tiles are neatly centerd
                    floor_tile.transform.parent = grid_empty.transform;
     
                    // add the tile to our dictionary.
                    floor_list.Add(((x + 1),(y + 1)), floor_tile);
                
            
            if (debugging)  print("Created Floors: " + (x_grid_size * y_grid_size) + " Floor List Size: " + floor_list.Count);
        
     
        // sets the middle tile the player is spawned on, also checks to make sure grid size is set correctly
       void SetMiddleTile()
        
            // these check to make sure grid size isnt 0 and are odd
            if (x_grid_size == 0 && y_grid_size == 0)  print("Forgot to set grid size! setting default to 7x and 7y"); x_grid_size = 7; y_grid_size = 7; 
            if (x_grid_size % 2 == 0)  print("x_grid_size is set to an even number(" + x_grid_size + "), changing it to odd(" + (x_grid_size + 1) + ")"); x_grid_size += 1; 
            if (y_grid_size % 2 == 0)  print("y_grid_size is set to an even number(" + y_grid_size + "), changing it to odd(" + (y_grid_size + 1) + ")"); y_grid_size += 1; 
     
            // splits the grid and half
            float x = x_grid_size / 2 + 0.5f;
            float y = y_grid_size / 2 + 0.5f;
     
            // set the players current tile to middle tile
            players_current_tile = (x, y);
     
            // set the to tile to current tile to avoid null instances when first checking our dictionary
            to_tile = players_current_tile;
     
            // used for debugging
            if (debugging)  print("the middle tile is: x(" + x + ")" + " y(" + y + ")"); 
        
    
         

【讨论】:

以上是关于基于unity物体定点移动与模拟刹车的细节 GIF 图文详解——线性差值函数以及平滑阻尼的运用和实践(Lerp AND SmoothDamp)的主要内容,如果未能解决你的问题,请参考以下文章

unity怎么计算物体移动速度

unity放大缩小很慢

unity里怎么让一个物体在在5秒内从某点移动到另外一点?

unity物理引擎浅谈

unity细节基于unity子对象(如相机)为什么无法进行z轴的拖拽移动和z轴自动归位的问题

Unity基于响应式编程(Reactive programming)入门