Home - Forums - Documentation - Gallery - Bugs

Contents

Introduction

A common requirement for games (and other CEL applications) is to display a menu. One option for this is to use a third party UI component like CEGUI (http://www.cegui.org.uk/). For simple menus however it's quicker and easier to use CEL billboards.

This tutorial uses XML to define entities and C++ behaviours, but should be adaptable if you use other languages.

Creating a basic billboard entity

First lets get a basic billboard working. Add the following to your entities XML file, or create one if you don't already have one:

<world>
  <textures>
    <texture name="menu_exit">
      <file>/menu/menu_exit.png</file>
      <keepimage />
    </texture>    
  </textures>

  <materials>
    <material name="menu_exit">
      <texture>menu_exit</texture>
    </material>    
  </materials>
  
  <addon plugin="cel.addons.celentity" entityname="menu_exit_entity" >
    <behaviour layer="thebl" name="menu_behave" />
    <propclass name="pcbillboard">
      <property name="name" string="menu_exit" />
      <property name="materialname" string="menu_exit" />
      <property name="movable" bool="false" />
      <property name="clickable" bool="true" />
      <property name="restack" bool="false" />
      <property name="width" long="61440" />
      <property name="height" long="34800" />
      <property name="x" long="19000" />
      <property name="y" long="76600" />
    </propclass>
  </addon>
</world>

The above file creates a texture called menu_exit from the /menu/menu_exit.png file. It then assigns that texture to a material called menu_exit. Finally using the celentity addon we create a CEL entity called menu_exit_entity. We give the entity the menu_behave behaviour and a pcbillboard property class. The properties of the billboard define it's position and dimensions, set it so it is clickable and assign it the menu_exit material.

Note that billboard screen space is 307200*307200 which is a magic value divisible by common screen resolutions (600, 640, 800, 1024...). To convert sizes from pixels do the following:

  • take billboard screen space width 307200
  • divide by screen resolution width in pixels (e.g. 1024 for 1024x768 resolution)
  • multiply by number of pixels width of your image
  • do the same for height

You'll need to change the name of the behaviour layer from thebl to whatever your behaviour layer is called.

Implementing the behaviour

First in our application initialisation we need to load the property class factory for billboards like this:

if (!pl->LoadPropertyClassFactory ("cel.pcfactory.billboard"))  return ReportError ("Error loading pcbillboard factory!");

Then we implement our behaviour...

behaviourmenu.h

#ifndef BEHAVIOURMENU_H
#define BEHAVIOURMENU_H

#include <crystalspace.h>
#include <physicallayer/pl.h>
#include <physicallayer/entity.h>

#include "behaviourlayer.h"
#include "behaviourcommon.h"

class BehaviourMenu : public BehaviourCommon {
private:
  csStringID id_pcbillboard_select;

public:
  BehaviourMenu( iCelEntity* entity, BehaviourLayer* bl, iCelPlLayer* pl);
  virtual ~BehaviourMenu () { }

  virtual const char* GetName () const { return "menu_behave"; }
  virtual bool SendMessage (csStringID msg_id, iCelPropertyClass* pc, celData& ret, iCelParameterBlock* params, va_list arg);
};

#endif

This is a fairly standard behaviour header file.


behaviourmenu.cpp

#include "behaviourmenu.h"
#include "screenmanager.h"

BehaviourMenu::BehaviourMenu( iCelEntity* entity, BehaviourLayer* bl, iCelPlLayer* pl) : BehaviourCommon (entity, bl, pl) {
  id_pcbillboard_select = pl->FetchStringID ("pcbillboard_select");
}

bool BehaviourMenu::SendMessage (csStringID msg_id, iCelPropertyClass* pc, celData& ret, iCelParameterBlock* params, va_list arg) {
  if (msg_id == id_pcbillboard_select) {
    const char *entityName = this->GetEntity()->GetName();
    if ( !strcmp( entityName, "menu_exit_entity")) {
      printf("***Exit clicked***");
    }
  } else 
    return BehaviourCommon::SendMessage (msg_id, pc, ret, params, arg);

  return true;
}

In our constructor we create a string ID for the select message which is sent when the user clicks on the billboard. Then in the SendMessage method we check that it was the exit menu entity that was clicked and if it was print ***Exit clicked***. Replace this print message with your exit handling code here.

Adding other menu items

To add other menu items simply add another texture, material and menu item entity to your entities file, here we add a start menu item:

<world>
  <textures>
    <texture name="menu_start">
      <file>/menu/menu_start.png</file>
      <keepimage />
    </texture>
    <texture name="menu_exit">
      <file>/menu/menu_exit.png</file>
      <keepimage />
    </texture>    
  </textures>

  <materials>
    <material name="menu_start">
      <texture>menu_start</texture>
    </material>
    <material name="menu_exit">
      <texture>menu_exit</texture>
    </material>    
  </materials>
  
  <addon plugin="cel.addons.celentity" entityname="menu_start_entity" >
    <behaviour layer="thebl" name="menu_behave" />
    <propclass name="pcbillboard">
      <property name="name" string="menu_start" />
      <property name="materialname" string="menu_start" />
      <property name="movable" bool="false" />
      <property name="clickable" bool="true" />
      <property name="restack" bool="false" />
      <property name="width" long="61440" />
      <property name="height" long="34800" />
      <property name="x" long="19000" />
      <property name="y" long="26600" />
    </propclass>
  </addon>

  <addon plugin="cel.addons.celentity" entityname="menu_exit_entity" >
    <behaviour layer="thebl" name="menu_behave" />
    <propclass name="pcbillboard">
      <property name="name" string="menu_exit" />
      <property name="materialname" string="menu_exit" />
      <property name="movable" bool="false" />
      <property name="clickable" bool="true" />
      <property name="restack" bool="false" />
      <property name="width" long="61440" />
      <property name="height" long="34800" />
      <property name="x" long="19000" />
      <property name="y" long="76600" />
    </propclass>
  </addon>
</world>

Then update the SendMessage method in your behaviourmenu.cpp file:

bool BehaviourMenu::SendMessage (csStringID msg_id, iCelPropertyClass* pc,	celData& ret, iCelParameterBlock* params, va_list arg) {
  if (msg_id == id_pcbillboard_select) {
    const char *entityName = this->GetEntity()->GetName();
    if( !strcmp( entityName, "menu_start_entity")) {   
      printf("***Start clicked***");
    } else if ( !strcmp( entityName, "menu_exit_entity")) {
      printf("***Exit clicked***");
    }
  } else 
    return BehaviourCommon::SendMessage (msg_id, pc, ret, params, arg);

  return true;
}

Final thoughts

| Article | Discussion | View source | History |