Increase the speed of an object as it moves (Tkinter) - python

I'm still programming my Flappy Bird game and I'm starting the "debugging" part now
For that, I repaired some problems like the text of the score which did not appear and I even customized the icon as well as the name of the window (little attention to detail).
Except that I have to correct a rather unfortunate detail, the speed of the fall of the bird is too slow.
Thus, there are displacements impossible to achieve for the player, especially when the bird goes from a pair of very high pipes to a pair of very low pipes.
However, the bird's jumps are perfect and I do not want to change them of course!
It was then logical for me to increase the speed of fall of the bird as it falls but nothing to do, I used various methods to program this increasing speed but they have not not completed. I tried, for example, to break down the movement of the bird but it did not change anything.
I am aware that I have little experience but I thought to be able and I can not find similar topics on the net
In order for you to help me, I have reduced my program to the strict minimum and there is only the movement of the objects of the game, I am aware that I ask you too much help during this week and I am sorry but promised, after this adjustment you will never see me again!
Here is the .zip file of the images of my game!
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global mouvement
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x,score,pause
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-379.8)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(272,523)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-379.8)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(272,523)
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(272,523)
H=randint(272,523)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
canvas.pack()
canvas.focus_set()
deplacement()
canvas.bind("<space>",sauter)
fenetre.mainloop()

