[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ] app.cpp

Here is our main application source file.

#include <crystalspace.h>

#include <celtool/initapp.h>
#include <propclass/zone.h>
#include <propclass/camera.h>
#include <propclass/mesh.h>
#include <propclass/linmove.h>
#include <propclass/actormove.h>
#include <propclass/input.h>
#include <physicallayer/propclas.h>

#include "app.h"
#include "behave.h"

MainApp::MainApp ()
  SetApplicationName ("CEL Tutorial");

MainApp::~MainApp ()

In LoadLevel() we create the level entity. iCelPlLayer has a CreateEntity() convenience function which creates the entity, assigns a behaviour to it and also creates the property classes for it. In this particular case we create an entity called `level' and assign it with the behaviour that is called `level_behave' (this one will be defined later in this tutorial). We also create a `pczonemanager' property class (see section Zone Manager) for it. This property class manages loading and unloading of levels (map files) in the game.

CEL_QUERY_PROPCLASS_ENT is an important macro that you can use to fetch a reference to one of the property classes in an entity. Every property class implements some interface in addition to the standard iCelPropertyClass interface. In case of the zone manager this interface is iPcZoneManager.

The ReportError() method is part of csApplicationFramework and makes it easier to use the reporter (Crystal Space plugin) to report errors to the user.

After creating the entity we need to setup the property classes. In this case this means the zone manager. It is possible to setup the zone manager from an XML descriptor file but in this case we set it up manually to load two files: one `walktut_world' file which is created in `Blender' and contains the 3D geometry and one `walktut_entities' file which we created manually. The entities file contains definitions for entities in our game.

