Latest Entries »

Overriding Graphics in GLib

Similar to 3D game engines, GLib handles most rendering for you. However, they may be a good reason to override the default drawing behaviour, especially considering GLib’s infancy. For example, there is virtually no support for scrolling levels.

It is easiest to override sprite drawing, and we handle that first.

If you’ve seen the code for GLib, you would have noticed that the Sprite class has two methods for drawing: render and draw. Render draws the current image, if any, and then calls draw. The draw method is blank, and acts as the hook for overriding. Simply override the draw method for any custom code. Draw takes a single parameter, a Graphics object you can safely downcast to a Graphics2D object.

We now look at the steps in the drawing process and discuss the methods of overriding them.

  1. First, a background color (A java.awt.Color object) is drawn across the screen. This step can only be skipped, call the setBackgroundColor method of the Screen object with parameter null.
  2. A background image (A java.awt.image.BufferedImage object) is drawn. This also can only be skipped, by calling setBackground(null) on the Screen object.
  3. The world is then drawn, using the world object. This can also be only skipped, by calling super.world=null; in the subclass, or setWorld(null) on the object.
  4. The game object’s preDraw() method is called. This method accepts a Graphics object, and can be used for all custom drawing.
  5. All sprites are drawn. This step can only be skipped, but each sprite can have custom rendering as discussed earlier.

 

Every game has a concept, or basic outline. Metroid: Table is based on the popular Metroid series, but it is probably a good idea not to distribute it as an ‘official’ game. In fact, the final version will have no connection to the Metroid games. So let’s see the concept of Metroid: Table.

  • The player’s character is Samus Aran in her morph ball form. She can drop infinite regular bombs, which explode and deal slight damage over a small area. You may wish to implement Power Bombs, which have a huge area (the whole screen) and extreme damage. To keep things simple, Samus can only deploy three bombs at a time.
  • At certain places on the screen are guns that periodically (or randomly, for more difficulty) shoot missiles in a particular direction. This is a good place to consider further expansions: If the guns can move along fixed paths, or even move and turn freely(in which case they become full-on enemies). Samus must place one bomb near each gun to destroy it.
  • When each gun is destroyed, the next level is loaded.

The final version will allow you to specify arbitrary levels, but I will give just two: a starting level and a boss fight. The boss fight will be against the series’ recurring antagonist, Ridley. The concept for this boss battle is:

  • In most games, Ridley flies around and attacks (in one, he falls down a shaft along with you). We have his four feet on the floor, and move them to random places to ‘stomp’ on Samus.
  • In all games, Ridley shoots fire at Samus. We will have fire balls randomly spawning across the screen that damage Samus.
  • Ridley’s tail is invincible, and he uses it either by flying around and stabbing the floor or swishing it around like a whip. We will have his tail randomly move along straight lines across the floor.
  • In some games Ridley is also capable of using missiles. Squares on the screen will be grey, and turn progressively darker, until the missile explodes (as it would if the missile is falling and casting an increasingly ominous shadow).
  • In order to defeat Ridley, bomb all four feet to make him stumble and fall. His head will appear and can be bombed to damage him.

In subsequent tutorials, I will show you how to implement this game. I will also be changing the original library and updating the basic tutorials for quite a bit. I do not use any animated sprites: All are represented as single images. Feel free to use your own instead, or get one from a good sprite library online.

Possible Updates