To increase the downwards speed, you must increase DY, it is currently fixed at 5; however, you are also using DY in the sauter method, that you want to keep intact...
I suggest to add one variable DY_fall, and set it at a larger value than the current DY; this way, you do not need to modify sauter.
You will need to:
initialize DY_fall with the value you want.
change the movement of the bird in movement, to use DY_fall:
canvas.move(image_oiseau, 0, DY_fall)
[EDIT]:
I added a variable GRAVITY that increase the velocity of a downward fall, without interfering with the sauter function.
This required adding yet another variable, dy_fall that is increased by the acceleration from GRAVITY, as long as the bird falls. This is reset to the default DY_fall value each time the bird 'jumps'.
You will need to tweak the values to get a smooth game, but I think it should behave as you wished.
from tkinter import *
import random
from random import randint
def sauter(event):
global dy_fall
dy_fall = DY_fall
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global mouvement, dy_fall
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x,score,pause
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
canvas.move(image_oiseau, 0, dy_fall)
dy_fall *= GRAVITY
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-379.8)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(272,523)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-379.8)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(272,523)
canvas.after(100, deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
dy_fall = DY_fall = 5
GRAVITY = 1.5
tuyx=316
tuyx2=488
h=randint(272,523)
H=randint(272,523)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
canvas.pack()
canvas.focus_set()
deplacement()
canvas.bind("<space>",sauter)
fenetre.mainloop()

Related

Python - easily converting float to rounded number for set checking (turtle graphics)

I'm having an issue with my snakegame on the collision detection side. What I wrote originally to do so was this:
snakecollisionchecklist = len(snake.snake_coord_list)
snakecollisionchecklistset = len(set(snake.snake_coord_list))
if snakecollisionchecklist != snakecollisionchecklistset:
return
The idea being that if any segment position in the snake was equal to another segment position it would abort the program. The problem I found was that the .position() function I was using to find the position of each segment returned a 10 decimal float value, and for whatever reason this wasn't consistent even if the snake was sharing the same place. Converting it to int doesnt work either as that sometimes will make 60 59 etc. if the variance is high/low. To deal with this I put this together, but I feel like this isn't the most efficient way to handle this:
values_to_convert = segments.position()
xvaluestoconvert = round(values_to_convert[0],2)
yvaluestoconvert = round(values_to_convert[1],2)
self.snake_coord_list[segloc] = (xvaluestoconvert, yvaluestoconvert)
This just takes the individual pieces of the position() and forces it to be rounded. I also had a similar issue with trying to make food not spawn inside the snake, which ended up looking like this:
newloc= positionlist[0]
while newloc in positionlist:
randomx = round((random.randint(-7,7) * 20))
randomy = round((random.randint(-7,7) * 20))
newloc = [randomx, randomy]
self.goto(float(randomx),float(randomy))
print(f"{randomx} and {randomy}")
But then I still get overlap.
Is there a better way to do this? If you're curious about full code it's here:
gamesetup.py
import turtle as t
class FullSnake:
def __init__(self) -> None:
self.snake_part_list = []
self.snake_coord_list = [(-40,0),(-60,0), (-80,0)]
self.moment_prior_coord_list = [(-40,0),(-60,0), (-80,0)]
for nums in range(3):
new_segment = t.Turtle("circle")
new_segment.penup()
new_segment.color("white")
self.snake_part_list.append(new_segment)
new_segment.goto(self.snake_coord_list[nums])
self.snake_head = self.snake_part_list[0]
self.headingverification = 0
def add_segment(self,):
"""This adds a segment to the snake. the segment added should be initialized to be add the end of the list."""
new_segment = t.Turtle("circle")
new_segment.penup()
new_segment.color("white")
self.snake_part_list.append(new_segment)
#self.snake_coord_list.append((self.snake_part_list[0].xcor(),self.snake_part_list[0].ycor()))
#self.snake_coord_list.append(new_segment)
# current_final_seg = self.snake_part_list[-2]
current_final_seg_pos = self.moment_prior_coord_list[-1]
#self.move_snake()
new_segment.goto(current_final_seg_pos[0],current_final_seg_pos[1])
self.snake_coord_list.append(current_final_seg_pos)
def right(self):
if self.headingverification != 180:
self.snake_head.setheading(0)
# time.sleep(0.031)
# self.move_snake()
#ime.sleep(0.05)
def up(self):
if self.headingverification != 270:
self.snake_head.setheading(90)
# time.sleep(0.031)
#self.move_snake()
def left(self):
if self.headingverification != 0:
self.snake_head.setheading(180)
# time.sleep(0.031)
# self.move_snake()
def down(self):
if self.headingverification != 90:
self.snake_head.setheading(270)
#time.sleep(0.031)
# self.move_snake()
def move_snake(self):
"""moves snake. snake moves forward 20 units, and prior units get updated"""
self.moment_prior_coord_list = list(self.snake_coord_list)
for seg_num in range(len(self.snake_part_list)-1,0,-1):
new_x = round(self.snake_part_list[seg_num-1].xcor(),2)
new_y = round(self.snake_part_list[seg_num-1].ycor(),2)
self.snake_part_list[seg_num].goto(new_x, new_y)
self.snake_head.forward(20)
#print(self.snake_head.position())
for segments in self.snake_part_list:
segloc = self.snake_part_list.index(segments)
#for some reason segments.position() a varied float, so this just forces it to be samesies
values_to_convert = segments.position()
xvaluestoconvert = round(values_to_convert[0],2)
yvaluestoconvert = round(values_to_convert[1],2)
self.snake_coord_list[segloc] = (xvaluestoconvert, yvaluestoconvert)
print(self.snake_coord_list)
main.py:
import turtle as t
from gamesetup import FullSnake
import time
import food
import score_board as sb
screen = t.Screen()
screen.setup(food.screensize[0],food.screensize[1])
screen.bgcolor("Black")
screen.title("My Snake Game")
screen.tracer(0)
snakefood = food.Food()
snake = FullSnake()
scoreboard = sb.ScoreBoard()
screen.listen()
screen.onkey(snake.up,"Up")
screen.onkey(snake.down,"Down")
screen.onkey(snake.right,"Right")
screen.onkey(snake.left,"Left")
#game_is_on = True
#while game_is_on:
def snakemovefct():
snake.move_snake()
screen.update()
#what happens when you hit food, add to length of snake, increase score and move pellet to place that snake isnt
if snake.snake_head.distance(snakefood) <5:
snake.add_segment()
snakefood.refresh(snake.snake_coord_list)
scoreboard.score_event()
screen.update()
time.sleep(0.1)
#set gameover if you hit boundary
if snake.snake_head.xcor() > 150 or snake.snake_head.xcor() < -150 or snake.snake_head.ycor() > 150 or snake.snake_head.ycor() < -150:
scoreboard.game_over()
return
#check collision
snakecollisionchecklist = len(snake.snake_coord_list)
snakecollisionchecklistset = len(set(snake.snake_coord_list))
if snakecollisionchecklist != snakecollisionchecklistset:
scoreboard.game_over()
return
#this makes sure you cant press up and left when moving right to go left
snake.headingverification = snake.snake_head.heading()
#keep snake moving in loop, if no recursion it only moves once
screen.ontimer(snakemovefct,150)
screen.update()
screen.ontimer(snakemovefct,150)
screen.mainloop()
food.py
from turtle import Turtle
import random
screensize = (340, 340)
class Food(Turtle):
def __init__(self) -> None:
super().__init__()
self.shape("square")
self.penup()
#self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color("red")
self.speed("fastest")
self.refresh([(20,0),(0,0), (-20,0)])
def refresh(self, positionlist):
newloc= positionlist[0]
while newloc in positionlist:
randomx = "{:.2f}".format(random.randint(-7,7) * 20)
randomy = "{:.2f}".format(random.randint(-7,7) * 20)
newloc = [randomx, randomy]
self.goto(float(randomx),float(randomy))
print(f"{randomx} and {randomy}")
scoreboard.py
import turtle as t
class ScoreBoard(t.Turtle):
def __init__(self) -> None:
super().__init__()
self.hideturtle()
self.penup()
self.pencolor("white")
self.speed("fastest")
self.score = 0
self.goto(0,120)
self.write(f"Current score: {self.score}", False, align="center")
def score_event(self):
self.score +=1
self.clear()
self.write(f"Current score: {self.score}", False, align="center")
def game_over(self):
self.goto(0,0)
self.write("GAME OVER", False, align="center")

How to manage the conditions of the increase of a score?

I have a problem with the program of my game Flappy Bird. I managed to increase a score on the screen. However, it increases every time the pipes come out of the screen, which is not what I want ...
Indeed, I would like the score to increase each time the bird passes between the pipes and so each time that tuyx and tuyx2 are equal to 67 but when I change the conditions, the score stays at 0.
Would anyone have the solution to my problem?
Here is the program and the pictures of my game ;)
Here are the pictures
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x,score
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
if y1 < 416:
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-241)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(256,505)
score+=1
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-241)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(256,505)
score+=1
lscore.config(text=str(score))
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(256,505)
H=randint(256,505)
oisx=67
oisy=244
solx=144
sol2x=432
score=0
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
lscore=Label(fenetre,text='0')
lscore.pack()
deplacement()
canvas.pack()
canvas.focus_set()
canvas.bind("<space>",sauter)
fenetre.mainloop()

