Unity - Panel is disabled and re-enabled immediately afterwards (C#) - c#

Just need a way to get around this problem. I know what the issue is, I just need a viable solution.
So basically I have a script called MouseManager, which detects if you're looking at a specific game object and if you press "f" it tells another script, called UIManager, to open the GameObject's panel (I have a game where you can press f on a console and it enables a UI with a puzzle).
Basically, I want the user to be able to press "f" or "esc" and have it exit the panel, however if you press "f", it disables the panel but instantly re-enables it as you're still looking at the gameobject, and both checks are in Update()
I need a workaround, if anyone knows one PLEASE comment as this issue is getting on my nerves :P
Thanks
EDIT: By console I mean there's a GameObject that looks like a console.
EDIT 2: Here's the code, I'll put some comments in it so you can understand what each thing does. I will be refactoring it soon because it's messy...
void Start()
{
uiPuzzles = GameObject.Find("PuzzlesUI").GetComponentInChildren<UIManager>(); // Currently I have the UIManager set to a canvas called PuzzlesUI. I will soon be moving it to an empty GameObject.
}
void Update()
{
if (!uiPuzzles.invActive && !uiPuzzles.puzzleActive) // invActive and puzzleActive are two public booleans that just show if the inventory or any puzzle panel is empty (UIManager handles opening and closing the inventory too)
{
if (Input.GetButtonDown("Use") && !uiPuzzles.invActive && !uiPuzzles.puzzleActive) // Use key is bound to "f". Currently Im just checking if puzzleActive and invActive are still false.
{
ray = GetComponentInChildren<Camera>().ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 7)) // Raycast where you're looking
{
uiPuzzles.openPuzzleOverlay(hit.collider.name); // function in UIManager which will check if the hit collider is in a list of all the puzzle colliders currently added and if it is, open that panel.
}
}
}
}
As for UIManager:
public void openPuzzleOverlay(string collider) // Get a panel name
{
foreach (Puzzle item in puzzleList)
{
item.panelGameObject.SetActive(false); // Disable all puzzle panels.
if (item.consoleGameObject.name == collider) // unless its the given panel,
{
item.panelGameObject.SetActive(true); // then enable it.
Cursor.lockState = CursorLockMode.None; // Unlock the mouse.
Cursor.visible = true; // Show the mouse.
DisablePlayerUI(); // Disable the UI (e.g. crosshair)
puzzleActive = true; // Disable movement. (Because player movement requires puzzleActive and invActive to also be false
}
}
}
void Update ()
{
if (Input.GetButtonDown("Use") && puzzleActive && !invActive && // If you want to use a console and the puzzle is already active
!puzzleList.Where(p => p.panelGameObject.activeSelf).FirstOrDefault().panelGameObject.GetComponentInChildren<InputField>().isFocused) // Check if the currently active panel's input field is not focused.
{
ClosePuzzleOverlay(); // Close the puzzle.
EnablePlayerUI(); // Enable the UI.
}
}
public void ClosePuzzleOverlay()
{
foreach (Puzzle item in puzzleList)
item.panelGameObject.SetActive(false);
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
puzzleActive = false;
}

The problem is being caused by the two input checks. When F is pressed both are true so the UI is being closed and then opened in the same frame.
Take the Input code out of UIManager and keep it in one place. Then you can rework your input class to be something like this:
//MouseManager Update
if(Input.GetButtonDown("Use")){
if(uiPuzzles.isActive)
uiPuzzles.Hide();
else
uiPuzzles.Show(GetPuzzleName());
}
Now it will only call either open or close on the frame which F is pressed down.

Related

I'm trying to make an animation that can always happen when clicking a button after entering a trigger

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);
}
}

Turn off toggle box and graphic when game starts

