"Permanent" pointer-like references in C#? Or design that works for this situation?

Started by
6 comments, last by frob 9 years, 7 months ago

I'm a self-taught noob and taught myself programming with C++, but now I'm building a game in Unity with C# and I'm fairly new to C#.

I have a class called Parameters that pretty much is just a giant list of variables of stats (HP, attack, defense, etc.) for the game's characters.

Then I have a class called Item and a child class called Potion.

I want the Potion class to be able to hold a variable that tells it which one of the variables in the Parameters class it needs to affect.

I don't want to create a separate class for weapon potion, health potion, defense potion, etc, so that an individual instance of Potion could be easily customized by a random generator (or player generation system). And then I could just call the Use() function of the Potion class that does something like whateverVariableInParametersThisItemWorksWith += potionStrengthValue.

I feel like the way I should accomplish this is have a pointer within Potions that points to a variable in Parameters. But... that's not really how C# is supposed to work, so I really don't know what to do. I don't want to use a pointer and I want to understand C# better...

Advertisement
Whenever you're using classes in C#, you're using pointers.

C++ gives you two options:
Parameter a =...;
Parameter* b =...;
^ the 2nd one is a pointer to a value and the first is a value.

C# only gives you one option:
Parameter c =...;
... But unlike in C++, this is actually a pointer to a value!


In C#, if you're working with a class then you're always using pointers, but if you're working with a struct then you're always working with raw values.

In C++, class/struct are virtually the same, and you can use the '*' on your variables to decide whether to use pointers or not.
You might want to start reading a little bit about Object Oriented Programming (OOP) or a Component Entity System (CES). Like Hodgman said, all class objects are pointers.
Here's a rough first stab of an OOP approach. You might have an Item base class that would hold data and operations common to all Items. Things like its name, inventory icon, quantity, etc. Potion would inherit from Item so it includes all the Item data. and override the Item's Use() method. A Potion would have potion data like Heals 20 hp / sec for 40 seconds. Using a potion consumes it and applies the effect.
Your inventory system would have a list of Items. Since Potions, Swords, Scrolls, etc all inherit from Item, they ARE items. When your player right clicks on an item to "Use" it, the inventory system doesn't know if the clicked item is a Potion or Sword. It just calls the Item's Use() method. However, the clicked item knows which specific kind of item it is. "I'm a potion, so I'll reduce my quantity by one and apply my effect". If the Item was a sword, it might try to equip itself on the player.
And.... I should really stop answering questions when I first wake up in the morning. I see that's not the question you're asking, but I'm leaving this post up in case someone else finds it useful. So...Back to your original question. How do design your potion class...
I'm going to change the name of "Parameters" to CharacterStats for the rest of this discussion. (Option 1) If your Potion has a CharacterStats member variable named affectedStats, then you can have a potion that does anything. It's simple and powerful, but a little bit clunky. But if you have a simple healing potion that gives 20 health, you'll still have a copy of all the other stats Strength, Dexterity, Armor Class, etc. In all honesty, it's probably good enough for now.
Another option (option 2) is to code up a PotionEffect system. Create a StatAdjustmentEffect and an enum(eration) of your CharacterStats. The StatAdjustmentEffect has the AffectedStat and the Adjustment to make. When applying the potion's effect, you'd map the enumeration value over to the data of the CharacterStats class.

enum CharacterStatEnum
{
    Strength,
    Dexterity,
    // more stats...
    Health,
    ArmorClass,
}
 
public abstract class PotionEffect
{
    public float Duration {get;set;}
 
    // Each potion effect child class implements ApplyEffect.
    public abstract void ApplyEffect(GameCharacter potionDrinker);
}
 
public class StatAdjustmentEffect : PotionEffect // Base class for all potion effects
{
    public CharacterStatEnum AffectedStat {get;set;}
    public int Adjustment {get;set;}
  
