Tutorial: Hello World from scratch

From FIFE development wiki
Jump to: navigation, search

Scope of this tutorial

  • Walk through the creation of a a simple FIFE application from scratch
  • Bare bones code -> no profiling, no Psyco, only limited error handling etc.

Introduction

The tutorial assumes that FIFE is already installed in Folder.png fife.

Create a folder Folder.png hello_world in the same directory that contains Folder.png fife. In Folder.png hello_world, create a file called File.png run.py.

Preparing the basic functionality

Import the FIFE Modules

File.png hello_world/run.py

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
import sys, os
 
fife_path = os.path.join('..', 'fife', 'engine', 'python')
if os.path.isdir(fife_path) and fife_path not in sys.path:
	sys.path.insert(0, fife_path)
 
from fife import fife
print 'FIFE path: ' + os.path.dirname(fife.__file__)


On execution, the output should be something like:

$ ./run.py 
FIFE path: ../fife/engine/python/fife

Load settings.xml

File.png hello_world/run.py

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
import sys, os
 
fife_path = os.path.join('..', 'fife', 'engine', 'python')
if os.path.isdir(fife_path) and fife_path not in sys.path:
	sys.path.insert(0, fife_path)
 
from fife import fife
print 'FIFE path: ' + os.path.dirname(fife.__file__)
 
from fife.extensions.fife_settings import Setting 
settings = Setting(app_name='hello_world',	settings_file='./settings.xml')


Note that I did two things: I added the import for the class 'Setting' and I created an object called 'setting' of that type. In the demos, the name TDS is used instead of 'setting', but I have no clue what it means (I hazard that the 'S' is short for Settings).

Creating the Application Class

For the third step, we need a class that is derived from 'ApplicationBase' that allows us to start the application using the method 'run()'. Following the conventions from the demo, we will put the class into a module called 'scripts'. To do so, we have to perform the following steps (read up on Python modules if you are interested in the details):

  • Create Folder.png hello_world/scripts
  • Create the empty file File.png hello_world/scripts/__init__.py (this essentially tells Python that the directory contains packages)
  • Create File.png hello_world/scripts/helloworld.py that contains the class definition


File.png hello_world/scripts/helloworld.py

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
from fife.extensions.basicapplication import ApplicationBase
 
class HelloWorldApplication(ApplicationBase):
	def __init__(self, settings):
		super(HelloWorldApplication, self).__init__(settings)


For the moment, this simply defines a class called 'HelloWorldApplication' whose parent is 'ApplicationBase'.

Starting the main application

Currently, the application class is not used at all. In order to change this, we need to add code to 'run.py' that creates an object of this type and invokes its 'run()' method:

File.png hello_world/run.py

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
import sys, os
 
fife_path = os.path.join('..', 'fife', 'engine', 'python')
if os.path.isdir(fife_path) and fife_path not in sys.path:
	sys.path.insert(0, fife_path)
 
from fife import fife
print 'FIFE path: ' + os.path.dirname(fife.__file__)
 
from fife.extensions.fife_settings import Setting
from scripts.helloworld import HelloWorldApplication 
settings = Setting(app_name='hello_world',
	settings_file='./settings.xml')
 
def main():	app = HelloWorldApplication(settings)	app.run() if __name__ == '__main__':	main()


Note the following changes:

  • We import the class HelloWorldApplication using "from scripts.helloworld import HelloWorldApplication"
  • We define a function called 'main()' that instantiates a new object of type 'HelloWorldApplication' and invokes its 'run()' method
  • If 'run.py' is being executed as script (__name__ == '__main__'), call the function defined previously

Note: I suppose that the last step could be omitted (i.e. I think it would be OK to unconditionally call main(), but since I don't know much about Python, I am not sure about the implications of not using the __name__ == '__main__' check, and all the demos use it, so I didn't touch that).


Output of File.png run.py:

$ ./run.py 
FIFE path: ../fife/engine/python/fife
Traceback (most recent call last):
  File "./run.py", line 25, in <module>
    main()
  File "./run.py", line 21, in main
    app = HelloWorldApplication(settings)
  File "/home/tobi-wan/src/hello_world/scripts/helloworld.py", line 9, in __init__
    super(HelloWorldApplication, self).__init__(settings)
  File "../fife/engine/python/fife/extensions/basicapplication.py", line 80, in __init__
    self.initLogging()
  File "../fife/engine/python/fife/extensions/basicapplication.py", line 170, in initLogging
    logmodules = self._setting.get("FIFE", "LogModules", ["controller"])
  File "../fife/engine/python/fife/extensions/fife_settings.py", line 455, in get
    if self._readSettingsCompleted[module] is not True:
