Nov 11
Tutorial nr.1
This tutorial has been written by Nyxojaele
CREATING A “BAREBONES” APPLICATION IN THE LTE GAME ENGINE:
(Copy + Paste the block of text inside the square brackets [] to find the section you want to read)
Table of Contents:
Intro..................[INTR]
Makefile...............[MAKE]
Application............[APCT]
Final Code.............[FULL]
Closing comments.......[CLSE]
[INTR]
It is assumed, coming into this tutorial, that you have a basic understanding of C++, and that you have already successfully setup your development environment to program using the LTE GE SDK.
A “barebones” application is something that will do only what is absolutely required to get the application running without doing anything else.
Barebones applications are incredibly useful for 2 reasons (that really are only 1 reason with 2 slightly different purposes): Creating a barebones application and saving it as thus, makes it very easy to start up your next LTE GE project without having to write your startup code again. Very useful for people who like to write many small programs. Additionally, having a pre-created barebones application available makes it quicker to throw together a “sandbox” application to test chunks of your main application’s code to see if it’s creating your latest bug that’s been depriving you of sleep for weeks at a time. Quite useful for those of us who are writing very large applications in excess of 10,000 lines of code.
The LTE GE (LTE Game Engine) comes with some examples that use some common code to put the PSP into a state of mind ready to properly accept your LTE GE application. At first I’d thought about writing a tutorial that eliminates the need for this external file, but since it’s likely that you’ll never need any code at such a low level in your program, I personally find it quite acceptable to leave this file as is, and simply include it where we’re doing our initialization routines. That said, it is assumed that during this tutorial, you have a copy of the common.h file that comes with the examples included with the LTE GE SDK package.
[MAKE]
The first thing you’ll need is a barebones makefile that makes it easy to do things as you see fit:
A makefile is a plain text file saved with the name “Makefile” (yes, there’s no extension!)
Makefile
TARGET = BAREBONES
OBJS = main.oCFLAGS = -O2 -G0
#CFLAGS = -O2 -G0 -g
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
#CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti
ASFLAGS = $(CFLAGS)LIBDIR =
LIBS = -lengine -lglut -lGLU -lGL -lpspvfpu -lm -lstdc++ -lpsppower -lpsprtc -lpspaudio -lpspwlan
LDFLAGS =
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Barebones Application
#PSP_EBOOT_ICON = ICON0.PNG
#PSP_EBOOT_ICON1 = ICON1.PMF
#PSP_EBOOT_UNKPNG = PIC0.PNG
#PSP_EBOOT_PIC1 = PIC1.PNG
#PSP_EBOOT_SND0 = SND0.AT3
PSPSDK = $(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
Most of this stuff will be fine to leave alone for any application, but I'l explain the parts I've created to be customized as you see fit.
This line merely gives an internal name to the application, to be used for things such as the folder name the application will use in your memory stick, and a filename for your compiled .ELF file. Change this to something unique that gives somebody a clue as to what the application is.
TARGET = BAREBONES
You’ll notice that 1 of these lines has a # in front of it: That means that particular line is commented out, and thus, ignored. At any given point in time, only 1 of these lines should be valid (That is, 1 should always have a # in front of it!) The line that is currently commented out has 1 additional flag assigned: -g . What -g does is tells the compiler to compile your application with symbols, allowing for easier debugging, typically. If you’re never going to be debugging via psp-gdb, eclipse, etc, then I recommend you just leave things how they are.
CFLAGS = -O2 -G0
#CFLAGS = -O2 -G0 -g
Again, I have a set of 2 lines, 1 commented out. The only difference is that 1 line allows exceptions, and the other line doesn’t. If you’re never going to use any try/catch/throw lines, then just leave this how it is.
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
#CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti
I only bring this line to attention to point out the fact that the lpspwlan library is being included. What this means is that this tutorial is meant for users of the LTE GE 2.1 or higher, since that is when wireless support was added, and the entrypoint of the application was changed in direct response to that. If you’re using an earlier version, either update, or you’ll have to do a couple of things differently. I’ll try to remember to point these out, but no guarantees^^
LIBS = -lengine -lglut -lGLU -lGL -lpspvfpu -lm -lstdc++ -lpsppower -lpsprtc -lpspaudio -lpspwlan
This line defines the name that appears on the PSP’s XMB when you’re selecting the application from your memory stick. Name it something cool^^
PSP_EBOOT_TITLE = Barebones Application
These lines define external files that go beyond the scope of this tutorial. By uncommenting any of these lines, it is assumed that the corresponding file exists. Every line here is optional. A quick rundown of each file’s purpose:
ICON0.PNG is the image shown in the List of programs on your memory stick when you’re browsing thru the PSP’s XMB
ICON1.PMF is an animation to be used instead of ICON0.PNG
PIC0.PNG is a “content description” graphic. It fades in after the this application is selected in the XMB. It’s a graphic file, but usually it only shows text, and has transparency to allow you to see the background still.
PIC1.PNG is a background image that fills the screen after the application is selected in the XMB.
SND0.AT3 is a looping sound file that plays after the application is selected in the XMB
#PSP_EBOOT_ICON = ICON0.PNG
#PSP_EBOOT_ICON1 = ICON1.PMF
#PSP_EBOOT_UNKPNG = PIC0.PNG
#PSP_EBOOT_PIC1 = PIC1.PNG
#PSP_EBOOT_SND0 = SND0.AT3
That should give you whatever kind of Makefile you want, with the option to easily change it to do more for you. It should be noted that everytime you create a new .h/.cpp file pair, a new object needs to be compiled, and you have to define that in your Makefile in this line:
OBJS = main.o
Simply add filenames after “main.o”, seperated by spaces. So if I created a camera class out of the files camera.h/camera.cpp, then I would change the line to this:
OBJS = main.o camera.o
This allows the compiler to know what all it needs to compile. If you don’t do this, you’ll get errors everywhere that refers to the files that haven’t been compiled. The most common error would be something about being unable to find a reference to an object.
[APCT]
During this section, contrary to the last section, I will be providing lines of code first, and a final compilation of all the code at the end. This is because the final product is more than can be easily seen at the same time as reading descriptions.
Since in our makefile, we defined the main.o object as needing to be compiled, our first file should create this object. Our first file will be named main.cpp (which will compile into main.o). Inside our main.cpp file is where we put our code:
The first thing you need when using the LTE GE, is to include the header file for it. This gives you access to everything you could possibly want to use from the engine, so there’s no hunting down which files you need to include for a particular class.
#include <engine.h>
The next thing you should do is tell the LTE GE whether you’re wanting to compile in DEBUG mode or not. By leaving this line uncommented, you are basically allowing the LTE GE to setup your PSP to allow for debug output and whatnot, that you would otherwise not need. I recommend leaving this line uncommented for the duration of your coding, and commenting it out only to test a “release build”, or to actually create a release build when you’re done. (It is common coding practice with large projects, and highly recommended, to compile a “release build” periodically thruout the duration of your application’s development, to avoid as many release build only bugs as possible beforehand) This #define line MUST appear before you #include the common.h file, since the common.h file is where the LTE GE checks if PSP_ENABLE_DEBUG has been defined or not.
#define PSP_ENABLE_DEBUG
#include "common.h"
At this point, you can set yourself to be using any/all of the namespaces that you need. For those of you who are unfamiliar with namespaces, a basic idea of them is that the LTE GE is divided into sections (called namespaces) to make it easier to handle. Each namespace has it’s own set of variables, and they cannot interfere with each other. For simple applications, it’s quite acceptable to just use all the namespaces at the same time.
using namespace engine;using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
using namespace audio;Note that you can simply NOT use any namespaces at all, but then you must refer to every class by means of it’s namespace as well. For example, the texture class used in the LTE GE is in the video namespace, and the video namespace is in the engine namespace. So without using namespaces, you would declare a pointer to the class like this:
engine::video::ITexture *texture;
WITH using namespaces, your code would look like this:
using namespace engine;
using namespace video;...ITexture *texture;
Namespaces can save you a whole buncha typing time^^
Now, at this point, I'm going to create some global variables. In general, you should avoid global variables like the plague, but to keep this barebones application as simple as possible, this is the best thing to do (and if you handle them properly, global variables are fine in small applications). As you can see, these are all pointers, and all of them are NULL. We're only declaring them so that they're there when we need them.
engineDevice *device = NULL;
IVideoDriver *driver = NULL;
ISceneManager *smgr = NULL;
IGUIEnvironment *guienv = NULL;
Okay, at this point we will create 3 basic functions to make it easier to start simple applications: initScene(), createScene(), and killScene(). The initScene() function will be called once only, before anything happens. This would be typically where you would load textures, precalculate values, etc.. The createScene() function is called every time a frame is rendered to the screen. This would be where your logic/movement/etc code would go. The killScene() function is called only once, after the application has finished the rendering loop, and is ready to exit. This is where you would do your final cleanup so you don’t anything undealt with. Since this is a barebones application, all of these functions are empty, but they are all called at the appropriate places inside your entrypoint function.
void initScene() {
}void createScene() {
}void killScene() {
}
NOW we define our entrypoint function. Normally this would be main(), and even in earlier versions of the LTE GE, main() was used as well, but since the implementation of wireless capability into the LTE GE, the entrypoint has changed, since the LTE GE itself needed to use the main() entrypoint. Everything we need is here, we can even pass in commandline parameters, although why would you want that on the PSP? It's possible, but not needed for normal purposes.
int engineMain(unsigned int argc, void *argv) {
The first thing we should do inside our entrypoint is to setup our PSP to be ready to run the LTE GE properly. This call is the only thing we’ll be calling from our “common.h” file- everything else is handled in that file.
setupPSP();
Now we can define all those global variables so they aren’t NULL anymore. I do this here instead of in the init() function because this will never change from application to application, whereas the init() function is intended to be application specific.
The important part to look at is the createDevice() function call. The engineDevice pointer that’s returned from that is the most important thing you’ll need when using the LTE GE. EVERYTHING you need can be [in]directly retrieved from the engineDevice, as you can see from the following pointer definitions.
device = createDevice(NULL, true);
driver = device->getVideoDriver();
smgr = device->getSceneManager();
guienv = device->getGUIEnvironment();
After this, we can call our init() function to do any application specific initialization.
initScene();
This is our main game loop. Roughly 120% of games created run thru a loop similar to this. In this case, the loop is tailored specifically to the LTE GE, but games that don’t use the LTE GE still use a loop similar to this. Basically what this loop does is render to our PSP screen. Our loop runs so long as device->run() returns true, which it will until there is a fatal error, or we tell it to, specifically. Inside our loop, our IVideoDriver will begin it’s rendering code (basically, it initializes itself to create a fresh new screen of graphics), the ISceneManager will draw all the 3d objects it has data on, our application specific createScene() will perform any logic/movement/etc it needs to, the IGUIEnvironment will draw all the GUI Elements it has data on, and the IVideoDriver will finish up things and draw it all to the PSP’s screen. Very simple, and it does everything we need it to. This loop will almost never need to be changed unless you start getting into much more complex coding.
while(device->run()) {
driver->beginScene(true, true, SColor(255, 14, 4, 31));smgr->drawAll();
createScene();
guienv->drawAll();driver->endScene();
}
Our next line of code is the first thing that happens after device->run() returns false (either from an error, or because we told it to quit). This is where we clean up our code, finish anything we have pending, and close the application gracefully. Since when we're quitting, the PSP will typically be shutting down anyways, we don't -really- need to worry about making sure we clean up our RAM usage etc, but it's good coding practice to make sure you always do so anyways. This is the last of our 3 functions that were defined earlier on.
killScene();
The last thing we do now is tell the PSP that our application is done. This is likely the only low level PSP code you’ll see when you’re programming using the LTE GE, and you don’t really need to worry about exactly what it does; just know that it tells the PSP that our application is done and happy. We then return 0 (telling the PSP that all is well), and our application ends.
sceKernelSleepThreadCB();
return 0;
}
[FULL]
Our final barebones application now looks like this:
#include <engine.h>
#define PSP_ENABLE_DEBUG
#include "common.h"
using namespace engine;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
using namespace audio;
engineDevice *device = NULL;
IVideoDriver *driver = NULL;
ISceneManager *smgr = NULL;
IGUIEnvironment *guienv = NULL;
void initScene() {
}
void createScene() {
}
void killScene() {
}
int engineMain(unsigned int argc, void *argv) {
setupPSP();
device = createDevice(NULL, true);
driver = device->getVideoDriver();
smgr = device->getSceneManager();
guienv = device->getGUIEnvironment();
initScene();
while(device->run()) {
driver->beginScene(true, true, SColor(255, 14, 4, 31));
smgr->drawAll();
createScene();
guienv->drawAll();
driver->endScene();
}
killScene();
sceKernelSleepThreadCB();
return 0;
}
[CLSE]
This is our barebones application. When we run it, all we will get is a blue-ish background color with a simple windows-style cursor on screen, that we can move with our analog sticks. This is basically just an emulated mouse. If we had anything to interact the mouse with (such as GUI Elements…), we can “click” the mouse with the Cross (X) button.
Well, that should be everything you need to have just a basic application that doesn’t do anything. Doesn’t it feel good to expend all that effort to do nothing? I sure feel good about it ^-^v
Stay tuned for my next tutorial: Game States