FantasyEngine - Internals Documentation

FantasyEngine is an engine that can be used for creating 2D turn-based games. Written in Java for portability concerns, the program is split up into four main packages: com.FantasyEngine, com.FantasyEngine.GameState, com.FantasyEngine.Graphics, and com.FantasyEngine.Interaction. These are all part of the engine and given to the developer, who will then create a package for the implementation of the game (see Game Implementations which describe working pieces of code provided with the engine).

High-Level Description:

Integral in our design is the com.FantasyEngine which acts as a hub to provide the overarching framework to tie the other packages together. This tying together is done by the main FantasyEngine class as it stores such things as an instance of the game that is being run, an instance of the main Graphics class that allows contact with other parts of the Graphics module, and an instance of the KeyConfiguration, where keyboard input is mapped to certain actions that the HumanPlayer in the game should do.

The GameState module, in the com.FantasyEngine.GameState package, is integral in holding the data associated with all the game objects, as well as providing interfaces to which the game objects must conform and implementations of these objects. This module can best be described as "Data Structures". The classes in this package have to be sure to keep track of what changes in the game, so that the Graphics module can redraw the appropriate parts of the game. The implementation of units, maps, map squares, and other elements of the game are provided in this package. The relationship between this package and the Graphics package, provided through an connection with com.FantasyEngine, is that this package contains the information for all the objects, while the Graphics package displays that information, and the changes that occur in this information.

The Graphics module, in the com.FantasyEngine.Graphics package, encapsulates the graphical user interface for the game. Classes are provided that implement the main map, the mini map, the statistics panel, and the main game frame that holds and displays all these features. Animation of various objects in the game are also set up and implemented in this package.

The com.FantasyEngine.Interaction package provides a means of mapping keyboard input to actions that a human player in a game should do. These actions are important for the GameState package as it must keep track of where the players are, and where they move, and therefore allowing Graphics package to know what to update. For the implementation of the players, it is important that they know where they have moved so they know if their health has decreased for example. 

Finally, with the engine complete, the developer provides a game which plugs into this engine, and takes advantage of its features. An instance of the game is provided to FantasyEngine, which is allows the access to the game to be disseminated to the various packages of the engine. Through a configuration file, the game sets up the map used for the game, and the game further provides initialization of the players, whose implementation is provided in this package, of the units, and of various other game objects.

JavaDoc:

While creating the engine, comments were made in such a way as to provide an interface with the tool JavaDoc, which will automatically create webpages that show all the classes used, organized alphabetically, or by package. Function descriptions are given, as well as the arguments taken by each. This is an easy way for the developer to see how everything is linked together, and what functions can be called in the engine, and how those must be called.

To make the appropriate comments in the code that you develop while creating games using the engine, see Sun's How-To. The JavaDoc webpages should be in the API directory. If they are not, use the "make publicdocs" or "make privatedocs" command in Unix/Linux, using the makefile provided, or use the makeDocumentation.bat file in Windows.

FantasyEngine

com.FantasyEngine

The FantasyEngine package contains the FantasyEngine class, which is the overarching class that brings the classes in the other three packages together.

When designing a new game, an instance of the FantasyEngine class must be created, with the implementation of the game, conforming to the interface I_Game, passed to the FantasyEngine constructor. In the constructor, FantasyEngine begins the initializing graphical user interface and the user interaction.

The graphical user interface is instantiated by creating a new GraphicsHouse object, from the com.FantasyEngine.Graphics package. This object provides the instantiation of the main JFrame for the game, as well as creating the timer that controls the animation and delivering general housekeeping for the Graphics package (for more info, see com.FantasyEngine.Graphics).

The user interaction is initialized with the creation of a KeyConfiguration object, which provides a way of mapping specified keyboard input to the game-related commands, such as moving a unit to the right (for more info, see com.FantasyEngine.Interaction).

Finally, FantasyEngine also provides an event handler for the game menu, executing the appropriate function according to the selected menu item.

com.FantasyEngine.GameState

