Following my previous tkinter tutorial, I'll make another tutorial to illustrate the use of tkinter canvas methods to handle sprite collisions(However, I'll only be using simple graphics in this tutorial), like in java.
This game is simple. There is a ball in the game. When I click somewhere on the canvas, the ball will try to run into that direction. On the way, it will meet food ( grows bigger), traps(grow smalle), powerups(increase size or mega points). The game will end when i) the ball grows too big, too small, or when the time runs out in 1 minute.
Here's the prototype code :
gamefile.py 7.88KB
2169 downloads
Also, I'll be using the tkinter root.after() method to substitute the use of a thread (e.g. java applet Runnable) so that my game can proceed on a separate time line.
Let's start by importing the tkinter module and the random module (a game is much fun with some random elements) and then writing the <__init__>, <run> functions
from tkinter import * from random import* '''New game to illustrate the use of tkinter to make a game that can handle collisions. ''' class game: def __init__(self): self.root=Tk() self.RUN=False self.frame=Frame(bg="black") self.frame.pack(); self.canvas=Canvas(self.frame, bg="black",width=300,height=300) self.canvas.pack() self.clock=Label(self.frame, bg="black", fg="white") self.clock.pack() self.points=Label(self.frame, bg="black", fg="white") self.points.pack() self.button=Button(self.frame, bg="black", fg="white", text="Click to start") self.button.pack() self.root.mainloop() def run(self): if self.RUN is True: self.root.after(10, self.run) app=game()
This is what the current code looks like.
Notice that I have used self.RUN as a controller for the run function so that I can stop it simply by making it False. The <root.after> function means that after 10 milliseconds, it will invoke self.run again, making it a recursive function.
Then, we need to design the <start> function which will be the command invoked by the button when we click it to start the game.
def start(self): self.time=0 self.RUN=True self.foodX=[] self.foodY=[] self.trapX=[] self.trapY=[] self.powerupX=[[],[]] self.powerupY=[[],[]] self.TEXT="Welcome to tkinter" self.point=0 self.x=100 self.y=100 self.tempx=100 self.tempy=100 self.UP=False self.DOWN=False self.LEFT=False self.RIGHT=False self.size=3 self.canvas.bind("<ButtonPress-1>", self.onMClick) self.run()
Right, once we got the things on the outside out, let's start designing the interior, the <paint> function, it will then be put into the <run> function, along with the <end> function to stop the game.. Note also that I have bound the self.onMClick to the canvas on the left button click, so we need to do something about that too.
from tkinter import * from random import * '''New game to illustrate the use of tkinter to make a game that can handle collisions. ''' class game: def __init__(self): self.root=Tk() self.RUN=False self.frame=Frame(bg="black") self.frame.pack(); self.canvas=Canvas(self.frame, bg="black",width=300,height=300) self.canvas.pack() self.clock=Label(self.frame, bg="black", fg="white") self.clock.pack() self.points=Label(self.frame, bg="black", fg="white") self.points.pack() self.button=Button(self.frame, bg="black", fg="white", text="Click to start",command=self.start) self.button.pack() self.root.mainloop() def start(self): self.time=0 self.RUN=True self.foodX=[] self.foodY=[] self.trapX=[] self.trapY=[] self.powerupX=[[],[]] self.powerupY=[[],[]] self.TEXT="Welcome to tkinter" self.point=0 self.x=100 self.y=100 self.tempx=100 self.tempy=100 self.UP=False self.DOWN=False self.LEFT=False self.RIGHT=False self.size=3 self.canvas.bind("<ButtonPress-1>", self.onMClick) self.run() def paint(self): self.canvas.delete(ALL) self.canvas.create_text(100,100, text=self.TEXT, fill="green") if self.time//100<=60: if 10*self.size >0: self.TEXT="Welcome to tkinter" ball=self.canvas.create_oval(self.x-10*self.size,self.y-10*self.size,self.x+10*self.size,self.y+10*self.size, fill="white") elif 10*self.size>150: self.clock['text']="You lost" self.end() else: self.clock['text']="You lost" self.end() else: self.clock['text']="Time's up" self.end() def onMClick(self,event): self.tempx=event.x self.tempy=event.y if event.x> self.x and self.x is not self.tempx : self.RIGHT=True self.LEFT=False elif event.x< self.x and self.x is not self.tempx : self.LEFT=True self.RIGHT=False else: self.x=self.tempx self.RIGHT=False self.LEFT=False if event.y> self.y and self.y is not self.tempy : self.DOWN=True self.UP=False elif event.y< self.y and self.y is not self.tempy : self.UP=True self.DOWN=False else: self.y=self.tempy self.DOWN=False self.UP=False def run(self): if self.RUN is True: self.time+=1 self.clock['text']="TIME:" + str(self.time//100) self.points['text']="Points gathered: " + str(self.point) self.paint() self.root.after(10, self.run) def end(self):
app=game()
the code is rewritten with modifications in the <run> function to play the paint.
Here's how it looks like.
Note that the <onMClick> function controls the direction of the circle on click. However, we don't have a code to control how it moves yet. I didn't put the <move> code into the <onMClick> function because the <__init__> can't handle events.
So, I allowed the event code to handle the directions, and another <move> function to handle the movement of the circle. Note we'd also have to write code to allow the override of directions. i.e. when it moves left, it cannot move right.
def move(self, b,speed): if self.UP==True and self.y-b>0: self.y-=speed elif self.UP==True and self.y-b<=0: self.UP=False self.DOWN=True if self.DOWN==True and self.y+b<300: self.y+=speed elif self.DOWN==True and self.y+b>=300: self.DOWN=False self.UP=True if self.LEFT==True and self.x-b>0: self.x-=speed elif self.LEFT==True and self.x-b<=0: self.LEFT=False self.RIGHT=True if self.RIGHT==True and self.x+b<300: self.x+=speed elif self.RIGHT==True and self.x+b>=300: self.RIGHT=False self.LEFT=True def run(self): if self.RUN is True: self.time+=1 self.clock['text']="TIME:" + str(self.time//100) self.points['text']="Points gathered: " + str(self.point) self.move(10*self.size,2) self.paint() self.root.after(10, self.run)
Now then, add it to the main code and play with it for a while. The circle could only move in 8 directions, though i have also include a collision handler. Note that the ball bounces off walls!
I will continue on creating food and collision handlers for them in the next post. That's all for now, happy coding!
Edited by DanielTan, 04 August 2013 - 05:57 PM.