Final Design

Final Design Document
[Download Powerpoint]

Breakout Design Document
[Download Powerpoint]

Minigolf Design Document
[Download Powerpoint]

Arcade Code
[Link]

User Guide

Duvall's Diva's Game Engine User Guide

How to Create a Game Using Duvall's Diva's Game Engine

Our framework consists of the hierarchy

Game -> Level -> GamePieces

Where each Game contains Levels and each Level contains GamePieces.

When creating a new game, one doesn't have to change anything about the Game class. All you have to do is create a subclass of the Level class. Using our framework, your game will be won when all the levels are beaten. You lose the game when you lose a level.

  1. Subclass Level for your game
    1. Can use default Level constructor or specify a type of collision detection with Level(CollisionDetection detector)
    2. Specify a serialVersionUID since your level will be serialized when created in the level editor.
    3. Implement hasLost() - returns true if you have lost the game, false otherwise
    4. Implement hasWon() - returns true if you have won the game, false otherwise
    5. Implement getLevelTemplatePieces() to specify what GamePieces should exist in a level when the level editor is opened. Note: don't forget to set the position of these pieces, or they will all appear at the same default position.
    6. Optional Overrides
      1. Override onLevelChange() to specify what happens between levels. By default, a level won dialog is displayed.
      2. Override onGameEnd() to specify what happens when the game ends. This can be used to clear game-specific state for example. This does nothing by default.

Example Level subclass:

public class FooLevel extends Level