So I was wondering what could I do with GLib, and I’ve thought of the following changes:

  • Internal Plumbing: A lot more basic tasks could be automated by GLib. Ensuring sprites don’t fly off the screen, handling simple collisions (so a sprite stops before hitting a tile, for example), and so on currently need to be hard-coded; and future updates (currently being worked on) would make these simple method calls.
  • Template Sprites: A lot of sprites work in similar ways: bullets, players, enemies, and so on. These basic sprites could be included in GLib in a new package.
  • Sprite Constraints: A sprite could be constrained to move along a particular path, it’s position based on time. For example, the equations x=a*cos(t), y=b*sin(t); create an ellipse, the path taken by a projectile as it is thrown. Modifying the values of a and b changes the range (=2a) and maximum height (=b) of the projectile.
  • Descriptor files: Similar to TileMaps being read from text files, a lot of resources could also be read in a similar manner. The same code for TileMaps would work with PictureSets, for example. Sprites can be simply declared without all the tedious coding.
  • Cross -platform support: GLib may be able to run on Android in the future. The one obstacle is my lack of knowledge on the Android platform. Another difficulty is that games will not be immediately portable to Android as a lot of Standard Java SE classes are not implemented on Android. A uniform data access API would be clunky and confusing to Java SE programmers, while having two separate loading classes and opt for one depending on the platform involves a lot of duplication of code, especially on the programmer’s (i.e, your) end.
  • Constrain worlds to sprites: To facilitate scrolling levels, a world object may be constrained to a sprite: in other words, the player’s view of the world will move to keep up with a sprite. This allows rooms to have the additional difficulty of not being entirely visible to the player.
  • Darkness: Multiple games utilize darkness to further induce difficulty. In Pokemon, a particular gym is usually dark. Defeating other trainers increases the amount of light.
  • Cutscenes: Cutscenes are an integral part of many games. A cutscene may be created by allowing the programmer to script events, or by loading a full video.
  • AI to Player text: When a character in-game wishes to talk (either to the character or to you), a staple in most 2D games was to have text on-screen, possibly also a small picture of the talking character. The text is usually in a small area, with a border. This would scroll up, text would appear character by character, and it would then scroll down. The idea is that the programmer supply the text from files, and specify the properties in-game.
  • Overlays: When the character dies, or wins a level, or (insert possible reason here), the game pauses, and a rectangle is displayed at the center with some text (You Won!, You lost!, etc). Overlays could possibly be the base of AI to Player text.
  • Language ports: underneath GLib are multiple technologies from the Java Platform (Swing, for example). Although GLib does not completely hide them from view, an equivalent to GLib could be written in another language using it’s standard toolkit. This does not seem likely right now as the only other languages I know (C# and VB.NET) already have a very good game engine.

The level generation API is the under-construction API in the glib.world package. This package has five top-level classes that handle each level as well as information about the sprite’s position in the level. The classes are:

  • Info: A set of booleans giving information about a sprite.
  • Tile: The building block of the world.
  • TileCollision: An enumeration that indicates the properties of the tile.
  • TileMap: A mapping of characters to tiles to help speedy generation of levels.
  • World: The representation of the level.

The building block of each level is a tile. Consider the following image from Metroid: Zero Mission.

Tiles in Zero Mission

An image from Metroid: Zero Mission. Note the tiles, with an orange border

The tiles ave an orange border. This images show ‘null tiles’: Tiles with no image that allow the background to be seen. The location of tiles is specified with a tile map. A Tile map for the above image could be:

….f…..f……
….p…..p……
….mm…mm……
….mm…mm……
……………..
….mmmmmmm……
rrrmmmmmmmmmmmmmm

The first thing you will notice is that the tile map does not seem to ‘fit’ the level. This is because of the variable width. The letters ‘m’ and ‘f’ have different widths, and the period is very narrow compared to them. Each character is mapped by the tile map to an individual tile: ‘f’ to a carved face, ‘m’ to a brick of metal, ‘r’ to a rock, ‘p’ to a pedestal, and ‘.’ to a null tile.

Tiles may be TileCollision.PASSABLE, meaning they are bits of scenery (null tiles, for example); or TileCollision.PLATFORM, indicating they are solid and stop the player in his tracks. The Tile class has the following constructors:

  • Tile(): A null tile, this tile has no image
  • Tile(File, TileCollision): A tile with specified collision and image.

Tile objects have public properties tileImage (of type BufferedImage) and collision (of type TileCollision). Tile maps are specified by the TileMap class. TileMap has the following constructors:

  • TileMap(): A TileMap with no Tiles
  • TileMap(File): Reads a TileMap from the specified file.Directly using Utils.loadTileMap is preferred.

TileMaps can be stored in a file with the following format:

character image URL
character:image URL
character: image URL

The accepted delimiters between characters and URLs are a space, a colon, and a colon followed by a space. The tile map for the above image could be:

f: images/brinstar/face.png
p: images/brinstar/pedestal.png
m images/brinstar/metal.png
r:images/brinstar/rock.png

Tiles generated are, by default, PASSABLE. TileMaps have the following methods:

  • setTile(char,BufferedImage): Creates a PASSABLE tile with the specified image and sets it to the character.
  • setTile(char,Tile): Sets the tile to the specified character.
  • getTile(char): Gets the tile associated with the given character.
  • setDefaultTile(Tile t)/getDefaultTile(): Set or get a tile to be used if the TileMap reads an unregistered character.
  • SetSpriteTile(SpriteTile)/getSpriteTile: Set or get a SpriteTile object (see next section).
  • placeTile(char,int,int): Gets the tile associated with the given character. This method is used by GLib to place sprites (see next section).

Internally, TileMap uses a HashMap<Character,Tile>.

TileMaps also allow sprites to be placed using the SpriteTile interface. SpriteTile contains only one method, placeTile(char,glib.utils.Vector). This method is used as follows:

  1.   The World object calls TileMap’s placeTile method with a character.
  2. If a SpriteTile object is installed, it is queried for the tile.
  3. If the SpriteTile object knows the character, it returns the tile at the given place, and places a Sprite (the Vector location corresponds to the upper-left corner of the tile). If it does not know the character, it returns null.
  4. If there is no SpriteTile, or the SpriteTile returned null; getTile is called to place a Tile. The default tile (if installed) is used if getTile is null.

GLib is designed to handle nulls very well. Instead of throwing NullPointerExceptions, GLib tries to use a sensible default behaviour as far as possible. Null is an accepted value for a Tile, and Tiles with value null are skipped over -as though they were transparent PASSABLE tiles.

Complete levels are specified by World objects. Each object maintains a set of Tiles in a Tile[][] array. World objects are responsible for maintaining the tiles and drawing them to the screen. Games are drawn in the following manner:

  1. First, a rectangle of the Screen object’s background color is drawn. By default, this is white. If the backgroundColor property is null, this is skipped.
  2. A background image, if it exists, is drawn
  3. If it exists, The tiles in the World object are drawn.
  4. The game object’s preDraw() method is called
  5. All sprites are rendered. Sprites are added to the game in lists, using the screen’s add(List<? extends Sprite>) method. Lists added first are drawn first, and sprites earlier in the list are drawn earlier.

It’s possible to add empty lists, so that bullets, bombs, and such can be dynamically added to and removed from drawing. GLib normally handles nulls very well -it may default to a reasonable behavior, or just skip the affected steps. Nulls are also handled equally well here, null lists and null sprites being simply passed over. Keep in mind though, that sending nulls is a bad coding practice -they’re a time bomb waiting to go off. Use empty lists instead.

World also allows a sprite to know about its surroundings. Sprites are informed of it before they are updated, using the tell() method. This uses the Info class, which we learn about now.

The Info class is really a struct, with four boolean variables. All four are public, and tell a sprite about it’s surroundings. They are:

  • shouldStopUp
  • shouldStopDown
  • shouldStopLeft
  • shouldStopRight

Each variable tells a sprite if it is about to collide with a tile in the specified direction. If your sprite has a PlayerEventHandler object associated with it, then it goes a step further and informs it to stop moving in that direction. Sprites may also be bouncing sprites, indicating that when they collide with a non-PASSABLE tile, they are to bounce off. A future update will allow sprites to destroy themselves on contact, simplifying beams, missiles, and other such weaponry.

The methods of World are:

  • left(): Shifts the world to the right. This is because, due to parallax, if a player moves to the right, the world  shifts to the left.
  • right(): Shifts the world to the left.
  • up(): Shifts the world downwards.
  • down(): Shifts the world upwards.
  • getInfo(Sprite s): Returns an Info object about the sprite’s location. This method is currently buggy, and is continuously being fixed and tweaked.
  • setScreenDimensions(int,int): set the width and height. Automatically called by Screen.

This concludes the level generation API. Keep in mind that this is still incomplete, and liable to change. This is also the last of the basic tutorials. These tutorials are written to set a base framework on which further tutorials will build up on. Further tutorials will show how to build a simple game, and extend the basic classes of GLib. These will lead up to the final set, which will showcase a basic platformer game.

I may have mentioned before that GLib uses a lot of standard Java classes to do it’s dirty work. Event handling, in particular, uses the classes in java.awt.event almost exclusively. There are a few classes to help ease the event handling process, and they have a very limited use. These are in the glib.event package, and handle key events.

The class Screen extends the class javax.swing.JPanel, and so accepts any event handlers JPanel can. When creating a game, event handlers are added to Screen before it is added to a JFrame. This tutorial will focus on the Utility class PlayerEventHandler, and the interface PlayerKeyListener.

The class PlayerEventHandler allows the player’s sprite to respond to key events, specifically to move left, right, up and down. PlayerEventHandler uses a Vector that represents the sprites’s speed if the left and up keys are pressed. PlayerEventHandler allows you to set the keys corresponding to each direction. Most games use Up,Down,Left, and Right, as well as W,A,S, and D. These configurations are referred to as UDLR and WASD, respectively. Keys are registered using the constants KeyEvent.VK_*, where * refers to a particular key. Thee constructors are:

  • PlayerEventHandler(): UDLR, no movement whatsoever.
  • PlayerEventHandler(double,double):UDLR, with specified x and y speeds if left and up are pressed.
  • PlayerEventHandler(Vector): UDLR, with specified base vector.
  • PlayerEventHandler(Vector, int, int, int, int): Base vector and direction keys specified.
  • PlayerEventHandler(Vector, PlayerEventHandler): Base vector and direction keys specified.

Consider Mario, where the character can only move left and right. If you want to specify this, use the following line:

PlayerEventHandler marioHandler=new PlayerEventHandler(new Vector(32,0),PlayerEventHandler.WASD);

the ’32′ refers to how much Mario (or Luigi) moves by one press, in pixels. 0 represents the inability to move up or down. The player controls Mario by the A and D keys for left and right. For a Pokemon clone, wherein a player can move in all four directions, use:

PlayerEventHandler pokemonHandler=new PlayerEventHandler(new Vector(32,32),PlayerEventHandler.UDLR);

PlayerEventHandler has the following methods you can use to customize it’s behaviour:

  • haltHorizontal(boolean): If true, ignore the left and right keys.
  • haltVertical(boolean):If true, ignore the up and down keys.

For further control, consider:

  • rejectDirection(boolean): If true, reject any motion along Direction. Direction may be Up,Down,Left,or Right.
  • isRejectingDirection(): Returns true if Direction is being rejected.
  • rejectAlong(Vector): Rejects up/down or left/right depending on the location of the Vector. If the vector points left and up (North-West), it rejects left and up.

None of the above methods affect the behavior along any other direction.

When done, call the makeKeyListener() method to get an object of java.awt.event.KeyEventListener. Add this object to the game’s Screen object.

Of course, you want to do more than just move a character when the buttons are pressed. The character’s image needs to be changed (unless you want it to walk backwards and sideways), for example. To do this, the interface glib.utils.PlayerKeyListener (and the adapter class PlayerKeyAdapter) have been written to automate responses to key presses. They rely on the PlayerKeyEvent class and a key map, which we discuss first.

A key map maps integers to String messages. These integer values are, as has been mentioned, the constants defined in the KeyEvent class. The strings may be arbitrary strings like “up”, “Up”, “DIR_0″, and so on. There is no KeyMap class, simply pass a Map<Integer, String> to a PlayerEventHandler object.

As with Swing events, the adapter class exposes the same methods, but with no implementation, allowing you to select the methods you wish to implement. To register a PlayerKeyListener object, call addPlayerKeyListener(PlayerKeyListener) on the PlayerEventHandler object. The PlayerKeyListener class exposes two methods:

  • public void keyPressed(PlayerKeyEvent): Informs that a key has been pressed. Unlike the Java SE KeyListener, this does not generate multiple presses if a key is held down.
  • public void keyReleased(PlayerKeyEvent): Informs that a key has been released.

The PlayerKeyEvent class exposes five public variables:

  • boolean up: Indicates that the sprite is moving up.
  • boolean down: Indicates that the sprite is moving down.
  • boolean left: Indicates that the sprite is moving left.
  • boolean right: Indicates that the sprite is moving right.
  • String key: The message set in the key map corresponding to the key pressed. If there is no corresponding string, a default is passed.

The value of key is given by the following rules:

  • If the key is the up, down, left, or right key the value “u”, “d”, “l”, or “r” is passed.
  • If the key is not a registered direction key, the value of the key code (as the constant KeyEvent.VK_*, where * is the key) is passed as a String.
  • If the key is registered in the key map, that value is given instead of the default.
  • If the key map is null, the default value is provided.

These variables are public, and don’t have get/set methods.

Of course, you can always create and register your own KeyListener objects with the Screen object.

Sprites are the meat-and-bones of any game. Anything that you draw on the screen, other than platforms and backgrounds is a sprite. Bullets, enemies, bosses, whatever. This tutorial also discusses collisions and collision handling.

The class glib.sprite.Sprite is to be extended to create a sprite. All properties, except for it’s name, are protected and accessible by subclasses.

  • Vector location: The x and y co-ordinates of the top-left corner of the sprite. The origin is the upper-left corner of the screen.
  • Vector speed: The amount by which the x and y values are updated each frame.
  • PictureMap images: The sprite’s images.
  • Bounds bounds: The sprite’s bounds, for collision checking.
  • PlayerEventHandler playerEventHandler: a utility object to help move the sprite.
  • String name: The sprite’s name.

Apart from bounds and playerEventHandler, the properties are simple enough to understand. The class Sprite also has a few methods worth studying:

  • update(): Moves the sprite.
  • draw(Graphics): Draws the sprite to the screen
  • willCollide(Sprite): checks if this sprite will collide with the other.
  • onCollision(Sprite): Allows the programmer to specify collision behaviour.
  • tell(Info inf): Informs the sprites of it’s surroundings.

Sprite also has accessors and mutators for location, speed, and bounds; as well as a mutator for playerEventHandler.  If update or draw are overridden, than remember to call super.update or super.draw if you plan on simply adding functionality. I’ll talk about Info in the level generation tutorial.

Sprite collisions are handled by the onCollision method. This method is overridden by subclasses that wish to be notified of collisions. Assume a simple game in which the player shoots bullets at enemies (then again, isn’t that any game?). The player, bullet, and enemy all implement onCollision. When the bullet collides with an enemy, onCollision() is called on both the bullet and the enemy. In the bullet’s onCollision(), it is replaced with an explosion and in the enemy’s onCollision() it’s health is reduced and it (possibly) dies.

Collision checking is handled by the bounds class. The simplest way to check collisions is by creating a rectangle around the sprite. Consider Fig. 1, a scene from Nintendo’s Super Metroid showing the protagonist Samus Aran trying to get an item (the screw attack) which is on a statue.

Bounding Boxes

A Scene from Super Metroid, note the bounding boxes

The Red boxes show each character’s bounding boxes. As far as GLib is concerned, The character’s sprite IS the red box. If a monster happens to be in the box, but not touching Samus Aran, it’s still a collision. Usually, this isn’t a problem as Sprites tend to be rectangular. But notice the statue. When the item is collected, it’s image disappears, as does the orange bounding box. When Samus now jumps onto the statue, her red box collides with the statue’s, and she stops. But if she was above the item’s erstwhile position, it would appear as if she was floating in mid-air above the statue’s palm. The solution, is to let the statue have TWO boxes (the blue ones in the picture). Multiple boxes allow you to approximate the shape better and give a more realistic game, but come at a cost: more checks result in a slower game. The solution, therefore, is to combine both: first check if the red boxes collide. If they do, then the sprites are in the vicinity of each other. If Samus’ red box collides with either of the statue’s blue boxes, then it is a definite collision.

Enough theory: let’s put this into practice.

The Bounds class keeps the sprite’s position, the box’s width and height; and an array of Bounds objects (this has been removed as it was found to be buggy, but the final version 1.0 will have it). Consider the statue again. The position is the upper-left corner of the red box, the width and height it’s dimensions. The array of Bounds objects holds the two blue boxes. Bounds has three constructors:

  • Bounds(): A default constructor
  • Bounds(Vector): A Bounds at the specified location
  • Bounds(Vector, int, int): A Bounds at the specified location with specified dimensions.

Bounds has accessor and mutator methods for the location, width, height, and the inner bounds. inner bounds is an array and is an Indexed property. Bounds are set to the bounds property of a sprite object.

The last bit of the Sprite class refers to the accessors and mutators for the following properties:

  • minimumX
  • maximumX
  • minimumY
  • maximumY

These fields are protected, and can be directly accessed from derived classes. These can be used to specify limits on the sprite’s motion. A sprite can never move left of minimumX, right of MaximumX, below maximumY, or above maximumY. These are set to the very reasonable limits of Integer.MIN_VALUE (minimumX/Y) and Integer.MAX_VALUE (maximumX/Y). These will not be used much if the level scrolls (in fact most sprites are required to fully exit the screen before they can be safely removed).

We’ve now finished almost all of the basic tutorials -only the level generation API and event handling are left. The level generation API is still being written, although it can be used right now. After the basic tutorials, we will start putting them together into a game. I will start with a simple one based on the Metroid series, followed by a platformer.

A lot of GLib relies on standard java classes. Images are, for example, handled by the class java.awt.image.BufferedImage. The package glib.image has support for Flipbook animations in the Animation class. The Animation class is usually enough for 2D animations, but it can be subclassed should the need arise.

All classes in glib.image handle a group of images. In each, a HashMap is used to store images. If you haven’t come across the class java.util.HashMap, think of it as an array that can use arbitrary objects like Strings as indexes. The PictureMap class allows you to specify the objects used as keys (indexes). The PictureSet class uses Strings exclusively. Animation actually uses Integer objects, but this is hidden from you.

I’ll show you how to use PictureMap and PictureSet first, before we go to the much simpler Animation class.

PictureMap and PictureSet work on the following principle: You load all required images (one facing left, one facing right, and one jumping for example). Then, depending on the given conditions (key presses, time, etc.) you set a particular image as the current image. When the sprite (or character) is drawn onto the screen, only the current image is drawn.

You initialize the objects as:

PictureMap<String> map=new PictureMap<>();

or

PictureSet set=new PictureSet();

Any class can be used with PictureMap, but String is the simplest. An overloaded constructor accepts the number of images as an int. Images are loaded by the utility class glib.utils.Utils. Images are then added to the classes by the add method:

map.add(“left”, leftImg);

or

set.add(“left”,leftImg);

The image drawn to the screen at any time is called the current image. The current image is manually set by calling  the set method:

map.set(“left”);

or

set.set(“left”);

PictureSet extends PictureMap and so can be used where a PictureMap is required.

We now come to animations, handled by the Animation class. The Animation class extends PictureMap<Integer>, and is used in the following way:

The Animation class can be instantiated with either of two constructors:

Animation a=new Animation();//has space for 4 images

or

Animation a=new Animation(10);//has space for 10 images

PictureMap, and it’s derivative classes use a HashMap internally and so dynamically increase memory if required. The overloaded constructor is preferred for fast initialization. Animation also has two constructors that accept boolean values to indicate whether the animation loops (true) or plays only once (false):

Animation loopingAnimation=new Animation(true);

Animation loopingAnimation=new Animation(10,true);

Images are subsequently added to the animation class. Each subsequent images forms a later frame in the animation: the first image forms the first frame, and so on.

a.add(firstImg);
a.add(secondImg);

The GLib engine handles updating and rendering animations. We are now ready to learn about creating player and enemy characters, or sprites, which will be covered in the next tutorial.

Game content refers to images, sounds, and animations. The last -animations are supported in the package glib.image. Sound is supported by the interface java.applet.AudioClip.

the package glib.utils contains three classes: Utils, which contains utility methods; Vector, which handles two-dimensional vectors, and PlayerEventHandler, which allows you to quickly control a sprite by the keyboard. PlayerEventHandler is covered in the event handling tutorial.

Vectors:

The class glib.utils.Vector represents a two-dimensional vector. If you haven’t studied vectors, think of it as a point with an x and y co-ordinate for now. It is not necessary to learn any of these methods, so feel free to skip this section. Vector has the following methods:

  • getX(), setX(double), getY(), setY(double): Accessor and mutator methods for the x and y co-ordinates.
  • getLength() and setLength(double): Accessor and mutator methods for the vector’s length. In point terms, it is the length of the line connecting the point to the origin. Changing this length moves the point along the line, closer to or further from the origin
  • xComponent() and yComponent(): return the X and Y components of the vector. In point terms, the X component of a point (h,k) is the point (h,0).
  • getAngle(): returns the angle of the vector with the positive x-axis, in radians. In point terms, it is the angle made by the line connecting the point to the origin and the x axis, or the angle POX (pun unintended), where P is the given point, O the origin, and X the x axis.
  • getUnitVector(): Returns the unit vector. It is equivalent to copying the vector and calling setLength(1) on the copy.
  • into(double): Multiply the length by the given value
  • dot(Vector): Evaluate and return the dot product. The dot product of two vectors (h1,k1) and (h2,k2) is h1*h2+k1*k2
  • equals(Vector): Returns true if both Vectors are the same.

There are also some useful static methods of the Vector class:

  • newPolarVector(double,double): Returns a new vector with specified length and angle
  • connect(Vector,Vector): Returns a new vector originating from the first and terminating at the second

The Vector class has the following constructors:

  • Vector(): Constructs a null vector, or a vector whose x and y components are both zero.
  • Vector(double,double): Constructs a new vector with specified x and y co-ordinates

The vector class also defines the constant ZERO as a vector with x and y components zero.

Utility methods:

The class Utils contains a lot of utility methods that I divide into the following groups:

1) General utilities

  • array(T…): Quickly generate an array of objects, for example: Vector[] vectors=array(ZERO, new Vector(1,1), new Vector(3,5));
  • list(T…): Quickly generate a List (a LinkedList, actually) of objects
  • print(Object… ): Prints the given objects on one line, separated by a comma
  • clamp(double, double, double): Ensures that the first value is between or equal to the other two parameters. If it exceeds either, it is set to the limit. For example, clamp(2,1,5) is 2 but clamp(2,3,5) is 3.

