Home - Forums - Documentation - Gallery - Bugs
m
Current revision (16:49, 8 July 2008) (edit) (undo)
m (We need Python)
 
(2 intermediate revisions not shown.)
Line 54: Line 54:
I use Python 2.5, but you can use another Python version, if you want. Please build the cspython at least (and optionally _cspace.pyd) when this modules are not compiled yet.
I use Python 2.5, but you can use another Python version, if you want. Please build the cspython at least (and optionally _cspace.pyd) when this modules are not compiled yet.
You can download Python 2.5 from [http://www.python.org/download/releases/2.5/ here.]
You can download Python 2.5 from [http://www.python.org/download/releases/2.5/ here.]
 +
 +
At the end of tutorial, you find a solution ''without'' Python.
=== Note for MSVC users ===
=== Note for MSVC users ===
Line 329: Line 331:
You can load here another scripting plugin. Second option, you implement your own command handling code (like walktest).
You can load here another scripting plugin. Second option, you implement your own command handling code (like walktest).
-
<!-- I show you a very simple and limited version to do this, without explanation. You can type a few command without attributes. You can download here:
 
-
download link-->
+
I show you a very simple and limited version to do this, without explanation. You can type a few command without attributes. You can download [http://www.crystaldoc.atw.hu/src/cim/step1-no_python.zip here]
 +
:
 +
 
 +
 
[[User:Nagyjoco|Nagyjoco]] 11:46, 16 May 2008 (CEST)
[[User:Nagyjoco|Nagyjoco]] 11:46, 16 May 2008 (CEST)

Current revision

Contents

Introduction

This tutorial is the first part of a long (hopefully interesting) serial. In this serial we create a game step by step with Crystal Space. Every step will have a detailed description, with source code of course. I assume, you’ve read (at least) the first three tutorials in the Crystal Space Manual, if not, please, do it first. I assume too that you can build a new (working) CS application in your system, and you don’t have issues when running CS apps in general.

Cally in the maze(Cim) will be a small, but real game. It will contain anything that full games have: GUI, background music, sound effects, story line, debug console, cheat mode, simple rules, load/save system, hall of fame, and installer (on Windows) or install script (on Linux, tested on Suse 10.0).

Cally in the maze falls under the LGPL license. My main system is WindowsXP with MSVC 8 Express. I use the latest stable version of Crystal Space (currently 1.2).

Step One: Creating a Python console

Very first step

The simpmap tutorial is a good starting point to begin Cally In The Maze. It has 3d graphics, collision detection, map loading, so it's ideal for us. You can find the details here: tutorial


Nothing new. We load and initialize the CS environment, load the necessary plugins, load a map, and create a collider actor and a camera. The user can move in the world and can jump. A wise programmer always makes a debug console for his app. Just look at walktest. You can watch, test, debug etc. your maps, or just play around, using console commands. We will use the console heavily later, let’s go to create it.

Console basics

The console in CS has three parts, a console input ( it ensures that you can type into the console), a console output (it ensures that you can show the console’s content), and a so called execution callback (when you press enter, this callback will handle your commands).

The CS SDK has one console input plugin, and two console output plugins (standard and fancy, we will use the standard, but you can use the fancy version, if you want). This execution callback struct is only a pure interface (no standard implementation of course, almost every application needs a different execution callback), so we need to implement this iConsoleExecCallback SCF interface. (There is not a big deal.)

We encapsulate the three classes in one class, called cimConsole. The console class provides all neccessary services, to toggle, show and hide, intialize, etc, the console. The console has an Initialize function, this function loads the needed plugins. The console class is easy to use ( I hope):

class cimConsole:public csRefCount
{
private:
    iObjectRegistry* object_reg; 
    bool visible;

public:
    csRef<iConsoleInput> conin;
    csRef<iConsoleOutput> conout;

    ~cimConsole();
    bool Initialize(iObjectRegistry* obj_reg);
    void Show();
    void Hide();
    void Toggle();
    bool HandleEvent (iEvent& ev);
    bool IsVisible();
};

About csRefCount

Some note about CsRefCount. This class will ensure, that cimConsole can be used with csRef template. Every class, that implement DecRef() and IncRef can be used with csRef. csRefCount implements this functions - so we use csRefCount as ancestor and continue our work happily.

We need Python

We need a script interpreter, the console is useless without this. We don’t write one (like in walktest), we will use Python. When Python is enough for the guys by NASA or Google, it will be enough for Cally in the maze too. You can download it from here: Python homepage

I use Python 2.5, but you can use another Python version, if you want. Please build the cspython at least (and optionally _cspace.pyd) when this modules are not compiled yet. You can download Python 2.5 from here.

At the end of tutorial, you find a solution without Python.

Note for MSVC users

When you use MSVC, like MSVC8 Express, you can see, the official wkstypical solution don’t have project for this. Don’t worry, just follow my MSVC and Python buliding tutorial. Other option: you can download the dll here: cspython.zip

This dll requires CS 1.2, Python 2.5 and works with MSVC8 Express only (and maybe with other MSVC8 editions). Ok, Python is ready to work, let’s go to create an execution callback!

Creating an execution callback

struct ExecCallback : public scfImplementation1<ExecCallback,iConsoleExecCallback>
{
    ExecCallback () : scfImplementationType (this) { }
    virtual ~ExecCallback () { }
    virtual void Execute (const char* cmd);
    csRef<iScript> python;
    bool InitPython(iObjectRegistry *obj_reg);
};


We implement the iConsoleExecCallback struct. This struct has one member function only, the Execute function. We add an InitPython function, and a reference for the python module.

We hide this struct inside the main Console class, (the app’s other elements don’t need to reach this). The console class will be something like this:

class cimConsole: public csRefCount
{
private:
    iObjectRegistry* object_reg;

    struct ExecCallback : public scfImplementation1<ExecCallback,iConsoleExecCallback>
    {
        ExecCallback () : scfImplementationType (this) { }
        virtual ~ExecCallback () { }
        virtual void Execute (const char* cmd);
        csRef<iScript> python;
        bool InitPython(iObjectRegistry *obj_reg);
    };

    bool visible;

public:
    csRef<iConsoleInput> conin;
    csRef<iConsoleOutput> conout;
	
    ~cimConsole();

    bool Initialize(iObjectRegistry* obj_reg);
    void Show();
    void Hide();
    void Toggle();
    bool HandleEvent (iEvent& ev);
    bool IsVisible();
};

Ok, we go to write the implementation.

void cimConsole::ExecCallback::Execute(const char *cmd)
{
    //we call the python interpreter:
    python->RunText(cmd);
}

We call the python interpreter(the RunText function send our text to the Python interpreter) ,and the execution is done (let’s work python!). We initialize the python binding with this code:

bool cimConsole::ExecCallback::InitPython(iObjectRegistry *obj_reg)
{
    python = csQueryRegistryOrLoad<iScript> (obj_reg,
	  "crystalspace.script.python");
    if (!python)
    {
        csReport (obj_reg,
	    	CS_REPORTER_SEVERITY_ERROR, "cim.console",
		"Can't load the python script module!");
        return false;
    }
    python->RunText("from cspace import *");
    if (!python->LoadModule("cshelper") )
        return false;
    return true;
}

We try to load the python plugin first. When done, we import two python module. The cspace module is essential, when we want to use CS functions from python. (Officialy the cspython plugin loads cspace at startup, but I have to load it manually for some reason .).

These scipts are in the CS/scripts/python folder.

The cshelper module is a very useful, it redirects the python stdout and stderr streams to the reporter plugin. The reporter uses our console to show the messages, so we can see the Python interpreter’s any anwser.

Note, when you run the app with –python-enable-reporter comandline option, the cshelper module will be loaded internally, you don’t need to load this from code.

Finishing our console class

bool cimConsole::Initialize(iObjectRegistry* obj_reg)
{
    object_reg = obj_reg;

    conout = csQueryRegistryOrLoad<iConsoleOutput> (object_reg,
        "crystalspace.console.output.standard");
    if (!conout)
    {
        csReport (object_reg,
	    	CS_REPORTER_SEVERITY_ERROR, "cim.console",
		"Can't load the output console!");
        return false;
    }
    conin = csQueryRegistryOrLoad<iConsoleInput> (object_reg,
  	"crystalspace.console.input.standard");
    if (!conin)
    {
        csReport (object_reg,
	    	CS_REPORTER_SEVERITY_ERROR, "cim.console",
		"Can't load the input console!");
        return false;
    }

We load or query the csconin and csconout plugins, the csQueryRegistryOrLoad template is very useful to do this easily. When everything is fine, we bind our console input to the console output, set the prompt, create a new execution callback object, and bind to the console input.

    conin->Bind (conout);
    conin->SetPrompt ("cim:");
    ExecCallback* cb = new ExecCallback ();
    conin->SetExecuteCallback (cb);
    if(!cb->InitPython(object_reg)) return false;

    cb->DecRef ();
    conout->SetVisible (false);

    return true;
}

When the console gets an event, we delegate it to the console output plugin:

bool cimConsole::HandleEvent(iEvent &ev)
{
    return conin->HandleEvent(ev);
}

This functions control the console’s visiblity:

void cimConsole::Hide()
{
    visible = false;
    conout->SetVisible(false);
}

void cimConsole::Show()
{
    visible = true;
    conout->SetVisible(true);
}

void cimConsole::Toggle()
{
    visible = !visible;
    conout->SetVisible(visible);
}

bool cimConsole::IsVisible()
{
    return visible;
}

The console is ready. We create one instance in the main app. Add the following private member to the cimGame class:

Final steps

This comes into the OnInitialize function (cim.cpp):

    csRef<cimConsole> console;
    console.AttachNew(new cimConsole);
    if ( !console->Initialize(GetObjectRegistry() ))
        return ReportError("Failed to initialize console!");
    console->Show();


This comes to the OnKeyboard function:

    //Old code:
    csKeyEventType eventtype = csKeyEventHelper::GetEventType(&ev);
    if (eventtype == csKeyEventTypeDown)
    {
        // The user pressed a key (as opposed to releasing it).
        utf32_char code = csKeyEventHelper::GetCookedCode(&ev);
	
    //End old code
    //The user pressed tab to toggle console
    if (code == CSKEY_TAB)
    {
        console->Toggle();
        return false;
    }

When the user presses the TAB key, we change the console’s visiblity. When the console is active, we have to delegate all keyboard event to the console class:

    if (console->IsVisible() )
        return console->HandleEvent(ev);

Finally, we have to check, when the user presses the arrays, that console is visible or not. When visible, we don’t make any move in the 3d world:

void cimGame::ProcessFrame ()
{
    // First get elapsed time from the virtual clock.
    csTicks elapsed_time = vc->GetElapsedTicks ();

    csVector3 obj_move (0);
    csVector3 obj_rotate (0);

    if (!console->IsVisible())
    {
        if (kbd->GetKeyState (CSKEY_SHIFT))
        {
            // If the user is holding down shift, the arrow keys will cause
            // the camera to strafe up, down, left or right from it's
            // current position.
            if (kbd->GetKeyState (CSKEY_RIGHT))
            // etc.

On the FinishFrame function, we render the console output onto the screen:

void cimGame::FinishFrame ()
{
    // the console rendering part
    if ( console->IsVisible() )
    {
        g3d->BeginDraw (CSDRAW_2DGRAPHICS);
        console->conout->Draw2D (0);
        g3d->BeginDraw (CSDRAW_3DGRAPHICS);
        console->conout->Draw3D (0);
    }	
    // Just tell the 3D renderer that everything has been rendered.
    g3d->FinishDraw ();
    g3d->Print (0);
}

And now...

And now compile the app and test it. With the TAB key you can toggle the console, and you can add regular Python commands, Python answers on the console. We’re done. We have a console, we can control his visiblity, we call python to interpret our commands.

Get the sources from here

I don't want to use Python!

When you want use other scripting langeuge, just change this function:

bool cimConsole::ExecCallback::InitPython(iObjectRegistry *obj_reg)
{
    python = csQueryRegistryOrLoad<iScript> (obj_reg,
	  "crystalspace.script.python");
.
.
.
//more code
    

You can load here another scripting plugin. Second option, you implement your own command handling code (like walktest).

I show you a very simple and limited version to do this, without explanation. You can type a few command without attributes. You can download here


Nagyjoco 11:46, 16 May 2008 (CEST)


Tutorial Home

Next: Step2 - Basic configuration handling

| Article | Discussion | View source | History |