Creating a single class or use polymorphism?
Hi all,
So I'm trying to make a 2D game where I have 50 unique collectables(items). These collectables are obtained by the player under certain circumstances, ex: defeating monsters, in-game events...etc.
Upon getting these collectables, the icon of it will appear on the "show room" panel, and the status of it becomes "unlocked".
These collectable has no real interactions with the game, they only contains some data like an example below:
public class Collectable
{
public int id {get;set;}
public string name {get;set;}
public ItemType type {get;set;}
public int rarity {get;set;}
public float bonus {get;set;}
}
Notice that "bonus" is the bonus given to the player in some in-game events, ex: 0.2 additional chance to obtain Collectable XXX upon defeating monsters.
Now I have two questions:
-Should I create only one class like above then assign the values to it at runtime upon creation, or, create a Collectable base class and then create other 50 classes that inherit from the base class? Or if there's any better way you suggest?
//Option 1:
public void CreateCollectables()
{
collectablelist.Add(new Collectable(){1,"item1", ItemType.Battle,1,0.1});
collectablelist.Add(new Collectable(){1,"item2", ItemType.Type1,1,0.1});
collectablelist.Add(new Collectable(){1,"item3", ItemType.Type2,1,0.1});
...
...
collectablelist.Add(new Collectable(){1,"item50", ItemType.Event,1,0.1});
}
//Option 2:
public class Collectable1 : Collectable{
public Collectable1()
{
id = 1;
name= "item1"
...
}
}
//then
public void CreateCollectables()
{
collectablelist.Add(new Collectable1());
collectablelist.Add(new Collectable2());
collectablelist.Add(new Collectable3());
...
...
collectablelist.Add(new Collectable50());
}
-If I were to map a collectable to a slot in the panel, what would be the ideal structure?
public class Collectable
{
...
CollectableSlot slot;
}
//or?
public class CollectableSlot
{
...
Collectable collectable;
}
Any comments are welcomed, thank you!
Answer by streeetwalker · Apr 26, 2020 at 07:46 AM
Hi @coolguyjoey, my take on OOP is that is always about you and your team's ability to create, understand and maintain the code functionality you need vs code performance concerns:
For example:
Will 50 scripts for each type of behavior going to make your job easier or harder in terms of finding a particular class code you need to adjust or modify? That will depend on what code 50 class have in common, and how you organize the code files. If you don't name and organize the script files using some method, it can be hard to the script you need to edit. If 50 scripts have code in common and you need to edit, do you want to edit that 50 times? On the other hand, if you don't similarly organize your inheritance using some method, it can make code equally difficult to understand and maintain.
Will having 50 scripts affect the performance of your game? There is a performance hit, albeit small per reference use per object instance, for referencing object and properties on objects - especially where the properties themselves are references to other objects with properties the code may need to access. Linking to any properties of 50 class instances from some manager script, and having to do that repeatedly, is going to cost more than having everything in one script. If you have to loop through references to access properties of other objects a million times, it can be costly. That is why we create 'cache' variables - to reduce the de-referencing costs.
Always take the route that balances those concerns.
It sounds like in your example that you don't need 50 scirpts, but some scheme that disassociates the the treatment of a collectable from the data of the collectables - I think into some kind of collection (list/dictionary) and, probably on a separate non-MonoBehavior class to be instanced on a read through a json file, because it is much easier to set up all that data in a text file. If your collectables reference game objects, you need to represent those in the json file too - and not difficult to do.
If it were me, I would set up a json file that represents an array of objects that contains all of the information for each unique collectable. I would also write a non-MonoBehavior Collectable class to embody each collectable as an object instance and reference each instance in a dictionary element that will be accessed by the players. If the collectables have any behaviors I would include it in the Collectables class definition. Players will need a list/collection to reference the Collectable class instances they acquire.
At runtime, during the game set up, initialize the collectables dictionary and in a loop grab each json representation use the data to Instantiate a Collectable class object, and then finally add to the dictionary. When it is done, we have a dictionary of all the collectables.
Then when a player finds a collectable, a reference to that specific collectable is added to the players unique list.
I doesn't sound like you need Inheritance unless collectables themselves have different attributes or behaviors. In that case, I would created a Base Collectables class and as many sub-classes as necessary to embody those. The json elements for each collectable would need to reflect which subclass a collectable belong to, and the logic of the initialization loop would need to choose which subclass of Collectable would be Instantiated base on that data.
If there are Collectable sub-classes, the dictionary treatment needs to be changed because now it needs to accommodate sub-class members in one dictionary. So it will need to be initialed on the base Collectables class. There will be some nuances to how the dictionary is read from - in some cases we may need to Caste what is read from the dictionary into the proper subclass.
All that is off the top of my head - I've done this before for a weapons/inventory system, but it's been a couple of years
BTW, OOP 'Polymorphism' means overriding methods/functions - you're code may call for that, but the core issue in your question is one of OOP Inheritance.
Hi @streeetwalker ,
Thank you very much for your thorough explanation, it is exactly what I was looking for.
Would you be able to give me some examples of how to use json a file to represent data of collectables?
At runtime, during the game set up, initialize the collectables dictionary and in a loop grab each json representation use the data to Instantiate a Collectable class object, and then finally add to the dictionary. When it is done, we have a dictionary of all the collectables.
Best,
@coolguyjoey , here is a sample that you should be able to follow, how ever I could not find the specific example I mentioned - it's actually been about 4 years since I worked on this1
This sample creates a new $$anonymous$$elee player in a maze game on the basis of a chosen character type, defined in a json file.
This is a first person game, so the player has no visible representation. But game objects are easy to include by co$$anonymous$$g up with a coding scheme for different prefabs as a field the json file, and using that to instance the correct prefab from the saved code in the json file..
This sample is too big for this forum, so I have attached it here:https://github.com/streetwalker-dreams/dreams/blob/master/samplecode.zip
You should be able to click the download button
A couple more notes: my sample only has one CharacterStats type defined in the json file. The SaveData$$anonymous$$anager.cs will create a dictionary element for as many as you have defined in the json file. Note in the code that will create a new CharacterStats object for each, and adds it to the dictionary using the key from the json file.
In your case, you will have all the collectables defined in json, and your SavedData$$anonymous$$anager (or what ever you want to call the file), will a create Collectables object for each and add it to the dictionary.
You can then attach a collectable to your player using the dictionary - and you'll probably need a list of keys, or some way of marking which collectable it finds, so your player code knows which dictionary key to use.
For example, in the main program at the bottom of my sample, I have instructed my code to create a player using the $$anonymous$$elee character type stored in the dictionary.
Likewise you will need to write the code for your player "picking up" a collectable - that is, adding a reference in your players collectable list to the collectable dictionary element.
Hope it help!
Great stuff!
Thanks for the code, now I can integrate this to my game, aweeesome!