2) General File loading utilities:

GLib’s file loading utilities are for files bundled in the game’s .jar file as resources (a good example is here). Normally, these files are loaded by this line: this.getClass().getResource(“some name”). This returns an object of class java.net.URL that you can use to load the resource. GLib’s utilities do not throw any checked exceptions, rather a java.lang.RuntimeException is thrown with the checked exception as a cause.

  • getResourceAsFile(String, Class<?>): Load a resource as a File object for input and output. The second parameter can be obtained by calling getClass() on this.
  • toFile(URL): If a URL is already obtained, it can be converted to a File object by this method

3) Text File loading utilities:

These methods read a file and return them as an array of Strings. Each string corresponds to a single line in the file.

  • loadTextResource(String, Class<?>): Reads a text file and returns it’s contents.
  • loadTextResource(File): Reads a text file and returns it’s contents.

4) Image loading utilities:

GLib uses the class java.awt.image.BufferedImage to represent images. Gif, JPEG, PNG, BMP, and WBMP formats are supported. Internally, the class ImageIO is used so support for other file formats can be plugged in.

  • loadImage(String, Class<?>): Loads an image
  • loadImage(File): Loads an image

5) Tile Map loading utilites:

Tile Maps are a part of the Level Generation API in glib.world. They will be covered in the level generation tutorials.

  • loadTileMap(String, Class<?>): Loads a TileMap.
  • loadTileMap(File f): Loads a TileMap.

