I have a button (in UI), that I want to hide sometimes (but show again later), which means that it shouldn't show anymore and I shouldn't be able to click it. The only solution I found (that has actually managed to hide the button), is SetActive().
But in Update(), when I do SetActive(false) (the true/false is controlled with the variable wave_happening in my script), Update() doesn't run anymore, so I can't set it to true again.
When I do GameObject.Find("Start Button").SetActive(true) in another script, it just gives me a NullReferenceException (Object reference not set to an instance of an object).
This is my Update() function:
void Update() {
wave_happening = enemy_spawner_script.wave_happening;
Debug.Log(wave_happening);
transform.gameObject.SetActive(wave_happening);
}
Is there a solution to stop this problem, or another way to hide a button?
I'm fairly new to Unity and C#, so I don't know very much.
You can try disabling the button's rendering and functionality components:
GetComponent<Button>().enabled = false; // remove functionality
GetComponent<Image>().enabled = false; // remove rendering
Now, adding that to your Update function, plus a few changes for performance so you are not enabling/disabling every single frame, only when needed:
private bool isShowing = true; // or whatever your default value is
void Update() {
wave_happening = enemy_spawner_script.wave_happening;
Debug.Log(wave_happening);
if(wave_happening != isShowing) {
show(wave_happening);
isShowing = wave_happening;
}
}
void show(bool isShow) {
GetComponent<Button>().enabled = isShow; // remove functionality
GetComponent<Image>().enabled = isShow; // remove rendering
}
It's really difficult to do this well. Lots of issues arise:
Inevitably the button will be in a H or V layout group, and, as a basic software engineering issue of course you want it to work whether or not it is in a layout group, as that layout detail may be changed by your designers as the project goes on. For this reason it is a really good idea, as the OP initially guessed, to simply use .SetActive
But then you have the problem of the button being off so Update is not running. A simple solution is just to put the button in a wrapper. That is to say, simply in a UI Panel. Have the button manager script on that wrapper rather than on the button per se.
Then you just have a pretty Property on the manager script,
public bool Showme
{
etc...
and then you can just go ...
void Update()
{
Showme = enemy_spawner_script.wave_happening;
}
Can't get simpler looking code.
However, if you use a wrapper Panel. It is true that you have to be pretty expert at using the UI (particularly the auto sizing stuff) to make it work just the way you want. But, that's part of building Unity expertise unfortunately. :/
The concerns about performance is ... truly ridiculous. There's no difference between Unity's raw code "checking a boolean" and yourself "checking a boolean". However for sure as a matter of style it's crap to poll it in Update. (Note that in point "B" just above, if you're going to have heinously ugly "anti-polling" code, bury it in that Property.)
And then ...
"Trick" solution
A tip in Unity UI is you can add a CanvasGroup anywhere. Why would you do this? It allows you to FADE the whole thing, which, is often a quick solution to achieve what you want.
Hence ..
public CanvasGroup fader;
and then ..
void Update()
{
fader.alpha = enemy_spawner_script.wave_happening ? 1f : 0f;
}
and you're done!
Related
This current project has 3rd party controllers/cameras that I am converting over to the new Input system 1.0.1. I'd like to keep its functionality within the old controllers but gain the benefit of being able to switch action maps and such. Most of the controller code does their work in MonoBehavior Update.
I am currently struggling to find a solution to converting these:
if (Input.GetButton())
if (Input.GetButtonDown())
if (Input.GetButtonUp())
I have been referencing the migration documentation to try and find a solution found here: https://docs.unity3d.com/Packages/com.unity.inputsystem#1.0/manual/Migration.html , but I seem to be misunderstanding or missing something with the conversion for these specific controls.
For the button controls, they state that one should use the ButtonControl props, but I am not sure the proper way to get a hold on these controls. Here is an example of what I tried, at the top of the 3rd party controller Update, that was most promising:
var fire1Btn = _inputManager.PlayerInputs.Player.Fire1.activeControl as ButtonControl;
*_inputManager is a MonoBehavior object that stores my ref to the PlayerInputs object which is my : IInputActionCollection, IDisposable class which has the InputActionAsset, this is the .cs file generated automatically from the .inputactions file by Unity.
Then in the 3rd party controller code I replace the respective GetButtonDown GetButton GetButtonUp with this:
if (fire1Btn != null && fire1Btn.wasPressedThisFrame)
if (fire1Btn != null && fire1Btn.isPressed)
if (fire1Btn != null && fire1Btn.wasReleasedThisFrame)
This causes some issues though. .activeControl is null when actions are not firing and I don't think this behaves in the same way as the legacy controls. For example, when I go into the scene and start moving the player controller around, press forward, then release, the character continues to run forward, as if the value coming in from that control did not go back to 0 before the activeControl was nulled out again. There is also a .controls[] array on the object but this has a long list of controls that I could not easily distinguish which, if any, were the correct control.
*edit1: after testing it appears as though wasReleasedThisFrame on an activeControl is never true, which I guess makes sense if this control object is only ever active when its being used.
Is there a proper way to get a hold on these controls? Is this approach naive in some way? Any help or documentation on what to do here would be greatly appreciated.
Based on my reading, tests, and responses from various individuals, I am going to tentatively assume the answer to my original question is:
No, you cannot tap into an input control when its not active
Update 12/8/2020
Based on more recent findings I found that .activeControl will run into issues on composites and I found a better way to tackle this issue. .wasReleasedThisFrame continues to be troublesome though. This newer solution uses an extension method to check press states, with only released state requiring some kind of tracking on previous frame state of a button. Here are the better ways to check state, though the wasReleasedThisFrame method did not work correctly:
inputAction.ReadValue<float>() > 0f; // for isPressed state
inputAction.triggered && inputAction.ReadValue<float>() > 0f; // for wasPressedThisFrame
inputAction.triggered && inputAction.ReadValue<float>() == 0f; // for wasReleasedThisFrame ** Does not function properly **
As for .wasReleasedThisFrame you'll have to implement some kind of tracking of previous frame similar to the one I did below. I ended up using a dictionary on my input manager class to track the states, then passed it into the static extension method on checks.
public static bool WasReleasedThisFrame(this InputAction inputAction, Dictionary<string, ButtonControlMask> maskStates)
{
return maskStates.ContainsKey(inputAction.name) && maskStates[inputAction.name].wasReleasedThisFrame;
}
Bear in mind that if you check these values in some other mono-behavior update method (like how I do mine in my manager class), you could run into ScriptOrder issues unless you are checking the values in LateUpdate() like I am.
I'm still on the hunting for a better solution for the .wasReleasedThisFrame problem.
This is the my previous solution: Based on my original code from the question and specifically assuming you have the correct entries in your InputActionAsset, generated or created the c# input class from the asset file, instantiated the input class, and have reference to it, something like this should work:
public class ButtonControlMask
{
public bool wasPressedThisFrame { get; set; }
public bool isPressed { get; set; }
public bool wasReleasedThisFrame { get; set; }
}
private bool _fire1BtnWasActiveLastFrame = false;
private void LateUpdate() // works in Update as well
{
// set our controls
dynamic fire1Btn = _inputManager.PlayerInputs.Player.Fire1.activeControl as ButtonControl;
if (fire1Btn == null)
{
// never let our control be null, use mask and dynamics to prevent having to check for null
fire1Btn = new ButtonControlMask()
{
// _fire1BtnWasActiveLastFrame will check out last frames state
wasPressedThisFrame = false,
isPressed = false,
wasReleasedThisFrame = _fire1BtnWasActiveLastFrame
};
// if it was active, and is null now, we can now reset our flag
if (_fire1BtnWasActiveLastFrame) _fire1BtnWasActiveLastFrame = false;
}
else
{
// we have an active control, set our active last frame flag
_fire1BtnWasActiveLastFrame = true;
}
// migrated code below ---
if (fire1Btn.wasPressedThisFrame) // This replaces Input.GetButtonDown()
if (fire1Btn.isPressed) // This replaces Input.GetButton()
if (fire1Btn.wasReleasedThisFrame) // This replaces Input.GetButtonUp()
}
Just using the .activeControl we have values for our .wasPressedThisFrame and .isPressed, however, .wasReleasedThisFrame is unknown to us as the control goes null the instant its not being used. To go around this we store the previous frames state in the _fire1BtnWasActiveLastFrame variable based on if the control was null or not. Then in the next frame, if the control is null, _fire1BtnWasActiveLastFrame will inform us that .wasReleasedThisFrame should be true on our masked class for ButtonControl only for this frame.
It's not pretty but this should allow for clean migration of Input.GetButtonDown Input.GetButton Input.GetButtonUp within your legacy code without having to change or check logic.
I update UI-Components (espcially Text) regularly in my game. My code looks like this:
private void ShowScore(string newScore) {
var scoreText=Find("Score").GetComponent<Text>();
if (scoreText.text!=newScore) scoreText.text=newScore;
}
My idea behind this is to only update the textbox when the value has really changed and so preventing unneccessary updates that might cause performance issues and/or bad UI experience like "flickering" and so on.
Now my question is: Is that even necessary or does Unity itself already the same validation internally?
Since the source code of Unity's UI system is open-source, you can actually look this up. Regarding your question, the specific code is here: https://bitbucket.org/Unity-Technologies/ui/src/31cbc456efd5ed74cba398ec1a101a31f66716db/UnityEngine.UI/UI/Core/Text.cs#lines-212
The relevant part of the setter is
else if (m_Text != value)
{
m_Text = value;
SetVerticesDirty();
SetLayoutDirty();
}
which, as you suspected, makes sure that the element will only be marked as "dirty" (requiring a visual update) if the new text is different from the one it had before
Original question on the Unityforums here
I've been trying to get an animation to not only slow down and speed up, but also play backwards depending on user input for my Hololens-application. I am using the Mecanim system, not legacy animations.
The whole thing is supposed to happen at runtime, through dynamic user input.
I know that it's possible through scripting, as I had it working before I lost local progress and some files during some Unity-Collaborate issues. As stupid as it sounds, since then I have not been able to remember what I did different from my current approach.
Right now I'm manipulating the value Animator.speed, but that only works for values >= 0.
Any help would be greatly appreciated!
Edit: In case the link is not working or visible for anybody, here is my code:
private Animator anim;
//...
anim = gameObject.GetComponent<Animator>();
//...
public void OnManipulationUpdated(ManipulationEventData eventData)
{
if (anim.isActiveAndEnabled)
{
anim.speed = eventData.CumulativeDelta.x;
anim.Play("KA_Cover_Anim");
return;
}
//...
}
Edit2: Incorrectly marked as dupicate! The linked question does not regard a similar problem and required a different solution
Edit3: For clarification, the linked "duplicate" uses the legacy animation system which is irrelevant for my question. In Mecanim, the new animation system in Unity 5.xx, you can not access the Animations directly as shown in the selected answer. Neither is it possible to alter the animation speed as shown in in the second answer.
I'm not exactly sure what you're end goal is, but you can play animations backwards and at different speeds by using a parameter.
On the animation state, you can make it watch a parameter and multiply it against the default speed of the animation. All you need to do in code is something like
animator.setFloat("Speed",-1.0f);
Hope that helps.
I need to rework a UI created using the old UI.
I can't change the architecture of the program. The program uses a lot of static variables and my UI need to access them and edit them (i know static variables are quite bad but can't rework the whole architecture like i said).
Currently i'm doing something like this :
public void SetBool()
{
UIData.SomeBool = SomeToggle.isOn ;
}
public void SetAnotherBool()
{
UIData.SomeOtherBool = SomeOtherToggle.isOn ;
}
...
So i have a function for each static variable i want to edit associated with a specific toggle, this is ugly. How can i create a generic function to edit bool using toggle and slider to edit float values.
I thought about something like this :
public void SetBool(bool boolToSet)
{
boolToSet = gameObject.GetComponent<Toggle>.isOn;
}
And then add listener to the toggle for the event OnValueChanged and this function with the bool i want to change as parameter. But I have heard that calling GetComponent is bad for the performance.
I wanted to know if u had any better idea. Maybe i'm missing something important i'm quite to new to unity and c#.
Thanks
Sorry for my poor english.
GetComponent performance isn't that bad as it's just searching a list on a GameObject. It's just not advisable to call it every frame in many places without caching. For a user initiated UI action it will have basically no performance impact. FindObjectOfType on the other hand searches the whole hierarchy so is quite slow, and GetComponentsInChildren may be slow if the branch of the hierarchy you are searching is large.
Still it is good practice to cache your own component references in Awake(). I do internal state configuration in Awake and any external connections and dependencies in Start to make sure an object is properly initialized before speaking to the outside world.
If you want to pass in a Value type and change the value you need to pass by reference:
private Toggle toggle;
public void Awake() {
toggle = GetComponent<Toggle>();
}
public void SetBool(ref bool boolToSet) {
if (toggle != null) {
boolToSet = gameObject.GetComponent<Toggle>.isOn;
}
}
Then from wherever you call SetBool:
SetBool(ref myBoolField);
If you can guarantee a Toggle component exists you can remove the null check from SetBool. RequireComponent can help with this but it only stops you assigning something in the inspector... if code changes or you construct via script I don't think it does anything.
Is there any function to freeze all the game or a certain class for a moment?
I'm searching for a Wait function like in Matlab.
Thanks.
EDIT: The wait function stops all the processing for a given time.
A one-line hack would be to call Thread.Sleep(x) in the main Update(), but that'll hang the game for the specified amount of time. The user will think your app has become unresponsive and he might kill it and restart it! It's probably not what you want.
The way I architecture games, each major component has its own Update() (or Tick()) method that is called on each logical update. You can selectively freeze components by simply not calling their Update() for a while. There are things you certainly never want to freeze, like refreshing the screen, responding to user input or sound processing.
Some example code:
// To pause the game for x number of frames, set pauseDelay = to x
int pauseDelay;
public void Update() {
if (pauseDelay > 0) {
--pauseDelay;
}
else {
physics.Update();
ai.Update();
}
input.Update();
sound.Update();
}
If you've based your game on GameComponents you can usually just set the Enabled flag to false which prevents the update logic being executed, this obviously depends on other factors (Object manipulation outside of the classes update logic, update logic being performed in the draw or other method ect..)
public class Car : GameComponent
{
// This logic is only called if the Enabled property is set to true.
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
}
If you've not used game component then it would be quite simple to introduce an Enabled flag to your clases and check for that before executing update logic.
If your looking for a very simple way (Andy's seems better, but this is quick way)
In your update Method a class, add at the top:
if (isPaused)
break;
You can use isActive to check is the window is active too.