I'm sorry for any messy code, I'm relatively new to this. I made a working teleport in Unity but whenever I teleport from one of the teleports to the other, I wanna make it so there's a 5 second cooldown before you can use the teleporter again. So I used IEnumerator, added 5 seconds before "justTeleported" became false again, but when I teleported, I instantly got teleported back, and had to wait 5 seconds before I could try again. So my though was maybe I'm touching the trigger too quickly, before it can become false, that's why I added the two seconds. But now, whenever I get on the teleporter, it goes from true to false to true a couple times, and then I eventually get teleported back to where I came from. If anyone could help, I would be very thankful. Thank you.
{
public Transform Destination;
bool justTeleported;
public GameObject Player = GameObject.FindGameObjectWithTag("Player");
// Start is called before the first frame update
void Start()
{
justTeleported = false;
}
private void Update()
{
print(justTeleported)
}
private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.tag == "Player" && justTeleported == false)
{
StartCoroutine("Cooldown");
}
}
IEnumerator Cooldown()
{
justTeleported = true;
yield return new WaitForSeconds(2f);
Player.transform.position = Destination.transform.position;
yield return new WaitForSecondsRealtime(5f);
justTeleported = false;
}
Because each of the teleports has its own bool justTeleported, setting the first true doesn't automatically set the other true, so you need some way to tell the Destination teleport script to start it's own cooldown coroutine. Here's my test script, it works for me.
using System.Collections;
using UnityEngine;
public class Teleport : MonoBehaviour
{
public Teleport Destination = null;
public float CooldownTime = 5f;
private bool justTeleported = false;
public void StartCoolDown()
{
StartCoroutine(Cooldown());
}
private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.CompareTag("Player") && !justTeleported) //CompareTag("tag") is better than .tag == "tag"
{
TeleportPlayer(coll.gameObject);
}
}
private void TeleportPlayer(GameObject player)
{
if (Destination) //if Destination is not null
{
StartCoolDown();
Destination.StartCoolDown(); //tell Destination to cooldown
player.transform.position = Destination.transform.position;
}
}
private IEnumerator Cooldown()
{
justTeleported = true;
yield return new WaitForSeconds(CooldownTime);
justTeleported = false;
}
}
Teleport1 (Destination: Teleport2)
Teleport2 (Destination: Teleport1)
Related
so imagine a replica of angry birds, I’ve made a hook (a circle that I’ve made transparent), and I’ve made the movement I want, but now, I was to let’s say, attach my SpringJoint2D to a new hook when I trigger it (OnTriggerEnter), so like I’m on an old hook or anchor point, and I release my ball (like a bird flying) and I want to go through another hook/anchor point, and get the ability to shoot again
heres my code:
using System.Collections;
using UnityEngine;
public class Player : MonoBehaviour
{
public Rigidbody2D rb;
bool isPressed = false;
public float releaseTime = .10f;
void Update()
{
if(isPressed)
{
rb.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
}
void OnMouseDown()
{
isPressed = true;
rb.isKinematic = true;
}
void OnMouseUp()
{
isPressed = false;
rb.isKinematic = false;
StartCoroutine(Release());
}
IEnumerator Release()
{
yield return new WaitForSeconds(releaseTime);
GetComponent<SpringJoint2D>().enabled = false;
}
void OnTriggerEnter(Collider other)
{
if(other.gameObject.CompareTag("Hook"))
{
GetComponent<SpringJoint2D>().enabled = true;
GetComponent<SpringJoint2D>().ConnectedRigidBody = other.GetComponent<Rigidbody2D>();
}
}
}
so what i want is
My unity project is freezing and there are no errors before clicking play button after that it freezes the code:
In this code I am trying to detect if the player is triggering enemy collider with OnTriggerStay2D
so if it does I want the code to add score every second with IEnumerator function. But if the player is not colliding with the enemy for more than 2 seconds it gets destroyed.
The code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Trigger : MonoBehaviour
{
public GameObject other;
bool touching = false;
int score = 0;
public void Update()
{
AddScore();
}
private void OnTriggerStay2D(Collider2D other)
{
Debug.Log("Touching");
touching = true;
}
public void AddScore()
{
if(touching == true)
{
while (touching == true)
{
score++;
StartCoroutine(wait());
}
Debug.Log(score);
}
else
{
Debug.Log("Not Touching");
StartCoroutine(waiter());
}
}
IEnumerator waiter()
{
yield return new WaitForSecondsRealtime(2);
Destroy(other);
}
IEnumerator wait()
{
yield return new WaitForSecondsRealtime(1);
}
}
Just delete while (touching == true), that is breaking your game.
The if condition should be enough for the logic.
me and my colleagues are creating a 2D sidescroller game in Unity with C#.
The player, is supposed to be able to pick objects (by touching them) and throw them.
Our plan is this:
1) make variables for the objects, their booleans and their rigid bodies.
2) verify if the object is touching the player
3) if it's true, then the object will parent to the player (setting their position to the player's hand).
4) to throw, the code will check if the player has the object (by using a boolean) and then it will unparent and throw by addforce.
The code doesn't have any errors and it works except on the throwing part (the player can grab and ungrab but can't throw).
The player can pick and unpick but not throwing and i don't understand why because the code looks right and the console doesn't show me any errors :/
Have a look to my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public bool ispicked1 = false;
public GameObject pickable1;
public Rigidbody2D pickable1rb;
public GameObject Parent;
public float force;
void Start() {
pickable1rb = GetComponent<Rigidbody2D>();
}
void Update() {
if (ispicked1 == true) {
pickable1.transform.position = Parent.transform.position;
}
else if (ispicked1 == false) {
pickable1.transform.parent = null;
}
if (Input.GetMouseButton(1) && ispicked1 == true) {
ispicked1 = false;
pickable1rb.AddForce(transform.up * force, ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "pickable1") {
Debug.Log("Tocou em objecto");
ispicked1 = true;
pickable1.transform.SetParent(Parent.transform);
}
}
}
Side question: i want the player to throw at the direction he's facing, what is the best way to do that? I can only choose between right, left or up :/
UPDATE:
I solved all the problems and i created a side script for the object to be thrown and they are 100% working! Here are the codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public bool ispicked1 = false;
public bool ispicked2 = false;
public GameObject pickable1;
public GameObject pickable2;
public GameObject Parent;
public bool isThrown = false;
public ThrowableObject throwableinstance1;
public ThrowableObject throwableinstance2;
public bool isfull = false;
void Start() {
throwableinstance1 = GameObject.Find("pickable1").GetComponent<ThrowableObject>();
throwableinstance2 = GameObject.Find("pickable2").GetComponent<ThrowableObject>();
}
void Update() {
if (ispicked1 == true) {
pickable1.transform.position = Parent.transform.position;
isfull = true;
}
else if (ispicked1 == false) {
pickable1.transform.parent = null;
}
if (ispicked2 == true) {
pickable2.transform.position = Parent.transform.position;
isfull = true;
} else if (ispicked2 == false) {
pickable2.transform.parent = null;
}
if (Input.GetMouseButton(1) && ispicked1 == true) {
ispicked1 = false;
isThrown = true;
throwableinstance1.Throw();
isfull = false;
}
if (Input.GetMouseButton(1) && ispicked2 == true) {
ispicked2 = false;
isThrown = true;
throwableinstance2.Throw();
isfull = false;
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "pickable1" && isfull == false) {
ispicked1 = true;
pickable1.transform.SetParent(Parent.transform);
}
if (collision.gameObject.name == "pickable2" && isfull == false) {
ispicked2 = true;
pickable2.transform.SetParent(Parent.transform);
}
}
}
Here's the code to throw the pickable object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowableObject : MonoBehaviour
{
public Inventory inventoryinstance;
public Rigidbody2D throwablerb;
public Transform Player;
public GameObject PickableObject;
public EnemyHealth1 enemyhealth1instance;
public EnemyHealth2 enemyhealth2instance;
public EnemyHealth3 enemyhealth3instance;
void Start()
{
inventoryinstance = GameObject.Find("Player").GetComponent<Inventory>();
enemyhealth1instance = GameObject.Find("enemy1").GetComponent<EnemyHealth1>();
enemyhealth2instance = GameObject.Find("enemy2").GetComponent<EnemyHealth2>();
enemyhealth3instance = GameObject.Find("enemy3_leper").GetComponent<EnemyHealth3>();
}
public void Throw()
{
if(Player.localScale.x < 1)
{
throwablerb.AddForce(transform.right * -1);
} else if(Player.localScale.x > 0)
{
throwablerb.AddForce(transform.right);
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "enemy1") {
enemyhealth1instance.GetComponent<EnemyHealth1>().EnemyHealthbar1-= 1;
Destroy(PickableObject);
}
if (collision.gameObject.name == "enemy2") {
enemyhealth2instance.GetComponent<EnemyHealth2>().EnemyHealthbar2-=1;
Destroy(PickableObject);
}
if (collision.gameObject.name == "enemy3_leper") {
enemyhealth3instance.GetComponent<EnemyHealth3>().EnemyHealthbar3-=1;
Destroy(PickableObject);
}
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.name == "enemy1_hitbox") {
enemyhealth1instance.GetComponent<EnemyHealth1>().EnemyHealthbar1-=1;
Destroy(PickableObject);
}
}
}
While you're answering my question, I'll do some quick code reviewing:
Overall, it feels weird to be keeping track and maintaining "holding & throwing an item" in a class called "Inventory" that has references to a separate object, that is being held & a reference to the player. What is this script? The item, the player or a third party; a separate inventory? (Questions to ask yourself, you don't need to answer them - just think about it 😊)
pickable1
pickable1object
pickable1rb
These variable names makes little sense to me in current context; why not just isPickedUp, object, rb?
if (pickable1 == true) {
pickable1object.transform.position = Playerparent.transform.position;
}
if (pickable1 == false) {
pickable1object.transform.parent = null;
}
pickable1 will not be both true and false, doing 2 ifs are just unnecessary computations. What you're looking for here is an if/else or if/elseif.
Furthermore, if you child it to the player, logically, shouldn't you NOT have to set the position every frame? Else, what's the purpose of childing?
void FixedUpdate() {
if (Input.GetMouseButton(1)) {
// [...]
}
// [...]
}
Logically, you shouldn't be checking for input in the fixed update since inputs are tied to a frame, meaning Update(). In this case, it may accidentally work reliably most of the time because you're not checking if the player CLICKED a button, rather you're checking if player is HOLDING a button. (GetMouseButton vs GetMouseButtonDown). If you were to check for if the player CLICKED a button in the FixedUpdate, it would only work on the few lucky frames where FixedUpdate and Update were running at the exact same time.
Regarding your question: The code looks alright, it's probably not behave in the way you expect. From what I can tell in the code it will...
If player has picked up an item, it will have force added to it if holding down Mouse 1 for as long as player is holding down mouse.
Why nothing is happening may be due to inspector values being off. Debug the values of force and see if the code is triggering at all. Consider moving the parent = null code (or just remove the parenting alltogether).
It could also be that as soon as you push the item outside of the player, it falls down and triggers OnCollisionEnter and is grabbed by the player again.
The problem with your code is that you use transform.up which will just cause it to throw upwards rather than the direction the player is facing. So you can use transform.right for the direction the player is facing. You can use both to create more of an arc.
You'll have some problems with pivoting with the way you have it setup.
What I recommend is to have two transforms which are children of the player to pivot the sprite properly. (You can change the position of the transform in the keyframes of the player's animation)
The code would look something like this:
public class Inventory : MonoBehaviour
{
private bool carryingItem => pickedUpItem == null;
private PickableItem pickedUpItem;
[SerializeField] private Vector2 throwingForce;
[SerializeField] private Transform spawnPosition; //Should be a child of the player gameobject. This will be position of the object
[SerializeField] private Transform handTransform; //Should also be a child of the player gameobject. This needs to be animated for the keyframes that move the hand
void Update()
{
if (carryingItem && Input.GetMouseButtonDown(1))
{
pickedUpItem.transform.SetParent(null);
pickedUpItem.transform.position = spawnPosition.position;
pickedUpItem.GetComponent<Rigidbody2D>().AddForce(transform.right * throwingForce.x + transform.up * throwingForce.y);
pickedUpItem = null;
}
}
private void OnCollisionEnter2D(Collision2D other)
{
var pickable = other.transform.GetComponent<PickableItem>();
if (pickable && pickedUpItem == null) //This kind of depends on the game design because maybe you want to pick it up if you're carrying something already
{
pickedUpItem = pickable;
pickable.transform.SetParent(handTransform);
}
pickable.transform.localPosition.Set(0,0,0);
}
}
You set the picked up item to the parent where you want to be pivoted around.
When my Player (GameObject) meets Lava, they should respawn in a specific scene.
This is the code I have assigned to the Player:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Lava")
{
GameObject.Find("Controller").GetComponent<Controller>().Respawn();
}
}
Controller is a GameObject, that I don't want to Destroy by Changing level, so this is the code for my Controller GameObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Controller : MonoBehaviour
{
private static bool created = false;
public static Controller instance;
GameObject Player;
Vector3 respawnPoint;
void Awake()
{
if (instance = null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
Player = GameObject.Find("Player");
respawnPoint = GameObject.Find("RespawnPoint").transform.position;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
Player.transform.position = respawnPoint;
}
}
RespawnPoint is just an invisible Cube GameObject, where I want the player to respawn.
Let's say the Game Starts with Scene "0" (this is where the RespawnPoint is, too.)
Then the Player goes to Scene "1" and dies (meets Lava). Then I want the Game to change back to Scene "0" and teleport the Player to the RespawnPoint.
The Scene-Change works good, but the player always starts at the same position, where he starts the first time and he's not teleported to the RespawnPoint.
What am I doing wrong?!
First of all you lack the "==" in the first 'if' from the Awake: if (instance == null
The code is fine or it does seem so to me, but the RespawnPoint should be in the Scene your Player meets the lava not in the Scene you are loading. If not the starting position of the player will always be (0,0,0).
I would recommend coming to this in a completely different way. I would make a public Transform[] Spawnpoints. Since the transform is public you can assign different objects to it. Make an empty game object and position it where you want to spawn. Then use
Void OnTriggerEnter(collider2D, other){
if(other.gameObject.tag == lava) {
transform.position = spawnpoints[0].position;
}
}
In the inspector set the Transform to be a size of 1 and set the respawn GameObject as the one transform.
Thanks to your answers, they helped me solving this problem.
I added a "DontDestroyOnLoad" to the RespawnPoint and than i changed the Controller Code to this:
{
private static bool created = false;
public static Controller instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
GameObject.Find("Player").transform.position = GameObject.Find("RespawnPoint").transform.position;
}
}
now the player gets teleported to the correct RespawnPoint. Thanks for your help!
I am using the Unity Engine to make a 2D game... to give you an idea of what it looks like, there is a generic space background, 4 rocks bouncing around the screen, and a tie fighter. My goal was to make the tie fighter explode (Which I did succeed in doing), destroy itself, and have a prefab take its place. I am new to c#, so I don't know much of the API. I tried using the second script to destroy the tie fighter, then instantiate a prefab... Now, whenever I run the game, it spawns enough clones to the point where Unity crashes and I do not know how to fix it. I tried googling stuff and doing a manual fix (hence the bools), but nothing seems to be working. Any help would be greatly appreciated. Also, please don't just comment; write it as an answer so that I can mark it correct if it works. Here are the scripts (I am assuming the error is in the second one, but I included both for reference):
First Script:
using UnityEngine;
using System.Collections;
public class tfScript : MonoBehaviour
{
Vector3 tfPos;
Vector3 worldPos;
float mousePosInBlocksx;
float mousePosInBlocksy;
int lives;
public Sprite tieFight; // Drag your first sprite here
public Sprite kaboom; // Drag your second sprite here
private SpriteRenderer spriteRenderer;
float makeThingsGoBoom;
// Use this for initialization
public void Start ()
{
tfPos = new Vector3 (3f, 3f, -4f);
lives = 20;
spriteRenderer = GetComponent<SpriteRenderer>(); // we are accessing the SpriteRenderer that is attached to the Gameobject
if (spriteRenderer.sprite == null) // if the sprite on spriteRenderer is null then
{
spriteRenderer.sprite = tieFight; // set the sprite to sprite1
}
}
// Update is called once per frame
public void Update ()
{
GameObject controller = GameObject.Find ("controller");
gameController gameCon = controller.GetComponent<gameController> ();
mousePosInBlocksx = ((Input.mousePosition.x / Screen.width) * 16);
mousePosInBlocksy = ((Input.mousePosition.y / Screen.width) * 12)+2;
tfPos.x = Mathf.Clamp (mousePosInBlocksx, .5f, 15.5f);
tfPos.y = Mathf.Clamp (mousePosInBlocksy, .5f, 11.5f);
this.transform.position = tfPos;
if (makeThingsGoBoom == 0)
{
gameCon.Update();
}
}
public void ChangeTheDarnSprite ()
{
if (spriteRenderer.sprite == tieFight) { // if the spriteRenderer sprite = sprite1 then change to sprite2
spriteRenderer.sprite = kaboom;
}
else
{
spriteRenderer.sprite = tieFight;
}
}
public void OnCollisionEnter2D (Collision2D collider)
{
if (collider.gameObject.name.Contains("spacerock") )
{
lives--;
print (getLives ());
}
if (collider.gameObject.name.Contains("spacerock")) // If the space bar is pushed down
{
spriteRenderer.sprite = kaboom;
makeThingsGoBoom = 0;
}
}
public void increaseLives()
{
lives++;
}
public double getLives()
{
return lives;
}
}
Second Script:
using UnityEngine;
using System.Collections;
public class gameController : MonoBehaviour
{
public GameObject tf;
public GameObject tfpf;
public bool iBlowedUp = false;
public void Start()
{
}
public void Update ()
{
boom ();
}
public void boom()
{
iBlowedUp = true;
if (iBlowedUp = true)
{
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
iBlowedUp = false;
}
}
public IEnumerator waitForIt()
{
print ("Bob lives #2!");
yield return new WaitForSeconds (1);
print ("John is a turtle #2!");
}
}
You are calling following method in a Update function which is executed constantly.
public void boom()
{
iBlowedUp = true;
if (iBlowedUp = true)
{
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
iBlowedUp = false;
}
}
An if iBlowedUp = true; if (iBlowedUp = true){ doesn't make sense, because the statement is true always.
It should be similar to:
public void boom()
{
if (iBlowedUp == true)
{
iBlowedUp = false;
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
}
}
Probably you want to set iBlowedUp to true somewhere else. As I consider in a tfScript.Update() method, instead of calling Update method.
if (makeThingsGoBoom == 0)
{
gameCon.iBlowedUp = true;
//gameCon.Update();
}
if (iBlowedUp = true)
{
iBlowedUp = false;
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
}
before instantiate make isBlowedUp false, I cant say ı understood well this "Unity crashes". there is some complexity in your code. I hope you fix them as well