I have a game and I have the world moving towards you, and you can jump at the same time. both functions work separately, but they don't work when used together. sometimes it gives me an error(ValueError: could not convert string to float: expected) couldnt convert, sometimes the character does a weird jittery motion, sometimes it does both, and sometimes it just crashes. this is my code
import Tkinter as tkinter
import thread
from time import sleep
def is_even(n):
if n % 2 == 0:
retVal = True
else:
retVal = False
return retVal
class gameScreen:
def move(self, event):
d = self.getdir(event.keysym)
self.canvas.move(self.char, d[0], d[1])
self.canvas.update()
if self.collision():
self.canvas.move(self.char, -d[0], -d[1])
def fall(self):
speed = .01
x = 0
while not self.collision():
self.canvas.move(self.char, 0, 1)
self.canvas.update()
sleep(speed)
if x == 2:
speed -= speed *.04
x = 0
else:
x += 1
self.canvas.move(self.char, 0, -1)
def jump(self):
j = 0
from time import sleep
jump = [6, 6, 6, 6, 4, 4, 2, 1, 0]
for i in range(0, (len(jump)*2)):
x = jump[j]
self.canvas.move(self.char, 0, -x)
if not is_even(i):
j +=1
self.canvas.update()
sleep(.05)
self.fall()
def getLast(self, lvl):
x = 0
for i in lvl:
coords = self.canvas.coords(i)
if x < coords[3]:
x = coords[3]
last = i
return last
def gameThread(self, event):
thread.start_new_thread(self.gameStart, ())
def gameStart(self, event=None):
from time import sleep
last = self.getLast(self.lvl1)
coords = self.canvas.coords(last)
while coords[2] > 0:
print coords[2]
for i in self.lvl1:
self.canvas.move(i, -1, 0)
coords = self.canvas.coords(last)
self.canvas.update()
sleep(.002)
if not self.touchingGround(event):
self.fall()
def touchingGround(self, event = None):
self.canvas.move(self.char, 0, 5)
if self.collision():
retVal = True
else:
retVal = False
self.canvas.move(self.char, 0, -5)
return retVal
def onKey(self, event):
if self.touchingGround(event):
thread.start_new_thread(self.jump, ())
def collision(self):
coords = self.canvas.coords(self.char)
collision = len(self.canvas.find_overlapping(coords[0]-1,
coords[1]-1,
coords[2]-1,
coords[3]-1)) >=2
return collision
def getdir(self, s):
direction = {"Left":[-5, 0], "Right":[5, 0]}
try:
retVal = direction[s]
except KeyError:
retVal =False
return retVal
def __init__(self, master):
self.lvl1 = []
self.objects = []
self.master = master
master.attributes("-fullscreen", True)
master.title("Game")
self.width=master.winfo_screenwidth()
self.height=master.winfo_screenheight()
self.canvas = tkinter.Canvas(master, width=self.width,
height=self.height)
self.canvas.pack(expand="YES",fill="both")
self.objects.append(self.canvas.create_rectangle(0, self.height,
self.width,
self.height-100,
fill="grey"))
self.lvl1.append(self.canvas.create_rectangle(self.width,
self.height-100,
self.width + 100,
self.height-150,
fill="grey"))
self.objects.extend(self.lvl1)
self.char = self.canvas.create_rectangle((self.width/2)-25,
self.height- 100,
(self.width/2)+25,
self.height-150,
fill="red")
self.x = 0
self.y = 0
master.bind("<Key-Up>", self.onKey)
master.bind("<Return>", self.onKey)
def destroy(event):
root.destroy()
root = tkinter.Tk()
my_gui = gameScreen(root)
root.bind("<Escape>", destroy)
root.mainloop()
i tried adding the afters like tadhg said, but it still only jumps sometimes, and most of the time it dosent do a full jump, or just goes up one or two pixels, then goes back down, then repeats for the amount of time it should be jumping.
Related
I have built an A* Pathfinding algorithm that finds the best route from Point A to Point B, there is a timer that starts and ends post execute of the algorithm and the path is draw, this is parsed to a global variable. so it is accessable when i run the alogrithm more than once (to gain an average time).
the global variable gets added to a list, except when i run the algorithm 5 times, only 4 values get added (I can see 5 times being recorded as the algorithm prints the time after completion). when displaying the list it always misses the first time, and only has times 2,3,4,5 if i run the algorithm 5 times. here is main.py
import astar
import pygame
def main():
timing_list = []
WIDTH = 800
WIN = pygame.display.set_mode((WIDTH, WIDTH))
for x in range(0, 4):
astar.main(WIN, WIDTH)
timing_list.insert(x, astar.full_time)
print(timing_list)
if __name__ == "__main__":
main()
and
astar.py
from queue import PriorityQueue
from random import randint
import pygame
from timing import Timing
WIDTH = 800
WIN = pygame.display.set_mode((WIDTH, WIDTH))
pygame.display.set_caption("A* Path Finding Algorithm")
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 255, 0)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (128, 0, 128)
ORANGE = (255, 165, 0)
GREY = (128, 128, 128)
TURQUOISE = (64, 224, 208)
global full_time
class Spot:
def __init__(self, row, col, width, total_rows):
self.row = row
self.col = col
self.x = row * width
self.y = col * width
self.color = WHITE
self.neighbors = []
self.width = width
self.total_rows = total_rows
def get_pos(self):
return self.row, self.col
def is_closed(self):
return self.color == RED
def is_open(self):
return self.color == GREEN
def is_barrier(self):
return self.color == BLACK
def is_start(self):
return self.color == ORANGE
def is_end(self):
return self.color == TURQUOISE
def reset(self):
self.color = WHITE
def make_start(self):
self.color = ORANGE
def make_closed(self):
self.color = RED
def make_open(self):
self.color = GREEN
def make_barrier(self):
self.color = BLACK
def make_end(self):
self.color = TURQUOISE
def make_path(self):
self.color = PURPLE
def draw(self, win):
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.width))
def update_neighbors(self, grid):
self.neighbors = []
if self.row < self.total_rows - 1 and not grid[self.row + 1][self.col].is_barrier(): # DOWN
self.neighbors.append(grid[self.row + 1][self.col])
if self.row > 0 and not grid[self.row - 1][self.col].is_barrier(): # UP
self.neighbors.append(grid[self.row - 1][self.col])
if self.col < self.total_rows - 1 and not grid[self.row][self.col + 1].is_barrier(): # RIGHT
self.neighbors.append(grid[self.row][self.col + 1])
if self.col > 0 and not grid[self.row][self.col - 1].is_barrier(): # LEFT
self.neighbors.append(grid[self.row][self.col - 1])
def __lt__(self, other):
return False
def generate_num(x, y):
return randint(x, y)
def h(p1, p2):
x1, y1 = p1
x2, y2 = p2
return abs(x1 - x2) + abs(y1 - y2)
def reconstruct_path(came_from, current, draw):
while current in came_from:
current = came_from[current]
current.make_path()
draw()
def algorithm(draw, grid, start, end):
count = 0
open_set = PriorityQueue()
open_set.put((0, count, start))
came_from = {}
g_score = {spot: float("inf") for row in grid for spot in row}
g_score[start] = 0
f_score = {spot: float("inf") for row in grid for spot in row}
f_score[start] = h(start.get_pos(), end.get_pos())
open_set_hash = {start}
while not open_set.empty():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
current = open_set.get()[2]
open_set_hash.remove(current)
if current == end:
reconstruct_path(came_from, end, draw)
end.make_end()
return True
for neighbor in current.neighbors:
temp_g_score = g_score[current] + 1
if temp_g_score < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = temp_g_score
f_score[neighbor] = temp_g_score + h(neighbor.get_pos(), end.get_pos())
if neighbor not in open_set_hash:
count += 1
open_set.put((f_score[neighbor], count, neighbor))
open_set_hash.add(neighbor)
neighbor.make_open()
draw()
if current != start:
current.make_closed()
return False
def make_grid(rows, width):
grid = []
gap = width // rows
for i in range(rows):
grid.append([])
for j in range(rows):
spot = Spot(i, j, gap, rows)
grid[i].append(spot)
return grid
def draw_grid(win, rows, width):
gap = width // rows
for i in range(rows):
pygame.draw.line(win, GREY, (0, i * gap), (width, i * gap))
for j in range(rows):
pygame.draw.line(win, GREY, (j * gap, 0), (j * gap, width))
def draw(win, grid, rows, width):
win.fill(WHITE)
for row in grid:
for spot in row:
spot.draw(win)
draw_grid(win, rows, width)
pygame.display.update()
def get_clicked_pos(pos, rows, width):
gap = width // rows
y, x = pos
row = y // gap
col = x // gap
return row, col
def main(win, width):
rows = 50
grid = make_grid(rows, width)
start = None
end = None
t = Timing()
run = True
setup_config = True
while run:
draw(win, grid, rows, width)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
setup_config = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
setup_config = False
while setup_config:
for x in range(0, 800):
row_pos = generate_num(0, rows - 1)
col_pos = generate_num(0, rows - 1)
spot = grid[row_pos][col_pos]
if not start and spot != end:
start = spot
start.make_start()
elif not end and spot != start:
end = spot
end.make_end()
elif spot != end and spot != start:
spot.make_barrier()
t.start()
for row in grid:
for spot in row:
spot.update_neighbors(grid)
algorithm(lambda: draw(win, grid, rows, width), grid, start, end)
global full_time
full_time = t.stop()
setup_config = False
run = False
pygame.QUIT
main(WIN, WIDTH)
and timing.py my timer class
import time
class TimerError(Exception):
"""A custom exception used to report errors in use of Timer class"""
class Timing:
def __init__(self):
self._start_time = None
def start(self):
"""Start a new timer"""
if self._start_time is not None:
raise TimerError(f"Timer is running. Use .stop() to stop it")
self._start_time = time.perf_counter()
def stop(self):
"""Stop the timer, and report the elapsed time"""
if self._start_time is None:
raise TimerError(f"Timer is not running. Use .start() to start it")
elapsed_time = time.perf_counter() - self._start_time
self._start_time = None
print(f"Elapsed time: {elapsed_time:0.4f} seconds")
return elapsed_time
EDIT:
the misterious 5th print is coming from this line, of course
full_time = t.stop()
With 4 iteractions of your main loop, you reach at this line 5 times. That's the reason you have 5 prints.
You are very right on your comment, when you import astar with the command
import astar
on your main.py file, you are executing the main(WIN, WIDTH) that you have at the end of astar.py. Thus resulting in 5 prints.
>>> for i in range(0, 4):
... print(i)
...
0
1
2
3
>>>
Your main for loop iterates 4 times, each time inserting one element to your empty list. By the way, I would to it like this rather than the cumbersome insert.
list.append(astar.full_time) # append inserts at the end of the list
I am surprised that you have 5 of the output of the format
print(f"Elapsed time: {elapsed_time:0.4f} seconds")
By the way, I am also wondering why do you have these statements:
run = True
while run:
run = False
and a similar statement with a boolean named setup_config and a while. You could get yourself a cleaner code by suppressing this while, since it only executes once. Please, correct me if I am wrong or I am missing something from your context.
Notice that you can also remove the ifs related to the pass commands.
def main(win, width):
rows = 50
grid = make_grid(rows, width)
start = None
end = None
t = Timing()
draw(win, grid, rows, width)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pass
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pass
for x in range(0, 800):
row_pos = generate_num(0, rows - 1)
col_pos = generate_num(0, rows - 1)
spot = grid[row_pos][col_pos]
if not start and spot != end:
start = spot
start.make_start()
elif not end and spot != start:
end = spot
end.make_end()
elif spot != end and spot != start:
spot.make_barrier()
t.start()
for row in grid:
for spot in row:
spot.update_neighbors(grid)
algorithm(lambda: draw(win, grid, rows, width), grid, start, end)
global full_time
full_time = t.stop()
pygame.QUIT
The extra print is mostly like coming from a surprising extra iteraction of this for loop
for event in pygame.event.get():
Check the value of event, by printing it and check if there's something unusual there.
I'm working on creating an AI to play the classic game Snake, and have implemented it somewhat successfully, however every so often the program crashes upon reaching its target. I'm using a modified version of A* for pathfinding and pygame for the graphics.
import numpy as np
import pygame
import random
# Set constants for program
GRID_WIDTH = 25
GRID_HEIGHT = 25
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
displayX = 500
displayY = 500
SCALEX = displayX/GRID_WIDTH
SCALEY = displayY/GRID_HEIGHT
# Setyp pygame
gameDisplay = pygame.display.set_mode((displayX,displayY))
pygame.display.set_caption('Snake')
clock = pygame.time.Clock()
# Mainly stores values at each point in the game area
class Block():
def __init__(self, x, y, val):
self.x = x
self.y = y
self.val = val # Stores what type this block is e.g. snake or fruit
self.f = 0 # Values for A-Star Algorithm
self.g = 0
self.h = 0
def get_value(self):
return self.val
def set_value(self, new_val):
self.val = new_val
def set_cost(self, g, h):
self.g = g
self.h = h
self.f = self.h - self.g
def set_parent(self, parent):
self.parent = parent # Used for retracing the path
class Snake():
def __init__(self, x, y):
self.x = x
self.y = y
self.L = 4 # Length of snake
self.body = [] # List of all parts in snake
self.body.append([x, y])
self.path = None
def move(self, board):
print(self.path)
if self.path == None or self.path == []: # Check if a path exists
self.A_Star(board) # If not calculate path
else:
self.x = self.path[0][0] # Move to next point in path then remove it from list
self.y = self.path[0][1]
self.path.pop(0)
self.body.append([self.x, self.y]) # Add body part
self.body = self.body[-self.L:]
def get_snake(self):
return self.body[-self.L:]
def get_head(self):
return [self.x, self.y]
def eat(self): # Increase length and reset path
self.L += 1
self.path = None
def A_Star(self, board): # Modified version of A Star to prioritise moving away from target
start = board.get_grid()[self.x, self.y]
end = board.get_fruit()
if start != None and end != None:
open_list = []
closed_list = []
current = start
open_list.append(current)
while open_list != []:
current = open_list[0]
for block in open_list:
if block.f > current.f:
current = block
open_list.remove(current)
closed_list.append(current)
if current == end:
path = self.retrace_path(start, end)
return True
neighbours = board.get_node_neighbours(current)
for neighbour in neighbours:
if neighbour.get_value() != "body" and not neighbour in closed_list:
if not neighbour in open_list:
neighbour.set_cost(neighbour.g, board.get_distance(current, end))
neighbour.set_parent(current)
if not neighbour in open_list:
open_list.append(neighbour)
return False
def retrace_path(self, start, end):
current = end
path = []
while current != start:
path.append([current.x, current.y])
current = current.parent
self.path = path
self.path.reverse()
'''def survive(self, board):
neighbours = board.get_node_neighbours(board.get_grid()[self.x, self.y])
for neighbour in neighbours:
if neighbour.val == "empty":
self.path = [neighbour.x, neighbour.y]
break'''
class Board():
def __init__(self, snake):
self.grid = np.empty((GRID_WIDTH, GRID_HEIGHT), dtype=object)
for x in range(0, GRID_WIDTH):
for y in range(0, GRID_HEIGHT):
self.grid[x,y] = Block(x, y, "empty") # 2D Array containing all blocks
self.fruit = self.new_fruit(snake) # Generate new fruit
self.score = 0
def check_fruit(self, snake): # Check collision between snake and fruit
snake_head = snake.get_head()
if snake_head[0] == self.fruit[0] and snake_head[1] == self.fruit[1]:
snake.eat()
self.score += 1
self.fruit = self.new_fruit(snake)
def check_death(self, snake): # Check to see if snake is dead
snake_head = snake.get_head()
snake_body = snake.get_snake()
if snake_head[0] >= GRID_WIDTH or snake_head[0] < 0 or snake_head[1] >= GRID_HEIGHT or snake_head[1] < 0:
return True
collisions = 0
for part in snake_body:
if snake_head == part:
collisions += 1
if collisions == 2:
return True
def draw(self, snake): # Draw everything to screen
self.grid = np.empty((GRID_WIDTH, GRID_HEIGHT), dtype=object)
for x in range(0, GRID_WIDTH):
for y in range(0, GRID_HEIGHT):
self.grid[x,y] = Block(x, y, "empty")
for part in snake.get_snake():
self.grid[part[0], part[1]].set_value("body")
self.grid[snake.get_head()[0], snake.get_head()[1]].set_value("head")
self.grid[self.fruit[0], self.fruit[1]].set_value("fruit")
for x in range(0, GRID_WIDTH):
for y in range(0, GRID_HEIGHT):
if self.grid[x, y].get_value() == "fruit":
pygame.draw.rect(gameDisplay,RED,(x*SCALEX,y*SCALEY,SCALEX,SCALEY))
elif self.grid[x, y].get_value() == "body" or self.grid[x, y].get_value() == "head":
pygame.draw.rect(gameDisplay,WHITE,(x*SCALEX,y*SCALEY,SCALEX,SCALEY))
def new_fruit(self, snake): # Generate a new fruit location
complete = False
fail = False
while not complete:
fruit_loc = [random.randint(0, GRID_WIDTH-1), random.randint(0, GRID_HEIGHT-1)]
for part in snake.get_snake(): # Check that fruit is not in snake
if part == fruit_loc:
fail = True
if not fail:
complete = True
return fruit_loc
def get_node_neighbours(self, block): # Get surrounding blocks from block
neighbours = []
options = [[-1, 0], [1, 0], [0, -1], [0, 1]]
for option in options:
checkX = block.x + option[0]
checkY = block.y + option[1]
if checkX >= 0 and checkX < GRID_WIDTH and checkY >= 0 and checkY < GRID_HEIGHT:
neighbours.append(self.grid[checkX,checkY])
return neighbours
def get_distance(self, start, end): # Get distance between two points
dx = abs(start.x - end.x)
dy = abs(start.y - end.y)
return dx + dy - 1
def get_grid(self):
return self.grid
def get_fruit(self):
return self.grid[self.fruit[0], self.fruit[1]]
def main():
snake = Snake(0, 0)
board = Board(snake)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
break
snake.move(board) # Move snake
if board.check_death(snake): # End program if snake is dead
running = False
break
board.check_fruit(snake) # Call fruit check
gameDisplay.fill(BLACK) # Draw to screen
board.draw(snake)
pygame.display.update()
clock.tick(100)
main()
pygame.quit()
No error messages appear and the only way to tell that the program has stopped is that pygame no longer allows you to quit out and crashes instead. As far as I can tell the point at which it happens if completely random but occurs on most rounds. The snake will reach the fruit but will stop right before eating it.
The issue is the method new_fruit in the class Board. You've generated and endless loop. Actually if the random position fails at the first try, fail will stay True forever.
fail = False has to be set in the outer loop, rather than before the loop:
class Board():
# [...]
def new_fruit(self, snake): # Generate a new fruit location
complete = False
# fail = False
while not complete:
fail = False # <----
fruit_loc = [random.randint(0, GRID_WIDTH-1), random.randint(0, GRID_HEIGHT-1)]
for part in snake.get_snake(): # Check that fruit is not in snake
if part == fruit_loc:
fail = True
if not fail:
complete = True
return fruit_loc
Replace all your (x*SCALEX,y*SCALEY,SCALEX,SCALEY) to (int(x*SCALEX),int(y*SCALEY),int(SCALEX),int(SCALEY))
I am trying to create a "Basketball game" using python and tkinter and I'm having trouble making my animation work. When I press the spacebar the basketball appears but it doesnt go upwards. I tried placing self.status.shoot == Status.shoot in multiple areas but the animation doesn't start once I hit the spacebar, instead the image of the basketball simply appears without movement. Any thoughts?
Here is my code:
model.py
import enum,math,random,time
# sizes of each images (pixels)
from Projects.MVC import controller
backboard1_height = 150
backboard1_width = 150
backboard2_height = 150
backboard2_width = 150
backboard3_height = 150
backboard3_width = 150
bg_height = 750
bg_width = 1000
floor_height = 180
floor_width = 1000
player_height = 250
player_width = 250
ball_height = 50
ball_width = 50
class Status(enum.Enum):
run = 1
pause = 2
game_over = 3
terminate = 4
shoot = 5
class HorizontalDirection(enum.IntEnum):
left = -1
none = 0
right = 1
class VerticalDirection(enum.IntEnum):
up = -1
none = 0
down = 1
class ImgDirection(enum.Enum):
left = -1
right = 1
class GameModel:
def __init__(self):
self.status = Status.pause
self.elements = []
self.next_time = time.time() # when we try drop a ball
# create elements
self.bg = Background(bg_width / 2, bg_height / 2)
self.floor = Floor(bg_width / 2, bg_height - (floor_height / 2))
self.backboard1 = Backboard1(bg_width / 2, player_height / 2)
self.backboard2 = Backboard2(bg_width / 10, player_height / 2)
self.backboard3 = Backboard3((bg_width / 2) + 400, player_height / 2)
self.player = Player(bg_width / 2, bg_height - floor_height )
self.text = TextInfo(80, 30)
self.init_elements()
def init_elements(self): # layer images on top of each other in order for every thing to be seen
self.elements = []
self.elements.append(self.bg)
self.elements.append(self.floor)
self.elements.append(self.backboard1)
self.elements.append(self.backboard2)
self.elements.append(self.backboard3)
self.elements.append(self.player)
self.elements.append(self.text)
def add_ball(self):
ball = Ball(self.player.x, self.player.y-125)
print("first self.player.y: {}".format(self.player.y))
#if self.status == Status.shoot:
self.elements.append(ball)
def random_ball_drop(self):
# if self.status == Status.shoot:
if self.next_time - time.time() < -2:
# if self.status == Status.shoot:
self.next_time = time.time() + 2
self.add_ball()
print("First time: {}".format(time.time()))
print("first Add.ball: {}".format(self.add_ball()))
#if self.status == Status.shoot:
elif self.next_time - time.time() < 0:
if random.uniform(0, 1) < 0.01:
self.next_time = time.time() + 2
self.add_ball()
print("Second time: {}".format(time.time()))
def check_status(self, element):
if type(element) is Ball:
dist = math.sqrt((element.x - self.backboard1.x) ** 2 + (element.y - self.backboard1.y) ** 2)
if dist < self.backboard1.catch_radius:
self.text.score += 1
return False
elif element.y >= bg_height:
self.text.lives -= 1
print("Text.lives: {}".format(self.text.lives))
return False
return True
def remove_ball(self):
self.elements = [e for e in self.elements if self.check_status(e)]
#print("self.element: {}".format(self.elements))
def update(self):
# if self.status == Status.shoot:
for element in self.elements:
element.update()
#print("first element.update: {}".format(element.update()))
# if self.status == Status.shoot:
self.random_ball_drop()
self.remove_ball()
print("Random_ball_drop from update block: {}".format(self.random_ball_drop()))
print("remove_ball: {}".format(self.remove_ball()))
def change_to_initial_state(self):
self.init_elements()
for e in self.elements:
e.change_to_initial_position()
class GameElement:
def __init__(self, x, y, direction_x, direction_y, speed):
self.initial_x = x
self.initial_y = y
self.x = x
self.y = y
self.direction_x = direction_x
self.direction_y = direction_y
self.speed = speed
def change_to_initial_position(self):
self.x = self.initial_x
self.y = self.initial_y
def update(self):
pass
class Background(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Floor(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Backboard1(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard1_height / 2) + (ball_height / 2) + 10
class Backboard2(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard2_height / 2) + (ball_height / 2) + 10
class Backboard3(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard3_height / 2) + (ball_height / 2) + 10
class Player(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=6)
self.img_direction = ImgDirection.left
def update(self):
if self.direction_x == HorizontalDirection.left:
if self.x > 0:
self.move()
elif self.direction_x == HorizontalDirection.right:
if self.x < bg_width:
self.move()
def move(self):
self.x += self.direction_x * self.speed
self.direction_x = HorizontalDirection.none
class Ball(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.up, speed=10)
def update(self):
self.y += self.direction_y*self.speed
print("This is self.y: {}".format(self.y))
class TextInfo(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=0)
self.score = 0
self.lives = 3
def change_to_initial_position(self):
self.score = 0
self.lives = 3
super().change_to_initial_position()
controller.py
from model import *
class GameController:
def __init__(self, model):
self.model = model
pass
def start_new_game(self):
self.model.change_to_initial_state()
self.model.status = Status.run
def continue_game(self):
self.model.status = Status.run
print(Status.run)
def exit_game(self):
self.model.status = Status.terminate
def press_p(self, event):
if self.model.status == Status.run:
self.model.status = Status.pause
print(Status.pause)
def press_left(self, event):
self.model.player.direction_x = HorizontalDirection.left
self.model.player.img_direction = ImgDirection.left
print(HorizontalDirection.left)
def press_right(self, event):
self.model.player.direction_x = HorizontalDirection.right
self.model.player.img_direction = ImgDirection.right
print(HorizontalDirection.right)
def press_space(self, event):
if self.model.status == Status.run:
self.model.status = Status.shoot
self.model.update()
print(Status.shoot)
def update_model(self):
if self.model.status == Status.run:
self.model.update()
view.py
import tkinter as tk
from PIL import ImageTk, Image
from model import *
class GameImages:
def __init__(self):
# background
self.bg_pil_img = Image.open('./resources/bg.png')
self.bg_img = ImageTk.PhotoImage(self.bg_pil_img)
# floor
self.floor_pil_img = Image.open('./resources/floor.png')
self.floor_img = ImageTk.PhotoImage(self.floor_pil_img)
# backboard1
self.backboard1_pil_img = Image.open('./resources/backboard1.png')
self.backboard1_pil_img = self.backboard1_pil_img.resize((backboard1_height, backboard1_width))
self.backboard1_img = ImageTk.PhotoImage(self.backboard1_pil_img)
# backboard2
self.backboard2_pil_img = Image.open('./resources/backboard2.png')
self.backboard2_pil_img = self.backboard2_pil_img.resize((backboard2_height, backboard2_width))
self.backboard2_img = ImageTk.PhotoImage(self.backboard2_pil_img)
# backboard3
self.backboard3_pil_img = Image.open('./resources/backboard3.png')
self.backboard3_pil_img = self.backboard1_pil_img.resize((backboard3_height, backboard3_width))
self.backboard3_img = ImageTk.PhotoImage(self.backboard3_pil_img)
# player
self.player_pil_img = Image.open('./resources/player.png')
self.player_pil_img_right = self.player_pil_img.resize((player_height, player_width))
self.player_pil_img_left = self.player_pil_img_right.transpose(Image.FLIP_LEFT_RIGHT)
self.player_img_right = ImageTk.PhotoImage(self.player_pil_img_right)
self.player_img_left = ImageTk.PhotoImage(self.player_pil_img_left)
# ball
self.ball_pil_img = Image.open('./resources/ball.png')
self.ball_pil_img = self.ball_pil_img.resize((ball_height, ball_width))
self.ball_img = ImageTk.PhotoImage(self.ball_pil_img)
def get_image(self, element):
if type(element) is Background:
return self.bg_img
if type(element) is Floor:
return self.floor_img
if type(element) is Backboard1:
return self.backboard1_img
if type(element) is Backboard2:
return self.backboard2_img
if type(element) is Backboard3:
return self.backboard3_img
if type(element) is Player:
if element.img_direction == ImgDirection.left:
return self.player_img_left
else:
return self.player_img_right
if type(element) is Ball:
return self.ball_img
return None
class DisplayGame:
def __init__(self, canvas, _id):
self.canvas = canvas
self.id = _id
def delete_from_screen(self):
self.canvas.delete(self.id)
class DisplayGameImage(DisplayGame):
def __init__(self, canvas, element, img):
super().__init__(canvas, canvas.create_image(element.x, element.y, image=img))
class DisplayGameText(DisplayGame):
def __init__(self, canvas, element):
text = "Score: %d\nLives: %d" % (element.score, element.lives)
super().__init__(canvas, canvas.create_text(element.x, element.y, font='12', text=text))
class DisplayMenu(DisplayGame):
def __init__(self, root, canvas, controller):
menu = tk.Frame(root, bg='grey', width=400, height=40)
menu.pack(fill='x')
new_game = tk.Button(menu, text="New Game", width=15, height=2, font='12', command=controller.start_new_game)
new_game.pack(side="top")
continue_game = tk.Button(menu, text="Continue", width=15, height=2, font='12', command=controller.continue_game)
continue_game.pack(side="top")
exit_game = tk.Button(menu, text="Exit Game", width=15, height=2, font='12', command=controller.exit_game)
exit_game.pack(side="top")
_id = canvas.create_window(bg_width / 2, bg_height / 2, window=menu)
super().__init__(canvas, _id)
class GameView:
def __init__(self, model, controller):
self.model = model
self.controller = controller
# root
self.root = tk.Tk()
self.root.title('Basketball Game')
# load images files
self.images = GameImages()
# canvas
self.canvas = tk.Canvas(self.root, width= bg_width, height= bg_height)
self.canvas.pack()
self.root.update()
# canvas elements id
self.elements_id = []
self.add_elements_to_canvas()
self.add_event_handlers()
self.is_menu_open = False
self.draw()
self.root.mainloop()
def add_elements_to_canvas(self):
for e in self.model.elements:
if type(e) is TextInfo:
self.elements_id.append(DisplayGameText(self.canvas, e))
else:
self.elements_id.append(DisplayGameImage(self.canvas, e, self.images.get_image(e)))
if self.model.status == Status.pause or self.model.status == Status.game_over:
self.elements_id.append(DisplayMenu(self.root, self.canvas, self.controller))
self.is_menu_open = True
def add_event_handlers(self):
self.root.bind("<Left>", self.controller.press_left)
self.root.bind("<Right>", self.controller.press_right)
self.root.bind("p", self.controller.press_p)
self.root.bind("<space>",self.controller.press_space)
def draw(self):
self.controller.update_model()
if self.model.status == Status.run or not self.is_menu_open:
self.is_menu_open = False
self.canvas.delete("all")
self.add_elements_to_canvas()
if self.model.status == Status.terminate:
self.root.destroy()
else:
self.canvas.after(5, self.draw)
Why is my program slow while rendering 128 particles? I think that's not enough to get less than 30 fps.
All I do is rendering 128 particles and giving them some basic gravitation
on_draw function
def on_draw(self, time=None):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
self.particles.append(Particle())
for particle in self.particles:
particle.draw()
if particle.is_dead:
self.particles.remove(particle)
Particle class
class Particle:
def __init__(self, **kwargs):
self.acceleration = Vector2(0, 0.05)
self.velocity = Vector2(random.uniform(-1, 1), random.uniform(-1, 0))
self.position = Vector2()
self.time_to_live = 255
self.numpoints = 50
self._vertices = []
for i in range(self.numpoints):
angle = math.radians(float(i) / self.numpoints * 360.0)
x = 10 * math.cos(angle) + self.velocity[0] + 300
y = 10 * math.sin(angle) + self.velocity[1] + 400
self._vertices += [x, y]
def update(self, time=None):
self.velocity += self.acceleration
self.position -= self.velocity
self.time_to_live -= 2
def draw(self):
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glPushMatrix()
glTranslatef(self.position[0], self.position[1], 0)
pyglet.graphics.draw(self.numpoints, GL_TRIANGLE_FAN, ('v2f', self._vertices), ('c4B', self.color))
glPopMatrix()
self.update()
#property
def is_dead(self):
if self.time_to_live <= 0:
return True
return False
#property
def color(self):
return tuple(color for i in range(self.numpoints) for color in (255, 255, 255, self.time_to_live))
I'm not overly happy about using GL_TRIANGLE_FAN because it's caused a lot of odd shapes when using batched rendering. So consider moving over to GL_TRIANGLES instead and simply add all the points to the object rather than leaning on GL to close the shape for you.
That way, you can easily move over to doing batched rendering:
import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time, sleep
from math import *
from random import randint
key = pyglet.window.key
class CustomGroup(pyglet.graphics.Group):
def set_state(self):
#pyglet.gl.glLineWidth(5)
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
#glColor4f(1, 0, 0, 1) #FFFFFF
#glLineWidth(1)
#glEnable(texture.target)
#glBindTexture(texture.target, texture.id)
pass
def unset_state(self):
glLineWidth(1)
#glDisable(texture.target)
class Particle():
def __init__(self, x, y, batch, particles):
self.batch = batch
self.particles = particles
self.group = CustomGroup()
self.add_point(x, y)
def add_point(self, x, y):
colors = ()#255,0,0
sides = 50
radius = 25
deg = 360/sides
points = ()#x, y # Starting point is x, y?
prev = None
for i in range(sides):
n = ((deg*i)/180)*pi # Convert degrees to radians
point = int(radius * cos(n)) + x, int(radius * sin(n)) + y
if prev:
points += x, y
points += prev
points += point
colors += (255, i*int(255/sides), 0)*3 # Add a color pair for each point (r,g,b) * points[3 points added]
prev = point
points += x, y
points += prev
points += points[2:4]
colors += (255, 0, 255)*3
self.particles[len(self.particles)] = self.batch.add(int(len(points)/2), pyglet.gl.GL_TRIANGLES, self.group, ('v2i/stream', points), ('c3B', colors))
class main(pyglet.window.Window):
def __init__ (self, demo=False):
super(main, self).__init__(800, 600, fullscreen = False, vsync = True)
#print(self.context.config.sample_buffers)
self.x, self.y = 0, 0
self.sprites = OrderedDict()
self.batches = OrderedDict()
self.batches['default'] = pyglet.graphics.Batch()
self.active_batch = 'default'
for i in range(1000):
self.sprites[len(self.sprites)] = Particle(randint(0, 800), randint(0, 600), self.batches[self.active_batch], self.sprites)
self.alive = True
self.fps = 0
self.last_fps = time()
self.fps_label = pyglet.text.Label(str(self.fps) + ' fps', font_size=12, x=3, y=self.height-15)
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
#self.bg.draw()
self.batches[self.active_batch].draw()
self.fps += 1
if time()-self.last_fps > 1:
self.fps_label.text = str(self.fps) + ' fps'
self.fps = 0
self.last_fps = time()
self.fps_label.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main(demo=True)
x.run()
Bare in mind, on my nVidia 1070 I managed to get roughly 35fps out of this code, which isn't mind blowing. But it is 1000 objects * sides, give or take.
What I've changed is essentially this:
self.batch.add(int(len(points)/2), pyglet.gl.GL_TRIANGLES, self.group, ('v2i/stream', points), ('c3B', colors))
and in your draw loop, you'll do:
self.batch.draw()
Instead of calling Particle.draw() for each particle object.
What this does is that it'll send all the objects to the graphics card in one gigantic batch rather than having to tell the graphics card what to render object by object.
As #thokra pointed out, your code is more CPU intensive than GPU intensive.
Hopefully this fixes it or gives you a few pointers.
Most of this code is taking from a LAN project I did with a good friend of mine a while back:
https://github.com/Torxed/pyslither/blob/master/main.py
Because I didn't have all your code, mainly the main loop. I applied your problem to my own code and "solved " it by tweaking it a bit. Again, hope it helps and steal ideas from that github project if you need to. Happy new year!
I am running a creature simulator in python 2.7 using tkinter as my visualizer. The map is made up of squares, where colors represent land types, and a red square represents the creature. I use canvas.move, to move that red square around the board. It has to move quite a lot. But I know exactly where it should start and where it should end. I have run the simulation, bit by bit, and when it is regulated to maybe two moves ie. the sim isn't really running I'm just testing it. I can see the movements. But when I really run the sim, everything buzzes by and all I can see of the canvas is the map, but no creature and certainly no creature movement. So my question is this. Firstly, how can I possibly slow down the process so that I can see the movements? Or why would the simulation run and now show any of the tkinter?
The simulation is quite large and it would be hard to pick out just the important bits, so the code below is more of a simplification. But it matches how I did the tkinter stuff. My sim just added more calculations and loops. It's worth noting that this example works perfectly.
Driver.py:
from Tkinter import *
import animation
class Alien(object):
def __init__(self):
#Set up canvas
self.root = Tk()
self.canvas = Canvas(self.root, width=400, height=400)
self.canvas.pack()
#Vars
self.map = [[1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 1, 0, 0], [1, 0, 0, 1, 0]]
self.x = 0
self.y = 0
r = 50
self.land = {}
#Draw Init
for i, row in enumerate(self.map):
for j, cell in enumerate(row):
color = "black" if cell else "green"
self.canvas.create_rectangle(r * i, r * j, r * (i + 1), r * (j + 1),
outline=color, fill=color)
self.land[(i, j)] = self.canvas.create_text(r * i, r * j, anchor=NE, fill="white", text="1", tag=str((i, j)))
self.creature = self.canvas.create_rectangle(r * self.x, r * self.y, r * (self.x + 1), r * (self.y + 1),
outline="red", fill="red")
self.canvas.pack(fill=BOTH, expand=1)
#Action
movement = animation.Animation(self.root, self.canvas, self.creature, self.land)
self.root.after(0, movement.animate)
#Clost TK
self.root.mainloop()
a = Alien()
animation.py:
from random import randrange
import sys
class Animation():
def __init__(self, root, canvas, creature, land):
self.x = self.y = 0
self.ctr = 10
self.canvas = canvas
self.creature = creature
self.root = root
self.land = land
#self.root.after(250, self.animate)
self.canvas.move(self.creature, 2 * 50, 2 * 50)
def animate(self):
self.ctr -= 1
if self.ctr > 0:
for i in range(2):
i = randrange(1, 5)
if i == 1:
self.y = -1
elif i == 2:
self.y = 1
elif i == 3:
self.x = -1
elif i == 4:
self.x = 1
#root.after(250, self.animate(canvas, creature))
"""Moves creature around canvas"""
self.movement()
self.root.after(250, self.animate)
def movement(self):
self.canvas.move(self.creature, self.x * 50, self.y * 50)
you can slow down the process using tkinters after method which can be used by any difrent widget.
canvas.after(time, dosomething) #the first parameter is how many milliseconds it will wait before it will call the second parameter.
where your dosomething function should be your way of updating the your cnavas.
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html