Explanation on why program must maintain simplicity:I am a peer tutor of a computer programming class and my teacher has asked me to develop a few simple games to teach the class. This is the first year the school has taught Python (thanks to my final project last semester, and now I'm stuck doing the teaching... =/ ). Because of this, my teacher cannot assist me very successfully.

I am aware of PyGame and wxPython but my teacher really wants me to keep things as simple and self contained as possible (he does not want to reimage every computer in the school with an external Python module). I am also aware of canvas.delete(item) but again, he wants me to use the "draw a shape, then draw the same shape in the background colour" method of animation.

Problem:
Sorry for the long preamble, here is my problem. I have a pong game started and it works for the most part (the ball does not move on a diagonal yet, but I have not gotten that far). The left paddle can flawlessly be controlled by the user and the right paddle remains stationary for now (I will make computer control this paddle, again not there yet). I have the ball moving back and forth and bouncing off the paddles, but after a few bounces back and forth the speed of the ball significantly drops a few times and then seems to maintain a constant slow speed (does not slow down to a halt). I'm not sure what would be causing this? but here is my code, maybe someone can spot the problem. Thank you for your time.

from tkinter import *#imports tkinter module
import random#imports random module

def DrawPaddle1():
    #draws paddle on left side
    global xPos1,yPos1
    canvas.create_rectangle(xPos1,yPos1,xPos1+25,yPos1+100,fill="white",width=0)
    
def ErasePaddle1():
    #erases paddle on left side
    global xPos1,yPos1
    canvas.create_rectangle(xPos1,yPos1,xPos1+25,yPos1+100,fill="black",width=0)
    
def DrawPaddle2():
    #draws paddle on right side
    global xPos2,yPos2
    canvas.create_rectangle(xPos2,yPos2,xPos2+25,yPos2+100,fill="white",width=0)

def ErasePaddle2():
    #erases paddle on right side
    global xPos2,yPos2
    canvas.create_rectangle(xPos2,yPos2,xPos2+25,yPos2+100,fill="black",width=0)

def DrawBall():
    #draws ball
    global ballX,ballY
    canvas.create_rectangle(ballX,ballY,ballX+20,ballY+20,fill="white",width=0)
    
def EraseBall():
    #erases ball
    global ballX,ballY
    canvas.create_rectangle(ballX,ballY,ballX+20,ballY+20,fill="black",width=0)
       
def KeyPress(event):
    #callback function for when a key is pressed
    global xPos1,yPos1
    if event.keysym == "Up":
        #moves paddle up if "Up" key is pressed
        ErasePaddle1()
        yPos1-=10
        DrawPaddle1()
    if event.keysym == "Down":
        #moves paddle down if "Down" key is pressed
        ErasePaddle1()
        yPos1+=10
        DrawPaddle1()

###################################################
root=Tk()#initializes toplevel window

widthVal=1000#width value of canvas
heightVal=700#height value of canvas
canvas = Canvas(width=widthVal, height=heightVal, bg='black')#initializes the canvas
canvas.pack()#prepares canvas

moveAmt=-0.9#variable which determines speed/direction of the ball
xPos1=20#variable for x coordinate of left paddle (user's)
yPos1=yPos2=heightVal//2-50#variables for y coordinates of left (user's) and right (computer's) paddles
xPos2=widthVal-45#variable for x coordinate of right (computer's) paddle
ballX=widthVal//2-10#variable for x coordinate of the ball
ballY=heightVal//2-10#variable for y coordinate of the ball

DrawPaddle1()
DrawPaddle2()
canvas.create_line (widthVal//2,heightVal,widthVal//2,0,fill="white",width=4,dash=(100))#draws middle line
DrawBall()                                                                       

root.bind_all('<Key>',KeyPress)#binds all keys 

quitGame=False
while quitGame==False:
    if ballX < xPos1+26 and ballY > yPos1 and ballY+20 < yPos1+100:#checks for collision on left paddle
        moveAmt=0.9
    elif ballX+20 > xPos2-1 and ballY > yPos2 and ballY+20 < yPos2+100:#checks for collision on right paddle
        moveAmt=-0.9
    EraseBall()
    ballX+=moveAmt#changes ball's x coordinate value by the move amount 
    DrawBall()
    canvas.update()#updates canvas
    if ballX > widthVal//2-25 and ballX <widthVal//2+5:#redraws line when ball passes over it
       canvas.create_line (widthVal//2,heightVal,widthVal//2,0,fill="white",width=4,dash=(100))

root.mainloop()

You can really simplify and make your animation much smoother using canvas.move() rather then erase and redraw your canvas shapes. Here is a typical example ...

# Tkinter animate via canvas.move(obj, xAmount, yAmount)
# tested with Python 3.1.2

import time
import tkinter as tk

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

# canvas.create_rectangle(x0, y0, x1, y1, option, ... )
# x0, y0, x1, y1 are corner coordinates of ulc to lrc diagonal
rc1 = canvas.create_rectangle(20, 260, 120, 360, outline='white',
    fill='blue')
rc2 = canvas.create_rectangle(20, 10, 120, 110, outline='white',
    fill='red')

for x in range(50):
    y = x = 5
    time.sleep(0.025)
    # move recangle by increments x, y
    canvas.move(rc1, x, -y)
    canvas.move(rc2, x, y)
    canvas.update()

root.mainloop()

Edited 6 Years Ago by vegaseat: n/a

Thank you for the reply "vegaseat".

Yes I am aware of canvas.move() function and how it works, but I would then have to introduce the students to object oriented programming when they only started their first programming language a few weeks ago. Although it is not that difficult of a command or anything, I would still have to get the students to understand that "move" is being called from the "object" canvas.

My teacher things this is too complex of a jump to make and would prefer me to do animation this way.

Is tkinter incapable of animating a game in these simple terms?

Using canvas.delete() speeds things up considerably. I have added timing to the DrawBall() function. It starts around 0.000060 seconds and takes double the amount of time after a few seconds. I would guess that this is because of the use of global variables (globals take more time than locals), but I don't really know. Try your code using a class structure, or passing one dictionary containing all of the variables to and from the functions instead of the globals.

def DrawBall():
    #draws ball
    start = datetime.datetime.now()
    print "/ndraw ball start", start
    global ballX,ballY
    canvas.create_rectangle(ballX,ballY,ballX+20,ballY+20,fill="white",width=0)
    print "draw ball end  ", datetime.datetime.now() - start 
    return ball

def EraseBall(ball):
    #erases ball
    canvas.delete(ball)

Okay I'll tell my teacher the canvas.delete is a must.

I don't think he'll want to teach classes yet, how would a dictionary of variables work? is that like a list or array at all? Sorry, Turing is my best language, I taught myself Python starting in Decemeber.

This article has been dead for over six months. Start a new discussion instead.