I thought I'd play around with vPython so I wrote a 3D breakout game (I used to have one on my Amiga and I haven't seen one for Windows). Everything is done except for one wee problem. I want to idle while waiting for the user to click the left mouse button. Because the user must still be able to move the paddle (to position the shot for a new ball), I can't just do a getclick or getevent (which pauses the loop). I tried

if newball:

  if myscene.mouse.clicked:
      do stuff

because events are not being cleared from the queue even if I do
myscene.mouse.events = 0
which is supposed to clear out any queued mouse events

Similarly I can't just idle until myscene.mouse.button = "left" because this attribute never seems to be updated as the following program will show

from visual import *

myscene = display(title="test",width=500,height=320)

status = label(text="test",color=(1,0,0))

while True:
    rate(100)
    if myscene.mouse.button is None:
        status.text = "None"
    else:
        status.text = myscene.mouse.button

On my machine (Windows 7 Pro, Active Python 2.7.2.5, vPython 5.71) the status stays frozen on "None". Is this a bug in vPython? Does anyone have a suggestion on either what I am doing wrong or how to work around the bug if I am not?

I got this way also right mouse event visible, but mouse wheel still not and middle event came from 'chord middle' ie both left and right down simultanously:

from visual import *

myscene = display(title="test",width=500,height=320)
myscene.userspin = myscene.userzoom = False

status = label(text="test",color=(1,0,0))

while True:
    rate(100)
    for count in range(myscene.mouse.events):
        m = myscene.mouse.getevent()
        status.text = "%s %s %s: %s" % (m.button, m.drag, m.drop, m.pos)

It seems the mouse.button not updating thing is a bug, however, I have a workaround.

def EmptyBuffer (myscene):
    while myscene.mouse.clicked > 0:
        temp = myscene,mouse.getclick()
.
.
.
while True:
.
.
.
    if gameover:
    
        if myscene.mouse.clicked > 0:
            numbricks = Reset(brickLeft+brickRight+brickTop+brickBack)
            numballs  = 3
            gameover  = False
            UpdateStatus(status,numballs,numbricks)

as long as the section of code that sets gameover to True also calls EmptyBuffer then everything works fine.

If anyone is interested here is the complete 3D Breakout code. Constructive comments are always appreciated.

#########################################################################################
#                                                                                       #
#  Name:                                                                                #
#                                                                                       #
#    Breakout3D.py                                                                      #
#                                                                                       #
#  Description:                                                                         #
#                                                                                       #
#    A 3D version of Breakout (requires red/cyan glasses). Black out the bricks on 4    #
#    walls to win.                                                                      #
#                                                                                       #
#  Future:                                                                              #
#                                                                                       #
#    Change velocity vector depending on what part of the paddle is hit.                #
#                                                                                       #
#  Audit:                                                                               #
#                                                                                       #
#    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()

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'

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))
status = label(text="",pos=(0,wallBottom+5,wallFront),height=20)

ball = sphere (material=materials.shiny, radius = 5.0)
ball.mass = 1.0
ball.velocity = vector(0.15, 0.23, 0.27)
ball.visible = False
#ball.trail = curve(color=(1,0,1))          #uncomment to see ball path

dt = 1.0

numbricks = Reset(brickLeft+brickRight+brickTop+brickBack)
numballs  = 3

status.text = "Left Click to launch or ESC to exit"

newball  = True
hitfound = False
gameover = False

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:

        ball.pos = ball.pos + ball.velocity * dt
        #ball.trail.append(pos=ball.pos)        #uncomment to see ball path

        #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 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:
                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"

If anyone cares it turns out that the problem is that the documentation is wrong. It should say to get the event, E = scene.mouse.getevent(), then look at E.button.

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.