Another Pong game :P
Hopefully it will help someone. It's my first real attempt at programming
anything in PB and it was quite fun :D
[pbcode]
; PROJECT : PbPong
; AUTHOR : Ken Tankerous
; CREATED : 10/4/2005
; EDITED : 10/9/2005
;
; ---------------------------------------------------------------------
; My version of Pong using PlayBasic - the main twist is the ball
; stays in play. Your opponent gains points if you allow the ball
; to hit the wall behind your paddle. You will also give your opponent
; more points if you do not get out of the way quick enough when the ball
; bounces off the wall behind you (the AI *tries* it's best to get out of
; the way). If the player or AI hits the ball with a moving paddle it will
; impart a bit of additional up/down movement rate to the ball, set with
; the Constant MOVEMULTIPLIER#, up to a point, at which the up/down
; movement rate is restored to the original rate. Starting movement rates
; (X and Y) are random and set by the min/max of Constants SPEEDX# and SPEEDY#.
; You can vary the match length with the Constant SCORELIMT.
;
; You can change the first 9 Constants to make the game easy or hard
; and the AI player better or useless.
;
; I purposely used sprites and sprite collisions so I could learn something
; about sprite functions in PB.
;
; I've tried to heavily comment the code with the intent of using my
; learning experience as a tutorial for others. This is my first try at
; doing something with PB.
; I'm open to suggestions/comments on things that could have been done
; better or if you would like me to clarify something.
; ---------------------------------------------------------------------
; =========================================================
; Set Global Variables
; =========================================================
Global Height = GetScreenHeight()
Global Width = GetScreenWidth()
Global MovX#, MovY#, MovYOrg#
Global LastCollision
Global LeftPaddlePos# = Width*0.10
Global RightPaddlePos# = Width*0.90
; =========================================================
; Set Game Constants
; =========================================================
; This first group of Constants effect how hard the game is
; by changing the ball speed with SPEEDX#, SPEEDY# and MOVEMULTIPLIER#
; and how good the AI player is by changing AIMOVESCALE#,
; R_REACTZONE# and L_REACTZONE#
Constant AIMOVESCALE# = 4;how fast AI moves paddle
Constant PLAYERMOVESCALE# = 8; how fast player moves paddle
Constant SPEEDX# = 7;max X movement rate for ball
Constant SPEEDY# = 4;max Y movement rate for ball & Y multiplier for paddle hit
Constant SCALEBALL = 5;ball size (small # - big ball)
Constant R_REACTZONE# = 0.60; % board length where AI player start to react
Constant L_REACTZONE# = 0.40; % board length where AI player start to react
Constant MOVEMULTIPLIER# = 1.25; multiplier for ball Y movement rate
Constant SCORELIMT = 5; game ends when a player reaches the number
;----------------------------------------------------------
Constant MANUAL = 1
Constant AUTO = 0
Constant RED = RGB(255,0,0)
Constant WHITE = RGB(255,255,255)
;----------------------------------------------------------
; Makes reading the sprite index easy
Constant BALL = 1
Constant LSIDE = 10
Constant RSIDE = 11
Constant TOP = 12
Constant BOTTOM = 13
Constant R_PADDLE = 14
Constant L_PADDLE = 15
Constant SCOREBOX = 16
;----------------------------------------------------------
; =========================================================
; Main Program Loop (can leave at anytime with Esc Key)
; =========================================================
While EscKey() = 0
; =========================================================
; Display the Main Menu
; =========================================================
Match = MainMenu()
; Match variable is returned with AI vs. AI or Human vs. AI
; if Human vs. AI then set the ReactZoneL to 100 which allows
; the human player to move paddle freely up and down any time
If Match = MANUAL
ReactZoneL# = 100
ReactZoneR# = R_REACTZONE#
Else
ReactZoneL# = L_REACTZONE#
ReactZoneR# = R_REACTZONE#
EndIf
; =========================================================
; Create Array to hold paddle Y positions used in VaryBallY()
; =========================================================
Dim PaddleMove#(3)
; =========================================================
; Create Array to hold each players score in PlayerScore(0)
; & PlayerScore(0)
; =========================================================
Dim PlayerScore(1)
; =========================================================
; Create Ball (send data to MakeSprite Function)
; =========================================================
;scale the ball size To the screen
Size=(Width-Height)/SCALEBALL
MakeSprite(Size/2,Size/2,Size/2,0,Size,Size,BALL,1,Width/2,Height/2)
; =========================================================
; Create Sides (send data to MakeSprite Function)
; =========================================================
MakeSprite(0,0,10,Height,10,Height,LSIDE,2,1,Height/2)
MakeSprite(0,0,10,Height,10,Height,RSIDE,2,Width-1,Height/2)
MakeSprite(0,0,Width,10,Width,10,TOP,3,Width/2,1)
MakeSprite(0,0,Width,10,Width,10,BOTTOM,3,Width/2,Height-5)
; =========================================================
; Create Paddles (send data to MakeSprite Function)
; =========================================================
MakeSprite(0,0,15,70,15,70,R_PADDLE,4,RightPaddlePos#,Height/2)
MakeSprite(0,0,15,70,15,70,L_PADDLE,4,LeftPaddlePos#,Height/2)
; =========================================================
; Create Score Box (send data to MakeSprite Function)
; =========================================================
MakeSprite(0,0,Width,50,200,50,SCOREBOX,0,Width/2,50)
; =========================================================
; Limit speed of the program To 60 Frames per second
; =========================================================
SetFPS 60
; =========================================================
; Get a random start movement For ball between + and - speed constants
; =========================================================
While ok = 0
MovX#= RndRange(Neg(SPEEDX#),SPEEDX#)
MovY#= RndRange(Neg(SPEEDY#),SPEEDY#)
; Speed can't be 0 For x or y direction
If MovX# = 0 Or MovY# = 0
ok = 0
Else
ok = 1
EndIf
EndWhile
; Store original Y movement variable For use in VaryBallY() function
MovYOrg# = MovY#
; =========================================================
; Main Game Loop (can leave at anytime with Esc Key)
; =========================================================
While EscKey() = 0
; Clear the Screen To back (0 = black)
Cls 0
; Draw all the sprites now
DrawAllSprites
; Loop To check collision events for setting movement/direction
; Loop will exit early if a collision is detected
For n = 10 To 15
If CollisonCheck(n) Then Exit
Next
; get input For Right paddle (always AI player)
MovePaddle(ReactZoneR#,AUTO, R_PADDLE)
; get input For left paddle (can be AI or Human based on Match variable
MovePaddle(ReactZoneL#,Match, L_PADDLE)
; move ball sprite
MoveSprite BALL,MovX#,MovY#
; display score board with score
ScoreBoard()
; check score and see if player wants to try again or quit
; if SCORELIMT is reached by either player
If PlayerScore(0) => SCORELIMT Or PlayerScore(1) => SCORELIMT
ShowExit()
;check For key press - looks for either the 1 or 2 key
RawKeyCode = ScanCode()
If RawKeyCode = 2
PlayerScore(0) = 0
PlayerScore(1) = 0
; Exit the game
End
EndIf
If RawKeyCode = 3
PlayerScore(0) = 0
PlayerScore(1) = 0
; Exit the game while loop and start new game
ExitWhile
EndIf
EndIf
; Display the screen And Loop back To the While statement To continue the loop
Sync
EndWhile
EndWhile
; =========================================================
; Paddle movement Function For player and AI
; AI and Player response are scaled using the AIMOVESCALE#
; and PLAYERMOVESCALE# Constants
; (larger number - better/faster response)
; the prior and new Y pos of the paddle are held in PaddleMove#()
; array and used in the VaryBallY function
; =========================================================
Function MovePaddle(Reactzone#, mode, paddle)
; used to set the approproiate index for the PaddleMove# array
; and to check to see if ball x pos is within the paddles
; permited reaction zone, set with passed variable Reactzone#
If paddle = R_PADDLE
If GetSpriteX(BALL) > Width*Reactzone# Then Move = 1
index0 = 0
index1 = 1
Else
If GetSpriteX(BALL) < Width*Reactzone# Then Move = 1
index0 = 2
index1 = 3
EndIf
; player paddle
If mode = MANUAL And Move = 1
; store the paddle Y pos before moving in PaddleMove#() array
PaddleMove#(index0) = GetSpriteY(paddle)
; Checks For up arrow key press & stops paddle movement at top
If UpKey() = 1 And GetSpriteY(paddle) > (Abs(GetSpriteHandleY(paddle))+5)
MoveSpriteY paddle, Neg(PLAYERMOVESCALE#)
EndIf
; Checks For down arrow key press & stops paddle movement at bottom
If DownKey() = 1 And GetSpriteY(paddle) < ((Height-5)-(Abs(GetSpriteHandleY(paddle))))
MoveSpriteY paddle, PLAYERMOVESCALE#
EndIf
; store the new paddle Y pos in the next element in PaddleMove#() array
PaddleMove#(index1) = GetSpriteY(paddle)
EndIf
; AI paddle (check against Ball Y position)
If mode = AUTO And Move = 1
; store the paddle Y pos before moving in PaddleMove#() array
PaddleMove#(index0) = GetSpriteY(paddle)
BallXPos = GetSpriteX(BALL)
BallY = GetSpriteY(BALL)
; AI paddles are moved based on the ball position -
; If in front move up/down toward ball Y pos
If BallXPos > LeftPaddlePos# And BallXPos < RightPaddlePos#
If paddle = L_Paddle
; if the ball is moving away from the left paddle move paddle toward center
If MovX# > 0
If PaddleMove#(index0)-Height/2 > 0 Then MoveSpriteY paddle, Neg(AIMOVESCALE#)
If PaddleMove#(index0)-Height/2 < 0 Then MoveSpriteY paddle, AIMOVESCALE#
; if the ball is moving toward the left paddle move paddle toward ball
Else
If PaddleMove#(index0)-BallY > 0 Then MoveSpriteY paddle, Neg(AIMOVESCALE#)
If PaddleMove#(index0)-BallY < 0 Then MoveSpriteY paddle, AIMOVESCALE#
EndIf
EndIf
If paddle = R_Paddle
; if the ball is moving away from the right paddle move paddle toward center
If MovX# < 0
If PaddleMove#(index0)-Height/2 > 0 Then MoveSpriteY paddle, Neg(AIMOVESCALE#)
If PaddleMove#(index0)-Height/2 < 0 Then MoveSpriteY paddle, AIMOVESCALE#
; if the ball is moving toward the right paddle move paddle toward ball
Else
If PaddleMove#(index0)-BallY > 0 Then MoveSpriteY paddle, Neg(AIMOVESCALE#)
If PaddleMove#(index0)-BallY < 0 Then MoveSpriteY paddle, AIMOVESCALE#
EndIf
EndIf
; If ball is behind move up/down away from ball Y pos (avoids repeat collision with back wall)
Else
If PaddleMove#(index0)-BallY > 0 Then MoveSpriteY paddle, (AIMOVESCALE#)
If PaddleMove#(index0)-BallY < 0 Then MoveSpriteY paddle, Neg(AIMOVESCALE#)
EndIf
; store the new paddle Y pos in the next element in PaddleMove#() array
PaddleMove#(index1) = GetSpriteY(paddle)
EndIf
EndFunction
; =========================================================
; Checks For collisions between ball And paddles/walls
; Sets movement direction based On collision
; (i.e. If ball collides with bottom Or top X movement does
; Not change, but Y movement is reversed, If ball collides
; with left Or right wall Or paddle Y movement does Not change
; but X movement reverses)
; =========================================================
Function CollisonCheck(SpriteIndex)
ThisSprite = SpriteHit(BALL,SpriteIndex,GetSpriteCollisionClass(SpriteIndex))
; Insures that a repeated collision with the same object does not happen
; (i.e. ball movement should only change If the last collision object is
; different than the current collision object)
If ThisSprite <> 0 And ThisSprite <> LastCollision
Select ThisSprite
Case LSIDE
;Inc(PlayerScore(0))
PlayerScore(0) = PlayerScore(0) + 1
MovX# = Neg(MovX#)
Case RSIDE
;Inc(PlayerScore(1))
PlayerScore(1) = PlayerScore(1) + 1
MovX# = Neg(MovX#)
Case TOP
MovY# = Neg(MovY#)
Case BOTTOM
MovY# = Neg(MovY#)
Case R_PADDLE
MovX# = Neg(MovX#)
VaryBallY(ThisSprite)
Case L_PADDLE
MovX# = Neg(MovX#)
VaryBallY(ThisSprite)
EndSelect
EndIf
; There was a hit, No need To check anymore this cycle
If ThisSprite > 1
LastCollision = ThisSprite
Exitfunction True
EndIf
EndFunction False
; =========================================================
; Used To add some variability to the ball movement by checking
; If paddle was moved since last cycle - If yes use multiplier on current
; ball Y movement rate as long as it is below the originial Y move rate
; times the max Y speed set with constant SPEEDY# (will get real fast
; if SPEEDY# or MOVEMULTIPLIER# are large)
; =========================================================
Function VaryBallY(ThisSprite)
; used to set the approproiate index for the PaddleMove# array testing
If ThisSprite = R_PADDLE
index0 = 0
index1 = 1
Else
index0 = 2
index1 = 3
EndIf
If Abs(MovY#) < Abs(MovYOrg# * SPEEDY#)
If (PaddleMove#(index0)-PaddleMove#(index1) <> 0)
MovY# = MovY# * MOVEMULTIPLIER#
EndIf
Else
If MovY# < 0
MovY# = Neg(Abs(MovYOrg#))
Else
MovY# = Abs(MovYOrg#)
EndIf
EndIf
EndFunction
; =========================================================
; Do all the sprite creation stuff For walls, ball And paddles
; =========================================================
Function MakeSprite(Tx,Ty,Bx,By,ImgW,ImgH,SpriteNum,CollisionClass,Xpos,Ypos)
; Get the Image Index that is currently free for use
Image = GetFreeImage()
; Creates a blank image in memory read for use using Image
CreateImage Image,ImgW,ImgH
; directs drawing operations to Image rather than the screen
RenderToImage Image
;BoxC and CircleC draw a color filled box and circle
If By > 0
BoxC Tx,Ty,Bx,By,1,WHITE
Else
CircleC Tx,Ty,Bx,1,WHITE
EndIf
; directs all of drawing operations to the screen
RenderToScreen
CreateSprite SpriteNum
; Center this sprite's handle for its assigned image
AutoCenterSpriteHandle SpriteNum,True
; Set each Sprite To use image for its artwork
SpriteImage SpriteNum,Image
; Enable Collision for Sprite
SpriteCollision SpriteNum,On
;set the position of a sprite to specific X And Y position
PositionSprite SpriteNum,Xpos,Ypos
; Set Sprite's collision Class
SpriteCollisionClass SpriteNum,CollisionClass
; Display outline of Sprite's collision mode region (testing only)
SpriteCollisionDebug SpriteNum, 0
EndFunction
; =========================================================
; Score Board Display
; Uses PlayerScore1 And PlayerScore2 variables that are
; set in the CollisonCheck() Function each time ball hits
; wall On left Or right side (i.e. missed by paddle)
; =========================================================
Function ScoreBoard()
Ink RED
Text (Width/2)-80, 30, "Player 1"
Text (Width/2)+20, 30, "Player 2"
Text (Width/2)-55, 55, Str$(PlayerScore(1))
Text (Width/2)+45, 55, Str$(PlayerScore(0))
EndFunction
; =========================================================
; Final Score Display
; Displays the final score And the winner
; =========================================================
Function ShowExit()
; use ScoreBoard() function to set the display
ScoreBoard()
If PlayerScore(0) < PlayerScore(1)
Text (Width/2)-79, 15, "Winner"
Else
Text (Width/2)+21, 15, "Winner"
EndIf
Text (Width/2)-60, Height/2, "Press 1 To Exit"
Text (Width/2)-60, Height/2+20, "Press 2 To Try Another"
;delete all sprites (ball, paddles and walls)
DeleteALLSprites
EndFunction
; =========================================================
; Main Menu Display
; Asks user to choose between Human vs. AI or AI vs. AI
; using mouse click in the appropriate box
; Returns the choice to the main program loop using the variable Match
; =========================================================
Function MainMenu()
Cls 0
; set the 2 box locations based on screen size
Box1X1 = Width*0.20
Box1Y1 = Height*0.45
Box1X2 = Width*0.40
Box1Y2 = Height*0.55
Box2X1 = Width*0.60
Box2Y1 = Height*0.45
Box2X2 = Width*0.80
Box2Y2 = Height*0.55
; display the 2 boxes in white
BoxC Box1X1, Box1Y1, Box1X2, Box1Y2, 1, WHITE
BoxC Box2X1, Box2Y1, Box2X2, Box2Y2, 1, WHITE
; place red text inside the boxes and as title and user help
Ink RED
CenterText Width/2, Height/2*(0.25), "PLAYBASIC PONG"
CenterText ((Box1X2-Box1X1)/2)+Box1X1, ((Box1Y2-Box1Y1)/2)+Box1Y1, "Human vs AI"
CenterText ((Box2X2-Box2X1)/2)+Box2X1, ((Box2Y2-Box2Y1)/2)+Box2Y1, "AI vs AI"
CenterText Width/2, Height/2*(1.75), "Choose the type of Game"
Sync
; loop until left mouse click is detected inside one of the boxes
While (Result1 = 0 And Result2 = 0)
If MouseButton()=1
Xpos = MouseX()
Ypos = MouseY()
EndIf
Result1 = PointInBox(Xpos, Ypos, Box1X1, Box1Y1, Box1X2, Box1Y2)
Result2 = PointInBox(Xpos, Ypos, Box2X1, Box2Y1, Box2X2, Box2Y2)
EndWhile
; store the result of the player choice in the match variable & return in to the main loop
If Result1 = 1 Then Match = MANUAL
If Result2 = 1 Then Match = AUTO
EndFunction Match
[/pbcode]