StandAlone tutorial

& localization）

Summary

This is our first basic C++ tutorial. It also
shows how to write a stand alone executable using orx and how to use
the localization module (orxLOCALE).

As
we are NOT using the default executable anymore for this tutorial, its
code will be directly compiled into the executable and not into an
external library.

This implies that we
will NOT have the default hardcoded behavior we had in the previous
tutorials:

• F11 will not affect vertical sync toggler
• Escape
won't automatically exit
• F12 won't capture a
screenshot
• Backspace won't reload configuration files
• the
[Main] section in the config file won't be used to load a plugin
(“GameFile” key)

• F11 切换垂直同步
• Escape
退出
• F12 截屏
• 退格键(Backspace)
重新载入全部配置文件
• 配置文件中的[Main] 配置段被用于加载一个插件（“GameFile” 键）

A program based
directly on orx

1)

, by default, will
also NOT exit     if  it receives the orxSYSTEM_EVENT_CLOSE event.To do
so, we will either have to use the helper orx_Execute() function (

see below

) or handle it
ourselves.

ORX 启动器)，默认收到orxSYSTEM_EVENT_CLOSE事件后不退出。为了改变这种情况，我们必须使用orx_Execute()

See
previous

basic tutorials

basic

object creation

,
clock handling

,
frames hierarchy

,
animations

,
cameras &
viewports

,

sounds &
musics

,

FXs

,
physics

and
scrolling

.

object creation

,
clock handling

,
frames hierarchy

,
animations

,
cameras &
viewports

,

sounds &
musics

,

FXs

,
physics

and
scrolling

As we're on our own
here, we need to write the main function and initialize orx manually.The
good thing is that we can then specify which modules we want to use,
and deactivates display or any other module at will, if needed.

If we still want a semi-automated
initialization of orx, we can use the orx_Execute() function.This
tutorial will cover the use of orx with this helper function, but you
can decide not to use it if its behavior doesn't suit your needs.

This helper function
will take care of initializing everything correctly and exiting
properly.

It
will also make sure the clock module is constantly ticked (as it's part
of orx's core) and that we exit if the orxSYSTEM_EVENT_CLOSE event is
sent.

This
event is sent when closing the windows, for example, but it can also be
sent under your own criteria (escape key pressed, for example).

orxSYSTEM_EVENT_CLOSE事

This code is also a basic C++ example to show
how to use orx without having to write C code.

This tutorial could
have been architectured in a better way (cutting it into pieces with
headers files, for example) but we wanted to keep a single file per
*basic* tutorial.

（例如分割成多个头文件）但是我们希望每个基础教程都只有一个文件。

This stand alone executable also creates a
console (as does the default orx executable), but you can have you own
console-less program if you wish.

In order to achieve that, you only need to
provide an argc/argv style parameter list that contains the executable
name.

If
you don't, the default loaded config file will be orx.ini instead of
being based on our executable name (ie. 10_StandAlone.ini).

orx可执行文件一样），但是如果你喜欢也可以创建一个没有终端的可执行文件。

For
visual studio

users (windows), it
can easily be achieved by writing a WinMain() function instead of
main(), and by getting the executable name (or hardcoding it, as it's
shamelessly done in this tutorial

This tutorial simply
display orx's logo and a localized legend. Press space or click left
mouse button to cycle through all the availables languages for the
legend's text.

Some explanations
about core elements that you can find in this tutorial:

• Run
function: Don't put *ANY* logic code here, it's only a backbone where
you can handle default core behaviors (tracking exit or changing locale,
for example) or profile some stuff. As it's directly called from the
main loop and not part of the clock system, time consistency can't be
enforced. For all your main game execution, please create (or use an
existing) clock and register your callback to it.

• 运行
函数（Run
function)：不要在这里放置任何逻辑代码，它只是一个处理默认核心行为（例如跟踪退出或者更改locale）或简要描述一些东西的主干。由于它直
接从main循环中调用并且不是clock系统的的一部分，时间一致性无法被强制执行。对你所有的主游戏可执行文件，请在此创建（或者使用已有
的）clock并且注册你的回调函数。

• Event handlers: When
an event handler returns orxSTATUS_SUCCESS, no other handler will be
called after it for the same event. On the other hand, if
orxSTATUS_FAILURE is returned, event processing will continue for this
event if other handlers are listening this event type. We'll monitor
locale events to update our legend's text when the selected language is
changed.

• 事件处理器（Event
handlers）：当一个事件响应函数返回orxSTATUS_SUCCESS后，对此事件就不会再调用其他事件响应函数。另一方面，如果返回的是
orxSTATUS_FAILURE，事件会传递给其他正在监听这个事件的响应函数。我们将监视locale事情以便当选择的语言切换时更新我们的说明文
本。（备注：这个翻译有点乱，主要要了解Orx的事件处理方式）

• orx_Execute():
Inits and executes orx using our self-defined functions (Init, Run and
Exit). We can of course not use this helper and handles everything
manually if its behavior doesn't suit our needs. You can have a look at
the content of orx_Execute()

2)

