0
\$\begingroup\$

I want to do a game where the map is cell based, like Pokemon, where when you press Left, you move from 210x100 to 209x100.

Like this:

    void Update()
    {
        var x = Input.GetAxis("Horizontal");
        var z = Input.GetAxis("Vertical");

        if (x == 0 && z == 0)
        {
            return;
        }

        if (x > 0)
        {
            x = 1;
        } else if (x < 0)
        {
            x = -1;
        }

        if (z > 0)
        {
            z = 1;
        } else if (z < 0)
        {
            z = -1;
        }

        transform.Translate(
            x,
            0f,
            z,
            Space.World
        );
    }

But with this approach, when you quick press and release the left, the Update is executed many times and you go from 210 to 203 easily.

How can I move from 210 to 209 in 0,5 seconds and don't allow other movement until the current movement has finished?

\$\endgroup\$
1
  • \$\begingroup\$ FWIW: the reason the player moves from 210 to 203 so quickly and easily is that you've told the player sprite to move a whole unit each from (and a frame is between 1/30th and 1/60th of a second; depends on how fast your game is running), so even at the fastest tap imaginable (a tenth of a second), that arrow key is going to be held down for anywhere from 3 to 6 frames. \$\endgroup\$ Commented Oct 21, 2017 at 16:13

1 Answer 1

2
\$\begingroup\$

I would suggest you to actually create a grid of tiles. Each tile represents some position. You then move from current tile to the tile you have picked next. For example, Grid[currentX + movementX, currentZ + movementZ].

You can use coroutines to move between tiles. Or you can simply check if the tile currently used by your player hasn't changed yet. For example, in Update -

if (player.CurrentTile != player.CurrentDestinationTile) { Player moves to that tile } 
else if (movementVector != Vector3.Zero) { player.CurrentDestinationTile = Grid[currentX + movementVector.x, currentZ + movementVector.z] }

NOTE: that movementVector can't be set to have movement on X and Z at the same time if you don't want a player to move diagonally.

If you want to use coroutines. You can start by creating a boolean that indicates if a player is already moving to some tile.

private bool IsPlayerMoving = false;
private void Update
{
    // Calculate movementVector here
    if (!this.IsPlayerMoving && movementVector != Vector3.Zero) 
    { 
        this.StartCoroutine(this.MovePlayer(player.position + movementVector)); 
    }
}

[SerializeField] private float PlayerMovementSpeed = 3f;
private IEnumerator MovePlayer(Vector3 newPosition)
{
    this.IsPlayerMoving = true;

    Vector3 playerStartingPosition = player.position;
    float progress = Time.deltaTime * this.PlayerMovementSpeed;

    while (progress <= 1)
    {
        player.position = Vector3.Lerp(playerStartingPosition, newPosition, progress);

        progress += Time.deltaTime * this.PlayerMovementSpeed;
        yield return null;
    }

    player.position = newPosition;

    this.IsPlayerMoving = false;
}

Best way to do that in a more advanced way is if you have player FSM - Finite State Machine. When you are in movement state - you don't set the input for his movement. If he is in an IDLE state, you set him to movement state and set the destination.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .