Jump to content


Check out our Community Blogs

DanielTan

Member Since 22 Jul 2013
Offline Last Active Apr 16 2014 04:09 PM
-----

Topics I've Started

Tkinter bouncy ball with full control over direction

07 August 2013 - 06:16 AM

Right, in my previous tutorial

http://forum.codecal...all-snake-game/,

I wrote some code for a ball snake game. Problem is, in that game, there's only eight directions. Now then, let's get greedy and make it go any way we want.

 

Also, we will use parametric functions do make paths for our sprites, which in this case, for simplicity, will be simple geometric shapes.

 

I got this idea to use vectors from a website for java programming (forgot what it was though), and it proved useful for games like these, where you need every direction possible. The thing was, it did not hurt to have some understanding of physics and maths.

 

Besides, it's easier to write maths in python.

 

Here's the code first:

Attached File  bounceeverywhere.py   3.3KB   590 downloads

 

I will just copy paste the outer shell of the interface here, to save some time.

from tkinter import *
from random import*
from math 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.xa=200
        self.ya=100
        self.tempx=0
        self.tempy=0

        self.size=3
        self.canvas.bind("<ButtonPress-1>", self.onMClick)
        self.run()

    def run(self):
        if self.RUN is True:
            self.time+=1
            #Here's where the ball circles round and round
            self.xa+=25*sin((pi/3)*self.time)
            self.ya+=25*cos((pi/3)*self.time)
            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 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")
                hey=self.canvas.create_oval(self.xa-10,self.ya-10,self.xa+10,self.ya+10, fill="white")
                
    def move(self, b,speed):
        #movement handler

    def onMClick(self,event):
        #mouseclick events
        
        

app=game()        

The outer shell of the interface is covered in that link above, so let's continue with the juicier parts. We want the ball to move where we click.

 

FIrst, we should have the mouseclick update the direction vector, which is a vector, of length one.

def onMClick(self,event):
        #using unit vectors here
        X=event.x-self.x
        Y=event.y-self.y
        self.tempx=X/((X**2 + Y**2)**(0.5))
        self.tempy=Y/((X**2 + Y**2)**(0.5))
        print(self.tempx, self.tempy)

once again, i allow the event handler to handle the direction, and another function to handle movements and collisions.

def move(self, b,speed):
        if self.tempy and self.y-b>0 and  self.y+b<300:
            self.y+=speed*self.tempy
        elif self.tempy<=0 and self.y-b<=0 :
            self.tempy*=-1
            self.y+=speed*self.tempy
        elif self.tempy>0 and self.y+b>=300:
            self.tempy*=-1
            self.y+=speed*self.tempy
        if self.tempx and self.x-b>0 and  self.x+b<300:
            self.x+=speed*self.tempx
        elif self.tempx<=0 and self.x-b<=0 :
            self.tempx*=-1
            self.x+=speed*self.tempx
        elif self.tempx>0 and self.x+b>=300:
            self.tempx*=-1
            self.x+=speed*self.tempx

The good thing about vectors is that just by multiplying it by negative one, you reverse the direction.

There! add this to the outer shell and you're done.

 

Also, you might notice that there is a rotating circle.

 

from this:

    def run(self):
        if self.RUN is True:
            self.time+=1
            #Here's where the ball circles round and round
            self.xa+=25*sin((pi/3)*self.time)
            self.ya+=25*cos((pi/3)*self.time)
            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)

Where the sin and cos are both parametric equations of a circle. 

 

And voila, your ball will move anywhere you want it and it will bounce off walls.


Tkinter ball snake game

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

Attached File  game1.gif   13.56KB   116 downloads

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.

Attached File  game2.gif   14.11KB   110 downloads

 

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! 

 

Attached File  game3.gif   16.03KB   110 downloads

 

I will continue on creating food and collision handlers for them in the next post. That's all for now, happy coding!

 

 

 


Tkinter Tic Tac Toe tutorial

22 July 2013 - 06:17 AM

Having seen the java tutorial on tic tac toe, i felt that the python tutorials page needed more push.
Since we already have a shell IO game, I'll do one using tkinter (not the best, but I like it)
 
I'm taking that you have a basis in basic tkinter(if not, another tutorial is underway, i guess), and let's build on that. 

I like tkinter in that by manually writing the code, it gives me more control over where, what, how of the program.

http://effbot.org/tkinterbook/

This site is where I learnt tkinter, though. 
 
Note that you can choose whether if you want to do single player mode, or double player mode.
But we'll start with double player as our basis, shall we?
First, we start by importing what we need and starting the __init__ function main class, along with calling the root. 

from tkinter import Frame, Canvas, Label, Button, LEFT,RIGHT, ALL, Tk
from random import randint

class main:
   
    def __init__(self,master):
        self.frame = Frame(master)
        self.frame.pack(fill="both", expand=True)
        

root=Tk()
app=main(root)
root.mainloop()

This is a basic model of a GUI. I  could have done <from tkinter import * >for simplicity but i think this would improve performance.
then, we add the canvas, and a few buttons to control the flow of the game.

from tkinter import Frame, Canvas, Label, Button, LEFT,RIGHT, ALL, Tk
from random import randint

class main:
   
    def __init__(self,master):
        self.frame = Frame(master)
        self.frame.pack(fill="both", expand=True)
        self.canvas = Canvas(self.frame, width=300, height=300)
        self.canvas.pack(fill="both", expand=True)
        self.label=Label(self.frame, text='Tic Tac Toe Game', height=6, bg='black', fg='blue')
        self.label.pack(fill="both", expand=True)
        self.frameb=Frame(self.frame)
        self.frameb.pack(fill="both", expand=True)
        self.Start1=Button(self.frameb, text='Click here to start\ndouble player', height=4, command=self.start1,bg='white', fg='purple')
        self.Start1.pack(fill="both", expand=True, side=RIGHT)
        self.Start2=Button(self.frameb, text='Click here to start\nsingle player', height=4, command=self.start2,bg='purple', fg='white')
        self.Start2.pack(fill="both", expand=True, side=LEFT)  