The GameState package contains the data structures and interfaces that are integral in providing functionality for the map, map squares, tiles, terrains, units, game objects, and players, as well as defining the interface for new games created by the developer. Furthermore, there is a Properties class that provides a HashMap implementation of storing various integer, string, or object properties for the all the objects (instances of classes such as units, players) that are placed on the map.

Currently, the supported map for games created using FantasyEngine is a grid made up of equal-sized squares. The map interface, I_Map, specifies the bare minimum functionality that the map must provide, including getting specific squares, adding units, and retrieving the size of the map. The class MainMap implements the interface and adds more functionality. The map can be created by either specifying the size, or by creating a configuration file and passing that filename to the constructor. Using a configuration file is the optimal way, as changes can be easily be made to the map by editing the text of the config file, and the parser for the file is found inside the constructor, with helper functions for setting properties, backgrounds, and other aspects of the squares of the map.

The map square interface, I_MapSquare, specifies that each map square must be able to return properties of itself, such as its tile or terrain type, and information about objects on it, including a collection of units, of players controlling those units, of game objects, of items, and of resources. MapSquare provides the implementation for these functions, as well as adding new functionality. Functions are provided to allow the addition or deletion of units, game objects, items, and resources. A service is provided for the graphics module by specifying whether the map square has anything on it is animated and therefore needs to be refreshed frequently.

The Tile class is a small class that handles creating a new graphics object that will be used as the background for the map square that instantiated it, and it returns the graphics object when the graphics module refreshes the map.

The TerrainType class is a small class that deals with setting properties of the terrain of the map square. These properties can be used for such things as movement cost, though for some games created this may not be a needed feature.

Each unit that is created must meet the requirements specified in the interface I_Unit. A unit must know its location, be able to move to a new location, know which player controls it, and know the items that it has picked up. The Unit class implements this interface class and provides extra functionality by providing an interface functions with the statistics panel. While the Units class does implement the majority of features that could be desired for units placed on the map, this class can be extended by the developer to add new methods or change the functionality of current ones. One important thing to be sure of is that the id property of the units is kept unique if the Unit class is extended. Either the superclass constructor can be called, via a call to super(), or code must be added to the new constructor to assign the new unit a unique id.

A design decision was made that those elements which were not active in the game, but that could be used for decorative purposes or for enhancing the resources of a particular square, would conform to the I_GameObject interface, and be implemented by GameObject. This simple class primarily deals with setting the graphics for the object and setting its properties, though this class can be extended to add new methods desirable for a particular game.

The I_Player interface provides an extensible outline of functionality that a player should provide. All players should conform to the basic interface of keeping track of the units that it controls, of providing functions to move to the currently selected unit controlled by the player to the right or to the left or up or down, and setting a delay for movement so that those players controlled by the computer do not instantaneously move when a player controlled by the user moves. The Player class provides this basic functionality, allowing a new player to be created and to be usable in a new game. However, many of the functions provided, such as the movement functions, should be overridden by creating a new class that extends Player. For example, this new class could deal with movement costs, artificial intelligence logic if the player were to be controlled by the computer, or other player-related functions that are specific to the game being created. These classes extending Player would be placed into the game implementation package that the developer creates (for an example, look at the description of the AIPlayer and HumanPlayer classes in the com.DoomVariant package of Game Implementations).

A class directly related to the Player class is the Players class. This class provides a way of keeping track of all the various players, that are conformed to the I_Player interface, used in a game. New players can be added to the Players object and retrieved via an integer index. Hence, this object acts essentially as a wrapper of a vector of I_Players. Also provided is a class called DefaultHumanPlayer. This class extends Player, adding default implementations and default movement abilities, giving the developer a base from which to extend and create other players that require user interaction (i.e. via the keyboard).

Finally, the I_Game interface is provided for aiding in easy game development. This interface specifies a base level of functions that must be implemented for a game created. The abstract class AbstractGame provides an base implementation of these functions. First of all, since the games created using FantasyEngine are turn-based games, it is important to know which player's turn it is. This is accomplished using the index whosTurn, which directly references an element in the Players object. The endTurn() function is used to increment whosTurn, transferring control to the next player. The engine, defined in com.FantasyEngine, has an instance of the created game, and is able to reference whosTurn and get the appropriate player to execute the appropriate player functions based on key strokes, for example, defined in com.FantasyEngine.Interaction. The ability to keep track of various maps, which are defined as Levels, is provided. This AbstractGame class further provides an initialization of the graphics, creating a new GraphicsHouse (see com.FantasyEngine.Graphics) and then creating the JFrame that encapsulates the game.