to have a better idea
on how to do this.

• orx_Execute()：通过我们自定义
的函数（Init，Run和Exit）初始化和执行orx。当然，如果它不符合我们的需求，我们可以不使用这个辅助函数并且手动处理所有事情。你可以通过
参考orx_Execute()的内容（注2，在orx.h中实现）了解怎么这样做。（备注，作者的better
idea不是说更好的注意，而是对于how to do this的修饰，就是我们不知道时候说，I have no idea一样）

Details

includes.

#include "orx.h"

That's all you need to
include so as to use orx. This include works equally with a C or a C++
compiler

3)

.

这就是你要使用orx所需要包含的唯一文件。它在C和C++（注释3，在这种情 况下预编译宏 __orxCPP__

Let's
now have a look at our StandAlone class that contains orx's Init(),
Run() and Exit() callbacks.

Init()、Run()和Exit()回调函数。

class StandAlone

{

public:

static orxSTATUS
orxFASTCALL EventHandler(const orxEVENT *_pstEvent);

static
orxSTATUS orxFASTCALL Init();

static void orxFASTCALL Exit();

static
orxSTATUS orxFASTCALL Run();

void SelectNextLanguage();

StandAlone() :
m_poLogo(NULL), s32LanguageIndex(0) {};

~StandAlone()
{};

private:

orxSTATUS InitGame();

Logo *m_poLogo;

orxS32
s32LanguageIndex;

};

All
the callbacks could actually have been defined out of any class. This
is done here just to show how to do it if you need it.

We see that our
StandAlone class also contains our Logo object and an index to the
current selected language.

Let's now have a look
to our Logo class definition.

class Logo

{

private:

orxOBJECT
*m_pstObject;

orxOBJECT *m_pstLegend;

public:

Logo();

~Logo();

};

Nothing fancy here, we
have a reference to an orxOBJECT that will be our logo and another one
that will be the displayed localized legend.

As you'll see we won't
use the reference at all in this executable, we just keep them so as to
show a proper cleaning when our Logo object is destroyed. If we don't
do it manually, orx will take care of it when quitting anyway.

orxOBJECT作为我们的logo，另一个用来指向显示的本地化说明。如你所见，我们在这个可执行文件里将不会使用这个可执行文件的所有引用，我们只

Let's now see its
constructor.

Logo::Logo()

{

m_pstObject =
orxObject_CreateFromConfig("Logo");

orxObject_SetUserData(m_pstObject, this);

m_pstLegend =
orxObject_CreateFromConfig("Legend");

}

As seen in the previous tutorials we
create our two objects (Logo and Legend) and we link our Logo C++ object
to its orx equivalent using orxObject_SetUserData().

（Logo和Legend）并且我们通过

orxObject_SetUserData()把Logo C++对象链接到它对应的orx对象上。

Logo::~Logo()

{

orxObject_Delete(m_pstObject);

orxObject_Delete(m_pstLegend);

}

Simple cleaning here
as we only delete our both objects.

Let's now see our main
function.

int
main(int argc, char **argv)

