- Home /
Best way to access a script component without knowing it's name?
I've only been programming for a couple months, and I've run into a problem that I suspect stems from my lack of experience with basic OOP concepts. Please bear with me and help me understand where I'm going wrong here :)
I'm working on a stock market trading game. The core gameplay relies on a few different "Market Elements" that interact with one another: Stocks, Commodities, and Statistics. So for instance the "BirthRate" statistic needs to affect the "BabyFood Corporation" stock.
I've built each element as a separate script that gets attached to a generic game object. I then fill in the data for each object using the inspector (will probably switch this to XML at some point). So there is a "BirthRate" object, with a "Statistic" script attached to it. These objects each have a list of "Dependencies" that includes any other Market Elements that should be affected by that particular object.
The problem comes when I need to access an object's Dependencies... Because the Dependency objects can have a Stat Component, a Stock Component, or a Commodity Component, I can't ever be sure which Type to look for.
My solution up until this point was to create a separate "Market Element Interface" script that was added to each element. Each Stock/Stat/Commodity script sends the data I need to it's Interface script, allowing me to grab it from there. This solution works when I need to access data from an element, but it still doesn't allow me to CHANGE data.
How would you approach this design? I'm wondering if I should just put the Stock/Stat/Commodity scripts into one big "MarketElement" script? This seems messy, but it would allow me to access the script freely. Are there any other tricks involving generics or reflection that might work in a case like this? I haven't delved into those topics yet and everything I've read didn't seem to apply to this case.
If you made it this far... then thank you for reading. Hopefully someone can give me a clue as to where I should go from here. Thanks in advance!
The dependencies could each have a different tag or a build-in variable that deter$$anonymous$$es their type. Then when you access them, you could just have 3 different if statements checking which type it is and acting accordingly.
Though this might get messy, depending on what you're doing.
Either way, I feel your pain. I can't count the number of times I had to re-write my whole base structure because I run into some problem I could've technically foreseen, if I had the experience. I suppose it comes with the territory of coding games out of thin air, with no documentation and the like.
Thanks for the empathy :)
Yeah one thing I had thought of was to run through each dependency object and test which component they have using if statements as you mentioned. This could be done by trying to GetComponent each element type, checking for "!= null". I would then store them in an ArrayList since ArrayLists can store varying types.
That should leave me with an ArrayList that references the proper scripts on each dependency. It's not a pretty solution though so I thought I'd ask around to see if there is a better way to structure the whole thing before this turns into a rat's nest. :)
I'll go ahead and try that method though and see if it does what I need.
Does Stock/Stat/Commodity have common attributes/methods that you need to access?
this seems like a good spot for getters and setters in a separate file then changes can be made with a touch of ease and getting info is again done with ease.
Answer by fafase · Jun 28, 2013 at 11:24 AM
From what I get you have either stock or Stat or Commodity but never the three of them on one object.
The most OOP style would be to use an abstract class (since interface cannot inherit they cannot be turned into component) like:
public abstract class Dependency:MonoBehaviour{
public abstract void GetTradeInfo();
public void abstract void SetTradeInfo();
}
Now each of the dependency can inherit from Dependency and will have to implement the method the appropriate way.
As a result, it does not matter what is the type of your dependency since they are all of type Dependency and will for sure have the method implemented the proper way.
In the end, you call the same method name but the compiler will redirect to the appropriate one whether it is stock,stat or commodity.
So you can simply do:
GameObject obj;
Dependency dep = obj.GetComponent<Dependency>();
dep.SetInfo();
This is good OOP. i think we are all a bit confused on the relationships of the ops objects/classes.
After reading up on Abstract Classes, I think this is exactly what I need. The three market elements do indeed have some methods in common. I had figured I might need to create a structure of inheritance, but I was concerned it would be too rigid. Abstract Classes seem to provide enough flexibility to suit my needs.
I'll give this a try and report back on how it goes. Thanks to everyone for all the help! This is an incredible community.
Hey there @fafase ! I'm your biggest fan, you're helping me a lot with your answers!
Just to check if I actually got it: if I have an abstract class Character, and then multiple classes inheriting from it (ex: Swordman : Character and Archer : Character), each attached to a different character gameobject.
Can another script (such as the $$anonymous$$anagerScript) simply get whichever script that character has (be it Swordman or Archer) by using this?
GetComponent<Character>();
Like, it the character doesn't have a Character script, but ins$$anonymous$$d it has just a Swordman script with inherits from Character, will the system call the proper Swordman script in it by using the above code?
Answer by SinisterRainbow · Jun 28, 2013 at 10:35 AM
You shouldn't put more in one script. :) But, in many game designs, it's OK to have a main Game Script for example that has access to everything (well, all major classes for example). You can then easily find the main script, and access the other scripts through this game class. Btw, the title of your question - why wouldn't other game objects know it's name? Sometimes it's unavoidable that two classes know things about each other. It's hard to make everything completely modular, and sometimes undesirable.
Another solution, which I only recommend infrequently, is to use delegates in a message passing system. You can notify other scripts when an event happens (baby boom data comes out).. each of the scripts listening for baby boom data can then update themselves instantly.
Similarly, there's also the concept of listeners. When one class has new data available, any other class that is 'listening' to that class can update itself. For example, I implemented a listening system for keypresses. I have an inputhandler that takes certain keyboard input on certain game states. When a player say, fires a gun, i have a few classes that listen for this signal who are 'registered' to the inputhandler. rather than have all classes constantly listening to all input, they only get what they need. So fire gun can activate an animation class, deduct a bullet from an ammunition manager, etc.
Answer by Bunny83 · Jun 28, 2013 at 12:37 PM
A component should never directly "change" another component. A component represents a certain aspect of your whole project. It is responsible to fulfill this purpose. If it needs information from other components it should ask for this information and collect it.
A component might produce some kind of "event" and others might react to this event.
For this kind of communication in a component based system (loose coupling), Unity provides the SendMessage() function. However in your case interfaces would be the best way i guess.
You can use GetComponent with an interface, however the generic functions won't work since they are constraint to the type "Component". However the type-version works great:
public interface ISomeInterface
{
int SomeValue{get;}
void SomeAction();
}
public class SomeComponent : MonoBehaviour, ISomeInterface
{
//...
}
ISomeInterface myRef = otherObject.GetComponent(typeof(ISomeInterface)) as ISomeInterface;
myRef.SomeAction();
var val = myRef.SomeValue;
You can also make your own generic "GetInterface" function like this:
public static T GetInterface<T>(this GameObject aObj) where T : class
{
return aObj.GetComponent(typeof(T)) as T;
}
public static T GetInterface<T>(this Component aObj) where T : class
{
return aObj.GetComponent(typeof(T)) as T;
}
which can be used exactly like GetComponent:
ISomeInterface myRef = otherObject.GetInterface<ISomeInterface>();
myRef.SomeAction();
var val = myRef.SomeValue;
The main concern I had with Interface was since it is not a component, you cannot drag and drop in the Inspector an object of the interface type. $$anonymous$$aybe you know a way for that? That is why I ended up using abstract classes for this purpose since it turns out to be a component.
Answer by Ashkan_gc · Jun 28, 2013 at 12:02 PM
You can make it modular and reusable by a Dependency manager component on each object. Then you can add three List members to it for lists of stats, stocks and Comodities. Then you can just tell any of those scripts to add themselves to the appropreate list in the dependency manager which is a component attached to the same game object.
When you want to contact the class just you can contact the DependencyManager and check the lists and have public methods for working with other classes as well to wrap them.
If this additional layer is not required just doing GetComponents to see if something is available or not is ok. The only advantage of the manager is that you can create a layer of abstraction as a general dependency on top of different kinds of it, if you don't need it as a concept forget about it.
BTW if it helps, GetComponent has a version that takes script names as string, unrelated to your problem in a direct way however.
Your answer
Follow this Question
Related Questions
Structs in C# Question 3 Answers
error CS8025: Parsing error in C# code 3 Answers
Inspector vs Script: Component best practice? 1 Answer
Inventory system using an array... 3 Answers
Unity freezes on play 2 Answers