0

I may be asking the questions wrong, since I haven't been able to find anything online on this...

Essentially my goal is to have a move class defined in project\Schemas\Move.cs:

public class Move
{
    private int power;
    private float accuracy;
    private string type; 
    private string style; 

    public Move(int p, float a, string t, string s, float sh)
    {
        this.power = p;
        this.accuracy = a;
        this.type = t;
        this.style = s;
    }

}

And I'd like a collection of ALL moves to be accessible from my game engine, but I want that move collection to be built automatically based on other files under project\Moves\move_*.

I'm honestly not sure how to go about doing this in C#. In another language I would simply initialize a global collection and in each move file I'd just call GlobalCollection.Add(new Move(...)) or something similar.

Hopefully someone can help me out!

Ethan
  • 787
  • 1
  • 8
  • 28
  • Why can't you do the same in C#? I mean, in order to initialize the global collection you would need to know before hand the list of moves possible (in `project\Moves\move_*`. (PS: The name `move_` is a bit weird in C#) – Haytam Sep 28 '19 at 20:18
  • Because in C# the compiler won't accept a function that's run outside of a namespace or class, and importing/initializing every single item as a class is what I am hoping to avoid... I'm not deadset on using move_ or really any naming scheme, the main goal is to have something that dynamically loads up everything within the parent folder into a globally accessible collection or object. – Ethan Sep 29 '19 at 21:25

3 Answers3

1

If I understand you correctly what you are trying is to have one e.g. List<Move> and you want to "register" each created instance of Move here. So why not simply something like

// This is a static class so it does not have to be instantiated
// but rather simply "lives" in the assets
public static class MoveManager
{
    public static List<Move> Moves = new List<Move>();
}

Then you could so something like

public class Move
{
    private int power;
    private float accuracy;
    private string type; 
    private string style;  

    public Move(int p, float a, string t, string s, float sh)
    {
        power = p;
        accuracy = a;
        type = t;
        style = s;

        // a static class member is simply accessed via the type itself
        MoveManager.Moves.Add(this);
    }
}

problem: When do you remove them from the list?

Usually you want to do this in the Deconstructor e.g.

~Move()
{
    MoveManager.Moves.Remove(this);
}

BUT in Unity the deconstructor isn't automatically called - especially not while anything is still referencing this Move instance ... spoiler alert: The Moves list will always do! So you if required you will have to cleanup the Moves list "manually" every time you really want to destroy a Move instance.

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Awesome, this actually looks like exactly what I was looking for! There shouldn't ever be a need to remove moves from this list, since it'll be the global registrar... Thanks @derHugo! – Ethan Oct 04 '19 at 17:51
0

There's no (good) way of doing what you want.

Option 1:

Write it out by hand yourself. E.g. like I did here. This will always work and is the thing you're trying to avoid.

Option 2:

Reflectively examine the Assembly and load all classes it contains, like this. But this will be prone to doing unexpected things. Such as Unity culling those classes from being compiled when you create a standalone built because they aren't referenced anywhere. Or inadvertently instancing an object that shouldn't be. Or failing to remember that "oh yeah, MoveXYZ has a unique constructor that requires an additional parameter."

Community
  • 1
  • 1
  • Interesting, this gives me a little bit to think about and look into... Neither of those options seem particularly appealing, but I suppose if they're the only ways then that's how the chips fall. I'll keep looking for a bit though and if I end up using one of those I'll accept the answer. – Ethan Sep 29 '19 at 21:26
0

Adding on to the answer given by derHugo, just in case someone else stumbles across this. I'm using the method suggested, but found that in order to "import" the types at runtime that [RuntimeInitializeOnLoadMethod] had to be set used. There are probably other ways, too, but here's the 3 move files I ended up with for answer completion's take.

MoveManager.cs

 public static class MoveManager
 {
    public static List<Move> Moves = new List<Move>();
 }

Move.cs

public class Move
{
    public string name;
    public string description;
    public Target target;
    public int power;
    public float accuracy;
    public int cost;
    public game_Type type;
    public Style style;

    public Move(
        string name
        , string description
        , int power
        , float accuracy
        , int cost
        , string typeName
        , string styleName
        , string targetType
    )
    {
        this.name = name;
        this.power = power;
        this.accuracy = accuracy;
        this.description = description;

        this.type = game_Types.getByName(typeName);
        this.style = MasterStyles.getByName(styleName);
        this.target = new Target(targetType);

        // a static class member is simply accessed via the type itself
        Debug.Log("Registering " + name + " type!");
        MoveManager.Moves.Add(this);
    }
}

move_basic.cs

class move_basic
{
    [RuntimeInitializeOnLoadMethod]
    static void OnRuntimeMethodLoad()
    {
        // Name this move
        string name = "Attack!";
        // Describe this move
        string description = "Strike out at the enemy with tooth, claw, weapon, or whatever else is available!";
        // Choose the type of target this move is for
        // self/ally/selfAlly/enemy/enemies
        string target = "enemy";
        // The move typing - This determines cost reduction as well as potential attack bonuses
        string typeName = "physical";
        game_Type type = game_Types.Types.Find(x => x.name == typeName);
        // The move style - This determines additional bonuses and modifiers
        string styleName = "martial";
        // Style style = ??
        // Power of the move, base/'normal' power is 50.
        int power = 50;
        // Accuracy of the move, base/normal is 90
        int accuracy = 90;
        // MP Cost of the move
        int cost = 0;


        Move mv = new Move(name, description, power, accuracy, cost, typeName, styleName, target);
    }
}

The next step, now that I've confirmed this works will be to add methods to the registered moves which is where the real magic is IMO.

This seems like a pretty optimal solution when you need to create an easily extendable library of objects which may need to contain callable methods. If not for that need, it probably would have been better to import properties from a csv or something.

I'm pretty new to unity and game design in general, so I'm more than happy to have this post edited/corrected/improved to assist future searchers.

Ethan
  • 787
  • 1
  • 8
  • 28