Simple PONG-game tutorial for Beginner Coders

The place for any Hollywood tutorials
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

This is Hollywood Tutorial for people who dont know programming in advance.

Instead of traditional way of explaining, this guide will aim at teaching you through simple game examples.

In this first tutorial, we will go through some basics, and make a simple PONG game.

I wont explain you in detail how to make and run your Hollywood programs, I suppose you can figure that much out yourself.

Simply make program with text editor, and compile it with hollywood and run.

There are also text editors that support Hollywood in themselves, making programming much easier, as they will give you tips on the code syntaxes, as well as you can simply run the code by clicking "run" button, instead of first compiling the code into exe and then running it.


Couple of things about my explanations.

1. Sometimes I will use numbers at beginning of codelines to make it easier to explain the code. However, if you try out these example codes yourself, never use the line numbers, or code wont work.

2. If line starts with "->", it means this is what will be printed to the screen when program is executed.


Lets start:

Code: Select all

print("Hello World")
-> Hello World



There is only one command in this code: "Print" Most Hollywood commands have "()" at end of them, ie. "Print()"

This "()" - part is the part, where the coder can and usually even needs to put stuff for that commands purpose, in this case the text "Hello World".


If you try out this code for yourself, you will notice there is a one problem. You probably wont see that text at all, since right after Hollywood prints that "Hello World"-text to the screen, program ends and hence closes itself.

If you want to see and experiment with this, try following:

Code: Select all

Print("Hello World")
REPEAT
FOREVER
If you now execute the program, this "Hello World" - message will stay on your screen until you hit "CONTROL" + "C" - buttons together, this will tell Hollywood to quit the program.

I will at later point explain you the REPEAT - FOREVER part, although it is somewhat self explanatory.


As you might have noticed, I used '"'-commas on edges of the text that i wanted to print to screen. If you remove these '"'-commas, you will notice it stops working, but if you remove the space between these two words, you will notice something even stranger to happen:

Code: Select all

Print(HelloWorld)
-> 0

This is, because now print() function is trying to print out variable instead of a text. But what exactly are variables?

You probably still remember from school those math questions in type of "2 + X = 3" determine the X.

That "X" is almost a variable.

If we rather change this math question to "2 + X = ?" then we have a variable.

Depending upon what the X is, will also decide, what the result will be, and X can be any number.


Idea with Variables is, that hey can store info. This info can be either numbers or letters.

For example, variable X could store number 4. Or variable X could store someones name ie. "Andreas Falkenhahn".

The practical use of Variables is many. But as simple example, lets suppose Company uses to send their letters with their CEOS full name in them:

"Greeting from Amiga Inc. This is Bill McEwen...", "Amiga Inc. CEO Bill McEwen is happy to..." and so on. There be multiple letters with CEOs name written on it. But what if the CEO Changes? It would mean, that into all different hundred letters, CEOs name would need to be changed.

And here comes variables. What if, instead of writing CEOs full name, it would be using a VARIABLE:

"Greetins from Amiga Inc. This is X...", "Amiga Inc. CEO X is happy to...".

Now when the program would be run, you would first tell it what is X, in this case "X = "Bill McEwen". When Program is now run, everytime it would encounter the X, it would change it into "Bill McEwen". And if it now happens that CEO changes, all you would need to do, would be to change the first line of code to "X = "Trevor Dickinson", and instead of doing hundred changes, you would do only one.


Lets see the usage of variables in practice:

Code: Select all

X = "Hello World"
print(X)
-> Hello World

as you can see, this works exactly like the previous program did, but instead of telling print command to print "Hello World", we are now telling it to print out what ever variable X is containing.


Variable names can be anything, as long as some specific characters are not used, and it doesnt collide with another Hollywood command or reserved words.

Therefore, variables name could be for example: "hillbillybilly", "ceo", "agentx"... All of these are completely acceptable, and can have anything inside them:

Code: Select all

hillbillybilly = 1
ceo = "Bill McEwen"
agentx = "James Bond"

print(hillbillybilly)
print(ceo)
print(agentx)
-> 1
-> Bill McEwen
-> James Bond


however, when using variables, you should aim at naming them soemthing descriptive, as example:

Code: Select all

text = "Hello World"
print(text)
-> Hello World

using varibale name like "text" for storing the text to be printed out, makes it much easier to understand what is happening in your code.


Contents of variable can naturally be changed during the programs execution, otherwise these variables wouldnt be much use.

Code: Select all

text = "Hello World"
print(text)
text = "Anotehr text"
print(text)
text = 123
print(text)
-> Hello World
-> Another Text
-> 123


Notice also, that although I have so far used to print only one variable at a time with Print command, i can as well print out several:

Code: Select all

text1 = "Hello"
text2 = "World"

print(text1..text2)
-> HelloWorld

By simply using ".." two dots between two variables, I can add as many of them as I like. Notice however, that there is now one mistake in this code, for what I really wanted, was to get "Hello World", but now it was "HelloWorld", without the space. This is naturally, because I only told print command to dirst print contents of "text1" and right after, the contents of "text2", hence there is no space.

But this can be fixed, because I can also add letters myself without always using variables:

Code: Select all

1: text1 = "Hello"
2: text2 = "World"
3:
4: print(text1..text2)
5: print(text1.."I forgot to add space"..text2)
6: print(text1.." Now i remembered space "..text2)
7: print(text1.." "..text2)
->HelloWorld
->HelloI forgot to add spaceWorld
->Hello Now i remembered space World
->Hello World


As you can see, in line 5, I use my own writing to but between those two variables, but since i havent add any space, it writes it right after them.
in line 6 i however add those spaces, and in line 7, i put nothing but the space, and it finally comes out the way i intended it to come in the first place.


When variables have numbers inside them, they can be used to make mathematic calculations:

Code: Select all

mysalary = 2000
wifessalary = 1000

totalsalary = mysalary + wifessalary

print("Me and my wifes total income is "..totalsalary.." per month")
->Me and my wifes total income is 3000 per month


As you probably have already figured out, code is executed line by line basis, this means, that if you are referring to something, it must already exist before that line of code.

To take the previous example, you might think it would be nice to first make the actual action line, and then put all the variables to the end of the code so they would be easy to find:

Code: Select all

1: totalsalary = mysalary + wifessalary
2: print("Total Salary: "..totalsalary)
3: 
4: mysalary = 2000
5: wifessalary = 1000
-> 0

While this is clear for a human what should be happening in this small piece of code, computer doesnt get it. When computer executes line 1 to determine value for variable "totalsalary", it doesnt understand to go and look for the variables values from lines 4 and 5, but instead, simply makes the calculations based upon "mysalary" and "wifessalary"s current values.
As they havent been initialised with any value yet, their value is therefore 0. Hence, totalsalary = 0 + 0 which in total is 0.


This is also the reason why print(helloworld) resulted in 0, as after removing the commas around the "helloworld", print command thought it was suppsoed to print what is inside variable called "helloworld" and as that variable hadnt been initialized, the value was 0.


You can think code execution to be like following driving instructions. If instructions are:
1. Turn left, 2. Turn Right, 3. Drive straight forward - you cant expect to get to the right place, if you instead of first turning to left, decide that you rather start by turning right and only after that to the left.

This is same with coding. If you havent already earlier told some value to the variable, you cant expect computer to figure out that it is supposed to get that value from later point of the code, especially when considered that same variable might have different values at different points of code.


So far our program flow have been pretty straight forward. Execute line 1, then 2, then 3... after all the lines are executed, then end the program.

But that isnt very useful and the real use of variables isnt just to be some copy-paste stores, but for them to store different states of different things which will be used to dynamically create things to happen.

If you wonder what is difference between dynamic and static. The static is something that always stays the same. As example, our first
Print("Hello World")

This will always result in exactly same result.

However, dynamic version would be the:

Code: Select all

text="Hello World"
Print(text)
This "print(text)" is dynamic version, since depending upon what is inside the "text" - variable, depends what comes to screen.


Similarly any game code for example is always dynamic by nature, since depending upon players actions would for example depend where the player is drawn to screen.


The basic, most common way to affect things, is by using "IF" statement.

At simplest, "IF" is followed by "THEN" statement.

as example:

Code: Select all

1: A=1
2: IF A=1 THEN text="Hello World"
3: IF A=2 THEN text="Bye World"
4: print(text)
-> Hello World

In this program, we in first line decide that variable A contains number value of 1.
second line check if A equals 1 (which it is) and if it is, then makes content of variable "text" have "Hello World" inside it.
third line does similar check to second lines, but checks if A equals 2 (which is doesnt), and if it does, it would make content of variable "text" have "Bye World" inside it.
fourth line is simply printing out what ever is inside the variable "text", which in this case is "Hello World".

However, if you change first lines A=1 instead to A=2, you will get:
-> Bye World

And if you would change A into any other number, it would result in:
-> 0

since when "text" is not initialized its value is 0, and "text" contains data only if line 2 or line 3 is true (A equals 1 or 2), in other cases, lines 2 and 3 are ignored, in which case "text" stays uninitialized. and hence results in "0".

IF - THEN is the simplest form of IF statement, but many times it becomes necessary to execute multiple commands instead of just one, in which case you would need to write:

Code: Select all

IF A=1 THEN text = "Hello World"
IF A=1 THEN B=3
IF A=1 THEN print(text)
and this would be exhausting, but to avoid doing this, you can instead do:

Code: Select all

IF A=1
   text="Hello World"
   B=3
   print(text)
ENDIF

This works so, that when you state the "IF A=1" then if A equals 1, then all the lines after this line will be executed, until comes the line that says "ENDIF"

Since in the first "IF" example, there were two different IF - THEN statements, and they both had to do with variable "A"s statement, there is another way to do it:

IF - ELSEIF - ELSE statement.

This works the following way:

Code: Select all

1:  A=2
2: 
3:  IF A=1
4:     text="Hello World"
5:  ELSEIF A=2
6:     text="Bye World"
7:  ELSEIF A=3
8:     text="Still World"
9:  ELSE
10:    text="A:s value should be between 1 and 3"
11: ENDIF
12: 
13: print(text)
-> Bye World

By changing value of A, you can get all the 4 different texts printed out.

This system works following:

in line 3 it is first checking the first statement, wether it is true or not. If it is true, then it goes to line 4 and does the stuff, in this case, assign "Hello World" to variable "text". After this it would go to line 5, and see there is another elseif statment.
since IF statement already was true, it would in this case work same as "ENDIF" and instead of executing further, jump directly to line 11, which is ENDIF, and continue executing from that point on to line 12 (empty) and 13 (print).

If line 3s statement is not true, then it next checks statamenent at line 5, if that is true, execute line 6 and from line 7 jump to ENDIF again.

