Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
* * * - - 1 votes

Tkinter ball snake game

tkinter python game

  • Please log in to reply
1 reply to this topic

#1 DanielTan

DanielTan

    CC Regular

  • Member
  • PipPipPip
  • 25 posts

Posted 01 August 2013 - 05:34 AM

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 : Attached File  gamefile.py   7.88KB   1641 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()

game1.gif

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):
        self.RUN=False
        self.canvas.unbind("<ButtonPress-1>")

 

app=game()

the code is rewritten with modifications in the <run> function to play the paint.

 

Here's how it looks like.

game2.gif

 

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! 

 

game3.gif

 

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.

  • 2

#2 DanielTan

DanielTan

    CC Regular

  • Member
  • PipPipPip
  • 25 posts

Posted 04 August 2013 - 06:36 PM

Right, now then, we would want to add some goals for our game, and I'll set it to be getting the highest points within a minute. Going along that, let's write some code for food that will give points, and to give a better challenge, let our ball grow.

def create_food(self,ball):
        if len(self.foodX) <self.time//1500 +1:
            self.foodX.append(randint(50,250))
        if len(self.foodY) <self.time//1500 +1:
            self.foodY.append(randint(50,250))
        for i in range(0,len(self.foodX)):
            self.canvas.create_rectangle(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10, fill="blue")
        for i in range(0,len(self.foodX)):
            if len(self.canvas.find_overlapping(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10):
                    self.point+=100
                    self.size+=0.5 
                    self.foodX.pop(i)
                    self.foodY.pop(i)
                    self.create_food(ball)

I have used the canvas.find_overlapping as a collision handler for the food (It will eat it if it touches it), but you can also change it to <find_enclosed> so that it will devour it only if the food is inside the circle. 

The increased size can be a boon or bane for the player because next, we will also be creating traps to cut the game points and decrease the size.

def create_trap(self,ball):
        if len(self.trapX) <self.time//1500 +1:
            self.trapX.append(randint(50,250))
        if len(self.trapY) <self.time//1500 +1:
            self.trapY.append(randint(50,250))
        for i in range(0,len(self.trapX)):    
            self.canvas.create_rectangle(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10, fill="red")            
        for i in range(0,len(self.trapX)):
            if len(self.canvas.find_overlapping(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10):
                    self.point-=50
                    self.size-=1
                    self.trapX.pop(i)
                    self.trapY.pop(i)
                    self.create_trap(ball)

The [self.time//1500] code means that squares will increase every 15 seconds (time is ticked by the millisecond).

The blue square is food while the red square is a trap.

 

game4.gif

 

Then we'll also be doing some powerups, that will appear for about a second and a half, every ten seconds.

def create_powersize(self,ball):
        if len(self.trapY) is 0 or self.time%1000 == 0 :
            self.powerupX[0].append(randint(50,250))
            self.powerupY[0].append(randint(50,250))      
        for i in range(0,len(self.powerupX[0])):    
            self.canvas.create_rectangle(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10, fill="yellow")            
        for i in range(0,len(self.powerupX[0])):
            if len(self.canvas.find_overlapping(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10):
                    self.point+=150
                    self.size+=2
                    self.powerupX[0].pop(i)
                    self.powerupY[0].pop(i)
                    self.create_powersize(ball)

    def create_powercoin(self,ball):
        if len(self.trapY) is 0 or self.time%1000 == 0 :
            self.powerupX[1].append(randint(50,250))
            self.powerupY[1].append(randint(50,250))      
        for i in range(0,len(self.powerupX[1])):    
            self.canvas.create_rectangle(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10, fill="yellow")            
        for i in range(0,len(self.powerupX[1])):
            if len(self.canvas.find_overlapping(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10):
                    self.point+=500
                    self.size-=0.5
                    self.powerupX[1].pop(i)
                    self.powerupY[1].pop(i)
                    self.create_powercoin(ball)

Now then, let's put everything into our <paint> code and voila! the basic game is completed.

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 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)

    def end(self):
        self.RUN=False
        self.canvas.unbind("<ButtonPress-1>")

    def create_food(self,ball):
        if len(self.foodX) <self.time//1500 +1:
            self.foodX.append(randint(50,250))
        if len(self.foodY) <self.time//1500 +1:
            self.foodY.append(randint(50,250))
        for i in range(0,len(self.foodX)):
            self.canvas.create_rectangle(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10, fill="blue")
        for i in range(0,len(self.foodX)):
            if len(self.canvas.find_overlapping(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.foodX[i], self.foodY[i], self.foodX[i]+10, self.foodY[i]+10):
                    self.point+=100
                    self.size+=0.5 
                    self.foodX.pop(i)
                    self.foodY.pop(i)
                    self.create_food(ball)

    def create_trap(self,ball):
        if len(self.trapX) <self.time//1500 +1:
            self.trapX.append(randint(50,250))
        if len(self.trapY) <self.time//1500 +1:
            self.trapY.append(randint(50,250))
        for i in range(0,len(self.trapX)):    
            self.canvas.create_rectangle(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10, fill="red")            
        for i in range(0,len(self.trapX)):
            if len(self.canvas.find_overlapping(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.trapX[i], self.trapY[i], self.trapX[i]+10, self.trapY[i]+10):
                    self.point-=50
                    self.size-=1
                    self.trapX.pop(i)
                    self.trapY.pop(i)
                    self.create_trap(ball)

    def create_powersize(self,ball):
        if len(self.trapY) is 0 or self.time%1000 == 0 :
            self.powerupX[0].append(randint(50,250))
            self.powerupY[0].append(randint(50,250))      
        for i in range(0,len(self.powerupX[0])):    
            self.canvas.create_rectangle(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10, fill="yellow")            
        for i in range(0,len(self.powerupX[0])):
            if len(self.canvas.find_overlapping(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.powerupX[0][i], self.powerupY[0][i], self.powerupX[0][i]+10, self.powerupY[0][i]+10):
                    self.point+=150
                    self.size+=2
                    self.powerupX[0].pop(i)
                    self.powerupY[0].pop(i)
                    self.create_powersize(ball)

    def create_powercoin(self,ball):
        if len(self.trapY) is 0 or self.time%1000 == 0 :
            self.powerupX[1].append(randint(50,250))
            self.powerupY[1].append(randint(50,250))      
        for i in range(0,len(self.powerupX[1])):    
            self.canvas.create_rectangle(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10, fill="yellow")            
        for i in range(0,len(self.powerupX[1])):
            if len(self.canvas.find_overlapping(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10)) is not 1:
                if ball in self.canvas.find_overlapping(self.powerupX[1][i], self.powerupY[1][i], self.powerupX[1][i]+10, self.powerupY[1][i]+10):
                    self.point+=500
                    self.size-=0.5
                    self.powerupX[1].pop(i)
                    self.powerupY[1].pop(i)
                    self.create_powercoin(ball)
            
    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")
                self.create_food(ball)
                self.create_trap(ball)
                if self.time%1000 <=100 :
                    if randint(0,100)%2==0:
                        self.create_powersize(ball)
                    else:
                        self.create_powercoin(ball)
            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 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 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
        

app=game()        

The game will look something like this:

game5.gif

 

That's it for now! :)

 


  • 0





Also tagged with one or more of these keywords: tkinter, python, game