I am having real problems with placement of scripts now my project is becoming much larger, and wondered if any of the devs that have produced complex games would be willing to share their advice on the matter.
I come from the C++ world where an object contains all the code it requires as a single entity. Calling this entity does not require having to setup a reference first. The flexibility of Flash and script placement was one of the reasons I could never get to grips with it for anything complex.
My issue is Unity is that some of my complex objects contain multiple scripts. And they may often need to call other scripts on other objects that are equally as complex.
For example, I have a Spaceship object with a Controls script and a Collision script. I have a UI Manager object that contains sprites for my HUD like fuel left, laser recharge timer. I have to code into my Controls script a call to my HUD script to reduce the fuel when thrusting. I have to check when the laser is charged from the Control script too. This is a really simple example but some scenarios involve 3 or more scripts across different objects.
Issuing GameObject.Find and then GetComponent will allow me to find them, but I’ve got to the point where I dread adding anything complex because I have to start tracing through and finding our where different bits of functionality are in different scripts on different objects then setting up global references and Find commands in the Start function. It is just becoming messy and unmanageable.
I checked through the Unity documentation and the Wiki and could find no advice on this matter. There is plenty on what GameObjects are, What Components are but no best practice on how to structure your code in a complex app.
Any advice would be great appreciated.
At this stage I am almost contemplating starting again in Objective C
If you are familiar with C++ you should try C#, it’s OO with references in place of pointers.
I like to keep most scripts small and specialized: in your example I would keep one class only for the laser with only all code needed to control it.
This way you can reuse the same script for different ships or easily replace the laser with other weapons if needed (you could use one Interface if you plan to switch objects in game).
Instead of the Find method I prefer to use public object references in monobehaviour classes and drag and drop the object in the ide to assign it.
Thanks. I am fluent in C# and use it all the time in ASP.NET. Just found Javascript simpler in Unity.
So are you saying here that for each GameObject you would always roll all your functionality into a single script, even if there are lots of different functions (e.g. control, collision detection)?
Can you explain what you mean by Public Object references, that is something I am not familiar with in Unity. Can it make reference across different GameObjects or across Components in the same GameObject. Perhaps a simple code sample?
If the object is CPU controlled yes, all code of the object is in the same script.
Collision detection and GUI are the exception: I keep all the collision detection in one “LevelManager” class and all objects that can be hit have one OnCollision function that call the LevelManager method. For player events, I check it in the LevelManager class and it call the correct object.
For example in my space game only the player’s ship can collide to other objects so I have only one OnCollide method, when called it calls the LevelManager.Collision passing the collision informations. There I check the tag of both objects and if it’s a gem it calls Gem.Destroy and a function to check if the level is clear, if one object is a missile it calls Missile.Destroy and PlayerShip.Damage and so on.
Most scripts are really simple and contain only the Update method where they update their position or rotation or rotate towards the player position.
About the reference you can declare a public GuiTexture and assign it in the ide, the same GuiTexture can be assigned to more objects. The same with monobehaviour script you write: you can write a PlayerShip class, declare a public PlayerShip class in another class and assign it in the ide. I am sure there is a tutorial about that.
@ steddyman: I hear your pain, I"ve spent much of the last 13 years in Java, and OO, and I too find myself frustrated at times with the apparent implementation options. Trying to build a simple array of highscores, I keep fighting with my need to create some kind of simple object to contain the records and perform actions on them…
I’ve found that if you store foundation classes in one of the four higher “build paths” (like plugins) you can achieve some level of “normal” development, however I have a feeling that what I’ve heard about building your own DLLs (which may be limited to the pro version only) can let you properly isolate your object libraries and go back to Unity like bliss Perhaps someone else can comment on that?
I am getting a little confused by your use of C# terms and how they relate to their Unity counterparts.
So you your LevelManager class actually enclosed in a top level LevelManager GameObject with a LevelManager script attached?
Do you then declare a variable of type GameObject in each of your per object collision scripts, then perform a GetComponent for a LevelManager in the Start function?
Or are you declaring a variable to type LevelManager and somehow dropping that in the IDE. I didn’t you could drop Components that are children of GameObjects into variable holders in other scripts. I know you can drop GameObjects and Prefabs and I use that functionality today.
Thanks for all the insights here. This is really useful to me.
I’m currently forced into using these higher (poorly named) build paths to references scripts in different languages. If Unity did nothing else but allowed cross language calling without these gymnastics, it would make an incredible difference to the usability of public scripts. Either that or drop all languages but one.
You can definitely declare a variable with the type of one of your own scripts and assign an instance of your script that is attached to another game object.
Just declare a public variable of type LevelManager (This is importatnt! You’re using Javascript, so don’t get lazy here… Unity must know you want to assign an object of type LevelManager and not Object, GameObject or anything else…) and then either choose the instance of LevelManager from the drop down list that is added in the inspector, or drag to the inspector the game object the LevelManager script is attached to. Unity will know to assign the LevelManager script and not the game object itself.
Personally I find using private variables and a GameObject.Find() and GetComponent() on the Start() function more reliable in the sence that if your scene gets messed up, you’ll need to drag the exact instance of the script to each public variable, and if it happens a couple of months after you designed it - chances are you will make a mistake or will have to spend some time figuring out what exactly was supposed to be assigned to that variable…