If line 5s statement is not true either, then jump to line 7.

If line 7s statement is not true either, then finally jump to line 9, and since it tells "ELSE", that means that in case none of the previous IF - ELSEIF statements were true, then execute the lines between ELSE and ENDIF.


First of all, I want to point out, that unlike IF - THEN statement, this IF - ELSEIF - ELSE doesnt limit the number of lines to execute, there can be several lines executed:

Code: Select all

IF A=1
   text = "Hello World"
   B=2
ELSEIF A=2
   text = "Bye World"
ELSE
   text = "Error - A is not in range"
ENDIF
When IF/ELSEIF/ELSE statement is true, it will keep executing all the lines between that statement and next ELSEIF/ELSE statement, or ENDIF (In "ELSE"s case, it is always ENDIF).


Second, you can choose wether to use ELSE and ELSEIF. You can use both or only one of them, or actually not even either of them (IF - END - execution of multiple lines):

Code: Select all

IF A=1
   text = "Hello World"
ELSE
   text = "Error"
ENDIF

Code: Select all

IF A=1
   text = "Hello World"
ELSEIF A=2
   text = "Bye World"
ENDIF

You can also have multiple "IF" statements, inside "IF" statements:

Code: Select all

A=1
B=1

IF A=1
   IF B=1 
      text="Hello World"
   ELSEIF B=2
      text="Bye World"
   ENDIF
ENDIF

print(text)
-> Hello World

If you change "B" to 2, you will get "Bye World", if you change "A" into something else, you will get 0, since this A=1 block, which inside itself had B=something block will then never be executed.


Notice that sometimes there might be a reason, that instead of using IF - ELSEIF - ELSE statment, you want to use multiple IF statements, or perhaps absolutely dont want to use:

Code: Select all

A=1

IF A=1
     text="Hello World"
     A=2
ELSEIF A=2
     text="Bye World"
     A=3
ELSEIF A=3
     Text="Another World"
     A=1
ENDIF
Perhaps idea in your project is, that there are three different texts, and they should be continuously rotated.

Using previous examples way, this can be achieved. Each time code comes here, it checks what number A equals. If A equals 1, it makes content of "text" - variable to be "Hello World" and also changes variable A to become 2, so that when code next time comes to this same place, it would instead pick the "A=2" statement place to be executed.

What is important to notice is, that each time code gets here, only one of these statement places is going to be executed. Therefore when A=1 is already executed (making A=2 become true) it jumps directly to ENDIF and continues executing code.

Many times, this is the preferred way, since if it would execute each IF - ELSEIF statment as long as they are real, then in this previous example, it would always execute them all, and instead of ending up printing different text each time, it would always be printing the "Another World" out since first A would equal 1, then because of that, it would change to 2, after that it would check if A equals 2, and once again change to 3 and then finally check if A = 3 and get text as "Another World". However, this is not how IF - ELSEIF - ELSE works, it will always only execute one of those conditions, the first one that is true.


But sometimes it might be favored that all be executed one after another, in this case you will just have to use multiple "IF"s:

Code: Select all

IF A=1
   text="Hello World"
   A=2
ENDIF

IF A=2
   text="Bye World"
   A=3

IF A=3
   text="Another World"
   A=1
ENDIF

This code of course makes no sense, but just to get the idea, since sometimes this is how you wish it to work out.


Notice also, that when using IF - ELSEIF - ELSE statements, only the first one to be true is executed:

Code: Select all

A=1
B=1

IF A=1 
    text="Hello World"
ELSEIF B=1
    text="Bye World"
ENDIF

In previous examples i was always using same variable (A) on IF and ELSEIF statments, but sometimes it might be that you have multiple "IF" conditions, which are not necessarily excluding each other out, in these kind of cases you have to be careful not to make a mistake where both A=1 and B=1 lines were supposed to be executed, but in this case, only A=1 is executed, since it is the first true statement the program sees, and after executing that statements lines, it jumps directly to ENDIF which ends the whole IF - ELSEIF - ELSE block.


Up to this point, our code have always went directly from beginning to end, line by line, perhaps jumping some lines thanks to "IF", statements, but regardless, they have always been doomed to sooner or later, end.

And the example about text that would be changing between three different texts is useless, if code ie executed only once.

While there are programs in which code is suppsoed to be executed just once, most of the programs, for example games, are supposed to keep executing the code again and again forever, until user himself decides to quit the program.

To achieve this, we could for example use REPEAT - UNTIL/FOREVER statement.

as example:

Code: Select all

A=1


REPEAT

IF A=1
     text="Hello World"
     A=2
ELSEIF A=2
     text="Bye World"
     A=3
ELSEIF A=3
     Text="Another World"
     A=1
ENDIF

Print(text)

FOREVER
-> Hello World
-> Bye World
-> Another World
-> Hello World
-> Bye World
->...


When program sees "REPEAT", it will mark that spot.
When program comes to the point of "FOREVER", it will jump to the REPEAT and continue executing code from there, effectively repeating the IF - ELSEIF - ELSE statement again and again, and hence changing the contents of the text, as well as the number stored inside variable "A". And during each REPEAT - FOREVER cycle, also print the current value of "text" at PRINT(text).

Only way to stop this, is by quitting the program using "CTRL" + "C" combination.

However, instead of using FOREVER to make it last forever, we can use UNTIL, which tells the condition at when to continue past that UNTIL point.

example:

Code: Select all

1: A=1
2:
3: REPEAT
4: A = A + 1
5: print("Hello World")
6: UNTIL A=5
7: 
8: Print("End of Code")
->Hello World
->Hello World
->Hello World
->Hello World
->End of Code


in line 1 This program first assigns value 1 to variable "A"
At line 3 comes the REPEAT command, which makes it so that lines 4 and 5 are being continuously executed, until the line 6s UNTIL statement of "A equals 5" is true, at which point it will continue executing the code forward.

What happens at lines 4 and 5 is following:
line 4 says that A = A + 1, this means, that if A is for example 2, then A is same as 2 + 1, which is 3. This is good way to get A to increase its value by 1 each cycle.
At line 5 it simply prints out text "Hello World" using print() command.

Notice one thing here. When you look at the code, you first look, and programmer have probably so also intended, that "Hello World" would be written 5 times before exiting the REPEAT - UNTIL CYCLE, but if you look it cycle by cycle you notice the error.
A starts from 1, and before "Hello World" is wrote even for first time, A is already increased to 2. Therefore Print command is only executed when A is 2, 3, 4 or 5. Since when it is 5, it is already going to continue the code forward.

This kind of bug is very easy to miss on codes.


Just like "IF"s, so can REPEAT - UNTIL commands be multiple inside themselves, and you can have even multiple "IF"s inside REPEAT - UNTILs as well as multiple REPEAT - UNTILs inside "IF"s.

Code: Select all

1:  A=0
2:  B=0
3:
4:  REPEAT 
5:     A=A+1
6:     B=0
7:     REPEAT
8:        Print(A..B)
9:        B=B+1
10:    UNTIL B=10
11: UNTIL A=10
->01
->02
->03
...
->10
->11
->12
... all the way until 99


What happens in this is that at beginning, lines 1 and 2 A and B is assigned with value 0.

Then comes the first REPEAT at line 4, which keeps cycling through lines 5 and 6. Each time this cycle happens, A will increase its value by 1, and B will get back to 0 (actually, line 2s initialization of B is not even necessary since same happens here anyway)

But before this lines 4 and 5 cycle is repeated, there will first be execute another REPEAT - UNTIL, which cycles through line 8 and 9.

Line 8 prints out current values of A and B, effectively making it 00, 01, 02... (A being 0 for the duration of the whole REPEAT - UNTIL, B being the number changing)
When B reaches 10, it will end the REPEAT - UNTIL, and continue the previous REPEAT - UNTIL. However, part of previous parts cycle, was this second REPEAT - UNTIL, and it will be executed again and again, until A reaches 10 as well.

By other words, first cycle is executed only 10 times, while second cycle is executed 100 times in total (10 times the 10 cycles).

There could be even more REPEAT - UNTILs inside these, as well as IF statements and any other Hollywood program flow control commands.

In examples before, I have been using variable equals something, but there is also other options instead of direct comparison of value. You could also use statements like "Greater than" or "Less than" for example. There are some obvious uses for these kind of statements, for example on untils.

Example:

Code: Select all

REPEAT
  A=A+2
UNTIL A=10
This works fine as long as A is for example -2, 0, 2, 4... But what if A is for example 1 when it enters this REPEAT - UNTIL cycle?

Surely idea is that when A reaches 10, it would continue forward, but now it would never reach exact 10, since it would first become 9, then 11, 13, 15... making the UNTIL statement unintentionally never true. Or what if A is already bigger than 10 when it reaches that point? Same problem again.

Instead of comparing exactly, we can use Relative comparing.

Code: Select all

REPEAT
  A = A + 2
UNTIL A > 10
Now it compares if A is bigger than 10, if it is, regardless of how much bigger it is, it will continue executing the program.

Now cycle works fine, regardless if A is 1 or above 10 when it enters the cycle.


With these basic commands, we are already able to make a simple PONG game.

Naturally, I wouldnt really make a Pong game this way, but would use different technics to achieve the same, but I by purpose kept it simple and with minimal amount of commands.

There are commands that I havent gone through yet, but they are much simpler from their nature than these basic programming commands I have went through now.

I will keep explaining place by place what I am doing and why, as well as explain the commands that havent been explained yet:

Code: Select all

@SCREEN {Mode = "FakeFullScreen"}
@DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}


stickwidth = 40
stickheight = 200

playerx = 1800
playery = 500

computerx = 110
computery = 500
computerspeed = 3

ballx = 930
bally = 500
ballsize = 30
ballstartspeed = 1
ballspeed = ballstartspeed
ballspeedincrease = 1.10
ballxdirection = "right"
ballydirection = "up"

top = 0
bottom = 1050
leftedge = 50
rightedge = 1900

playerscore = 0
computerscore = 0


quit = 0



BeginDoubleBuffer()


Repeat