com.FantasyEngine.Graphics

The graphics package is primarily concerned with creating the graphical user interface, repainting it, and dealing with animation in the game that is created.

The heart of the graphics module is the GraphicsHouse class. This class does a large share of the housekeeping associated with the graphical user interface. This class creates the animation timer that is used to display animated images at the desired frame rate. A new GameFrame is also created, which contains the various visual components of the game. Functionality for starting and stopping the animation timer is found in this class, as well as the ability to redraw the animated portions of the game. Furthermore, a very useful feature of the GraphicsHouse is its ability to center on a specific position, so this can be used by the game implementation to center on the selected unit of a player when the turns switch from one player to another.

Two basic classes are the abstract classes A_GamePanel and A_DisplayPanel_2D. A_GamePanel extends the JPanel by specifying whether the JPanel needs to be completely repainted when something in the panel changes, and whether the panel is animated. All JPanels used in the Graphics module inherit from this abstract class. A_DisplayPanel_2D further extends A_GamePanel by providing functions that manipulate grids that are displayed in 2 dimensions, such as providing zoom capabilities.

MainMap_2D and MiniMap_2D are two related classes that both extend A_DisplayPanel_2D. MainMap_2D includes code for dealing with receiving the focus for keyboard input. In order to optimize buffering, only the area inside the bounds of map that is displayed is updated in the offscreen buffer. The class further contains functions for repainting various objects: those that are drawable and those that are temporary. The MiniMap_2D class is a class that displays the entire main map in a miniature fashion. As such, this class deals only with repainting the graphics since it does not receive the keyboard focus, as the main map does.

For dealing with animation, AnimationTimer builds on the capabilities of Java's Swing Timer class, creating a new timer with the delay corresponding to the desired frame rate in frames per second.

An important interface in the Graphics module is I_FGraphics, which provides a means of interaction between the Graphics module and objects with graphical images associated with them. This interaction includes the ability of returning the size of the object's associated image and working with animated graphics. FGraphics implements this interface. First, an initialize function must be called, reading in a mapping of various images to tag names to be used throughout the program. After this init function is called, FGraphics deals with implementation issues of getting buffered images, incrementing the current frame in the animation, and the like.

One type of graphics that may need to be represented by a game are temporary graphics that are not specifically part of the map, but which need to be displayed for a brief time and will then disappear, such as moving an image from one location to another. The TemporaryGraphic class gives this functionality. Various parameters that must be specified are the current location of the image, the destination location, and how many frames it should appear for. The class TemporaryGraphicsBag uses a Hashtable to keep track of the TemporaryGraphic objects. One important thing to note is that the TemporaryGraphic adds itself to the TemporaryGraphicsBag, rather than being added from somewhere else.

For the main JFrame that holds all the panels of the game, a program called Visaj was employed. GameFrame extends the class that Visaj created, adding the various game components (the main map, the mini map, and the statistics panel) to the JFrame. This class further controls such things as setting up the menu bar and displaying the background. Furthremore, this class extends the WindowListener class, so animation is stopped when the game window loses focus or is iconified and is restarted when the game window regains focus. An inner class is further declared, called BackgroundPainter, which creates a JPanel that shows a background behind all the other JPanel components of the game.

The GameMenuBar class is similar to GameFrame class in that it too extends a class that Visaj was used to create. This employs the use of the interface I_MenuListener, which allows menu events to be processed. GameMenuBar makes use of the command in I_MenuListener that is stored as a string and performs the appropriate action.

With the speed of the game as a concern, we realized that we needed to cache the graphics data. The GraphicsCache class employs a HashMap to store the images. The real work of the class is found in fetchData where the image is retrieved, created, and displayed .

The class Grid2D represents the 2D map, creating the horizontal and vertical lines of the grid. This representation of the game map also has the ability to draw itself.