{

private static final long serialVersionUID = -179177457789930485L;

@Override

public List<GamePiece> getLevelTemplatePieces()

{

List<GamePiece> template = new ArrayList<GamePiece>();

template.add(new FooMan(new DoublePoint(200, 200));

template.add(new Monster(new DoublePoint(300, 300));

return list;

}

//when the player is out of lives, you lose

@Override

protected boolean hasLost()

{

return getPlayer().getLives() <= 0;

}

//when there are no more monsters, you win

@Override

protected boolean hasWon()

{

return count(new Monster()) <= 0;

}

}

All levels must have a protagonist player GamePiece. ControllableGamePiece represents the character you will play in the level.

  1. Implement one class that extends ControllableGamePiece
    1. Implement setUpNextLevelPlayer(ControllableGamePiece nextLevelPlayer), which sets the state of the new player object for the next level. This method is called every time you advance to the next level to transfer variables of the level you just beat player to the next level. The method passNextPlayerScoreAndLives may be useful to use in this method.
    2. Write methods that will be called on any desired key press, mouse press or mouse release. These methods will be connected to their proper actions in Section 4. Note: these methods should take no parameters, except for if they are related to mouse events. In that case, the methods take a DoublePoint parameter. This DoublePoint parameter contains the mouse coordinates of user actions.
    3. Implement the necessary methods from GamePiece (see #3)

Levels also have non-player GamePieces, which act as labels, enemies, powerups, etc. By convention, each GamePiece only controls its own state, but the ControllableGamePiece can alter the state of other GamePieces.

  1. Implement desired game pieces (subclasses of GamePiece)
    1. Specify a serialVersionUID since GamePieces will be serialized when created in the level editor.
    2. Implement respondToCollision(GamePiece objectCollidedWith, DoublePoint normal) to dictate what happens when this game piece collides with any other game piece.
    3. Implement respondToCollisionWithBoundaries(DoublePoint normal) to dictate what happens when this game piece collides with any of the level boundaries
    4. The bounce() and bounceOffBoundary() methods are useful to use in the above methods for GamePieces that simply bounce.
    5. Implement resetMe(), which is called when the level is reset, and should reset the game piece however you choose.
    6. If desired, override any of the other methods of GamePiece. The most helpful methods to override are as follows.
      1. translate() - changes the position of the game piece. Uses the velocity exclusively by default.
      2. draw() - draws the game piece. Uses the image associated with this GamePiece's class in the images resource file by default.
      3. respondToTouching() - specifies what to do when this GamePiece is touching another GamePiece. This is for the case when GamePieces are not “uncollided.” Does nothing by default. The difference between a collision and touching is that a collision happens the moment two GamePieces touch, but respondToTouching is continuously called.
      4. getNormal() - specifies the surface normals of the GamePiece based on the GamePiece that collides with it. Returns normals for a rectangular object by default.
      5. doTimeDrivenEvent() - this method is called every frame. The current time is passed in, in milliseconds, so events depending on time can be executed. Does nothing by default. Using the modulus operator on the time value can be useful in this method. Note: the modulus operator can be useful in this case to do events every x milliseconds.
    7. Prepare for Level Editor - the pieces you make can be made to be dragged and dropped in the level editor. Their properties can be modified in the level editor.
      1. GamePieces are modifiable in the level editor by default. To make it so a GamePiece cannot be modified in the level editor, use setModifiable(false) in the constructor.
      2. Any GamePiece that you want to be created in the level editor must have a default constructor.
      3. If desired, override getModifiableAttributes and setModifiableAttributes. By default, both allow the user to edit position, rotation, velocity, size, and the image in the level editor.
        1. getModifiableProperties returns a LinkedHashMap of Strings to Strings, the key being the name of the attribute, and the value is the value of the attribute as a String.
        2. setModifiableProperties takes the same LinkedHashMap and sets the GamePiece’s state from the String values in the LinkedHashMap.
      4. Add the pieces you want to be able to drag and drop to your editor properties file (detailed below)

Example ControllableGamePiece (GamePiece as well)

public class FooMan extends ControllableGamePiece

{

private static final long serialVersionUID = -1175608104847682464L;

private static final int DEFAULT_NUM_LIVES = 3;

private static final int STEP_LENGTH = 2;

private int myHairLength;

public FooMan()

{

super(DEFAULT_NUM_LIVES);

}

/*

* Methods that are mapped to keys

*/

public void walkLeft()

{

setPosition(new DoublePoint(getPosition().x - STEP_LENGTH,

            getPosition().y));

}

public void walkRight()

{

setPosition(new DoublePoint(getPosition().x + STEP_LENGTH,

            getPosition().y));

}

public void walkUp()

{

setPosition(new DoublePoint(getPosition().x,

            getPosition().y - STEP_LENGTH));

}

public void walkDown()

{

setPosition(new DoublePoint(getPosition().x,

            getPosition().y + STEP_LENGTH));

}

@Override

protected void setUpNextLevelPlayer(ControllableGamePiece nextLevelPlayer)

{

passNextPlayerScoreAndLives(nextLevelPlayer);

}

@Override

protected void resetMe()

{

resetPosition();

}

@Override

protected void respondToCollision(GamePiece objectCollidedWith,

            DoublePoint normal)

{

if(objectCollidedWith instanceof Monster)

{

loseALife();

}

}

@Override

protected void respondToCollisionWithBoundaries(DoublePoint normal)

{

//Kill all the monsters if you collide with the west wall, you win

if(normal.equals(Direction.WEST))

{

removeAllGamePiecesOfTypeFromMyLevel(new Monster());

}

}

//Return all the default modifiable attributes as well as myHairLength

@Override

public LinkedHashMap<String, String> getModifiableAttributes()

{

LinkedHashMap<String, String> map = super.getModifiableAttributes();

map.put("Hair Length", myHairLength+"");

return map;

}

//Set all the default modifiable attributes as well as myHairLength

@Override

public void setModifiableAttributes(LinkedHashMap<String, String>

              attributes)

{

myHairLength = Integer.parseInt(attributes.get("Hair Length"));

super.setModifiableAttributes(attributes);

}

}

  1. Write a properties files with any key or mouse actions you would like to use, as the keys, and the associated methods as the values
    1. For keys, the key is just the lowercase letter the key represents
    2. For mouse actions, use MOUSEDOWN and MOUSEUP

Example controls properties file:

a = walkLeft

d = walkRight

w = walkUp

s = walkDown

    If you don’t create this file, your game will work but you will not have any controls.

  1. For GamePieces that intersect each other, it is useful to specify which type of GamePiece will be drawn on top of the other. In our engine, this is called the draw order. You may create a properties file with the draw orders of GamePieces. GamePieces with a higher draw order will be drawn on top of GamePieces with a lower drawOrder.

Example draw order properties file:

Foo.FooMan = 100

Foo.Monster = 50

Foo.Grass = 0

    If you don't create this file, your game will work, but no GamePieces will be given priority in terms of draw order.

  1. Create a level editor properties file. You must include the type of Level that the level editor can create, which is the subclass of Level you have created, and the class names of the GamePieces that can be created in the Level Editor.

Example level editor properties file:

LevelType = Foo.FooLevel

CreatablePieces = Foo.FooMan,Foo.Monster,Foo.Grass

    If you don't create this file, the level editor will not be able to edit levels.

  1. Create an images properties file. This file specifies the image file for each GamePiece. By default, the key to represent a GamePiece is its class name. The value is the path to the image. If you want to use the default splash screens, you must set their image files in this properties file as well.

Example image properties file:

Foo.FooMan = /dir/to/man.gif

Foo.Monster = /dir/to/monster.gif

Foo.Grass = /dir/to/grass.gif

  1. Create a sounds properties file. This file specifies the sound file for each type of sound. The format is as follows: the key is the name of the sound you will use to reference the sound. The value is the path to the sound file. There are some special keys that can be used to define special, predefined sounds events that you don't have to add yourself. You only have to define the sound file if you want to use them. If you choose not to define any of these, they simply will not be played. Supported sound files are wav, aiff, au, mid, and rmf files.

LevelComplete is the sound file played when a level is beaten. It will play only once.

BackgroundMusic is the sound file played during the game as background music. It will loop until the game has ended.

GameWin is the sound file played when the game is beaten. It will only play once.

GameWinLoop is the sound file played when the game is beaten. It will loop until the win splash screen is removed.

GameLose is the sound file played when you lose. It will only play once.

GameLoseLoop is the sound file played when you lose. It will loop until the lose splash screen is removed.

Title is the sound file played when the title screen is shown. It will only play once.

TitleLoop is the sound file played when the title screen is shown. It will loop until the title splash screen is removed.

Example sound properties file:

Die = /dir/to/die.wav

Growl = /dir/to/growl.wav

GameWin = /dir/to/TickTock.wav

    If you don't create this file, no sounds will be played in your game.

  1. Once you have created your levels in the level editor, create a levels properties file. This file specifies the level files for your game. The key is the level number and the value is the path to the level file. The keys don't have to be written in exact order, but they will be played based on the order of the keys numerically.

Example levels properties file:

1=/dir/to/Level1.level

2=/dir/to/Level2.level

    If you don't create this file, your game will have no levels.

  1. If desired, create a display strings properties file. This file contains all the display strings for your game so that they can be easily changed and localized.

Example display strings properties file:

Win = You Win!

Lose = You Lose!

    If you don't define this file, but use VoogaResources to get display strings, GUI String Not Found: (your key) will be returned.

  1. Optional: Create subclasses of SplashScreen for screens to be displayed when the player opens the game and when the player loses the game.
    1. By default, the image for the splash screens will be retrieved from the images properties file.
    2. You may subclass SplashScreen in order to add buttons or other components to your SplashScreen.
  2. In the root of the source folder, create a file named setUp.properties. This file contains the filenames of the other properties files. Use the keys below to specify each resource file and the values are the paths to those files.

Example setUp.properties

DefaultControlsFile=/dir/to/defaultControls.properties

SoundsFile=/dir/to/sounds.properties

ImagesFile=/dir/to/images.properties

DrawOrderFile=/dir/to/drawOrder.properties

DisplayFile=/dir/to/display.properties

LevelEditorFile=/dir/to/editor.properties

LevelsFile=/breakout/resources/levels.properties

TitleScreen=Foo.FooTitleScreen

Useful Classes:

Classes with static methods that are always available

  1. VoogaResources - use its methods to access the display strings in your resource file. See Javadoc for all usage options.
  2. MessageDisplayer - display messages on the screen during game play. See Javadoc for all usage options.
  3. Sounds - play sounds, using the sounds you defined in the resource file. See Javadoc for all usage options.

If you are having trouble with something, we have provided a debug mode with our engine. To enable the debug mode, set the DEBUG_ON variable in the Game class to true. When the debug mode is on, the stack traces to exceptions will be shown to give more information about your errors. You may also click on any GamePiece during game play to get information on that GamePiece. The bounds for every GamePiece are also shown.

Happy coding.

Refactored Design

Refactored Breakout Design Download
Refactored MiniGolf Design Download
Project Artifact Download

Design Overview

We are using the model view controller pattern for our project.

The Game class in the Engine package is the model, the Controller class in the Controller package is the controller and the views are in the Views package.

This model allows us to compeletely decouple the model from the view. We used observable observer model between the model and the controller, to decouple the model(observable) and the controller(observer). The view is also decoupled with the controller. It doesn't have to know about the controller since it is being passed in to the controller which then adds action listeners. Therefore, both the model and the view are standalone enclosed objects with a public API that hides the inner workings of these objects.

Package Overview

  • Controller - Facilitates the communication between view and the model
  • Engine - Holds all classes that are critical abstractions to all games
  • View - Visualizes the game engine
  • Breakout - Contains all classes, levels, and resources specific to breakout
  • MiniGolf - Contains all classes, levels, and resources specific to mini golf
  • Tests - Contains unit tests

Controller Package

  • Controller - Facilitates communication between model and view
  • GameplayAreaController - Sub-controller dedicated to connecting AbstractGameView and model

View Package

  • EditorView - Visual representation of level editor
  • EditorPane - Sub-controller dedicated to connecting AbstractGameView and model

Engine Sub-Packages

  • Kit - Holds generic reusable objects for game makers
  • SplashScreen - Holds all classes related to the logic of splash screens
  • Util - Holds all utility classes used by the engine

Engine > Kit

  • Images - Holds generic reusable objects for game makers
  • Sounds - Holds all classes related to the logic of splash screens
  • GamePieces - Reusable components that game makers can use to same time
    • GameLabel - generic text label ina game
    • LivesLabel - text label linked to the player's lives
    • PassiveGamePiece - abstract game piece that doesn't respond to collisions
    • ScoreLabel - text label linked to the player's score
    • Wall - Non interactive object that other game pieces can collide with

Level Editor Design 2.1

The goal of the level editor is for users to easily generate .level files that is read by the game to initialize the level.

The interface to create a new level is in two panes. The left pane contains an empty template for the level and the right pane contains all the tools to modify the level.

The right pane contains 3 tabs, one tab is for opening up a pre-existing .level file or a .level template to minimize the amount of work a level maker would have to do.

Another tab is a list of creatable in game objects represented as images. These images are drag able in to the left pane to create the object in the level. When the object is created on the blank template the third tab opens which allows users to modify parameters for the object.

The way we would implement this is to create an interface called Creatable which game pieces to be included in the level editor would implement. This interface would require the game pieces to implement the methods getModifiableProperties() and setModifiableProperties(List variables). setModifiable properties contain list.adds that adds variables that can be modified to the variables List. Then setModifiableProperties(List variables) will return a list of variable names that the user wishes to be modifiable.

When the user clicks on an object in the game area of the level editor, we would immediately call this method and use the list of variable names along with reflections to look up the values for all the text fields we wish to create. The GUI would then use that information to create user modifiable fields to set the object parameters.

At the bottom of this series of text fields we would have a button entitled “Submit Changes.” When the user clicks this button, we would then iterate through each of the modifiable variables, and use reflection to directly set them inside the object to be changed. We realize that this violates the principle of encapsulation, but we feel that this will be the most pain free, direct way to achieve our goals.

Once the user is pleased with the exciting level he has created, there will be a button called, “Save,” which will allow the user to save the .level file. Eventually he can load that .level file onto the website so that he can enjoy playing his awesome level. If time permits, we will integrate this level editor into the website, so that the user can make awesome levels without ever having to leave his browser (IE 6).

Design 2.0

Game Design

Arcade

Our arcade is completely web based and a preliminary version can be seen at http://vooga.ocirs.com. The page is generated with the combination of PHP and MySQL.

A visitor can either chose to create a login or play as a guest.

A visitor would simply click on the title or screenshot (yet to be implemented) of game they want to play in the home screen and be taken to the page where the game is embedded to the webpage. At the end of each level if the visitor is logged in, the score would be passed out of the jar via javascript in to the MySQL db to store the player’s performance.

Visitors can also upload their own games by going to add games and upload a jar file containing a class called Applet in the default package that will run the game. The newly uploaded game will automatically show up in the home page. It is also possible for visitors to upload new versions of their game in the update game page or delete their uploaded games in the delete game page.

There would also be a page displaying the players with the highest total score, most levels played and highest score for each kind of game, which would be generated from the MySQL database.

The PHP code was designed to be extensible. All of the heavy lifting is in the game class, which can create, remove or update games in both the web directory and the database. It can display the embed for games given the game name. All of the MySQL queries and file modifications are abstracted by the game class.

The webpage is also not limited to jar files, flash files or any type of embeddable content can be uploaded if there is a case for it in a switch statement in the game class.

Level Editor Design

The goal of the level editor is for users to easily generate .level files that is read by the game to initialize the level.

The interface to create a new level is in two panes. The left pane contains an empty template for the level and the right pane contains all the tools to modify the level.

The right pane contains 3 tabs, one tab is for opening up a preexisting .level file or a .level template and modify that and save it as a new level.

Another tab is a list of creatable in game objects represented as images. The images are drag able in to the left pane to create the object in the level. When the object is created on the blank template the third tab opens which allows users to modify parameters for the object.

The way we would implement this is to create a new object called “creatableObject”. Creatableobject has a method that returns an array of all the parameters that can be set by the user and also default values for those parameters.

Our level editor would find all classes in that implements creatableobject and lists those objects under the second tab in the left pane.

When the user clicks save, the level editor would store the location of parameter of all the objects in the .level format and send it via javascript to the MySQL database.

When a player clicks custom game, a list of custom level is pulled via javascript from the database.

User Guide

  1. Subclass Level for your game
    1. Implement hasLost() – returns true if you have lost the game, false otherwise
    2. Implement hasWon() – returns true if you have won the game, false otherwise
  2. Implement one class that extends ControllableGamePiece
    1. Implement setUpNextLevelPlayer(ControllableGamePiece nextLevelPlayer), which sets the state of the new player object for the next level
    2. If desired, override any of these methods that control the state of the ControllableGamePiece
      1. loseALife()
      2. gainALife()
      3. increaseLives(int numberOfLives)
      4. setScore(int score)
      5. setLives(int lives)
      6. increaseScoreBy(int num)
      7. resetScore()
      8. resetLives()
      9. passNextPlayerScoreAndLives(ControllableGamePiece nextLevelPlayer)
    3. Implement the necessary methods from GamePiece (see #3)
  3. Implement desired game pieces (subclasses of GamePiece)
    1. Implement respondToCollision(GamePiece objectCollidedWith, Point whereCollided) to dictate what happens when this game piece collides with any other game piece
    2. Implement respondToCollisionWithBoundaries(Point whereCollided) to dictate what happens when this game piece collides with any of the canvas boundaries
    3. Implement resetMe(), which is called when the level is reset, and should reset the game piece to its default state
    4. Set appropriate constants that would be helpful, such as default position, velocity or image filename.
    5. If desired, override any of the other methods of GamePiece. The most helpful methods to override are as follows.
      1. translate() – changes the position of the game piece
      2. draw() – draws the game piece. (Uses the image by default)
  4. Write a properties files with any key or mouse actions you would like to use, as the keys, and the associated methods as the values
    1. For keys, the key is just the lowercase letter the key represents
    2. For mouse actions, use MOUSEDOWN and MOUSEUP
  5. Optional: Create subclasses of SplashScreen for screens to be displayed when the player opens the game and when the player loses the game.
    1. Pass the appropriate information to the constructor of SplashScreen, including the desired image, and the desired buttons
  6. Write or generate .level file(s) using the following format:

(Brackets are not typed)
<Class name of level subclass>
AddPlayerGamePiece:
<Class name of ControllableGamePiece subclass> <# of parameters to constructor>(
<Class name of parameter> <# of parameters to constructor>(
<parameter value>
)

)
AddGamePiece:
<Class name of GamePiece subclass> <# of parameters to constructor>(
<Class name of parameter> <# of parameters to constructor>(
<parameter value>
)

)
AddGamePiece:

  1. Create a setup.properties file in the src directory that contains the following information: (omit the title screen and lose screen if you have not implemented them, and the defaults will be used)

 