root=Tk()
app=main(root)
root.mainloop()

self refers to the main class.
the <frame.pack()> means that I'm using the pack geometry manager which basically helps me to add the widgets into the frames.
Frame are used to organize the widgets.
The canvas is where the lights and actions meet.
Labels are used to display text.
Now then notice that each button, Start1 and Start2, unlike java, has their own method to call functions.
We'll write the single player function, start1 first.
 

...
def start1(self):
        self.canvas.delete(ALL)
        self.label['text']=('Tic Tac Toe Game')
        self.canvas.bind("<ButtonPress-1>", self.sgplayer)  
        self._board()
        self.TTT=[[0,0,0],[0,0,0],[0,0,0]]
        self.i=0
        self.j=False

First, we refresh the canvas by deleting everything, then redrawing them again.
I bound a function self.sgplayer, which will do the gaming to the canvas when the button is clicked
The self._board() is the function we will use to draw the board.
self.TTT is the matrix I use to record what is drawn at where.
Note that in other languages, they might not choose to use to use a matrix to store such data (the java tutorials don't, anyway), but using the matrix will simplify our calculations, as you'll see later on, especially in the AI code (We are never going to go through each winning case manually,never! )
self.i is a counter(for turn taking) and self.j is simply there to be used in end() where we end the game.
 
You might notice that the python code is pretty self explanatory. So I don't do much commenting. There isn't much of a need
 
then we draw the board.

...
def _board(self):
        self.canvas.create_rectangle(0,0,300,300, outline="black")
        self.canvas.create_rectangle(100,300,200,0, outline="black")
        self.canvas.create_rectangle(0,100,300,200, outline="black")

now that's done, on to the gaming!
 

def sgplayer(self,event):
        for k in range(0,300,100):
            for j in range(0,300,100):
                if event.x in range(k,k+100) and event.y in range(j,j+100):
                    if self.canvas.find_enclosed(k,j,k+100,j+100)==():
                        if self.i%2==0:
                            X=(2*k+100)/2
                            Y=(2*j+100)/2
                            X1=int(k/100)
                            Y1=int(j/100)
                            self.canvas.create_oval( X+25, Y+25, X-25, Y-25, width=4, outline="black")
                            self.TTT[Y1][X1]+=1
                            self.i+=1
                        else:                         
                            X=(2*k+100)/2
                            Y=(2*j+100)/2
                            X1=int(k/100)
                            Y1=int(j/100)
                            self.canvas. create_line( X+20, Y+20, X-20, Y-20, width=4, fill="black")
                            self.canvas. create_line( X-20, Y+20, X+20, Y-20, width=4, fill="black")
                            self.TTT[Y1][X1]+=9
                            self.i+=1
        self.check()

Ah. Now this gets a little tricky. When the user input is registered, the computer:
1)check where the user clicked.
2)set a bounding box
3)check if there is nothing in that box (so that it can draw
4)check whose turn is it.
5) draw the circle or cross. and add the corresponding value to self.TTT, the matrix
6) check if anybody wins.(that's what the self.check() is for.
 
Onto to the self.check()

def check(self):
        #horizontal check
        for i in range(0,3):
            if sum(self.TTT[i])==27:
                self.label['text']=('2nd player wins!')
                self.end()
            if sum(self.TTT[i])==3:
                self.label['text']=('1st player wins!')
                self.end()
        #vertical check
        #the matrix below transposes self.TTT so that it could use the sum fucntion again
        self.ttt=[[row[i] for row in self.TTT] for i in range(3)]
        for i in range(0,3):            
            if sum(self.ttt[i])==27:
                self.label['text']=('2nd player wins!')
                self.end()
            if sum(self.ttt[i])==3:
                self.label['text']=('1st player wins!')
                self.end()
        #check for diagonal wins
        if self.TTT[1][1]==9:
            if self.TTT[0][0]==self.TTT[1][1] and self.TTT[2][2]==self.TTT[1][1] :
                self.label['text']=('2nd player wins!')
                self.end()
            if self.TTT[0][2]==self.TTT[1][1] and self.TTT[2][0]==self.TTT[1][1] :
                self.label['text']=('2nd player wins!')
                self.end()
        if self.TTT[1][1]==1:
            if self.TTT[0][0]==self.TTT[1][1] and self.TTT[2][2]==self.TTT[1][1] :
                self.label['text']=('1st player wins!')
                self.end()
            if self.TTT[0][2]==self.TTT[1][1] and self.TTT[2][0]==self.TTT[1][1] :
                self.label['text']=('1st player wins!')
                self.end()
        #check for draws
        if self.j==False:
            a=0
            for i in range(0,3):
                a+= sum(self.TTT[i])
            if a==41:
                self.label['text']=("It's a pass!")
                self.end()

here I used the sum functions for lists just to add the elements together. Also, i checked if there are passes, i.e. 5(1)+4(9)=41. five circles and four crosses are the maximum, but if nobody wins then it's a pass.

So you don't have to set boolean checkers for each row== 

note: it also checks if self.j has been triggered so that it won't check if someone has won.

If it is a win, then we should end the game and stop anybody from clicking.

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

And we're done! copy all that into the main class and you got yourself a game. (NOTE: the functions for the second button has not been written so you'll have to delete all those single player controls if you were to play double player.)

 

I'll continue onto the juicier single player controls in my next post. Au revoir for now.


Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download