Character Generator

I think a way forward here is to use the CharacterCreator class for just instantiating a Character.

The Character class by the looks of things is going to need to be able to store its base values (those being generated by the CharacterCreator class initially), and it’s current values. By having the base values separate, you would be able to apply temporary modifier effects to the values which can then wear off and the current value returned to the original base value where appropriate.

I’m uncertain at this point, not knowing the actual game rules well, as to whether the modifiers should also be stored. My gut feeling is telling me yes. As an example, if there was a modifier array for each characteristic, you could then re-calculate the modifiers in play at any given time, or, when another is added. It would also mean that you could potentially store the positive and negative values, e.g.

strengthModifiers
+2, +1, -3, +4

Iterate the array, total the values - sum +4, then apply that to the base value for strength and use the total as the current value. Call a recalculate anytime a modifier is applied, or expires.

Lifting the lid on this subject in this brief posts has already demonstrated very well how easy it is for things to spiral out of control. Referencing back to my previous comment about minimum viable product, one of the things you should do is decide which of these features are a must at the very beginning.

For example… if we spawned a player object using the creator at the moment, you have a set of characteristics. You could perhaps use the same creator to generate a few monsters. Drop a couple of the Ethan models (standard assets) into the scene, apply their scripts, and you’re not far away from a combat situation, that is something which can happen without the need of the modifiers at this point.

This is why we break all of the high level features down into smaller features and then determine which are going to be included, initially, later on, maybe not at all.

the mods are going to be a must because everything the player can do is based off of them. the constitution mod gives you extra hp on level up your class die roll + mod it also gives you your fortitude saves, dex mod is used for your armor class, initiative, reflex saves, and ranged attacks. Intelligents is for your skill points. wisdom is for your will saves, Charisma is for diplomacy, bluffs, and other skill.
there is not much that will realy.

I wasn’t suggesting not to add them now, I was just referring back to how projects can come to a dramatic end when they get unweildy, so identifying features up front, can be really useful. If you want to make an exact D&D clone then you could argue, obviously, that everything is needed, but the point I was trying to make is that if you wanted to take the project as a whole, cut it into deliverable slices that actually do something, which really helps with your own motivation as you develop the project, that right now, modifiers aren’t actually necessary. For example, you mentioned that they are a must because everything the player can do is based off of them - we don’t have a player yet :wink:

We can of course put them in, in some shape or form at this point. But learning what / when is a skill that is definitely worth learning with game development, or indeed any development. From experience I can certainly say that projects that have spiralled out of control near the beginning become a real grind to work on, and at the end, assuming they get to the end, its more a sense of relief that one of success.

What did you think to the earlier suggestion of the arrays for the modifiers?

sorry for my misunderstanding as for the array i think that will think that it can work

Ok, so how about this as a next step

Keep CharacterCreator as-is for the moment and have a go at creating a new class yourself which will represent the player’s character. Ideally, this class will be able to represent any character, in the future.