    public override void ApplyEffect(GameCharacter potionDrinker)
    {
        switch(AffectedStat)
        {
            case CharacterStatEnum.Health:
            potionDrinker.Health += Adjustment;
            break;
           
           // More case mappings.
        }
 
        // Keep up with these effects somehow. could attach them to the potionDrinker or potionEffectManager class. 
        if(Duration > 0)
             throw new NotImplementedException(); 
    }
}
Then your Potion class can have a List<PotionEffect>. It's powerful, clean, but more complex. However, this system allows for an easier expansion into things like an Invisibility potion.
Another option (option 3) would be to include some sort of scripting library, and a potion can have the script that should run when the potion is drunk. This could be argued to be more or less complex than option 2, but you won't know until you get into it.
These are the types of design problems that make programing so fun ... and frustrating. There isn't just one correct answer and even Option 1 should be seriously considered. It might be simple enough to get the job done and you can move on to features that add more coolness than a robust potion featureset.
- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

@Hodgman: Thanks for the reply. I realize that classes work that way, but what I'm wanting to do is point to a value (a member variable of a class), and not the class itself. There are many different variables in the class Parameters and I want the Potion class to know which one to access in its own functions. I realize I can point to a member it to access it once, but I want to be able to hold a reference to know which member to access each time. IDK if I am explaining this properly, sorry.

@Eck: Thank you. I was originally doing pretty much exactly what you used for the example in Option 2, but I felt like there should be a more simple/compact way to do it.

I feel like this is similar to what you meant in Option 1 but I'm not really sure. Would it be possible for you to expand on that just a bit more?

Specifically, "If your Potion has a CharacterStats member variable named affectedStats," what type of variable would affectedStats be and what would it hold? It still can't hold a refernece or pointer, so I don't think I'm understanding this example properly. Plus every instance of Potion would have a different affected stat and I'm not sure how it would keep track of that.

What I'm wanting to do is point to a value/class member variable, and not the class itself. I feel like Option 2 will end up being the way I have to go, though.

I just feel like there's some kind of better way that I'm missing though. Like as if there should be a way to just store in a variable which of the CharacterStats variables I want to affect, and just be able to use that variable to change the value.

Like to just be able to write a single line like target.CharacterStats.affectedStat += potionStrength; and just be able to have affectedStat essentially 'point' to one of the CharacterStats variables, and be different for every created Potion object.

I realize what I just typed doesn't actually make sense, but I feel like it should be that simple. Is it just not possible to do something like this or something similar enough/simple enough in C#?

I also realize after typing that example that what I was trying to do wouldn't really work with a pointer anyway, as there's no actually instantiated variable with an address to point to in this case. But just... I feel like there's a solution here I'm not seeing. Perhaps I'm not explaining what I want to do well enough to 'click' with the right answer, or perhaps what I'm wanting to do simply isn't possible in any simple form like I'm imagining.

I don't mind doing the Option 2, as that's what I was originally doing anyway, but if there's a better way, I'd like to know about it/try it. I feel like this kind of concept could be really useful for all kinds of things in the future, too.

The problem with .Net is that the class instances can actually move around in memory at runtime due to garbage collection, so you can't store a simple, long-term pointer to the address of one of their members (at least, not in the normal way you'd expect - you CAN do this with delegates).

You could make a Variable<T> class, using it to define each of the player's parameters. This would let you get a long-term reference, similar to a pointer. The downside is that it's still pretty cumbersome to use. It also incurs a performance penalty for all normal operations, not just pointer-like operations.

You could make a class which uses reflection, storing the Object and FieldInfo you want to affect, but then you lose all type safety. Performance is also not as good as it could be.

The other consideration is that you probably want to be able to create a Potion without binding it to a Player immediately. What if you're in a game with multiple players/characters and want to give the potion to a different character? You don't want to have the potion point to a player directly until you need to use it.


Here's an option that is fairly simple and clean:


public class Player
{
    public float HitPoints;
}

public delegate void ApplyEffectToPlayer(Player player);

public class Potion
{
    public ApplyEffectToPlayer ApplyTo;

    public Potion(ApplyEffectToPlayer applyTo)
    {
        ApplyTo = applyTo;
    }

    public void Use(Player player)
    {
        ApplyTo(player);
    }
}