{

orx_Execute(argc, argv,
StandAlone::Init, StandAlone::Run, StandAlone::Exit);

return
EXIT_SUCCESS;

}

As
we can see, we're using the orx_Execute() helper that will initialize
and execute orx for us.

In order to do so, we need to provide it our
executable name and the command line parameters along with three
callbacks: Init(), Run() and Exit().

We will only exit from
this helper function when orx quits.

Let's have a quick
glance at the console-less version for windows.

#ifdef __orxMSVC__

int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE PrevInstance,

LPSTR
lpCmdLine, int nCmdShow) {

// Inits and executes orx

orx_WinExecute(StandAlone::Init, StandAlone::Run, StandAlone::Exit);

// Done!

return
EXIT_SUCCESS;

}   #endif

Same as for the traditional main() version
except that we use the orx_WinExecute() helper that will compute the
correct command line parameters and use it.

4)（the ones given
as parameter don't contain the executable name which is needed to
determine the main config file name

This only works for a
console-less windows game

5)

.(which uses WinMain()

Let's now see how our
Init() code looks like.

orxSTATUS
StandAlone::Init()

{

orxLOG("10_StandAlone Init() called!");

return
soMyStandAloneGame.InitGame();

}

We simply initialize our StandAlone instance
by calling its InitGame() method.

Let's see its content.

EventHandler);

m_poLogo = new Logo();

std::cout << "The available
languages are:" << std::endl;

for(orxS32 i = 0; i <
orxLocale_GetLanguageCounter(); i++)

{

std::cout << " - "
<< orxLocale_GetLanguage(i) << std::endl;

}

orxViewport_CreateFromConfig("Viewport");

We simply register a
callback to catch all the orxEVENT_TYPE_LOCALE events.

We then instanciate
our Logo object that contains both logo and legend.

We also outputs all
the available languages that have been defined in config files. We could
have used the orxLOG() macro to log as usual (on screen and in file),
but we did it the C++ way here to show some diversity.

We finish by creating
our viewport, as seen in all the previous tutorials.

orxEVENT_TYPE_LOCALE事件的回调函数。

Let's now see our
Exit() callback.

void
StandAlone::Exit()

