hi guys I just started with unity, c # ... I created a script that should make a writing appear and disappear ... the problem is that once the writing appears, then it never reappears since I destroy the object " gameObject "and therefore can no longer be recreated ... How can I fix the code ??
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Com : MonoBehaviour
{
public GameObject uiObject ;
void Start(){
uiObject.SetActive(false);
}
void OnTriggerEnter(Collider player)
{
if(player.gameObject.tag=="Player"){
uiObject.SetActive(true);
StartCoroutine("WaitForSec");
}
}
IEnumerator WaitForSec(){
yield return new WaitForSeconds(5);
Destroy(uiObject);
Destroy(gameObject);
}
}
You need to understand first what is Object pooling, which is making a pool of object that are stored in memory, and then you access them when need one, this will save the hiccup of creating a new instance.
With regard to your code, to reuse the same game object again, you do not need to destroy it, destroy method means it will remove the object from memory. what you need to do is to use gameobject.SetActive(false);
when you need to reuse the same gameobject again, just use gameObject.SetActive(true);
Keep in mind you may need to reset some of its properties like its rotation or position OnEnable
Related
there is a problem with withdrawing the amount of money through Unity Event. Money is updated only for killing demons already on the stage at the time of the game launch, but not updated for killing demens created from the prefab. What could be the reason for this behavior? I attach the code and screenshots. Thank you in advance.
Demon
[SerializeField] public UnityEventInt onChangeMoney;
void DeathProcess()
{
onChangeMoney.Invoke(deathCost);
...
}
PlayerObjectScript
[SerializeField] public UnityEventInt onChangeMoney;
public int money;
public void ChangeMoney(int delta)
{
money += delta;
onChangeMoney.Invoke(money);
}
PlayerMoneyController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMoneyController : MonoBehaviour
{
public Text text;
void Start()
{
}
public void UpdateHandler(int money)
{
text.text = money.ToString();
}
void Update()
{
}
}
P.S. Sorry for my English, translated by a translator
What could be the reason for this behavior?
It's about references, when you set up your scene, your enemy can have a reference about your turret because both of them exist in the same scene, both are objects that will exist in runtime. But at the moment that you make your enemy a prefab, they will turn into an asset, and an asset can't (at least not without additional effort) know about runtime items, can't ref references to it because they don't have any guarantee that when you drag your enemy into scene your turret will exist and that this turret reference is the same turret that you have set when they exist in hierarchy only. This is the reason why only your objects that exist in the scene will keep the right behavior.
What you need to do is find a way to get turret reference, maybe store it in another place and find it in a setup phase when instantiating new enemies. You can also create an event calling layer that can be a scriptable object, and a scriptable object as prefabs are both assets, so they can safely reference each other. You can also change this event to code only and turn it into a static event. You have many approaches to solve it, good luck
I've tried a few ways of doing it. I've asked a few friends but, nothing seems to work so far. I have changed the script several times so I don't have the other ways that I have tried anymore. However, this is the one that gave me the least errors. Others I had to continuously change it to a recommended way by Unity but ended at a dead end and nothing worked.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LightActivator : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
gameObject.SetActive(false);
}
else if (Input.GetKeyDown(KeyCode.Space))
{
gameObject.SetActive(true);
}
}
}
I want to be able to turn on and off the ambient/directional light with just 1 button. Is that not possible?
two little flaws here
after you do
gameObject.SetActive(false);
this very same object is now inactive -> this component as well -> Update is no longer being called at all ;)
You always will ever only treat the first case .. the second one is never called at all since the condition will always match already with the first block!
Instead separate the logic from the target object and do e.g.
// This class should go to a different object that is always active in your scene
public class LightActivator : MonoBehaviour
{
// Here you reference the object you want to (de)activate via drag&drop in the Inspector
public GameObject theLight;
void Update()
{
// You only need one condition check
if (Input.GetKeyDown(KeyCode.Space))
{
// INVERT the active state of the light object whatever it currently is
theLight.SetActive(!theLight.activeSelf);
}
}
}
In order to keep things together you could e.g. simply make the according light object a child of the LightActivator object ;)
I'm studying C#'s Constructer and destructor with unity component system.
I'm sorry if the English of this question is weird. I used a translator cause I am not good at English.
The log output came out like this.
The Constructor log was displayed without pressing the play button. Why?
When I pressed the play button, I saw a log of something being created and immediately disappearing. I didn't write a code to create an object after the game started, where does this phrase run?
This is my code.
project working structure
Pressing the space bar brings the pre-made pre-fab to the game world,
and Prefab has a component that attached to test the constructor and
destructor.
CubeFactory.cs / when Press the space bar, It creates Prefab.
this component was attached to "GameObject" Gameobject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeFactory : MonoBehaviour
{
public GameObject obj;
private int pos = 1;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(obj,new Vector3(pos,0,0),Quaternion.identity);
pos++;
}
}
}
ClassTest.cs / Component for Testing Constructor and Destructor. It attached Cube prefab.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClassTest : MonoBehaviour
{
public ClassTest(){
Debug.Log("I was born!");
}
~ClassTest(){
Debug.Log("I Died x0x");
}
}
This is my project file. (I'm sorry to compress it up. I haven't learned how to use Git yet.)<
https://github.com/Scincy/UnityStudy
Please change your ClassTest.cs script to use simpler functions. Never use constructors and finalizers in Unity. What I understood from your logic is that when you press Spacebar, an object is instantiated. The ClassTest.cs is attached to this object. Better use:
void Start()
{
Debug.Log("I was born!"); //Will be called when the gameObject is active for the first time
}
Now you also want to know if the gameObject is destroyed, you can do this with another script attached to an always-active gameObject such as:
public GameObject cube;
void Update()
{
cube= GameObject.Find("Cube(Clone)"); //Depending on how your object is called
if(cube == null)
{
Debug.Log("Either not created or is destroyed");
}
}
I have used Update() which is called every frame. You could use a different function that you would like to call from another script only once.
Ye olde wisdom says that you should only use C# finalizers if your class contains a handle (IntPtr) to an object in unmanaged memory. If you're unclear what that means, then you probably don't need to be using finalizers; either way, you should never need to use them in a UnityEngine.Object-derived class.
Instead, prefer either of these Unity messages:
void OnDestroy()
{
// (1) called when the Object is about to be fully destroyed.
}
void OnDisable()
{
// (1) called when the Object is disabled in the Scene hierarchy.
// OR
// (2) called right before OnDestroy() would be called.
}
These Unity messages are of course available on MonoBehaviours, but also on all ScriptableObjects too! (For the latter, OnDisable essentially resolves to the same thing as OnDestroy since they are not Scene-bound Objects.)
Similarly, you should also avoid using C# constructors on MonoBehaviours and ScriptableObjects. The messages to prefer instead are:
void Awake()
void Start()
void OnEnable()
Each has its own quirks, so you may want to familiarize yourself with the differences and write some tests.
You can refer to the relevant documentation here: (see section header "Messages")
MonoBehaviour docs
ScriptableObject docs
im working on an demolition/simulator type game. im working on the basics but my scripts doesnt seem to be working. the idea of the script is, that if the objects encounters an "hard" enough force, it will get replaced by a destroyed version of that object. it works in on small structures. but when the structures get bigger instead of spawning 1 destroyed object, it seems to spawn waaaayy more. and thus the game starts to lag. I think it is becouse some objects hit other objects. when i destroy them by clicking it doesn't happen, only when they hit each other
hope anyone can help.
here's the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destructible : MonoBehaviour {
public GameObject debrisPrefab;
void OnCollisionEnter( Collision collision )
{
if (collision.relativeVelocity.magnitude > 3f)
{
Destroy(gameObject);
Destroy();
}
}
void OnMouseDown()
{
Destroy(gameObject);
Destroy();
}
void Destroy()
{
if (debrisPrefab)
{
Instantiate(debrisPrefab, transform.position, transform.rotation);
}
Destroy(gameObject);
}
}
Does you debris prefab contain other destructible object? Maybe the newly instantiated debris are all destroyed if the spawn and have a collision right away.
Another thing to check is how many elements can trigger the collision. If you have many colliders on your object, onCollisionEnter will be called several times. Also, are you sure you added your script only once to the object?
Finally, if this doesn't help, try using debug lines:
Debug.Log(name + " collision enter");
to print lines in the console with the name of the colliding object, to try to identify which object causes too many collisions
I have the following code:
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update() {
if (Input.GetKeyDown("w")) {
SpawnBall();
DestroyBall();
}
}
void SpawnBall()
{
Instantiate(ball, sp, transform.rotation);
}
void DestroyBall()
{
if (ball.transform.position.y >= -5.7f)
{
Destroy(ball);
}
}
and the code is supposed to generate a new ball every time when "w" is pressed, but for some reason it creates multiple clones and it crashes the engine. How can I create a single clone only?
And also the destroy method doesn't do anything, although it should remove the clone when it passes -5.7 on the y-axis.
Thanks in advance
Create a new script named "SpawnRigidbody" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnRigidbody : MonoBehaviour
{
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update()
{
if (Input.GetKeyDown("w")) {
SpawnBall();
}
}
void SpawnBall()
{
Debug.Log ("spawn");
GameObject go = Instantiate(ball, sp, transform.rotation).gameObject;
go.AddComponent<DestroyAfterPosition> ();
}
}
Now create another script named "DestroyAfterPosition" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyAfterPosition : MonoBehaviour
{
void Update ()
{
if (transform.position.y <= -5.7f)
{
Destroy(gameObject);
}
}
}
Create an empty game object and attach the SpawnRigidbody and then assign your ball in the inspector.
Hope this help you.
There are a couple problems with your Destroy() statement. First, it is destroying ball which is the Rigidbody2D. You will instead want to destroy the gameobject attached to the ball:
Destroy(ball.gameObject);
Second, you are trying to destroy the ball immediately after it is created, but only if y>=-5.7f. I think instead what you are looking to do is continuously check if the ball is above that point and destroy it if it is. Since you are creating multiple balls, they will all need their own check, which means you need to create a script for the ball prefab and in the Update() check its position and Destroy(gameObject) as necessary.
I think you may want to declare ball as a GameObject at the beginning of your script as well, so you are instantiating a gameobject and not just a Rigidbody2D:
public GameObject ball;
Object.Instantiate creates a clone of the provided object, so each time it is called a new object is created. MonoBehaviour.Update is called every frame, which means it's called at least 30 times per second (usually 60 or more on a PC).
So 30+ times per second, your code checks if the w key is held down, and, if so:
A clone of ball is created
Destroy the original ball if it is beyond -5.7 on the y-axis.
Thus, you're creating 30+ ball clones per second, and none will be deleted (because the DestroyBall method only looks at the original ball, not the clones).
If you want to work with the cloned object, assign the result of Instantiate to a field:
ballClone = Instantiate(ball, sp, transform.rotation);
Then you can check if ballClone exists and skip the SpawnBall call if the clone already exists. You can also use ballClone in the DestroyBall method.
ryemoss' answer seems like it's
also important. This answer is just based on looking at your code and the public Unity docs; I don't have a lot of experience with Unity.
Your destroy call is being called once, and only immediately after creating the object.
So it's currently doing this...
User clicks button...
Create ball...
Check to see if the ball is a certain height. It's not, so ignore...
Ball drops down based on gravity or whatever force compels it.
... and is never deleted.
If you want the ball to be destroyed when it reaches a certain point, either...
Have SpawnBall return the game object, which you store and check / delete later in Update,
...or...
Create a script that just checks the object's transform.position and blows up if it's where it needs to be. Attach that to the prefab of the ball you're creating a duplicate of.