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\$ Oct 21 '17 at 16:13
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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.