DefaultControlsFile=<path to the controls properties file>
Levels=<path to first level>, <path to second level>, …
TitleScreen=<Class name of title screen>
LoseScreen=<Class name of lose screen>

GUI Implementation

The GameView class is entirely responsible for displaying information related to the game currently being played. This would be instantiated for each new game that is being played, and is used to display all the GamePieces. This class receives a list of GamePieces from the Controller, and for each time its paintComponent method is called, it instructs each GamePiece to draw itself appropriately.

The Frame, the main container for all GUI related displays, interacts heavily with the Controller to achieve its functionality. The Controller essentially initializes all necessary displays when they are needed, and informs the Frame that it is time to showcase their splendidness.

All SplashScreens are initialized by the Controller, and then displayed at appropriate times by the display. The buttons in the splash screen, who actions initiate a variety of content inside the game world, are handled by the Controller, which subsequently informs the appropriate player inside the game engine that his or her time has arrived.

The ControlAdjustmentView class will ultimately be responsible for adjusting the control mappings inside of the ControlScheme object, thus allowing the user to alter the control setup for a particular game, and then play in the manner most pleasing to them. Just like the others, this class will interact heavily with the Controller in order to achieve this functionality. The Controller will hold a copy of the ControlScheme, and user input will subsequently alter this ControlScheme.

