DevMaster.net Forums
[[ Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us ]]

Go Back   DevMaster.net Forums > Site Discussions > Articles Discussion
User Name
Password
Register FAQ Members List Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
Old 05-13-2003, 11:35 AM   #1
baldurk
DevMaster Staff
 
baldurk's Avatar
 
Join Date: Jan 2003
Location: Mars
Posts: 1,140
Default

<h1><center>An introduction to games coding on UNIX like systems</center></h1>
<h2>Introduction</h2>
Well, I guess that I am the UNIX OS mod, so I'd better get the finger out and write an article. What better topic that game development on a UNIX like system. In This article I'm going to discuss the basic things you need to be able to do in your OS, and also provide a little code for you .
I also only have Linux to hand, so I can't guarantee that this will work in all *nix OSes. I've got a fair idea that it will though.
<h2>Simple commands</h2>
This first section will teach you all the things you need to be able to do to navigate your OS, and do stuff. Most of you will be able to skip this section, but it might be useful for newbies. I talk about these as console commands. You can use a file manager like konqueror or nautilus, but I
don't so I can't tell you. First, fire up a console emulator of your choice. It can be gterminal, konsole, or my favourite, Eterm. Or you could even do this in the real console, if you are a hardcore 1337 d00d. I'm going to provide a table with all the commands I'm going to explain. You can miss out
any you already know.
<table border=1><tr><td>Command Name</td>
<td>Summary</td>
<td>Examples</td></tr>
<tr><td align=center>ls</td><td align=justify>
A command which displays information about the file in a directory. e.g. <code>$ ls</code> will list the files in the current directory. Use the -l switch to get more information. You can also provide a directory or file as the last parameter to see that directory/file.

Certain characters have special meanings in this context. * means that any number of any character can go in there. e.g test* matches test1, test3, testing and test-oompa-loompa.
</td><td width=38%>
<code>$ ls
test1 test2 test3 testing test-oopma-loompa

$ ls -l
-rw-r--r-- 1 baldurk users 0 Apr 1 19:59 test1
..etc..

$ ls *-*

test-oompa-loompa
</code>
</td></tr><tr><td align=center>cd</td><td align=justify>
A simple command to Change Directory. Simply provide a directory in either relative or absolute terms, and it will change the directory. There are a few special cases, .. is the parent directory to the current. - is the last directory you were in and ~ or " " (nothing) is your home directory.
</td><td>
(in /home/baldurk)

<code>$ cd tmp</code>

(in /home/baldurk/tmp)

<code>$ cd /usr/src/linux</code>

(in /usr/src/linux)

<code>$ cd ..</code>

(in /usr/src)

<code>$ cd -</code>

(in /usr/src/linux)

<code>$ cd ~</code>

(in /home/baldurk)
</tr><tr><td align=center>gcc</td><td align=justify>
This is the GNU C/C++ Compiler, and is worthy of an article all on it's own (there's an idea ...) I'll discuss this seperately when we are compiling our code, but for now we'll leave it alone


</td><td></td></tr><tr><td align=center>make</td><td align=justify>
The make utility is one of the most useful programs that you will use. It is controlled by simple text files. It simplifies the build process immensely by allowing you to just type "make" to compile your project. Again, I'll explain this in more detail when we get to that stage. </td><td></td></tr><tr><td align=center>man</td><td align=justify>
Man is similar to info, but more widely used (AFAIK). It is a program that gives you access to a huge number of documents on all sorts of things. Programs, commands, the entire standard C library, most libraries. Anything you installed will probably have a manpage. It is very simple to do. All you do is type man, then the name of the page you want. The problem is if you have a system program and a C call with the same name. For ease of use, each type of manpage (system call, function call, program etc.) is assigned a different number, representing its type. You can put this as the first parameter to say "I only want the C call". The switch -a also produces all the manpages matching the one you want. When you are told/want to "look up a manpage", this is how you do it. NOTE: there is a special case. You cannot look up calls like ls, for they are part of your shell. bash, tcsh, sh, whatever. You need to look up the manpage for your shell for these commands.
Browsing in a manpage is pretty easy. Up and down scroll. If the lines are too long, left and rightscroll along them. Pageup and Pagedown, amazingly, go up and down one page. 'q' exits, and '/' intiatesa search. To search, press '/', then type in the search, or press up. It will then zoom to the result,or tell you if it didn't find any results. Press return if it didn't to return to normal mode.
</td><td>
<code>$ man printf</code>

