This is the game that I have made I want the circle that has been generated to move across the line without actually touching it, the circle moves to the right and downwards when no keypresses are made and is supposed to jump when the spacebar is pressed, when it collides with the line (logic I made for hitreg) it will restart with a brand new line and the circle at the starting position. When it goes from one end to the other it'll restart with a new line that has more divisions so that the game is difficult as you progress through the levels. The only issue I am having right now is that I am not able to start up the game correctly without it exiting the code immediately and I think the reason might be that the coordinates I have calculated are not accurate enough and sometimes the circle crosses the line and still doesn't register a collision.
Code for the game
from tkinter import *
from random import randint as rand
import keyboard, math
window = Tk()
window.geometry("1440x720")
canvas = Canvas(window, width=1440, height=720,
bg="white")
l1, l2, l3 = [], [], []
x = 1
b = canvas.create_oval(0, 300, 10, 300 60)
c = b
def genLine():
global x
f = 0
z = 360
y = 1440 / x
while x != 0:
ran = rand(300, 420)
u = canvas.create_line(f, z, y, ran)
r = canvas.coords(u)
l1.append(r)
f = y
x -= 1
y = y
z = ran
def hitReg():
global l2, l3
for i in l1:
grad = ((i[3] - i[1]) / (i[2] - i[0]))
l2.append(grad)
for i in l2:
for f in l1:
x = 0
length = f[2] - f[0]
while x != length:
point = x * i f[1]
l3.append(math.ceil( point))
x = 0.25
def move_b():
global l3, x
canvas.move(c, 1, 1)
y = canvas.coords(c)
if keyboard.is_pressed('space'):
canvas.move(b, 1, -5)
elif y[1] in l3 or y[3] in l3:
exit()
elif y[2] >= 1440:
x = 1
genLine()
hitReg()
move_b()
else:
pass
window.after(10, move_b)
genLine()
hitReg()
move_b()
canvas.pack()
window.mainloop()
CodePudding user response:
Main problem is
elif y[1] in l3 or y[3] in l3:
because l3
has all y
values which are on line and it compares y[1]
and y[3]
with all y
values but it should compare only with the nearest - with point which has the same x
as center of oval
.
I was thinking to use canvas.find_overlapping()
or canvas.find_closest()
but it would need to create small rectange
or small oval
for every hit point on line.
Finally I created hit points every 1px
instead of 0.25px
so I could put them on list and easily get
point_y = points[ int(oval_center_x) ]
and later check oval_y1 < point_y < oval_y2
.
I also used window.bind("<space>", function)
to move oval
so I don't need extra module keyboard
. Besides, I use Linux and keyboard
on Linux needs admin
privilege.
I also changed names of variables and functions to make them more readable.
As for exit()
I created function reset()
which sets level
, removes all lines canvas.delete(*lines)
, moves oval
to beginning canvas.moveto(oval, 0, 330)
, and generates new line (using value level
) and generates hit points
. Using reset(level 1)
I can generate next level, and using reset(1)
I can go back to first level.
import tkinter as tk # PEP8: `import *` is not preferred
from random import randint # PEP8: every module in separted line
# --- functions --- # PEP8: all functions directly after imports
# PEP8: `lower_case_names` for functions and variables
def generate_lines(level):
regions = []
lines = []
x1 = 0
y1 = 360
step = 1440 / level
x2 = step
for i in range(level):
y2 = randint(300, 420) # 360-60, 360 60
region = (x1, y1, x2, y2)
regions.append(region)
line = canvas.create_line(x1, y1, x2, y2)
lines.append(line)
x1 = x2
y1 = y2
x2 = step
return lines, regions
def generate_hit_points(regions):
points = []
for x1,y1,x2,y2 in regions:
steps = int(x2 - x1)
dy = (y2 - y1) / steps
y = y1
for _ in range(steps): # cant use `range(y1, y2, dy)` because `dy` is `float`, not `int`
points.append(y) # I don't need `x`
y = dy
return points
def move_oval():
canvas.move(oval, 1, 1)
x1, y1, x2, y2 = canvas.coords(oval)
oval_center_x = int(x1 10)
point_y = points[oval_center_x]
#print(y1, '<', int(point_y), '<', y2, '?')
#if y1 >= point_y or point_y >= y2: # less readable
if not (y1 < point_y < y2): # more readable
print('end')
reset(1)
elif x2 >= 1440:
print('next level')
reset(level 1)
window.after(25, move_oval)
def on_press_space(event):
canvas.move(oval, 0, -15)
def reset(new_level):
# all globals moved to one function
global lines
global points
global level
level = new_level
canvas.delete(*lines) # remove all lines
canvas.moveto(oval, 0, 330) # move oval to the beginning
lines, regions = generate_lines(level)
points = generate_hit_points(regions)
# --- main ---
lines = [] # PEP8: every variable in separted line
points = [] # PEP8: every variable in separted line
level = 1
window = tk.Tk()
window.geometry("1440x720")
canvas = tk.Canvas(window, width=1440, height=720, bg="white")
canvas.pack()
oval = canvas.create_oval(0, 360-30, 10, 360 30)
window.bind('<space>', on_press_space)
window.bind('q', lambda event:window.destroy())
reset(1)
# start moving
move_oval()
window.mainloop()