How to display and update a score on screen?

My question is about displaying and updating text, in order to display the score on screen.
I would like to create a score like the real game that would appear on the screen. But after researching Google, I have not found anyone wishing to increase a score on the screen ...
Indeed, I would like the score to increase each time the bird passes between the pipes and therefore whenever the pipes have an X of 67 pixels. So does anyone know how to do this?
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
if y1 < 416:
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-241)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(256,505)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-241)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(256,505)
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(256,505)
H=randint(256,505)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
deplacement()
canvas.pack()
canvas.focus_set()
canvas.bind("<space>",sauter)
fenetre.mainloop()
Could someone explain the problem to me because I thought it would be easy :(
Here are the pictures of the game :)
Here are the pictures of the game
Here is one approach to display the scores: It uses a tk.Label, that is updated at the same time the score increases.
The trigger that increases the score is currently a random call to on_change; you can modify this to be a test if a pipe x coordinates becomes lower than the bird x coordinates (the bird successfully crossed the obstacle)
You can, if you want relocate the score label on the canvas.
import random
import tkinter as tk
WIDTH, HEIGHT = 500, 500
def create_pipes():
pipes = []
for x in range(0, WIDTH, 40):
y1 = random.randrange(50, HEIGHT - 50)
y0 = y1 + 50
pipes.append(canvas.create_line(x, 0, x, y1))
pipes.append(canvas.create_line(x, y0, x, HEIGHT))
return pipes
def move_pipes():
for pipe in pipes:
canvas.move(pipe, -2, 0)
x, y0, _, y1 = canvas.coords(pipe)
if x < 0:
canvas.coords(pipe, WIDTH+20, y0, WIDTH+20, y1)
if random.randrange(0, 20) == 10:
on_change()
root.after(40, move_pipes)
def on_change():
global score
score += 1
score_variable.set(f'score: {score}')
root = tk.Tk()
tk.Button(root, text='start', command=move_pipes).pack()
score = 0
score_variable = tk.StringVar(root, f'score: {score}')
score_lbl = tk.Label(root, textvariable=score_variable)
score_lbl.pack()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="cyan")
canvas.pack()
pipes = create_pipes()
root.mainloop()

Tkinter Canvas game needs a slight rework

