A Small Game Tutorial

From QB64 Wiki
Revision as of 07:17, 22 April 2011 by imported>Clippy
Jump to navigation Jump to search

So I decided to make a tutorial of a space game, a sidescroller where you avoid obstacles and collect lives. All the graphics will be made internally so there are no need to download external files.


In this tutorial I will use a method that was common in the 90's for creating graphics, yes DATA, the image that will be created is easily seen by this method, so let's first create the graphics, I'll show you how to use the graphics later. For simplicity we'll make all the graphics 10*10 which will be sufficient for this kind of game.


SCREEN 13 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,015,015,015,015,015,000,000,000 DATA 000,015,015,015,015,007,007,015,000,000 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,015,015,015,015,015,015,015,015,015 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,000,015,015,015,015,015,015,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT SLEEP


Yes, that is a ship, I'm not the best at making graphics but I think we can all agree that this is a ship. 015 represent white, and 007 represent grey (the cockpit window). Now we can put the graphics into a image, we'll just name it "ship". After we have put the graphics into a image we'll erase the graphics using CLS to prepare it for making more graphics.


SCREEN 13 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,015,015,015,015,015,000,000,000 DATA 000,015,015,015,015,007,007,015,000,000 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,015,015,015,015,015,015,015,015,015 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,000,015,015,015,015,015,015,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 ' I use 1 TO 10 here because it is more intuitive (we don't normally count from 0 to 9), FOR x = 1 TO 10 ' in actuality you can PSET (0, 0), c as well which is the first valid pixel. READ c ' read the DATA and store it in c. PSET (x, y), c ' the values in the DATA are colors so we just PSET the colors. NEXT NEXT ' we use 11,11 since 0 to 10 is 11 pixels (in both width and height). ' / / ship = _NEWIMAGE(11, 11, 13) 'here we both create the new image and get the handle for it. ' \ ' we will operate in SCREEN 13 and thus create a SCREEN 13 compatible image. ' ' ' the source argument is empty and therefor assumes the current source (the screen). ' / ' destination v source _PUTIMAGE (1, 1)-(10, 10), , ship, (1, 1)-(10, 10) CLS ' Clear the screen \ ' destination argument (which is the ship imagehandle) ' 'We have now successfully stored the ship in the image, the handle for it is stored in the ship variable. 'We make the rest of the graphics exactly the same way (the only things that need to change are the graphics 'and the name of the variable to reference the image).


Now we have stored the graphics into the ship image. I have tried to explain each step by comments. If you are uncertain at how _PUTIMAGE works or how _NEWIMAGE works you can visit these pages:


_PUTIMAGE, _NEWIMAGE, Images


I will briefly explain more about the DATA and READ before we go on. After each READ the DATA pointer moves to the next element, so in this DATA grid we have 10 elements * 10 elements (which represents a box of graphics), each element is used as a color for the pixel in the box. The box is then drawn using PSET with the information in the DATA. (FOR x = 1 TO 10 and FOR y = 1 TO 10 represents the 10*10 box). If you try to read more than is available in the DATA you will get "Out of DATA" error. If you want to reset the DATA pointer you use the keyword RESTORE, this is not needed in our game as we only need to read the DATA once. It doesn't matter where the DATA is in the program, as long as it isn't in any SUB or FUNCTION, I have decided to put it on top of the routines that use it (as it seems more intuitive) but all DATA can as well be collected at the bottom of the program (as many programmers chose to do, as they want to limit the code in the main body).


Ok, since the code I've done so far might seem more complicated than it is because of all the comments I've made a version without comments here:


SCREEN 13 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,015,015,015,015,015,000,000,000 DATA 000,015,015,015,015,007,007,015,000,000 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,015,015,015,015,015,015,015,015,015 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,000,015,015,015,015,015,015,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT ship = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , ship, (1, 1)-(10, 10) CLS


Comments obviously have no impact on the program itself, I think it's cleaner without them but new concepts will always be commented anyway. Keep the comments in the first version if you are still uncertain how things are accomplished.


Now, let's create the other graphics as well, we want a obstacle that can destroy the ship and we want to collect lives. I've decided that progress will be to collect the numbers 1 to 9, when 9 is collected you have made the game and each number increases the speed at which the obstacles come at you (since this is such a simple game I don't want to complicate it by making more than one "level" but you are free to change it to your liking if you want to :), perhaps add some enemies? Ability to shoot things? Only your imagination sets the limit!


We only have to make the obstacle and the life graphics, 1 to 9 can be printed to the screen.


SCREEN 13 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,015,015,015,015,015,000,000,000 DATA 000,015,015,015,015,007,007,015,000,000 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,015,015,015,015,015,015,015,015,015 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,000,015,015,015,015,015,015,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT ship = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , ship, (1, 1)-(10, 10) SLEEP CLS DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,006,006,006,000,000,000,000 DATA 000,000,006,006,006,006,006,000,000,000 DATA 000,006,012,006,006,006,006,006,000,000 DATA 000,006,006,006,006,012,006,006,000,000 DATA 000,006,006,006,006,006,006,006,000,000 DATA 000,000,006,006,012,006,006,000,000,000 DATA 000,000,006,006,006,006,000,000,000,000 DATA 000,000,000,006,006,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT obstacle = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , obstacle, (1, 1)-(10, 10) SLEEP CLS DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,004,004,000,004,004,000,000,000 DATA 000,004,012,012,004,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,000,004,012,012,012,004,000,000,000 DATA 000,000,000,004,012,004,000,000,000,000 DATA 000,000,000,000,004,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT life = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , life, (1, 1)-(10, 10) SLEEP CLS

As you can see I've only changed the actual DATA information and the variables that reference the new images, everything else works the same way. Here I've used SLEEP to enable you to view the graphics before it is cleared by CLS, just press any key to see all three graphics one by one.


If you are unsatisfied with the graphics you can change them and view the changes by running the program again (that is what I did).


The graphics part of the game is done now, so now we need to put the graphics on the screen and add a keyboard routine, and the game mechanics (collision detection, and moving the objects).


We'll store the obstacle x,y and speed information in arrays, as well as the life x,y and speed information. We can then check against the arrays to see if the ship has hit anything. There can only be one number on the screen at one time so that doesn't need a array, but simple x, y and speed variables.


We're getting ahead of ourselves though (which is a good thing, cause having the future concept firmly in your mind will help you know what to do later). First of all let's just put the ship on the screen and control it using the keyboard (I will repost all the code everytime, so each example can be run on its own):


SCREEN 13 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,015,015,015,015,015,000,000,000 DATA 000,015,015,015,015,007,007,015,000,000 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,015,015,015,015,015,015,015,015,015 DATA 000,015,015,015,015,015,015,015,015,000 DATA 000,000,015,015,015,015,015,015,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT ship = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , ship, (1, 1)-(10, 10) CLS DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,000,006,006,006,000,000,000,000 DATA 000,000,006,006,006,006,006,000,000,000 DATA 000,006,012,006,006,006,006,006,000,000 DATA 000,006,006,006,006,012,006,006,000,000 DATA 000,006,006,006,006,006,006,006,000,000 DATA 000,000,006,006,012,006,006,000,000,000 DATA 000,000,006,006,006,006,000,000,000,000 DATA 000,000,000,006,006,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT obstacle = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , obstacle, (1, 1)-(10, 10) CLS DATA 000,000,000,000,000,000,000,000,000,000 DATA 000,000,004,004,000,004,004,000,000,000 DATA 000,004,012,012,004,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,004,012,012,012,012,012,004,000,000 DATA 000,000,004,012,012,012,004,000,000,000 DATA 000,000,000,004,012,004,000,000,000,000 DATA 000,000,000,000,004,000,000,000,000,000 DATA 000,000,000,000,000,000,000,000,000,000 FOR y = 1 TO 10 FOR x = 1 TO 10 READ c PSET (x, y), c NEXT NEXT life = _NEWIMAGE(11, 11, 13) _PUTIMAGE (1, 1)-(10, 10), , life, (1, 1)-(10, 10) CLS DO 'We'll put the entire game in this DO...LOOP. CLS 'clear the screen each loop _PUTIMAGE (shipx, shipy), ship IF _KEYDOWN(CVI(CHR$(0) + "H")) THEN shipy = shipy - 1 'UP IF _KEYDOWN(CVI(CHR$(0) + "P")) THEN shipy = shipy + 1 'DOWN IF _KEYDOWN(CVI(CHR$(0) + "K")) THEN shipx = shipx - 1 'LEFT IF _KEYDOWN(CVI(CHR$(0) + "M")) THEN shipx = shipx + 1 'RIGHT IF _KEYDOWN(27) THEN END 'ESC _DISPLAY 'display the contents drawn to the screen LOOP


We use _KEYDOWN for keyboard detection, it's good since you can detect many keys at once (to move diagonally for example). CHR$(0) + "H", "P", "K", "M" is the codes for UP, DOWN, LEFT and RIGHT presses on the keyboard, I convert it to a integer value using CVI.


In _PUTIMAGE we only had to use the x and y destination coordinates and the ship variable as the reference for the source image the rest is assumed (if you omit the destination image then it will be drawn to the current destination, which is the screen).


You'll notice that the ship moves just a tad too fast, we don't have anything that limits the speed so the loop is run at the maximum speed your computer can offer. Another issue is that the ship easily gets lost outside the screen border, as we don't have any detection for that yet. This can easily be solved though.


---Stay tuned for more updates on this tutorial---