This will probably not return the C call.

<code>$ man 3 printf</code>

This will.

<code>$ man -a printf</code>

This will bring up printf(1), then when you exit that, printf(3)
</td></tr><tr><td align=center>mkdir</td><td align=justify>
This command simply creates the directory(s) that you specify on the commandline
</td><td>
<code>$ mkdir ~/foobar</code>

Create a 'foobar' dir under your home directory

<code>$ ls ~
... foobar ...</code>
</td></tr></table>
<h2>Setting up the project environment</h2>
Whew! That was long, wasn't it? Well, now we're starting some actual coding so you'll be glad of that. First, create a directory with mkdir. I have a ~/Projects dir, for all coding, so you might want to use ~/Projects/SDLGLDemo or something. It's up to you. Next, cd to that dir and create the file Makefile in that dir. Open it up in your favourite editor.

<code>$ mkdir ~/Projects/SDLGLDemo
$ cd ~/Projects/SDLGLDemo
$ xemacs Makefile
</code>
My favourite editor is xemacs, in case you didn't guess. Now we get onto our first topic, Makefiles. (By the way, xemacs will automagically create the file if it isn't already there.)
<h2>Makefiles and the make utility</h2>
Make is a utility that parses certain files, called makefiles, in the current directory and performs the the actions within. For the most part they are similar to shell scripts, but they have a few great features.
Firstly, they can be made to do different things depending on the parameter. When you invoke make, you can provide different so-called 'targets', and make will automagically find the target, and run the commands there. This way you can have a target for making, one for installing, one for uninstalling etc. The first target listed in the file is the default target, which is run if no target is specified on the command line. Targets are specified like so:
<code>
SDLGLDemo:
# ... commands (# for a comment)

install:
# install commands

</code>
There are various commands which are normally included in makefiles: install, uninstall, clean. The first two should be self-explanatory, but clean is not so. It is simple however, meaning that all object files and results of compilation should be deleted. Note that because this is a very small project, install and uninstall aren't really necessary, and so won't be included.

I'll provide the makefile we'll be using here, then explain what it all means and does:


<code>
CC=g++
CCFLAGS=-Wall `sdl-config --cflags`
LDFLAGS=`sdl-config --libs` -L/usr/X11R6/lib -lGL -lGLU
OBJECTS=SDLGLDemo.o
OUTPUT=SDLGLDemo

$(OUTPUT): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $(OUTPUT)

SDLGLDemo.o: SDLGLDemo.cpp SDLGLDemo.h
$(CC) $(CCFLAGS) -c SDLGLDemo.cpp -o SDLGLDemo.o

