Game Logic For Beginners
Strictly speaking, this isn't a tutorial - it's more of a guide to point you in the right direction. It's aim is to get you started on that game project you are thinking about and it's aimed at new programmers in Dark Basic. An elementary knowledge of the basic DB commands is useful, but nothing you shouldn't be able to pick up by scrutiny of the demos that came with it - or spending half an hour or so reading the help files.
As it's talking about programming principles, it should be of use whether you are using DB Classic or DB Professional - or whether your project uses 2D or 3D for that matter.
So I won't be showing you any game code - or how to actually write a game like Pong or Space Invaders because there are already tutorials out there which do this. Instead I want to give you a bit of information on how to turn your game idea into programming code.
As you will see on these forums again and again, the advice to start small and simple is good advice. Think about how many years it takes you to learn to speak your own native language proficiently - normally many years. DB as a language is no different and you can't master it in a few days or weeks either.
If you are new to programming, then writing pong or hangman can be frustratingly difficult. How do you even start translating what you see in your mind to code in DB?
Starting in 2D sounds boring - especially when you've got that nice shiny new programming language which makes 3D soooooo easy. However, using 2D first will teach you an awful lot about the basic building blocks of programming and programming logic - without all the intricacies of 3D. When you have the basics, you simply switch 3D on and apply what you've learned.
Far too many new users jump into 3D straight away, get bogged down and quickly give up. Start with 2D and you reduce the chances of this happening to you...
Pong
"You're trying to run before you can walk! Start with something simple like Pong..."
Wise words indeed, and you'll see them on these forums regularly. But, if you are completely new to programming then where do you start? OK, you can cut and paste code from other people's programs, but the chances are you don't fully understand what that code does, so it's much more educational if you start from scratch with your own program - learning as you go.
So how does Pong work in coding terms?
Well, as with all projects, it's best to split the game into sub-sections with a section of program to handle each one. So, we need code to handle the following:
Player 1 up/down movement
Player 2 up/down movement
Computer control for player 2 if selected
Ball movement (including bouncing off walls)
Collision with the bat
Scoring
Score tracking
Screen updating
A menu screen
As with any program, variables are of utmost importance, so you need to know what different types there are and how to use them. (If you don't fully understand what variables are, there is a tutorial on the subject on my web site, along with lots of others - link in my sig at the bottom).
Essentially, in a game, anything that moves on the screen is positioned using variables so that they can be changed by your program - making the respective object move.
In 2D DB, the 'object' can be a Sprite or an Image pasted onto the screen. You could use simple images for the paddles and the ball, or use sprites. Whichever you use, the code for calculating their screen positions is the same.
The Ball
With only two dimensions (2D), we only need a variable for the X position on the screen (left/right) and one for the Y position (up/down). So, we could use the variables BallPosX and BallPosY to position and move the ball.
We also use two other variables for the movement (or speed) of the ball - one for the X direction and one for the Y. We'll call these BallDirX and BallDirY.
When we want to move the ball we simply add BallDirX to the ball's current BallPosX position and BallDirY to the ball's current BallPosY position. The larger BallDirX and BallDirY are, the faster the ball moves.
If BallDirX is a positive number then the ball will move from left to right and if negative it will move from right to left.
If BallDirY is a positive number then the ball will move down the screen and if negative it will move up.
So, whatever the current direction of travel, it can be reversed by using BallDirX = 0-BallDirX for the left/right direction and BallDirY = 0-BallDirY for the up/down direction. This will work regardless of the actual value of the variables, so if you decide to speed up the ball by increasing the value of BallDirX and BallDirY then this will still retain the current speed when the direction is reversed.
As our program alters the ball's X and Y position, we simply check before updating the screen to see if:
a) the ball has hit the wall at the top or bottom of the screen and reverse the Y direction
b) it has hit a paddle - in which case we reverse the ball's X direction
c) it got past a player's paddle and the other player gets another point
In DB, checking to see if something has happened is called testing for a 'condition' and is done at the simplest level with the If...Then statement. If we have the ball's X and Y screen position in variables we can use something like:
If BallPosY<5
BallPosY = 5
BallDirY = 0-BallDirY
Endif
This checks to see if the ball's Y position is at the top of the screen (it's Y position is 5 or less) and if it is, repositions it at position Y=5. As this must mean that the ball is moving up, then at the same time it reverses the Y direction so it will move down at the next screen position calculation. To the human eye this looks like the ball has bounced off the top wall. Notice that the X direction and speed is not altered.
We can use a similar method to find out if the ball has been missed by a paddle - for the leftmost paddle we simply check to see if the ball's X position has reached 0 or less. If it has, then it must have got past the paddle and points can be awarded. We don't reverse the X direction in this case - that's only done when a paddle is hit. Remember though that you have to condition test for the ball hitting the paddles at both sides of the screen, the walls at the top and bottom as well as going past the paddles at both sides...
The Paddles
The paddles at the left and right of the screen only move up and down, so we just need to check if the keys for up or down have been pressed and alter the Y variable for the paddle position. For example we could use the variables P1PaddleY and P2PaddleY. The X value can be a fixed value (a constant) as it never changes. As with the ball, you should use another variable for the rate of vertical movement so you can increase or decrease the paddle's speed as required.
For a two player game, you could designate the Q and A key for player one up/down and the Cursor Up/Down keys for player two. It's up to you.
Apart from a menu, screen information and any other embellishments you feel like adding (sound?), that's essentially it - apart from one other vital aspect...
Program Layout
How you lay out your program is vital to prevent it from degenerating into a tangled mess - known as 'spaghetti code'.
You could write a linear program in one continuous lump which starts at the beginning and ends at the end with lots of Goto's jumping all over the place. Very difficult to follow, debug and maintain, so avoid at all costs!
Instead, you should write procedural code with a main loop and lots of well-commented and named procedures (and/or functions) handling different tasks. That way, if you have a problem with the ball movement, then you only have to look in the Move_Ball procedure for example to find the error - rather than ploughing through that aforementioned tangled mess.
While the game is being played, your program goes continually round the main loop jumping out to do tasks using the Gosub command - only to return automatically afterwards to continue round the loop.
The Main Menu would also be a loop inside a procedure which is only exited when the game starts - at which point program control drops out to the main program loop. At the end of the game, program control passes back to the Main Menu procedure loop and waits to start the next game.
Choosing Exit from the main menu would end the program there and then without dropping out of the procedure back to the main loop.
So, your program layout might look something like this:
Gosub Setup
Gosub MainMenu
Rem Main Program Loop
Do
Gosub ReadKeys
Gosub Player1_Move
Gosub Player2_Move
Gosub Ball_Move
Gosub Collision_Check
Gosub Scores
Sync: Rem Update Screen
Loop
End
Rem ***** All Procedures Here *****
ReadKeys:
Rem Code For Reading Keyboard Input Here
Return
Player1_Move:
Rem Code For Updating Player 1 Paddle Position Here
Return
Player2_Move:
Rem Code For Updating Player 2 Paddle Position Here
Return
Ball_Move:
Rem Code For Updating Ball Position Here
Return
Collision_Check:
Rem Code To Check For Ball Hitting Bat Here
Return
Scores:
Rem Code For Updating And Displaying Scores Here
Return
Menu:
Rem Code For Displaying Menu Here
Repeat
If 'Play Game' Option Is Selected Then PlayGame = 1
If 'Exit Game' Option Is Selected Then End
Until PlayGame = 1
Return
Setup:
Rem Code For Initialising Program Variables Here
Return
OK, it looks complicated for a simple game of Pong and it is. However, use this layout now with the simple programs and the habit will reap dividends when you start writing the big programs...
Hangman
This is a game heavily based on manipulating string variables and string arrays. For a simple version, you could include lots of Data statements at the end of your program with all the words for the game in a long list. For a game with more longevity, you would be better off having all the words in an external text file. That way, you can add more words to the game just by adding more words to the text file - and not having to create another version of your program.
If you are wondering how to do this, it's quite straight forward...
All we need to do is DIMension a string array to hold enough words, open the text file to read, then use a Repeat...Until loop reading all the words into the string array until there are no more words left in the file. The code for this looks like this:
Dim WordArray$(5000)
NumberOfWords = 0
Open To Read 1,"WordList.txt"
Repeat
Inc NumberOfWords
READ STRING 1,WordArray$(NumberOfWords)
Until File End(1)
Close File 1
Notice that we are using 'Until File End(1)'. This is called an 'implied' condition and in this case it returns 1 (true) when there is no more data in the file to read in.
All conditions are boolean - the result from testing them is always either a 0 for false or 1 for true. You would therefore normally say:
Until File End(1) = 1
... but if you leave the '= 1" off the end then testing for true is implied (assumed).
Also notice the NumberOfWords variable. This counts the number of words read in from the file and allows us to add and remove words at will without having to alter the program.
For example, if our text file had 200 words in it we could have used 'For A=1 To 200' instead of Repeat...Until. The problem is that if we add more than 200 words to the text file the extra words wouldn't be read in and if we had less than 200 the program wouldn't work.
Using the above method, it doesn't matter how many words are in the file, they will all be read in and we can use the NumberOfWords variable in our program when we need to know how many words are available.
The Dim statement in the code above allows us to have up to 5000 words in our text file and to make life easier for yourself, I recommend that you make all the words in the text file (or data statements) completely upper case.
As for the game itself, having read all the words into a string array and selected one at random, next comes the clever bit...
The randomly selected word is stored in a normal string variable - let's call it SelectedWord$.
By using the Len() function we know how long this word is, so we can create another string variable of the same length but using hyphens (minus signs) instead of letters in it. It's this variable we display on the screen and we'll call it DisplayWord$.
As the player selects a letter from the alphabet, we simply check to see if it is present in SelectedWord$ (using Mid$() in a For...Next loop) and if any are, replace the respective hyphens in DisplayWord$ with the selected letter. When it's displayed on screen, the correctly chosen letters are seen.
We repeat this until DisplayWord$ = SelectedWord$ - at which point the word has been completed.
How about incorrectly chosen letters?
Well first, we still check DisplayWord$, but as the letter isn't present we don't alter it. Instead we add an extra bit to the gallows - keeping track of how many pieces have been added. We can have a variable called MaxAttempts and set it to say 10 at the start of the program. This means that the game ends when the player has had 10 incorrect guesses.
Another variable called AttemptsWrong is set to 0 (zero) and incremented each time the player tries an incorrect letter. In our program, if AttemptsWrong = MaxAttempts then we know the player has used up their goes and has lost.
When it comes to the gallows, there's more than one way to do it. The easiest would be to create a number of images (the same number you use for the MaxAttempts variable) in a paint program. Simply draw the gallows in the same order as you would have it appear in the game after each incorrect guess, saving the image as Image1, Image2, Image3 and so on as you go along.
Your program simply loads all the images in and pastes them at the same location on the screen using the AttemptsWrong variable. So, after the first wrong attempt, AttemptsWrong = 1 so using Paste Image AttemptsWrong, X,Y will put the first image on the screen at position X,Y. After the second wrong try, AttemptsWrong will equal 2 and the same line would paste image 2 over image 1 - effectively adding another piece to the gallows. And so on...
However, we don't want the player to waste turns selecting the same letter more than once. To prevent this, we can use another useful trick...
We create a string called Alphabet$ and set it to "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
When the player selects let's say the letter T then we loop through Alphabet$ looking for it with Mid$() in a For...Next loop like so:
LetterPresent = 0
For A = 1 To Len(Alphabet$)
If Mid$(Alphabet$,A) = Chosen Letter
LetterPresent = A
Endif
Next A
If when dropping out of this loop LetterPresent does not equal 0 then the chosen letter was present - and therefore that letter has not been chosen before. So using Left$() and Right$() we remove the letter from the string to leave us with:
"ABCDEFGHIJKLMNOPQRSUVWXYZ"
The next time the player chooses the letter T and the above loop is executed, the shorter length of Alphabet$ is accounted for and with the T missing, LetterPresent will equal 0 when the loop is exited - allowing you to display a 'Sorry - Letter Already Used' message.
So your program essentially selects a random word then loops around:
* Reading in the players letter
* Checking to see if the letter has been used already
* If it has not, then remove it and then check to see if it's in the random word
* If it is then alter the displayed version of the word
* Check to see if the displayed word is the same as the random word and end the game with a 'Player Wins' message
* If the letter is not in the random word then display the next image in the gallows set
* Check if all the users goes have been used up and if so end with a 'You Lose' message
Don't forget to use the program layout as shown above!
Well that's all for now. If anyone is interested, I can add something similar for card games - like dealing a pack of cards randomly (like they have been shuffled) in a way that you don't repeat any cards.
There are many more tutorials and example code snippets on TGPF - my game programming forums. Click on the link below - it's free to join and everyone's welcome!
Visit TGPF
TDK_Man