0

I'm having a similar problem with two programs.

In the "Pong" program, the ball lags randomly, making it difficult to play.

In the "Breakout" program, I cannot get the ball to move at a good speed.

Sleep(0.001) is too slow, and having no sleep statement is too fast. The best fix I have is to spam Print("") to create lag (Line 75/76), but this only sometimes works. I suspect the problem is that I am using Threads within Tkinter which I have heard is not a great idea, but idk what else to do.

Also, yes, I know I should use Pygame for these instead, but I mostly program at school where they refuse to install Pygame.

Breakout:

from tkinter import *
from tkinter import messagebox
from random import choice, randint
from time import sleep,time
from threading import Thread
import sys

HEIGHT = 500
WIDTH = 1000

BATWIDTH = 200
BLOCKWIDTH = 80
BLOCKHEIGHT = 40

ballLaunched = False

root = Tk()
root.title("Breakout")
root.resizable(0,0)
root.config(bg = "#000000")

def main():
    global canvas, bat, ball, direction, ballCoord, blocks, lives, livesText, board

    board = Frame(root)
    board.grid()
    board.config(bg = "#000000")

    canvas = Canvas(board, width=WIDTH, height=HEIGHT, bg = "#262626")
    canvas.grid()

    bat = canvas.create_rectangle(0,0,0,0, fill="red")

    direction = [1,1]
    ball = canvas.create_oval(0,0,0,0, fill="#FFFFFF", tags = "Ball")

    lives = 3
    livesText = canvas.create_text(33,13,text="Lives: "+str(lives),fill="#FFFFFF",font="helvetica")


    #Makes all the blocks
    blocks = []

    #Create the blocks
    for i in range(6):
        for j in range(int(WIDTH/BLOCKWIDTH)):
            topX = 5 + (BLOCKWIDTH*j) + (WIDTH%BLOCKWIDTH)/2
            topY = BLOCKHEIGHT*2 + (BLOCKHEIGHT*i)
            block = canvas.create_rectangle(topX,topY,topX+BLOCKWIDTH,topY+BLOCKHEIGHT, tags = "Block")

            if i == 0:
                canvas.itemconfig(block,fill = "#ff0000")
            elif i == 1:
                canvas.itemconfig(block,fill = "#ff8000")
            elif i == 2:
                canvas.itemconfig(block,fill = "#ffff00")
            elif i == 3:
                canvas.itemconfig(block,fill = "#80ff00")
            elif i == 4:
                canvas.itemconfig(block,fill = "#00ffff")
            elif i == 5:
                canvas.itemconfig(block,fill = "#0080ff")
            blocks.append(block)






def update():
    global direction, lives
    ballCoord = {"topX":mouseXStart-20/2,"topY":400,"bottomX":mouseXStart+20/2,"bottomY":420}

    while True:
        #sleep(1/1000)
        print()

        ballCoord["topX"]+=direction[0] 
        ballCoord["topY"]+=direction[1]
        ballCoord["bottomX"]+=direction[0]
        ballCoord["bottomY"]+=direction[1]

        canvas.coords(ball,ballCoord["topX"],ballCoord["topY"],ballCoord["bottomX"],ballCoord["bottomY"])
        if len((canvas.find_overlapping(ballCoord["topX"]-1,ballCoord["topY"]-1,ballCoord["bottomX"]+1,ballCoord["bottomY"]+1))) >= 2 and ballCoord["bottomY"] <= 400 and ballCoord["topY"] >= 20:
            for i in blocks:
                blockTopX = canvas.bbox(i)[0]
                blockTopY = canvas.bbox(i)[1]
                blockBottomX = canvas.bbox(i)[2]
                blockBottomY = canvas.bbox(i)[3]
                for j in canvas.find_overlapping(canvas.bbox(i)[0],canvas.bbox(i)[1],canvas.bbox(i)[2],canvas.bbox(i)[3]):
                    if "Ball" in canvas.gettags(j):
                        #Moving Top Left
                        if direction == [-1,-1]:
                            if ballCoord["topY"] == blockBottomY:
                                direction = [-1,1]
                            elif ballCoord["topX"] == blockBottomX:
                                direction = [1,-1]
                            canvas.delete(i)
                            blocks.remove(i)

                        #Moving Top Right
                        elif direction == [1,-1]:
                            if ballCoord["topY"] == blockBottomY:
                                direction = [1,1]
                            elif ballCoord["bottomX"] == blockTopX:
                                direction = [-1,-1]
                            canvas.delete(i)
                            blocks.remove(i)

                        #Moving Bottom Right
                        elif direction == [1,1]:
                            if ballCoord["bottomY"] == blockTopY:
                                direction = [1,-1]
                            elif ballCoord["bottomX"] == blockTopX:
                                direction = [-1,1]
                            canvas.delete(i)
                            blocks.remove(i)

                        #Moving Bottom Left
                        elif direction == [-1,1]:
                            if ballCoord["bottomY"] == blockTopY:
                                direction = [-1,-1]
                            elif ballCoord["topX"] == blockBottomX:
                                direction = [1,1]
                            canvas.delete(i)
                            blocks.remove(i)


        #Bat Collision
        try:
            #Left side
            if len(canvas.find_overlapping(mouseX-BATWIDTH/2,450,mouseX, 470)) == 2:
                    direction = [-1,-1]
            #Right side
            if len(canvas.find_overlapping(mouseX,450,mouseX+BATWIDTH/2, 470)) == 2:
                    direction = [1,-1]
        except NameError:
            pass


        #Right Wall Collision
        if ballCoord["bottomX"] >= WIDTH:
            if direction == [1,-1]:
                direction = [-1,-1]
            if direction == [1,1]:
                direction = [-1,1]


        #Roof Collision                
        if ballCoord["topY"] <= 0:
            if direction == [1,-1]:
                direction = [1,1]
            if direction == [-1,-1]:
                direction = [-1,1]


        #Left Wall Collision
        if ballCoord["topX"] <= 0:
            if direction == [-1,-1]:
                direction = [1,-1]
            if direction == [-1,1]:
                direction = [1,1]

        #Offscreen
        if ballCoord["topY"] >= HEIGHT:
            global ballLaunched
            lives -= 1
            canvas.itemconfig(livesText,text="Lives: "+str(lives))
            sleep(1)
            ballLaunched = False
            if lives <= 0:
                gameOver()
            break

