Search Unity

2D Pokemon-style Movement with working collision

Discussion in '2D' started by Movinghead333, Oct 3, 2019.

  1. Movinghead333

    Movinghead333

    Joined:
    Apr 24, 2013
    Posts:
    2
    Hey everyone,

    I recently picked up Unity again with the idea of making a game with 2D Tilebased movement similar to what the old Pokemon games offered.

    So my basic constraints were that the player can only move from tile to tile in horizontal OR vertical direction allowing no diagonal movement. The second constraint is that similar to Pokemon when the player only shortly presses a move key they can change the direction there character is facing without actually moving the player. Incase they are already facing the direction they are looking then the player would just move normally.

    My approach is based on this tutorial which has great tile-movement but sadly no tile collision:


    The current version allows the player to collide with tiles one a given tilemap which the PlayerMovement scripts receives as a reference.

    So here is quick demonstration video as well as the source code for the PlayerMovement c# script:


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. // require SpriteRenderer for changing the player's sprite
    5. [RequireComponent(typeof(SpriteRenderer))]
    6. public class PlayerMovement : MonoBehaviour
    7. {
    8.     // walkspeed in tiles per second
    9.     public float walkSpeed = 3f;
    10.  
    11.     // the tilemap which has the tiles we want to collide with
    12.     public Tilemap tilemap;
    13.  
    14.     // the amount of time we can press an input without moving in seconds
    15.     public float moveDelay = 0.2f;
    16.  
    17.     // our player's direction
    18.     Direction currentDir = Direction.South;
    19.  
    20.     // a vector storing the input of our input-axis
    21.     Vector2 input;
    22.  
    23.     // states if the player is moving or waiting for movement input
    24.     bool isMoving = false;
    25.  
    26.     // position before a move is executed
    27.     Vector3 startPos;
    28.  
    29.     // target-position after the move is executed
    30.     Vector3 endPos;
    31.  
    32.     // stores the progress of the current move in a range from 0f to 1f
    33.     float progress;
    34.  
    35.     // stores the time remaining before the player can move again
    36.     float remainingMoveDelay = 0f;
    37.  
    38.     // since we currently do not use any animation components we just use four
    39.     // different sprites for our four directions
    40.     public Sprite northSprite;
    41.     public Sprite eastSprite;
    42.     public Sprite southSprite;
    43.     public Sprite westSprite;
    44.  
    45.     public void Update()
    46.     {
    47.         // check if the player is moving
    48.         if (!isMoving)
    49.         {
    50.             // The player is currently not moving so check if there is keyinput
    51.             input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"));
    52.  
    53.             // if there is input in x direction disable input in y direction to
    54.             // disable diagonal movement
    55.             if (input.x != 0f)
    56.                 input.y = 0;
    57.  
    58.             // check if there is infact movement or if the input axis are in idle
    59.             // position
    60.             if (input != Vector2.zero)
    61.             {
    62.                 // save the old direction for later use
    63.                 Direction oldDirection = currentDir;
    64.  
    65.                 // update the players direction according to the input
    66.                 #region update Direction
    67.                 if (input.x == -1f)
    68.                     currentDir = Direction.West;
    69.                 if (input.x == 1f)
    70.                     currentDir = Direction.East;
    71.                 if (input.y == 1f)
    72.                     currentDir = Direction.North;
    73.                 if (input.y == -1f)
    74.                     currentDir = Direction.South;
    75.                 #endregion
    76.  
    77.                 // since there is currently no further animation components we
    78.                 // just set the sprite according to the direction
    79.                 switch (currentDir)
    80.                 {
    81.                     case Direction.North:
    82.                         gameObject.GetComponent<SpriteRenderer>().sprite = northSprite;
    83.                         break;
    84.                     case Direction.East:
    85.                         gameObject.GetComponent<SpriteRenderer>().sprite = eastSprite;
    86.                         break;
    87.                     case Direction.South:
    88.                         gameObject.GetComponent<SpriteRenderer>().sprite = southSprite;
    89.                         break;
    90.                     case Direction.West:
    91.                         gameObject.GetComponent<SpriteRenderer>().sprite = westSprite;
    92.                         break;
    93.                 }
    94.  
    95.                 // if the currentDirection is different from the old direction
    96.                 // we want to add a delay so the player can just change direction
    97.                 // without having to move
    98.                 if (currentDir != oldDirection)
    99.                 {
    100.                     remainingMoveDelay = moveDelay;
    101.                 }
    102.  
    103.                 // if the direction of the input does not change then the move-
    104.                 // delay ticks down
    105.                 if (remainingMoveDelay > 0f)
    106.                 {
    107.                     remainingMoveDelay -= Time.deltaTime;
    108.                     return;
    109.                 }
    110.  
    111.                 // for the collision detection and movement we need the current
    112.                 // position as well as the target position where our player
    113.                 // is going to move to
    114.                 startPos = transform.position;
    115.                 endPos = new Vector3(startPos.x + input.x, startPos.y + input.y, startPos.z);
    116.  
    117.                 // we subtract 0.5 both in x and y direction to get the coordinates
    118.                 // of the upper left corner of our player sprite and convert
    119.                 // the floating point vector into an int vector for tile search
    120.                 Vector3Int tilePosition = new Vector3Int((int)(endPos.x - 0.5f),
    121.                                                          (int)(endPos.y - 0.5f), 0);
    122.  
    123.                 // with our freshly calculated tile position of the tile where our
    124.                 // player want to move to we can now check if there is in fact
    125.                 // a tile at that position which we would collide with
    126.                 // if there is no tile so the GetTile-function return null then
    127.                 // we can go ahead and move towards our target
    128.                 if (tilemap.GetTile(tilePosition) == null)
    129.                 {
    130.                     // we set our moving variable to true and our progress
    131.                     // towards the target position to 0
    132.                     isMoving = true;
    133.                     progress = 0f;
    134.                 }
    135.             }
    136.         }
    137.  
    138.         // check if the player is currently in the moving state
    139.         if (isMoving)
    140.         {
    141.             // check if the progress is still below 1f so the movement is still
    142.             // going on
    143.             if (progress < 1f)
    144.             {
    145.                 // increase our movement progress by our deltaTime times our
    146.                 // above specified walkspeed
    147.                 progress += Time.deltaTime * walkSpeed;
    148.  
    149.                 // linearly interpolate between our start- and end-positions
    150.                 // with the value of our progress which is in range of [0, 1]
    151.                 transform.position = Vector3.Lerp(startPos, endPos, progress);
    152.             }
    153.             else
    154.             {
    155.                 // if we are moving and our progress is above one that means we
    156.                 // either landed exactly on our desired position or we overshot
    157.                 // by some tiny amount so in ordered to not accumulate errors
    158.                 // we clamp our final position to our desired end-position
    159.                 isMoving = false;
    160.                 transform.position = endPos;
    161.             }
    162.         }
    163.     }
    164. }
    165.  
    166. // small Enumeration to help us keep track of the player's direction more easyly
    167. enum Direction
    168. {
    169.     North, East, South, West
    170. }
    Since this is my very first appoach at this issue, if you have any suggestion regarding the code I am always open to feedback. Otherwise feel free to use this script in a Pokemon-style 2D game. :)
     

    Attached Files:

  2. Fraserspree

    Fraserspree

    Joined:
    Nov 10, 2019
    Posts:
    1
    where is the animation controller?
     
  3. Movinghead333

    Movinghead333

    Joined:
    Apr 24, 2013
    Posts:
    2
    Well as I mentioned in the comments in the code there is currently no animation controller being used, since I am still relatively new to unity and do not quite know how to use them yet. If you can suggest a good tutorial for animation controllers feel free to comment it below.
     
  4. Kevin_hdez

    Kevin_hdez

    Joined:
    Mar 15, 2014
    Posts:
    1
    Hi, dude, I think i'm kinda late haha.

    You can use this code as is follow, it uses raycast to determine where the Player is Colliding.

    Here's the code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Tilemaps;
    5.  
    6. public enum Direction
    7. {
    8.     up, down, left, right
    9. }
    10.  
    11. public class PlayerController : MonoBehaviour
    12. {
    13.  
    14.     public float Speed = 0f;
    15.     public LayerMask TileCollision;
    16.  
    17.     Animator Anim;
    18.     Vector3 TargetPosition;
    19.     Direction Direction;
    20.  
    21.     bool GetCollision
    22.     {
    23.         get
    24.         {
    25.             RaycastHit2D rh;
    26.  
    27.             Vector2 dir = Vector2.zero;
    28.  
    29.             if (Direction == Direction.down)
    30.                 dir = Vector2.down;
    31.  
    32.             if (Direction == Direction.left)
    33.                 dir = Vector2.left;
    34.  
    35.             if (Direction == Direction.right)
    36.                 dir = Vector2.right;
    37.  
    38.             if (Direction == Direction.up)
    39.                 dir = Vector2.up;
    40.  
    41.             rh = Physics2D.Raycast(transform.position, dir, 1, TileCollision);
    42.  
    43.             return rh.collider != null;
    44.         }
    45.     }
    46.  
    47.  
    48.     private void Start()
    49.     {
    50.         Anim = GetComponent<Animator>();
    51.         TargetPosition = new Vector2(transform.position.x, transform.position.y);
    52.         Direction = Direction.down;
    53.  
    54.     }
    55.  
    56.     private void Update()
    57.     {
    58.  
    59.         Vector2 AxisInput = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    60.         Anim.SetInteger("Direccion", (int)Direction); //you can use this to manage the Animation on the Animator Window.
    61.  
    62.         if(AxisInput != Vector2.zero && TargetPosition == transform.position)
    63.         {
    64.             if(Mathf.Abs(AxisInput.x) > Mathf.Abs(AxisInput.y))
    65.             {
    66.  
    67.                 if(AxisInput.x > 0)
    68.                 {
    69.                     Direction = Direction.right;
    70.  
    71.                     if(!GetCollision)
    72.                         TargetPosition += Vector3.right;
    73.                 }
    74.                 else
    75.                 {
    76.                     Direction = Direction.left;
    77.  
    78.                     if (!GetCollision)
    79.                         TargetPosition += Vector3.left;
    80.                 }
    81.  
    82.              
    83.  
    84.             }
    85.             else
    86.             {
    87.                 if (AxisInput.y > 0)
    88.                 {
    89.                     Direction = Direction.up;
    90.  
    91.                     if (!GetCollision)
    92.                         TargetPosition += Vector3.up;
    93.                 }
    94.                 else
    95.                 {
    96.                     Direction = Direction.down;
    97.  
    98.                     if (!GetCollision)
    99.                         TargetPosition += Vector3.down;
    100.                 }
    101.             }
    102.         }
    103.         transform.position = Vector3.MoveTowards(transform.position, TargetPosition, Speed * Time.deltaTime);
    104.     }
    105. }
    106.  
    107.  
    I hope this code can help you.
     
  5. Plazmin

    Plazmin

    Joined:
    Jun 19, 2020
    Posts:
    1
    But there is still no tutorial for the Animator