So this should be a simple answer but I cannot seem to find it anywhere. In Unity, I have a toggle box I want to turn off when the game starts, but unlike most game objects or text, SetActive does not work with toggle boxes. What is the command to turn the graphic off?
public void StartGame () {
mainText.SetActive (false);
startButton.SetActive (false);
StartCoroutine (SpawnBalls ());
//Turn off toggle box graphic
playing = true;
}
public void AddBall () {
if (ballBox.isOn) {
ballNumber = 3;
//Debug.Log ("yes");
} else {
ballNumber = 2;
}
If I understand you correctly you want to be able to remove the display of the toggle on the start of the game? If so set a reference to the toggle you want to click on and off heres a small example:
Toggle T = GameObject.Find("myToggle").GetComponent<Toggle>();
T.gameObject.SetActive(false);
Make a reference to it and you should be able to click it of and on as you need it tested it out on a game I'm working and it worked for me let me.

Changing sprite after mouse click on collider (working with many colliders and many different sprites)

I won't hide that I am new to the Unity and C#. I am trying to make mini escape game.
My problem: Changing sprites using colliders works only on one object. After clicking second object it works one time and then either the first and the second object don't work.
Description:
On the main screen I will have many items that are "clickable" and some that are "pickable". I created 2 scripts- one that close up to the clicked item, and second that return to the main view.
Main view looks like that: they are 3 colliders and each one close-up to different view. Colliders are the children of the Background. After close-up I don't want the child colliders of background to be working. Only the collider of the close-up should work.
So the question: Am I doing anything wrong? Is there any better method to change sprites after mouse click?
My code:
First script:
public class GetCloser : MonoBehaviour // shows close-up of clicked object
{
public GameObject Actual, Background;
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit.collider != null)
{
Background.SetActive(false);
Actual.GetComponent<Collider2D>().enabled = true;
Actual.GetComponent<SpriteRenderer>().enabled = true;
}
}
}
Second script:
public class ReturnTo : MonoBehaviour //hide the close-up image and return to the background
{
public GameObject Actual, Background;
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit.collider != null)
{
Background.SetActive(true);
Actual.GetComponent<Collider2D>().enabled = false;
Actual.GetComponent<SpriteRenderer>().enabled = false;
}
}
}
Last script:
public class PickUp : MonoBehaviour { //hide clicked object- works every time
// Use this for initialization
void Start () {
gameObject.GetComponent<SpriteRenderer>().enabled = true;
}
// Update is called once per frame
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit == true && hit.collider != null)
{
hit.collider.gameObject.GetComponent<SpriteRenderer>().enabled = false;
Destroy(hit.collider);
}
}
}
So if the issue is the colliders for your main screen still being active after zoom that's simply adding in your first script "zoom script" deactivating the colliders for all background or even just simply turning the gameobjects in the background off, then when you zoom back out simply turn the objects back on or colliders back on depending on which you decided to turn off.
Wait I just reread and realize after you click the first object you are no longer able to get the other objects to react any longer. Looking over your code it is probably because you are destroying the collider in your third script therefore you are no longer able to "hit" the other objects to trigger the code of a collision.

How do I use animation events in Unity as conditions?

I'm trying to make a "player can't move while this animation is playing" check in my method for movement.
I have a 3x8 grid of panels I'm using for this application, with the player moving from panel to panel. I have 2 animations for this: MovingOut which plays when the player is moving away from a panel, and "MovingIn" which plays when the player is moving into a panel. So the flow I want is:
Player presses a movement key → movement is disabled → "MovingOut" plays → player's transform.position is moved to the target position → "MovingIn" plays → movement is re-enabled.
Each animation is only 4 frames. I currently have an animation event at the beginning of "MovingOut" which sets an int CanMove to 0, and another animation event at the end of "MovingIn" which sets CanMove to 1.
Here's what my code looks like so far:
public void Move(int CanMove)
{
//this lets me use panelManager to access methods in the PanelManager script.
panelManager = GameObject.FindObjectOfType(typeof(PanelManager)) as PanelManager;
animator = GetComponent<Animator>();
if (Input.GetAxisRaw("Horizontal") == 1 && CanMove == 1) //go right
{
movingToPanel += 1;
if (IsValidPanel(movingToPanel))
{
//play animation MovingOut
animator.Play("MovingOut");
transform.position = panelManager.GetPanelPos(onPanel + 1);
onPanel += 1;
}
else
{
movingToPanel -= 1;
}
}
//else if( ...the rest of the inputs for up/down/left are below.
}
I have MovingIn set up in the animator so that it plays at the end of the MovingOut animation, which is why I don't call it in the script:
I can't for the life of me figure out how to get CanMove passed into the method without being forced to define it upon calling the method. Currently I have Move(1); being called in my Update() method just to check if movement is working (other than this issue I'm having) and it works in the sense that I can move panel to panel, but since CanMove is being defined as 1 in the Update Function, the animation's events don't prevent the movement.
Any insight would be greatly appreciated!
You should change your design slightly. Remove canMove as a parameter being passed to Move and make it a field of the player's class. Then have one function to set canMove. Then have a separate function that allows you to move if canMove is true. Something like this:
private bool canMove = true;
public void SetMove(int setCanMove) // called with animation events
{
canMove = setCanMove == 1 ? true : false;
}
public void Move()
{
if (Input.GetAxisRaw("Horizontal") == 1 && canMove == true) //go right
{
//movement code and animation call...
}
// Other directions...
}
Then you can call the setMove funciton using your animation events and stop the player moving for the duration of them. i.e call setMove(false) at the start of the MovingOut animation and setMove(true) at the end of the MovingIn animation. This will stop canMove being set in your Update loop.
you can get the integer value from animator's GetInteger function which will return the integer value set to current animation.
if(this.GetComponent<Animator>().GetInteger(canmove)==0)
//move
else
//can't move

