Inventory system design and development

This tutorial describes the design and development of an inventory system for a dungeon crawler game. An object-oriented paradigm is employed, with a UML class diagram created using the draw.io tool, used to identify the main objects and their relationships. This class diagram will be used as a framework for the development of code.

For this initial iteration of the inventory design, the design consists of four classes. All item objects (represented by the Item class) include name and weight attributes. Two specialisations of the Item class are defined using inheritance. The first of these is the Weapon subclass which adds two additional attributes representing the minimum and maximum amounts of damage done by the weapon. The Armour subclass has one additional attribute that is used to represent the level of protection provided by the armour.

The Inventory class is used to keep track of all items carried by a player character. The directed link from the Inventory class to the Item class indicates that each inventory contains 0 to many items. Attributes in the class are used to represent items that are currently equipped (in use) by the player character. Methods are included that enable items to be added and removed from the inventory. The total_weight method calculates the combined weight of all items in the inventory – this can be used to determine whether a character is encumbered. The last three methods shown are used to equip items.

Item class

The Item class contains an initialiser method for creating new instances. The name of the item and its weight is passed as arguments to the initialiser method, these are then used to set the name and weight attributes. A third attribute, bonus, is used to represent a (typically magical) bonus that improves the capabilites of the item (e.g. a weapon would deal more damage, while armour would provide more protection).

As shown in the class diagram, two subclasses of the Item class are created. Subclasses inherit the attributes and methods of its parent class, but may include additional attributes and/or method, or may indeed override (re-implement) methods of the parent class. In our example the Armour and Weapon subclasses are introduced. They both have additional attributes that make the subclass as specialisation of its parent. Armour includes an additional attribute, armour_class, which models the amount of protection that the armour provides. The Weapon class includes attributes representing the minimum and maximum amount of raw damage done by the weapon.

Both of the subclasses override the initialiser method of the parent class. The initialiser method for the Armour class includes an additional argument, ac, used to the armour_class attribute. The initialiser of the subclasses also calls the initialiser method the parent class using the super method, as shown on lines 10 and 16 of the code listing below.

class Item:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight
        self.bonus = 0

class Armour(Item):
    def __init__(self, name, weight, ac):
        super().__init__(name, weight)
        self.armour_class = ac

class Weapon(Item):
    def __init__(self, name, weight, min_damage, max_damage):
        super().__init__(name, weight)
        self.min_damage = min_damage
        self.max_damage = max_damage

Inventory class

The Inventory class keeps track of the items a player character has in their possession. The collection of items is stored in a list, which is initially empty. Additional attributes are used to model the items that are currently equipped by the player character. For this initial design, items held in the right and left hands, together with any armour worn are modelled.

A method for adding items to the inventory is defined next, followed by a method that is called to remove an item from the inventory. For the latter method a check is done to ensure that the item is in the inventory before it is dropped.

class Inventory:
    def __init__(self):
        self.contents = []
        self.right_arm = None
        self.left_arm = None
        self.armour = None

    def add_item(self, item):
        self.contents.append(item)

    def drop_item(self, item):
        if item in self.contents:
            self.contents.remove(item)

The following method calculates the total weight of the items in the inventory. This is done using a list comprehension to collect all of the weights into a list. The sum function is then applied to this to find the total weight.

    def total_weight(self):
        return sum([i.weight for i in self.contents])

The following method is used to equip a weapon in the right hand of the player character. A check if done to ensure that the item is in the inventory contents and that the item is a weapon. The method returns a Boolean-valued result indicating whether or not the method was successful.

    def equip_right_arm(self, item):
        if item in self.contents and type(item).__name__ == "Weapon":
            self.right_arm = item
            return True
        else:
            return False

The following method creates a string representation of the inventory. The equipped items are shown first, using an inline if statement to check whether or not the equipment slots contain an item. A for loop is then used to represent the complete contents of the inventory.

    def __str__(self):
        result = "Equipped items\n"
        result += "---------------\n"
        result += "Right Arm ::" + (self.right_arm.name if self.right_arm else "empty") + "\n"
        result += "Left Arm ::" + (self.left_arm.name if self.left_arm else "empty") + "\n"
        result += "Armour ::" + (self.armour.name if self.armour else "empty") + "\n"
        result += "\nOther items\n"
        result += "---------------\n"
        for i in self.contents:
            result += i.name + "\n"
        return result

Driver Code

The driver code shown below is used to test the correctness of the previous code. Instances of armour and weapon classes are created. A new inventory object is created with the item instances added to this inventory. The contents of the inventory is then printed using the string representation method which is called implicitly when the inventory object is passed as the print argument. Further tests are done to check the correctness of the other methods from the inventory class.

from item import *
from inventory import *