The Stastics class provides an implementation for storing and retrieving statistics, such as a unit's health, to be displayed in a JPanel, whose implementation must be specified in a class that extends Statistic. Bar_Statistic is one such class that does this, providing labels and a progress bar to show statistics with integer values. String_Statistic is a related class, yet the statistics are only Strings, so it uses only labels to display the statistics.

The I_StatsPanel interface specifies that whatever extends it must take an object with statistics and update the display of those statistics. StatsPanel is a class that does this. Inheriting from A_GamePanel, this JPanel displays all the statistics of the particular object.

com.FantasyEngine.Interaction

This package deals with the interaction between the user and game via keyboard input.

The base class is KeyConfiguration which provides a method for adding, removing, and retrieving mappings between KeyStrokes and a specified action from the InputMap and ActionMap defined in the FantasyEngine object.

The abstract class Command is used to specify the actions that become associated with the KeyStrokes. Rather than being extended by another class specifically, this class is usually created and defined on the fly. DefaultKeyConfiguration is a class which extends KeyConfiguration, providing the default key mappings to instances of Command that do such things as moving to the right or to the left, up or down. For each new KeyStroke, a new Command class is created with the appropriate declaration of the doCommand() function.

To allow the user to change the key mappings to suit his/her own tastes, a dialog box is displayed. This is accomplished in KeySetupWin, which extends a class created using Visaj.

Game Implementations:

With the above engine's features, a developer can easily develop games. Below are some examples of games implemented using FantasyEngine.

com.DoomVariant

The DoomVariant game package contains three classes: the main DoomVariant game implementation and two Player implementations.

The DoomVariant class contains a main function that creates a new instance of the class itself, which in turn creates and runs the DoomVariant game, making a call to initialize the FantasyEngine engine as well. The features that DoomVariant, which extends our AbstractGame implementation, had to implement were initializing the game's graphics and map. This is done using text configuration files that are passed to the appropriate graphics and game state modules to be parsed. The players are also created and then the units are added to the map. 

HumanPlayer extends the DefaultHumanPlayer class, providing an implementation for a player that revolves around user interaction. Located in this extension is game logic dealing with the movement cost that the main HumanPlayer unit faces. It is HumanPlayer's turn until valid keyboard is encountered that moves the main unit. HumanPlayer can also pick up various game objects, such as a scroll or elixir, and these will be added to his inventory that are displayed in the StatsPanel.

AIPlayer also extends the Player class, providing an implementation for a player that is controlled by the computer. This contains additional game logic that attempts to move these "enemy" units towards the HumanPlayer's main unit. Furthermore, code has been added to create a delay between the moves of each unit that the AIPlayer controls.

com.PacManDemo

The PacManDemo game package also contains three classes: the main PacManDemo class and two Player implementations.

This game simulates the arcade classic PacMan, and illustrates the use of multiple levels in a game. PacManDemo extends AbstractGame, implementing the various functions required by this abstract class. It also further extends the functionality with dots, that are added to the map, which the PacMan character can pick up, and it initialized various levels as well (in this case, three levels).

The PacMan class extends DefaultHumanPlayer, and contains the logic not only to move around the map, but also to pick up the dots on the map.

The BadGuys class extends the Player class, and provides two implementations of movement, and chooses between them based on the level. One type of movement is just random movement, while the other movement is a smarter movement where the BadGuy tries to chase down PacMan.

While requiring a small amount of code to write, this game could be easily expanded in terms of the artificial intelligence to simulate exactly the PacMan game of long ago. 

com.ChessDemo

The ChessDemo game package provides a simple implementation to show the possibilities that can be accomplished using FantasyEngine. It provides two classes: the main ChessDemo class, and the ChessHumanPlayer class.

The ChessDemo class extends AbstractGame, but this example includes adding more than one unit to each Player implementation.

The ChessHumanPlayer extends DefaultHumanPlayer and expands its functionality by not only allowing movement, but also by allowing selection of different units controlled by the player. This implementation is used to show how our engine could be used to implement chess - all that is needed is the logic to only legal moves.