I'm trying for my player to walk over a game object and have to press "e" to pickup the object and display the text stored in it on pickup, not on collision. This is the code I have right now:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NoteV2 : MonoBehaviour
{
[SerializeField] private GameObject uiObject;
[SerializeField] private GameObject note;
private SpriteRenderer sr;
private bool pickUpAllowed;
void Start()
{
uiObject.SetActive(false);
sr = GetComponent<SpriteRenderer>();
}
void Update()
{
if (pickUpAllowed && Input.GetKeyDown(KeyCode.E))
{
PickUp();
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
pickUpAllowed = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
pickUpAllowed = false;
}
}
private void PickUp()
{
Destroy(gameObject);
Debug.Log("Note running");
}
}
It kinda works. I store the note gameObject and the uiObject (the canvas) to display the text on pickup, which I haven't coded yet and then destroy the gameObject. For some reason the text displays on collision and the gameObjects gets destroyed when I exit the collider, regardless of if I press "e" or not. But if I press "e" the debug message runs, so I guess it does work.
I also want to stop player movement until they have read the text stored in it but I'll handle that once the pickup works correctly.
I had this before, which works but is pickup only on collision, not when pressing any buttons. It is a little buggy but it mostly works:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Note : MonoBehaviour
{
[SerializeField] private GameObject uiObject;
[SerializeField] private GameObject note;
private SpriteRenderer sr;
//disable/enable input
public static bool PlayerControlsDisabled = false;
// Start is called before the first frame update
void Start()
{
uiObject.SetActive(false);
sr = GetComponent<SpriteRenderer>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
uiObject.SetActive(true);
sr.color = new Color(1f,1f,1f,0);
startIn();
}
}
public void startIn()
{
StartCoroutine(waitTantico());
}
IEnumerator waitTantico()
{
PlayerControlsDisabled = true;
print("Hello World");
Debug.Log(Time.time);
yield return new WaitForSeconds(3);
Debug.Log("Waited");
PlayerControlsDisabled = false;
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerExit2D(Collider2D collision)
{
uiObject.SetActive(false);
Destroy(note);
}
}
Related
I have a game that is similar to "Flappy Bird" and I have main menu where I can start game and change skin of a pigeon. My skin collection is implemented with scroll rect and in the center there is a trigger which starts an animation of scaling a pigeon, it works fine until I click "start" and the scene changes to game and when I return to my main menu and click "skins" this trigger doesn't work anymore.
Script what is attached to all scroll rect elements to detect collisions with trigger:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ResizeFieldScript : MonoBehaviour
{
private Animator _anim;
private void Start()
{
_anim = GetComponent<Animator>();
}
public void OnTriggerEnter2D(Collider2D collider)
{
Debug.Log("Trigger is working");
if(collider.tag == "ResizeField")
{
Debug.Log("Condition is working");
_anim.SetBool("isInTrigger", true);
}
}
public void OnTriggerExit2D(Collider2D collider)
{
_anim.SetBool("isInTrigger", false);
}
}
Script what is attached to an empty object to change scenes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
public class UIController : MonoBehaviour
{
[SerializeField] private List<string> sceneNameList;
private string sceneToFind;
private int index = 0;
public void SceneChanger()
{
sceneToFind = EventSystem.current.currentSelectedGameObject.name;
foreach(string str in sceneNameList)
{
if(str == sceneToFind)
{
SceneManager.LoadScene(index);
index = 0;
break;
}
index++;
}
}
public void Exit()
{
Application.Quit();
}
public void BackMenu()
{
SceneManager.LoadScene(4);
}
}
OnTriggerEnter2D doesn't work after switching scenes. We could use OnTriggerEnter2D to jump scenes.
code show as below:
private void Update() {
// If E is pressed
if (Input. GetKeyDown(KeyCode. E)) {
// scene switching
SceneManager.LoadScene(4);
}
}
private void OnTriggerEnter2D(Collider collision) {
if (collision. tag == "ResizeField") {
// The UI prompts the user to press E to jump
EnterDialog.SetActive(true);
Debug.Log("Condition is working");
// _anim.SetBool("isInTrigger", true);
}
}
Hope it helps you.
So, I am making a game, and whenever you press tab an inventory should pop up. Right now its just some gray boxes and TMP. I have an empty GO, with two other empty GOs. Pockets and a backpack. On both it has the boxes and TMP as a child.
using System.Collections.Generic;
using UnityEngine;
public class InventorySys : MonoBehaviour
{
//getting the player, and the OV_playerData
[SerializeField] private GameObject player;
private OV_PlayerData playerData;
//Backpack Bool
private bool bag;
//Getting the inventory
[SerializeField] private GameObject Inventory;
[SerializeField] private GameObject backpack;
//Open inventory bool (useful for the backpack to be showing only when the inventory is open)
private bool OpenInv;
// Start is called before the first frame update
void Start()
{
playerData = player.GetComponent<OV_PlayerData>();
bag = playerData.Backpack;
//making sure that the inventory is set to false
Inventory.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
Inventory.SetActive(true);
OpenInv = true;
}
while (OpenInv == true)
{
if (bag == true)
{
backpack.SetActive(true);
}
}
}
}
After I run the game it works normally until I press tab when it just freezes.
You have no exit condition for your while loop.
You don't need a while loop to make sure something is visible constantly, when a game object is active its going to be rendered until you set it inactive again.
using System.Collections.Generic;
using UnityEngine;
public class InventorySys : MonoBehaviour
{
//getting the player, and the OV_playerData
[SerializeField] private GameObject player;
private OV_PlayerData playerData;
//Backpack Bool
private bool bag;
//Getting the inventory
[SerializeField] private GameObject Inventory;
[SerializeField] private GameObject backpack;
//Open inventory bool (useful for the backpack to be showing only when the inventory is open)
private bool OpenInv;
// Start is called before the first frame update
void Start()
{
playerData = player.GetComponent<OV_PlayerData>();
bag = playerData.Backpack;
//making sure that the inventory is set to false
Inventory.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
OpenInv = true;
Inventory.SetActive(true);
if (bag == true)
{
backpack.SetActive(true);
}
}
}
}
Here is the code for the same.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class changescene : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
if(Input.GetKeyDown(KeyCode.Space))
{
SceneManager.LoadScene(1);
}
}
}
}
Running this with a 3D cube tagged player is not changing scenes and neither showing any errors.
Input.GetKeyDown is only true once per key press and only during a single frame where the key went down.
It is extremely unlikely that you press the key down exactly in the FixedUpdate physics all when the trigger enters your collider!
You want to split your physics / collision detection from user input and rather do e.g.
public class changescene : MonoBehaviour
{
private bool isEntered;
private void OnTriggerExit(Collider other)
{
if (!other.CompareTag("Player")) return;
isEntered = false;
}
private void OnTriggerEnter(Collider other)
{
if (!other.CompareTag("Player")) return;
isEntered = true;
}
private void Update()
{
if(isEntered && Input.GetKeyDown(KeyCode.Space))
{
SceneManager.LoadScene(1);
}
}
}
Or if you want to make it a bit more efficient without an Update method that runs most of time unnecessary you can use a Coroutine like e.g.
public class changescene : MonoBehaviour
{
private void OnTriggerExit(Collider other)
{
if (!other.CompareTag("Player")) return;
StartCoroutine(CheckSpaceRoutine());
}
private void OnTriggerEnter(Collider other)
{
if (!other.CompareTag("Player")) return;
StopAllCoroutines();
}
private IEnumerator CheckSpaceRoutine()
{
while(true)
{
if(Input.GetKeyDown(KeyCode.Space))
{
SceneManager.LoadScene(1);
yield break;
}
yield return null;
}
}
}
I have a couple of problems with object pooling in Unity with my 2D game, cannon balls don't want to stop when there is a collision with the wall, and 1 of them bursts shoot and the other 9 shoot together connected with each other, my cannon is static object on scene. Can someone help or give me some hint about it.
Here is my code, 3 scripts:
ObjectPooling.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooling : MonoBehaviour
{
public static ObjectPooling instance;
[SerializeField] public GameObject objectToPool;
private List<GameObject> cannonBalls = new List<GameObject>();
private int numberOfObjects = 20;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
for (int i = 0; i < numberOfObjects; i++)
{
GameObject gameObject = Instantiate(objectToPool);
gameObject.SetActive(false);
cannonBalls.Add(gameObject);
}
}
// Update is called once per frame
void Update()
{
}
public GameObject GetCannonBallObject()
{
for (int i = 0; i < cannonBalls.Count; i++)
{
if (!cannonBalls[i].activeInHierarchy)
{
return cannonBalls[i];
}
}
return null;
}
}
Cannon.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Cannon : MonoBehaviour
{
[SerializeField] private Rigidbody2D rb;
[SerializeField] private GameObject cannonBall;
[SerializeField] private Transform cannonBallPosition;
void Start()
{
}
private void Update()
{
Fire();
}
private void Fire()
{
cannonBall = ObjectPooling.instance.GetCannonBallObject();
if(cannonBall != null)
{
cannonBall.transform.position = cannonBallPosition.position;
cannonBall.SetActive(true);
}
}
}
CannonBall.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CannonBall : MonoBehaviour
{
private float speed = 10f;
[SerializeField] private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
rb.velocity = Vector2.left * speed;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("FloorAndWall"))
{
// Destroy(this.gameObject);
gameObject.SetActive(false);
}
}
}
Why is you cannonball static? Have your tried not having it marked as static? Also, this problem has nothing to do with object pooling. Make sure that when your objects are enabled, all the components are active. Finally, when working with rigidbodies, you should handle them inside FixedUpdate(), and not inside Update().
I'm developing a TopDown 2D game on Unity with some RPG elements and I did as so, when the player steps on a set of tiles placed on the map, it triggers an animation with a UI showing some text. However, the animation gets called multiple times while the player keeps stepping on the tiles and even when he exits the trigger area.
What I need help with is how to make the animation only trigger once and, while the UI is in on the screen, the player is not allowed to move until the player presses the button in the UI (already programmed and working).
Here is the code I used to make the trigger function:
public class TriggerScript : MonoBehaviour
{
public string popUp;
public Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.PopUp(popUp);
Debug.Log("trigger");
animator.SetTrigger("pop");
}
}
And here is the PopUpSystem that sets the text:
public class PopUpSystem : MonoBehaviour
{
public GameObject popUpBox;
public Animator popupanimation;
public TMP_Text popUpText;
public void PopUp(string text)
{
popUpBox.SetActive(true);
popUpText.text = text;
popupanimation.SetTrigger("pop");
}
}
If, in order to help me, you need more information and details, please ask me in the comment section.
Note that I am new to Unity and have zero experience with it so, if you can be patient and explain things in a simple way, I would enjoy that!
Thank you for reading.
Edit: this is the animator window:
Edit 2: The code that I use for the movement of the player:
public class PLayerController : MonoBehaviour
{
private Rigidbody2D MyRB;
private Animator myAnim;
public string popUp;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
MyRB = GetComponent<Rigidbody2D>();
myAnim = GetComponent<Animator>();
}
private void Move()
{
myAnim.SetTrigger("popUp");
}
// Update is called once per frame
void Update()
{
MyRB.velocity = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")) * speed * Time.deltaTime;
myAnim.SetFloat("moveX", MyRB.velocity.x);
myAnim.SetFloat("moveY", MyRB.velocity.y);
if ((Input.GetAxisRaw("Horizontal")==1) ||(Input.GetAxisRaw("Horizontal") == -1)||(Input.GetAxisRaw("Vertical") == 1)||(Input.GetAxisRaw("Vertical") == -1))
{
myAnim.SetFloat("LastMoveX", Input.GetAxisRaw("Horizontal"));
myAnim.SetFloat("LastMoveY", Input.GetAxisRaw("Vertical"));
}
}
}
Edit 3: Code with boolean:
public class TriggerScript : MonoBehaviour
{
public string popUp;
public Animator animator;
bool condition = false;
void Start()
{
animator = GetComponent<Animator>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (condition == false)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.PopUp(popUp);
Debug.Log("trigger");
animator.SetTrigger("pop");
condition = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
animator.SetTrigger("close");
}
All you have to do is to set your TriggerScript to something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerScript : MonoBehaviour
{
public string popUp;
private void OnTriggerEnter2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
if (collision.gameObject.tag == "Player")
{
pop.PopUp(popUp);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.closeBox();
}
}
and then set your PopUpSystemScript to something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PopUpSystem : MonoBehaviour
{
public GameObject popUpBox;
public Animator popupanimation;
public TMP_Text popUpText;
public void PopUp(string text)
{
popUpBox.SetActive(true);
popUpText.text = text;
popupanimation.SetTrigger("pop");
}
public void closeBox()
{
popupanimation.SetTrigger("close");
}
}
Now click on the popUp and close animation clips (in Animations folder) and remove the Loop Time check mark. and you should be all set to see the animation appears and disappears.
To stop the player, you can either use Time.timeScale. or set the rigidbody to isKenimatic.