armour1 = Armour("Plate Armour", 120, 18)
armour2 = Armour("Leather Armour", 20, 12)

weapon1 = Weapon("Longsword", 15, 1, 8)
weapon2 = Weapon("Axe", 8, 1, 6)

# create new inventory and add items
inventory = Inventory()

inventory.add_item(armour1)
inventory.add_item(armour2)
inventory.add_item(weapon1)
inventory.add_item(weapon2)

print(inventory)

# remove item from inventory

inventory.drop_item(armour2)
print(inventory)

# check the total weight of the inventory
print(inventory.total_weight())

# equip a weapon with right arm
inventory.equip_right_arm(weapon1)
print(inventory)

# try equipping right arm with armour
inventory.equip_right_arm(armour1)

print(inventory)

Alice Tutorial: Indefinite Loops

The learning objectives for this tutorial are:

  • Students can design and implement programs using indefinite loops
  • Students can use the random number function in their programs
  • Students can design and use class level methods

In this activity you will design and develop a cat and mouse game. The cat chases the mouse at the same time the mouse moves in a random direction trying to evade the cat. When the cat finally catches the mouse it will enjoy a tasty meal. To assist in the development of the program we provide a number of suggested development steps.

Step 1: creating the world

Create a new world that includes a Cat object (Animals) and a Mouse object (Animals). Ensure that the mouse is initially a reasonable distance away from the cat and that it is facing away from the cat. You may wish to add some additional scenery to make the chase a little more interesting.

Cat and mouse game

Cat and mouse game

Step 2: creating Chase method

Create a new class method for the Cat class called chase. In this method the cat should turn to face the mouse, then move forward 1 metre. You may wish to add some animation to the cat’s movement, but you should do this after you have completed the other steps.

Step 3: creating run away method

Create a new class method for the Cat class called runAway. In this method the mouse should turn left or right a random amount (up to a maximum of ¼ of a revolution in either direction). You will need to use the random number function to determine the amount to turn. The mouse should then move forward 1 metre.

Step 4: Introducing a loop

In my first methodcreate a while loop. In the body of the while loop you must call the methods chase and runAway. You will need to design a test for the while loop, such that the loop continues until the cat is within a certain distance of the mouse (use an appropriate function to determine the distance between the cat and mouse).

STEP 5: catching the mouse

Create a new class method for the Cat class called eatMouse. The method should perform an appropriate action once the cat catches the mouse. Call the eatMouse method in my first method after the while loop.

Alice Tutorial: Collision Detection

Introduction

Collision detection is a common feature of many animation tools, so a question that sometimes is asked when students first start using Alice is “where is built-in support for collision detection?”. The short answer is that there isn’t any. Remember that Alice is a programming environment in which students can develop animations, as opposed to a purpose built animation tool.  However, because it is a programming environment, Alice gives programmers the ability to readily develop their own support for collision detection. This tutorial provides a description of how such collision detection support can be implemented.

The Alice World

The world for this tutorial consists of a Humvee van and a number of obstacles. Event handling methods and event handlers have already been implemented that allow the user to move the car around the world. For example the code for driving the Humvee forward is as follows. A “Do together” block is used to drive the move the car forward and at the same time rotate the wheel.


humvee.driveforward()
  No variables
  Do together
    humvee move forward 5 meters
    humvee.frontRightWheel turn forward 1 revolutions
    humvee.backLeftWheel turn forward 1 revolutions
    humvee.backRightWheel turn forward 1 revolutions
    humvee.frontLeftWheel turn forward 1 revolutions

 

Setting up collision detection

To set up collision detection and collision avoidance we begin by adding a method, called collision, to the world object  that detects and reacts to collisions. We begin by setting this up for a single obstacle, in this case a building and then extend the code so that we can handle multiple obstacles.  To detect a collision we use the “is within threshold of” function from the humvee object. This is used as the conditional of an if-then-else statement. If the condition is true, then the humvee reacts to the collision by moving backwards a short distance.

When using the “is within threshold of” function, we need to bear in mind that the function is measuring the distance between the centre of the Humvee and the centre of the obstacle. A collision with the side of the obstacle will therefore occur when the distance between the centres is half of the obstacle width + half of the Humvee depth. This distance will when the Humvee is approaching from a different direction, e.g. from the front of the building (in which case we might use the half of the obstacle depth). For this tutorial we just use the above calculation, but we could include a more sophisticated check by determining which direction the Humvee is approaching the obstacle from using the “is in front of” and similar functions.

After developing the collision method, we create a “while world is running” event handler. This event handler will call the collision method, such that whenever a collision is detected the Humvee will react and move backwards.

Dealing with multiple obstacles