def moveBat(event):
    global mouseX,mouseY   
    mouseX, mouseY = event.x, event.y
    #if mouseX >= 100 and mouseX <= 900:
    canvas.coords(bat, mouseX-BATWIDTH/2, 450,  mouseX+BATWIDTH/2, 470)
    try:
        if not ballLaunched:
            canvas.coords(ball, mouseX-20/2, 400,  mouseX+20/2, 420)
    except NameError:
        canvas.coords(ball, mouseX-20/2, 400,  mouseX+20/2, 420)

def launchBall(event):
    global ballLaunched, mouseXStart
    if not ballLaunched:
        ballLaunched = True
        mouseXStart = mouseX
        Thread(target=update).start()


def gameOver():
    if messagebox.askquestion("Game Over","Play Again?") == "yes":
        board.destroy()
        main()
    else:
        root.destroy()
        sys.exit()



main()
root.bind("<Motion>", moveBat)
root.bind("<Button-1>", launchBall)
root.mainloop()

Pong (Credit to Bryan Oakley for the base classes I copied a little):

from tkinter import *
from tkinter import messagebox
from random import choice, randint,uniform
from time import sleep,time
from threading import Thread
import sys

HEIGHT = 750
WIDTH = 1000

BATHEIGHT = 100
BATWIDTH = 20        


class Board:
    def __init__(self):
        self.pressed = {}
        self._create_ui()
        self.ballLaunched = False

    def start(self):
        self._animate()
        self.root.mainloop()

    def _create_ui(self):
        self.root = Tk()
        self.root.title("Pong")
        self.root.resizable(0,0)
        self.root.config(bg = "#262626")

        self.canvas = Canvas(width=WIDTH, height=HEIGHT,bg = "#262626")
        self.canvas.grid()

        self.p1 = Paddle(self.canvas, tag="p1", x=50, y=HEIGHT/2)
        self.p2 = Paddle(self.canvas, tag="p2", x=WIDTH-50, y=HEIGHT/2)

        self.ball = Ball(self.canvas, tag="Ball", x=WIDTH/2, y=HEIGHT/2,direction=choice([[1,1],[-1,-1],[1,-1],[-1,1]]),paddleLeft = self.p1,paddleRight = self.p2)

        height = 0
        for i in range(29):
            height += 20
            self.canvas.create_rectangle((WIDTH/2)-5,height,(WIDTH/2)+5,height+10,fill="white",width=0)
            height += 5

        global leftScore,rightScore,leftScoreText,rightScoreText
        leftScore,rightScore = 0,0
        leftScoreText = self.canvas.create_text((WIDTH/2)-100,75, text = leftScore, font=("System",75), fill= "white")
        rightScoreText = self.canvas.create_text((WIDTH/2)+100,75, text = rightScore, font=("System",75), fill= "white")

        self.startText = self.canvas.create_text(WIDTH/2,(HEIGHT/2)-40,text="Press <Space> To Start", font=("System",20), fill= "white")

        self._set_bindings()                

    def _animate(self):
        if self.pressed["w"]: self.p1.move_up()
        if self.pressed["s"]: self.p1.move_down()
        if self.pressed["o"]: self.p2.move_up()
        if self.pressed["l"]: self.p2.move_down()

        self.p1.redraw()
        self.p2.redraw()
        self.root.after(10, self._animate)

    def _set_bindings(self):
        for char in ["w","s","o", "l", " "]:
            self.root.bind("<KeyPress-%s>" % char, self._pressed)
            self.root.bind("<KeyRelease-%s>" % char, self._released)
            self.pressed[char] = False

    def _pressed(self, event):
        self.pressed[event.char] = True

    def _released(self, event):
        if self.pressed[" "] and not self.ballLaunched:
            self.canvas.delete(self.startText)
            self.ballLaunched = True
            Thread(target=self.ball.redraw).start()

        self.pressed[event.char] = False


