0
\$\begingroup\$

I have grid movement set up for the player within a set space. The player moves one unit at a time and can move several units when holding down a direction key. I would like the player to respawn back to a respawn point marked by an empty object when the player collides with a hazard's collider.

I have the GridMovement script on the player:

using System.Collections;
using UnityEngine;

public class GridMovement : MonoBehaviour {
    [SerializeField] private float forwardBoundary = 13f;
    [SerializeField] private float leftBoundary = -13f;
    [SerializeField] private float backBoundary = -13f;
    [SerializeField] private float rightBoundary = 13f;

    //[SerializeField] private Transform respawnPoint;

    private bool isMoving;

    //time it takes for player to move from originalPosition to targetPosition. In seconds (so 1/5th of a second)
    private float timeToMove = 0.2f;


    void Update() {

        if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) StartMove(Vector3.forward);
        if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) StartMove(Vector3.left);
        if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) StartMove(Vector3.back);
        if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) StartMove(Vector3.right);
    }

    private bool StartMove(Vector3 direction) {
        if (isMoving) return false;
        StartCoroutine(MovePlayer(direction));
        return true;
    }

    private IEnumerator MovePlayer(Vector3 direction) {
        isMoving = true;

        float elapsedTime = 0;

        Vector3 originalPosition = transform.position;
        Vector3 targetPosition = originalPosition + direction;

        if (ValidateGridPosition(targetPosition) == targetPosition) {
            while (elapsedTime < timeToMove) {
                transform.position = Vector3.Lerp(originalPosition, targetPosition, (elapsedTime / timeToMove));
                elapsedTime += Time.deltaTime;
                yield return null;
            }
        }
        transform.position = ValidateGridPosition(targetPosition);

        isMoving = false;
    }

    private Vector3 ValidateGridPosition(Vector3 position) {
        if (position.x > rightBoundary) {
            position.x = rightBoundary;
        } else if (position.x < leftBoundary) {
            position.x = leftBoundary;
        } else if (position.z > forwardBoundary) {
            position.z = forwardBoundary;
        } else if (position.z < backBoundary) {
            position.z = backBoundary;
        }
        return position;
    }
}

The player has an empty script on it called Player because it is more scalable than using Tags in Unity. This is the Respawn script on the hazard:

using UnityEngine;

public class Respawn : MonoBehaviour {
    [SerializeField] private Transform player;
    [SerializeField] private Transform respawnPoint;

    private void OnTriggerEnter(Collider other) {
        Debug.Log("We've collided!");
        Player p = other.GetComponent<Player>();
        if (p != null)
            player.transform.position = respawnPoint.transform.position;
    }
}

"We've collided!" displays in the console so I'm guessing the player isn't being teleported back to the respawn point because of Lerp in the GridMovement script? How do I interrupt that?

\$\endgroup\$
3
  • \$\begingroup\$ Presumably you considered using StopCoroutine or StopAllCoroutines as listed in the documentation? \$\endgroup\$
    – DMGregory
    Commented Feb 7, 2023 at 0:48
  • \$\begingroup\$ I'm not sure where to interrupt the Coroutine and place StopCoroutine. I'm very new at this so at first I thought, maybe I could call the OnTriggerEnter from Respawn in the GridMovement script? I don't think it works that way...would I write something in MovePlayer in the while loop? I need this more broken down. Thank you so much. \$\endgroup\$ Commented Feb 7, 2023 at 12:19
  • \$\begingroup\$ Ask yourself "when do I want the coroutine to stop"? When you respawn the player, right? So try putting it in the code block that sends the player back to the checkpoint. \$\endgroup\$
    – DMGregory
    Commented Feb 7, 2023 at 12:20

3 Answers 3

0
\$\begingroup\$

Before really setting the position, do something like disabling the script. You can easily do this by doing GetComponent<GridMovement>().enabled = false and when you're basically done setting the new position (respawning), do the opposite GetComponent<GridMovement>().enabled = true. Hope this helps :)

\$\endgroup\$
2
  • \$\begingroup\$ So I tried what you suggested and it didn't do anything. The player still stands in the same spot of the hazard and doesn't teleport. "We've collided!" still displays. \$\endgroup\$ Commented Feb 7, 2023 at 12:15
  • \$\begingroup\$ This makes sense but still something is missing. Maybe I need to change something in GridMovement? \$\endgroup\$ Commented Feb 7, 2023 at 12:21
0
\$\begingroup\$

Just get a reference to the GridMovement component and tell it to stop running its coroutine.

In GridMovement:

public void Stop() {
    StopAllCoroutines ();
    isMoving = false;
}

public void TeleportTo(Vector3 position) {
    Stop();
    transform.position = position;
}

Then Respawn can look like this:

public class Respawn : MonoBehaviour {
    [SerializeField] private Transform respawnPoint;

    private void OnTriggerEnter(Collider other) {
        if (other.gameObject.TryGetComponent(out GridMovement player)) {
        
            player.TeleportTo(respawnPoint.transform.position);
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for this. I actually figured out the following (I submitted it as an answer). Thank you for your help! \$\endgroup\$ Commented Feb 8, 2023 at 14:56
0
\$\begingroup\$

I figured out if I wanted the player to move, collide with a hazard, stop moving, respawn and then be able to move again, I needed to write the following in the GridMovement class:

public Coroutine movePlayerCoroutine;

In the StartMove method I set:

movePlayerCoroutine = StartCoroutine(MovePlayer(direction));

Then I created these 2 methods:

public void stopMovePlayer() {
     StopCoroutine(movePlayerCoroutine);
}

public void startMovePlayer() {
     StartCoroutine(MovePlayer(this.transform.position));
}

stopMovePlayer() and startMovePlayer() creates public methods I can call in Respawn to stop and then start the coroutine once the player has respawned.

This is the updated Respawn class:

using UnityEngine;

public class Respawn : MonoBehaviour {
    [SerializeField] private Transform player;
    [SerializeField] private Transform respawnPoint;

    private void OnTriggerEnter(Collider other) {
        Debug.Log("We've collided!");
        Player p = other.GetComponent<Player>();
        if (p != null)
            FindObjectOfType<GridMovement>().stopMovePlayer();
            player.transform.position = respawnPoint.transform.position;
            FindObjectOfType<GridMovement>().startMovePlayer();
    }
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ If you already have a startMovePlayer method, it it better to have your Start() use that one as well. If you ever need to change how your start move works, you dont need to hunt down other places that affect your movement \$\endgroup\$
    – Zibelas
    Commented Feb 8, 2023 at 15:11
  • \$\begingroup\$ Remember to click the checkmark in the top left to mark your answer as Accepted if it solved your problem. \$\endgroup\$
    – DMGregory
    Commented Feb 8, 2023 at 17:22

You must log in to answer this question.

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