How to stop a if for a second before executing the rest of the commands

I been working on a script for a pause menu and i don't know how to make it stop before start executing the next line
This is the code, i ask for this because execute the "if" multiple times, because detects that i'm still pressing the Cancel button. (I use c# and work on Unity 5)
Thanks
using UnityEngine;
using System.Collections;
public class MenuPausa : MonoBehaviour {
public GameObject menuPausa;
private bool pausaMenu = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Cancel") || pausaMenu == false) {
menuPausa.SetActive (true);
Time.timeScale = 0;
WaitForSeconds(1);
pausaMenu = true;
} else {
menuPausa.SetActive (false);
Time.timeScale = 1;
Invoke("waiting",0.3f);
}
}
}
I would use a coroutine kinda like this:
bool paused = false;
void Update ()
{
if (paused) // exit method if paused
return;
if (Input.GetButtonDown("PauseButton"))
StartCoroutine(OnPause()); // pause the script here
}
// Loops until cancel button pressed
// Sets pause flag
IEnumerator OnPause ()
{
paused = true;
while (paused == true) // infinite loop while paused
{
if (Input.GetButtonDown("Cancel")) // until the cancel button is pressed
{
paused = false;
}
yield return null; // continue processing on next frame, processing will resume in this loop until the "Cancel" button is pressed
}
}
This only "pauses" and resumes this individual script. All the other scripts will continue executing their Update methods. To pause the frame rate independent methods (i.e. physics), set Time.timeScale = 0f when pausing and back to 1f when unpaused. If pausing all the other scripts is desired and they're dependent upon frame rate (i.e. Update methods) then use a global pause flag instead of the local paused variable used in this example and check to see if the flag is set in each Update method, like in this example.
I apologize if I'm misunderstanding this, but it seems to me like you're opening and closing a pause menu using the "Cancel" button, and that your script seems to be immediately closing it after opening it due to the "Cancel" button still being pressed. It also seems to me that this script is intended to be a component of the menu object that is being turned on and off. If that is the case, I would suggest the following:
Have this script be used on an object other than the menu itself (such as a MenuManager object or something - also I would change this script's name to MenuManger to avoid confusing it with the actual menu) that will remain active in the scene. Have a PauseMenu object in the scene that gets assigned to this MenuManager's "menuPausa" property. I would then remove the pausaMenu variable. Also, make sure this script is added as a component to ONLY the one object (the MenuManager), otherwise a second object might update in the same frame and turn the menu right back off.
At the end of the method, put
Thread.Sleep(1000);
It pauses the execution for 1000 miliseconds == 1 second.
Is this simple solution enough or do you need a more complex one with a Timer?
You can use a timer, insert it from the toolbar, change the interval which is in milliseconds, then once thats done use the scripts timer.Start(); and timer.Stop();
thats what i used.

Categories

Resources