I have a problem in my project. I created an animation to open the door and made it open whenever a button was pressed. Then I copied the animation and entered its value -1 to make the door close after 2 seconds, but if the character presses the door open button again while playing the closing animation, it plays that animation and this causes a bug in the game. I apologize for telling a little complicated. I am waiting for your answers. Here is my code ->
public Text text;
public Animator anim;
private void Start()
{
text.enabled = false;
anim = GetComponent<Animator>();
}
private void Update()
{
if(text.enabled && Input.GetKeyDown(KeyCode.E))
{
anim.Play("DoorOpen");
}
}
private void OnTriggerExit(Collider other)
{
text.enabled = false;
}
private void OnTriggerEnter(Collider other)
{
text.enabled = true;
}
}
You shouldn't use the Animator Play() method and instead create parameters like a Trigger that.. well... triggers the animation.
Also, you don't say what's the error you're getting so it is hard to help you, but I guess the animation is restarting? You can set so the animations can't be transitioned to itself, so it won't restart if triggered again before finishing. Edit your AnimatorController asset, and click on the animation that shouldn't transition to itself, then uncheck where it says precisely that: "Can transition to itself". (Maybe you have to double click it, maybe you have to click on the transition, don't really remember right now, you'll figure it out :) ).
Related
I'm making a game with unity 2022 and was struggling to make this animator [bool turn true] after entering a trigger and pressing "E". and then go false after still holding "E" still or not pressing it. also am struggling with Get Key stuff. it isn't working mate and I have been trying for hours at a time with struggles so yeah. this is my code. it's basically someone pressing a button (the object in unity I'm using), making it click until you click it again, please help this is for a school summer project!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Fixedpress : MonoBehaviour
{
public Animator Tributton;
bool YUIp = false;
// Start is called before the first frame update
void OnTriggerEnter(Collider other)
{
if(other.tag == "player")
{
YUIp = true;
}
}
void OnTriggerStay(Collider other)
{
if (other.tag == "Player" && YUIp == true)
{
if (Input.GetKeyDown(KeyCode.E))
{
Tributton.SetBool("Fiveyon", true);
Debug.Log("THY");
}
else if (Input.GetKey(KeyCode.E))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THe");
}
else if (Input.GetKeyUp(KeyCode.Return))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THane");
}
}
}
void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
YUIp = false;
}
}
}
I mostly need help with the press e code stuff to make work so yeah :D
thank you
sorry for the bad grammar its 3 am rn
I think I got it now.
You want
Player has to be in the trigger in order to start the animation
Pressing E should trigger the animation only ONCE
I would no use a Bool parameter in that case but rather a Trigger and then Animator.SetTrigger.
Your transitions should be somewhat like e.g.
Idle State --condition-Fiveyon--> Your Animation
Your Animation --exit-time-no-further-condition--> Idle State
Then you could probably do something like e.g.
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
private Coroutine routine;
private void OnTriggerEnter(Collider other)
{
// not sure if needed right now, some of the events are triggered even on disabled components
if(!enabled) return;
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if(!other.CompareTag("player")) return;
// just in case to prevent concurrent routines
if(routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if(!other.CompareTag("player")) return;
// when exiting the trigger stop the routine so later button press is not handled
if(routine != null) StopCoroutine(routine);
}
}
I am trying to do when i destroy all boxes something happen.
My code is;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class destroy : MonoBehaviour
{
private string BALL_TAG = "ball";
public AudioClip coin;
public AudioSource src;
public float numBox = 120f;
public bool isDestroyed;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(BALL_TAG))
{
src.clip = coin;
src.Play();
Destroy(gameObject);
isDestroyed = true;
}
}
private void Update()
{
boxes();
}
public void boxes()
{
if(isDestroyed == true)
numBox -= 1f;
if(numBox == 119)
SceneManager.LoadScene("mainManu");
}
private IEnumerator Two()
{
yield return new WaitForSeconds(1f);
Destroy(gameObject);
}
}
But it doesn't work.
It is suppose to do when I broke 1 box it sends me to menu.
I think its problem in "numBox -= 1f;" because I don't know hot to make this.
I don't understand your code completely. So, I need to make some assumptions.
I think the Script is attached to the box and every box has this Script. I also think, that your player Shoots Ball. Those Balls have a collider with an ball tag.
There are multiple problems with your code.
The first one is, that your count variable, numBox, is saved in your destroy Script, which is placed on each box.
this means, that every Box is counting for itself.
You have to centralize this. There are multiple ways for doing this.
One way is to declare this variable as static(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static)
This is not best practice, but works.
A Better way is to have a Script on Your Player, which holds this number and every Box searches for this Script and change this number if it is destroyed.
The second big Problem is, that your are doing some really weird thing in your Update and the collision handling
First of all, you are setting isDestroyed to true. Then in your boxes method, which is called in every Frame, you are decrementing your numBox variable by one, if this is Destroyed is true.
So if your Box gets hit, you are decrementing every frame.
After that you are checking every frame if your numBox is 119
If so, you change the Scene.
This is the reason, why you are getting to your MainMenu after only one boy
This behaviour is very weird, because it is totally unnecessary. You can reduce your variable directly in in your OnCollisionEnter2D Method.
There are some little things, which can be improved.
When you are trying to play a Sound, you don't have to specify the AudioClip in code. You can assign this directly in Unity on the AudioSource Component via drag and drop. This makes your code simpler.
You are not calling the Two Coroutine. You've specified this Coroutine but don't call it.
//Script on Player
public class PlayerBoxDestroyManager:MonoBehaviour
{
public int StartBoxes = 120;
private int Boxes;
private void Start()
{
Boxes = StartBoxes;
}
public void DestroyBox()
{
//Reduce our Boxes count
//This is equal to Boxes -= 1
// Boxes = Boxes -1
Boxes--;
// If we have less or zero Boxes left, we End call our EndGame methode
if(Boxes <= 0)
{
EndGame();
}
}
private void EndGame()
{
// We change the Scene to the mainMenu
SceneManager.LoadScene("mainManu");
}
}
```
//Script on all Boxes
public class Box : MonoBehaviour
{
public string Balltag = "ball";
//Audio Source the Audio Clip has to be assigned in the Unity editor
public AudioSource Coin;
private void OnCollisionEnter2D(Collision2D collision)
{
//Check it colliding Object has the right Tag
if(collision.transform.tag == Balltag)
{
//Get the reference to the Player Script
PlayerBoxDestroyManager PBDM = FindObjectOfType<PlayerBoxDestroyManager>();
//We can now access the Destroy Box Methode
PBDM.DestroyBox();
//Play the sound
Coin.Play();
//If we destroy our Object now, the Sound would also be deletet.
//We want to hear the sound, so we have to wait, till the sound is finished.
StartCoroutine(WaitTillAudioIsFinished());
}
}
IEnumerator WaitTillAudioIsFinished()
{
//we wait till the sound is finished
while (Coin.isPlaying)
{
yield return null;
}
//if finished, we destroy the Gameobject
Destroy(gameObject);
}
}
I hope I helped you. If you have questions, feel free to ask.
And sorry for my English:)
Im making my first 2D game in unity and I have successfully made it. However I want a start button.I have looked up videos on how to make a start button and they all seem to have a start button on a different screen and once clicked it goes to another scene. Where as I would like my button to be on the same screen as my game and once clicked it starts the game, while having the button disappear. Can the code also be in c# thank you.
If you don't want to have different scenes (which, by the way, it's the recommended pattern) you can pause the game until the player presses the button. More informations can be found here.
using UnityEngine;
using UnityEngine.UI;
class GameManager: MonoBehaviour
{
public Button startButton;
private void Awake()
{
Time.timeScale = 0f;
}
private void OnEnable()
{
startButton.onClick.AddListener(StartGame);
}
private void OnDisable()
{
startButton.onClick.RemoveListener(StartGame);
}
private void StartGame()
{
Time.timeScale = 1f;
// Hides the button
startButton.gameObject.SetActive(false);
}
}
Put your button on your main scene that's it.
I created a dot following my mouse around in 2D and I created a cube object changing position on x and y. Now when I point my mouse to cube, it deactivates I set that, and now I want to activate it again. I try on trigger exit, but it didn't work.
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
StartCoroutine(spawnEnemyTime());
}
private void RandomSpawnObject()
{
tapObject.transform.position = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
}
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.SetActive(false);
}
IEnumerator spawnEnemyTime()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
}
Once inactive the scripts on that object are not executed anymore => messages like OnTriggerExit are not called/executed.
One solution is to simply wrap the target object in a parent object and attach your script to the parent instead but make it (de)activate the child.
So the parent stays active and receives the message.
I am just going to repeat what everyone else here said:
A inactive object in Unity is truly inactive, meaning it does not receive any updates, can't collide with stuff and all the MonoBehaviour stuff that usually calls your code does not work either. You have to manually re-activate the object using a reference that you cached somewhere.
But, instead of just flat out disabling the whole object you could disable the components that you don't want to be active.
Example:
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = true;
}
This only deactivates your renderer component but leaves everything else as it is. So your object can still collide and it's still registered via e.g. OnTriggerExit.
Keep in mind that GetComponent<T>() is a pretty expensive operation so caching your component references is a good idea. The best solution would be to start out with a reference by creating a variable for it and assign it in the inspector.
Example:
//Set in inspector
public Renderer renderer
private void OnTriggerEnter2D(Collider2D collision)
{
renderer.enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
renderer.enabled = true;
}
When a GameObject is not active in Unity , you can't click it(no rendering,no colliding , nothing )
But ,You can create a hotkey (new script or in other script) , that can set it back to active , if it is not active.
public GameObject GO;
Use GO.setactive(true);
whereas gameobject is the object use to define the specific thing or object which needs to be active and the whole code needs to written in the method "spawnEnemyTime" so that it could be get active after the specific time period
You can just use an empty GameObject and get a reference the object that you want to enable/disable. If you get the reference before you disable it you will be able to activate it again.
the alternative is to do what TehMightyPotato said. Disable components it's actually the best way to solve this problem, but if you have lot's of components/subcomponents disable the gameobjects is faster.
I want to do menu animation. In the animator, its contain 2 animations. One is opening animation, the other is closing animation. I set the opening animation as default state and added condition between of them. The condition is a bool type parameter. I drag the script which controls they behaviours and animator component on gameobject, but when the opening animation plays and player clicks the play button the parameter turns into true but it doesn't play.
Animator canvasAnim;
public Button lvlSelector;
Button lvlSelector_A;
// Use this for initialization
void Start () {
canvasAnim = GetComponent<Animator>();
lvlSelector_A = lvlSelector.GetComponent<Button>();
lvlSelector_A.onClick.AddListener(LevelSelector);
}
IEnumerator SlideLevelSelectMenu()
{
yield return new WaitForSeconds(1f);
SceneManager.LoadScene("LevelSelectMenu");
}
void LevelSelector()
{
canvasAnim.SetBool("clickedclose", true);
StartCoroutine(SlideLevelSelectMenu());
}
Animator:
Gameobject:
This problem occurs because of your Animator. You should call Animator on Canvas game object (Not on Menu Manager). Just change your script to this:
void Start () {
canvasAnim = GameObject.Find("Canvas").GetComponent<Animator>();
...
}
I hope it helps you.