- Home /
Need to combine 2 scripts into 1 and lose a Static variable ? (Solved)
OK.. I had two scripts running nicely, one instantiated an object and threw it into the air every x seconds, the other destroyed the object ready to instantiate a new one.
instantiate script-
using UnityEngine;
using System.Collections;
public class LavaThrower : MonoBehaviour
{
public Transform lavaPoint;
public Rigidbody lavaBallPrefab;
public static int ballNumbers = 0;
public int throwPower;
void Update()
{
if(ballNumbers <=0)
{
ThrowTheLavaBall();
}
}
void ThrowTheLavaBall()
{
ballNumbers += 1;
Rigidbody lavaBall;
lavaBall = Instantiate(lavaBallPrefab, lavaPoint.position, lavaPoint.rotation) as Rigidbody;
lavaBall.AddForce(lavaPoint.up * throwPower);
}
}
destroy script -
using UnityEngine;
using System.Collections;
public class DestroyLavaBall : MonoBehaviour
{
void Awake()
{
StartCoroutine(DestroyTheLavaBall());
}
IEnumerator DestroyTheLavaBall()
{
yield return new WaitForSeconds(6.5f);
LavaThrower.ballNumbers -= 1;
Destroy(gameObject); // Destroy Self
}
}
All was going well until I placed another spawner into my scene and then realised that because I'd used a static variable one script was overwriting the other.
I think (please correct me if wrong) that I'd be better off with one script per spawner that was handling the instantiate AND the destroy. Therefor removing the need for any static variables.
And that's where my problem is. I've tried a few approaches (and to be honest) I'm screwing it up more than I'm making progress. So I'm looking for some suggestions as to how I write/structure the script.
As usual any help is greatly appreciated.
Answer by Digital-Phantom · Feb 28, 2015 at 11:57 AM
OK sorted. Now each spawner works independently of any others and I can set the spawn/launch times for each spawner.
here's the script in case anybody else ever needs to do something similar -
using UnityEngine;
using System.Collections;
public class LavaThrower : MonoBehaviour
{
public Transform lavaPoint;
public Rigidbody lavaBallPrefab;
private int ballNumbers = 0;
public float waitTime = 6.5f;
public int throwPower;
void Update()
{
if(ballNumbers <=0)
{
ThrowTheLavaBall();
}
}
void ThrowTheLavaBall()
{
ballNumbers += 1;
Rigidbody lavaBall;
lavaBall = Instantiate(lavaBallPrefab, lavaPoint.position, lavaPoint.rotation) as Rigidbody;
lavaBall.AddForce(lavaPoint.up * throwPower);
StartCoroutine(DestroyTheLavaBallRoutine());
}
IEnumerator DestroyTheLavaBallRoutine()
{
yield return new WaitForSeconds(waitTime);
ballNumbers -= 1;
}
}
Big thanks to @BoredMormon and @maccabbe Couldn't have figured it out with the help and input from you guys. Appreciate the help from both of you.
:)
Answer by Kiwasi · Feb 28, 2015 at 09:32 AM
And now you know why static is evil. :)
Keep two scripts. But give the lavaball a reference back to its spawner. Something like this.
Remove the static
Add at line 26 of the first script
lavaBall.GetComponent<DestroyLavaBall>().lavaThrower = this;
At the top of the second script
public LavaThrower lavaThrower;
Chane line 16 of the second script to
lavaThrower.ballNumbers--;
Note that this is not the best way to do it. You'll discover the principle of encapsulation later, and realise that this could still be made better. You are welcome to google the term to avoid heart ache. Or steam ahead and post back here when you come across the problem yourself. (Insert evil grin here)
That depends on which problem you are referring?
I now get the error message -
UnassignedReferenceException: The variable lavaPoint of LavaThrower has not been assigned. You probably need to assign the lavaPoint variable of the LavaThrower script in the inspector. UnityEngine.Transform.get_position () (at C:/BuildAgent/work/d63dfc6385190b60/artifacts/EditorGenerated/UnityEngineTransform.cs:28) LavaThrower.ThrowTheLavaBall () (at Assets/LavaThrower.cs:25) LavaThrower.Update () (at Assets/LavaThrower.cs:17)
Which is confusing as I definitely 'have' assigned it. And for good measure I've tried assigning from the hierarchy, from the inspector drop down and directly from my prefabs list
???
You said you had 2 spawners. Are both of their lava points assigned?
yes both were assigned. Even changed names in hierarchy with added number to make sure I'm assigning the correct one to the correct place in inspector.
Nope, that should not have occurred. Try a Debug.Log(lavaPoint) to see if it is null everywhere.
Do you $$anonymous$$d posting the scripts again, with the changes implemented? Sometimes things get lost in translation.
Try adding checks around lines 25&26 to see where the error is
if(lavaPoint!=null){
lavaBall = Instantiate(lavaBallPrefab, lavaPoint.position, lavaPoint.rotation) as Rigidbody;
lavaBall.AddForce(lavaPoint.up * throwPower);
}
else{
Debug.Log(gameObject.name);
}
Although it would be easier and you would avoid this bug by using the transform of the gameObject LavaThrower is assigned to ins$$anonymous$$d of a third transform, i.e..
lavaBall = Instantiate(lavaBallPrefab, transform.position, transform.rotation) as Rigidbody;
lavaBall.AddForce(transform.up * throwPower);
Answer by maccabbe · Feb 28, 2015 at 09:51 AM
You're right that you need to stop using static for ball numbers if you want multiple spawners but whether you want to use one or two scripts is basically a design choice.
If you want to combine the scripts then you could add the following to LavaThrower
public void DestroyTheLavaBall(){
StartCoroutine(DestroyTheLavaBallRoutine());
}
IEnumerator DestroyTheLavaBallRoutine()
{
yield return new WaitForSeconds(6.5f);
ballNumbers -= 1;
Destroy(gameObject); // Destroy Self
}
Then since the new DestroyTheLavaBall() method is public you can run it instead of creating new DestroyLavaBall objects. For instance
public class Controller{
public GameObject lavaThrowerObj;
void Start(){
lavaThrowerObj.GetComponent<LavaThrower>().DestroyLavaBall();
}
}
You could also have two separate scripts. For instance you can use the following.
public class DestroyLavaBall : MonoBehaviour {
void Awake() {
StartCoroutine(DestroyTheLavaBall());
}
IEnumerator DestroyTheLavaBall() {
yield return new WaitForSeconds(6.5f);
gameObject.GetComponent<LavaThrower>().ballNumbers-=1;
Destroy(this); // Destroy Self
}
}
Then you would use it something like
public class Controller {
public GameObject lavaThrowerObj;
void Start() {
lavaThrowerObj.AddComponent<DestroyLavaBall>();
}
}
all gone weird now. Its spawning from both spawners in rapid fire, damn lava balls everywhere.
getting the following messages -
LavaThrower1 (UnityEngine.Transform) UnityEngine.Debug:Log(Object) LavaThrower:Update() (at Assets/LavaThrower.cs:15)
LavaThrower2 (UnityEngine.Transform) UnityEngine.Debug:Log(Object) LavaThrower:Update() (at Assets/LavaThrower.cs:15)
null UnityEngine.Debug:Log(Object) LavaThrower:Update() (at Assets/LavaThrower.cs:15)
NullReferenceException: Object reference not set to an instance of an object DestroyLavaBall+c__Iterator1.$$anonymous$$oveNext () (at Assets/Scripts/DestroyLavaBall.cs:18)
NullReferenceException: Object reference not set to an instance of an object DestroyLavaBall+c__Iterator1.$$anonymous$$oveNext () (at Assets/Scripts/DestroyLavaBall.cs:18)
Then it crashed.
And the LavaThrowers are simple empty game objects with just a single script added to them (not parents/children or anything extra about them)
???
$$anonymous$$aybe I'm over simplifying it but wouldn't I just be better making a simple script that spawns a lavaBall every 'x' seconds. $$anonymous$$ake the time between spawns a public variable.
(although that would mean I'd need an individual ball prefab for each spawner) other wise I couldn't have individual destroy times, right?
Your answer
Follow this Question
Related Questions
Yeild and WaitForSeconds and Instantiate 1 Answer
instatiate gameobject after 1 second 1 Answer
Instantiate an object as soon as another object is destroyed 2 Answers
how to instantiate object with content prefab and not him self 2 Answers
Check if object is destroyed on level load, if so instantiate prefab? 1 Answer