KeyError: 'FIFE'


Ouch! Maybe now is a good time to create and populate the file 'settings.xml'.

Creating settings.xml

Here's the contents of a minimal File.png hello_world/settings.xml:

<?xml version='1.0' encoding='UTF-8'?>
<Settings>
	<Module name="FIFE">
		<Setting name="FullScreen" type="bool">False</Setting>
		<Setting name="PlaySounds" type="bool">True</Setting>
		<Setting name="RenderBackend" type="str">OpenGL</Setting>
		<Setting name="ScreenResolution" type="str">1024x768</Setting>
		<Setting name="Lighting" type="int">0</Setting>
	</Module>
</Settings>


File.png settings.xml contains one (or multiple) 'Module' sections identified by their 'name' attribute, and each module contains settings.


Ah, finally some progress! If we now execute our 'run.py', we get a load of output and - for a very short time - even something that looks like it could be an application window!

Output of File.png run.py:

$ ./run.py 
FIFE path: ../fife/engine/python/fife
Controller:LOG:================== Engine initialize start =================
Controller:LOG:Time manager created
Controller:LOG:Creating VFS
Controller:LOG:Adding root directory to VFS
Controller:LOG:Adding zip provider to VFS
Controller:LOG:Engine pre-init done
Controller:LOG:Creating event manager
Controller:LOG:Creating resource managers
Controller:LOG:Creating render backend
Controller:LOG:OpenGL Render backend created
Controller:LOG:Initializing render backend
Controller:LOG:Querying device capabilities
Controller:LOG:Creating main screen
Controller:LOG:Main screen created
Controller:LOG:Creating sound manager
Controller:LOG:Creating renderers
Controller:LOG:Creating model
Controller:LOG:Adding pathers to model
Controller:LOG:Adding grid prototypes to model
Controller:LOG:Engine intialized
Traceback (most recent call last):
  File "./run.py", line 25, in <module>
    main()
  File "./run.py", line 21, in main
    app = HelloWorldApplication(settings)
  File "/home/tobi-wan/src/hello_world/scripts/helloworld.py", line 9, in __init__
    super(HelloWorldApplication, self).__init__(settings)
  File "../fife/engine/python/fife/extensions/basicapplication.py", line 95, in __init__
    pychan.init(self.engine, debug = self._finalSetting['PychanDebug'])
  File "../fife/engine/python/fife/extensions/pychan/__init__.py", line 290, in init
    manager = Manager(_munge_engine_hook(engine),debug,compat_layout)
  File "../fife/engine/python/fife/extensions/pychan/compat.py", line 68, in _munge_engine_hook
    engine.getSettings().getDefaultFontGlyphs()
  File "../fife/engine/python/fife/fife.py", line 3596, in setDefaultFont
    return _fife.GUIChanManager_setDefaultFont(self, *args)
RuntimeError
Controller:LOG:Destructing engine
Controller:LOG:================== Engine destructed ==================


That looks nice except for the 'runtimeError'.

What I figured out so far is that this seems to indicate that the application is unable to load the default font. Here's how I solved that problem:

Font

First, I copied the directory (and its contents) Folder.png fife/demos/rpg/fonts/ to Folder.png hello_world/.

Next, I added a setting for 'Font' to File.png hello_world/settings.xml:

<?xml version='1.0' encoding='UTF-8'?>
<Settings>
	<Module name="FIFE">
		<Setting name="FullScreen" type="bool">False</Setting>
		<Setting name="PlaySounds" type="bool">True</Setting>
		<Setting name="RenderBackend" type="str">OpenGL</Setting>
		<Setting name="ScreenResolution" type="str">1024x768</Setting>
		<Setting name="Lighting" type="int">0</Setting>
		<Setting name="Font" type="str">fonts/FreeSans.ttf</Setting>	</Module>
</Settings>


If we start our application now, we get some output and a black window that does not terminate immediately (actually, it only terminates once you press Ctrl+C in the terminal). Not bad, not bad at all. Now we have something to work with.

Creating a map

As a next step, we want to replace the black window with a map of our own. Before we can create the map, however, we need some tiles for it. For that, we need to create so-called objects. We want the objects to be located in the diretory Folder.png hello_world/objects/ground/ and the maps in Folder.png hello_world/objects/maps/, so we create those directories.

Creating some ground tiles

Next, we create some graphics (using any graphics program you are comfortable with, as long as it can produce PNG graphics). For our application, we want the graphic to be 64x64 pixels, and the corners of the ground tile within it should be at the pixel locations (0, 32), (32, 48), (64, 32) and (32, 16).