Initial Design

Classes Overview

GUI Mockup

Use Case

This is what happens when the ball hits the last block in an Arkanoid level:

1) The main event loop, which is implemented in the Game class, will call the update method on the current Level.

2) Once the ball hits this block, the Level's update method will call CollisionDetection.checkCollisions to check all GamePiece objects for collisions with each other

3) When checkCollisions detects a collision, it calls the respondToCollision method on both GamePieces.

a) The Block object's respondToCollision method will first decrement the block's health. If the health becomes zero or less, the Block will be removed from the Level. To do this, it will be added to a list of GamePieces that need to be removed. On the Level's next execution of update(), the GamePieces in this list will be removed from the list of active GamePieces, and thus will not be drawn.

b) The BouncingBall's respondToCollision method will change the velocity of the ball based on the integer whereCollided, which is passed in from the checkCollisions method. If the ball struck an East or West side, the x component of its velocity will be negated. If the ball struck on a North or South side, the y component of its velocity will be negated.

4) If the Level no longer contains any blocks, the Level's hasWon method will return true if called. This method will be called from Game on its next iteration of update.

5) The Game object will increment its Level index, and will call update on the next Level from now on, so then the next level will be drawn.

Goals

Joe LevyProject ArtifactTeam LeadingBreakoutGamePiece
Dean ChenWebsiteBowmanKeyToAction
Alex GalonskyLunar LanderGameLevel
Trevor NarayanSome gameGUIGameMap