Cls(#BLACK) 



If ballxdirection = "right"
	ballx = ballx + ballspeed
	If ballx > rightedge
		ballx = 1000
		bally = 500
		ballspeed = ballstartspeed
		computerscore = computerscore + 1
	EndIf
ElseIf ballxdirection = "left"
	ballx = ballx - ballspeed
	If ballx < leftedge
		ballx = 1000
		bally = 500
		ballspeed = ballstartspeed
		playerscore = playerscore + 1
	EndIf
EndIf



If ballydirection = "down"
	bally = bally + ballspeed
	If bally > bottom
		ballydirection = "up"
	EndIf
Else
	bally = bally - ballspeed
	If bally < top
		ballydirection = "down"
	EndIf
EndIf



If computery < bally
	computery = computery + computerspeed
ElseIf computery > bally
	computery = computery - computerspeed
Else
    /*nothing*/
EndIf


playery = MouseY()



Box(ballx, bally, ballsize, ballsize, #WHITE)
Box(computerx, computery, stickwidth, stickheight, #WHITE)
Box(playerx, playery, stickwidth, stickheight, #WHITE)	
TextOut(300, 30, computerscore)
TextOut(1630, 30, playerscore)





col = Collision(#BOX, ballx, bally, ballsize, ballsize, computerx, computery, stickwidth, stickheight)
If col = 1
	ballxdirection = "right"
	ballspeed = ballspeed * ballspeedincrease
EndIf

col = Collision(#BOX, ballx, bally, ballsize, ballsize, playerx, playery, stickwidth, stickheight)
If col = 1
	ballxdirection = "left"
	ballspeed = ballspeed * ballspeedincrease
EndIf


Flip()


If IsRightMouse()=1 Then quit = 1
	
Until quit=1


Two first lines are

Code: Select all

@SCREEN {Mode = "FakeFullScreen"}
@DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}

These are both commands that tell us what kind of screen to open. FakeFullScreen means that it is opening a window of size that occupies your whole screen, and while it is actually a window (you can even drag it around), it looks like a screen. (There is difference between screen and window, which I am not going into at this)
@DISPLAY on the other hand tells other stuff about your display, like that the resolution is HD resolution (1920x1080), borderless means that there are no visible borders seeable, SCALEMODE_AUTO tells that regardless of the size of your display, it will always autoamtically stretch it to right size. So if you have screensize of 640x256, it will shrink the picture to right size to fit your screen, and similarly, if your resolution is bigger, it will stretch the images to fit it right.

You dont really need to understand those two lines at this point yet. But they are necessary to put in some way, just copy paste from here.

After these, there are many Variables I am initializing for the program. I am putting all the variables to beginning, so it is easiest to see what bariables are being used, as well as change them. For example, you could try changing the value of ballspeed or computerspeed and see what happens. Notice that some variables are shared. What I mean by this is that for example size of the pongstick is same for both computers and players pongstick, hence I have only decided to use one stickwidth variable, which i use to put both players as well as computers stick. Therefore, if you change this value, size of both players as well as computers pongstick changes.

Notice the Quit = 0 part.

This is how I have put QUIT option for player. Following lines are important for it:

Code: Select all

QUIT = 0
REPEAT
if isrightmouse() = 1 then QUIT = 1
UNTIL QUIT=1


What happens is, that first it is told that value that variable "QUIT" holds, is 0.

Then comes the REPEAT - UNTIL. Lines between these make the actual game, and these lines are repeatedly being executed to make the game move.

UNTIL condition is that if QUIT equals 1, then it will continue forward (and end the program, since there isnt anything else there beyond that point left to execute)

Only way to make QUIT become 1, is by clicking Right Mouse Button.

isrightmouse() tells the state of the right mouse button. It works in states 0 and 1. 0 means that it is not pressed, 1 means, it is being pressed.

What that line is effectively doing, is checking if Right Mouse button is being pressed (IsLeftMouse() = 1) and if that happens, then it changes the value of QUIT to 1, which means that when REPEAT - UNTIL cycle reaches UNTIL part, it will continue out of the cycle and effectively ends the program.



Next I am explaining the movement. Movement is very simple in this. Ball moves only to 4 different directions. Upleft, upright, downleft, downright.

I am using variables balldirectionx and balldirectiony to tell which direction ball should be moving to.

Code: Select all

If ballxdirection = "right"
	ballx = ballx + ballspeed
If ball is moving to "right", then it should increase its ballx. To fully understand this, you need to understand bit about screens.

For example, if display is FULL HD display with 1920 x 1080 resolution, it means that there are 1920 different spots where something can be drawn on vertical (x) line.

Displays work so, that location 1 is to the left edge, and location 1920 is to the right edge of the screen. Therefore, if i want to move something towards right, I need to draw it bigger numbered location. Therefore ballx = ballx + 1 moves ball, one pixel towards right.

Similarly Horizontally (y) numbers work 1 being on top of the screen, and bigger numbers being at bottom of screen. location 1,1 is therefore at topleft corner, while location 1920,1080 is at bottom right corner.


You may also notice, that I didnt use ballx = ballx + 1, but isntead used, ballx = ballx + BALLSPEED.

This is so I can have better control at what is the speed of the ball, as well as have possiblity to increase balls movement speed during play. At beginning Ballspeed is 1, hence at beginning of game, saying ballx = ballx + 1, is true, although at later point it isnt anymore.


After I have moved the ball, there comes another thing I have to check, did it reach the right edge yet?

Code: Select all

If ballx > rightedge
		ballx = 1000
		bally = 500
		ballspeed = ballstartspeed
		computerscore = computerscore + 1
	EndIf
Therefore if ballx is bigger than the rightedge (rightedge being decided at beginning of code as one of the variables) then it means goal have happened. Since I ahve fixed it so that computer is always on left side of screen and player at right side, we will know that if ballx reaches the right edge, then it means the computer is scoring one point. Similarly if left edge is reached, we know that player have scored one point.

If this happens, then we first decide that ballx = 1000 and bally = 500, effectively making ball go to about middle of screen.
ballspeed = ballstartspeed. Reason for ballstartspeed is, that during the game, ballspeed keeps getting higher. By using this variable "ballstartspeed" we can simply change this number at beginning variables place, and everytime score happens, get the balls speed go back to the starting speed.

finally, I am using variable computerscore = computerscore + 1. This is simply the amount of points the computer has. Since we know this happens only when right edge is reached, it is clearly a point for computer.


In balldirectiony case things work slightly different:

Code: Select all

If ballydirection = "down"
	bally = bally + ballspeed
	If bally > bottom
		ballydirection = "up"
	EndIf
First check if ballydirectiony = "down"

if this is the case, then move the ball similarly as with x we did.

After that once again check if y have reached the edge of screen (or in this case - bottom)

If y indeed have reached bottom, then in this case change the "balldirectiony" to "up", so the ball would next time go upwards. Similarly in Hitting top code is similarly chaging balldirectiony to become "down" again. Effectively making ball go from top to bottom and back forever and ever more, making it look like that if it hits the edge of screen, it bounces from it.



Then comes the simple AI that this game has:

Code: Select all

If computery < bally
	computery = computery + computerspeed
ElseIf computery > bally
	computery = computery - computerspeed
Else
    /*nothing*/
EndIf
This will simply compare computery with bally, if ball is higher than computerstick, then computerstick will aim at getting higher by amount of computer speed, and similarly if ball is at lower place than stick, then computery will go towards down to reach balls y.

In practice this means that computer simply follows the ball, and when speed of ball becomes higher than computers speed, then computer will fail to hit the ball anymore.

notice the ELSE statement. There are two things to learn here.

First is the "/*" and "*/", this is so called comment tag. "/*" starts the commenting, which means that we can write anything we like after that one without affecting the code in any way, and then the "*/" ends it and after that point all that is written, will be executed as normal code again.

But more importantly, there are now cases of computer y and ball y are bigger or smaller, but what if it is the same? This ELSE is there to handle that, although there isnt really anything to handle since idea is taht coputer stays still, in which case no values of any variables are changed.

Basically there isnt even any need for that ELSE, but I put it there just for educational purposes.

Now that programs logic is eplained, lets go to graphics part:

Code: Select all

Box(ballx, bally, ballsize, ballsize, #WHITE)
Box(computerx, computery, stickwidth, stickheight, #WHITE)
Box(playerx, playery, stickwidth, stickheight, #WHITE)	
TextOut(300, 30, computerscore)
TextOut(1630, 30, playerscore)
At this point, we are simply drawing 3 boxes (computer pongstick, playerpongstick and ball) as well as displaying scores using TextOut.

TextOut works simple. you tell x and Y spot where you want to display the text, in this case 300, 30 and 1630, 30, and then you tell what text to print there, which in this case is number variable holding computers and players current score.

BOX commands arent really any more difficult. First you simply tell where in screen you want to place them by giving X and Y co-ordinates. Two next numbers tell the width and height of the box you wish to draw. Last detail is the color of the box, which i in this case used #WHITE.

Since ball is simply a rectangle, I am using for both its width as well as height variable "ballsize". You change that, and it becomes smaller or bigger rectangle.

Since pong sticks size is same for player as well as computer, I have simply used stickwidth and stickheight which i use for both computer as well as player.

Notice that playerx and computerx are actually static. During the program execution, they never change. Sticks only move in Y.

Similarly the line

Code: Select all

playery = MouseY()
is simply taking Mouse pointers Y-co ordinate and telling to use the same Y as players Y, and that way when ever you move your mouse in Y-axis, so will the stick move too.

practically whole movement of players pong stick is handled in:

Code: Select all

repeat
cls(#BLACK)
playery = MouseY()
Box(playerx, playery, stickwidth, stickheight, #WHITE)
until quit=1

After all the graphics are drawn, we are going to Collision testing to see if one of the pong sticks have touched the ball:

Code: Select all

col = Collision(#BOX, ballx, bally, ballsize, ballsize, computerx, computery, stickwidth, stickheight)
If col = 1
	ballxdirection = "right"
	ballspeed = ballspeed * ballspeedincrease
EndIf

col is simply a variable to handle the result of collision test (which is either 0 - no collision, or 1 - collision)

Collision command works so, that first we are telling what kind of collision we are checking for, in this case we are looking wether two rectangular areas are colliding with each other (we wouldnt actually even need to draw anything to check this, since this doesnt check if any two images are colliding, just if two defined areas are touching).

Then we first tell the x and y of the first rectangle areas, and then the width and height of it. Then similarly second boxes x and y and width and height. After this Hollywood calculates if these would collide or not.

If collision did happen, then it goes inside the if statements execution. Since this is very simple game, we know that for example in this case, if ball touches computers pongstick, then we already know the ball was travelling from right to left, and hence we can simply tell it to change its direction to go towards right from now on.

In addition I am also increasing balls moving speed. This is handled by using ballspeedincrease variable, which works with simple mathematics.

I am using 10 percent increase on each hit in this program. I could do it some other way, but easiest way to get 10 percent increase mathematically, is by multiplying the number by 1.10.

For example 100 X 1.10 = 110 (which is 10 percent more), 110 x 1.10 = 121 x 1.10 = 133 x 1.10 = 146 x 1.10 = 160...


As a last thing, our graphics explanation is not complete. There are still:

Code: Select all

Cls(#BLACK) 
BeginDoubleBuffer()
and Flip()
unexplained.

Cls is same as Clear Screen, and it does exactly that. It will make the whole screen become of the defined color (in this case Black), making it look like screen is cleared.

This is necessary, since each time we draw a box, it would simply be drawn on top of the previous ones, but by clearing the screen between these drawing, it makes it look like these boxes are moving.

You can try remove CLS line and see for yourself what happens.


However, this is not so simple a thing as simply putting CLS command, since without Doublebuffer and Flip, it would work so, that you would see each thing happening. First screen being cleaned, then each rectangle being drawn. This would most likely cause annoying flickering (depending upon your hardware etc.)

Therefore there is this BeginDoubleBuffer() and Flip()

You first start with BeginDoubleBugger() (and remember to do it before the graphics REPEAT - UNTIL loop starts, since you are supposed to do it only once), and after that instead of drawing stuff visibly, Hollywood will draw everything on background, unseeable to player.

When Flip() is used, it will update the screen to the current one. You could think it as if someone would keep drawing boxes on paper, that you can either have it so that he takes clean paper and starts showing whole drawing process to you, or then he instead shows you the paper only after he have already finished drawing everything there.

This is practically same that is happening with DOuble Buffering.

This was the simple PONG, experiment with the numbers and see what happens.

I however, at this end still want to put one more thing that i by purpose left out of this example, but which fixes one problem.

Now all the movement is based upon ball and computer moving some fixed amount each cycle. However, How long it takes for one cycle depends upon your machine. Vrey slow machine, and it is deadbeat slow, and very fast machine, and you will lose before you even notice it.

You can see this same problem with old Sierra games. They had slow, normal, fast, fastest speed as options, if you chose the fastest, they were going as fast as they could, and with modern machines, thats real fast. fast enough that you wont even see the screen when its already changed to next one (since your character moved to the edge already).


There is a fix for this. Instead of tweaking with numbers on each different machine trying to find suitable speed, we can use Clock to speed everything so, that regardless how fast or slow your machine is, it will always work the same speed for everyone.


First of all, add following line right before REPEAT

Code: Select all

StartTimer(1)
This tells computer to start Clock number 1. This is like someone in sports starting the clock to time how fast people are running 100 meters.

Next, as last thing before UNTIL you put following:

Code: Select all

Repeat
timepassed = GetTimer(1)
Until timepassed > 3
ResetTimer(1)

timepassed = timepassed / 1000

REPEAT - UNTIL loop is there constantly looping, until at least 4 time units have passed. "timepassed" variable saves the current amoutn of time passed since clock was started/reseted, and if it isnt at least 4, it keeps continuing to do so, until clock shows at least 4.

After UNTIL condition is met, there comes the ResetTimer(1), which tells HOllywood to put clock number 1, back to 0 and start calculating from beginning. This way we will each time get a new time that have passed since the last time the cycle was completed.

Notice that it is very important that right after GetTimer, as soon as possible, ResetTimer is executed.

one thing that tempts programmingwise would be to first have the timepassed = timepassed / 1000, but if these two would be upside down, then that would mean, that if for some reason machine would get stuck for this line, it wouldnt affect the clock in anyway, making the game move at wrong speed.

Reason for this timepassed = timepassed / 1000 is, that first of all, 1000 time units, is same as 1 second of time passed.

This line is for mathematical reasons to make things later simpler. This has similar idea as the 1.10 multiplier to get the 10 percent increase.

In this case, lets say time have passed 100 units, that is 1/10th of a second. By dividing this number by 1 000, we will get 0.1 as a result.

If some character is supposed to move at speed of 200 pixels per second, we can use following calculation: characterx = characterx + (characterspeed * timepassed), which would be characterx = characterx + (200 * 0.10), which means, in 1/10th of a second, this character would move 20 pixels - exactly the amount it should move.

Now that the timer is done, we still need to apply the system to all the movements (except players which speed is based upon the speed player moves the mouse):, you have to change couple of lines:

Code: Select all

ballx = ballx + ballspeed  ---> change into --->  ballx = ballx + (ballspeed * timepassed)
and similarly

Code: Select all

ballx = ballx - (ballspeed * timepassed)
bally = bally + (ballspeed * timepassed)
bally = bally - (ballspeed * timepassed)
computery = computery + (computerspeed * timepassed)
computery = computery - (computerspeed * timepassed)

notice now that speeds are also now very different. For example, ballspeed = 1 defined at beginning, will now mean that it moves 1 pixel each second, making the trip from one edge to other take about half an hour. Personally I think that is bit too slow, and I would recommend rather changing it into something like 300. Similarly Computer speed of 3, is deadbeat slow as well, perhaps 1000 for that now.
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

I noticed the UNTIL example where it is supposed to print numebrs from 00 to 99, have an error in it. It is actually printing numbers starting from 10, instead of 00.
Yasu
Posts: 17
Joined: Mon Jun 30, 2014 3:12 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Yasu »

I have started to seriously look into the code (I was abroad for 3 weeks) and I'm having a problem. Your examples, like:

Code: Select all

hillbillybilly = 1
ceo = "Bill McEwen"
agentx = "James Bond"

print(hillbillybilly)
print(ceo)
print(agentx)
Shows every one of the inputs as seperate lines. But when I write the same code, they are all one line. Plus, without the REPEAT FOREVER in the end they all just blink and disappear. Is this something specific to MorphOS? If not, can you change the original text so we can avoid lose ends?
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

Thanks from pointing that.

Im going to think which way to fix that problem.

For most part i didnt test my codes. Basically only the actual game.

This is also first time i was using "Print()" command. Usually I use Debugprint, which automatically changes line. But with print you need to use \n to change line. As example:
print("first line\nsecondline")

if you put "depbugprint" instead of "print", how does it show it on MorphOS?

For I wonder which way to do this, to add those "\n" line changes, or to use DebugPrint() instead.

Debugprint would be simpler due to lack of "\n", but Print would be slightly more readable, but would also reguire to make some extra line like "Wait 50" to make it wait for a while and user to see the end result of the program. Or otherwise program is executed so fast that user cant see what happened.

That was actually my own thought error, since i had just been reading programming with LUA book that was recommended for learning methods in Hollywood docs, and it presumes that you are executing those LUA programs from commandline (ie. dos), and hence prints would be seeable, as they stay on that CLI window, until something else comes there. But Hollywood is always by default ran in its own window, hence when program ends, it also automatically closes the window.

Debugprint would also be very good thing to learn right at beginning, since that is very useful when hunting for bugs.

However, what I am most worried in Debugprint is where it shows it. In PC-versions programming environment and WINUAE with Cubic IDE, Debugprints are shown nicely below actual code in their own place, while if you compile and run, it will annoyingly for simple programming exercises point of view, open its own separate "debug"-window and show them there.
Yasu
Posts: 17
Joined: Mon Jun 30, 2014 3:12 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Yasu »

What is most important is that the reader doesn't have to guess why things look different from the example text to the final text. Remember, we are doing this for absolute beginners. They (I) will not the able to understand what's wrong. It doesn't matter if you use debugprint or print as long as it's explained what the difference is.

I tried using "\n", but I could never get it to work for some reason.

I wrote this text:

Code: Select all

text1 = "Hello"
text2 = "World"

debugprint(text1..text2)
debugprint(text1.."I forgot to add space"..text2)
debugprint(text1.." Now i remembered space "..text2)
debugprint(text1.." "..text2)
And the outcome is this:
System:Applications/Programming/Hollywood/Genberg/AF11> del06
HelloWorld
HelloI forgot to add spaceWorld
Hello Now i remembered space World
Hello World
System:Applications/Programming/Hollywood/Genberg/AF11>
It looks good. Except that you get a window opened and closed in a blink of an eye. If you use REPEAT FOREVER in the end, you get a black window. It would be good if you would either get the text in Shell or in a window. This just looks odd.

My own experience is that if it looks wrong, you want to correct it. But if you can't, you start to think that you will never be able to and give up.
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

try this one:

Code: Select all

Print("hello\nworld")
Wait(50)
If it doesnt change a line, then I guess you found a bug from MorphOS version, and we better use debugprint, otherwise I think I will use something like that "Wait(50)" after each line. It would be quite strange if a bug like this would exist in MorphOS version without anyone noticing it, since print() is such a basic function.

I anyway will change it so that i will explain both debugprint as well as print and difference between them, since it is good to know the debugprint as well as print, although i have actually never used print before this.
Yasu
Posts: 17
Joined: Mon Jun 30, 2014 3:12 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Yasu »

Just tried it out and it works fine. No bugs here, just a guy not knowing better :P

Great! Please write it here when you have done the changes and I will try it out from the start again.
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

I probably do it during this weekend, I think I will edit my original posting, and also check the text through otherwise too.
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

This is Hollywood Tutorial for people who dont know programming in advance.

Instead of traditional way of explaining, this guide will aim at teaching you through simple game examples.

In this first tutorial, we will go through some basics, and make a simple PONG game.

I wont explain you in detail how to make and run your Hollywood programs, I suppose you can figure that much out yourself.

Simply make program with text editor, and compile it with hollywood and run.

There are also text editors that support Hollywood in themselves, making programming much easier, as they will give you tips on the code syntaxes, as well as you can simply run the code by clicking "run" button, instead of first compiling the code into exe and then running it.


Couple of things about my explanations.

1. Sometimes I will use numbers at beginning of codelines to make it easier to explain the code. However, if you try out these example codes yourself, never use the line numbers, or code wont work.

2. If line starts with "->", it means this is what will be printed to the screen when program is executed.


Lets start:

Code: Select all

print("Hello World")
-> Hello World



There is only one command in this code: "Print". Most Hollywood commands have "()" at end of them, ie. "Print()"

This "()" - part is the part, where the coder can - and usually even needs to - put stuff for that commands purposes, in this case the text "Hello World".


If you try out this code for yourself, you will notice there is one problem. You probably wont see that text at all, since right after Hollywood prints that "Hello World"-text to the screen, program ends and hence closes itself.

If you want to see and experiment with it, try following:

Code: Select all

    Print("Hello World")
    REPEAT
    FOREVER

If you now execute the program, this "Hello World" - message will stay on your screen until you hit "CONTROL" + "C" - buttons together, this will tell Hollywood to quit the program.

I will at later point explain you the REPEAT - FOREVER part, although it is somewhat self explanatory.

From this point on I will however replace this REPEAT - FOREVER, with "Wait(50)".

what this command does, is exactly as it says. It will wait for a while, before it continues forward, or in our example programs case, will end the program, since there are no more lines to be executed.

The number after "Wait" command, tells to program how many time units i wish it to wait. By default, Wait command uses so called "Ticks" as time units. 50 ticks are same as one second. Hence "Wait(50)" will result in program waiting for 1 second.

If you wish for it to wait for longer, you may either put bigger number, or replace "Wait(50)" with REPEAT - FOREVER, like in previous example.

There is also another possiblity. For instead of using "Print" command, you could use "Debugprint" command. debugprint is a command that prints text outside of program so to say, this is for debugging your code purposes, things that are basically not shown to user, but are shown in specific debug place (for example debug window). By default, these debugprint commands are seeable after program execution as well, and i myself for example havent actually ever used "print" command before in Hollywood until writing this Tutorial, but have instead always used Debugprint, since it have served my purpose everytime.

I just decided to stick to print-command, since Debugprint might show in different ways in different environments, so with print-command, i can control better, that everyone following this tutorial, will have same results.

Notice also, that if you use debugprint instead of Print, you can ignore the "\n" that comes in this tutorial at later point, since Debugprint automatically changes line.


As you might have noticed, I used '"'-commas on edges of the text that i wanted to print to screen. If you remove these '"'-commas, you will notice it stops working, but if you remove the space between these two words, you will notice something even stranger to happen:

Code: Select all

    
Print(HelloWorld)
wait(50)

-> NIL

This is, because now print() function is trying to print out variable instead of a text. But what exactly are variables?

You probably still remember from school those math questions in type of "2 + X = 3" determine the X.

That "X" is almost like typical programming variable.

If we rather change this math question to "2 + X = ?" then we have a typical variable.

Depending upon what the X is, will also decide, what the result will be, and X can be any number, and even change during the program, therefore resulting also in the result of this equation changing during the program.


Idea with Variables is, that hey can store info. This info can be either numbers or letters.

For example, variable X could store number 4. Or variable X could store someones name ie. "Andreas Falkenhahn".

To take real life example of Variables in real life use without computers would be to take US president. If we want to write a letter to US president, we can either put the recipient as Barack Obama, or we can put the recipient as US President.

Difference is, that in case president changes during the time our letter goes from our place to US, then adressing it to Barack Obama, will take it to Barack Obama - the former president, while using US President as the recipient, will always get delivered to the current US President, regardless who it happens to be.

The practical use of Variables is many. But as simple example, lets suppose Company has their letters in fixed format having CEOs name always written fully in them:

"Greeting from Amiga Inc. This is Bill McEwen...", "Amiga Inc. CEO Bill McEwen is happy to..." and so on. There be multiple letters with CEOs name written on it. But what if that CEO Changes?
It would mean, that into all different hundred letters, CEOs name would need to be manually changed.

And here comes variables. What if, instead of having fully written letters, there would be templates of letters, which would be having the full text otherwise, but instead of writing "Bill McEwen", there would write (VARIABLE) X, as example:


"Greetins from Amiga Inc. This is X...", "Amiga Inc. CEO X is happy to...".

Now when the program would be run, you would first tell it what is X, in this case "X = "Bill McEwen". When Program is now run, everytime it would encounter the X, it would change it into "Bill McEwen". And if it now happens that CEO changes, all you would need to do, would be to change the first line of code to "X = "Trevor Dickinson", and instead of doing hundred changes, you would do only one. This is example of simple practical use of Variables.


Lets see the usage of variables in practice:

Code: Select all

    X = "Hello World"
    print(X)
wait(50)
-> Hello World

as you can see, this works exactly like the previous program did, but instead of telling print command to print "Hello World", we are now telling it to print out what ever variable X is containing.


Variable names can be anything, as long as some specific characters are not used, and it doesnt collide with another Hollywood command or reserved words, hence you cant use for example variable named "print", since it is already a commans name, and Hollywood would think you are trying to use that Command instead of a variable.

Therefore, variables name could be for example: "hillbillybilly", "ceo", "agentx"... All of these are completely acceptable, and can have anything inside them:

Code: Select all

    
    ceo = "Bill McEwen\n"
    agentx = "James Bond\n"
    hillbillybilly = 1

    print(ceo)
    print(agentx)
    print(hillbillybilly)

wait(50)
-> Bill McEwen
-> James Bond
-> 1


however, when using variables, you should aim at naming them soemthing descriptive, as example:

Code: Select all

    text = "Hello World"
    print(text)
wait(50)
-> Hello World

using variable name like "text" for storing the text to be printed out, makes it much easier to understand what is happening in your code.


By the way, you perhaps noticed that in previous example before this one, i used "\n" in some of the variables names (James Bond\n and Bill McEwen\n)

This is due to how print command works.

If I would do for example:

Code: Select all

Print("First Line")
print("Second Line")
wait(50)

-> First LineSecond Line


my purpose was obviously to have them be printed on different lines, however, Print command doesnt automatically change line after being executed, like Debugprint does, but instead, you have to tell it when you wish to change a line by putting "\n" into the text. Each time Print command faces "\n" it will change a line.

as example:

Code: Select all

Print("First Line\n\nSecondLine")
wait(50)
->First Line
->
->Second Line


As you can see, when i put "\n" twice, it actually changed the line twice, and also, that i used this "\n" in middle of line, instead of at end of line.



Contents of variable can naturally be changed during the programs execution, otherwise these variables wouldnt be of much use other than as doing replace:

Code: Select all

    text = "Hello World\n"
    print(text)
    text = "Another text\n"
    print(text)
    text = 123
    print(text)
wait(50)
-> Hello World
-> Another Text
-> 123


Notice also, that although I have so far used to print only one variable at a time with Print command, i can as well print out several:

Code: Select all

    text1 = "Hello"
    text2 = "World"

    print(text1..text2)
wait(50)
-> HelloWorld

By simply using ".." two dots between two variables, I can add as many of them as I like. Notice however, that there is now one mistake in this code, for what I really wanted, was to get "Hello World", but now it was "HelloWorld", without the space. This is naturally, because I only told print command to first print contents of "text1" and right after, the contents of "text2", hence there is no space.

But this can be fixed, because I can also add letters myself without always using variables:

Code: Select all

    1: text1 = "Hello"
    2: text2 = "World\n"
    3:
    4: print(text1..text2)
    5: print(text1.."I forgot to add space"..text2)
    6: print(text1.." Now i remembered space "..text2)
    7: print(text1.." "..text2)
    8: wait(50)
->HelloWorld
->HelloI forgot to add spaceWorld
->Hello Now i remembered space World
->Hello World


As you can see, in line 5, I use my own writing to put between those two variables, but since i havent add any space, it writes it right after them.
in line 6 i however add those spaces, and in line 7, i put nothing but the space, and it finally comes out the way i intended it to come in the first place.


When variables have numbers inside them, they can be used to make mathematic calculations:

Code: Select all

    mysalary = 2000
    wifessalary = 1000

    totalsalary = mysalary + wifessalary

    print("Me and my wifes total income is "..totalsalary.." per month")

wait(50)

->Me and my wifes total income is 3000 per month



As you probably have already figured out, code is executed line by line basis, this means, that if you are referring to something, it must already exist before that line of code.

To take the previous example, you might think it would be nice to first make the actual action line, and then put all the variables to the end of the code so they would be easy to find:

Code: Select all

    1: totalsalary = mysalary + wifessalary
    2: print("Total Salary: "..totalsalary)
    3:
    4: mysalary = 2000
    5: wifessalary = 1000
    6: wait(50)
-> 0

While this is clear for a human what should be happening in this small piece of code, computer doesnt get it. When computer executes line 1 to determine value for variable "totalsalary", it doesnt understand to go and look for the variables values from lines 4 and 5, but instead, simply makes the calculations based upon "mysalary" and "wifessalary"s current values.
As they havent been initialised with any value yet, their value is therefore 0. Hence, totalsalary = 0 + 0 which in total is 0.

Although i said the value to be 0, it is not completely true, for in reality, if there is variable that has nothing stored in it, or by otherwords doesnt have any value, or even other said, havent been even initialized therefore, it is not exactly 0 that is inside it, but a NIL. NIL and 0 are evry similar but there is slight difference between them. For NIL means there is no value at all, while although 0 is same as nothing, it is still a value.

But when you use NIL for mathematical calculations, it for most of time treats it as number 0. It is bit more advanced thing at this point, but just know that if there is some strange bug in your code going around number 0, problem might be that it is actually NIL instead of 0.

This is also the reason why at beginning of this tutorial "print(helloworld)" resulted in "NIL", as after removing the commas around the "helloworld", print command thought it was supposed to print what is inside variable called "helloworld" and as that variable hadnt been initialized, the value was NIL.
And while in this previous program, values again were uninitialized and hence NILs, they however worked as 0s, since they were used in Mthematical calculations, and therefore also gave a total result of value 0, instead of value NIL.

Hence NIL + NIL equals = 0, in mathematical calculations, instead of NIL.



You can think code execution to be like following driving instructions. If instructions are:
1. Turn left, 2. Turn Right, 3. Drive straight forward - you cant expect to get to the right place, if you instead of first turning to left, decide that you rather start by turning right and only after that to the left.

This is same with coding. If you havent already earlier told some value to the variable, you cant expect computer to figure out that it is supposed to get that value from later point of the code, especially when considered that the same variable might have different values at different points of code.


So far our program flow have been pretty straight forward. Execute line 1, then 2, then 3... after all the lines are executed, then end the program, which is what have been happening after "wait(50)" line.

But that isnt very useful and the real use of variables isnt just to be some copy-paste stores, but for them to store different states of different things which will be used to dynamically create things to happen.

If you wonder what is difference between dynamic and static. The static is something that always stays the same. As example, our first
Print("Hello World")

This will always result in exactly same result.

However, dynamic version would be:

Code: Select all

    text="Hello World"
    Print(text)

This "print(text)" is dynamic version, since depending upon what is inside the "text" - variable, depends what comes to screen.


Similarly, any game code for example is always dynamic by nature, since depending upon players actions would for example depend where the player is drawn to the screen.


The basic, most common way to affect things, is by using "IF" statement.

At simplest, "IF" is followed by "THEN" statement.

as example:

Code: Select all

    1: A=1
    2: IF A=1 THEN text="Hello World"
    3: IF A=2 THEN text="Bye World"
    4: print(text)
    5: wait(50)
-> Hello World

In this program, we in first line decide that variable A contains number value of 1.
second line check if A equals 1 (which it is) and if it is, then makes content of variable "text" have "Hello World" inside it.
third line does similar check to second line, but checks if A equals 2 (which it doesnt), and if it would, it would make content of variable "text" have "Bye World" inside it.
fourth line is simply printing out what ever is inside the variable "text", which in this case is "Hello World".

However, if you change first lines A=1 instead to A=2, you will get:
-> Bye World

And if you would change A into any other number, it would result in:
-> NIL

since when "text" is not initialized its value is NIL, and "text" contains data only if line 2 or line 3 is true (A equals 1 or 2), in other cases, lines 2 and 3 are ignored, in which case "text" stays uninitialized. and hence results in "NIL" printed out.

IF - THEN is the simplest form of IF statement, but many times it becomes necessary to execute multiple commands instead of just one, in which case you would need to write:

Code: Select all

    IF A=1 THEN text = "Hello World"
    IF A=1 THEN B=3
    IF A=1 THEN print(text)

and this would be exhausting, but to avoid doing this, you can instead do:

Code: Select all

    IF A=1
       text="Hello World"
       B=3
       print(text)
    ENDIF
This works so, that when you state the "IF A=1", then if A equals 1, then all the lines after this line will be executed, until comes the line that says "ENDIF"


Since in the first "IF" example, there were two different IF - THEN statements, and they both had to do with variable "A"s statement, there is another way to do it as well:

IF - ELSEIF - ELSE statement.

This works the following way:

Code: Select all

    1:  A=2
    2:
    3:  IF A=1
    4:     text="Hello World"
    5:  ELSEIF A=2
    6:     text="Bye World"
    7:  ELSEIF A=3
    8:     text="Still World"
    9:  ELSE
    10:    text="A:s value should be between 1 and 3"
    11: ENDIF
    12:
    13: print(text)
    14: wait(50)
-> Bye World

By changing value of A, you can get all the 4 different texts printed out.


This system works following:

in line 3 it is first checking the first statement, wether it is true or not. If it is true, then it goes to line 4 and does the stuff; in this case, assign "Hello World" to variable "text". After this it would go to line 5, and see there is another ELSEIF statment.
since IF statement already was true, it would in this case work same as "ENDIF" and instead of executing further, jump directly to line 11, which is ENDIF, and continue executing from that point on to line 12 (empty) and 13 (print).

If line 3s statement is not true, then it next checks statamenent at line 5, if that is true, execute line 6 and from line 7 jump to ENDIF again.

If line 5s statement is not true either, then jump to line 7.

If line 7s statement is not true either, then finally jump to line 9, and since it tells "ELSE", that means that in case none of the previous IF - ELSEIF statements were true, then execute the lines between ELSE and ENDIF, meaning that there is no situation where one of these branches wouldnt be executed, but at least one of these branches everytime does get executed.



First of all, I want to point out, that unlike IF - THEN statement, this IF - ELSEIF - ELSE doesnt limit the number of lines to execute, there can be several lines executed:

Code: Select all

    IF A=1
       text = "Hello World"
       B=2
    ELSEIF A=2
       text = "Bye World"
    ELSE
       text = "Error - A is not in range"
    ENDIF

When IF/ELSEIF/ELSE statement is true, it will keep executing all the lines between that statement and next ELSEIF/ELSE statement, or ENDIF (In "ELSE"s case, it is always ENDIF that ends it.).


Second, you can choose wether to use ELSE and ELSEIF. You can use both or only one of them, or actually not even either of them (IF - END - execution of multiple lines is in practice just variation of IF-ELSEIF-ELSE-ENDIF structure, it is just missing the ELSEIF and ELSE parts):

Code: Select all

    IF A=1
       text = "Hello World"
    ELSE
       text = "Error"
    ENDIF

Code: Select all

    IF A=1
       text = "Hello World"
    ELSEIF A=2
       text = "Bye World"
    ENDIF


You can also have multiple "IF" statements, inside "IF" statements:

Code: Select all

    A=1
    B=1

    IF A=1
       IF B=1
          text="Hello World"
       ELSEIF B=2
          text="Bye World"
       ENDIF
    ENDIF

    print(text)
wait(50)
-> Hello World

If you change "B" to 2, you will get "Bye World", if you change "A" into something else, you will get NIL, since this A=1 block, which inside itself had B=something block will then never be executed.


Notice that sometimes there might be a reason, that instead of using IF - ELSEIF - ELSE statment, you want to use multiple IF statements, or perhaps absolutely dont want to use:

Code: Select all

    A=1

    IF A=1
         text="Hello World"
         A=2
    ELSEIF A=2
         text="Bye World"
         A=3
    ELSEIF A=3
         Text="Another World"
         A=1
    ENDIF
Perhaps idea in your project is, that there are three different texts, and they should be continuously rotated.

Using previous examples way, this can be achieved. Each time code comes here, it checks what number A equals. If A equals 1, it makes content of "text" - variable to be "Hello World" and also changes variable A to become 2, so that when code next time comes to this same place, it would instead pick the "A=2" statement place to be executed.

What is important to notice is, that each time code gets here, only one of these statement places is going to be executed. Therefore when A=1 is already executed (making A=2 become true) it jumps directly to ENDIF and continues executing code.

Many times, this is the preferred way, since if it would execute each IF - ELSEIF statment as long as they are real, then in this previous example, it would always execute them all, and instead of ending up printing different text each time, it would always be printing the "Another World" out since first A would equal 1, then because of that, it would change to 2, after that it would check if A equals 2, and once again change to 3 and then finally check if A = 3 and get text as "Another World". However, this is not how IF - ELSEIF - ELSE works, it will always only execute one of those conditions, the first one that is true.


But sometimes it might be favored that all be executed one after another, in this case you will just have to use multiple "IF"s:

Code: Select all

    IF A=1
       text="Hello World"
       A=2
    ENDIF

    IF A=2
       text="Bye World"
       A=3
    ENDIF

    IF A=3
       text="Another World"
       A=1
    ENDIF


This code of course makes no sense, but just to get the idea, since sometimes this is how you wish it to work out.


Notice also, that when using IF - ELSEIF - ELSE statements, only the first one to be true is executed:

Code: Select all

    A=1
    B=1

    IF A=1
        text="Hello World"
    ELSEIF B=1
        text="Bye World"
    ENDIF

In previous examples i was always using same variable, as example - A - on IF and ELSEIF statements, but sometimes it might be that you have multiple "IF" conditions with different VARIABLES, which are not necessarily excluding each other out. In these kind of situations you have to be careful not to make a mistake where both A=1 and B=1 lines were supposed to be executed, but in this case, only A=1 is executed since it is the first true statement the program sees, and after executing that statements lines, it jumps directly to ENDIF which ends the whole IF - ELSEIF - ELSE block, and hence also skips the "ELSEIF B=1" line.


Up to this point, our code have always went directly from beginning to end, line by line, perhaps jumping some lines thanks to "IF" statements, but regardless, they have always been doomed to sooner or later, end.

And as example the example about text that would be changing between three different texts is useless, if code ie executed only once, since it will then also change only once, instead of keep rotating from one text to other.

While there are programs in which code is supposed to be executed just once, most of the programs, for example, and especially - games, are supposed to keep executing the code again and again forever, until user himself decides to quit the program himself.

To achieve this, we could for example use REPEAT - UNTIL/FOREVER statement.

as example:

Code: Select all

    A=1


    REPEAT

    IF A=1
         text="Hello World"
         A=2
    ELSEIF A=2
         text="Bye World"
         A=3
    ELSEIF A=3
         Text="Another World"
         A=1
    ENDIF

    Print(text.."\n")

    FOREVER

-> Hello World
-> Bye World
-> Another World
-> Hello World
-> Bye World
->...


When program sees "REPEAT", it will mark that spot.
When program comes to the point of "FOREVER", it will jump back to the REPEAT line and continue executing code from there, effectively repeating the IF - ELSEIF - ELSE statement again and again, and hence changing the contents of the text, as well as the number stored inside variable "A". And during each REPEAT - FOREVER cycle, also print the current value of "text" at PRINT(text).

Only way to stop this, is by quitting the program using "CTRL" + "C" combination.


However, instead of using FOREVER to make it last forever, we can use UNTIL, which tells the condition at when to continue past that UNTIL point.

example:

Code: Select all

    1: A=1
    2:
    3: REPEAT
    4: A = A + 1
    5: print("Hello World\n")
    6: UNTIL A=5
    7:
    8: Print("End of Code")
    9: wait(50)
->Hello World
->Hello World
->Hello World
->Hello World
->End of Code


in line 1 This program first assigns value 1 to a variable "A"
At line 3 comes the REPEAT command, which makes it so that lines 4 and 5 are being continuously executed, until the line 6s UNTIL statement of "A equals 5" is true, at which point it will continue executing the code forward.

What happens at lines 4 and 5 is following:
line 4 says that A = A + 1, this means, that if A is for example 2, then A is same as 2 + 1, which is 3. This is good way to get A to increase its value by 1 each cycle.
At line 5 it simply prints out text "Hello World" using print() command.


Notice one thing here. When you look at the code, you first look, and programmer have probably so also intended, that "Hello World" would be written 5 times before exiting the "REPEAT - UNTIL" - CYCLE, but if you look it cycle by cycle you notice the error.
A starts from 1, and before "Hello World" is wrote even for first time, A is already increased to 2. Therefore Print command is only executed when A is 2, 3, 4 or 5. And when it is 5, it is already going to continue the code forward.

This kind of bug is very easy to miss on codes.


Just like "IF"s, so can REPEAT - UNTIL commands be multiple inside themselves, and you can have even multiple "IF"s inside REPEAT - UNTILs as well as multiple REPEAT - UNTILs inside "IF"s, there is no limit to how many of each others can be inside each other. You can think them as bit like subdirectories, there is no limit how many directories (or Folders for windows users) there can be inside any directory. Each REPEAT - UNTIL and each IF is its own directory, which might contain as many REPEAT - UNTIL and IF directories inside themselves as you ever like.

Code: Select all

  
    1:  A=-1
    2:  B=0
    3:
    4:  REPEAT
    5:     A=A+1
    6:     B=0
    7:     REPEAT
    8:        Print(A..B.."\n")
    9:        B=B+1
    10:    UNTIL B=10
    11: UNTIL A=10
    12: wait(50)

->01
->02
->03
...
->10
->11
->12
... all the way until 99


What happens in this is that at beginning, lines 1 and 2 A is assigned value of -1 and B is assigned with value 0.

Then comes the first REPEAT at line 4, which keeps cycling through lines 5 and 6. Each time this cycle happens, A will increase its value by 1, and B will get back to 0 (actually, line 2s initialization of B is not even necessary since same happens here anyway)

But before this lines 4 and 5 cycle is repeated, there will first be execute another REPEAT - UNTIL, which cycles through lines 8 and 9.

Line 8 prints out current values of A and B, effectively making it 00, 01, 02... (A being 0 for the duration of the whole REPEAT - UNTIL, B being the number changing, which is also the reason why we had to make the A:s value as -1 at beginning, since before it is executed even once, A:s value is already increased by 1, which at beginning means, it becomes 0)
When B reaches 10, it will end the REPEAT - UNTIL, and continue the previous REPEAT - UNTIL. However, part of previous parts cycle, was this second REPEAT - UNTIL, and it will be executed again and again, until A reaches 10 as well.


By other words, first cycle is executed only 10 times, while second cycle is executed 100 times in total (10 times the 10 cycles).


In case that sounded bit complicated, I will explain this with directory parallel as well.

At start we assign A as -1 and B as 0. Then we go to first REPEAT - UNTIL directory, Which i will call LOOP1.

In loop 1, it will first increase A:s value by 1, making it become 0, and also set B:s value to 0, regardless what Bs value was before that.

Then it goes to next REPEAT - UNTIL directory, called LOOP2, which resides inside LOOP1-directory, effectively meaning, that LOOP2 is a subdirectory of LOOP1.

In LOOP2 it keeps printing current values of A and B, as well as increasing B:s value until it is 10, and then it returns to its parent directory, which is LOOP1, and does the same thing again as LOOP1 directory is supposed to do, which is to increase A:s value by 1, making it become 1, as well as making B become 0 again, and to execute the LOOP2 directory again. And this will be repeated until A reaches 10 as well, at which point it goes to LOOP1:s parent directory and in practice ends the program.


There could be even more REPEAT - UNTILs inside these, as well as IF statements and any other Hollywood program flow control commands, for there is no limit as to how many directories and subdirectories there be inside any directory/subdirectories (to use the directory parallel).


In examples before, I have been using variable equals something, but there is also other options instead of direct comparison of value. You could also use statements like "Greater than" or "Less than" for example. There are some obvious uses for these kind of statements, for example on untils.

Example:

Code: Select all

    REPEAT
      A=A+2
    UNTIL A=10

This works fine as long as A is for example -2, 0, 2, 4... But what if A is for example 1 when it enters this REPEAT - UNTIL cycle?

Surely idea is that when A reaches 10, it would continue forward, but now it would never reach exact 10, since it would first become 9, then 11, 13, 15... making the UNTIL statement unintentionally never true. Or what if A is already bigger than 10 when it reaches that point? Same problem again.

Instead of comparing exactly, we can use Relative comparing.

Code: Select all

    REPEAT
      A = A + 2
    UNTIL A > 10

Now it compares if A is bigger than 10, if it is, regardless of how much bigger it is, it will continue executing the program.

Now cycle works fine, regardless if A is 1 or above 10 when it enters the cycle.


With these basic commands, we are already able to make a simple PONG game.

Naturally, I wouldnt really make a Pong game this way, but would use different technics to achieve the same, but I by purpose kept it simple and with minimal amount of commands.

There are commands that I havent gone through yet, but they are much simpler from their nature than these basic programming commands I have went through now.

I will keep explaining place by place what I am doing and why, as well as explain the commands that havent been explained yet:

Code: Select all

    @SCREEN {Mode = "FakeFullScreen"}
    @DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}


    stickwidth = 40
    stickheight = 200

    playerx = 1800
    playery = 500

    computerx = 110
    computery = 500
    computerspeed = 3

    ballx = 930
    bally = 500
    ballsize = 30
    ballstartspeed = 1
    ballspeed = ballstartspeed
    ballspeedincrease = 1.10
    ballxdirection = "right"
    ballydirection = "up"

    top = 0
    bottom = 1050
    leftedge = 50
    rightedge = 1900

    playerscore = 0
    computerscore = 0


    quit = 0



    BeginDoubleBuffer()


    Repeat

    Cls(#BLACK)



    If ballxdirection = "right"
       ballx = ballx + ballspeed
       If ballx > rightedge
          ballx = 1000
          bally = 500
          ballspeed = ballstartspeed
          computerscore = computerscore + 1
       EndIf
    ElseIf ballxdirection = "left"
       ballx = ballx - ballspeed
       If ballx < leftedge
          ballx = 1000
          bally = 500
          ballspeed = ballstartspeed
          playerscore = playerscore + 1
       EndIf
    EndIf



    If ballydirection = "down"
       bally = bally + ballspeed
       If bally > bottom
          ballydirection = "up"
       EndIf
    Else
       bally = bally - ballspeed
       If bally < top
          ballydirection = "down"
       EndIf
    EndIf



    If computery < bally
       computery = computery + computerspeed
    ElseIf computery > bally
       computery = computery - computerspeed
    Else
        /*nothing*/
    EndIf


    playery = MouseY()



    Box(ballx, bally, ballsize, ballsize, #WHITE)
    Box(computerx, computery, stickwidth, stickheight, #WHITE)
    Box(playerx, playery, stickwidth, stickheight, #WHITE)   
    TextOut(300, 30, computerscore)
    TextOut(1630, 30, playerscore)





    col = Collision(#BOX, ballx, bally, ballsize, ballsize, computerx, computery, stickwidth, stickheight)
    If col = 1
       ballxdirection = "right"
       ballspeed = ballspeed * ballspeedincrease
    EndIf

    col = Collision(#BOX, ballx, bally, ballsize, ballsize, playerx, playery, stickwidth, stickheight)
    If col = 1
       ballxdirection = "left"
       ballspeed = ballspeed * ballspeedincrease
    EndIf


    Flip()


    If IsRightMouse()=1 Then quit = 1
       
    Until quit=1



Two first lines are

Code: Select all

    @SCREEN {Mode = "FakeFullScreen"}
    @DISPLAY {Width = 1920, Height = 1080, Borderless = True, ScaleMode = #SCALEMODE_AUTO, FitScale=True}


These are both commands that tell us what kind of screen to open. FakeFullScreen means that it is opening a window of a size that occupies your whole screen, and while it is actually a window (you can even drag it around), it looks like a screen. (There is difference between screen and window, which I am not going into at this)
@DISPLAY on the other hand tells other stuff about your display, like that the resolution is HD resolution (1920x1080), borderless means that there are no visible borders seeable, SCALEMODE_AUTO tells that regardless of the size of your actual display, it will always automatically stretch it to right size. So if you have screensize of 640x256, it will shrink the picture to right size to fit your screen, and similarly, if your resolution is bigger, it will stretch the images to fit it right.

You dont really need to understand those two lines at this point yet. But they are necessary to be put in some way, so just copy paste them from here and you can perhaps try to fiddle with the numbers a bit just for experimentings sake.

After these, there are many Variables I am initializing for the program. I am putting all the variables to beginning, so it is easiest to see what variables are being used, as well as change them. For example, you could try changing the value of ballspeed or computerspeed and see what happens. Notice that some variables are shared. What I mean by this, is that for example size of the pongstick is same for both computers and players pongstick, hence I have only decided to use one stickwidth variable, which i use to put both players as well as computers stick. Therefore, if you change this value, size of both players as well as computers pongstick changes.



Notice the "Quit = 0" part.

This is how I have put QUIT option for player. Following lines are important for it:

Code: Select all

    QUIT = 0
    REPEAT
    if isrightmouse() = 1 then QUIT = 1
    UNTIL QUIT=1
What happens is, that first it is told that value of that the variable "QUIT" holds, is 0.

Then comes the REPEAT - UNTIL. Lines between these make the actual game, and these lines are repeatedly being executed to make the game move.

UNTIL condition is that if QUIT equals 1, then it will continue forward (and end the program, since there isnt anything else there beyond that point left to execute)

Only way to make QUIT become 1, is by clicking Right Mouse Button.

isrightmouse() tells the state of the right mouse button. It works in states of 0 and 1. 0 means that it is not pressed, 1 means, it is being pressed.

What that line is effectively doing, is checking if Right Mouse button is being pressed (IsRightMouse() = 1) and if that happens, then it changes the value of QUIT to 1, which means that when REPEAT - UNTIL cycle reaches UNTIL part, it will continue out of the cycle and effectively ends the program.



Next I am explaining the movement. Movement is very simple in this. Ball moves only to 4 different directions. Upleft, upright, downleft, downright, and actually i have divided even this into two different parts. Which is that ball is moving either up or down, as well as it is at same time moving also to either left or right.

I am using variables ballXdirection and ballYdirection to tell which direction ball should be moving to.

Code: Select all

    If ballxdirection = "right"
       ballx = ballx + ballspeed

If ball is moving to "right", then it should increase its ballx. To fully understand this, you need to understand bit about screens.

For example, if display is FULL HD display with 1920 x 1080 resolution, it means that there are 1920 different spots where something can be drawn on vertical (x) - line.

Displays work so, that location 1 is to the left edge, and location 1920 is to the right edge of the screen. Therefore, if i want to move something towards right, I need to draw it to bigger numbered location. Therefore ballx = ballx + 1 moves ball one pixel towards right.

Similarly Horizontally (y) numbers work so that number 1 is being on top of the screen, and bigger numbers are at bottom of screen. location (1,1) is therefore at topleft corner, while location (1920,1080) is at bottom right corner.


You may also notice, that I didnt use ballx = ballx + 1, but isntead used, ballx = ballx + BALLSPEED.

This is so that I can have better control at what is the speed of the ball, as well as have possiblity to increase balls movement speed during play. At beginning Ballspeed is 1, hence at beginning of game, saying ballx = ballx + 1, is true, although at later point when ballspeed have increased, it isnt anymore.


After I have moved the ball, there comes another thing I have to check, did it reach the right edge yet?

Code: Select all

    If ballx > rightedge
          ballx = 1000
          bally = 500
          ballspeed = ballstartspeed
          computerscore = computerscore + 1
       EndIf
Therefore if ballx is bigger than the rightedge (rightedge being decided at beginning of code as one of the variables) then it means goal have happened. Since I have fixed it so that computer is always on left side of screen and player at right side, we will know that if ballx reaches the right edge, then it means the computer is scoring one point. Similarly if left edge is reached, we know that player have scored one point.

If this happens, then we first decide that ballX = 1000 and ballY = 500, effectively making ball go to about middle of screen.
ballspeed = ballstartspeed. Reason for ballstartspeed is, that during the game, ballspeed keeps getting higher. By using this variable "ballstartspeed" we can simply change this number at beginning at variables place, and everytime score happens, get the balls speed to go back to the starting speed.

finally, I am using variable computerscore = computerscore + 1. This is simply the amount of points the computer has. Since we know this IF condition is true happens only when right edge is reached, it is clearly a point for computer.


In balldirectionY case, things work slightly different:

Code: Select all

    If ballydirection = "down"
       bally = bally + ballspeed
       If bally > bottom
          ballydirection = "up"
       EndIf

First check if ballYdirectiony = "down"

if this is the case, then move the ball similarly as with x we did.

After that once again check if y have reached the edge of screen (or in this case - bottom)

If y indeed have reached bottom, then in this case change the "balldirectiony" to "up", so the ball would next time go upwards. Similarly in Hitting top, code is similarly changing balldirectiony to become "down" again. Effectively making ball go from top to bottom and back forever and ever more, making it look like that if it hits the edge of screen, it bounces from it.



Then comes the simple AI that this game has:

Code: Select all

    If computery < bally
       computery = computery + computerspeed
    ElseIf computery > bally
       computery = computery - computerspeed
    Else
        /*nothing*/
    EndIf

This will simply compare computery with bally, if ball is higher than computerstick, then computerstick will aim at getting higher by amount of computer speed, and similarly if ball is at lower place than stick, then computery will go towards down to reach balls y.

In practice this means that computer simply follows the ball, and when speed of ball becomes higher than computers speed, then computer will fail to hit the ball anymore.

notice the ELSE statement. There are two things to learn here.

First is the "/*" and "*/", this is so called comment tag. "/*" starts the commenting, which means that we can write anything we like after that one without affecting the code in any way, since it is simply ignored by Hollywood, and then the "*/" ends it and after that point all that is written, will be executed as normal code again.


But more importantly, there are now cases of computer y and ball y are bigger or smaller, but what if it is the same? This ELSE is there to handle that, although there isnt really anything to handle since idea is that computer stays still, in which case no values of any variables are changed.

Basically there isnt even any need for that ELSE, but I put it there just for educational purposes.

Now that programs logic is explained, lets go to graphics part:

Code: Select all

    Box(ballx, bally, ballsize, ballsize, #WHITE)
    Box(computerx, computery, stickwidth, stickheight, #WHITE)
    Box(playerx, playery, stickwidth, stickheight, #WHITE)   
    TextOut(300, 30, computerscore)
    TextOut(1630, 30, playerscore)

At this point, we are simply drawing 3 boxes (computer pongstick, playerpongstick and ball) as well as displaying scores using TextOut.

TextOut works simple. you tell X and Y spot where you want to display the text, in this case 300, 30 and 1630, 30, and then you tell what text to print there, which in this case is number variable holding computers and players current score. Notice by the way, that the x,y - cordinates tells from where to start the writing, hence for example spot 300, 30 means, that no text will ever go under spot 300 in x, or under spot 30 in y, since everything is going to higher spots than those (meaning it will write towards left and down of that spot, and never to left and up). In practice 300,30 is the most leftup corner the text ever reaches in this case.

BOX commands arent really any more difficult. First you simply tell where in screen you want to place them by giving X and Y co-ordinates. Two next numbers tell the width and height of the box you wish to draw. Last detail is the color of the box, which i in this case used #WHITE, which is a so called reserved word for white color.

Since ball is simply a rectangle, I am using for both its width as well as height single variable "ballsize". You change that, and it becomes smaller or bigger rectangle.

Since pong sticks size is same for player as well as computer, I have simply used stickwidth and stickheight which i use for both computer as well as player.

Notice that playerx and computerx are actually static. During the program execution, they never change. Sticks only move in Y.

Similarly the line

Code: Select all

    playery = MouseY()
is simply taking Mouse pointers Y-coordinate and telling to use that same Y as players Y, and that way when ever you move your mouse in Y-axis, so will the stick move too.

practically whole movement of players pong stick is handled in:

Code: Select all

    repeat
    cls(#BLACK)
    playery = MouseY()
    Box(playerx, playery, stickwidth, stickheight, #WHITE)
    until quit=1


After all the graphics are drawn, we are going to Collision testing to see if one of the pong sticks have touched the ball:

Code: Select all

    col = Collision(#BOX, ballx, bally, ballsize, ballsize, computerx, computery, stickwidth, stickheight)
    If col = 1
       ballxdirection = "right"
       ballspeed = ballspeed * ballspeedincrease
    EndIf


"col" is simply a variable to handle the result of collision test (which is either 0 - no collision, or 1 - collision)

Collision command works so, that first we are telling what kind of collision we are checking for, in this case we are looking wether two rectangular areas are colliding with each other (we wouldnt actually even need to draw anything to check this, since this doesnt check if any two images are colliding, just if two defined areas are touching).

Then we first tell the x and y of the first rectangles area, and then the width and height of it. Then similarly second boxes x and y and width and height. After this Hollywood calculates if these rectangles being in given coordinates, would collide or not, regardless if these boxes really exist or not.

If collision did happen, then it goes inside the IF statements execution. Since this is very simple game, we know that for example in this case, if ball touches computers pongstick, then we already know the ball was travelling from right to left, and hence we can simply tell it to change its direction to go towards right from now on.

In addition I am also increasing balls moving speed. This is handled by using ballspeedincrease variable, which works with simple mathematics.

I am using 10 percent increase on each hit in this program. I could do it some other way, but easiest way to get 10 percent increase mathematically, is by multiplying the number in question by 1.10.

For example 100 X 1.10 = 110 (which is 10 percent more), 110 x 1.10 = 121 x 1.10 = 133 x 1.10 = 146 x 1.10 = 160...


As a last thing, our graphics explanation is not complete. There are still:

Code: Select all

    Cls(#BLACK)
    BeginDoubleBuffer()
    and Flip()
unexplained.

Cls comes from words "Clear Screen", and it does exactly that. It will make the whole screen become of the defined color (in this case Black), making it look like screen is cleared.

This is necessary, since each time we draw a box, it would simply be drawn on top of the previous ones, but by clearing the screen between these drawings, it makes it look like these boxes are moving, although in reality they are not really moving, since they are simply just redrawn again and again in different spots, which we then think as being movement.

You can try and remove the CLS line and see for yourself what happens.


However, this is not so simple a thing as simply putting CLS command, since without Doublebuffer and Flip, it would work so, that you would see each thing happen in real time: First, screen being cleaned, then, each rectangle being drawn separately. This would most likely cause annoying flickering (depending upon your hardware etc.)

Therefore there is this BeginDoubleBuffer() and Flip()

You first start with BeginDoubleBugger() (and remember to do it before the graphics REPEAT - UNTIL loop starts, since you are supposed to do it only once), and after that instead of drawing stuff visibly, Hollywood will draw everything on background, unseeable to player.

When Flip() is used, it will update the screen to the current one.

You could think it as if someone would keep drawing boxes on paper, that you can either have it so that he takes clean paper and starts drawing in front of you the whole process, or then he instead shows you the paper only after he have already finished drawing everything there. By other words, flipping next page in front of you only after he have already finished it.

This is practically same thing that is happening with Double Buffering.


This was the simple PONG. Experiment with the numbers and see what happens.


I however, at this end still want to put one more thing that i by purpose left out of this example, but which fixes one problem.

Currently all the movement is based upon ball and computer moving some fixed amount each cycle. However, How fast is one cycle depends upon your machine. Very slow machine, and it is deadbeat slow, and very fast machine, and you will lose before you even notice it.

You can see this same problem with old Sierra games. They had slow, normal, fast, fastest speed as options, if you chose the fastest, they were going as fast as they could, and with modern machines, thats real fast. fast enough that you wont even see the screen when its already changed to next one (since your character moved to the edge already).


There is a fix for this. Instead of tweaking with numbers on each different machine trying to find suitable speed, we can use Clock to speed everything in such way that regardless how fast or slow your machine is, it will always work the same speed for everyone.


First of all, add following line right before REPEAT

Code: Select all

    StartTimer(1)

This tells computer to start Clock number 1. This is like someone in sports starting the clock to time how fast people are running 100 meters.

Next, as last thing before UNTIL you put following:

Code: Select all

    Repeat
    timepassed = GetTimer(1)
    Until timepassed > 3
    ResetTimer(1)

    timepassed = timepassed / 1000


REPEAT - UNTIL loop is there constantly looping, until at least 4 time units have passed. "timepassed" variable saves the current amount of time passed since clock was started/reseted, and if it isnt at least 4, it keeps continuing to do so, until clock shows at least 4.

After UNTIL condition is met, there comes the ResetTimer(1), which tells Hollywood to put clock number 1 back to 0, by otherwords - start calculating from beginning. This way we will each time get a new time that have passed since the last time the cycle was completed.

Notice that it is very important that right after GetTimer - as soon as possible - ResetTimer is executed.

one thing that tempts programmingwise would be to first have the line "timepassed = timepassed / 1000" before "ResetTimer", but if these two would swap their places, then that would mean, that if for some reason machine would get stuck on this line, Clock would miss it since it is still using the old clock timing, which is not going to be saved anymore, but instead, will soon be reseted, and hence game would move at wrong speed.

Reason for this "timepassed = timepassed / 1000"-line is, that first of all, 1000 time units, is same as 1 second of time passed.

This line is for mathematical reasons to make things later simpler. This has similar idea as the 1.10 multiplier to get the 10 percent increase.

In this case, lets say time have passed 100 units, that is 1/10th of a second. By dividing this number by 1 000, we will get 0.1 as a result.

If some character is supposed to move at speed of 200 pixels per second, we can use following calculation: "characterx = characterx + (characterspeed * timepassed)", which would be: "characterx = characterx + (200 * 0.10)", which means, in 1/10th of a second, this character would move 20 pixels - which is exactly the amount it should move in that time.

Now that the timer is done, we still need to apply the system to all the movements (except to players movement, which speed is based upon the speed the player moves the mouse with). You have to change couple of lines:

Code: Select all

    ballx = ballx + ballspeed  ---> change into --->  ballx = ballx + (ballspeed * timepassed)
and similarly

Code: Select all

    ballx = ballx - (ballspeed * timepassed)
    bally = bally + (ballspeed * timepassed)
    bally = bally - (ballspeed * timepassed)
    computery = computery + (computerspeed * timepassed)
    computery = computery - (computerspeed * timepassed)

notice that speeds are now very different from original version. For example, ballspeed = 1 defined at beginning, will now mean that it moves 1 pixel each second, making the trip from one edge to other take about half an hour. Personally I think that is bit too slow, and I would recommend rather changing it into something like 300. Similarly Computer speed of 3, is deadbeat slow as well, perhaps 1000 for that now.

Keep experimenting with the code and numbers, and see what happens. Perhaps even take a test and try to make Computers and Players pongsticks be of different size.
Bugala
Posts: 1168
Joined: Sun Feb 14, 2010 7:11 pm

Re: Simple PONG-game tutorial for Beginner Coders

Post by Bugala »

Since it seems I didnt have edit-option available for my original post anymore, I had to post this new version as a new reply.

I havent checked those programs if they work or not. I trust you will check them out and report me if one of them fails to work correctly. I checked the text through, and made some alterations as well as additions. Also noticed couple of errors that i fixed.
Post Reply