This sounds complicated, but becomes clear with a few examples (as you can probably tell from them, I'm not an artist).

Important: The background of the graphic must be transparent!


File.png hello_world/objects/ground/grass01.png: Tutorial5 grass01.png File.png hello_world/objects/ground/water01.png: Tutorial5 water01.png


For each object, we also need an corresponding XML file to describe the object (this will be particularly important later, as far as I understand, when it comes to rotation of objects).


File.png hello_world/objects/ground/grass01.xml:

<?fife type="object"?>
<object id="grass01" namespace="http://www.fifengine.de/xml/hello_world" blocking="0" static="1">
	<image source="grass01.png" direction=”45” />
</object>


File.png hello_world/objects/ground/water01.xml:

<?fife type="object"?>
<object id="water01" namespace="http://www.fifengine.de/xml/hello_world" blocking="0" static="1">
	<image source="water01.png" direction=”45” />
</object>


The object ID should be some unique identifier for the object. Regarding rules for selecting a good namespace: I have absolutely no clue, I just stuck with what the demos suggested - maybe just using "hello_world" would also be OK, but I always understood XML namespaces to be URIs. Whatever.

Also, later, we might want to make the water tile 'blocking', but I'm not sure myself yet on how to make water 'non-walkable'. Let's wait and see ;-)

Now that we have two objects (on for grass, one for water), we can start creating the map.

Using the FIFE Editor

Start the editor by executing File.png fife/tools/editor/run.py.

The next steps are (always use the default settings, if nothing else is specified):

  • 'File' -> 'New map'
  • Map name: hello -> OK
  • Layer ID: ground -> OK
  • Camera ID: camera01; Reference cell Width: 72; Reference cell Height: 36; Rotation: 45; Tilt: 60 -> OK

The values for the reference cell, the rotation and the tilt of the camera create a 'perfect isometric' view. I got that information here: [1]


Now that we have the basic map, we need to load our object:

  • 'File' -> 'Import directory'
  • Navigate to Folder.png hello_world/objects/ and press 'Select'
  • Now you should be able to select the two tiles in the Object selector (see screenshot below):

Tutorial5 fife editor objects imported.png


Using the pencil, we can now draw some tiles and save the resulting map using 'File' -> 'Save as' and save the map as File.png hello_world/maps/hello.xml.

Tutorial5 fife editor hello map.png


Loading a map

Now, finally, we can load that map in our application. To do so, we create a new class called 'World' that will later control our whole world:

File.png hello_world/scripts/world.py

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
import os
 
from fife.extensions.loaders import loadMapFile
 
class World(object):
	def __init__(self, engine):
		self._engine = engine
		self._map = None
 
	def loadMap(self, filename):
		self._map = loadMapFile(os.path.join('maps', filename + '.xml'),
			self._engine, extensions = { 'lights': True })


Next, we need to create a 'World' object somewhere and load the first map. We will do that within the application class (HelloWorldApplication) and load the map file name from the settings.

File.png hello_world/scripts/helloworld.py:

#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
from fife.extensions.basicapplication import ApplicationBase
from scripts.world import World 
class HelloWorldApplication(ApplicationBase):
	def __init__(self, settings):
		super(HelloWorldApplication, self).__init__(settings)
		self._world = World(self.engine)		self._world.loadMap(str(settings.get('helloworld',			'MapFile')))


Note the following changes:

  • We import the class 'World' from 'scripts.world'
  • In the constructor, we create a 'self._world' object of type 'World' and load the map defined in the settings within the module 'helloworld' in the setting named 'MapFile'

Now, we need to add that setting to File.png hello_world/settings.xml and set it to 'hello' (the name of the map file):

File.png hello_world/settings.xml:

<?xml version='1.0' encoding='UTF-8'?>
<Settings>
	<Module name="FIFE">
		<Setting name="FullScreen" type="bool">False</Setting>
		<Setting name="PlaySounds" type="bool">True</Setting>
		<Setting name="RenderBackend" type="str">OpenGL</Setting>
		<Setting name="ScreenResolution" type="str">1024x768</Setting>
		<Setting name="Lighting" type="int">0</Setting>
		<Setting name="Font" type="str">fonts/FreeSans.ttf</Setting>
	</Module>
	<Module name="helloworld">		<Setting name="MapFile" type="str">hello</Setting>	</Module></Settings>


Voila! If you now start the application, you should see the map you just created (but nothing else yet)!

Going further

I am planning to extend this tutorial as my knowledge of FIFE progresses (if I find enough spare time). If, in the meantime, anybody wants to add to the content or correct me where I am wrong, please be invited to do so!