2

I'm trying to write a simple Arkanoid with the help of Python and Tkinter. The goal is to make the ball reflect from the top, right and left sides. And if the player misses the ball so it touches the bottom side, the game would stop.

Here's the code:

from Tkinter import *
import time

root = Tk()
canv = Canvas(root, highlightthickness=0)
canv.pack(fill='both', expand=True)
top = canv.create_line(0, 0, 640, 0, fill='green', tags=('top'))
left = canv.create_line(0, 0, 0, 480, fill='green', tags=('left'))
right = canv.create_line(639, 0, 639, 480, fill='green', tags=('right'))
bottom = canv.create_line(0, 478, 640, 478, fill='red', tags=('bottom'))

rect = canv.create_rectangle(270, 468, 365, 478, outline='black', fill='gray40', tags=('rect'))
ball = canv.create_oval(0, 20, 20, 40, outline='black', fill='gray40', tags=('ball'))

delta_x = delta_y = 3
new_x, new_y = delta_x, -delta_y
while True:
    time.sleep(0.025)
    if canv.find_overlapping(canv.coords(ball)[0], canv.coords(ball)[1], canv.coords(ball)[2], canv.coords(ball)[3])[0] == 1:
        new_x, new_y = delta_x, -delta_y
        canv.move(ball, new_x, new_y)
        print 'fitst if', new_x, new_y
    if canv.find_overlapping(canv.coords(ball)[0], canv.coords(ball)[1], canv.coords(ball)[2], canv.coords(ball)[3])[0] == 2:
        new_x, new_y = delta_x, delta_y
        canv.move(ball, new_x, new_y)
        print '2nd if', new_x, new_y
    if canv.find_overlapping(canv.coords(ball)[0], canv.coords(ball)[1], canv.coords(ball)[2], canv.coords(ball)[3])[0] == 3:
        new_x, new_y = -delta_x, delta_y
        canv.move(ball, new_x, new_y)
    if canv.find_overlapping(canv.coords(ball)[0], canv.coords(ball)[1], canv.coords(ball)[2], canv.coords(ball)[3])[0] == 4:
        new_x, new_y = delta_x, -delta_y
        canv.move(ball, new_x, new_y)
    print new_x, new_y
    canv.move(ball, new_y, new_y)
    canv.update()

def move_right(event):
        canv.move(rect, 7, 0)
        pass

def move_left(event):
    canv.move(rect, -7, 0)
    pass

root.bind('<Right>', move_right)
root.bind('<Left>', move_left)

root.geometry('%sx%s+%s+%s' %(640, 480, 100, 100))
root.resizable(0, 0)
root.mainloop()

Why the ball reflecting in the wrong way?

screenshot of program

Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
SaulTigh
  • 913
  • 2
  • 14
  • 27
  • 1
    Doesn't "reflect" imply motion? You need to move the ball around first, updating its position periodically based on velocity. After you get the ball moving, when its bounding box intersects the rectangle, reverse the sign of the speed (e.g. x speed from +5 to -5) depending on which side the intersection occurred. Also, if this is a homework assignment, please add the "homework" tag. – Dave Nov 02 '11 at 17:34
  • You really should avoid using `while True` and `time.sleep`. Neither of those work well with an event loop. Use code like I showed you where you update the screen using `after`. – Bryan Oakley Nov 03 '11 at 20:42
  • your code doesn't run. At all. It has indentation errors, and it references "self" which is never set anywhere. – Bryan Oakley Nov 03 '11 at 20:45
  • @BryanOakley, i've just corrected the code. it should be working now. – SaulTigh Nov 03 '11 at 21:06

2 Answers2

13

To move an object you need to use the coords method or the move method, which changes the coordinates of the object. You can use the coords method to get the current coordinates.

To do the animation you can use after. Call a function, then have it use after to call itself again a short time in the future. How far in the future will determine your frame rate (ie: every 10ms means roughly 100 frames per second)

For example:

def moveit(self):

    # move the object
    <get the existing coordinates using the coords method>
    <adjust the coordinates relative to the direction of travel>
    <give the object new coordinates using the coords method>

    # cause this movement to happen again in 10 milliseconds
    self.after(10, self.moveit)

Once you call moveit() just once, the cycle begins. That same method can be used to update more than one object, or you can have different methods for different objects.

edit: You've completely changed your question from "how do I move something on a canvas?" to "why does it move in the wrong direction?". The answer to the latter is simple: you're telling it to move in the wrong direction. Use a debugger or some print statements to see where and how you are calculating delta_y.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
-2

here is a simple hack for this problem:

delta_x = delta_y = 3
while True:
      objects = canv.find_overlapping(canv.coords(ball)[0], canv.coords(ball)[1], canv.coords(ball)[2], canv.coords(ball)[3])
      for obj in objects:
        if obj == 1:
            delta_y = -delta_y
        if obj == 2:
            delta_x = -delta_x
        if obj == 3:
            delta_x = -delta_x
        if obj == 4:
            delta_y = -delta_y

      new_x, new_y = delta_x, delta_y
      canv.move(ball, new_x, new_y)
      canv.update()
      time.sleep(0.025)

      root.bind('<Right>', move_right)
      root.bind('<Left>', move_left)
SaulTigh
  • 913
  • 2
  • 14
  • 27
  • 6
    This is answer shows a less than optimal way of doing animation. You shouldn't call `sleep` in a GUI, and you normally shouldn't have an infinite loop either. A GUI already has an infinite loop -- the event loop. – Bryan Oakley Apr 25 '13 at 17:30