class Paddle():
    def __init__(self, canvas, tag, x=0, y=0):
        self.canvas = canvas
        self.tag = tag
        self.x = x
        self.y = y

        self.bat = self.canvas.create_rectangle(self.x-BATWIDTH/2,self.y - BATHEIGHT/2,self.x + BATWIDTH/2,self.y + BATHEIGHT/2,tags=self.tag, fill="white",width=0)

        self.redraw()

    def move_up(self):
        self.y = max(self.y - 10, (BATHEIGHT/2)+3)

    def move_down(self):
        self.y = min(self.y + 10, HEIGHT-BATHEIGHT/2)

    def redraw(self):
        self.x0 = self.x - BATWIDTH/2
        self.x1 = self.x + BATWIDTH/2
        self.y0 = self.y - BATHEIGHT/2
        self.y1 = self.y + BATHEIGHT/2

        self.canvas.coords(self.bat,self.x0,self.y0,self.x1,self.y1)


class Ball():
    def __init__(self,canvas,tag,x=0,y=0,direction=[1,1],paddleLeft=0,paddleRight=0):
        self.canvas = canvas
        self.tag = tag
        self.x = x
        self.y = y
        self.direction = direction
        self.paddleLeft = paddleLeft
        self.paddleRight = paddleRight
        self.speed = 0.5
        self.ball = self.canvas.create_rectangle(self.x - 20/2,self.y - 20/2 ,self.x + 20/2,self.y + 20/2,tags=self.tag, fill="white",width=0)

    def redraw(self):
        while True:
            #sleep(1/1000)
            #print()
            self.x0 = self.x - 20/2 
            self.x1 = self.x + 20/2 
            self.y0 = self.y - 20/2 
            self.y1 = self.y + 20/2

            self.canvas.coords(self.ball,self.x0,self.y0,self.x1,self.y1)           

            self.move()
            if self.checkCollision() == "Top":
                if self.direction == [1,-1]: self.direction = [1,1]
                elif self.direction == [-1,-1]: self.direction = [-1,1]
            if self.checkCollision() == "Bottom":
                if self.direction == [1,1]: self.direction = [1,-1]
                elif self.direction == [-1,1]: self.direction = [-1,-1]
            if self.checkCollision() == "Left":
                if self.direction == [-1,1]: self.direction = [1,1]
                elif self.direction == [-1,-1]: self.direction = [1,-1]
            if self.checkCollision() == "Right":
                if self.direction == [1,-1]: self.direction = [-1,-1]
                elif self.direction == [1,1]: self.direction = [-1,1]

            global leftScore,rightScore,leftScoreText,rightScoreText  
            if self.checkCollision() == "Right Scored":
                rightScore += 1
                self.canvas.itemconfig(rightScoreText ,text=rightScore)
                self.resetBall()

            if self.checkCollision() == "Left Scored":
                leftScore += 1
                self.canvas.itemconfig(leftScoreText ,text=leftScore)
                self.resetBall()



    def move(self):
        self.x += self.direction[0] * self.speed
        self.y += self.direction[1] * self.speed

    def checkCollision(self):
        if self.y0 <= 0: return "Top"
        if self.y1 >= HEIGHT: return "Bottom"
        if len(self.canvas.find_overlapping(self.x0,self.y0,self.x1,self.y1)) >= 2 and self.x0 >= WIDTH-100: return "Right"
        if len(self.canvas.find_overlapping(self.x0,self.y0,self.x1,self.y1)) >= 2 and self.x0 <= 100: return "Left"

        if self.x < 0: return "Right Scored"
        if self.x > WIDTH: return "Left Scored"

    def resetBall(self):
        sleep(1)
        self.x,self.y = WIDTH/2,HEIGHT/2
        self.direction=choice([[1,1],[-1,-1],[1,-1],[-1,1]])





b = Board()
b.start()
Mrchooch
  • 171
  • 1
  • 1
  • 9
  • There are better ways to do animation than to have your own infinite loop. See http://stackoverflow.com/a/25431690/7432 – Bryan Oakley May 05 '16 at 22:56
  • @Bryan Oakley , thanks for helping. However, this doesnt seem to work with the breakout program, the ball wont move (Possibily because i didnt use classes in that one?). Also, whilst it works with the pong program, i realised that for some strange reason the position of the paddles changes the balls speed. Having them in opposite corners makes the ball go slow, whereas having them in the middle makes it go fast. Any idea why? – Mrchooch May 06 '16 at 08:04
  • Its not letting me edit my comment for some reason but i fixed the breakout problem, was just me being dumb – Mrchooch May 06 '16 at 08:33

0 Answers0