public class PotionFactory
{
    public static Potion CreateHealthPotion(float healingAmount)
    {
        return new Potion(player => player.HitPoints += healingAmount);
    }
}

I really like Nypren's proposed solution and this may be exactly what you're looking for. He's using delegates and that funkiness in the PotionFactory is called a lambda expression which is just syntactic sugar for a delegate. Do a little bit of research on those items. If you need more help understanding it, we'll be happy to help.

@Nypren - Very slick sir. I've used delegates for similar things, and I've used lambda expressions for looping/finding logic. But this little example speaks volumes of delegate coolness. Nice job.

For completeness, I'll go ahead and answer your question about Option 1. I thought Option 1 is what you were already doing. :)


@Eck: Thank you. I was originally doing pretty much exactly what you used for the example in Option 2, but I felt like there should be a more simple/compact way to do it.

I feel like this is similar to what you meant in Option 1 but I'm not really sure. Would it be possible for you to expand on that just a bit more?

Specifically, "If your Potion has a CharacterStats member variable named affectedStats," what type of variable would affectedStats be and what would it hold? It still can't hold a refernece or pointer, so I don't think I'm understanding this example properly. Plus every instance of Potion would have a different affected stat and I'm not sure how it would keep track of that.

CharacterStats was just a rename of your class Parameters (a giant list of stats HP, attack, defense, etc.). I just didn't like the name Parameters because I thought it would cause confusion. So, I'll switch the name back to calling it Parameters for this post. Parameters is a class, and so it's a reference type (like a C++ pointer). In your game init code, you can new up a Parameters object for all your healing potions. Parameters healingParameters = new Parameters(/*set health to 50, set rest of variables to 0*/). Potion has a member variable: Parameters AffectedParameters; When making a healing potion, just assign potion.AffectedParameters = healingParameters;. There's just one copy of the data of healingParameters since their reference types. Then the potion's Use() function can just add all of its stats to the character's stats. player.HP += affectedStats.HP; player.Attack += affectedStats.Attack; Since the rest of the variables were initialized to 0, the only thing that gets changed is the HP.

Again, I feel this is the hackiest of the options. It may be good enough for your needs, but... blech. :P

If you don't like Nypren's solution, you might want to look up Reflection. With reflection, you could look up a property by it's name and set its value. So you could for instance, mess with the variable named "HP".

http://stackoverflow.com/questions/7718792/can-i-set-a-property-value-with-reflection

Just be careful. Reflection lets you do things you're not allowed to do, like access private data members. There may or may not be some commented code in production that says // HAH! IN YOUR FACE OOP!!! attributed to me. >.>

- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

Thanks so much !!

@eck - OK, I understand your Option 1 better now. When I read it before, I thought you meant to create the affectedStats variable within the Parameters/CharacterStats class as a member of that class, not as a member of the Potion class. It was just an ambiguity of English language that I interpreted improperly :) And I understand why you were using CharacterStats. I just use "Parameters" because in Japanese games the characters' stats are always called Parameters and I refer to them as such often and it just feels natural to me. Though I probably should've thought about it more and used a different name for the sake of this topic :)

@npyren - Thank you! I will have to learn about delegates and lambda expression because this is something I haven't explored yet. Even if it doesn't lead to a solution that fits for my problem now, a big part of wanting to make this RPG was that I knew it would challenge me to learn much more about programming concepts and language tools without being too overwhelming.

Thanks everyone for their answers :3

It sounds like the concern is less about objects moving, but about the representation of a table of data.

In the descriptions it is a collection of "potions". You can have a number of different potions, each with their effects.

I see that as a data table, not a problem with pointers.

What the player sees is a bunch of different potion bottles, or stacks of potion bottles, or models in the shape of a bottle.

What the code sees is a structure, basically something like:

struct potion {
PotionType type,
unsigned int count,
Quality quality,
etc.
}

When you are looking for something to display, another data table maps the potion type and count and quality and other variables into an icon or a model or particle effects or whatever you need. Yet another data table maps them to the actual player effects.

This topic is closed to new replies.

Advertisement