I want to check if object is active and if it is I want to inactivate other objects. Here is my method. It is not the best one but I just want it to work. This method I have doesn't work right because it only runs once even though I have it in update(). Need some help here...
update: the problem is that when I choose the selection that selection becomes true and because another selection is already true this code is not working and right now I don't know how I can fix it.
public GameObject selectionTop, selectionBot, selectionSplash, selectionFloor;
private void Update() => Check();
public void Check()
{
if (selectionTop.activeSelf)
{
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionBot.activeSelf)
{
selectionTop.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionSplash.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionFloor.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
}
}
Update only gets called when the GameObject its script is on is activeInHierarchy and the script is enabled. My guess is that you have this script on one of the GameObjects that is being disabled and that is causing update not to get called.
So I would move this script to another game object that is not used one of the "selection" game objects so that update will always be called.
Use a variable to store what the current selection is.
Then when a new selection is made, hide the current selection, then call check.
or -
Make a function that hides all the items. Call that each time. Then set the items you want active.
I'm not sure if this is a silly answer, I've never worked with the game sdk but just looking at the code...
the
if(...)
{
}
else if (...)
{
}
I've always seen it written this way, but maybe it's just me, you can check if your code is executed both using the debugger and if it is not, you can use the Debug.Writeline to let something be printed inside the routines on your debug screen to see which code is executed.
Related
I'm doing a mechanic that if you shoot at a specific object it adds ammo, but I tried a lot of things and ammo is not being added when it collides with the object.
Here is the code:
public GlobalAmmo ammo;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.transform.tag == "Enemy")
{
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Ammo"))
{
ammo.ammo += 3;
}
}
Edit: sorry!
I already put this code and I put in the images that the "if" is working, so I don't understand why this is not working because the ammo decrease correctly.
I think it must be a very silly mistake, I'm actually doing this project to test everything I'm learning
if (collision.transform.tag == "Ammo")
{
print("Hit!");
ammo.ammo += 3;
}
Does the shooting itself work?
Try to write:
if (collision.gameObject.CompareTag("Ammo"))
{
Debug.Log("Work");
ammo.ammo += 3;
}
If you do not write "Work" on the console, then you have an error somewhere else.
Since your debugging shows that the if is properly executed, I see one main possibility (an easy mistake to make when not used to Unity).
Is that a script you dragged-dropped over 2 different game objects?
You are probably not modifying the proper instance of the GlobalAmmo class (script) . There might be two (or more) GlobalAmmo.
Instead you should refer to only one instance, which is the "source of truth".
For instance, during Init the public GlobalAmmo ammo; should be initialized by fetching the proper script instance if it is on another object etc.
As per your comments, it seems you figured it out! Feel free to add more details if this is not the case.
I am trying to create a situation where I can take "hold 'E' to interact" type of input. My first thoughts are, start a timer after the button has been held down. After the timer reaches 700 ms, take the action. But the problem, is I don't know how to write that in c# since I am new in this language. Any other approach is also appreciated.
I am adding pseudocode here
//pseudocode
//inside the update function
if (e is pressed)
{
start timer;
if (timer.time == 700)
{
Debug.Log("E is pressed for quite some time");
}
}
What I would do is use a coroutine.
When the user presses down the button, the coroutine would start and if the user stops pressing the button the coroutine would stop.
To do this, your code could look something like:
using System.Collections;
[SerializeField] int time;
void Update ()
{
if (Input.GetKeyDown(KeyCode.E)) StartCoroutine(Action());
if (Input.GetKeyUp(KeyCode.E)) StopCoroutine(Action());
}
IEnumerator Action ()
{
yield return new WaitForSeconds(time);
// do stuff
}
Thanks to derHugo for spotting an error in my example code - its now been edited.
Of course there are other ways to go about what you want to do, but I'm thinking a coroutine is your best option because it will then be super easy to add some feedback to the user when they are holding down, in your case, 'E'. For example, you could fill an image to create an effect similar to this:
And, well, even if you don't want to do that - coroutines are still a good option.
Hope this helps!
I am instantiating many buttons that when clicked need to call a function (via listener). But I'm also very often destroying them. Are these listeners then also destroyed or do I need to delete them otherwise?
Example:
public void makeButton()
{
GameObject spawnedButton = Instantiate(prefabButton, prefabButton.transform.position, prefabButton.transform.rotation) as GameObject;
spawnedButton.GetComponent<Button>().onClick.AddListener(()=>
{
listedButtonClicked(someOtherObjectThatWillNotBeDeleted, spawnedButton);
});
}
public void listedButtonClicked(GameObject target, GameObject button)
{
Debug.Log(target);
Debug.Log(button);
}
...so when spawnedButton is destroyed, will this listener remain? I'm instantiating and deleting a huge amount of buttons so it could be relevant for me.
Ok this is going to get a bit deep.
Consider this:
Button button;
void Start()
{
button.onClick.AddListener(Method);
}
void Method()
{
print("Hey");
}
This goes just fine, your Button component gets a "link" to Method so when the onClick is triggered, it jumps to the address of Method and runs the code from there.
Second scenario:
Button button;
void Start()
{
button.onClick.AddListener(Method);
Destroy(this);
}
void Method()
{
print("Hey");
}
Notice I destroy the currentcomponent, run this and trigger the onClick and no issue(??!!), it prints fine.
Third scenario:
Button button;
string str = "Hey there";
void Start()
{
button.onClick.AddListener(Method);
Destroy(this);
}
void Method()
{
print(this.str);
}
It crashes. And now for the explanation. A method is ALWAYS static, the compiler (or the creators) is smart enough to consider that there is no need for each instance to have their own method but instead to have a shared method template to which the instance can pass itself.
Methods are as such:
void ClassName.MethodName(this ClassName);
The this parameter is moved to the front when using instance call and is made non-compulsory. That this parameter is actually available within the method as you can use it. Again, it is not compulsory.
So, in the first case, it works though the Script did not exist anymore, this is because there was no usage of any instance member. In the last example, str is used and since the object is no longer, it throws null reference exception.
Considering the other way around.
If the creating script remains and the Button game object or component is being destroyed, then you have no risk at all. It would be wiser to clear the onClick event but since it will be destroyed, other way around. Your spawner loses any knowledge of the Button at the next iteration of the loop that is creating all the button:
for(i=0;i<10;i++)
{
GameObject go = null; // new reference added on method stack
go = Instantiate<GameObject>(btnPrefab); // new instance added to reference
} // go connection is lost right here, a new go added to stack in next iteration
I found your answer here to be more precise
I could be misunderstanding the wording of the docs but AddListener is meant to add a non.persistent listener.
But, the following shows a weird behaviour (at least until I get it explained):
private float myFloat = 10f;
[SerializeField] private Button button = null;
void Start( )
{
button.onClick.AddListener(MyMethod);
DestroyImmediate(this.gameObject);
}
public void MyMethod()
{
Debug.Log("Call " + this.myFloat);
this.myFloat ++;
}
the best part being that the object is surely gone, the this.myFloat still gets printed AND increased, and I don't have any persistent listener.
If I remove in Destroy then all goes fine. It seems the AddListener is creating a link to the object in memory and even triggering the GC will not collect the object. Using a weak reference would probably bring it back to life I am thinking.
What is even better is that if I create a reference to my script above and call for the myFloat value, I get it??!! Despite the inspector showing Missing(Type)??!!...
This tells me that we have here a memory leak if the programmer is not removing the listener manually, until I am proven wrong. obviously.
so.... yes and no. According to documentation it is destroyed, the inspector says its destroyed, but it is still in memory... and can be called.
edit there is a RemoveListener()method but it seems to do nothing but slow everything down, and i've seen it throw some strange errors. unless your really worried about performance, expect unity to do its best at removing them onDestroy() and don't worry about it to much...
What I am trying to do
I am trying to have one object with a serialized int that will spawn the number of objects I specify. When I change the number to a lower number, it should obviously delete and destroy the previous ones created.
This is causing an issue, however, do to the way Update works with ExecuteInEditMode, in that it gets executed whenever the scene gets changed, making this much harder.
What I've tried
[ExecuteInEditMode]
public class Floor : MonoBehaviour
{
[SerializeField] GameObject gameObject;
[SerializeField] bool destroy;
private void Update()
{
if (destroy)
{
destroy = !destroy;
gameObject.Destroy();
}
}
}
Unreal Engine used this pattern a lot of having a visible bool that turns itself off and behaves more like a button in the editor, which came in handy. This would allow me to press destroy and destroy the object. Obviously, without the check, it would destroy gameObject, and Update is called again, it will destroy it again and so on.
I thought by added this boolean check, when Update was called again, it wouldn't do anything so the loop would stop, but nope, it continues and gets me an infinite loop.
I've also tried using a Corroutine, but that didn't fix anything.
Is there any way to delete an object in ExecuteInEditMode without causing an infinite loop? Maybe there's a better method than Update that doesn't update when anything in the scene changes?
Try
GameObject.DestroyImmediate(gameObject);
in Edit mode: https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html
Use DestroyImmediate() in edit mode since Destroy() is delayed(delayed deconstruction will never be executed in edit mode).
unity docs: https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html
I have a seperate script of time which I use to show time in my scene. It contains hour and minute and seconds variable.I want to do some specified work e.g., code execution on specified time and currently i am doing something like this. in Update. I am running a function which check continously check time variable in order to run an animation.
void Update()
{
checkTrainArriveTime();
}
void checkTrainArriveTime()
{
if (timeManager.GetComponent<Scale2>().hour == trainArriveTimeHour && timeManager.GetComponent<Scale2>().min == trainArriveTimeMin
&& isTrainArriveConditionExecute)
{
isTrainArriveConditionExecute = false;
PlayAnimationClip("Start");
}
else if (timeManager.GetComponent<Scale2>().min != trainArriveTimeMin)
{
isTrainArriveConditionExecute = true;
}
}
As Time will match this function will play the animation. Now I have 50 script attached to 50 different game Object. It is
working fine but It definitely not the right way to use Update Event. In my code, It is necessary to check time on every frame and
extra load on update. Is there any efficient way to do this Job?.
I can see your struggle. You are right, it is definitely not the best way forward.
The best option I can see here would be creating Animation Manager which is a singleton instance (there is only one instance allowed per application).
I would suggest moving your animation triggering logic to an Update method of AnimationManager.
Once you have done that. You will be able to access its instance calling AnimationManager.getInstance() method.
Next step is creating internal registry that would be nothing else than just a list of your registered game objects that you want to trigger animation for.
I don't know what exactly is your timeManager but I can imagine it is probably an instance of TimeManager controller that you drag and drop onto your public timeManager property. Consider turning it into singleton as well or at least moving assignment of timeManager.GetComponent<Scale2>() into Awake() method.
It is important to not to call GetComponent() method from inside of Update()', as it has an impact on performance.GetComponent` is quite expensive to call.
Hope it helps.