So far we can only handle a single obstacle. We could of course develop similar methods to handle the other obstacles, but the DRY (don’t repeat yourself) principle suggests we should find a better way of doing this. Rather than repeating the code for the other obstacles, what we will do is generalise the existing code to handle many obstacles. To do this we use an list variable to represent the collection of all obstacles and then use the “for all do together” construct to implement collision detection and avoidance within a single method.

Collision Detection

Video Tutorial

The following video tutorial provides a step-by-step description of the development of the collision detection capabilities.

Alice Mini Task: Simple Programming

The learning objectives of this tutorial are:

  • Students can design a simple scenario using a combination of sequential and parallel actions, as well as simple loops
  • Students can implement their design using an appropriate combination of constructs.

Exercise 1


Create a new world using the space theme with a jump jet. In this exercise you will use “do in order” (sequential) and “do together” (parallel) constructs to move the jump jet.

  1. In my first method, use the move method from the JumpJet object to move the jump jet up 5 metres, then forward 40 metres.
  2. Modify your code so that the left and right jet engines are turned backwards ¼ of a revolution before the jet moves upwards. You should use a “do together” block to ensure that the two engines are turned at the same time.
  3. Modify your code so that the jet engines are turned to their starting positions before the jet moves forwards.

alice_space_scene

Exercise 2

Create a new world with a dinosaur (or other large animal that has a moveable jaw) and a second animal, such as a chicken. In this exercise you will get the dinosaur to turn to face the chicken, then say “Hello” to the chicken while moving its mouth at the same time.

  1. Write down a pseudo code design for the above scenario.
  2. Use the “Do together” and “Do in order” constructs to implement the above scenario.

Now modify your code, using a simple loop, so that the dinosaur opens and closes its mouth 2 times each time the dinosaur says something. You will need to change the duration for the jaw movement commands.

Alice Tutorial: Getting Started

Before attempting the steps shown below you may wish to complete the tutorials that come packaged with Alice. Click on the Tutorial tab in the Welcome to Alice screen. You may not understand all the concepts in the tutorials – do not worry we will explain these later in the course.

Step 1


Run the Alice program and create a new world by clicking on the Templates tab on the Welcome to Alice screen.  Then select the grass Template.

Selecting a background

Selecting a background

Select the Add Objects button that appears next to the world view. The world view will be expanded and the Local Gallery will appear at the bottom of the screen.

From the Local Gallery, select the Medieval folder, then select the Horse object and Princess object and drag them to the world view.

Inserting objects

Inserting objects

At the moment the two objects are most likely not facing each other.  In the Object Tree, right click on the Horse object, select methods from the menu and then select “horse turn to face”. Select Princess as the target. Do the same for the Princess object.


Finally make the Princess bow, by selecting the UpperBody part of the Princess and then selecting the turn method, turning the upper body forward ¼ of a revolution.

At this stage you should experiment with other methods on the princess and horse to move various body parts. At any stage you can press undo to cancel a particular move.

Step 2

Add a Knight object from the Medieval folder.  This will give you practise in positioning objects in the 3D world. Our aim is to sit the Knight on the Horse. The easiest way to position objects relative to other objects in the world is to use the quad view.You will need to use the different movement controls shown under the quad view button. This will take a bit of experimentation to get right, so don’t panic if you don’t get it right first try.

alice_intro_position

You will notice that the legs of the rider merge into the horse, which doesn’t look great. What you need to do is reposition the legs so that they look more natural. You should experiment with the turn methods on the different parts of the rider’s legs. This will again take a few tries, but remember you can always use undo if you make a mistake.

Programming Assignment

AliceSpashScreen

The learning objectives for this project are:

  • Students can design and implement programs using the Alice programming environment
  • Students can present their completed designs and implementations and reflect on lessons learnt.

For this assignment you will design and program a story or simple game using the Alice 3D programming environment. The aim of this assignment is for students to learn the basic and core concepts of computer programming, and to learn how to document and describe your work.

The assignment contains a number of separate tasks (or milestones) that must be completed at various stages over the course of the unit on  programming.

The assignment is worth 50% of the total marks for the Application Programming unit.

Tasks

Story/Game outline [5%]

For this task you are required to write a story or an outline of a simple game involving some of the characters and objects from the Alice 3D world.

Initial Design [5%]

For this task you are required to write a design (using pseudo-code) for your Alice story/game. The design should include

  • sequencing (actions that occur in order);
  • concurrency (actions that occur at the same time);
  • conditionals (actions that only occur if a certain condition is met);
  • and loops (repeating actions).

The design should address a large portion of your story/game, but does not have to address all aspects yet.

Feedback on your design will be provided.

Portfolio [30%]

At the end of the Application Programming unit, each student will be required to complete a portfolio that includes:

  • The final story/game outline
  • The final design
  • A report on your development process and lessons learned (this should include the items discussed in your presentation)
  • The final code

The final code must be submitted to the teacher Dropbox.