Pong »
You do not own this project, so changes will not be saved
/**
* Pong
*
* @date 1/5/12
* @author Curtis Ullerich
*
* The intention of this game is to use as a tool
* for teaching programming concepts. This implementation
* is not perfect, but it's pretty solid. I wrote this in
* one sitting (a few hours) and it's my first game written
* from scratch, so it's pretty basic.
*
* Note about the AI: The "AI" player is very simple. It moves
* toward the ball's y position at a pseudorandom speed that is
* flighted based on the level's difficulty. In an attempt to
* make it easier (and possible at all) to beat, the AI paddle
* only moves when the ball is on the right half of the court.
* The biggest strategic aid to the human player is that the
* velocity of the ball is affected based on the point of contact
* with the paddle (human only). Hitting at an edge results in a
* 60% increase in speed and hitting in the center results in a 10%
* reduction in speed. Careful contact with the ball causes it to
* move faster than the AI is able to react. Any suggestions for
* improving this are welcome.
*
*/
$PADDLE_WIDTH = 10
$PADDLE_HEIGHT = 80
$SIM_OFFSET = 0
$PADDLE_SPEED = 7
$BALL_DIAMETER = 20 //yes, it's really a rectangle. Get over it.
$GAME_STATE = 0 //0 for new, 1 for in play, 2 for game over
$PLAYER_SCORE = 0
$SIM_SCORE = 0
$BOOP = new Sound("pong.wav")
$BOP = new Sound("pong2.wav")
$MAX_SCORE = 10
player = new Paddle(10, getScreenHeight() / 2, false)
sim = new Paddle(getScreenWidth()-10, getScreenHeight()/2, true)
ball = new Ball()
game = new Game()
timer = new Timer(1000)
onEachFrame() do
if !timer.isExpired() && $GAME_STATE != 0
ball.draw()
//wait
else
fill(0,0,0) //if you don't do this at each frame, you end up with trails everywhere
if $GAME_STATE == 0 //title screen
game.titleScreen()
else if $GAME_STATE == 1 //game currently in play
game.printScores()
ball.checkCollisions(player, sim)
ball.update()
player.update(ball.getX(), ball.getY())
sim.update(ball.getX(), ball.getY())
if (ball.checkPoint())
ball = new Ball()
timer.reset()
end
else //somebody won
game.endScreen()
end
end
end
//contains functions for printing title and ending screens,
//initializes scores, and updates scores
class Game
def new()
$PLAYER_SCORE = 0
$SIM_SCORE = 0
end
//doesn't seem to work at the moment.
//intended to be used after each score so a new ball isn't generated immediately
def delay(ms)
end
//the opening screen
def titleScreen()
setFont('Arial', 100)
fillText("Pong",getScreenWidth()/2,getScreenHeight()/2,true)
setFont('Arial',15)
fillText("Move your paddle with the arrow keys.\n\nScore ten points to win.\n\nPress any key to play.",getScreenWidth()/2,getScreenHeight()*4/5,true)
if (getControls().isKeyDown())
$GAME_STATE = 1
end
end
//after someone wins
def endScreen()
setFont('Arial', 100)
if $SIM_SCORE == $MAX_SCORE
fillText("You lose.", getScreenWidth()/2, getScreenHeight()/2,true)
end
if $PLAYER_SCORE == $MAX_SCORE
fillText("You win.",getScreenWidth()/2, getScreenHeight()/2,true)
end
setFont('Arial', 30)
fillText("Press space to play again.", getScreenWidth()/2,getScreenHeight()-40,true)
if (getControls().isKeyDown(:space))
$GAME_STATE = 1
$PLAYER_SCORE = 0
$SIM_SCORE = 0
end
end
//updates scores after each point
def printScores()
setFont('Arial', 50)
fillText($SIM_SCORE,getScreenWidth()*5/6, getScreenHeight()*5/6)
fillText($PLAYER_SCORE,getScreenWidth()/6, getScreenHeight()*5/6)
end
end
//A simple rectangular paddle. Either "ai" or human-controlled.
class Paddle
def new(x,y, isSim)
@x = x
@isSim = isSim
@y = y
@dY = $PADDLE_SPEED //the velocity of the paddle in pixels per frame
end
def getX()
return @x
end
def getY()
return @y
end
//updates the position of the paddle based on control or ai
def update(ball_x, ball_y)
//this logic determines how fast the ai paddle moves towards the ball.
//dependent on what the highest current score is
cap = $PLAYER_SCORE
if $SIM_SCORE > $PLAYER_SCORE
cap = $SIM_SCORE
end
if cap < 4
cap = 4
end
if @isSim //move paddle towards ball
if (@y < ball_y - 20 && ball_x > getScreenWidth() / 2)
@y = @y + cap + $SIM_OFFSET
end
if (@y > ball_y + 20 && ball_x > getScreenWidth() / 2)
@y = @y - cap - $SIM_OFFSET
end
end
if !@isSim //respond to user control
if (getControls().isKeyDown( :down ) && @y <= (getScreenHeight() - $PADDLE_HEIGHT / 2))
@y = @y + @dY
end
if (getControls().isKeyDown( :up ) && @y >= ($PADDLE_HEIGHT / 2) )
@y = @y - @dY
end
end
//draw the rectangle based on the updated coordinates.
//called once per frame in main loop
fillRect(@x, @y, $PADDLE_WIDTH, $PADDLE_HEIGHT,true)
end
end
//the ball. Actually a rectangle.
class Ball
def new()
@x = getScreenWidth() / 2
@y = getScreenHeight() / 2
//this determines the highest randomly generated speed
//for a ball at time of spawning
cap = $PLAYER_SCORE
if $SIM_SCORE > $PLAYER_SCORE
cap = $SIM_SCORE
end
if cap < 3
cap = 3
end
//randomly (50% of spawns) sends a ball
//left instead of right
pos = 1
if randBoolean()
pos = -1
end
//determines the initial velocity of the ball based on
//x and y delta values
@dX = pos * rand(2, cap)
@dY = pos * rand(2, cap)
end
//checks to see if the ball is out of bounds
//i.e. if there is a score to update
def checkPoint()
refresh = false
if @x < 0
$SIM_SCORE = $SIM_SCORE + 1
return true
end
if @x > getScreenWidth()
$PLAYER_SCORE = $PLAYER_SCORE + 1
return true
end
//ends the game if somebody reached ten points
if $SIM_SCORE == $MAX_SCORE || $PLAYER_SCORE == $MAX_SCORE
$GAME_STATE = 2
end
return false
end
//checks to see if a paddle has made contact with a ball
def checkCollisions(player, sim)
//checks the player's paddle
if (isRectOverlap( @x, @y, $BALL_DIAMETER, $BALL_DIAMETER, player.getX(), player.getY(), $PADDLE_WIDTH, $PADDLE_HEIGHT,true))
//reset the ball's x position so it's never "inside" the paddle, which causes multiple collisions
@x = player.getX()+$PADDLE_WIDTH/2+$BALL_DIAMETER/2
@dX = @dX * -1
offset = (player.getY() - @y).abs() / ($PADDLE_HEIGHT / 2)
@dX = @dX * (offset *.6 +.9)
@dY = @dY * (offset *.6 +.9)
$SIM_OFFSET = rand(-1,3)
$BOOP.play()
end
//checks the ai paddle
if (isRectOverlap( @x, @y, $BALL_DIAMETER, $BALL_DIAMETER, sim.getX(), sim.getY(), $PADDLE_WIDTH, $PADDLE_HEIGHT,true))
@dX = @dX * -1
$SIM_OFFSET = rand(-1,3)
$BOOP.play()
end
//checks upper and lower boundaries
if (@y > getScreenHeight() - $BALL_DIAMETER/2 || @y < $BALL_DIAMETER/2)
@dY = @dY * -1
$BOP.play()
end
end
def getX()
return @x
end
def getY()
return @y
end
def update()
@x = @x + @dX
@y = @y + @dY
fillRect(@x,@y,$BALL_DIAMETER, $BALL_DIAMETER,true)
end
def draw()
fillRect(@x,@y,$BALL_DIAMETER, $BALL_DIAMETER,true)
end
end
ERRORS
- ▶Language Reference
- ▶Core
- ▶Graphics
- ▶Image Class
- new
- clear
- clearTransforms
- clone
- drawCircle
- drawEllipse
- drawImage
- drawInnerSegment
- drawLine
- drawPixel
- drawPolygon
- drawRect
- drawRotatedImage
- drawRoundedRect
- drawSegment
- drawText
- fill
- fillCircle
- fillEllipse
- fillInnerSegment
- fillPolygon
- fillRect
- fillRoundedRect
- fillSegment
- fillText
- getAlpha
- getBlue
- getColors
- getGreen
- getHeight
- getPixel
- getPixelAlpha
- getPixelBlue
- getPixelGreen
- getPixelRed
- getRed
- getWidth
- isOverlap
- isPixelOverlap
- multAlpha
- rotate
- scale
- setAlpha
- setBlend
- setColor
- setFont
- setPixel
- setTransform
- size
- transform
- translate
- undoTransform
- ▶Transformations
- How Alignment Works
- Using Colors
- drawCircle
- drawEllipse
- drawImage
- drawInnerSegment
- drawLine
- drawPixel
- drawPolygon
- drawRect
- drawRotatedImage
- drawRoundedRect
- drawSegment
- drawText
- fill
- fillCircle
- fillEllipse
- fillInnerSegment
- fillPolygon
- fillRect
- fillRoundedRect
- fillSegment
- fillText
- getAlpha
- getBlue
- getColors
- getGreen
- getRed
- multAlpha
- setAlpha
- setBlend
- setColor
- setFont
- ▶Image Class
- ▶Audio
- ▶Controls
- ▶Collisions
- ▶Utility
- ▶Debugging