You know the characteristics that the character needs, and that current plan is to store a base value for each, and you know we will need to be able to store the character’s race and class (not to be confused with a C# class!). Have a think with regards to those last two, ideally we don’t want to recreate those same enums again…

For the modifiers, you have seen how to create arrays from the Dice.cs / Example.cs script on GitHub, so that should point you in the right direction.

Let me know how you get on, when you’re ready, post up your scripts and we can have a look/chat about them.

I can already envisage an issue with the arrays for the modifiers, but lets head down this road for now and then discuss again after.

so i should be able to pull the info for the races and classes from the character creator in to the player script right? and have the spots for the stats?

Pull the info yes, but we don’t want to duplicate the enum in another class.

am i on the right track

  using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Player : MonoBehaviour
{
    public Text strength;
    public Text dexterity;
    public Text consititution;
    public Text intelligence;
    public Text wisdom;
    public Text charisma;

    [SerializeField] float maxHealthPoints = 100f;

    float currentHealthPoints = 100f;

    public float healthAsPercentage
    {
        get
        {
            return currentHealthPoints / maxHealthPoints;
        }
    }
        
    [Header("Class Type")]

    [SerializeField]
    [Tooltip("Select the Class of character")]
    private CharacterCreator.CharacterClass characterClass;
    private class characterclass
    {
    }
    
    [Header("Race Type")]

    [SerializeField]
    [Tooltip("Select the Race of character")]
    private CharacterCreator.PlayerRace PlayerRace;
    private class playerrace
    {
    }
    
    public RollStats GetRollStats;
    public void Start()
    {
        GetRollStats.RollStrength();
        GetRollStats.RollDexterity();
        GetRollStats.RollConsititution();
        GetRollStats.RollIntelligence();
        GetRollStats.RollWisdom();
        GetRollStats.RollCharisma();
    }    
}


I am stuck on trying to trying to get the value of the dice rolls to input the formula for the modifier.

Hi Michael,

Just a note, the characters for the code formatting are ``` as opposed to :slight_smile:

am i on the right track

Lets take a look. So we said above about having a class that was for the character, and potentially any character, as such I would probably rename it so, as at the moment it is specifically relating to the player.

It may not even need to inherit from MonoBehaviour, depending what we do with it later, but we will leave that in for now.

public class Character : MonoBehaviour

We also said about needing to be able to store the base values as well as any modified(current) values, so that at any point in the game we can refer back to what the player had when they started, despite any modifiers which may be in effect.

I would have approached that by having two sets of variables. Also, because the Character class is about storing data, it doesn’t need references to the Text UI GameObjects, think of this as more of a holder of the data;

    public int baseStrength;
    public int baseDexterity;
    public int baseConsititution;
    public int baseIntelligence;
    public int baseWisdom;
    public int baseCharisma;
	
    public int strength;
    public int dexterity;
    public int consititution;
    public int intelligence;
    public int wisdom;
    public int charisma;

I would typically create all of my member variables as private variables also, until we know which/when/why they need to be public, but again, for now, for simplicity, we will leave them as public, but add a note to remind us later.

With regards to race and class (of character), these wre our enums from the CharacterCreator class, because we didn’t specify a different underlying type, they will have defaulted to int, zero-based, so if you look back at the earlier posts,

    private enum CharacterClass { Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin, Ranger, Rogue, Sorcerer, Warlock, Wizard };

These would have the following int values;

  • Barbarian = 0
  • Bard = 1
  • Cleric = 2
  • Druid = 3

As such, we could just store these as an int in our Character class but for now we will use the distinct types which we created when we defined our enums. Later on, these could potentially evolve in complexity in such a way that we want/need to create specific classes for each type.

    public Race race;
    public CharacterClass characterClass

Note I have used characterClass rather than just class as the variable name. There are a lot of keywords/restricted words in C# which you either can’t or just shouldn’t use as it can lead to confusion. I would prefer to not have the word character as a prefix, as this is the name of our class, another option would have been type but that is also a reserved word. This is where looking at your game rules may come in handy, as there may be an alternative word which describes class which we could use. Another good source is often thesaurus.com - for now, I’ll leave the prefix.

So at the moment, I’d probably have something resembling this;

using UnityEngine;

public class Character : MonoBehaviour
{
    // TODO: reconsider access modifiers
    public CharacterClass characterClass;
    public Race race;

    // base attribute values
    public int baseStrength;
    public int baseDexterity;
    public int baseConsititution;
    public int baseIntelligence;
    public int baseWisdom;
    public int baseCharisma;

    // TODO: is baseHealthPoints needed?
    // TODO: consider dropping the suffix "points"
    public int baseHealthPoints;
	
    // current / modified atttribute values
    public int strength;
    public int dexterity;
    public int consititution;
    public int intelligence;
    public int wisdom;
    public int charisma;

    // TODO: Consider dropping the suffix "points"
    public int healthPoints; 
}

I spotted that you had snuck in the health points also :wink: so I popped that in too, but I don’t know what your maxHealthPoints means, is this effectively another base value which is calculated when the character is created? If so, we’d want to add another base variable for it too. I’m not overly keen on the points suffix, and would probably opt for just health and baseHealth (if appropriate), only because, all of the other attribute value are also points really, and it’s good to be consistent in our code - I really wouldn’t want to add that prefix to all of the variables, and I believe that because we have a type of int for these attributes, points is effectively inferred.

One final thing we will need to change, with regards to those enums, at the moment they are locked away inside our CharacterCreator class, and our Character class will have errors about accessibility for our two variables which reference them;

using UnityEngine;

public class CharacterCreator : MonoBehaviour {

    private enum CharacterClass { Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin, Ranger, Rogue, Sorcerer, Warlock, Wizard };  
    private enum CharacterRace { Dwarf = 0, Elf, Gnome, Half_Elf, Half_Orc, Halfling, Human };
 
    // ...
}	

I would move those up so that they were outside of the CharacterCreator class definition, and make them public, thus when they are used, you do not need to precede them with CharacterCreator each time, although later, as we find more things like this, we may want to consider giving them their own namespace.

I would also drop the prefix Player from PlayerRace and replace it with Character, our class is now generic enough to be used by perhaps an NPC character.

using UnityEngine;

public enum CharacterClass { Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin, Ranger, Rogue, Sorcerer, Warlock, Wizard };
public enum CharacterRace { Dwarf = 0, Elf, Gnome, Half_Elf, Half_Orc, Halfling, Human };

public class CharacterCreator : MonoBehaviour
{
    // ...
}

Note, on the enums we have prefixed them with the word Character, as enums are often shared across the entire project, at the moment it makes sense to do this. For example, if we wanted to introduce an enum for the class of Sword (Rare, Legendary perhaps), the enum name Class would have already been used, but we could do this;

public enum CharacterClass { ... };
public enum WeaponClass { ... };

These are now very distinctive.


I know you are working through the RPG course, so I don’t want to stress you out with posts here and the course materials to work through also, but for reference, the next steps I would be considering with the above would be;

  • make a decision/investigate ref TODOs about health
  • consider constructors / properties / member variable setting
  • make the connections between the scene, the dice rolling and the attribute storing
  • modifiers
    • calculating
    • storing
      • adding
      • removing

As mentioned before, my laptop appears to be fairly dead at the moment, I’ve managed to use SafeMode to create this post, but will be replacing drives and reinstalling Windows, I’m also away for a couple of days so my responses, certainly of this length, may not be quite as frequent.

A screenshot of your features breakdown would be good to see, not sure what you are using in the way of tools for the project planning.

Thank you for all the feed back i will get started on them. About the health points that is a place holder for right now. we are using the RPG course. the way players health is going to work is based off of their class exp: at level 1 a barbarian starts with 12 hp + his con modifier. Every time he levels up he rolls a d12 plus con modifier. There is a favored class feature that the player can gain +1 to their hp or gain and extra skill point.

I am sure i am just missing this, but can you tell me how to pull the sumOfDiceRolls from one script to another script?

Hi Michael,

Is this in the context of creating an instance of the Player class and then populating its values, or something else you are trying?

populating values i have been trying for thee days now and can’t figure out how to call it.

Hi Michael,

When we started out we were looking at creating the values via the Inspector, now that we are/have moved to the scene being the interface, it’s probably a good time to clean up the CharacterCreator class.

For now, I’ve excluded both Race and Class, you have a design consideration to make here for your scene. For example, will you have a drop down menu for each, or, a series of radio buttons, or maybe your character creation is a series of steps and choosing a class and then a race are steps which follow the current step displayed in your screenshot above?

Have a think about that and then we can factor these back in.

Regarding the points I made above;

  • health
    • you’ve confirmed both baseHealth and health are necessary
    • suffixes can be removed
    • population of these values now introduces two further aspects (scope creep);
      • level
      • experience (I’m assuming this will also be neccesary in order to “level up”)
    • constructors / properties / members variables - was perhaps a discussion point which could have come before making the connection between the scene, dice rolling and storing of ability values

The following are three scripts which provide a separation of concerns approach to your character creation.

  • CharacterCreatorUI.cs - this class interacts with the UI elements in the scene and to CharacterCreator class
  • CharcterCreator.cs - this class interacts with the CharacterCreatorUI class and also the Character class
  • Character.cs - this is the class which will hold the character data

CharacterCreatorUI.cs

using UnityEngine;
using UnityEngine.UI;

public class CharacterCreatorUI : MonoBehaviour
{
    // UI objects
    private Text strength;
    private Text dexterity;
    private Text intelligence;
    private Text constitution;
    private Text wisdom;
    private Text charisma;
   

    /// <summary>
    /// Event handler for strength ability
    /// </summary>
    public void RollStrength()
    {
        strength.text = CharacterCreator.RollForBaseStrengthAbility().ToString();
    }

    /// <summary>
    /// Event handler for dexterity ability
    /// </summary>
    public void RollDexterity()
    {
        dexterity.text = CharacterCreator.RollForBaseDexterityAbility().ToString();
    }

    /// <summary>
    /// Event handler for constitution ability
    /// </summary>
    public void RollConsititution()
    {
        constitution.text = CharacterCreator.RollForBaseConsititutionAbility().ToString();
    }

    /// <summary>
    /// Event handler for intelligence ability
    /// </summary>
    public void RollIntelligence()
    {
        intelligence.text = CharacterCreator.RollForBaseIntelligenceAbility().ToString();
    }

    /// <summary>
    /// Event handler for wisdom ability
    /// </summary>
    public void RollWisdom()
    {
        wisdom.text = CharacterCreator.RollForBaseWisdomAbility().ToString();
    }

    /// <summary>
    /// Event handler for charisma ability
    /// </summary>
    public void RollCharisma()
    {
        charisma.text = CharacterCreator.RollForBaseCharismaAbility().ToString();
    }

    /// <summary>
    /// Event handler for character creation
    /// </summary>
    public void CreateCharacter()
    {
        try
        {
            int baseStrength = int.Parse(strength.text);
            int baseDexterity = int.Parse(dexterity.text);
            int baseIntelligence = int.Parse(intelligence.text);
            int baseConstitution = int.Parse(constitution.text);
            int baseWisdom = int.Parse(wisdom.text);
            int baseCharisma = int.Parse(charisma.text);

            // returns a Character class
            CharacterCreator.Create
                (
                    baseStrength,
                    baseDexterity,
                    baseIntelligence,
                    baseConstitution,
                    baseWisdom,
                    baseCharisma
                );

            // TODO: Determine what happens next
            Debug.Log("Character created");
        }
        catch
        {
            throw new System.InvalidCastException("Base ability failed to parse.");
        }
    }
}

CharacterCreator.cs

// TODO: Move these to a more suitable location, potentially their own namespace

public enum CharacterClass { Barbarian, Bard, Cleric, Druid, Fighter, Monk, Paladin, Ranger, Rogue, Sorcerer, Warlock, Wizard };
public enum CharacterRace { Dwarf = 0, Elf, Gnome, Half_Elf, Half_Orc, Halfling, Human };

public static class CharacterCreator
{
    /// <summary>
    /// Used for character creation only at this time.  Di to be thrown, type, sort order, and number of di to ignore are hard coded
    /// Rolls 4 D6, ignores the lowest value, returns the sum of the remaining dice
    /// </summary>
    /// <returns>int</returns>
    private static int RollBaseAbility()
    {
        // TODO: remove the hard-coded "4"
        int[] results = Dice.Roll(Dice.DieType.D6, 4, Dice.SortOrder.Descending);

        int sumOfDiceRolls = 0;

        // TODO: remove the hard-coded "3"
        for (int i = 0; i < 3; i++)
        {
            sumOfDiceRolls += results[i];
        }

        return sumOfDiceRolls;
    }

    // TODO: The following methods could be replaced with just RollBaseAbility, setting it to Public
    //       but there may be value in separate methods depending on how the project unfolds.  Keep for now - 09/05/18

    /// <summary>
    /// Set strength ability
    /// </summary>
    public static int RollForBaseStrengthAbility()
    {
        return RollBaseAbility();
    }

    /// <summary>
    /// Set dexterity ability
    /// </summary>
    public static int RollForBaseDexterityAbility()
    {
        return RollBaseAbility();
    }

    /// <summary>
    /// Set constitution ability
    /// </summary>
    public static int RollForBaseConsititutionAbility()
    {
        return RollBaseAbility();
    }

    /// <summary>
    /// Set intelligence ability
    /// </summary>
    public static int RollForBaseIntelligenceAbility()
    {
        return RollBaseAbility();
    }

    /// <summary>
    /// Set wisdom ability
    /// </summary>
    public static int RollForBaseWisdomAbility()
    {
        return RollBaseAbility();
    }

    /// <summary>
    /// Set charisma ability
    /// </summary>
    public static int RollForBaseCharismaAbility()
    {
        return RollBaseAbility();
    }


    /// <summary>
    /// Creates a character
    /// </summary>
    /// <param name="strength">Strength base ability value</param>
    /// <param name="dexterity">Dexterity base ability value</param>
    /// <param name="intelligence">Intelligence base ability value</param>
    /// <param name="constitution">Constitution base ability value</param>
    /// <param name="wisdom">Widom base ability value</param>
    /// <param name="charisma">Charisma ability base value</param>
    /// <returns>Character</returns>
    public static Character Create
        (
            int strength,
            int dexterity,
            int intelligence,
            int constitution,
            int wisdom,
            int charisma
        )
    {
        Character character = new Character
            (
                strength,
                dexterity,
                intelligence,
                constitution,
                wisdom,
                charisma
            );

        return character;
    }
}

Character.cs

public class Character
{
    private int baseStrength;
    private int baseDexterity;
    private int baseConstitution;
    private int baseIntelligence;
    private int baseWisdom;
    private int baseCharisma;

    private int baseHealth;

    private int modifiedStrength;
    private int modifiedDexterity;
    private int modifiedConstitution;
    private int modifiedIntelligence;
    private int modifiedWisdom;
    private int modifiedCharisma;

    private CharacterClass characterClass;
    private CharacterRace race;

    private int health;


    /// <summary>
    /// Default constructor
    /// </summary>
    private Character()
    {
        // ...
    }

    /// <summary>
    /// Overloaded constructor
    /// </summary>
    public Character
        (
            int strength,
            int dexterity,
            int intelligence,
            int constitution,
            int wisdom,
            int charisma
        )
    {
        baseStrength = strength;
        baseDexterity = dexterity;
        baseIntelligence = intelligence;
        baseConstitution = constitution;
        baseWisdom = wisdom;
        baseCharisma = charisma;

        modifiedStrength = baseStrength;
        modifiedDexterity = baseDexterity;
        modifiedIntelligence = baseIntelligence;
        modifiedConstitution = baseConstitution;
        modifiedWisdom = baseWisdom;
        modifiedCharisma = baseCharisma;
    }
}

CharacterCreatorUI.cs will need to be attached to a GameObject in your scene in order to function.
CharacterCreator.cs does not need to be attached to a GameObject, it is a static class and provides “helper” functionality in effect.
Character.cs does not need to be attached to a GameObject (yet)

Wiring it up;

  • Drag each of your ability UI Text objects into the appropriate ability field within the CharacterCreatorUI GameObject.
  • For each of the ability buttons, drag the CharacterCreatorUI GameObject to the object field for the OnClick events, select the appropriate ability method from the dropdown function menu.
  • For the “Done” button, drag the CharacterCreatorUI GameObject to the object field for the OnClick event, select the Create method from the dropdown function menu

I’ve not tested the above within Unity yet, only put it together in Visual Studio, hopefully there won’t be any issues. If it works correctly you should be able to click each individual ability button and see a value displayed in appropriate text field in the scene. When you click the done button you should see a message displayed in the console.

Let me know how this goes and if it works as outlined above, if so, I can explain the next steps with regards to create a Player GameObject with the Character class attached, but we should resolve the class and race aspects too, so give some thought to your UI / player interaction please. :slight_smile:

everything looks like it is working but i am not getting the message when i click the done button.

Are there any errors in the console?

If not, add this line to the CharacterCreatorUI class, within the CreateCharacter method;

    Debug.Log("CreateCharacter method has been called");

Add it above the try statement, so that it is the first thing that happens, re-run the game and see if you have that message appear within the console.

If you don’t, check that you have set the OnClick event to the CharacterCreatorUI GameObject in the scene and set it’s function to be the CreateCharacter method.

I am likely to be off the airwaves for about 24 hours, travelling and laptop rebuild tomorrow when a new drive turns up. I will check in periodically via my mobile but responses will be limited.

the create character function is not coming up in the list.

You are definitely looking under CharacterCreatorUI yes?

Screenshot of the OnClick event details from the inspector for me please.

Screenshot%20(21)
Screenshot%20(20)

Privacy & Terms