Home > Software design >  teleport not working c# unity code (complete noob)
teleport not working c# unity code (complete noob)

Time:10-19

I am trying to get my gameobject character to move from one location to another in a video game after picking up 4 objects. It does teleport but once it does it'll keep teleporting to the specified location. I don't know how to stop it so it lets me move freely to the new location. The thing that makes it go to the new location is the if (scoreValue >= 4) {winTextObject.SetActive(true);}.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerScript: MonoBehaviour {
  private Rigidbody2D rd2d;
  public float speed;
  public Text scoreText;
  private int scoreValue;
  public GameObject winTextObject;
  public GameObject loseTextObject;
  private int lives;
  public Text livesText;

  // Start is called before the first frame update
  void Start() {
    rd2d = GetComponent < Rigidbody2D > ();
    scoreValue = 0;
    scoreText.text = "Score: "   scoreValue.ToString();
    winTextObject.SetActive(false);
    loseTextObject.SetActive(false);
    lives = 3;
  }

  // Update is called once per frame
  void FixedUpdate() {
    float hozMovement = Input.GetAxis("Horizontal");
    float verMovement = Input.GetAxis("Vertical");
    rd2d.AddForce(new Vector2(hozMovement * speed, verMovement * speed));
  }

  private void OnCollisionEnter2D(Collision2D collision) {
    if (collision.collider.tag == "Coin") {
      scoreValue  = 1;
      scoreText.text = "Score: "   scoreValue.ToString();
      Destroy(collision.collider.gameObject);
    } else if (collision.collider.tag == "Enemy") {
      lives -= 1;
      livesText.text = lives.ToString();
      Destroy(collision.collider.gameObject);
    }

  }

  void Update() {

    if (scoreValue >= 4) {
      winTextObject.SetActive(true);
    } {
      livesText.text = "Lives: "   lives.ToString();
    }
    if (lives <= 0) {
      loseTextObject.SetActive(true);
    }

  }

  private void OnCollisionStay2D(Collision2D collision) {
    if (collision.collider.tag == "Ground") {
      if (Input.GetKey(KeyCode.W)) {
        rd2d.AddForce(new Vector2(0, 3), ForceMode2D.Impulse);
      }
    }
    if (Input.GetKey("escape")) {
      Application.Quit();
    }
    if (scoreValue == 4) {
      transform.position = new Vector2(64.0 f, -1.0 f);
    }
  }
}

CodePudding user response:

You can add an additional boolean variable so that it will enter that if only once.

  1. At the beginning you declare private bool isGameOver;
  2. Inside Start() you make sure isGameOver = false
  3. Change your final condition with:
  if (scoreValue == 4 && !isGameOver)
  { 
    isGameOver = true;
    transform.position = new Vector2(64.0f, -1.0f);
  }

With this addition you will fire your "teleport" just once.

CodePudding user response:

I think you should put a flag where you want the transportation event occurs.

bool isTransported = false;

then compare in Update()

if (scoreValue >= 4) isTransported == true;

if (isTransported) transform.position = new Vector2(64.0 f, -1.0 f);

then reset the boolean if you want to do another transportation by doing:

isTranported == false;

And I also suggest that you if you want to do some more transportation events in your game. You should explicitly compare your score with a value or a range of values. If not, your event will not work correctly. For example, when score >=4 you want your character teleports to location A, when score >=10 your character moves to location B then the conditions overlap here. The game will execute both events because >=10 is also >=4 as well.

CodePudding user response:

In general instead of poll-checking these values every frame make you code event driven!

There is no need for an additional flag at all.

As OnCollisionEnter2D seems actually to be the only place where the two values are changed rather do e.g.

// NOT NEEDED AT ALL
// private void Update() { }

private void OnCollisionEnter2D(Collision2D collision) 
{
    // In general rather use "CompareTag" instead of string "=="
    // While "==" silently fails in case of typos or non-existent tags
    // "CompareTag" will throw an error making your debugging easier and is also slightly faster since it uses Hashes
    if (collision.gameObject.CompareTag("Coin")) 
    {
        Destroy(collision.gameObject);

        scoreValue  ;
        scoreText.text = $"Score: {scoreValue}";
        
        if(scoreValue == 4)
        {
            winTextObject.SetActive(true);
            // in general never set the position via Transform when dealing with Rigidbodis!
            rd2d.position = new Vector2(64.0 f, -1.0 f);
        }
    } 
    else if (collision.gameObject.CompareTag("Enemy")) 
    {
        Destroy(collision.gameObject);

        lives--;
        livesText.text = $"Lives: {lives}";

        if (lives <= 0) 
        {
            loseTextObject.SetActive(true);
        }
    }
}

private void OnCollisionStay2D(Collision2D collision) 
{
    if (collision.gameObject.CompareTag("Ground"))
    {
        if (Input.GetKey(KeyCode.W)) 
        {
            rd2d.AddForce(new Vector2(0, 3), ForceMode2D.Impulse);
        }
    }

    if (Input.GetKey("escape")) 
    {
        Application.Quit();
    }

    // no need to poll check the "lives" here either
}

You could even take it one more level further and use properties this way you can couple a behavior to any change of your fields and could even set them from the outside without having to double implement stuff:

// This only stores the value (backing field) but you will never touch it directly ;)
private int _scoreValue;

// This now is a property that allows everyone to read the value
// but only this class to set it (for now)
// additionally every time you set the value it will update the display and react to the => 4 condition
public int scoreValue
{
    get => _scoreValue;
    private set
    {
        _scoreValue = value;
        scoreText.text = $"Score: {_scoreValue}";
            
        if(_scoreValue == 4)
        {
            winTextObject.SetActive(true);
            // in general never set the position via Transform when dealing with Rigidbodis!
            rd2d.position = new Vector2(64.0 f, -1.0 f);
        }
    }
}

// The same thing you can also make for the lives
private int _lives;

public int lives
{
    get => _lives;
    private set
    {
        _lives = value;
        livesText.text = $"Lives: {_lives}";

        if (_lives <= 0) 
        {
            loseTextObject.SetActive(true);
        }
    }
}

So now yo would simply do

private void OnCollisionEnter2D(Collision2D collision) 
{
    // In general rather use "CompareTag" instead of string "=="
    // While "==" silently fails in case of typos or non-existent tags
    // "CompareTag" will throw an error making your debugging easier and is also slightly faster since it uses Hashes
    if (collision.gameObject.CompareTag("Coin")) 
    {
        Destroy(collision.gameObject);

        scoreValue  ;
    } 
    else if (collision.gameObject.CompareTag("Enemy")) 
    {
        Destroy(collision.gameObject);

        lives--;
    }
}

but can later add more and more way of how you can lose or gain score and lives without having to remember to also implement the text updates and condition checks. You only do these once within the properties ;)

  • Related