bool MainApp::LoadLevel ()
  level_entity = pl->CreateEntity ("level", bl, "level_behave",
  if (!level_entity)
    return ReportError ("Error creating level entity!");

  // Now get the iPcZoneManager interface so we can setup the level.
  csRef<iPcZoneManager> zonemgr = CEL_QUERY_PROPCLASS_ENT (level_entity,
  iCelZone* zone = zonemgr->CreateZone ("main");
  iCelRegion* region = zonemgr->CreateRegion ("main");
  zone->LinkRegion (region);

  iCelMapFile* mapfile = region->CreateMapFile ();
  mapfile->SetPath ("/cellib/lev");
  mapfile->SetFile ("walktut_world");

  iCelMapFile* entitiesfile = region->CreateMapFile ();
  entitiesfile->SetPath ("/cellib/lev");
  entitiesfile->SetFile ("walktut_entities");

  return true;

Here we create the player. This time we use the `player_behave' behaviour that we define later. The player entity also uses a lot more property classes. We need a camera, a mesh (3D geometry), the movement system, keyboard input, and an inventory.

After creating the entity we again have to setup the various property classes. The camera needs to know about the zone manager so we also fetch that from the level entity that we created earlier. In SetZoneManager() we also indicate where the camera should start. In this example we pick region `main' and the name of the camera is `Camera'.

For our player mesh we pick the `cally' model that is part of Crystal Entity Layer.

Since we are possibly using third-person camera mode the zone manager needs to know about the player mesh. This is needed so that the zone manager can load the needed regions depending on where the player moves.

Then we setup `pclinearmovement' and `pcactormove' with the proper movement parameters. This includes the dimensions of the collision box and various speed parameters with which we will move the player.

Finally we need to bind various keys to actual commands. The behaviour will get these commands (and not the keys) so this cleanly separates the actual keys and the operations performed by those keys. In real games you probably want to read the key definitions from a configuration file.

bool MainApp::CreatePlayer ()
  player_entity = pl->CreateEntity ("player", bl, "player_behave",
  if (!player_entity)
    return ReportError ("Error creating player entity!");

  // Get the iPcCamera interface so that we can set the camera.
  csRef<iPcCamera> pccamera = CEL_QUERY_PROPCLASS_ENT (
  	player_entity, iPcCamera);
  // Get the zone manager from the level entity which should have
  // been created by now.
  csRef<iPcZoneManager> pczonemgr = CEL_QUERY_PROPCLASS_ENT (
  	level_entity, iPcZoneManager);
  pccamera->SetZoneManager (pczonemgr, true, "main", "Camera");

  // Get the iPcMesh interface so we can load the right mesh
  // for our player.
  csRef<iPcMesh> pcmesh = CEL_QUERY_PROPCLASS_ENT (
  	player_entity, iPcMesh);
  pcmesh->SetPath ("/cel/data");
  pcmesh->SetMesh ("test", "cally.cal3d");
  if (!pcmesh->GetMesh ())
    return ReportError ("Error loading model!");

  if (pczonemgr->PointMesh ("player", "main", "Camera"))
    return ReportError ("Can't find region or start position in region!");

  // Get iPcLinearMovement so we can setup the movement system.
  csRef<iPcLinearMovement> pclinmove = CEL_QUERY_PROPCLASS_ENT (
  	player_entity, iPcLinearMovement);
  pclinmove->InitCD (
  	csVector3 (0.5,0.8,0.5),
  	csVector3 (0.5,0.4,0.5),
  	csVector3 (0,0,0));

  // Get the iPcActorMove interface so that we can set movement speed.
  csRef<iPcActorMove> pcactormove = CEL_QUERY_PROPCLASS_ENT (
  	player_entity, iPcActorMove);
  pcactormove->SetMovementSpeed (3.0f);
  pcactormove->SetRunningSpeed (5.0f);
  pcactormove->SetRotationSpeed (1.75f);

  // Get iPcCommandInput so we can do key bindings. The behaviour
  // layer will interprete the commands so the actor can move.
  csRef<iPcCommandInput> pcinput = CEL_QUERY_PROPCLASS_ENT (
  	player_entity, iPcCommandInput);
  // We read the key bindings from the standard config file.
  pcinput->Bind ("up", "forward");
  pcinput->Bind ("down", "backward");
  pcinput->Bind ("left", "rotateleft");
  pcinput->Bind ("right", "rotateright");
  pcinput->Bind ("m", "cammode");
  pcinput->Bind ("d", "drop");

  return true;

The following three methods are called by the event handler whenever a certain event occurs. ProcessFrame() is called every frame. Normally you would put the code here to draw 3D graphics. However in case of CRystal Entity Layer it is the camera property class that actually takes care of this so the implementation here is empty. In FinishFrame() we actually render everything on screen.

When the player presses a key the OnKeyboard() routine is called. In this case we only listen to the escape key to exit the application since all other keys are handled by the player entity.

void MainApp::ProcessFrame ()

void MainApp::FinishFrame ()
  // Just tell the 3D renderer that everything has been rendered.
  g3d->FinishDraw ();
  g3d->Print (0);

bool MainApp::OnKeyboard(iEvent& ev)
  // We got a keyboard event.
  csKeyEventType eventtype = csKeyEventHelper::GetEventType(&ev);
  if (eventtype == csKeyEventTypeDown)
    // The user pressed a key (as opposed to releasing it).
    utf32_char code = csKeyEventHelper::GetCookedCode(&ev);
    if (code == CSKEY_ESC)
      // The user pressed escape to exit the application.
      // The proper way to quit a Crystal Space application
      // is by broadcasting a csevQuit event. That will cause the
      // main runloop to stop. To do that we get the event queue from
      // the object registry and then post the event.
      csRef<iEventQueue> q =
        CS_QUERY_REGISTRY(GetObjectRegistry(), iEventQueue);
      if (q.IsValid()) q->GetEventOutlet()->Broadcast(
  return false;

Initialization routines. OnInitialize() is called by the application framework. In this routine we call RequestPlugins() to load various plugins that we will need. In addition to the standard plugins that most Crystal Space applications need (like OpenGL renderer, engine, level loader, ...) we also load the CEL physical layer and the OPCODE collision detection plugin. Most of these plugins we will not use directly but they are used by the property classes.

Application() is called when it is time to open the application screen. Here we fetch various modules from the object registry and store a reference in our main class.

Here we also create our behaviour layer and we register it to the object registry and also to the physical layer (with RegisterBehaviourLayer()).

Then we need to load the property class factories for all property classes that we plan to use in this tutorial.

Finally we load our level (LoadLevel()) and create the player (CreatePlayer()).

The last thing we do here is call Run() which will start the main event loop. This function only returns when the application exits.

bool MainApp::OnInitialize (int argc, char* argv[])
  if (!celInitializer::RequestPlugins (object_reg,
    	CS_REQUEST_PLUGIN ("cel.physicallayer", iCelPlLayer),
    	CS_REQUEST_PLUGIN ("crystalspace.collisiondetection.opcode",
    return ReportError ("Can't initialize plugins!");


  if (!RegisterQueue(object_reg, csevAllEvents(object_reg)))
    return ReportError ("Can't setup event handler!");

  return true;

bool MainApp::Application ()
  if (!OpenApplication (object_reg))
    return ReportError ("Error opening system!");

  g3d = CS_QUERY_REGISTRY (object_reg, iGraphics3D);
  engine = CS_QUERY_REGISTRY (object_reg, iEngine);
  loader = CS_QUERY_REGISTRY (object_reg, iLoader);
  vfs = CS_QUERY_REGISTRY (object_reg, iVFS);
  vc = CS_QUERY_REGISTRY (object_reg, iVirtualClock);
  kbd = CS_QUERY_REGISTRY (object_reg, iKeyboardDriver);

  pl = CS_QUERY_REGISTRY (object_reg, iCelPlLayer);
  bl.AttachNew (new BehaviourLayer(pl));

  // We also need to register it to the object registry.
  if (!object_reg->Register (bl, "iCelBlLayer"))
    return ReportError ("Can't register our behaviour layer!");

  // Make sure the application dir is mounted at /cel
  vfs->Mount ("cel", "$.$/");

  pl->RegisterBehaviourLayer (bl);
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.zonemanager"))
    return ReportError ("Error loading pczonemanager factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.solid"))
    return ReportError ("Error loading pcsolid factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.colldet"))
    return ReportError ("Error loading pccolldet factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.defaultcamera"))
    return ReportError ("Error loading pcdefaultcamera factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.mesh"))
    return ReportError ("Error loading pcmesh factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.meshselect"))
    return ReportError ("Error loading pcmeshselect factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.linmove"))
    return ReportError ("Error loading pclinmove factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.pccommandinput"))
    return ReportError ("Error loading pccommandinput factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.actormove"))
    return ReportError ("Error loading pcactormove factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.inventory"))
    return ReportError ("Error loading pcinventory factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.properties"))
    return ReportError ("Error loading pcproperties factory!");
  if (!pl->LoadPropertyClassFactory ("cel.pcfactory.timer"))
    return ReportError ("Error loading pctimer factory!");

  if (!LoadLevel ())
    return ReportError ("Error loading level!");
  if (!CreatePlayer ())
    return ReportError ("Couldn't create player!");

  Run ();

  return true;

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated using texi2html 1.76.