clean:
rm -rf $(OBJECTS) $(OUTPUT)
</code>
OK, let's see. The first section is exactly the same as a shell script in syntax. It sets variables to various values so that when the project changes, only these must be changed. The syntax for setting a variable is <code>VARNAME=VALUE</code>. Variables in shell scripts traditionally are uppercase, but this is not required. There is one special case that I would like to explain, that is the `s around "sdl-config --libs" or "sdl-config --cflags". This means that when the variable is substituted into a command, the "sdl-config --libs" is executed, like it was a command of it's own, then the output of that command is placed in where the "sdl-config --libs" was. So if I typed in:
<code>
echo "I am: `whoami`"
</code>
The output would be "I am: baldurk", or whatever your current username is. So it is running the command "whoami" which outputs the current username, and then puts that in place of the `whoami` bit. See? It's very useful.
Getting back to the point, this means that on every build, on every machine, it runs the sdl-config script to find out which libraries to add etc, so they aren't hardcoded into the makefile and so it's more portable.
The first target is for whatever the output is (in this case SDLGLDemo). It depends on all the objects. This is another useful property of makefiles: dependencies. This means that the makefile will check on all the dependencies, and if any have changed it will rebuild the target, but if none have then it will say that the target is up to date. If the dependencies don't exist (as may be the case here) then it will search through the makefile to see if there is a target that exactly matches that dependency. If it finds that target, it will build that first before the target specified. So you can recurse through many dependencies. In this makefile, the dependency is just SDLGLDemo.o, so it builds the target SDLGLDemo, which brings us onto the next target .
This is the first and only (subtle book reference which I don't think any of you will get) of our "object" targets. Each cpp file is compiled to a .o (object) file, then they are all linked into the executable. This is useful when you have a project of 20 cpp files or more, and you are only changing one. you rebuild that object file, then link it in with the others. If you add cpp files to this project, simply add the object file to OBJECTS, and add an equivalent target to this one. The main OBJECT target will work out which of the objects it needs to rebuild, and rebuild them.

The final target is the clean target. It simply removes all the objects and the output file.

That's the makefile explained. You may not understand what exactly the commands are that the targets execute, so my next section should help you to understand .
<h2>gcc the compiler and g++, it's twin</h2>
gcc is the GNU C Compiler. It is used for compiling files, and it can also use ld the GNU linker to link.gcc can compile C and C++ files, which it automagically guesses from the extension. .cpp, .cc, .cp, .C, .cxx and .c++ are C++ files and .c are plain C files. However, gcc does not link with the standard C++ library by default, whereas g++ does. Therefore it is better, if coding in C++, to use g++. This is why we use g++. Note, you can change to gcc, by simply changing the CC variable to gcc . You may encounter problems, and you may need to change other things, so I recommend against it. Now to explain gcc's parameters:
<code>
-Wall = -W* are to turn on various warning options, -Wall is to turn on all warnings. Consult the gcc man page to see the various other
possible warnings.
-L... = -L is used to add a directory in which libraries will be searched for. So that when you link to a library, this directory
will be searched through.
-l... = -l is different to -L. Notice that one is uppercase, one is lower. -l (the lowercase) searches the library paths for a given library.
You should put the name of the library, excluding extensions and the prefixed "lib", immediately after the -l. i.e, -lSDL looks for libSDL.so.
-lGL looks for libGL.so, etc.
-c = -c produces compiled, non-linked code instead of linked code. This is how we produce .o object files.
-o ... = -o specifies what file the output from the command will be stored in. You should leave a gap this time between the -o and the file.
</code>
There are many more other options you may use (and I really mean that), but these are the main ones. You can consult the man page to see all of them.

That should explain gcc.
<h2>Coding</h2>
Now you can open up that SDLGLDemo.h file and start adding the code. I'll give it bit by bit, and explain each bit.
<code>
#ifndef SDLGLDEMO_H
#define SDLGLDEMO_H

#include <SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#endif
</code>
This is simple enough. Simply including the GL headers, the SDL header, the math lib, standard io/library and time. You may not be familiar with the #ifndef/#endif block. I'll explain now.
An #include statement is very simple. If a cpp file #includes a header, the compiler processes the header preprocessor commands, (commands starting with a #) and the resulting code (may not be the full file) is inserted in the cpp file instead of the #include file. That's all an #include does . Now, this presents a simple problem. What happens if two cpp files include the same header? Well, the code is processed and inserted in both. A copy. This is bad if the header declares any variables, because they will be declared in both cpp files. Ick! Multiple definition errors all over the place! The easy way to deal with this is use our one card - the compiler (or actually to be precise the pre-processor, but it's part of the compiler) pre-processes the header before inserting the result. So, if we insert special code, then perhaps we can prevent multiple copies of the same code.
Enter the #ifndef/#endif block. It's a simple idea, but it solves that complex error. First time the header is inserted, we check if SDLGLDEMO_H is not defined, and because it isn't, process the rest of the file. This includes the #define statement, which cunningly defines SDLGLDEMO_H so that next time the header is processed, SDLGLDEMO_H *is* defined, and so we don't do anything until the #endif which is at the end.
Shame it's not much use in this situation. Because we compile each cpp file seperately, each file starts, even though the previous one defined SDLGLDEMO_H, with SDLGLDEMO_H undefined. This makes the #ifndef/#endif largely superfluous as it only works when you compile multiple cpp files together. It's always good practice to use it though. To give an unlikely example, a.cpp includes a.h and b.h. b.h includes a.h, so a.h is included by a.cpp and b.h, in the same build. You won't get multiple definition errors then. However, it's also good practice not to declare variables in the header. That way if you compile cpp files seperately, there isn't a problem.
Now time for SDLGLDemo.cpp: (by the way I'm missing out all the comments in these code snippets)
<code>
#include "SDLGLDemo.h"

void draw(float time)
{
</code>
This is simple. Include the SDLGLDemo.h file which we talked about above. Then begin the OpenGL drawing function. The code for the OpenGL drawing I'm not going to explain, that's not really the point of this tutorial. Needless to say it's an interesting effect, and that code is an OK simple intro to OpenGL. You might want to learn the basics somewhere else though.
<code>
static int first = 0;
static float x = 100.0f*rand()/(float)RAND_MAX, y = 100.0f*rand()/(float)RAND_MAX;

if(first < 2)
{
glClear(GL_COLOR_BUFFER_BIT);first++;
}

glClear(GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glBegin(GL_QUADS);
glColor3f(fabs(sin(y)), fabs(cos(x)), fabs(sin(x)));
glVertex3f(-1.0f+cos(x), 1.0f-cos(x), -5.0f);
glVertex3f( 1.0f+sin(x), 1.0f-sin(y), -5.0f);
glVertex3f( 1.0f-cos(y), -1.0f+sin(x), -5.0f);
glVertex3f(-1.0f-sin(x), -1.0f+cos(y), -5.0f);
glEnd();

x -= rand()/(float)RAND_MAX * time * 0.002f;
y += rand()/(float)RAND_MAX * time * 0.004f;
}

</code>
NOTE: I changed the variables from a, b to x, y for the forums. They are a and b in the source.
That's the OpenGL function. Nothing fancy, just an effect to show that the window does work . Now for the other function, main():
<code>
int main()
{
srand(time(NULL));
SDL_Init(SDL_INIT_VIDEO);
SDL_WM_SetCaption("Introduction to SDL + GL", NULL);
const SDL_VideoInfo *VideoInfo;
</code>
This is pretty simple. We seed the random function with the current time to get unique random results. Then we call the SDL Initialisation function.
This function takes one parameter - which subsystem(s) to init. You can OR together different flags, e.g. SDL_INIT_VIDEO | SDL_INIT_AUDIO. If you want
all the subsystems, use SDL_INIT_EVERYTHING. Next we set the window title to "Introduction to SDL + GL". The Icon is the default. Finally we declare a
variable which will be filled with video information.
<code>
if(!(VideoInfo = SDL_GetVideoInfo()))
{
printf("SDL_GetVideoInfo() failed. SDL Error: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}

int Flags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;

if(VideoInfo->hw_available)
Flags |= SDL_HWSURFACE;
else
Flags |= SDL_SWSURFACE;

if(VideoInfo->blit_hw)
Flags |= SDL_HWACCEL;

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

SDL_Surface *Surface = SDL_SetVideoMode(800, 600, 32, Flags);

if(!Surface)
{
printf("SDL_SetVideoMode() Failed. SDL_Error: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
</code>
Okie-dokie. let's see what we have here. The first bit gets the Video Information, and if this fails, returns 1 (an error). It Quits, and posts an
error to the console. Next we declare a variable to hold the flags we want to create the window with. By default, we want a hardware pallete, OpenGL Doublebuffering and OpenGL. This allows us to draw on the window using OpenGL. Next we check for optional/possible flags. If hardware surfaces are available, we add them, otherwise we use software surfaces. If we can blit in the hardware, we enable that. Then we enable OpenGL Doublebuffering. Then we actually create the window, dimensions 800x600 @ 32bits with the flags we've got. If that has failed, we then again print an error, Quit SDL and return 1.
<code>
SDL_ShowCursor(0);

glViewport(0, 0, 800, 600);
glMatrixMode(GL_PROJECTION);
gluPerspective(45.0f, 4.0f/3.0f, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
</code>

This is easy. We hide the cursor, then set up the OpenGL viewport to cover the whole window. After that we create the projection matrix with near 0.1f, far 100.0f planes and a fixed aspect ratio of 4:3 and a FOV of 45.0f.
<code>
bool done = false;
float prev = 0.0f, curr = 0.0f;
SDL_Event Event;

while(!done)
{
SDL_PollEvent(&Event);

switch(Event.type)
{
case SDL_QUIT:
{
done = true;
break;
}
case SDL_KEYDOWN:
{
if(Event.key.keysym.sym == SDLK_ESCAPE)
done = true;
if(Event.key.keysym.sym == SDLK_F1)
SDL_WM_ToggleFullScreen(Surface);

break;
}
}

prev = curr; curr = SDL_GetTicks();
draw(curr-prev);
SDL_GL_SwapBuffers();
}

SDL_Quit();
}
</code>
Maybe this bit of code looks hard, but trust me it ain't. First we declare some variables. One to indicate when the app should exit, one to contain info about the event we're processing, and two for framerate-independant movement. Then we start a while loop which will exit when the app is closed. We get an event. If there are no events waiting, this function returns.
Next we switch the type of event we've got, and provide case statements for the ones we are going to use. If it is a QUIT message, the close button has been pressed, so we have to quit. We set done to be true, so that the while loop and therefore the app exits. If it is a KEYDOWN message, we see if it's a key we're interested in, then we do appropriate things. If it's escape, we act as if it's a QUIT message, and if it's F1, we toggle fullscreen. NOTE: if you're not on linux/X11, then this function call won't work. You'll have to close the window, then start it up again with the SDL_FULLSCREEN. That's all the keypress processing.
Once we've processed the events, we draw the next frame. To do the framerate independant movement, we save the current time as the previous, then we get the current number of milliseconds since the app started (with SDL_GetTicks()) then subtract the previous, which gives us the time since the previous frame, and then we pass that to the draw function. This provides framerate independant movement, so it'll look the same on all PCs.
The last thing we do in the while loop is swap the buffers, and after the loop has finished we Quit SDL.
<h2>Conclusion</h2>
I hope you've learnt something from that, whether all of it is useful or not I don't know. It's a quick *nix intro, SDL intro. It also provides a
cool effect that you can sell for $1000000 if you want!!

--baldurk

Download the source + Makefile <a href=http://devmaster.net/articles/linuxintro/SDLGLDemo.tar.gz>here</a>
___________________________________________
baldurk
He who knows not and knows that he knows not is ignorant. Teach him.
He who knows not and knows not that he knows not is a fool. Shun him.
baldurk is offline   Reply With Quote
Old 05-14-2003, 07:49 AM   #2
Noor
Senior Member
 
Join Date: Jan 2003
Location: ON, Canada
Posts: 524
Default

Thank you for the article...
___________________________________________
"What ever happened to happily ever after?"
Noor is offline   Reply With Quote
Old 05-16-2003, 12:59 AM   #3
Phaetos
Member
 
Join Date: Feb 2003
Location: Krefeld, Germany
Posts: 57
Default

Wow Baldurk! Great work!

This should get everybody started to program under UNIX OS'es.

May I suggest another topic? An Introduction into the tools automake,
autoheader and autoconf would be the last step needed to provide the
configure ; make ; make install - environment for own projects!

I am not familiar with these tools, so I would be interested in these, too.

Greetings
Stefan
___________________________________________
http://www.3dcoding.de
Phaetos is offline   Reply With Quote
Old 05-16-2003, 10:37 AM   #4
baldurk
DevMaster Staff
 
baldurk's Avatar
 
Join Date: Jan 2003
Location: Mars
Posts: 1,140
Default

I'm not actually familiar with those tools, so I'd need to do some research to provide a tut. However, if there is a demand that's worth doing. The only problem I have with the auto* tools are the complete mess that they make of Makefiles and the project generally. Mind you, it's worth knowing about so I guess I might . We'll see, I have other plans for my next article.
___________________________________________
baldurk
He who knows not and knows that he knows not is ignorant. Teach him.
He who knows not and knows not that he knows not is a fool. Shun him.
baldurk is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Forum Jump


All times are GMT -7. The time now is 04:49 AM.


Powered by vBulletin
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.