- Home /
C# Dynamically add components?
EIDT: Ok, I am going to leave the original question, but I thought of an easier way to ask it, haha. How do I specify a class type from a string? So, in this case, "TestHeroActions" need to be, in whatever roundabout way, a type to be used to declare a class member.
So, I'm developing a game where several different hero types will be available, but the only difference will be stats and abilities. Movement and all other low level work is by a series of inheritance. I'd like to just have the stats and skills both be in their own scripts and be dynamically added to the player when it is instantiated based on the name of the hero. How should I do this? My first idea was a bust, and it was a gamble to begin with. I'm beginning to doubt it's possible. Below is a snippet from the universal PlayerObject class that is attached to the player prefab.
//TEST
public string CharacterName;
public Component Actions;
void Start(){
//For visual purposes -- Hero name derived from chosen hero by player
CharacterName = "TestHero";
Actions = gameObject.AddComponent(CharacterName + "Action");
print(Actions);
}
I originally thought that Component
was a generic name that could be used for scripts, and it actually seems to work just fine. The above compiles and runs, but as soon as I try to reference a member from Actions
it raises an exception. I am wondering about interfaces, but that's a C# feature I'm not strong with... so I'll be looking into that while waiting for a reply here. Hopefully I'v conveyed what I'm trying to do well. Please let me know if you have questions, and thanks in advance for the help! :D
EDIT: I just realized I'm basically looking for a "delegate class". I'm sure it doesn't exist, but if there's any way to make it possible I'd sure love to take a hack at it.
Answer by Kiwasi · Sep 08, 2014 at 07:45 AM
Had to do some reflection to get this. Not for the faint hearted. Here is some pseudo code for structuring ideas. Might not work, but should give you a picture of how it might be done.
public class StatsData : MonoBehaviour {
// Implement the various stats here
}
public class WizardStatsData : StatsData {
// Override the appropriate stats here
}
public static class StatsHelper {
private static Dictionary <string, Type> statsDatas = new Dictionary <string, Type>();
public static StatsHelper (){
statsDatas.Add ("Wizard", typeof(WizardStatsData));
// And so forth for the other types
}
public static bool AddStats (string statName, GameObject addTo){
if(!statsData.HasKey(statName)) return false;
addTo.AddComponent<statsData[statName]>();
return true;
}
}
Note that strings are inherently dangerous for this type of functionality. Typos in strings in code typically cause bugs that are difficult to locate and fix. I would suggest using an int as your key instead. In this case you could drop from a dictionary to a generic list.
There a bunch of other suggestions I would offer that depend on your code structure. Not all of them will be worthwhile ideas with your code and intentions. Consider:
Using foreach on your collection to generate names and other details for your choose your character screen
Set up instances of each StatsData in the home scene. Have each class 'check in' with the collection as part of its Awake method. This will make it easier to add and remove classes at will
Does stats class really need to inherit from monobehaviour? If its just a data object then there is no need for the extra overhead. Just store a reference to it on another component.
Use a factory method with a couple of constructers to produce the StatsData classes. Store uninitialized versions in your collection. This gives more flexibility then working with types.
This is the approach I was thinking of taking originally. But the problem now is accessing that member. The name of the component is essentially unknown to all other components. How would I call a function on wizardstatdata without using some massive switch statement?
That's where polymorphism comes in. Every function you want to call should be defined on StatsData. Then you simply call the function on StatsData.
public class StatsData {
public virtual void Attack (GameObject target){}
}
public class WizardStatsData {
public override void Attack (GameObject target){
// Do the wizards attack here
}
}
// This call will go to the most overridden method
// You only call methods on statsData
// No need to worry about what the actual type of statsData is.
statsData.Attack(gameObject);
So what you're saying is... I was just super overclomplicating this, haha. Well I'm going to give this a shot. It looks promising. Thank you very much.
In your example, statsData
is a dictionary. the statsData.Attack(gameObject)
wouldn't work. However I see how to do this. $$anonymous$$arking as answer. $$anonymous$$ight want to clarify that for future viewers though.
Answer by zharik86 · Sep 07, 2014 at 07:56 AM
Learn about polymorphism. There is no built-in class that you can derive from, which can do the specific things you need. Create one more class for your player and do differents on type:
public class StatsData {
//For example
public int str = 10;
public int dex = 10;
publuc int int = 10;
//And etc
}
And your function create this class and change him fields:
public StatsData myData;
void Start() {
//Create class
myData = new StatsData();
//change stats for type player
if(playerType == 1) {
myData.str = 15; //and etc
} else if(playerType == 2) {
myData.str = 8;
}
}
I hope that it will help you.
I like the idea, but unfortunately these scripts are going to contain the entire user facing functionality of each hero (of which there will be dozens), so creating a switch or if/else system to handle that would be an absolute nightmare.
@jtsmith1287 If you have very big distinction between characters, create for each character the class, and a variable make as "GenericClass".
Answer by orb · Sep 08, 2014 at 08:11 AM
Start with a Hero class, containing basic attributes and methods for delegates to attach to. This is derived from MonoBehaviour (the component class most used in Unity), and has a public string with the profession. Use this string in AddComponent() calls. Make classes corresponding to each profession - Warrior, Rogue, Poopsmith etc. Use RequireComponent attributes with each profession class to also include "kits" of components for actions. Avoid deep inheritance if you're embracing component-based programming - go wide.
The Hero class could have a generic NormalAttack() method, and the Warrior might add its own delegate which picks an appropriate attack based on the currently selected weapon, or a component required by the Warrior adds its delegates. This love letter to delegates explains some things fairly well. Note: Delegates should avoid allocating memory, so it's best for flipping toggles, changing values and playing animations.
Your answer
Follow this Question
Related Questions
Distribute terrain in zones 3 Answers
C# -- Build character unit from script 1 Answer
Multiple Cars not working 1 Answer
A node in a childnode? 1 Answer
Having issues with my animations 0 Answers