Vision

Dean would like to be responsbile for the Arcade Users, and the XML extensions. Trevor would like to be involved with the Networking and Physics Engine extensions, and potentially some dabbling in Graphics. Joe is interested in allowing the player to save and load and replay games, and Alex is interested in the Level Editor, and the Physics extension as well.

Class Analysis

We have a Game class, which houses the generic functionality of a game. It keeps track of levels, and is what will be initialized by the GUI. Next we have a Level class, which is abstract, and holds generic functionality for all types of levels. We also have a GamePiece class, which represents a general object within the game containing some behavior. The next class in our general design is a KeyToActionFactory, which generalizes the way in which key presses are mapped to actions, and allows for further extensibility of user input. We also have a Canvas class, which draws things. Lastly we have a CollisionDetector class, which is responsible for checking to see if any collisions have occured, and alerting the appropriate GamePieces.

Issues

Our team is having a lot of trouble figuring out how to implement the level editor. We will get to this eventually. We are also having some difficulty deciding exactly where the program should check for and respond to user input through the mouse.

Time Estimates

Game - 1 hours.

GamePiece - 8 hours.

Level - 6 hours.

CollisionMap - 4 hours.

KeyToActionFactory - 1 hours.

Canvas - 1 hours.

Breakout - 12 hours.

Moon Lander - 14 hours.

Bowman - 11 hours.

Trevor's Game - 12 hours.

Level Editor - 25 hours.

Extensions - 45 hours.

Testing - 5 minute call to India.

Project Artifact - 6 hours.

Total = 146 hours and 5 minutes.