The AudioClip class:

The AudioClip interface, written for use with applets, is an easy way to load and play sounds.

AudioClip objects are loaded from a URL by the following call:

AudioClip clip=Applet.newAudioClip(soundURL);

The methods play(), loop(), and stop() can be called on the object.

All games are run by logic, and with a good engine game logic is all you need to write. GLib handles a lot of logic-related activities for you: rendering, collision checking, updating sprites, even (to a limited extent) handling events. So let’s see how to use GLib to write games.

the package glib.logic has two classes: Game and Screen. Game handles the logic of the game, such as pausing, resuming, updating, and finishing the game. Screen draws the game onto the screen (or renders it), as well as running it’s life cycle. These classes do a lot of work, but the best part is that you don’t need to worry about them at all.

For example, consider the method pause() in Game. You never call pause() directly, rather you call pause() on the Screen object. the game is now paused, and Game’s pause() does nothing. All methods in Game are merely to inform the programmer of changes in the game’s state, and none of them really need to be implemented.

That said, consider the methods in the Game class:

  • start() notifies the programmer that the game has started
  • pause() notifies the programmer that the game has been paused
  • resume() notifies the programmer that the game has been resumed
  • finish() notifies the programmer that the game is over
  • step() notifies the programmer that the game has just updated
  • preDraw() notifies the programmer that the game is about to be rendered

