Related
I am trying to make a catch the egg game and it is going fine apart from the basket, which is supposed to look like an arc upside-down but it became a very strange shape, and I have searched as far as I could but no one has had such a problem like this.
I want it to look like this:
I'll have to post the whole program otherwise you wouldn't understand:
from itertools import cycle
from random import randrange
from tkinter import Canvas, Tk, messagebox, font
canvas_width = 800
canvas_height = 400
root = Tk()
c = Canvas(
root,
width=canvas_width,
height=canvas_height,
background="deep sky blue"
)
c.create_rectangle(
-5,
canvas_height - 100,
canvas_width + 5,
canvas_height + 5,
fill="sea green",
width=0
)
c.create_oval(-80, -80, 120, 120, fill="orange", width=0)
c.pack()
color_cycle = cycle([
"light blue",
"light green",
"light pink",
"light yellow",
"light cyan"
])
egg_width = 45
egg_height = 55
egg_score = 10
egg_speed = 500
egg_interval = 4000
difficulty_factor = 0.95
catcher_color = "blue"
catcher_width = 100
catcher_height = 100
catcher_start_x = canvas_width / 2 - catcher_width / 2
catcher_start_y = canvas_height - catcher_width - 20
catcher_start_x2 = catcher_start_x = catcher_width
catcher_start_y2 = catcher_start_y + catcher_height
catcher = c.create_arc(
catcher_start_x,
catcher_start_y,
catcher_start_x2,
catcher_start_y2,
start=200,
extent=140,
style="arc",
outline=catcher_color,
width=30
)
game_font = font.nametofont("TkFixedFont")
game_font.config(size=18)
score = 0
score_text = c.create_text(
10,
10,
anchor="nw" ,
font=game_font,
fill="darkblue",
text="Score: " + str(score)
)
lives_remaining = 3
lives_text = c.create_text(
canvas_width - 10,
10,
anchor="ne",
font=game_font,
fill="darkblue",
text="Lives " + str(lives_remaining)
)
eggs = []
def create_egg():
x = randrange(10, 740)
y = 40
new_egg = c.create_oval(
x,
y,
x + egg_width,
y + egg_height,
fill=next(color_cycle),
width=0
)
eggs.append(new_egg)
root.after(egg_interval, create_egg)
def move_eggs():
for egg in eggs:
(egg_x, egg_y, egg_x2, egg_y2) = c.coords(egg)
c.move(egg, 0, 10)
if egg_y2 > canvas_height:
egg_dropped(egg)
root.after(egg_speed, move_eggs)
def egg_dropped(egg):
eggs.remove(egg)
lose_a_life()
if lives_remaining == 0:
messagebox.showinfo("Game Over!", "Final Score: " + str(score))
root.destroy()
def lose_a_life():
global lives_remaining
lives_remaining -= 1
c.itemconfigure(lives_text, text = "Lives: " + str(lives_remaining))
def check_catch():
catcher_x, catcher_y, catcher_x2, catcher_y2 = c.coords(catcher)
for egg in eggs:
egg_x, egg_y, egg_x2, egg_y2 = c.coords(egg)
if egg_x<catcher_x<egg_x2 and (catcher_y2 - egg_y2) < 5:
eggs.remove(egg)
c.delete(egg)
increase_score(egg_score)
root.after(100, check_catch)
def increase_score(points):
global score, egg_speed, egg_interval
score += points
egg_speed = int(egg_speed * difficulty_factor)
egg_interval = int(egg_interval * difficulty_factor)
c.itemconfigure(score_text, text="Score: " + str(score))
def move_left(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x1 > 0:
c.move(catcher, -20, 0)
def move_right(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x2 < canvas_width:
c.move(catcher, 20, 0)
c.bind("<Left>" , move_left)
c.bind("<Right>", move_right)
c.focus_set()
root.after(1000, create_egg)
root.after(1000, move_eggs)
root.after(1000, check_catch)
root.mainloop()
You have a typo on line 27 - you have written catcher_start_x2 = catcher_start_x = catcher_width where you should have catcher_start_x2 = catcher_start_x - catcher_width.
Also, the backslash on line 29 is unnecessary as things contained in brackets automatically continue on multiple lines.
One more thing - once the typo is fixed, the eggs are only caught in the left area of the catcher because line 79 (if egg_x<catcher_x<egg_x2 and (catcher_y2 - egg_y2) < 5:) is checking if the left x coord of the catcher is inside the x coords of the egg not the other way around. To fix this, replace it with
if catcher_x - 10 < egg_x and egg_x2 < catcher_x2 + 10 and (catcher_y2 - egg_y2) < 5: (the -10 and +10 are to give the player a bit of leeway so the egg doesn't have to be exactly in the centre).
I wanted to get a little square to move to the left 10 pixels every 3 seconds and my code is below. I'm not sure why it only moves once. Some help would be greatly apprecaited!
import tkinter as tk
import time
root = tk.Tk()
WIDTH = HEIGHT = 400
x1 = y1 = WIDTH / 2
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
c1 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10)
c2 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10)
def draw_rect():
global c2
canvas.delete(c2)
c2 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="green")
def del_rect():
canvas.delete(c1)
#canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="white", opacity=0.5)
while 1:
root.update()
time.sleep(3)
del_rect()
x1 -= 10
draw_rect()
root.mainloop()
mainloop() runs loop which works until you close window - so it blocks all loop and it is executed only once and it move rectangle only once. In your version you should remove mainloop() and it will not block it - and because you use update() in loop so it will run correctly.
But you could do this in different way.
You can use root.after(3000, draw_rect) to execute draw_rect() after 3 seconds. And draw_rect() should run again root.after(3000, draw_rect) to run it again after another 3 seconds - this way it will loop and it will not blocked by mainloop() (mainloop() will run draw_rect() every 3 seconds)
Canvas has function move(object, dx, dy) so you don't have to delete rectangle and create it again.
I use 300ms to run it faster.
import tkinter as tk
# --- constants --- (PEP8: UPPER_CASE_NAMES)
WIDTH = 400
HEIGHT = 400
# --- functions ---
def move_rect():
canvas.move(c1, -10, 0) # move left
canvas.move(c2, 10, 0) # move right
# run again after 300ms
root.after(300, move_rect) # 300ms = 0.3s # I use smaller value to make it faster
# --- main ---
x1 = y1 = WIDTH / 2
root = tk.Tk()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
c1 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="green")
c2 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="red")
# run first time after 300ms
root.after(300, move_rect) # 300ms = 0.3s # I use smaller value to make it faster
root.mainloop()
PEP 8 -- Style Guide for Python Code
EDIT:
Version which uses variables speed1, speed2 to change direction when rectangle is near border.
import tkinter as tk
# --- constants --- (PEP8: UPPER_CASE_NAMES)
WIDTH = 400
HEIGHT = 400
# --- functions ---
def move_rect():
global speed1
global speed2
#x1, y1, x2, y2 = canvas.coords(c1)
pos = canvas.coords(c1)
if speed1 < 0 :
if pos[0] < 10:
speed1 = -speed1
else:
if pos[2] > WIDTH-10:
speed1 = -speed1
#x1, y1, x2, y2 = canvas.coords(c2)
pos = canvas.coords(c2)
if speed2 < 0:
if pos[0] < 10:
speed2 = -speed2
else:
if pos[2] > WIDTH-10:
speed2 = -speed2
canvas.move(c1, speed1, 0) # move left
canvas.move(c2, speed2, 0) # move right
# run again after 30ms
root.after(30, move_rect) # 30ms = 0.03s - I uses smaller value to make it faster
# --- main ---
x1 = y1 = WIDTH / 2
speed1 = -10
speed2 = +10
root = tk.Tk()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
print(dir(canvas))
c1 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="green")
c2 = canvas.create_rectangle(x1, y1, x1 + 10, y1 + 10, fill="red")
# run first time after 300ms
root.after(300, move_rect) # 300ms = 0.3s - I uses smaller value to make it faster
root.mainloop()
So I copied this code from a python book, and the code is meant to be like an egg catcher game, however if you run the code, you can see, tkinter creates everything and the game sets up, however, the game itself does not start. Does anyone know how to make it start? I tried pressing buttons and making the whole thing a function, however, they both do not work. Here is my code below...
from itertools import cycle
from random import randrange
from tkinter import Canvas, Tk, messagebox, font
canvas_width = 800
canvas_height = 400
root = Tk()
c = Canvas(root, width=canvas_width, height=canvas_height, background='deep sky blue')
c.create_rectangle(-5, canvas_height - 100, canvas_width + 5, canvas_height + 5, \
fill='sea green', width=0)
c.create_oval(-80, -80, 120, 120, fill='orange', width=0)
c.pack()
color_cycle = cycle(['light blue', 'light green', 'light pink', 'light yellow', 'light cyan'])
egg_width = 45
egg_height = 55
egg_score = 10
egg_speed = 500
egg_interval = 4000
difficulty_factor = 0.95
catcher_color = 'blue'
catcher_width = 100
catcher_height = 100
catcher_start_x = canvas_width / 2 - catcher_width / 2
catcher_start_y = canvas_height - catcher_height - 20
catcher_start_x2 = catcher_start_x + catcher_width
catcher_start_y2 = catcher_start_y + catcher_height
catcher = c.create_arc(catcher_start_x, catcher_start_y, \
catcher_start_x2, catcher_start_y2, start=200, extent=140, \
style='arc', outline=catcher_color, width=3)
game_font = font.nametofont('TkFixedFont')
game_font.config(size=18)
score = 0
score_text = c.create_text(10, 10, anchor='nw', font=game_font, fill='darkblue', \
text='Score: ' + str(score))
lives_remaining = 3
lives_text = c.create_text(canvas_width - 10, 10, anchor='ne', font=game_font, fill='darkblue', \
text='Lives: ' + str(lives_remaining))
eggs = []
def create_egg():
x = randrange(10, 740)
y = 40
new_egg = c.create_oval(x, y, x + egg_width, y + egg_height, fill=next(color_cycle), width=0)
eggs.append(new_egg)
root.after(egg_interval, create_egg)
def move_eggs():
for egg in eggs:
(egg_x, egg_y, egg_x2, egg_y2) = c.coords(egg)
c.move(egg, 0, 10)
if egg_y2 > canvas_height:
egg_dropped(egg)
root.after(egg_speed, move_eggs)
def egg_dropped(egg):
eggs.remove(egg)
c.delete(egg)
lose_a_life()
if lives_remaining == 0:
messagebox.showinfo('Game Over!', 'Final Score: ' + str(score))
root.destroy()
def lose_a_life():
global lives_remaining
lives_remaining -= 1
c.itemconfigure(lives_text, text='Lives: ' + str(lives_remaining))
def check_catch():
(catcher_x, catcher_y, catcher_x2, catcher_y2) = c.coords(catcher)
for egg in eggs:
(egg_x, egg_y, egg_x2, egg_y2) = c.coords(egg)
if catcher_x < egg_x and egg_x2 < catcher_x2 and catcher_y2 - egg_y2 < 40:
eggs.remove(egg)
c.delete(egg)
increase_score(egg_score)
root.after(100, check_catch)
def increase_score(points):
global score, egg_speed, egg_interval
score += points
egg_speed = int(egg_speed * difficulty_factor)
egg_interval = int(egg_interval * difficulty_factor)
c.itemconfigure(score_text, text='Score: ' + str(score))
def move_left(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x1 > 0:
c.move(catcher, -20, 0)
def move_right(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x2 < canvas_width:
c.move(catcher, 20, 0)
c.bind('<Left>', move_left)
c.bind('<Right>', move_right)
c.focus_set()
root.after(1000, create_egg)
root.after(1000, move_eggs)
root.after(1000, check_catch)
root.mainloop()
I see lots of functions but no calls to them, especially the function that runs the Tkinter main loop.
Given the similarity you would expect between the move_left() and move_right() functions, it's a near certainty that the final lines of the latter should not be indented to where they are.
With your current code, the lines doing the binding and timer call-back setups are only executed when move_right() is called. Which it isn't, because the key binding that would call it is only set up when it's called (which it isn't because ...
WARNING W-42: thought process terminated due to likely stack overflow in wet-ware :-)
In other words, your code should be something like:
def move_left(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x1 > 0:
c.move(catcher, -20, 0)
def move_right(event):
(x1, y1, x2, y2) = c.coords(catcher)
if x2 < canvas_width:
c.move(catcher, 20, 0)
# Run this at top level, NOT as part of move_right().
c.bind('<Left>', move_left)
c.bind('<Right>', move_right)
c.focus_set()
root.after(1000, create_egg)
root.after(1000, move_eggs)
root.after(1000, check_catch)
root.mainloop()
As an aside, it may just be the way you pasted the code in, but you should try to follow the PEP8 style guide when writing code, specifically the four-space-indent guideline. They generally make your code much easier to read and/or analyse.
And, just so you're aware, those move_xxx() functions will actually allow your catcher to go off the edges of the screen (up to twenty pixels, I think). If that's not what you want, you may want to make a small change:
move_sz = 20
def move_left(event):
(x1, y1, x2, y2) = c.coords(catcher)
c.move(catcher, -min(move_sz, x1), 0)
def move_right(event):
(x1, y1, x2, y2) = c.coords(catcher)
c.move(catcher, min(move_sz, canvas_width - x2 - 1), 0)
I haven't tested that (particularly the possibility of being out by one pixel - I'll leave that up to you) but the idea is just to limit how much you move if that would send your catcher outside the canvas bounds.
You'll see I've also replaced the magic constant 20 with a more appropriate variable - that's so you only need to change it in one place should you want to adjust the movement speed.
from tkinter import *
import random
tk = Tk()
canvas = Canvas(tk, width=400, height=400)
canvas.pack()
for x in range(0, 40):
x1 = random.randint(0,400)
y1 = random.randint(0,400)
x2 = random.randint(0,400)
y2 = random.randint(0,400)
x3 = random.randint(0,400)
y3 = random.randint(0,400)
my_triangle = canvas.create_polygon(x1, y1, x2, y2, x3, y3,\
fill =("blue"), outline="red")
Hi! I am playing with tkinter and generating random triangles. The problem is:
I want to use random on fill = " " to generate random colors
random.randint(start, end) returns numbers, but fill accepts only strings like
fill ="red" or hexadecimal = "#RGB"
if I enter a numeric valuer like fill = (1,1,0) it doesn't work. How could I generate random string values in fill?
Thank you
Simply use random.sample() from a known list of colors. And you can find the python tkinter color chart enumerated here. You can then randomize for both fill and outline values:
COLORS = ['snow', 'ghost white', 'white smoke', 'gainsboro', 'floral white', 'old lace' ...]
for x in range(0, 40):
x1 = random.randint(0,400)
y1 = random.randint(0,400)
x2 = random.randint(0,400)
y2 = random.randint(0,400)
x3 = random.randint(0,400)
y3 = random.randint(0,400)
my_triangle = canvas.create_polygon(x1, y1, x2, y2, x3, y3,\
fill = (random.sample(COLORS, 1)[0]),
outline = random.sample(COLORS, 1)[0])
Of course, always seed if you want to reproduce same exact random generated numbers:
random.seed(444) # WHERE 444 IS ANY INTEGER
In alternative just generate your random color and format it:
from tkinter import *
import random
def random_color():
return random.randint(0,0x1000000)
tk = Tk()
canvas = Canvas(tk, width=400, height=400)
canvas.pack()
for x in range(0, 40):
color = '{:06x}'.format(random_color())
x1 = random.randint(0,400)
y1 = random.randint(0,400)
x2 = random.randint(0,400)
y2 = random.randint(0,400)
x3 = random.randint(0,400)
y3 = random.randint(0,400)
my_triangle = canvas.create_polygon(x1, y1, x2, y2, x3, y3,\
fill =('#'+ color), outline="red")
tk.mainloop()
so there are a lot of problems going on with this. I attempted to make a snake game in python but It was just a mess so now I'm trying to make it where the only direction they can go after the initial right movement is down and they have to get to the coordinates of the food otherwise it starts over. if they 'eat' the food/square then it will make another level with ought starting new game so I can keep score, however the grow definition doesn't seem to be working at all no matter what I put under the if's. Im using python tkinter.
import Tkinter
import random
import math
from Tkinter import *
import time
# Create root window
####
root = Tkinter.Tk()
#####
# Create Model
######
speed_intvar = Tkinter.IntVar()
speed_intvar.set(1) # Initialize y coordinate
# radius and x-coordinate of circle
new_dir = 0
leng = 40
var = 1
x1 = 10
y1 = 10
x2 = 50
y2 = 10
x3 = x2
y3 = 10
direction = 3
foodx1 = 1
foodx2 = 8
foody1 = 1
foody2 = 8
food_present = 0
length = 8
food = 0# radians of angle in standard position, ccw from positive x axis
######
# Create Controller
#######
# Instantiate and place slider
# Create and place directions for the user
#canvas = Tkinter.Canvas(root, width=900, height=900, background='#FFFFFF')
#canvas.grid(row=0, rowspan=2, column=2)
#text = Tkinter.Button(root, text='Use WASD to move', command = new_game)
#text.grid(row=0, column =2)
#text = Tkinter.Label(root, text='Use WASD to move')
#text.grid(row=0, column =2)
######
# Create View
#######
# Create and place a canvas
def new_game():
c.delete("all")
canvas = Tkinter.Canvas(root, width=900, height=900, background='#FFFFFF')
canvas.grid(row=0, rowspan=2, column=1)
# Create a circle on the canvas to match the initial model
circle_item = canvas.create_line(x1, y1, x2, y2, x2, y3, fill='black', width = "8")
#circle_item2 = canvas.create_rectangle(8, 1, 9, 8,
# outline='#000000', fill='black')
#def move(event):
# if event.char=='w'
def food():
global foodx1, foodx2, foody1, foody2, food_present, food, var
food_present = 0
if food_present==0:
foodx1 = random.randint(1, 900)
foody1 = random.randint(1,900)
foodx2 = foodx1 + 8
foody2 = foody1 + 8
food = canvas.create_rectangle(foodx1, foody1, foodx2, foody2, fill = 'black')
food_present += 1
food()
#def make_longer():
# global food_present, food_list, x1, y1, x2, y2, circle_item
# #x1 = canvas.coords(circle_item)
# #y1 = canvas.coords(circle_item)
# #x2 = canvas.coords(circle_item)
# #y2 = canvas.coords(circle_item)
# xx1, yy1, xx2, yy2 = canvas.coords(circle_item)
# xx1 -= 10
# xx2 -= 50
# for i in range(len(food_list)):
# circle_item2=canvas.create_rectangle(xx1, yy1, xx2, yy2, fill ="black")
#make_longer()
def animate():
global x1, y1, x2, y2, x3, y3, var
x1, y1, x2, y2, x3, y3 = canvas.coords(circle_item)
#for i in range(food_present):
if var == 1:
x1 += 1
x2 += 1
#x3+= 1
if var == 4:
y1+=1
y2+=1
y3+=1
if var == 5:
x1-=1
x2-=1
x3-=1
if var == 6:
y1 -= 1
y2 -= 1
y3 -= 1
canvas.coords(circle_item, x1, y1, x2, y2, x2, y3)
#canvas.itemconfig(circle_item, x1, y1, x2, y2, x3, y3, fill="blue")
canvas.update()
#
#while
# Get the slider data and create x- and y-components of velocity
#velocity_x = speed_intvar.get() * math.cos(direction) # adj = hyp*cos()
#velocity_y = speed_intvar.get() * math.sin(direction) # opp = hyp*sin()
## Change the canvas item's coordinates
#canvas.move(circle_item, velocity_x, velocity_y)
#canvas.move(circle_item2, velocity_x, velocity_y)
# Get the new coordinates and act accordingly if ball is at an edge
#global location
## If crossing left or right of canvas
#if x2>canvas.winfo_width():
#
#if x1<0:
# canvas.move(circle_item, x+400, y)
#global direction
canvas.after(1, animate)
# Call function directly to start the recursion
animate()
#
def move(event):
global direction, var, x1, x2, x3, y1, y2, y3, new_dir
direction = 3
if event.char=='s' and direction == 3:
if direction == 1:
return
else:
new_dir = 1
var = 3
for i in range(10):
x1, y1, x2, y2, x3, y3 = canvas.coords(circle_item)
#for i in range(food_present):
if var == 3:
x1 += 1
x2 += 0
x3 +=0
y1 +=0
y2= y1
#x2 += 1
#x3 += 1
y3 += 1
canvas.coords(circle_item, x1, y1, x2, y2, x3, y3)
canvas.update()
if x1 == x3:
var = 4
direction = 1
canvas.update()
# if event.char=='s' and direction == 2:
# if direction == 1:
# return
# else:
# new_dir = 1
# var = 3
# for i in range(10):
# x1, y1, x2, y2, x3, y3 = canvas.coords(circle_item)
##for i in range(food_present):
# if var == 3:
# x1 -= 0
# x2 -= 1
# x3 -=1
# y1 -=0
# y2 -=1
# #x2 += 1
# #x3 += 1
#
# y3 -= 1
# canvas.coords(circle_item, x1, y1, x2, y2, x3, y3)
# canvas.update()
# if x1 == x3:
# var = 4
# direction = 1
# canvas.update()
root.bind("<Key>", move)
def x():
i = Tkinter.Canvas(root, width=900, height=900, background='#FFFFFF')
x = Tkinter.Button(root, text='Use S to move down. Try and eat the food.', command = new_game)
x.grid(row=0, column =1)
def grow():
global x1, y1, x2, y2, x3, y3, var, food_present, foodx1, foody1, foodx2, foody2
x1, y1, x2, y2, x3, y3 = canvas.coords(circle_item)
if x2>canvas.winfo_width() or x1<0:
x()
if y2>canvas.winfo_height() or y3>canvas.winfo_height():
food_present = 0
x()
canvas.update()
grow()
c = Tkinter.Canvas(root, width=900, height=900, background='#FFFFFF')
c.grid(row=0, rowspan=2, column=1)
text = Tkinter.Button(root, text='Use S to move down. Try and eat the food.', command = new_game)
text.grid(row=0, column =1)
root.mainloop()
If you want to check if two objects are overlapping on a Tkinter canvas, you may get their positions by the method of object Canvas .coords(objectname). You use the .coords method to check on a drawn object.
Example:
circle = canvas.create_oval(0, 0, 50, 50)
canvas.move(circle, 5, 0) # moves the circle by 5 pixels to the right
coords = canvas.coords(circle) # Returns an array (5, 0, 55, 0).
What you don't do is specify a coordinate to return what object is on it(what I think you are doing).