{

delete soMyStandAloneGame.m_poLogo;

soMyStandAloneGame.m_poLogo = NULL;

orxLOG("10_StandAlone Exit()
called!");

}

Simple Logo object
deletion here, nothing surprising.

Now let's have a look
to our Run() callback.

orxSTATUS
StandAlone::Run()

{

orxSTATUS eResult = orxSTATUS_SUCCESS;

if(orxInput_IsActive("CycleLanguage") &&
orxInput_HasNewStatus("CycleLanguage"))

{

soMyStandAloneGame.SelectNextLanguage();

}

if(orxInput_IsActive("Quit"))

{

orxLOG("Quit action triggered, exiting!");

eResult =
orxSTATUS_FAILURE;

}

return eResult;

}

Two things are done here.

First when the input
CycleLanguage is activated we switch to the next available language,
then when the Quit one is activated, we simply return orxSTATUS_FAILURE.

When the Run()
callback returns orxSTATUS_FAILURE orx (when used with the helper
orx_Execute()) will quit.

orx_Execute()辅助函数）将会退出。

Let's have a quick look to the
SelectNextLanguage() method.

void
StandAlone::SelectNextLanguage()

{

s32LanguageIndex = (s32LanguageIndex ==
orxLocale_GetLanguageCounter() - 1) ? 0 : s32LanguageIndex + 1;

orxLocale_SelectLanguage(orxLocale_GetLanguage(s32LanguageIndex));

}

We basically go to the
next available language (cycling back to the beginning of the list when
we reached the last one) and selects it with the
orxLocale_SelectLanguage() function.

When doing so, all created orxTEXT
objects will be automatically updated if they use a localized string.
We'll see how to do that below in the config description.

We can also catch any
language selection as done in our EventHandler callback.

orxSTATUS orxFASTCALL
StandAlone::EventHandler(const orxEVENT *_pstEvent)

{

switch(_pstEvent->eID)

{

case orxLOCALE_EVENT_SELECT_LANGUAGE:

orxLOG("Switching to '%s'.", pstPayload->zLanguage);

break;

default:

break;

}

return
orxSTATUS_FAILURE;

}

As
you can see, we only track the orxLOCALE_EVENT_SELECT_LANGUAGE event
here so as to display which is the new selected language.

orxLOCALE_EVENT_SELECT_LANGUAGE事件来显示新选择的语言。

We're now done with
the code part of this tutorial. Let's now have a look at the config.

First of all, as you
might have seen, we use different folder for different architectures.

In other words, the
tutorials for Mac OS X are in the /mac folder, the ones for Linux in the
/linux and so on. By default orx will look in the current folder to
find the main config file.

As we don't want to duplicate the config file
in all the architecture folders, we create a very simple one which
purpose is only to include the one that contains all the info and which
is in the parent folder.

/mac文件夹，Linux的教程放在/linux，以此类推。默认的情况下，orx会查找当前目录下的主配置文件。

Let's see how we do this by looking at the
content of 10_StandAlone.ini from one of the sub-folder (ie. one that is
stored in the same folder than the tutorial executable).

@../10_StandAlone.ini@

That's all we can find
in it. As you can see in the

template files

, we can include other
config files by writing @path/to/FileToInclude@.

/FileToInclude@ 在一个配置文件中包含其他的配置文件。（译注：这里怀疑是作者的问题,template
files实际是想链接到WIKI的配置的说明上去）

Let's now have a look at the config file
which is stored in the parent folder (ie.

../10_StandAlone.ini

).

../10_StandAlone.ini

First let's define our
display.

[Display]

ScreenWidth   = 800

ScreenHeight  = 600

Title         = Stand
Alone/Locale Tutorial

As you can see, we're creating a window of
resolution 800×600 and define its title.

800×600的窗口，并且定义了他的标题。

We now need to provide info for our viewport
and camera.

[Viewport]

Camera          =
Camera

BackgroundColor
= (20, 10, 10)

[Camera]

FrustumWidth  = @Display.ScreenWidth

FrustumHeight =
@Display.ScreenHeight

FrustumFar    = 2.0

Position      = (0.0,
0.0, -1.0)

Nothing new here as
everything was already covered in the

viewport tutorial

.

（viewport
tutorial）中提到的没有任何区别。

Let's now see which inputs are defined.

[Input]

SetList = MainInput

[MainInput]

KEY_ESCAPE  = Quit

KEY_SPACE   =
CycleLanguage

MOUSE_LEFT  = CycleLanguage

In the Input section, we define all our
input sets. In this tutorial we'll only use one called MainInput but we
can define as many sets as we want (for example, one for the main menu,
one for in-game, etc…).

The MainInput sets contain 3 mapping:

• KEY_ESCAPE
will trigger the input named Quit
• KEY_SPACE
and MOUSE_LEFT will both trigger the input named CycleLanguage

MainInput集合包括3个映射：

• KEY_ESCAPE
会触发名为Quit的输入
• KEY_SPACE 和 MOUSE_LEFT 都会触发名为CycleLanguage的输入

We can add as many
inputs we want in this section and bind them to keys, mouse buttons
(including wheel up/down), joystick buttons or even joystick axes.

Let's now see how we
define languages that will be used by the orxLOCALE module.

orxLOCALE模块使用的语言。

[Locale]

LanguageList =
English#French#Spanish#German#Finnish#Swedish#Norwegian

[English]

Content = This is
orx's logo.

Lang
= (English)

[French]

Content = Ceci est le logo d'orx.

Lang    = (Fran?ais)

[Spanish]

Content = Este es el
logotipo de orx.

Lang    = (Espa?ol)

[German]

Content = Das ist orx Logo.

Lang    = (Deutsch)

[Finnish]

Content = T?m? on orx
logo.

Lang
= (Suomi)

[Swedish]

Content = Detta ?r orx logotyp.

Lang    = (Svenska)

[Norwegian]

Content = Dette er orx
logo.

Lang
= (Norsk)

To
define languages for localization we only need to define a Locale
section and define a Language List that will contain all the languages
we need.

After
that we need to define one section per language and for every needed
keys (here Content and Lang) we set their localized text.

Locale配置段和一个包含我们所需要全部的语言的列表。

As the localization system in based on orx's
config one, we can use its inheritance capacity for easily adding new
languages to the list (in another extern file, for example), or even for
completing languages that have been partially defined.

Let's now see how we
defined our Logo object.

[LogoGraphic]

Texture =
../../data/object/orx.png

Pivot   = center

[Logo]

Graphic   =
LogoGraphic

FXList
= FadeIn # LoopFX # ColorCycle1

Smoothing = true

Again, everything we
can see here is already covered in the

object tutorial

.

（object tutorial）中涵盖了。

If you're curious you
can look directly at

10_StandAlone.ini

to see which kind of
FXs we defined, but we won't cover them in detail here.

10_StandAlone.ini

，但是我们不会在这里讨论它们的细节。

Next thing to check:
our Legend object.

[Legend]

ChildList = Legend1 # Legend2

Surprise! Actually
it's an empty object that will spawn two child objects: Legend1 and
Legend2.

Code-wise we were
creating a single object called Legend but apparently we'll end up with
more than one object.

The same kind of technique can be used to
generated a whole group of objects, or a complete scenery for example,
without having to create them one by one code-wise.

It's even possible to
chain objects with ChildList and only create a single object in our code
and having hundreds of actual objects created.

However, we won't have
direct pointers on them, which means we won't be able to manipulate
them directly.

That being said, for all non-interactive/background object
it's usually not a problem.

Be also aware that their frames (cf.
frame tutorial

) will reflect the
hierarchy of the ChildList 'chaining'.

Ok, now let's get back
to our two object, Legend1 and Legend2.

[Legend1]

Graphic       =
Legend1Graphic

Position      = (0, 0.25, 0.0)

FXList        =
ColorCycle2

ParentCamera
= Camera

[Legend2]

Graphic       =
Legend2Graphic

Position      = (0, 0.3, 0.0)

FXList        =
@Legend1

ParentCamera
= @Legend1

They
look very basic, they're both using the same FX (ColorCyle2), they both
have a Position and each of them has its own Graphic.

FX（ColorCyle2），它们也都有一个位置并且各自有它们的Graphic。

NB: We can also see
that we defined the ParentCamera attribute for both of them. This means
that their actual parent will become the camera and not the Legend
object in the end.

However Legend will
still remain their owner, which means that they'll automatically be
erased when Legend will be deleted.

Let's now finish by
having a look at their Graphic objects.

[Legend1Text]

String = $Content [Legend2Text] String =$Lang

[Legend1Graphic]

Pivot = center

Text  = Legend1Text

[Legend2Graphic]

Pivot = center

Text  = Legend2Text

We can see that each
Graphic has its own Text attribute: Legend1Text and Legend2Text.

They both have a
different String.

The leading $character indicates that we won't display a raw text but that we'll use the content as a key for the localization system. So in the end, the Legend1 object will display the localized string with the key Content, and Legend2 the one which has the key Lang. 我们可以看到每一个Graphic都有自己的 Text属性：Legend1Text和Legend2Text。 它们都有不同的String字段。（译注：这里的String是指配置） 开头的$字符说明我们不会显示一个原始的文本但

Everytime
we will switch to another language, both orxTEXT objects (ie.
Legend1Text and Legend2Text) will have their content updated
automagically in the new selected language.

As we saw earlier, we can catch the
orxLOCALE_EVENT_SELECT_LANGUAGE event to do our own specific processing
in addition, if needed.

Legend2Text）会根据新选择的语言自动更新它们的内容。：）

orxLOCALE_EVENT_SELECT_LANGUAGE事件来进行特定的处理。

Resources

10_StandAlone.ini

1)

2)

3)

__orxCPP__ 会被自动定义

4)

5)

Posted By 九天雁翎 at 九天雁翎的博客 on 2010年07月07日