I wrote this 3D Breakout game as a sample project to learn both Python and the visual module, vPython. It is based on a similar game I had on my Amiga back in the 80s. I don't expect that my Python is either standard or as elegant as it could be so constructive feedback is always appreciated.
Breakout 3D Sample vPython App
#########################################################################################
# #
# Name: #
# #
# Breakout3D.py #
# #
# Description: #
# #
# A 3D version of Breakout (requires red/cyan glasses). Black out the bricks on 4 #
# walls to win. #
# #
# Options: #
# #
# Change the following variables in the code to modify the game #
# #
# NO_LOSE_MODE = False #set True to make floor solid (self play mode) #
# SHOW_TRAIL = True #set True to make ball leave temporary trail #
# SHOW_COORDS = False #set True to display ball coordinates #
# #
# Future: #
# #
# Randomize start velocity on ball launch #
# Allow above options to be specified on the command line #
# Change velocity vector depending on what part of the paddle is hit. #
# Add shadow as training aid #
# #
# Audit: #
# #
# 2011-09-05 added NO_LOSE_MODE, SHOW_TRAIL & SHOW_COORDS options #
# change ball radius based on z coordinate to improve depth perception #
# ball is red if coming at you, green if going away #
# 2011-09-04 R J de Graff original code #
# #
#########################################################################################
from visual import *
def SideBrick(bricks,y,z): #check if a left or right side brick was hit
halfy = bricks[0].height / 2
halfz = bricks[0].width / 2
for brick in bricks:
if brick.visible:
if brick.y-halfy <= y <= brick.y+halfy and brick.z-halfz <= z <= brick.z+halfz:
return True,brick
return False,""
def TopBrick(bricks,x,z): #check if a top brick was hit
halfx = bricks[0].width / 2
halfz = bricks[0].length / 2
for brick in bricks:
if brick.visible:
if brick.x-halfx <= x <= brick.x+halfx and brick.z-halfz <= z <= brick.z+halfz:
return True,brick
return False,""
def BackBrick(bricks,x,y): #check if a back brick was hit
halfx = bricks[0].length / 2
halfy = bricks[0].height / 2
for brick in bricks:
if brick.visible:
if brick.x-halfx <= x <= brick.x+halfx and brick.y-halfy <= y <= brick.y+halfy:
return True,brick
return False,""
def UpdateStatus(status,numballs,numbricks):
status.text = "Balls=%d Bricks=%d" % (numballs,numbricks)
def Reset(bricks): #set all bricks visible
for brick in bricks:
brick.visible = True
return len(bricks)
def EmptyBuffer(myscene): #flush all buffered left clicks
while myscene.mouse.clicked > 0:
temp = myscene.mouse.getclick()
NO_LOSE_MODE = False #set True to make floor solid
SHOW_TRAIL = True #set True to make ball leave temporary trail
SHOW_COORDS = False #set True to display ball coordinates
brickLeft = [] #all bricks on the left wall
brickRight = [] #all bricks on the right wall
brickTop = [] #all bricks on the top wall
brickBack = [] #all bricks on the back wall
myscene = display(title="3D Breakout",width=1000,height=640,fullscreen=True)
myscene.select()
myscene.autoscale = True
myscene.userzoom = True
myscene.userspin = False
myscene.range = 360
myscene.cursor.visible = False
myscene.stereo = 'redcyan' #redblue yellowblue crosseyed passive active
wallLeft = -250 #x coordinate of the left wall
wallRight = 250 #x coordinate of the right wall
wallTop = 150 #y coordinate of the ceiling
wallBottom = -150 #y coordinate of the floor
wallBack = -300 #z coordinate of the back wall
wallFront = 200 #z coordinate of the front wall
brickColor = (0.7,0.7,1)
for x in range(-200,201,100): #draw the bricks on the top
for z in range(-260,141,100):
brickTop.append(box(pos=(x,wallTop,z),size=(95,0.1,95),color=brickColor))
for y in range(-120,121,60): #draw the bricks on the left and right
for z in range(-260,141,100):
brickLeft.append (box(pos=(wallLeft ,y,z),size=(0.1,55,95),color=brickColor))
brickRight.append(box(pos=(wallRight,y,z),size=(0.1,55,95),color=brickColor))
for x in range(-200,201,100): #draw the bricks on the back
for y in range(-120,121,60):
brickBack.append(box(pos=(x,y,wallBack),size=(95,55,0.1),color=brickColor))
paddle = box(pos=(0,wallBottom,0),size=(90,.1,90),color=(1,1,1))
ball = sphere (radius = 5.0, make_trail=False)
if SHOW_TRAIL:
ball.make_trail = True
ball.trail_type = "curve"
ball.interval = 10
ball.retain = 100
ball.mass = 1.0
ball.velocity = vector(0.15, 0.23, 0.27)
ball.visible = False
dt = 1.0 #0.5 is half normal speed and 2.0 is double normal
numbricks = Reset(brickLeft+brickRight+brickTop+brickBack)
numballs = 3
newball = True
hitfound = False
gameover = False
coords = label(text="",pos=(wallLeft+40,wallBottom+5,wallFront),height=20,color=(.5,.5,.5),box=False)
status = label(text="",pos=(0,wallBottom+5,wallFront),height=20,box=False)
status.text = "Left Click to launch or ESC to exit"
while true:
rate(400)
#update paddle position based on mouse position
px = min(max(2*myscene.mouse.pos[0],wallLeft+20),wallRight-20)
pz = -min(max(2*myscene.mouse.pos[1],wallBack),wallFront)-100
paddle.pos = (px,wallBottom,pz)
#If waiting to launch a new ball then idle until keypress. We have to poll rather than
#pause or we won't be able to move the paddle while we wait.
if gameover:
if myscene.mouse.clicked > 0:
numbricks = Reset(brickLeft+brickRight+brickTop+brickBack)
numballs = 3
newball = True
gameover = False
UpdateStatus(status,numballs,numbricks)
elif newball:
if myscene.mouse.clicked > 0:
newball = False
paddle.color = (1,1,1)
ball.pos = paddle.pos
ball.velocity = vector(0.15, 0.23, 0.27)
ball.visible = True
UpdateStatus(status,numballs,numbricks)
else:
#calculate new ball position and adjust size based on z position
ball.pos = ball.pos + ball.velocity * dt
ball.radius = 4 + (ball.z+400) / 100.0
if ball.velocity.z > 0:
ball.color = (1,0,0)
else:
ball.color = (0,1,0)
if SHOW_COORDS:
coords.text = "%4d %4d %4d" % (ball.x,ball.y,ball.z)
#check for hit on a visible brick
if ball.x <= wallLeft:
ball.velocity.x = -ball.velocity.x
hitfound,brick = SideBrick(brickLeft,ball.y,ball.z)
if ball.x >= wallRight:
ball.velocity.x = -ball.velocity.x
hitfound,brick = SideBrick(brickRight,ball.y,ball.z)
if ball.z <= wallBack:
ball.velocity.z = -ball.velocity.z
hitfound,brick = BackBrick(brickBack,ball.x,ball.y)
if ball.z >= wallFront:
ball.velocity.z = -ball.velocity.z
if ball.y >= wallTop:
ball.velocity.y = -ball.velocity.y
hitfound,brick = TopBrick(brickTop,ball.x,ball.z)
#if hit then clear brick
if hitfound:
brick.visible = False
hitfound = false
numbricks -= 1
UpdateStatus(status,numballs,numbricks)
#check if paddle hit or miss
if ball.y <= wallBottom and numballs > 0:
if NO_LOSE_MODE:
ball.velocity.y = -ball.velocity.y
else:
if paddle.x-45 <= ball.x <= paddle.x+45 and paddle.z-45 <= ball.z <= paddle.z+45 and wallBottom - ball.y <= 1:
ball.velocity.y = -ball.velocity.y
elif wallBottom - ball.y > 50:
ball.velocity.y = -ball.velocity.y
numballs -= 1
newball = (numballs > 0)
EmptyBuffer(myscene)
paddle.color = (1,1,0)
ball.visible = False
status.text = "Left Click to launch or ESC to exit"
#check if out of bricks (win) or balls (lose)
if not gameover and (numbricks == 0 or numballs == 0):
gameover = True
EmptyBuffer(myscene)
if numbricks == 0:
status.text = "Congratulations - You Win - Left Click for new game or ESC to exit"
else:
status.text = "Sorry - You've got no balls - Left Click for new game or ESC to exit"
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
Reverend Jim commented: This is a cumulative upvote for all the help on this thread. +4
Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
TrustyTony 888 ex-Moderator Team Colleague Featured Poster
Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.