home *** CD-ROM | disk | FTP | other *** search
- <head>
- <title="...forever...">
- <font=monaco10.fnt>
- <font=newy36.fnt>
- <font=time24.fnt>
- <image=back.raw w=256 h=256 t=-1>
- <buf=9050>
- <bgcolor=-1>
- <background=0>
- <link_color=000>
- <module=console.mod>
- <pal=back.pal>
- colors:
- 251 - black
- </head>
- <body>
- <frame x=0 y=0 w=640 h=9050 b=-1 c=-1>
-
- -- - --- -- --------------------------------------------------------------------
- <f1><c000> Making 2d and 3d games
- on Atari ST and Falcon Part #1
- <f0>
- by Saulot July'2002
- ------------------------------------------------------------------ -- - --- ----
-
- Special hi to: Rafal Kawecki, Sqward, mr.Pink, Moonwalker, Grey and all the
- people at #atariscne.
-
- *WARNING* This is not for experienced coders(or wonderful childs), so if you are
- considering yourself as one of those don't be surpised that you know everything
- (Feeling lucky, punk?).
-
- for comments, corrections, greetings, money sending ;): saulot@wp.pl Ah, and I
- forgot. Copying for commercial reasons prohibited!
-
-
- 1. Introduction
- --------------------------------------------------------------------------------
- This doc is intended for total beginners, but I hope that everyone interested
- will find something. Sometime ago I noticed that it's very hard to obtain any
- good doc about games programming (I don't mention great doc made by Earx/Fun
- which is m68k asm oriented.) for Atari specific hardware. The information is
- scattered and it can be quite difficult to grasp for uninitiated (Lo! Grey, code
- for me a demo! Miracles are everywhere! Graphicians learn 68k assembler, coders
- are making music and gfx, diskmag editors write programming tutorials ;)))).).
- As I found myself sometime ago in similar situation I decided to make a doc that
- could help some people to make their journey easier. This doc was also inspired
- by one polish book about games programming which was, of course, PC
- targetted (Pascal+wintel assembler). I liked this book alot, it contained some
- good basic ideas which could be easly ported to Atari and C language (and loads
- of errors ha,ha!).
-
- The tools I've chosen for writing games are: Pure C(which is ANSI standard, has
- easiest help system in the world) and of course m68k assembly language. If you
- find the german language of Pure C help files quite difficult (as I am) you can
- get an english help files by elfsoft (search elfhelp on the web). Its shareware,
- revamped reference and it cost's a little -recommended. Pascal is too slow for
- our purposes, the C language does a job a lot better(it works nearer to
- hardware, his little brothers java and c++ are industry standards!). The
- assembly is essential, because with it we will write some super-ultra fast
- routines for extra speed. It isn't too hard to learn, it's maybe less readable
- than other high level languages. I hope that this book(!?) will help someone. Of
- course text will be accompanied with examples of programs with comments. I
- really want to hear some comments, be it total praise or "this is piece of
- shit". Don't be intimidated at first seeing BGI tutorial. This is mainly for
- introductory reasons and it will be one chapter only to show some of you the
- basics. The main target is to learn writing all the needed routines by yourself
- in m68k assembler or C and incorporate it into your projects. I really have the
- feeling that Atari's (especially STE & FALCON) possibilities in games weren't
- fully exploited. There is also a bunch of people which are still very dedicated
- to Atari computers and I have a hope that this doc will motivate someone to take
- effort and do something (I assure you that it is very nice when you are writing
- programs that work :)). So gfx man, coders, musicians unite!
- In these series of docs you will read:
- Introduction to programming, using Pure C and assembler, how all this work
- together, passing parameters in assembler and C, screen modes, sound hardware,
- blitter & DSP usage, basics of 2d(bitmaps, sprites etc.) and <link=g48.scr>3d</l> graphics. And a
- lot more.
-
- Something for totally uninitiated
- ---------------------------------
- If you haven't any contact with programming languages this is a good place to
- start. Let's compile our first program. So let's start!
- 1) Run PC.PRG
- 2) From the toolbar choose "File"->"Open .C". When Fileselector appears write
- 1STPROGGY.C. To the window write down these lines of code and save it:
-
- /* this how we write comments in C */
- /* here we put the header file for standard Input/Output functions */
- #include <stdio.h>
-
- void main(void) /* this is our main program function - mandatory */
- { /* beginning of main function */
-
- printf("Blah, blah, blah, bleurgh, blurp !!!\n");
-
- } /* end of main function */
-
- 3) Make yourself a projectfile(to show the compiler how to make our proggy).
- From the toolbar "File"->"Open .PRJ". When the Fileselector appears write
- 1STPROGGY.PRJ. In the window write down these lines and save it:
-
- 1STPROGGY.TOS ;this how our executable program will be called
- = ;equation mark is mandatory!
- PCSTART.O ;this is startup object file that does all the dirty job like
- ;allocating memory for launched program etc...
- 1STPROGGY.C ;our newly created .C sourcefile
- PCSTDLIB.LIB ;pre compiled standard I/O libraries
-
- Notice few things if you start to write your own project files. Firstly you
- specify the name of output file. Then always appears equation mark. After that
- you can optionally put the functions written in assembler with .S extension.
- Then there is startup code(PCSTART.O in our case, thera are other ones but they
- are uninteresting to our purposes). After the startup code you throw in the
- names of files with your C source code. And finally you specify the libraries
- your code uses. Moreover in the project file you can use a switches. What the
- hell is that? Well these are special directives given to the compiler(be it C or
- m68k one). You can specify them from the tool bar, but if you want to spread
- your source code and be sure that it will compile in proper way on other
- machines, then you should use them. You can recognize them by [] brackets and
- you can turn them on with - and the letter [-C] for example. To learn more about
- that read the documentation.
- 4) After this Project->Choose project and find the 1STPROGGY.PRJ. And then
- compile the program with Project->Make all (or simply press Alt+X). And the you
- can run it with Project->Make and run. If you received something like this:
-
- Blah, blah, blah, bleurgh, blurp !!!
-
- You should be happy that you have made the very first step in programming. If
- there is something wrong check everything you wrote in C every mark is
- essential! So watch out.
- Making these project files in Pure C seems to be awful thing for the first
- sight, but it comes handy the more time you will use it.
-
- 2. Graphics with BGI
- -------------------------------------------------------------------------------
- Our journey with programming of graphics we will start with using BGI graphics,
- which is very simple and quick in use. The funny thing I noticed in polish book
- mentioned earlier, that it has a whole big BGI chapter called "BGI and games".
- Man reads this chapter and reads and after that he is informed that BGI is
- worthless $&%*@ and has nothing with doing games ha,ha! Top stuff! The term BGI
- comes from "Borland Graphics Interface" and formerly it was created for creating
- simple gfx in DOS enviroment(awful!) with Turbo Pascal(hideous!). The Borland
- company created something about 70 graphic functions and procedures that let us
- plot some simple pixels, lines and putting horizontally and vertically the
- characters. Hopefully we can use them in Pure C and can learn some basics. We
- will not use BGI graphics in our games, but this chapter can give you good idea
- how things work in general.
- The method of "stick and carrot" is a good one, so if you are writing programs
- that work well from beginning, then you are motivated to learn more and try the
- harder stuff (so, i think). If you know BGI already and you think that you know
- everything then skip all of this BGI shit. (but you have to wait awhile I'm
- afraid...)
-
- To use BGI functions you must include two additional lines:
- 1) In your program the pre-compiler directive #include <graphics.h>
- 2) In your project file add PCBGILIB.LIB with other lib files.
-
- Structure of the screen
- -----------------------
- The screen in any graphics mode has a little different layout than this you know
- from the maths lessons. The main difference is with the Y-axis; in carthesian
- one the Y-axis is directed upward, but on our screen is directed downward. The
- other difference is that point (0,0) is situated always in the upper-left corner
- of the screen. Every pixel has it's own coordinates. So if we have the screen
- with resolution of 640x480. The first pixel on screen will have (0,0)
- coordinates and the last one(in the lower-right corner of the screen) will have
- (639,399) coordinates.
-
- Every graphics mode has it's own specific parameters. These are:
-
- 1. Screen resolution or how many pixels can be putted on screen horizontally and
- vertically.
- 2. Amount of colours, sometimes given in bits(which describes colour depth). For
- example gfx mode with 4 bit colour depth can give 16 different colours. Why? The
- highest number you can represent with four bits is 15. To this we add the number
- 0 and we receive 16 different colours. Everything about bits we will learn in
- second part.
-
- Initializing of Graph module and graphics mode
- ----------------------------------------------
- As I mentioned earlier to be able to use any of the BGI routines we have to tell
- to our compy that we want to use them. Firstly in the beginning we have to write
- "#include <graphics.h>" in our source code and write PCBGILIB.LIB in our *.prj
- file. Wodda fukk is dat? -you ask. Well, graphics.h is a header file that
- contains information about BGI data types, functions and values used and being
- returned by these functions. The C compiler uses this info as a reference to
- PCBGILIB.LIB file, which contains implementation of functions and data types
- listed in graphics.h file translated to machine language already. Without info
- provided in header file we couldn't use any of these functions at all. You will
- learn more about it when we start to write functions by ourselves.
- The situation is analogic with #include <stdio.h> and #include <ext.h>.
- Declarations of used header files in C source code and PCSTDLIB.LIB and
- PCEXTLIB.LIB in *.PRJ file. Better take look at example program 1.1(p_1_1.c and
- p_1_1.prj -it should be somewhere around...).
-
- Next part is a declaring of used functions:
-
- int main(void);
-
- Function main is a mandatory one -it's our program. This is not neccessary to
- declare this, but in the future it becomes very important. That line informs the
- C compiler that we will be using function main, that doesn't need any
- parameters("void" in the brackets) and returns variable of integer data type.
- *IMPORTANT* Notice the semicolon after the declaration, it is how the C compiler
- sees the difference between declaration of function and it's implementation.
- Ehm, data types? They are describing the range of values which our variable can
- take. Take look at this:
-
- --------------------------------------------------------
- Data type Range
- --------------------------------------------------------
- char -128 to 127
- unsigned char 0 to 255
- signed char -128 to 127
- int -32 768 to 32 767
- unsigned int 0 to 65 535
- signed int -32 768 to 32 767
- long -2 147 483 648 to 2 147 483 647
- float -3.4E+38 to 3.4E+38
- double -1.7E+308 to 1.7E+308
-
- Treat this as a very rough information which doesn't include all the data types
- to have general idea.
-
- After a declaration we must specify our main function. We are doing it like
- this:
-
- int main(void)
- {
- return(0);
- }
-
- It is the shortest runnable C code. The implementation is similar to declaration
- part, but we are throwing out the semicolon and throw in {} brackets. The
- brackets tell where the function starts and where ends(we have to put our code
- somewhere!). As we declared that function main returns integer value, we are
- writing return(0) which returns 0 value, which means program termination. If you
- will receive negative numbers it means that some kind of error occurred. Inside
- the program we need variables to store some info etc.. We need to declare them
- first:
- int driver,mode,err=0;
-
- In this line we declared three variables of integer type called driver,mode and
- err. We simultanously initialized them with value of 0. Everything in one line!
- We could also write this like this:
-
- int driver=0;
- int mode=0;
- int err=0;
-
- Or like this:
-
- int driver;
- int mode;
- int err;
-
- driver=0;
- err=0;
- mode=0;
-
- Or in another way:
-
- int driver,mode,err;
- driver=0;
- err=0;
- mode=0;
- ********** IT'S ALL THE SAME !!!!!!! *********
-
- !!!!! Local and Global variables !!!!!!!!!!!!
- The place where you declare your variables is also important. The variables that
- you declared are visible(and you can use them) only in function where you have
- declared them, they vanish after function termination and aren't visible outside
- of this function. This sort of variables is called "local variable". It's wise
- to initialize local variables inside of our functions with some sort of value,
- because after program start they contain "trash or another garbage". So for your
- own good, initialize them to avoid future nerve wrecking bug hunting. The
- variables driver,mode and err are local variables in function main() and they
- vanish after main() termination.
- But sometimes we need variables that need to be accessed by any function. This
- variables are called "global" and are visible everywhere and can be
- accessed/changed by every function in your program. They are declared in normal
- way, outside of function main() and needn't to be initialized, because compiler
- is zeroing them at each program start. But don't abuse them too much, because
- they are stealing memory which cannot be retrieved, and could mess alot if too
- many functions will manipulate them behind your back. Watch out!
-
- But let's back to our source code. To initialize graphics mode we use initgraph
- function, which in header file looks like this:
- void initgraph( int *graphdriver, int *graphmode, char *pathtodriver );
-
- The function initgraph doesn't return any values (void before functions name)
- and uses several parameters. Damned! They look weird... I think I have to
- stop... Wot's dat? The "*" after the data type declaration means that this is a
- pointer to specified data type(pointer as name tells itself points to specific
- place in memory where is our variable, it is simply speaking address of cell in
- memory where our variable is hiding). Don't be fooled the "*" is also
- multiplication operator used by C!
-
- So now we must specify the graphics driver and gfx mode. We will use VDI for all
- the stuff. We don't need this PC bullshit.
- driver = VDI;
- mode = VDIMODE;
- initgraph(&driver, &mode, "");
- err = graphresult();
- if(err) {
- puts(grapherrormsg(err));
- return(err);
- }
- We declare that we want to use VDI so we assign to driver variable VDI(which has
- predefined value 256. Predefining values can be handy to make your programs more
- readable. In this case VDI is exchanged by compiler with 256 and VDI with 0).
- After that we are launching initgraph with proper parameters. But what are these
- funky "&" before driver and mode variable? And what for are those two
- apostrophes?
- The first thing means that you are passing adress in memory of your variable,
- not it's value.(more about it later)
- The blank string is provided for compatibility purposes(BGI were ported from
- PC's). It should contain path to the file of the graphics driver. As we don't
- need them on Atari we are leaving it blank.
- Next thing we do is checking if any error occurs. The function graphresult()
- does all the job for us. It returns negative number if there is something wrong.
- We need to save somewhere this info and we put it in err variable. Next we check
- if err variable is smaller than zero. If yes we print out the error message with
- puts function. Notice that the function grapherrormsg() was used as a parameter.
- It works like this: firstly grapherrormsg() is called with err as its parameter.
- Grapherrormsg returns pointer to the string which contains our desired message.
- After printing the message, the err value is passed to our main()
- function(return(err)) and our program terminates.
-
- But if everything is well initialized. We are setting textstyle, move our
- invisible cursor in the middle of the screen(getmaxx() and getmaxy() are
- returning maximal x and y resolution of the screen, dividing these values by two
- we receive approximate center of the screen)
- Then we print out the message. We wait for keypress, we are closing graphic mode
- with closegraph() and we are passing the value returned by graphresult() , which
- should be 0, to function main(). That's all.
- And now the hint. You know why the Pure C help system is easiest in the world.
- Then mark a clue word(or something like that) and press HELP key. It's
- addictive, fun, quick :). Play with it. But remember this help system isn't
- fully idiot-proof ;).
-
- PIXELS (P_1_5.c)
- ****************
- As you certainly know the screen images are made with these funny, small pixels.
- So let's throw some pixels on the screen. We can do that with function putpixel:
- void putpixel(int x,int y,int colour);
-
- x and y are the parameters of our pixel and colour is describing the colour of
- our pixel. We have 16 colours in our disposal (from 0 to 15). These are two
- possibilities of launching putpixel function:
- putpixel(100,100,RED);
- putpixel(100,100,4);
-
- The results are the same, but in first case we use predefined constant as a
- colour and in second case we simply pass the numeric value of that colour. It
- works exactly like VDI and VDIMODE in previous program(in 1.1 ha, ha! It looks
- like a big hole between!).
- Now we take a loop, erm a look at using "for" loops. The "for" loop repeats one
- instruction or group of instructions specified number of times. When we want to
- put several instructions in "for" loop we must put them in the brackets {}.
-
- for (counter=0;counter<100;counter++)
- printf("Programming sucks!!!!");
- printf("I should take a vacation from this...");
-
- This code will result in printing out to the screen 100 times sentence
- "Programming sucks!!!!" and one time "I should take a vacation from this...".
- counter++ is the equal to counter=counter+1 .
-
- for (counter=0;counter<100;counter++)
- {
- printf("Programming sucks!!!!");
- printf("I should take a vacation from this...");
- }
-
- But in above case we receive each sentence printed 100 times. Easy!
-
- The example program P_1_5.c prints out the message, puts something 6000 pixels
- in random coordinations of the screen, in random colours, waits for a keypress
- and quits. Function srand() is used to initialize the seed of random number
- generator, so we will receive different results on screen with each program
- start. And function random(x) which generates the pseudo random number from 0 to
- x-1. That's all folks!
-
- LINES (P_1_6.c)
- ***************
- For drawing lines we use line function:
- void line(int x1,int y1,int x2,int y2);
-
- Parameters x1,y1 are the coordinates of line's starting point and x2,y2 are the
- coordinates of it's end. To change the colour of the line we use the separate
- function called setcolor:
- void setcolor(int colour);
- The value colour can take range from 0 to 15 as in putpixel.
-
- Take look at P_1_6.c program. It's similar to the previous one. This one draws
- 3000 lines in random coordinates. After that waits for a keypress. No surprises
- this time :/.
-
- Functions (P_1_7.c and P_1_8.c)
- *******************************
- It's up to introduce functions to our programs. For what I need these functions
- ?! Take look at program 1.7 (p_1_7.c). Piece of cake, hum? During program
- execution you are forced to write down two times following lines:
-
- printf("Press any key to proceed\n");
- getch();
-
- So lets transform it into function, which we call press_key.(Look now to
- P_1_8.c)
-
- As you certainly noticed our new function we must:
- 1) declare that we want to use function by writing if it will return any
- parameters, how it will be called and if it will need any parameters *before*
- our main() block. As you can see our function press_key() doesn't need any
- parameters and returns no values- it is indicated by statement void both in
- place for expected parameters and in place before the name of function, which
- specifies data type of function's returned value. And don't forget about
- semicolon at the end!
- 2) Writedown the body of the function after declaration and before main() block.
- You must specify everything like in declaration(don't write the semicolon this
- time!). In {} brackets you can put the desired code.
-
- Now, remember those nasty PC***LIB.LIB files? So now you will be shocked. The
- header files contain lines like in our declaration part(they inform about
- functions we want to use) and these PC***LIB.LIB contain already compiled
- (naked)bodies of these functions! (uh, oh give me a break!)
-
- It's a very good moment to talk about giving names in C for your variables,
- functions etc. You can use upper case and lower case. Lower case and upper case
- characters are treated as competely different so if you will declare int
- variables Defibrylator and deFibrylator, then they will be treated like two
- different ones. The same situation is with function names.
-
- Functions and variables *should* be written in lower case (this is something
- like custom) and can have up to 32 characters in total. So it's good idea to
- give meaningful names.
- For example:
- int a; /* Wots dat? Wot for it iz? */
-
- float namethatstellseverything; /* *VERY* unreadable */
-
- double number_of_saulots_braincells=-0.0000000001;
- /*Nice, now it's very descriptive. And even readable. Use underlines a lot not
- spaces!*/
-
- If it comes about predefined constants they are all in UPPER CASE. And for most
- important /* comment */ your sources well. Imagine yourself a situation when you
- were testing air bags in your new car. And these air bags didn't worked, you had
- a car crash and you lost all your memory. How can you read your programs after
- that? If you have commented them well, there will be no problem at all, if not,
- sorry... Keep up this trends and you will get far!
-
- I also changed the declaration of main(). It doesn't return any values, so I put
- another void (void is cool word! Very SF or what? :] ). And we needn't to write
- this stupid return(0). Main can have also parameters. Do you remember those
- freaky TTP or GTP proggies? All the nasty things you write in commandline are
- passed to main() function.
-
- But we drifted off from functions subject. Functions are simply speaking the
- small programs or consider them as a bricks from which you build your
- complicated program(game). In the main block we used press_key() function only
- two times. It was really worth the effort? Imagine yourself a situation when you
- have to wait for a keypress in your program for hundred times. What you will
- choose - writing hundred times two lines of code or hundred times press_key()?
- It is maybe a stupid example. The point is that you can create your functions
- once, gather them in libraries and reuse them in future. You don't waste your
- time by rewriting everything from the beginning, you can reuse created functions
- in other programs by simply including them, your main program is more readable,
- it's smaller -you see? Only good things! No drawbacks!
-
- Functions with parameters(P_1_9.C and P_1_10.C)
- ***********************************************
- Imagine yourself that this short proggy(P_1_9.C) is a game in which the player
- fights with all the biggest asshloes in the imaginary world (Gill Bates and his
- evil brothers for example). On the beginning we see our old function press_key()
- (Don't you ever forget this brackets on the end, because compiler will think
- that you have putted a variable or something which you haven't declared at all
- or simply wrongly declared!).
- After that we have asshole_attack(). The task of this function is to throw in
- 1000 assholes to exterminate by our player. Program after start presents the
- player with legion of 3000 assholes in three series. It looks that everythings
- is ok, but you should, as a programmer, try to better optimize your(or not your)
- existing routines if the need arises. The need is a mother of invention! Maybe
- you want to make the program faster, make it smaller or reduce memory it uses.
- Speed is most important thing in games, especially on Atari, so it needs some
- extra work.
- Think for a minute how to make asshole_attack() better? We can throw toward
- player 1000 assholes, but our player could become bored if he knew that he has
- to fight 1000 assholes once again all the time. Conclusion: we need the
- parameter to manipulate the amount of assholes throwed into battle. Take look at
- program 1.9 (P_1_9.C).
-
- To the procedure asshole_attack() we added the parameter how_many_assholes of
- unsigned int type. It can take values from 0 to 65535, so it can contain quite
- impressive number of assholes. If we used for example unsigned char data type we
- could have 255 assholes at most, which certainly will reduce the number of
- annihilated assholes in the world. When you will write your own functions with
- parameters you should turn your attention to how big numbers will be passed to
- them and which data type is best suited. If you don't remember all the ranges
- then look in Pure C help !!!
-
- Functions returning values
- **************************
- And where are those procedures? C is shit because it cannot use procedures!
- Functions, functions, functions... To hell with them! Well, not really. The
- functions in C have all the functionality of procedures plus something more.
- It's like procedure+function in Turbo Pascal combined in one!
-
- So be calm, let's use function as a classical function returning values. We want
- to write the procedure add_two_numbers() which will receive two parameters in
- shape of two int type numbers(why they are so numb?;)). As usual everything in
- Program 1.11(P_1_11.C).
- I think that's there is no surprises there. We declare int type variable called
- result and initialize it with zero. We pass two numbers into function
- add_two_numbers() and we assign the returned value to the result variable.(we
- have to save the returned value somewhere). After that we print out the message
- on the screen. Notice that add_two_numbers() will be launched first, it will
- return value on the left side of equation mark. This is how it works. It's not
- incidental -each operator, data type or something else, has it's own priority
- and will be behaving in another way (C compiler reads code sometimes from right
- to left or left to right). This is somewhat complicated topic. Take look at
- operator precedence in C language section of Pure C help file. And take a look
- at books mentioned in literture reference at the end(is there any?) for
- additional information. That was a function, THATS ALL! No fireworks at all.
-
- About functions I have told *almost* everything. I haven't told about
- possibility of incorporating local, for particular function, variables and two
- variants of passing parameters. To declare local variables we simply declare
- them(like in main() function):
-
- void dragons_den(void)
- {
- int dragon,knight;
-
- /*some dirty things made by function*/
- }
- The variables dragon and knight can be used inside the function, but they will
- be invisible outside this function. As I mentioned earlier you must initialize
- the local variables to avoid errors. Local variables are not initialized at
- start of the function and contain garbage, rubbish and other random things. Be
- warned!
-
- Second thing are two variants of passing parameters to function. If we have the
- variable amount_of_potatoes_to_eat of int type and value equal to 0, and we pass
- it to function like this:
-
- void add_5000(int amount)
- {
- amount=amount+5000;
- }
-
- In this way:
- add_5000(amount_of_potatoes_to_eat);
-
- Then value of amount_potatoes_to_eat will be still 0! Why? The function uses the
- copy of the value of amount_of_potatoes and called function does all the
- operation on the copy of the variable without altering it. If we want to force
- procedure to make changes in original data we must change function like this:
-
- void add_5000(int * amount)
- {
- amount=amount+5000;
- }
-
- And call it in this way:
- add_5000(&amount_of_potatoes_to_eat);
-
- We must tell the function that it has to expect a pointer to integer
- variable(the place in memory where residents our stored variable). We have also
- tell to the called function that we are passing the address (the place in memory
- of where is our variable) to amount_of_potatoes_to_eat so we add "&" mark at the
- beginning of our variable name. Now each variable passed to function will be
- increased by 5000.
-
-
- <link=art48b.scr>Go to NEXT PART</l>
- </frame>
- </body>
-