Search Unity

tower defence stats system

Discussion in 'Scripting' started by Kvart, Feb 12, 2016.

  1. Kvart

    Kvart

    Joined:
    Nov 26, 2015
    Posts:
    13
    I'm trying to find a smart way to store and retrieve stats for a tower defence game, but am currently unsure as to which approach would be best.

    Description:

    The stats system should quite basic, there are 4 different towers, with up to 5 different levels each. One important requirement is that the stats should be stored in an XML file so that the game designer easily can access and balance the stats. (He knows nothing about programming)

    I can see two ways of doing this:

    1. By creating a TowerStats class which hold values for one type of tower for one level. Something like:

      publicclassTowerStats2{publicTowerType type;publicint lvl;publicint damageLow;publicint damageHigh;publicfloat range;publicfloat attackFrequency;}
    Then i would load the stats from the XMl file using Linq and XDocument and load it directly into a TowerStat List. I would do this for each tower so in the end there would be 4 different List's which would hold the different level stats for the specified tower.

    • gunTowerStats[0] would hold level 1 stats of the gun tower
    • rocketTowerStats[2] would be level 3 stats of the rocket tower
    When a new tower is bought by the player or when a tower is upgraded, the stats would then be taken from the List so something like:

    UpgradeTower(){
    lvl++;
    towerStats = gunTowerStats[towerLevel -1]}
    1. Alternativly I could hardcode all the values, and have one big class which would go something like this:

      gunTowerDamageLvl1 =4;
      gunTowerDamageLvl2 =8;
      gunTowerDamageLvl3 =15;
      rocketTowerDamageLvl1 =10;
    you get the point. I know that hardcoding is never never really the way to go. But does that also count for this kind of example where all possible values are known and will not change?

    Question I'm personally in favour of the first way, but I must admit that I find both ways a bit clumsy, would it be alright to do it one of these ways or is there a much smarter and more obvious way to deal with this that I'm completely unable to see?

    I hope my question is understandable.
     
  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Why do that when you can just use XML serialization?

    Runtime modified array of classes is better. If classes are derived from MonoBehaviour or you make a custom editor for it, you can easily modify values from inside editor as you see fit. That will make balancing easier. Well, you can use XML serialization, but it's a bit more... For modders I'd say, not for developing. Then they need to be [Serializable] you can serialize/deserialize them into XML with just several calls.
    It will however be tricky to both derive from MonoBehaviour and use serialization properly together if you want to have both MonoBehaviour's default editor and xml serialization (deserialization from files won't be easy as you can't just create Component using new).
     
    Last edited: Feb 12, 2016
  3. Kvart

    Kvart

    Joined:
    Nov 26, 2015
    Posts:
    13
    Hadn't thought about using XML serialization. I'm using Xml Linq for other things in the code because I need to load in a multidimensional array for a complex wave system. I will look into that.

    There are two reasons why we chose to use XML,
    1. I'm the only one working on the unity project because we are afraid of breaking the project by working more than one person on a project folder at the same time, so building an editor or simply changing values from the Inspector would mean that I should do it. Problem is I have my hands full, so changing small values back and forth is really taking a lot of time from the actual system development.

    2. The project leader / game designer, is afraid of code and unity. He literally gets scared when he looks at it. Which is kind of silly but sadly not really something I can change.

    That being said I really do get your point. It does seem like a bit of an overkill to go this path. I'm also still considering if it's actually worth it.


    About the stats managing in C#. Do you have any smart solutions for that issue?
     
  4. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Smarter than
    would be overkill as it'll get job done faster than any "smart" solution. ;)
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Gonna agree that putting the values in an external file is going to make it easier for you. Not gonna agree with XML. Unity comes with ScriptableObject for exactly this purpose. It'll be easier for your level designer to edit it (no possibility of breaking everything by deleting a '>'), and it'll be easier for you to work with in code (no parsing required).

    So your class would look something like:

    Code (csharp):
    1. public class Tower : ScriptableObject{
    2.     [Serializable]
    3.     public class TowerStats {
    4.         public int damageLow;
    5.         public int damageHigh;
    6.         public float range;
    7.         public float attackFrequency;
    8.     }
    9.  
    10.     public TowerStats level1Stats;
    11.     public TowerStats level2Stats;
    12.     public TowerStats level3Stats;
    13.     public TowerStats level4Stats;
    14.     public TowerStats level5Stats;
    15. }
    Then you would create one of those serializeable objects for each of the tower types, put those objects in your Resources folder, and hard-code a link from the tower type to the serializeable object.

    To create the asset, you need something like this:

    Code (csharp):
    1.     [MenuItem("Assets/Create New Tower")]
    2.     public static void CreateTower() {
    3.         AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<Tower>(), "Assets/NewTower.asset");
    4.     }
    And then you get an asset file looking like this:
    Demo.png

    Your designer should be able to work with that! If you write a custom inspector (google!), you can even make that inspector a lot prettier - you can have sliders and drop-downs and whatnot.

    Say you rename that to ArrowTower, and put it in Resources/Towers. Then you can do this in your tower class (or wherever):

    Code (csharp):
    1. public enum TowerType {
    2.     ArrowTower,
    3.     CannonTower,
    4.     //...
    5. }
    6.  
    7. private static Dictionary<TowerType, Tower> towerDict;
    8.  
    9. public static Tower GetTowerDetails(TowerType type) {
    10.     if (towerDict == null) {
    11.         towerDict = new Dictionary<TowerType, Tower>();
    12.         towerDict[TowerType.ArrowTower] = Resources.Load<Tower>("Towers/ArrowTower");
    13.         towerDict[TowerType.CannonTower] = Resources.Load<Tower>("Towers/CannonTower");
    14.     }
    15.  
    16.     return towerDict[type];
    17. }
    Then, when you want to create an Arrow Tower, you can load it by simply doing this:

    Code (csharp):
    1. Tower.GetTowerDetails(Tower.TowerType.ArrowTower)
     
    Teravisor likes this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Oh, and if your designer is a little baby and refuses to open Unity, that tower asset looks like this in a text file:

    %YAML 1.1
    %TAG !u! tag:unity3d.com,2011:
    --- !u!114 &11400000
    MonoBehaviour:
    m_ObjectHideFlags: 0
    m_PrefabParentObject: {fileID: 0}
    m_PrefabInternal: {fileID: 0}
    m_GameObject: {fileID: 0}
    m_Enabled: 1
    m_EditorHideFlags: 0
    m_Script: {fileID: 11500000, guid: 1a5d518a350b4c946a8e59041166a0d6, type: 3}
    m_Name: ArrowTower
    m_EditorClassIdentifier:
    level1Stats:
    damageLow: 0
    damageHigh: 0
    range: 0
    attackFrequency: 0
    level2Stats:
    damageLow: 0
    damageHigh: 0
    range: 0
    attackFrequency: 0
    level3Stats:
    damageLow: 2
    damageHigh: 4
    range: 6
    attackFrequency: 8
    level4Stats:
    damageLow: 0
    damageHigh: 0
    range: 0
    attackFrequency: 0
    level5Stats:
    damageLow: 0
    damageHigh: 0
    range: 0
    attackFrequency: 0


    Your designer-baby should hopefully not be scared away by the bolded part, which is what needs to be changed to edit the file.

    The format is YAML, by the way, which beats XML for human readability any day. Unity uses it internally for everything - from scenes to prefabs to assets. Now, it's like Python in that it's indentation-dependent, but that should not be too big a problem.
     
  7. Kvart

    Kvart

    Joined:
    Nov 26, 2015
    Posts:
    13
    @Baste
    That sounds like a really great solution actually!
    Would it then be possible for the designer to work on these objects from within another unity project, and then just change the files in the main project folder whenever he makes changes?


    This is exactly the expression I've been looking for :D
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    To work with the objects in a different Unity project, that would have to have an exact copy of the classes, as the inspector serializes and deserializes the yaml files to proper objects

    Our company uses git to share files, so that's all I've got experience with sharing files in Unity. The designers use a GUI tool to stay in sync, and it really simplifies things. You'll want source control for your project anyway, so you just need to teach the dude to use SourceTree or something like that.

    If your designer would balk at that (sounds like somebody who would), I guess that he could download the project, change the relevant asset files, and send them back to you.
     
  9. Kvart

    Kvart

    Joined:
    Nov 26, 2015
    Posts:
    13
    @Baste
    I actually tried implement git when we started this project. I'm the only programmer on the project and by no means an expert with git, so I ended up giving up trying to teach everyone how git functions, mostly because I myself didn't feel safe enough with it. I am using it myself to source control the code for the project, but only in a very basic way. I'm considering making an effort to getting it up and running for everyone again.

    So technically speaking when I have created the classes needed to make and read the scriptable objects, I could copy that project to a project the designer would be working on, and he could then export the scriptable objects whenever he changes something
    Alternatively we could work on the same project with git, or maybe just by working on different scenes.

    I have been reading a bit about scriptable objects combined with editor coding, it seems like a great tool to have! So no matter what I end up doing it's probably worth it learning theses things.
    I think there are some good options here, I'm gonna try to work out a solution from the advice you have given me.
    Thanks for the help :)