Make all the things…classes
This is a continuation on my series of articles on object-oriented programming. The first article, discusses how you can use role-playing game characters as a metaphor for object classes, and the second, discusses how party-systems and roles introduce polymorphism and class hierarchy.
Now that we have a basic character class and our character sheets, we can start to learn how these can be created in Java as object-oriented classes.
Classes in Java define four main things:
- What is the name of the class and how does it fit within the hierarchy and how is it classified using an interface
- What are the values that are stored and used within each unique instance of the class
- What are the actions that each unique instance of the class can perform
- What rules govern how each unique instance is created during construction
Creating some class
We can start with the first, naming the class. In Java we do that using the class statement. Each unique class is stored as a specific file, generally with the same name as the class.
If we look at our mage character sheet, this gives us a good reference to use as we create the class.
In our IDE, we will need to create our project and then create a file for the class. This file would be called Mage.java.
When we create the file, we then need to populate the class name and then use the class statement. A code block, using braces, then surrounds the code that defines the blueprint for our class. You'll notice that the only unique thing about a constructor method is that you don't need to provide a return type:
public class Mage {
}
We start with the public statement, since we want the class to be available within the program to use and create instances—we will cover more about the public statement in a bit. Then we follow with the class statement, and then the name of the class. Classes are usually capitalized, and each word is capitalized with no spaces.
Now that we have defined the basics of the class, we can start adding properties, or fields as they are sometimes called, to the class. These are essentially variables that will be unique for each instance of the class. Just like any other variable in Java, we need to type them to a primitive or class type and then give them a valid variable name.
We define these at the top of our class, within the class code block. If we look at our character sheet, we have nine items we need to add to our Mage class. These include the name of the character, the skill attributes and the expendable assets for health and magic, remembering that health and magic each are two individual values, the maximum possible value and the current value for the instance.
To define these, we need to declare their access level, which in this case would be public. If you have worked previously with basic Java apps without classes, you might have had to use public static. For this class, we only need to use public:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
}
Now, for each instance of our class, we will have nine unique fields that we can use and store values. These are unique and exclusive to each instance, but each instance will have all nine of these fields.
Now, we need to tell our class how to build each instance, or construct each instance. We do that with a special method called the constructor. The constructor method is the same name as the class, and has a code block to contain all of the rules for how we construct each instance, just like a regular method.
The constructor is usually placed after the fields have been defined at the top of the class:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
Mage() {
// Constructor
}
}
Inside the constructor we can then create the specific rules for how we create each class instance. For example, the skill attributes are calculated based on a specific number and the value of a 6-sided die. The hit points and mana are calculated based on the values of the skill attributes:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
Mage() {
// Constructor
strength = 7;
intelligence = 15;
agility = 8;
wisdom = 10;
strength += (int) (Math.random() * 6 + 1);
intelligence += (int) (Math.random() * 6 + 1);
agility += (int) (Math.random() * 6 + 1);
wisdom += (int) (Math.random() * 6 + 1);
maxHitPoints = hitPoints = strength;
maxMana = mana = intelligence + (wisdom * 2);
}
}
The last item we need to populate is the name. The name is something that we need to get more information on in order to create it. To do that, we can require that each time you create a mage, you need to provide a name, then that name is saved in the name field for the class instance. We can pass in a value into the constructor just like any Java method:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
Mage(String newName) {
// Constructor
name = newName;
strength = 7;
intelligence = 15;
agility = 8;
wisdom = 10;
strength += (int) (Math.random() * 6 + 1);
intelligence += (int) (Math.random() * 6 + 1);
agility += (int) (Math.random() * 6 + 1);
wisdom += (int) (Math.random() * 6 + 1);
maxHitPoints = hitPoints = strength;
maxMana = mana = intelligence + (wisdom * 2);
}
}
Finally, we need our mage to be able to do something. We can create a method that will display the stats of our mage, so we can see the attributes and health information for our instance. We would create that as a regular method, providing a return type (which in this case is void) and then wrap everything in a code block. We can then refer to the values, or fields, in our class by their variable names:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
Mage(String newName) {
// Constructor
name = newName;
strength = 7;
intelligence = 15;
agility = 8;
wisdom = 10;
strength += (int) (Math.random() * 6 + 1);
intelligence += (int) (Math.random() * 6 + 1);
agility += (int) (Math.random() * 6 + 1);
wisdom += (int) (Math.random() * 6 + 1);
maxHitPoints = hitPoints = strength;
maxMana = mana = intelligence + (wisdom * 2);
}
public void showStats() {
System.out.println("----------------------------------");
System.out.println(name + ", a mage:");
System.out.println(" Strength: " + strength);
System.out.println("Intelligence: " + intelligence);
System.out.println(" Agility: " + agility);
System.out.println(" Wisdom: " + wisdom);
System.out.println(" Hit Points: " + hitPoints + " / " + maxHitPoints);
System.out.println(" Mana: " + mana + " / " + maxMana);
System.out.println();
}
}
We now have a complete class. We have defined the class name, identified the fields in the class, specified the rules that govern how each class instance is created, and have given the class an action to perform.
Instantiate thyself, Class!
To create an instance of our class, we need to then go back to where our main() method is in our program. In the past, we have always used the main() method to start our program, and generally in Java, the main() method is the consistent starting point for every program.
In another class file, in this case, I called mine Main.java, I have a standard main() method that will start the program:
public class Main {
public static void main(String[] args) {
}
}
In my method, I can then create an instance of our new Mage class. Just like with any variable or container, I would need to define the type of value that is going to be stored in the variable. In this case, we would type it to the class. So the type—is Mage. We then need to provide a name for the class instance, which I will call myMage. We then say that we are creating a new instance of the Mage class with the new statement, and then trigger construction by calling the constructor method. Our constructor requires a String to be passed in with the character's name:
public class Main {
public static void main(String[] args) {
Mage myMage = new Mage("Francisco");
}
}
We now have our first instance of the Mage class created! But it we run the program, nothing happens. To prove that we have an instance created, we can add a line to the constructor that outputs text to the screen when an instance is built. We can add this line to the end of our constructor:
public class Mage {
// Name
public String name;
// Skill attributes
public int strength;
public int intelligence;
public int agility;
public int wisdom;
// Health and magic
public int maxHitPoints;
public int hitPoints;
public int maxMana;
public int mana;
Mage(String newName) {
// Constructor
name = newName;
strength = 7;
intelligence = 15;
agility = 8;
wisdom = 10;
strength += (int) (Math.random() * 6 + 1);
intelligence += (int) (Math.random() * 6 + 1);
agility += (int) (Math.random() * 6 + 1);
wisdom += (int) (Math.random() * 6 + 1);
maxHitPoints = hitPoints = strength;
maxMana = mana = intelligence + (wisdom * 2);
System.out.println("A new mage named " + name + " has been created!");
}
public void showStats() {
System.out.println("----------------------------------");
System.out.println(name + ", a mage:");
System.out.println(" Strength: " + strength);
System.out.println("Intelligence: " + intelligence);
System.out.println(" Agility: " + agility);
System.out.println(" Wisdom: " + wisdom);
System.out.println(" Hit Points: " + hitPoints + " / " + maxHitPoints);
System.out.println(" Mana: " + mana + " / " + maxMana);
System.out.println();
}
}
Now if we run our program, we get this message in the output panel:
A new mage named Francisco has been created!
Process finished with exit code 0
We can then access and work with various fields in our program, and execute actions that the instance can perform:
public class Main {
public static void main(String[] args) {
Mage myMage = new Mage("Francisco");
System.out.println(myMage.agility);
myMage.showStats();
}
}
We use the name of the class instance, myMage, to refer to the unique instance of the Mage class and then access the fields and methods of that instance to run our program.
A new mage named Francisco has been created!
14
----------------------------------
Francisco, a mage:
Strength: 10
Intelligence: 19
Agility: 14
Wisdom: 15
Hit Points: 10 / 10
Mana: 49 / 49
Process finished with exit code 0
Since we have a class, we can then create multiple instances of that class, referring to each one using a unique name:
public class Main {
public static void main(String[] args) {
Mage myMage = new Mage("Francisco");
myMage.showStats();
Mage myOtherMage = new Mage("Jaana");
myOtherMage.showStats();
}
}
Our two instances share identical blueprints, fields, methods and constructors, but the values stored within it are unique and exclusive to that instance. So when we run this program, we see the different values in each instance displayed on the screen:
A new mage named Francisco has been created!
----------------------------------
Francisco, a mage:
Strength: 12
Intelligence: 19
Agility: 13
Wisdom: 16
Hit Points: 12 / 12
Mana: 51 / 51
A new mage named Jaana has been created!
----------------------------------
Jaana, a mage:
Strength: 13
Intelligence: 18
Agility: 12
Wisdom: 16
Hit Points: 13 / 13
Mana: 50 / 50
Process finished with exit code 0
With this basic class structure, we can then expand and build on it. For our game, we have four unique player character types. We have a mage, fighter, priest and paladin. Each of these would be a unique class, and we can create a unique class file and definition for each one, and then create as many instances of them as we want.
Additional information
For more information on how to create classes in Java, check out this video from my "Computer Science Principles Lab: Java" course on LinkedIn Learning:
#roleplaying #games #learntocode #java #programming #programmer