Here's the Code firstly - I'll get into the problems after. Although I'm sure you can spot plenty without needing to scroll to far down.
from tkinter import *
import random
import math
import time
test = random.randint(10,40)
print(test)
class Game():
global x0,y0,x1,y1,Game,debug, Player, mousex0, mousey0, mousex1,
mousey1, moveTowardMouse, rayCast, speed, frayCast, fx0, fy0,
fx1, fy1, Food
def move(event):
global x0,y0,x1,y1,mouseX0,mouseY0,mouseX1,mouseY1,fx0, fy0,
fx1, fy1,Food
mouseX0 = event.x - 10
mouseY0 = event.y - 10
mouseX1 = event.x + 10
mouseY1 = event.y + 10
Game.coords(rayCast, x0, y0, mouseX1, mouseY1)
if x0 != mouseX0 and x0 < mouseX0:
x0 = x0 + speed
x1 = x1 + speed
Game.coords(Player, x0, y0, x1, y1)
if x0 != mouseX0 and x0 > mouseX0:
x0 = x0 - speed
x1 = x1 - speed
Game.coords(Player, x0, y0, x1, y1)
if y0 != mouseY0 and y0 < mouseY0:
y0 = y0 + speed
y1 = y1 + speed
Game.coords(Player, x0, y0, x1, y1)
if y0 != mouseY0 and y0 > mouseY0:
y0 = y0 - speed
y1 = y1 - speed
Game.coords(Player, x0, y0, x1, y1)
Game.coords(frayCast, x0,y0, fx0,fy0)
if fx0 > x0 and (fx0 - x0) < 20:
fx0 = fx0 + 0.5
fx1 = fx1 + 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fx0 < x0 and (fx0 + x0) < 20:
fx0 = fx0 - 0.5
fx1 = fx1 - 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fy0 > y0 and (fy0 - y0) < 20:
fy0 = fy0 + 0.5
fy1 = fy1 + 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fy0 < y0 and (fy0 - y0) < 20:
fy0 = fy0 - 0.5
fy1 = fy1 - 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fx0 > x0 and (fx0 - x0) < 5:
if fy0 > y0 and (fy0 - y0) <5:
Game.delete(Food)
x0 = x0 - fx1
y0 = y0 - fy1
Game.coords(Player, x0,y0,x1,y1)
fx0 = 20
fy0 = 20
fx1 = test + 20
fy1 = test + 20
x0 = -50
y0 = -50
x1 = 50
y1 = 50
speed = 1
mouseX0 = 0
mouseY0 = 0
mouseX1 = 0
mouseY1 = 0
debug = "DEBUGGED"
module = Tk()
Game = Canvas(module, width=1000, height=1000)
Player = Game.create_oval(x0,y0,x1,y1,fill="blue")
Food = Game.create_oval(fx0, fy0, fx1, fy1, fill="red")
rayCast = Game.create_line(x0,y0,mouseX1,mouseY1)
frayCast = Game.create_line(x0,y0,mouseX1,mouseY1)
module.bind('<Motion>', move)
Game.pack()
module.mainloop()
So I'm having just a slight "oh snap" just a moment ago when I realised that my code was basically useless.
In the game I'm creating, I'm wanting the Player controlled sprite on the canvas to move at a slow speed towards the mouse. I googled how to get the mouse coordinates, it told me that I could use the event to get the coords in a function. However since getting those coords I've slowly put all the major sprite movement calculations in the same function resulting in a functional game... that only does something as long as your moving the mouse.
The idea is that the NPC-sprite is a random size and spawns in a random space on the canvas. It moves in random directions in a slow speed until it is within "20" of the player controlled sprite, in which case it moves (faster) away from the player-controlled sprite.
Aside from the fact that all this only happens when you move the mouse (and that I'm still using raycasting to get a trajectory for the sprites to follow, there are a few more issues I need help with.
Firstly, the random size of the NPC sprite works great. But it spawns exactly the same place every time.
Secondly, the NPC's "avoid the player" code seems to be... less than functional. Basically it works fine, but then it just keeps working even after the player has moved "20" away from the sprite.
Lastly, I'm having an issue with the coords of the sprites themselves. You see the raycasting reveals that the true coords for the sprites are not in the centre of their canvas representation of a circle, but instead in the top left corner of what would be an invisible square around said circle. I need this to be in the centre of the sprite rather than not otherwise the gameplay mechanics become a little bit buggy.
Back to the biggest issue (With the way updates work for sprite coords) I'm fine with setting up like Updates per Tick within the game and run all my calculations every tick, but then I wouldn't know how to get the mouse coords outside of using that event.
Long story short some help would be much appreciated.
Here is your improved code. I explained everything in the comments.
from tkinter import *
import random
import math
import time
class Game():
# This __init__ will run first when you run Game()
# Learn to always set up your programs this way.
def __init__(self):
test = random.randint(10,40)
print(test)
# Put self. before the name of every variable you might want to use outside this __init__ function.
# this way you don't need to define them separately in every function
# You defined these xs and ys by hand so of course it remains in the same position.
# define them randomly and you'll be fine.
self.fx0 = 20
self.fy0 = 20
self.fx1 = test + 20
self.fy1 = test + 20
self.x0 = -50
self.y0 = -50
self.x1 = 50
self.y1 = 50
self.speed = 1
self.mouseX0 = 0
self.mouseY0 = 0
self.mouseX1 = 0
self.mouseY1 = 0
self.debug = "DEBUGGED"
# Added these two variables
self.tick_intervals = 10
self.food_exists = True
self.module = Tk()
#Game is the name of your class don't use it here: (use game instead)
self.game = Canvas(self.module, width=1000, height=1000)
self.Player = self.game.create_oval(self.x0,self.y0,self.x1,self.y1,fill="blue")
self.Food = self.game.create_oval(self.fx0, self.fy0, self.fx1, self.fy1, fill="red")
self.rayCast = self.game.create_line(self.x0,self.y0,self.mouseX1,self.mouseY1)
self.frayCast = self.game.create_line(self.x0,self.y0,self.mouseX1,self.mouseY1)
self.game.pack()
self.move()
self.module.mainloop()
def move(self):
# To get mouse position on your whole screen: .winfo_pointerx() and .winfo_pointery()
# To get position of widget (self.game) on screen .winfo_rootx()
# x and y below are the same as mouse event.x and event.y without the need to bind anything to self.module
x = self.game.winfo_pointerx()-self.game.winfo_rootx()
y = self.game.winfo_pointery()-self.game.winfo_rooty()
# If you have a value you use more than 1 time,
# it's best to define it first then put that here
# instead of writing it out every time.
# this way you can change it very easily
# better define these in __init__ function with self.something = ...
self.mouseX0 = x - 10 # define var for 10
self.mouseY0 = y - 10
self.mouseX1 = x + 10
self.mouseY1 = y + 10
# You should also design a (visible or invisible) wall around the screen
# so your Player and Food can't run off the screen.
# Basically it's numbers and some if statements.
# If you don't put elif here x and y might get resized then resized again.
# but you only need to resize them once a tick.
# You don't need != here. < > are enough.
# Look up += -= *= /= functions.
if self.x0 < self.mouseX0:
self.x0 += self.speed
self.x1 += self.speed
elif self.x0 > self.mouseX0:
self.x0 -= self.speed
self.x1 -= self.speed
if self.y0 < self.mouseY0:
self.y0 += self.speed
self.y1 += self.speed
elif self.y0 > self.mouseY0:
self.y0 -= self.speed
self.y1 -= self.speed
# Need to call these once a tick and not every time you change x or y
self.game.coords(self.rayCast, self.x0, self.y0, self.mouseX1, self.mouseY1)
self.game.coords(self.Player,self.x0,self.y0,self.x1,self.y1)
# After you eat food this shouldn't run any more.
# This is why Player kept getting bigger and bigger
if self.food_exists:
if self.fx0 > self.x0 and (self.fx0 - self.x0) < 20: # define var for 20
self.fx0 += 0.5 # define var for 0.5
self.fx1 += 0.5
elif self.fx0 < self.x0 and (self.fx0 + self.x0) < 20:
self.fx0 -= 0.5
self.fx1 -= 0.5
if self.fy0 > self.y0 and (self.fy0 - self.y0) < 20:
self.fy0 += 0.5
self.fy1 += 0.5
elif self.fy0 < self.y0 and (self.fy0 - self.y0) < 20:
self.fy0 -= 0.5
self.fy1 -= 0.5
if self.fx0 > self.x0 and (self.fx0 - self.x0) < 5: # define var for 5
if self.fy0 > self.y0 and (self.fy0 - self.y0) <5:
self.game.delete(self.Food)
self.x0 -= self.fx1
self.y0 -= self.fy1
self.food_exists = False
self.game.coords(self.Food,self.fx0,self.fy0,self.fx1,self.fy1)
self.game.coords(self.frayCast, self.x0,self.y0, self.fx0,self.fy0)
# This automatically runs self.move after (self.tick_intevals) miliseconds
self.game.after(self.tick_intervals,self.move)
# This IF runs the Game() only if you run the script yourself.
# This way if you imported this script into another program it wouldn't run Game()
# Learn to always use this if for your programs
if __name__=='__main__':
Game()

Pygame - Gravity Methods

I'm in the process of making a simple game in pygame. Its looking to be a platformer RPG. But that is neither final or relevant per this question. So far i have very little functionality in the game. Its just a skeleton at this point if that. My question is kind of two fold:
Whats the best (in terms of performance and flexibility) way to add gravity to classes in pygame?
What are the best practices for adding gravity in general? For example, do you just simply do a "if keyPressed == k_W then subtract 2pixels per tick from player-y for 20 ticks" or something with velocity in the up or negative-y direction?
I've seen other posts on adding gravity to games after the fact, where adding it really wasn't thought about during initial development. I want to add it in as early as possible so instead of adding gravity to other things, i can add other things to gravity. I'm going to continue to read up on this, so if you prefer to point me in the direction of some online resources, I'd much appreciate that as well!
Quick dislaimer: I do not know multiple ways to incorporate gravity, so I can not say which is "best". But, if you're fighting the performance battle in Python, you've probably fighting the wrong battle.
For gravity, you can use a vector system. Say a character jumps off the ground and has initial velocity of [5, -15] (negative y because positive y is down!), you can move your character's rect by this velocity every frame to simulate movement. To throw gravity into this, you need to add 9.8 to your y velocity component value every second. So 1 second in, the velocity will be about [5, -5]. This will have your character slow to a stop, and begin moving down.
For key pressed movement, I recommend using booleans. An example, upon pressing k_U , a variable that says you are moving up becomes True. Then, if this variable is True, you move him, say, [0, -5]. Upon keyup, set variable to false. Do this for north/east/south/west, and then you have a movement system in 4 directions, that moves you while you hold the key down.
I used the 1/2 mg^2 equation in this code, which has a snow like effect:
import math, sys, random, time
import pygame, inputbox
from pygame.locals import *
class flake:
def __init__(self, xpos, ypos, mass, color, drift):
self.xpos = xpos
self.ypos = ypos
self.mass = mass
self.rect = pygame.Rect(xpos, ypos, 2, 2)
self.checked = False
self.color = color
self.drift = drift
size = width, height = 510, 700
BLACK = (0,0,0)
WHITE = (255, 255, 255)
GREY = (128,128,128)
DARKGREY = (169,169,169)
SILVER = (192,192,192)
LIGHTGREY = (211,211,211)
LIGHTESTGREY = (220,220,220)
pygame.init()
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill(BLACK)
def init():
global theSnow, snowColours, clock, startrange
theSnow = []
snowColours = []
snowColours.append(WHITE)
snowColours.append(GREY)
snowColours.append(DARKGREY)
snowColours.append(SILVER)
snowColours.append(LIGHTGREY)
snowColours.append(LIGHTESTGREY)
for c in range(2000):
mass = 0.0
mass = float(random.randint(1,8) / 100.0)
xpos = random.randint(0,width)
ypos = random.randint(0,5)
ypos = -ypos
drift = ypos/10.0
colour = snowColours[random.randint(0,5)]
f = flake(xpos, ypos, mass, colour, drift)
theSnow.append(f)
print "flake x = " + str(f.xpos) + " y = " + str(f.ypos) + " mass = " + str(f.mass)
startrange = 200
clock = pygame.time.Clock()
def run():
global theSnow, clock
global startrange
newrange = 0
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
keys=pygame.key.get_pressed()
if keys[K_q]:
return
g = 3
for count in range(startrange):
yinc = 0.0
yuncertainty = float(random.randint(1,5)/10.0)
yinc = float(0.5 * theSnow[count].mass * (g*g)) + yuncertainty
theSnow[count].ypos += yinc
xuncertainty = random.randint(1,10)
if xuncertainty > 4:
theSnow[count].xpos += theSnow[count].drift
else:
theSnow[count].xpos -= theSnow[count].drift
theSnow[count].rect = pygame.Rect(theSnow[count].xpos, theSnow[count].ypos, 2,2)
if not theSnow[count].checked:
if theSnow[count].ypos > 30:
for c in range(newrange, startrange):
print " c= " + str(c)
theSnow[c].checked = True
if startrange < 2000:
startrange += 100
newrange = startrange - 100
print " newrange = " + str(newrange)
print " startrange = " + str(startrange)
update()
pygame.time.wait(10)
#clock.tick(10)
def update():
global theSnow, startrange
background.fill(BLACK)
for count in range(startrange):
pygame.draw.rect(background, theSnow[count].color, theSnow[count].rect)
screen.blit(background, (0, 0))
pygame.display.flip()
if __name__ == "__main__":
init()
run()

Categories