The game, by default, runs at 60 Frames per second, or 60 FPS.

The class Screen has two constructors:

  • Screen(Game): Creates a screen with the specified game and default frame rate (60 FPS)
  • Screen(long,Game): Creates a screen with specified game and frame rate

The first parameter in Screen(long,Game) is the period: the amount of time in which one frame is prepared and rendered. The required period is found as the inverse of the required frame rate, measured in nanoseconds. The Screen class has two constants, SIXTY_FPS_PERIOD , which is 16,666,666 nanoseconds and THIRTY_FPS_PERIOD, which is 33,333,333 nanoseconds. Normally, the first constructor is sufficient.

To use a Screen object, we need to know the following methods:

  • start(): Start the game. start() can be called multiple times, but it silently ignores subsequent calls.
  • pause(): Pause the game. Note that this method has to be called to explicitly to pause the game.
  • resume(): Resumes a paused game
  • setBackground(BufferedImage): Set an image to be used as the background. Support for scrolling backgrounds will be added soon, but can be implemented by the programmer
  • setBackgroundColor(Color): Set the color of a rectangle to be drawn as the background if no image is specified. By default, this is white.
  • setWorld(World): Set a World object to generate levels.

For now, ignore the last two. Backgrounds will be covered in the Utilities tutorial, and World object in the level generation tutorial.

So, to create a game we just need to create instances of Game and Screen. Screen, by the way, extends javax.swing.JPanel. Therefore, we need only add it to a JFrame object to start the game.

To start, download the GLib engine from the About page. The jar file contains the source code as well.

The important classes to write a game are glib.logic.Game, which handles game logic; glib.logic.Screen, which handles drawing and running the game, and glib.world.World and glib.world.Tile, which handle the quick generation of levels.

The last two form the still under development level development API. It currently allows quick generation of levels, but no more. I am currently working on it, and will post updates here.

Let’s see the class structure.

Glib contains the following packages:

  • glib.image: Classes concerning handling and drawing of images and animation
  • glib.logic: Classes concerning the game’s logic and life cycle
  • glib.sprite: Classes concerning sprites and player characters
  • glib.utils: Utility classes and methods
  • glib.world: Classes concerning level generation and maintainance

Each package will have an in-depth tutorial in upcoming posts

Follow

Get every new post delivered to your Inbox.