Skip to main content
Tweeted twitter.com/StackCodeReview/status/1382487274030530562
added 3 characters in body
Source Link

The goal of each frame of the animation to be the same length as the one before it, regardless of the amount of processing that takes place as the frame is being built.

This is a little animation demo that I wrote to show off the basics of a game loop in Python. With a newbies eyes in mind I have tried to keep to a limited tool kit; avoid pygame and threading and sticking to tkinter (included with most installations) to make the code as clear and as easy to understand as possible.

The end result works, but I am dissatisfied with the pacing on the game loop (onTimer). The goal to each frame of the animation to be the same length as the one before it, regardless of the amount of processing that takes place as the frame is being built. "After" isn't really to tool for the job.

This is a little animation demo that I wrote to show off the basics of a game loop in Python. With a newbies eyes in mind I have tried to keep to a limited tool kit; avoid pygame and threading and sticking to tkinter (included with most installations) to make the code as clear and as easy to understand as possible.

The end result works, but I am dissatisfied with the pacing on the game loop (onTimer). The goal to each frame of the animation to be the same length as the one before it, regardless of the amount of processing that takes place as the frame is being built. "After" isn't really to tool for the job.

The goal of each frame of the animation to be the same length as the one before it, regardless of the amount of processing that takes place as the frame is being built.

This is a little animation demo that I wrote to show off the basics of a game loop in Python. With a newbies eyes in mind I have tried to keep to a limited tool kit; avoid pygame and threading and sticking to tkinter (included with most installations) to make the code as clear and as easy to understand as possible.

The end result works, but I am dissatisfied with the pacing on the game loop (onTimer). "After" isn't really to tool for the job.

Source Link

Frame Pacing and Animation in Python/tkinter

This is a little animation demo that I wrote to show off the basics of a game loop in Python. With a newbies eyes in mind I have tried to keep to a limited tool kit; avoid pygame and threading and sticking to tkinter (included with most installations) to make the code as clear and as easy to understand as possible.

The end result works, but I am dissatisfied with the pacing on the game loop (onTimer). The goal to each frame of the animation to be the same length as the one before it, regardless of the amount of processing that takes place as the frame is being built. "After" isn't really to tool for the job.

In addition to being open to general comments, I am looking for suggestions to make the animation smoother and less at the whims of "after" and background event processing.

#Bounce

import tkinter
import time
import math
import random


#--- Initialize Globla Variables ----------------------------------------------
winWid = 640    #window size
winHei = 480
frameRate = 20  #length of a frame (include delay) in millesconds
frameDelay = 0  #the delay at the end of the current frame
frameStart = 0  #when the current frame started

balldiameter = 7
ballShape = []
ballX = []
ballY = []
spdX = []
spdY = []
ballCnt = 0
addTimer = time.perf_counter()

#--- Function/Tookkit list ----------------------------------------------------

def onTimer():
    global timerhandle, frameStart, frameDelay, addTimer
    
    #An animation frame is the work being done to draw/update a frame,
    #  plus the delay between the frames.  As the work to draw the
    #  frame goes up, the delay between the frames goes down
    elapsedTime = round((time.perf_counter() - frameStart)* 1000)  #time from start of frame until now
    frameDelay = frameRate - elapsedTime   #delay for this frame is the frame size, minus how long it took to process this frame
    if frameDelay < 1: frameDelay = 1  #bottom out with a single millesecond delay
    frameStart = time.perf_counter()   #start a new frame

    #if the frame delay hasn't bottomed out and a half second has passed
    if (time.perf_counter() - addTimer) > 0.25 and frameDelay > 1:
        addTimer = time.perf_counter()  #update the add time
        addBall()
        window.title("FD:" + str(frameDelay) + " - " + str(ballCnt))
        
    moveTheBalls()  #update the position of the balls
    
    timerhandle = window.after(frameDelay,onTimer) #fill update rest of this frame with a delay

    
def onShutdown():
    window.after_cancel(timerhandle)
    window.destroy()
    
def addBall():
    global ballCnt
    newX = random.randrange(0,winWid)
    newY = random.randrange(0,winHei)
    color = randomColor()
    ballShape.append(canvas.create_oval(newX,newY, newX+balldiameter,newY+balldiameter, outline=color, fill=color))
    ballX.append(newX)
    ballY.append(newY)
    spdX.append((random.random() * 2)-1)
    spdY.append((random.random() * 2)-1)
    ballCnt = ballCnt + 1

def moveTheBalls():
    for i in range(0,ballCnt):     #for each ball
        ballX[i] = ballX[i] + spdX[i]    #update its position
        ballY[i] = ballY[i] + spdY[i]
        for j in range(i+1,ballCnt):     #check for colision between other balls
            dist = math.sqrt(( (ballX[i]+(balldiameter/2)) - (ballX[j]+(balldiameter/2)))**2 + ( (ballY[i]+(balldiameter/2)) - (ballY[j]+(balldiameter/2)))**2) 
            if dist < balldiameter:      #if the balls are inside each other
                hold = spdX[i]           #swap their directions
                spdX[i] = spdX[j]
                spdX[j] = hold
                hold = spdY[i]
                spdY[i] = spdY[j]
                spdY[j] = hold
        if ballX[i] < 0 or ballX[i] > winWid-balldiameter: spdX[i] = spdX[i] * -1   #top or bottom? reverse directions
        if ballY[i] < 0 or ballY[i] > winHei-balldiameter: spdY[i] = spdY[i] * -1   #left or right? reverse directions
        canvas.coords(ballShape[i], (ballX[i], ballY[i], ballX[i]+balldiameter, ballY[i]+balldiameter))
    
#Random color - This is a helper function
#Returns a random color.
#Usage c4 = randomcolor()
def randomColor():
    hexDigits = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
    rclr = "#"
    for i in range(0,6):
        rclr = rclr + hexDigits[random.randrange(0,len(hexDigits))]
    return rclr


#--- Main Program - Executes as soon as you hit run  --------------------------
  
window = tkinter.Tk()   #Sets the window
canvas = tkinter.Canvas(window, width=winWid, height=winHei, bg="white")
canvas.pack()

addBall()

timerhandle = window.after(20,onTimer)  #start the game loop
window.protocol("WM_DELETE_WINDOW",onShutdown)  #provide a graceful exit
window.mainloop()   #Start the GUI