简体   繁体   English

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

[英]Grid-based/Tile Movement + Collisions in Unity?

Having a huge problem figuring out how to do a grid-based movement or tile based movement.在弄清楚如何进行基于网格的运动或基于图块的运动时遇到了一个巨大的问题。 Somewhat similar to Nitrome's Redungeon: Can't really post an image: so here's a gif.有点类似于 Nitrome 的 Redungeon:无法真正发布图片:所以这是一个 gif。 http://www.eneminds.com/redungeon/img/gifs/gif_1.gif http://www.eneminds.com/redungeon/img/gifs/gif_1.gif

I figured I should create a grid system.我想我应该创建一个网格系统。 I thought this was easy just like the old games(Pokemon etc.) Not sure how to do that though.我认为这很简单,就像旧游戏(口袋妖怪等)一样。但不知道该怎么做。 But I also want the movement to be fast.但我也希望动作快。

Unity has its own implementation of tilemaps which should allow you to build the background aka the ground you walk on: https://docs.unity3d.com/Manual/Tilemap.html Unity拥有自己的tilemap实现,应该可以让您以背景为基础构建背景: https ://docs.unity3d.com/Manual/Tilemap.html

Furthermore, Unity provides more 2d content to help you build things right here: https://github.com/Unity-Technologies/2d-extras 此外,Unity提供了更多2d内容来帮助您在此处构建内容: https : //github.com/Unity-Technologies/2d-extras

Collisions are outlined in numerous tutorials found on here: https://unity3d.com/learn/tutorials/s/2d-game-creation 在此处找到的许多教程中概述了冲突: https : //unity3d.com/learn/tutorials/s/2d-game-creation

To make a game like the one in the animated GIF, is not easy . 要制作像GIF动画那样的游戏并不容易 This is especially the case if you're new to game dev, so don't expect easy solutions. 如果您是游戏开发者的新手,那么尤其是这样,不要指望简单的解决方案。

You might want to check out the Unity Asset Store for a quick 'n dirty way of getting tile-based movement, but this as always will come with a price and will not help you learn. 您可能想查看Unity资产商店,以快速获得基于图块的移动的肮脏方式,但这将一如既往地附带价格,并且无济于事。 https://assetstore.unity.com/ https://assetstore.unity.com/

With regards to movement speed, this is always just a variable in your game which can be tuned to your preference. 关于移动速度,这始终只是游戏中的变量,可以根据自己的喜好进行调整。 In the case of a tile based game it would correspond to the time it takes for the character to be visually shifted from one tile to another. 在基于图块的游戏中,它对应于角色在视觉上从一个图块转移到另一个图块所花费的时间。 Try reading up on Lerping which is a part of the Unity engine. 尝试阅读Lerping,它是Unity引擎的一部分。 https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

I was able to pull it off. 我能够完成。 I just modified the input a little from the RogueLike Tutorial from Unity's Learn Section. 我刚刚从Unity学习部分的RogueLike教程中修改了输入内容。

So This is the code responsible for movement 所以这是负责运动的代码

    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;
    }
}

Here is what's responsible for input 这是负责输入的内容


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

Then just hook it up to an Update() function that listens to button press and add a list item to moveSequence List like 然后将其连接到一个Update()函数,该函数侦听按钮的按下并将一个列表项添加到moveSequence List中,例如

moveSequence.Add("up");

Just did a code like this for someone on reddit, came here to see if its been asked before.刚刚为 reddit 上的某个人做了一个这样的代码,来这里看看之前是否有人问过它。 I know I'm 2 years late but here it goes in case anyone needs it.我知道我迟到了 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 + ")"); }
        }
    }
         

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM