Every time I run this function it is slower and slower - python

I am trying to do code a simple game in python using tkinter where a block jumps over obstacles, however I got stuck on the jumping part. Every time I call the jump function it jumps slower and slower, and I don't know the reason. Ty in advance.
import time
import tkinter
import random
bg = "white"
f = 2
k=0
t = 0.01
groundLevel = 550
root = tkinter.Tk()
root.geometry("1000x600")
canvas = tkinter.Canvas(root,width = 1000,height = 1000,bg = bg)
canvas.pack(fill= tkinter.BOTH, expand= True)
posX = 50
posY= 530
startButton = tkinter.Button(canvas,text=" Start ")
def startPlayer(xx,yy):
canvas.create_rectangle(xx-20,yy-22,xx+20,yy+18,fill = "orange")
return(xx,yy)
def move(x,y,x2,y2,direction,fill,outline):
global f
#direction 0 = up
#direction 1 = down
#direction 2 = left
#direction 3 = right
if direction == 0:
canvas.create_rectangle(x,y,x2,y2,fill="cyan",outline="cyan")
canvas.create_rectangle(x,y-f,x2,y2-f,fill=fill,outline=outline)
if direction == 1:
canvas.create_rectangle(x,y,x2,y2,fill="cyan",outline="cyan")
canvas.create_rectangle(x,y+f,x2,y2+f,fill=fill,outline=outline)
if direction == 2:
canvas.create_rectangle(x,y,x2,y2,fill="cyan",outline="cyan")
canvas.create_rectangle(x,y,x2,y2,fill=fill,outline=outline)
if direction == 3:
canvas.create_rectangle(x,y,x2,y2,fill="cyan",outline="cyan")
canvas.create_rectangle(x,y,x2,y2,fill=fill,outline=outline)
def playerJump():
global groundLevel, f, k,posX,posY,t
while k != 1:
move(posX-20,posY-22,posX+20,posY+18,direction = 0, fill = "orange",outline = "black")
posY -= 2
canvas.update()
if (posY) == 480:
k = 1
time.sleep(t)
k = 0
while k != 1:
move(posX-20,posY-22,posX+20,posY+18,direction = 1, fill = "orange",outline = "black")
posY += 2
canvas.update()
if (posY) == 530:
k = 1
time.sleep(t)
k = 0
def start():
canvas.create_rectangle(0,0,1000,600,fill="cyan")
canvas.create_line(0,550,1000,550,width = 3)
startButton.destroy()
startPlayer(50,530)
startGameButton = tkinter.Button(canvas, text ="Go!",command = playerJump)
startGameButton.place(x = 35, y=400)
return(startGameButton)
def resetButton():
global startGameButton
startGameButton.destroy()
startGameButton = tkinter.Button(canvas, text ="Go!",command = playerJump)
startGameButton.place(x = 35, y=400)
startImage = tkinter.PhotoImage(file="C:/Users/marti/OneDrive/Desktop/Wheel finder/startSign.png")
canvas.create_rectangle(0,0,1000,1000,fill="green")
startButton.config(image = startImage,command = start)
startButton.place(x = 130, y= 25)
canvas.create_rectangle(300,400,700,500,fill="#113B08",outline = "black",width = 3)
canvas.create_text(500,450,text = "By: --------", font = "Arial 30",fill ="white")
I shrinking the sleep time every time it runs so its faster, but that is only a temporary solution and it didn't even work.

Problem with your code is you are always adding new items into your canvas. When you jump you update orange rectangle and repaint its old place. However they stack top of each other and handling too many elements makes slower your program.
We create player and return it to main function.
def startPlayer(xx,yy):
player=canvas.create_rectangle(xx-20,yy-22,xx+20,yy+18,fill = "orange")
return player
This is new start function. Check that we get player and send to playerjump function.
def start():
canvas.create_rectangle(0,0,1000,600,fill="cyan")
canvas.create_line(0,550,1000,550,width = 3)
startButton.destroy()
player = startPlayer(50,530)
startGameButton = tkinter.Button(canvas, text ="Go!",command = lambda :playerJump(player))
startGameButton.place(x = 35, y=400)
return(startGameButton)
And this is playerjump function.We get player and sent to move function.
def playerJump(player):
global groundLevel, f, k,posX,posY,t
while k != 1:
move(posX-20,posY-22,posX+20,posY+18,direction = 0, fill = "orange",outline = "black",player=player)
posY -= 2
canvas.update()
if (posY) == 480:
k = 1
time.sleep(t)
k = 0
while k != 1:
move(posX-20,posY-22,posX+20,posY+18,direction = 1, fill = "orange",outline = "black",player=player)
posY += 2
canvas.update()
if (posY) == 530:
k = 1
time.sleep(t)
k = 0
Except move lines, I didn't change anything in this function.
Okay now let's check key part.
def move(x,y,x2,y2,direction,fill,outline,player):
global f
#direction 0 = up
#direction 1 = down
#direction 2 = left
#direction 3 = right
if direction == 0:
canvas.coords(player,x,y-f,x2,y2-f)
if direction == 1:
canvas.coords(player,x,y+f,x2,y2+f)
if direction == 2:
canvas.coords(player,x,y,x2,y2)
if direction == 3:
canvas.coords(player,x,y,x2,y2)
look that instead of creating new rectangles,we updated existing one. which is much more stable
Also you forgot adding root.mainloop in your code snippet.

Related

How to fade from one colour to another in pygame?

How would I fade from one colour into another in pygame? I want to slowly change the colour of a circle from green to blue to purple to pink to red to orange to yellow to green. How would I do that? At the moment, I'm using
def colour():
switcher = {
0: 0x2FD596,
1: 0x2FC3D5,
2: 0x2F6BD5,
3: 0x432FD5,
4: 0x702FD5,
5: 0xBC2FD5,
6: 0xD52F91,
7: 0xD52F43,
8: 0xD57F2F,
9: 0xD5D52F,
10: 0x64D52F,
11: 0x2FD557,
}
return switcher.get(round((datetime.datetime.now() - starting_time).total_seconds()%11))
but that has really big steps in between the colours and looks clunky.
The key is to simply calculate how much you have to change each channel (a,r,g and b) each step. Pygame's Color class is quite handy, since it allows iteration over each channel and it's flexible in it's input, so you could just change e.g. 'blue' to 0x2FD596 in the below example and it will still run.
Here's the simple, running example:
import pygame
import itertools
pygame.init()
screen = pygame.display.set_mode((800, 600))
colors = itertools.cycle(['green', 'blue', 'purple', 'pink', 'red', 'orange'])
clock = pygame.time.Clock()
base_color = next(colors)
next_color = next(colors)
current_color = base_color
FPS = 60
change_every_x_seconds = 3.
number_of_steps = change_every_x_seconds * FPS
step = 1
font = pygame.font.SysFont('Arial', 50)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
text = font.render('fading {a} to {b}'.format(a=base_color, b=next_color), True, pygame.color.Color('black'))
step += 1
if step < number_of_steps:
# (y-x)/number_of_steps calculates the amount of change per step required to
# fade one channel of the old color to the new color
# We multiply it with the current step counter
current_color = [x + (((y-x)/number_of_steps)*step) for x, y in zip(pygame.color.Color(base_color), pygame.color.Color(next_color))]
else:
step = 1
base_color = next_color
next_color = next(colors)
screen.fill(pygame.color.Color('white'))
pygame.draw.circle(screen, current_color, screen.get_rect().center, 100)
screen.blit(text, (230, 100))
pygame.display.update()
clock.tick(FPS)
If you don't want to be dependent on the framerate but rather use a time based approach, you could change the code to:
...
change_every_x_milliseconds = 3000.
step = 0
running = True
while running:
...
if step < change_every_x_milliseconds:
current_color = [x + (((y-x)/change_every_x_milliseconds)*step) for x, y in zip(pygame.color.Color(base_color), pygame.color.Color(next_color))]
else:
...
...
pygame.display.update()
step += clock.tick(60)
You could go between all the values from one colour to the next by converting it into an int, increasing the number, and converting it back to hex. Then you just loop until you reach the next value with something like so:
value1 = 0xff00ff
value2 = 0xffffff
increment = 1 # amount to decrease or increase the hex value by
while value1 != value2:
if value1 > value2:
if int(value1)-increment < int(value2): # failsafe if the increment is greater than 1 and it skips being the value
value1 = value2
else:
value1 = hex(int(value1)-increment)
else:
if int(value1)+increment > int(value2):
value1 = value2
else:
value1 = hex(int(value1)+increment)
code_to_change_colour(value1)
See the edit by Prune for a much more elegant implementation of this. Note that code_to_change_colour(value1) should be changed to however you change the colour in your program. The increment will let you change how many colours are skipped. Obviously this code would need to be edited into a manner it can be used easily: e.g a function like def fade(value1, value2).
Edit from #Prune -- because code doesn't work well in comments.
Note that most of what you've written is "merely" loop control. You have known start and stop values and a fixed increment. This suggests a for loop rather than a while. Consider this:
value1 = int(0xff00ff)
value2 = int(0xffffff)
increment = 1 if value1 < value2 else -1
for current in range(value1, value2, increment):
code_to_change_colour(hex(value1))
value1 = value2
If you'd prefer just calculating the colors (without using any surfaces), you can do this:
First, you need to determine how long you want the dissolve to take. You also need to store the original and final colors. Last, calculate the blend. I would create a class for this:
import pygame
import time
class color_blend:
def __init__(self, start_color, end_color, duration=1000):
self.start_color = pygame.Color(start_color.r, start_color.g, start_color.b)
self.current_color = pygame.Color(start_color.r, start_color.g, start_color.b)
self.end_color = end_color
self.duration = float(duration)
self.start_time = color_blend.millis()
# Return current time in ms
#staticmethod
def millis():
return (int)(round(time.time() * 1000))
# Blend any 2 colors
# 0 <= amount <= 1 (0 is all initial_color, 1 is all final_color)
#staticmethod
def blend_colors(initial_color, final_color, amount):
# Calc how much to add or subtract from start color
r_diff = (final_color.r - initial_color.r) * amount
g_diff = (final_color.g - initial_color.g) * amount
b_diff = (final_color.b - initial_color.b) * amount
# Create and return new color
return pygame.Color((int)(round(initial_color.r + r_diff)),
(int)(round(initial_color.g + g_diff)),
(int)(round(initial_color.b + b_diff)))
def get_next_color(self):
# Elapsed time in ms
elapsed_ms = color_blend.millis() - self.start_time
# Calculate percentage done (0 <= pcnt_done <= 1)
pcnt_done = min(1.0, elapsed_ms / self.duration)
# Store new color
self.current_color = color_blend.blend_colors(self.start_color, self.end_color, pcnt_done)
return self.current_color
def is_finished(self):
return self.current_color == self.end_color
# Blend red to green in .3 seconds
c = color_blend(pygame.Color(255, 0, 0), pygame.Color(0, 255, 0), 300)
while not c.is_finished():
print(c.get_next_color())
You can easily modify this to do non-linear blending. For example, in blend_colors: amount = math.sin(amount * math.pi)
(I'm no Pygame expert - there may already be a built-in function for this.)
Set your foreground surface to the old color, over a background of the new one. Use set_alpha() to perform the fade. Once you're entirely on the new color, make that surface the foreground and make a new background of your third color. Repeat as desired.
This question and other references to "fade" and set_alpha() should allow you to finish the job.
Is that enough to get you moving?
I hesitated to post an answer because I came up with almost the same answer as Sloth's, but I'd just like to mention linear interpolation (short lerp, also called mix in OpenGL/GLSL). It's usually used to blend between two colors, but unfortunately pygame's Color class doesn't have a lerp method, so you have to define your own lerp function and use a list comprehension to interpolate the RGBA values.
Here's the lerp function from Wikipedia ported to Python (t is the weight and has to be between 0 and 1):
def lerp(v0, v1, t):
return (1 - t) * v0 + t * v1
Now you can lerp the RGBA values of two colors with a list comprehension.
color = [lerp(v0, v1, t) for v0, v1 in zip(color1, color2)]
E.g.:
>>> [lerp(v0, v1, .5) for v0, v1 in zip((0, 0, 0), (255, 255, 255))]
[127.5, 127.5, 127.5]
>>> [lerp(v0, v1, .25) for v0, v1 in zip((0, 0, 0), (255, 255, 255))]
[63.75, 63.75, 63.75]
If you don't need the alpha channel, you can also use pygame's Vector3 class which has a lerp method for your colors, then you'd just have to write: color = color1.lerp(color2, t).
import itertools
import pygame as pg
from pygame.math import Vector3
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
color_cycle = itertools.cycle([
Vector3(255, 0, 0),
Vector3(0, 255, 0),
Vector3(255, 255, 0),
Vector3(0, 0, 255),
])
color1 = next(color_cycle)
color2 = next(color_cycle)
color = color1
start_time = pg.time.get_ticks()
time_interval = 2000 # milliseconds
t = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
now = pg.time.get_ticks()
t = (now - start_time) / time_interval
if t > 1:
t = 0
start_time = now
color1, color2 = color2, next(color_cycle)
color = color1.lerp(color2, t) # Lerp the two colors.
screen.fill(color)
pg.display.flip()
clock.tick(60)
I had a similar problem not to long ago, I went the crazy route of learning a bit of Calculus to do it
R = X, G = Y, B = Z
then wrote a program to calculate the difference between R0 and R1 (X0 and X1) etc.
#Nick Poling
#Fade Test V2
import random
#import mpmath
#import sympy
import time
import pygame
import shelve
import sys
from pygame.locals import *
#Always Initialize
pygame.init()
#sympy contains function line_3d?
#top level?
#formated string?
#Change Name Displayed in Corner
MY_FONT = 'freesansbold.ttf'
FONTSIZE_1 = 20
pygame.display.set_caption('Fade Test V2')
GAME_NAME = pygame.font.Font(MY_FONT, FONTSIZE_1)
#Boards
GAME_SCREEN_WIDTH = 900
GAME_SCREEN_HEIGHT = 600
Main_Game_Loop = 1
def get_close_out():
#Save_Game = get_Save_Game()
pygame.quit()
sys.exit()
def get_Save_Game():
#Save
global Main_Game_Loop
global GAME_SCREEN_COLOR
global GAME_SCREEN_WIDTH
global GAME_SCREEN_HEIGHT
saveGameShelfFile = shelve.open('Fade Test V1')
saveGameShelfFile['GAME_SCREEN_WIDTH'] = GAME_SCREEN_WIDTH
saveGameShelfFile['GAME_SCREEN_HEIGHT'] = GAME_SCREEN_HEIGHT
def get_Load_Game():
global Main_Game_Loop
global GAME_SCREEN_COLOR
global GAME_SCREEN_WIDTH
global GAME_SCREEN_HEIGHT
try:
#Load
saveGameShelfFile = shelve.open('Fade Test V1')
GAME_SCREEN_WIDTH = saveGameShelfFile['GAME_SCREEN_WIDTH']
GAME_SCREEN_HEIGHT = saveGameShelfFile['GAME_SCREEN_HEIGHT']
except:
#Save
#Save_Game = get_Save_Game()
#Load
saveGameShelfFile = shelve.open('Fade Test V1')
GAME_SCREEN_WIDTH = saveGameShelfFile['GAME_SCREEN_WIDTH']
GAME_SCREEN_HEIGHT = saveGameShelfFile['GAME_SCREEN_HEIGHT']
GAME_SCREEN = pygame.display.set_mode((GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT),pygame.RESIZABLE)
#By putting the GAME_SCREEN here you can make the resize doable with the press of a button
#Does need to be un "#" to work when press "L"
def get_Colors():
#Colors
global BLACK
global WHITE
BLACK = [0, 0, 0]
WHITE = [255,255,255]
def get_Main_Game():
global Main_Game_Loop
global GAME_SCREEN_COLOR
global GAME_SCREEN_WIDTH
global GAME_SCREEN_HEIGHT
global BLACK
global WHITE
global Point_1
global Point_2
global Vector_1
global Vector_2
Colors = get_Colors()
GAME_SCREEN_COLOR = BLACK
#Load_Game = get_Load_Game()
GAME_SCREEN = pygame.display.set_mode((GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT),pygame.RESIZABLE)
while Main_Game_Loop == 1:
GAME_SCREEN.fill(GAME_SCREEN_COLOR)
Equation_Of_Lines_in_3D_Space = get_Equation_Of_Lines_in_3D_Space()
for t in range(0,255):
XYZ_1 = [Point_1[0] + (Vector_1[0] * t), Point_1[1] + (Vector_1[1] * t), Point_1[2] + (Vector_1[2] * t)]
XYZ_2 = [Point_2[0] + (Vector_2[0] * t), Point_2[1] + (Vector_2[1] * t), Point_2[2] + (Vector_2[2] * t)]
GAME_SCREEN_COLOR = XYZ_1
GAME_SCREEN.fill(GAME_SCREEN_COLOR)
ticks = pygame.time.delay(5)
pygame.display.update()
for event in pygame.event.get():
if event.type == QUIT:
close_out = get_close_out()
elif event.type == pygame.VIDEORESIZE:
# There's some code to add back window content here.
surface = pygame.display.set_mode((event.w, event.h),pygame.RESIZABLE)
GAME_SCREEN_HEIGHT = event.h
GAME_SCREEN_WIDTH = event.w
pygame.display.update()
def get_Equation_Of_Lines_in_3D_Space():
global Point_1
global Point_2
global BLACK
global WHITE
global Vector_1
global Vector_2
global LCM_X1
global LCM_X2
global LCM_Y1
global LCM_Y2
global LCM_Z1
global LCM_Z2
Point_1 = BLACK
Point_2 = WHITE
Vector_1 = []
Vector_2 = []
LCM_X1 = []
LCM_X2 = []
LCM_Y1 = []
LCM_Y2 = []
LCM_Z1 = []
LCM_Z2 = []
for i in range(0,3):
#
Delta_XYZ_1 = Point_2[i] - Point_1[i]
Vector_1.append(Delta_XYZ_1)
Delta_XYZ_2 = Point_1[i] - Point_2[i]
Vector_2.append(Delta_XYZ_2)
factors = get_factors()
def get_factors():
global num_1
global num_2
global Vector_1
global Vector_2
global LCM_XYZ_1
global LCM_XYZ_2
for i in range(1,7):
if i == 1:
num_1 = Vector_1[0]
num_2 = 1
elif i == 2:
num_1 = Vector_2[0]
num_2 = 2
elif i == 3:
num_1 = Vector_1[1]
num_2 = 3
elif i == 4:
num_1 = Vector_2[1]
num_2 = 4
elif i == 5:
num_1 = Vector_1[2]
num_2 = 5
elif i == 6:
num_1 = Vector_2[2]
num_2 = 6
get_largest_and_lowest_common_factors(num_1)
get_LCM_XYZ()
Vector_1[0] = Vector_1[0] / LCM_XYZ_1[0]
Vector_1[1] = Vector_1[1] / LCM_XYZ_1[0]
Vector_1[2] = Vector_1[2] / LCM_XYZ_1[0]
#
Vector_2[0] = Vector_2[0] / LCM_XYZ_2[0]
Vector_2[1] = Vector_2[1] / LCM_XYZ_2[0]
Vector_2[2] = Vector_2[2] / LCM_XYZ_2[0]
def get_largest_and_lowest_common_factors(x):
global num_1
global num_2
global Vector_1
global Vector_2
global LCM_X1
global LCM_X2
global LCM_Y1
global LCM_Y2
global LCM_Z1
global LCM_Z2
#This function takes a number and puts its factor into a list
for i in range(1, x + 1) or range(-x, x - 1, -1):
try:
if x % i == 0:
if num_1 == Vector_1[0] and num_2 == 1:
LCM_X1.append(i)
elif num_1 == Vector_1[1] and num_2 == 3:
LCM_Y1.append(i)
elif num_1 == Vector_1[2] and num_2 == 5:
LCM_Z1.append(i)
elif num_1 == Vector_2[0] and num_2 == 2:
LCM_X2.append(i)
elif num_1 == Vector_2[1] and num_2 == 4:
LCM_Y2.append(i)
elif num_1 == Vector_2[2] and num_2 == 6:
LCM_Z2.append(i)
except ZeroDivisionError:
return 0
def get_LCM_XYZ():
global LCM_X1
global LCM_Y1
global LCM_Z1
global LCM_X2
global LCM_Y2
global LCM_Z2
global LCM_XYZ_1
global LCM_XYZ_2
#If 1 is 0
check_1 = 0
check_2 = 0
check_3 = 0
for i in range(0,3):
if i == 0:
if LCM_X1 == [] and LCM_X2 == []:
check_1 = 1
elif i == 1:
if LCM_Y1 == [] and LCM_Y2 == []:
check_2 = 2
elif i == 2:
if LCM_Z1 == [] and LCM_Z2 == []:
check_3 = 3
F_check = check_1 + check_2 + check_3
if F_check == 1:
LCM_X1.extend(LCM_Y1)
LCM_X2.extend(LCM_Y2)
elif F_check == 2:
LCM_Y1.extend(LCM_X1)
LCM_Y2.extend(LCM_X2)
elif F_check == 3:
if check_2 == 0:
LCM_Z1.extend(LCM_Y1)
LCM_Z2.extend(LCM_Y2)
elif check_2 != 0:
LCM_X1.extend(LCM_Z1)
LCM_X2.extend(LCM_Z2)
LCM_Y1.extend(LCM_Z1)
LCM_Y2.extend(LCM_Z2)
elif F_check == 4:
LCM_X1.extend(LCM_Y1)
LCM_X2.extend(LCM_Y2)
LCM_Z1.extend(LCM_Y1)
LCM_Z2.extend(LCM_Y2)
elif F_check == 5:
LCM_Y1.extend(LCM_X1)
LCM_Y2.extend(LCM_X2)
LCM_Z1.extend(LCM_X1)
LCM_Z2.extend(LCM_X2)
elif F_check == 6:
LCM_X1.append(1)
LCM_X2.append(1)
LCM_Y1.append(1)
LCM_Y2.append(1)
LCM_Z1.append(1)
LCM_Z2.append(1)
LCM_X1 = set(LCM_X1)
LCM_Y1 = set(LCM_Y1)
LCM_Z1 = set(LCM_Z1)
LCM_X2 = set(LCM_X2)
LCM_Y2 = set(LCM_Y2)
LCM_Z2 = set(LCM_Z2)
LCM_XYZ_1 = list(set.intersection(LCM_X1,LCM_Y1,LCM_Z1))
LCM_XYZ_2 = list(set.intersection(LCM_X2,LCM_Y2,LCM_Z2))
LCM_XYZ_1.sort(reverse = True)
LCM_XYZ_2.sort(reverse = True)
Main_Game = get_Main_Game()
Pygame provides the pygame.Color object. The object can construct a color from various arguments (e.g. RGBA color channels, hexadecimal numbers, strings, ...).
It also offers the handy method lerp, that can interpolate 2 colors:
Returns a Color which is a linear interpolation between self and the given Color in RGBA space
Use the pygame.Color object and the lerp method to interpolate a color form a list of colors:
def lerp_color(colors, value):
fract, index = math.modf(value)
color1 = pygame.Color(colors[int(index) % len(colors)])
color2 = pygame.Color(colors[int(index + 1) % len(colors)])
return color1.lerp(color2, fract)
The interpolation value must change over time. Use pygame.time.get_ticks to get the current time in milliseconds:
colors = ["green", "blue", "purple", "pink", "red", "orange", "yellow"]
value = (pygame.time.get_ticks() - start_time) / 1000
current_color = lerp_color(colors, value)
See also Color - Lerp
Minimal example:
import pygame, math
def lerp_color(colors, value):
fract, index = math.modf(value)
color1 = pygame.Color(colors[int(index) % len(colors)])
color2 = pygame.Color(colors[int(index + 1) % len(colors)])
return color1.lerp(color2, fract)
pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
start_time = pygame.time.get_ticks()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
start_time = pygame.time.get_ticks()
colors = ["green", "blue", "purple", "pink", "red", "orange", "yellow"]
value = (pygame.time.get_ticks() - start_time) / 1000
current_color = lerp_color(colors, value)
window.fill((255, 255, 255))
pygame.draw.circle(window, current_color, window.get_rect().center, 100)
pygame.display.flip()
pygame.quit()
exit()

How do I perform a collision check?

I have no clue what to do here, I want to make it so that a turtle in Python will shoot a bullet and if it collides with the other turtle, the score goes up. Except if I run this code, the game will crash when I shoot the bullet. So far, I only tried it for one of the eight move commands.
#imports turtles
import turtle
import random
#Sets screen, turtle program, and colour group
turtle.setup(1000, 800)
screen = turtle.Screen()
wn = turtle.Screen()
wn.bgcolor("springgreen")
amir = turtle.Turtle()
amir.shape("arrow")
amir.shapesize(1)
amir.speed(10)
amir.pensize (2)
amir.color("blue")
amir.setheading(90)
amir.penup()
#bullet 1
jonafat = turtle.Turtle()
jonafat.shape("circle")
jonafat.shapesize(0.5)
jonafat.speed(2)
jonafat.pensize (1)
jonafat.color("black")
jonafat.penup()
jonafat.ht()
hossein = turtle.Turtle()
hossein.shape("arrow")
hossein.shapesize(1)
hossein.speed(10)
hossein.pensize (2)
hossein.color("gold")
hossein.setheading(90)
hossein.penup()
#bullet
jonafat2 = turtle.Turtle()
jonafat2.shape("circle")
jonafat2.shapesize(0.5)
jonafat2.speed(2)
jonafat2.pensize (1)
jonafat2.color("black")
jonafat2.penup()
jonafat2.ht()
#scoreboard
TT = turtle.Turtle()
TT.ht()
TT.penup()
TT.goto(-500,200)
TT.color("black")
TT.write("0", move = False, align = "center", font = ("Arial", 20, "normal"))
#second scoreboard
TT = turtle.Turtle()
TT.ht()
TT.penup()
TT.goto(-500,200)
TT.color("black")
TT.write("0", move = False, align = "center", font = ("Arial", 20, "normal"))
x = 0
y = 0
amirs = 2
hosseins = 2
auto = 15
vanguard = 15
trump = 0
time = 1
score = 0
panda = 295
def up():
global amir
global x
global amirs
global hosseins
amir.seth(90)
n = 1
for i in range(0, n):
amir.sety(amir.ycor()+10)
n + 1
def down():
global amir
global x
global amirs
global hosseins
amir.seth(270)
n = 1
for i in range(0, n):
amir.sety(amir.ycor()-10)
n + 1
def left():
global amir
global x
global amirs
global hosseins
amir.seth(180)
n = 1
for i in range(0, n):
amir.setx(amir.xcor()-10)
n + 1
def right():
global amir
global x
global amirs
global hosseins
amir.seth(0)
n = 1
for i in range(0, n):
amir.setx(amir.xcor()+10)
n + 1
def up2():
global hossein
global y
global hosseins
hossein.seth(90)
n = 1
for i in range(0, n):
hossein.sety(hossein.ycor()+10)
n + 1
def down2():
global hossein
global y
global hosseins
hossein.seth(270)
n = 1
for i in range(0, n):
hossein.sety(hossein.ycor()-10)
n + 1
def left2():
global hossein
global y
global hosseins
hossein.seth(180)
n = 1
for i in range(0, n):
hossein.setx(hossein.xcor()-10)
n + 1
def right2():
global hossein
global y
global hosseins
hossein.seth(0)
n = 1
for i in range(0, n):
hossein.setx(hossein.xcor()+10)
n + 1
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
def clock():
global time
time = time-1
def move():
global auto
global vanguard
global score
global time
wn.ontimer(clock,1000)
angle = amir.heading()
jonafat.ht()
jonafat.speed(10)
jonafat.setpos(amir.xcor(), amir.ycor())
if angle == 0:
trump = 1.
time = time-1
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()+300, amir.ycor())
n + 1
infLoop = 1
while infLoop == 1:
if collisionCheck(jonafat, hossein) == True:
infLoop = 0
break
score = score + 1
TT.clear()
TT.write(score, move = False, align = "center", font = ("Arial", 20, "normal"))
if time == 0:
break
infLoop = 0
if angle == 90:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor(),amir.ycor()+300)
n + 1
if angle == 180:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()-300, amir.ycor())
n + 1
if angle == 270:
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor(), amir.ycor()-300)
n + 1
def move2():
angle2 = hossein.heading()
jonafat2.ht()
jonafat2.speed(10)
jonafat2.setpos(hossein.xcor(), hossein.ycor())
if angle2 == 0:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor()+300, hossein.ycor())
n + 1
if angle2 == 90:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor(), hossein.ycor()+300)
n + 1
if angle2 == 180:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor()-300, hossein.ycor())
n + 1
if angle2 == 270:
jonafat2.showturtle()
jonafat2.speed(2)
n = 1
for i in range(0, n):
jonafat2.goto(hossein.xcor(), hossein.ycor()-300)
n + 1
wn.onkeypress(up, "w")
wn.onkeypress(down, "s")
wn.onkeypress(left, "a")
wn.onkeypress(right, "d")
wn.onkeypress(up2, "Up")
wn.onkeypress(down2, "Down")
wn.onkeypress(left2, "Left")
wn.onkeypress(right2, "Right")
wn.onkeypress(move2, "Shift_R")
wn.onkeypress(move, "space")
wn.listen()
This is the part I have trouble with
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
def clock():
global time
time = time-1
def move():
global auto
global vanguard
global score
global time
wn.ontimer(clock,1000)
angle = amir.heading()
jonafat.ht()
jonafat.speed(10)
jonafat.setpos(amir.xcor(), amir.ycor())
if angle == 0:
trump = 1.
time = time-1
jonafat.showturtle()
jonafat.speed(2)
n = 1
for i in range(0, n):
jonafat.goto(amir.xcor()+300, amir.ycor())
n + 1
infLoop = 1
while infLoop == 1:
if collisionCheck(jonafat, hossein) == True:
infLoop = 0
break
score = score + 1
TT.clear()
TT.write(score, move = False, align = "center", font = ("Arial", 20, "normal"))
if time == 0:
break
infLoop = 0
It appears your only checking if their position is the same, although two objects can pass close to each other so that they visually collide, but aren't on the exact same position as each other.
You'll want to take their dimensions into account to properly check for collisions. The easiest way to check if they collide is to take the distance between their positions and check if it's smaller than a certain value. Although better options exist.
Instead of all this:
def collisionCheck(jonafat, hossein):
crash = True
jonafat1X = jonafat.xcor()
jonafat1Y = jonafat.ycor()
hossein2X = hossein.xcor()
hossein2Y = hossein.ycor()
jonafatPos = (int(jonafat1X), int(jonafat1Y))
hosseinPos = (int(hossein2X), int(hossein2Y))
if jonafatPos != hosseinPos:
crash = False
if jonafatPos == hosseinPos:
crash = True
return crash
Let's take advantage of #JoshuaWaring's advice as well as turtle's distance() method and come up with something more like:
PERSONAL_SPACE = 10 # assign this to be how close is too close
def collisionCheck(jonafat, hossein):
return jonafat.distance(hossein) < PERSONAL_SPACE
Let's rework the rest of the code to produce a minimally playable game:
# imports turtles
from turtle import Turtle, Screen
PERSONAL_SPACE = 15 # assign this to be how close is too close
FIRE_DISTANCE = 300
FONT = ('Arial', 36, 'normal')
# Sets screen, turtle program, and color group
screen = Screen()
screen.setup(1000, 800)
screen.bgcolor('springgreen')
def collisionCheck(bullet, player):
return bullet.distance(player) < PERSONAL_SPACE
def up1():
player1.seth(90)
player1.forward(10)
def down1():
player1.seth(270)
player1.forward(10)
def left1():
player1.seth(180)
player1.forward(10)
def right1():
player1.seth(0)
player1.forward(10)
def clock1():
global time1, score1
if collisionCheck(bullet1, player2):
score1 += 1
TT1.undo()
TT1.write(score1, align='center', font=FONT)
time1 = 1
else:
bullet1.forward(1)
time1 -= 1
if time1:
screen.ontimer(clock1, 10)
else:
bullet1.hideturtle()
def fire1():
global time1
bullet1.setpos(player1.pos())
bullet1.setheading(player1.heading())
bullet1.showturtle()
time1 = FIRE_DISTANCE
screen.ontimer(clock1, 100)
screen.onkeypress(up1, 'w')
screen.onkeypress(down1, 's')
screen.onkeypress(left1, 'a')
screen.onkeypress(right1, 'd')
screen.onkeypress(fire1, 'space')
def up2():
player2.seth(90)
player2.forward(10)
def down2():
player2.seth(270)
player2.forward(10)
def left2():
player2.seth(180)
player2.forward(10)
def right2():
player2.seth(0)
player2.forward(10)
def clock2():
global time2, score2
if collisionCheck(bullet2, player1):
score2 += 1
TT2.undo()
TT2.write(score2, align='center', font=FONT)
time2 = 1
else:
bullet2.forward(1)
time2 -= 1
if time2:
screen.ontimer(clock2, 10)
else:
bullet2.hideturtle()
def fire2():
global time2
bullet2.setpos(player2.pos())
bullet2.setheading(player2.heading())
bullet2.showturtle()
time2 = FIRE_DISTANCE
screen.ontimer(clock2, 100)
screen.onkeypress(up2, 'Up')
screen.onkeypress(down2, 'Down')
screen.onkeypress(left2, 'Left')
screen.onkeypress(right2, 'Right')
screen.onkeypress(fire2, '0')
player1 = Turtle('arrow')
player1.speed('fastest')
player1.color('blue')
player1.setheading(180)
player1.penup()
player1.forward(10)
# bullet 1
bullet1 = Turtle('circle', visible=False)
bullet1.shapesize(0.5)
bullet1.speed('fastest')
bullet1.color('black', 'blue')
bullet1.penup()
player2 = Turtle('arrow')
player2.speed('fastest')
player2.color('gold')
player2.setheading(0)
player2.penup()
player2.forward(10)
# bullet 2
bullet2 = Turtle('circle', visible=False)
bullet2.shapesize(0.5)
bullet2.speed('fastest')
bullet2.color('black', 'gold')
bullet2.penup()
# scoreboard
TT1 = Turtle(visible=False)
TT1.penup()
TT1.goto(-450, 300)
TT1.color('blue')
TT1.write('0', align='center', font=FONT)
# second scoreboard
TT2 = Turtle(visible=False)
TT2.penup()
TT2.goto(450, 300)
TT2.color('gold')
TT2.write('0', align='center', font=FONT)
time1 = 0
score1 = 0
time2 = 0
score2 = 0
screen.listen()
screen.mainloop()
Still needs additional work, i.e. doesn't handle (or disable) multiple shots correctly, etc.

Python screen is black for a small amount of time then closes

The code in this gist (https://gist.github.com/tobias76/8dc2e1af90f1916a2106) is completely broken and nothing happens excl. a black screen and closure after seconds, I would be more specific with the code but there is absolutely no error message. As far as the program is concerned, it worked. As requested, here is my code (For some reason stack overflow doesn't like all of the code being formatted);
==============================================
import pygame
import sys
import random
from pygame.locals import *
pygame.init()
FPS = 60
FPSClock = pygame.time.Clock()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Graphical Slot Machine")
reelgroupone = pygame.sprite.Group()
reelgrouptwo = pygame.sprite.Group()
reelgroupthree = pygame.sprite.Group()
reelone = (reelgroupone, 1)
reeltwo = (reelgrouptwo, 2)
reelthree = (reelgroupthree, 3)
image = "assets/apple.png"
image2 = "assets/bar.png"
image3 = "assets/cherry.png"
image4 = "assets/orange.png"
background = "background.jpg"
class Reels(pygame.sprite.Sprite):
def __init__(self, reelgroup, reelnumbers):
self.reelgroup = reelgroup
self.reelnumber = reelnumbers
self.reellist = [0, 1, 2, 3, 4, 5]
self.reelmove = 1
self.reelnudge = 0
self.stoptime = 60
def start(self):
self.reelmove = 1
self.stoptime = 60
def stop(self):
self.reelmove = 0
def update(self):
if self.stoptime > 0:
self.stoptime -= 1
self.reelgroup.update()
else:
self.stop()
if self.reelnudge == 1:
self.reelmove = 0
def draw(self):
self.reelgroup.draw(screen)
class Fruit(pygame.sprite.Sprite):
def __init__(self, reelgroup, reel, FruitID):
pygame.sprite.Sprite.__init__(self)
self.reelgroup = reelgroup
self.FruitID = FruitID
self.machineReel = reel
# Depending on Reel ID use a specific image, place the file path in the string
if self.FruitID == 1:
self.reelImage = "assets/apple.png"
if self.FruitID == 2:
self.reelImage = "assets/bar.png"
if self.FruitID == 3:
self.reelImage = "assets/cherry.png"
if self.FruitID == 4:
self.picture = "assets/orange.png"
self.pic = pygame.image.load(self.reelImage).convert_alpha()
self.where = ((self.machineReel * 155) - 30, 490)
self.rectangle = self.pic.get_rect()
self.rectangle.TopLeft = self.where
# Make reels faster / slower here.
self.reelSpeed = 8
self.reelgroup.add(self)
def fruitupdate(self):
self.rectangle.y -= self.reelSpeed
if self.rectangle.y < 110:
self.kill()
class main():
def __init__(self):
self.money = 10
self.counter = 5
self.fruitlist = [[0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0]]
self.finished = 0
self.message = ""
def splash(self):
pass
def machine(self):
while self.credits > 0:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
self.keys = pygame.key.get_pressed()
# Change this key to whatever you want
if self.keys[K_p] and reelone.reelmove == reeltwo.reelmove == reelthree.reelmove == 0:
self.money -= 1
self.counter = 5
self.finished = 0
reelone.start()
reeltwo.start()
reelthree.start()
self.message = ""
if self.keys[K_ESCAPE]:
pygame.quit()
sys.exit()
# Put your image and font blitting here.
screen.blit(background,(0,0))
if self.money >= 10:
self.counter = 1
if reelone.reelMove == 1 and reelone.stopTime % 10:
fruit = Fruit(reelgroupone, 1, random.randint(1,4))
del self.fruitlist[0][0]
self.fruitlist[0].append(fruit.ID)
if reeltwo.reelMove == 1 and reeltwo.stopTime % 10:
fruit = Fruit(reelgrouptwo, 2, random.randint(1,4))
del self.fruitlist[1][0]
self.fruitlist[1].append(fruit.ID)
if reelthree.reelMove == 1 and reelthree.stopTime % 10:
fruit = Fruit(reelgroupthree, 3, random.randint(1,4))
del self.fruitlist[2][0]
self.fruitlist[2].append(fruit.ID)
else:
self.counter += 1
if reelone.reelmove == 1:
reelone.update()
if reeltwo.reelmove == 1:
reeltwo.update()
if reelthree.reelmove == 1:
reelthree.update()
# If all the reels are moving, check if the player has won and add the credits to their
# account if so.
if reelone.reelmove == reeltwo.reelmove == reelthree.reelmove == 0 and self.finished == 0:
if self.fruitlist[0][2] == self.fruitlist[1][2] == self.fruitlist[2][2]:
self.message = "Winner, want to play again?"
if self.fruitlist[0][2] == 1:
self.credits += 10
if self.fruitlist[0][2] == 1:
self.credits += 100
if self.fruitlist[0][2] == 3:
self.credits += 1000
if self.fruitlist[0][2] == 4:
self.credits += 10000
# Now this sets the state to finished
self.finished = 1
else:
self.message = "Sorry, no win this time!"
self.end = 1
reelgroupone.draw(screen)
reelgrouptwo.draw(screen)
reelgroupthree.draw(screen)
FPSClock.tick(FPS)
pygame.display.update()
fruit = main()
You're not actually running the main() as a function in your code, main is a class instead. Change main to a function, and instead, write this at the bottom of the program:
If __name__ == "__main__":
main()
This way, the code actually runs. Also, get rid of fruit = main(). It just makes it more confusing and you're going to be running it anyways with this, hence why it says that the process finished. You may also want to move things previously in the main class in the main function. For example, take functions inside of the class such as the machine() function and put it as a separate function outside of your code, and call it in the main function with machine().

Strange things with tkinter's canvas.after()

Ive been having some issues where if the shot is fired multiple times before it leaves the canvas it will speed up each time it is redrawn until its faster than the canvas can update it. Ive attributed the problem to the canvas.after command because if I impliment this as a while loop using the time.sleep command it works fine (unfortunately I can use the implementation of the code because it works as two separate loops)
#imports
from tkinter import *
#The Game
class Game:
def __init__(self):
#creating the static canvas background
self.window = Tk()
self.window.title('Shoot your friends')
self.canvas = Canvas(width= 900,
height= 900,
cursor= 'circle')
self.canvas.pack()
self.canvas.create_line(450, 900,
450, 0,
dash = (10))
self.p1_ship = PhotoImage(file = "red_ship.gif")
self.p2_ship = PhotoImage(file = "blue_ship.gif")
self.p1_laser = PhotoImage(file = "red_laser.gif")
self.p2_laser = PhotoImage(file = "blue_laser.gif")
#Buttons at the bottom
self.frame = Frame(self.window)
self.frame.pack()
#Determining the state of edge teleporting and toggling it
self.etb = True
def et():
if self.etb == True:
self.etb = False
Et["text"] = "Turn On Edge Teleporting"
else:
self.etb = True
Et["text"] = "Turn Off Edge Teleporting"
print ("Edge Telepoting Toggled")
Et = Button(self.frame, text="Turn Off Edge Teleporting", command = et, cursor= 'double_arrow')
Et.grid(row=0,column=0)
self.Rfb = False
def rf():
if self.Rfb == True:
self.Rfb = False
Rf["text"] = "Turn On Rapid Fire "
else:
self.Rfb = True
Rf["text"] = "Turn Off Rapid Fire"
print ("Rapid Fire Toggled")
Rf = Button(self.frame, text="Turn On Rapid Fire", command = rf, cursor= 'cross')
Rf.grid(row=0,column=1)
def restart():
print ('restart')
restart_b = Button(self.frame, text="Restart Game", command = restart, fg='Blue', bg= 'red', cursor='exchange' )
restart_b.grid(row=0,column=2)
self.y_p1 = 400
self.y_p2 = 400
self.ship_p1 = self.canvas.create_image(40, 450, image=self.p1_ship)
self.ship_p2 = self.canvas.create_image(860, 450, image=self.p2_ship)
self.canvas.move(self.ship_p1,0,0)
self.canvas.move(self.ship_p2,0,0)
# Functions that handle movement of the ships taking into account multiple variables
#For example If edge teleporting is ON the ship will teleport to the top of the screen if it is at the bottom and the down key is pressed and vice versa
#My implementation of this may not be the most efficient but I like the options it gives me for adding future features and it looks cool.
def p1_up(event):
if self.etb == True and self.y_p1 >= 100:
self.canvas.move(self.ship_p1,0,-100)
self.y_p1 += -100
elif self.etb == True:
self.canvas.move(self.ship_p1,0,+800)
self.y_p1 += +800
elif self.y_p1 >= 100:
self.canvas.move(self.ship_p1,0,-100)
self.y_p1 += -100
def p1_down(event):
if self.etb == True and self.y_p1 <= 799:
self.canvas.move(self.ship_p1,0,+100)
self.y_p1 += 100
elif self.etb == True:
self.canvas.move(self.ship_p1,0,-800)
self.y_p1 += -800
elif self.y_p1 <= 799:
self.canvas.move(self.ship_p1,0,+100)
self.y_p1 += 100
def p2_up(event):
if self.etb == True and self.y_p2 >= 100:
self.canvas.move(self.ship_p2,0,-100)
self.y_p2 += -100
elif self.etb == True:
self.canvas.move(self.ship_p2,0,+800)
self.y_p2 += +800
elif self.y_p2 >= 100:
self.canvas.move(self.ship_p2,0,-100)
self.y_p2 += -100
def p2_down(event):
if self.etb == True and self.y_p2 <= 799:
self.canvas.move(self.ship_p2,0,+100)
self.y_p2 += 100
elif self.etb == True:
self.canvas.move(self.ship_p2,0,-800)
self.y_p2 += -800
elif self.y_p2 <= 799:
self.canvas.move(self.ship_p2,0,+100)
self.y_p2 += 100
# Functions for shooting
self.p1_shot_out = False
self.p2_shot_out = False
def p1_shoot(event):
if self.p1_shot_out == True:
self.canvas.delete(self.laser_p1)
#draws the laser
self.laser_p1 = self.canvas.create_image(50, self.y_p1 +50, image=self.p1_laser)
self.x_p1_laser = 50
self.p1_shot_out = True
self.window.after(1, p1_shoot_move)
def p1_shoot_move():
#moves the laser until its outside the canvas
if self.x_p1_laser >= 930:
pass
else:
self.canvas.move(self.laser_p1,5,0)
self.x_p1_laser += 5
self.canvas.update()
self.window.after(3, p1_shoot_move)
def p2_shoot(event):
if self.p2_shot_out == True:
self.canvas.delete(self.laser_p2)
#draws the laser
self.laser_p2 = self.canvas.create_image(750, self.y_p2 +50, image=self.p2_laser)
self.x_p2_laser = 750
self.p2_shot_out = True
self.window.after(4, p2_shoot_move)
def p2_shoot_move():
#moves the laser until its outside the canvas
if self.x_p2_laser <= -110:
pass
else:
self.canvas.move(self.laser_p2,-5,0)
self.x_p2_laser += -5
self.canvas.update()
self.window.after(4, p2_shoot_move)
# Key bindings that trigger their respective functions
self.canvas.bind('w', p1_up)
self.canvas.bind('s', p1_down)
self.canvas.bind('<Up>', p2_up)
self.canvas.bind('<Down>', p2_down)
self.canvas.bind('<space>', p1_shoot)
self.canvas.bind('<Control_R>', p2_shoot)
self.canvas.focus_set()
# this mainloop thing is some sort of witchcraft! OH MY!!!
self.window.mainloop()
Game()
The problem is that each time you "shoot", you call after. If you "shoot" five times in rapid succession, you'll have 5 "moves" queued up. Each adds 5 to the x location, and they all will run just a few ms apart, so the laser will appear to be moving very fast. The five will run in rapid succession, then 3ms later they will run in rapid succession again, and so on.
What you need to do is keep a reference to the thing you schedule with after, and cancel it before starting a new shot. That, or keep an independent record of the x/y coordinate for each shot if you want more than one shot being active at a time.
For example:
def p1_shoot(event):
....
# cancel any previous shot
if self.p1_job is not None:
self.window.after_cancel(self.p1_job)
self.p1_job = self.window.after(1, p1_shoot_move)
Of course, you need to set self.p1_job every time you call after, and you need to initialize it to None at the start, and when the bullet is destroyed.

Tkinter - Canvas.move() doesn't move the last item in the list

I'm trying to write a Snake Game program using Tkinter
My snake consists of List of Circle's defined below. However, when i call the move method the last one doesn't move. What is the problem?
class Circle:
def __init__(self,canv,x,y,index,direc):
self._x = x
self._y = y
self._index = index
self._direction = direc
self._canvas = canv
coordinat = (self._x)-5,(self._y)-5,(self._x)+5,(self._y)+5
if index==0:
color = "gold"
else:
color = "green"
self._circle = canv.create_oval(coordinat ,fill=color, tags=str(self._index))
def _move(self):
if self._direction == "up":
delta = (0,-5)
elif self._direction == "down":
delta = (0,5)
elif self._direction == "left":
delta = (-5,0)
elif self._direction == "right":
delta = (5,0)
tag = str(self._index)
self._canvas.move(tag, delta[0],delta[1])
and her is how i call it
self._canvas = Canvas(self._playArea,width=750,height=450,bg="#1C1C1C")
x = int(self._parent.winfo_width() / 4.)
y = int(self._parent.winfo_height()/ 4.)
circle = Circle(self._canvas,x,y,0,"right")
self._circleList.append(circle)
self._addCircle()
self._addCircle()
self._addCircle()
self._addCircle()
self._addCircle()
self._canvas.place(x=0, y=0)
for i in range(0,500):
for x in self._circleList:
x._move()
root.update()
root.after(10)
and this is the addCircle method
length = len(self._circleList)
if self._circleList[length-1]._direction == "right":
x = (self._circleList[length-1]._x)-10
y = self._circleList[length-1]._y
d = "right"
elif self._circleList[length-1]._direction == "left":
x = (self._circleList[length-1]._x) + 10
y = self._circleList[length-1]._y
d = "left"
elif self._circleList[length-1]._direction == "up":
x = self._circleList[length-1]._x
y = (self._circleList[length-1]._y)+10
d = "up"
elif self._circleList[length-1]._direction == "down":
x = self._circleList[length-1]._x
y = (self._circleList[length-1]._y)-10
d = "down"
newCircle = Circle(self._canvas,x,y,length,d)
self._circleList.append(newCircle)
The problem is that you are creating the first item with index=0, and then using that index as the tag to identify the item. You are not supposed to use integers as tags, that is reserved for the id of the items, but, well, you can use it after all. Except that 0 is evaluated as false by Tcl, so you are not effectively defining any tags for it. It turns out you that when you call canvas.move(0, ...) you don't move any of the created circles. But the next item you create is assigned a tag "1", and when you call canvas.move(1, ...) you are actually moving the item you created earlier (the one with "gold" color) because it was automatically assigned the id "1" by Tcl. Repeat for all the other circles created.
The quick way to solve it is by changing the code to use index + 1 for all these indices you are passing. But there are several issues in the code you included, so here is a modified one that performs what you are after:
import Tkinter
UP, RIGHT, DOWN, LEFT = range(4)
class Circle:
def __init__(self, canv, x, y, direc, color):
self.x, self.y = x, y
self.direction = direc
self._canvas = canv
coord = (self.x)-5, (self.y)-5, (self.x)+5, (self.y)+5
self._index = canv.create_oval(coord, fill=color)
def move(self):
y_sign = 1 if self.direction == DOWN else -1
x_sign = 1 if self.direction == RIGHT else -1
if self.direction in (UP, DOWN):
delta = (0, y_sign * 5)
else:
delta = (x_sign * 5, 0)
self._canvas.move(self._index, delta[0], delta[1])
def add_circle(canvas, l):
d = l[-1].direction
if d in (UP, DOWN):
x = l[-1].x
y = (1 if d == UP else -1) * 10 + l[-1].y
else:
x = (1 if d == LEFT else -1) * 10 + l[-1].x
y = l[-1].y
circle = Circle(canvas, x, y, d, "green")
l.append(circle)
def move_circles(circle_l, root):
for c in circle_l:
c.move()
root.after(50, lambda: move_circles(circle_l, root))
root = Tkinter.Tk()
width, height = 750, 450
canvas = Tkinter.Canvas(width=width, height=height, bg="#1C1C1C")
canvas.pack(fill=None, expand=False)
circle_l = []
circle = Circle(canvas, width / 4, height / 4, RIGHT, "gold")
circle_l.append(circle)
for _ in range(5):
add_circle(canvas, circle_l)
move_circles(circle_l, root)
root.mainloop()
Not really an answer, but you can make your last piece of code a lot shorter / more efficient:
length = len(self._circleList)
x = self._circleList[length-1]._x
y = self._circleList[length-1]._y
d = self._circleList[length-1]._direction
if d == "right":
x -= 10
elif d == "left":
x += 10
elif d == "up":
y += 10
elif d == "down":
y -= 10
newCircle = Circle(self._canvas,x,y,length,d)
self._circleList.append(newCircle)
Set defaults first, then only modify the x or y if they have to be.

Categories

Resources