
This page contains the guide to configuring & using the editor to create game levels, and for creating C++ code to make use of your creations. It assumes that you are familiar with C++, and Haafs Game Engine (http://hge.relishgames.com/).
The Editor.
/editor/ folder of the Opal distribution.
editor.cfg file. The file is divided into three main sections. All of the options are commented in the config file that comes with the editor but I will give a brief description of them here.The first section is "editor options". These options can customize some of the colors in the editor, like the background and grid color. They also specify the directories for various filetypes, like maps, textures, border textures, etc. Your game will likely reference the same data as the level editor so this is where you can customise the directory structure of your project. The default values are:
editor options
{
//////////////////////////////////////////////////////////////////////////
// Editor Options
// Editor Window Title
window_title = "Opal Level Editor";
// Background Color
background = 0xFF444444;
//////////////////////////////////////////////////////////////////////////
// File+Directory Options
// Base Maps directory
map_base = "maps";
// Base Prefabs Directory
prefab_base = "maps/prefabs";
// Base Texture directory - subfolders of this directory are listed
tex_base = "data/textures";
// Border textures directory
tex_border = "data/border";
// Shadow textures directory
tex_border_shadow = "data/border/shadow";
//////////////////////////////////////////////////////////////////////////
// Interface Options
// Invert middle mouse pan control
invert_pan = 0;
// Grid Options
grid_color = 0x11FFFFFF;
grid_scale = 64.0;
}
The second section is point entitys. This is where you can define the various elements that will make up your game that will need placing into levels by a level designer. Point entitys are a polygon with only a single vertex and have a sprite file associated with them rather than a textured shape. Below is an example point entity:
point Turret
{
sprite = "data/ents/turret.png";
shoot_rate = 1.0;
bullet_speed = 200;
}
The point definition lets the editor know this is a point entity type, and the next word - Turret is the classname of the entity. You must pick a unique classname for your entitys. The next section is the definition of the properties that a Turret entity will posess. You can specify as many properties as you like here with or without default values specified. You can set or alter values on individual entitys when they are placed in the editor, and the specified values can later be read by your C++ code. The editor takes special note of the "sprite" property for point entitys. It will render the specified sprite when you create this entity within the editor.
The Turret entity from within the editor.
texture="path/to/texture.png"The texture used must exist within the configured base textures folder.
Creating a polygon.
You can now release the spacebar and keep clicking to add more points to the shape. When you are done adding points, right click with the mouse to close the shape.
Right click to close the shape.
The three texture bar modes
The toggle layer visibility buttons are located on the bottom right ahnd side of the GUI. Pressing a button will cause it to turn yellow & to hide any polygons in that layer from the view. Press the button again to toggle the layer to visible again.
The right click menu and layer visibility buttons
C:\Programming\Libs\Opal
Next, you must add the include directory folder to visual studio's list.
Adding the include directory to visual studio
/example directory that is set up correctly and can serve as a base to build your own project off.
Set the /MTd flag if in debug configuration, or /MT for release.
// Initialize Map
map = new hgePolygonMap ( hgeVector(SCREEN_WIDTH, SCREEN_HEIGHT) );
// Register Entitys
map->RegisterEntityClass("Player", new entPlayer() );
map->RegisterEntityClass("Turret", new entTurret() );
// Load File
map->LoadFile("maps/level1.map");
The first line of code initializes the hgePolygonMap object and supplies it with a hgeVector that defines the viewport size for the map. Basicly, the area it will draw to when you call hgePolygonMap::Render(). In most cases this value will match the size of the window.
The last line will load the map from disk and prepare it for rendering.
Ordered diagram of possible rendering layers.
enum hgeEntityLayers
{
HEL_BACK = 0,
HEL_MIDDLE1,
HEL_MIDDLE2,
HEL_FRONT
};
To do this, the class that we create must inherit from hgeEntity - which in turn inherits from hgePolygon. Below is an illustration of how the skeleton of our class will look:
class entBall : public hgeEntity
{
public:
entBall() : hgeEntity(HEL_FRONT, false) {}
entBall *Spawn() { return new entBall(); }
void Initialize()
{
}
void Update(float dt)
{
}
void Render()
{
}
};
There are a few important things to note here. First are the parameters passed to the hgeEntity::hgeEntity() constructor. The first parameter is the entitys render layer, the second parameter determines if the entity will behave as a static entity.
Next is the Spawn() function. Your class must have this function, and it must return a pointer to a copy of your class. It is used by hgePolygonMap to create instances of your class when it needs to.
Finally, the Initialize(), Update() and Render() routines are optional and I have drawn them in here just to illustrate where they go. Initialize() is called after all polygons & entitys have been loaded from the map. Update is called every frame, just before Render() is called.
Next we will look at the complete code to make our bouncing ball entity react to collisions with the map. In our example code we won't need to make use of Initialize() or Render() since the default implementations will suffice.
// Bouncing Ball Entity class entBall : public hgeEntity { protected:
hgeVector velocity;
public:
entBall() : hgeEntity(HEL_FRONT, false) {}
entBall *Spawn() { return new entBall(); }
void Update(float dt)
{
// Check we're visible
if (!map->TestVisibility(this, 100.0f)) return;
// Apply Gravity
velocity += hgeVector(0, 500 * dt);
// Test Collide
hgeCircle c = hgeCircle( GetPosition(), 20 );
hgeIntersect test = map->Test( &c, velocity * dt, NULL, COLLIDE_STATIC + COLLIDE_MULTI);
// Adjust velocity
if (test.collides)
test.SetResponseVector( &velocity );
// Move
SetPosition( GetPosition() + velocity * dt );
}
};
First we call hgePolygonMap::TestVisibility to check if our class is in the cameras view. We pass it a pointer to our class, and a padding value of 100 pixels around the screen edge.
Next we apply a gravity force to the velocity value member, which represents the objects speed in pixels per second. Knowing the objects current position, and its velocity we can perform a collision test to see if the object is going to collide in the next frame. We do this by creating a hgeCircle shape to represent the ball, and passing it to the hgePolygonMap::Test() routine. We pass Test() the COLLIDE_STATIC flag to collide with the map, and the COLLIDE_MULTI flag to detect multiple collisions in the test. The multiple collision test is slower to perform than a regular test, but it will produce more accurate collision results. It is useful for creating objects with realistic physics such as the bouncing ball.
The collision test will return a hgeIntersect struct with all the points of collision and the normals of the edges that were collided against. It also contains a bool, collides which determines if there was any collision at all. The test result has some useful routines for making use of the collision data. hgeIntersect::SetResponseVector() takes a pointer to a velocity value and will set the given value to the appropriate velocity change response for the collision that just occured. It works with or without the COLLIDE_MULTI flag, but produces better results when multi is set.
Finally after finding the velocity value for the next frame, we adjust the position of our entity using the Set and Get Position functions that are inherited from hgePolygon.
If you have any comments or suggestions regarding this project please feel free to contact me by email or MSN at mercior@hotmail.com.
1.5.8