Related
I'm currently taking a python course where we had to built a snake game. I did not follow the tutors instuctions and I tried to do it on my own. I could make it work for the most part, but I can't figure out why the purple food elements sometimes are not eaten by the snake, even though the snake directly strikes over the food. I would appreciate it, if someone with more experience than me could look over my code to find the bug, I certainly don't know where the mistake is. Here is the code:
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1.00)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10.00)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10.00 != 0.00:
x = float(random.randint(x_min, x_max))
while y % 10.00 != 0.00:
y = float(random.randint(y_min, y_max))
self.food.setposition((x, y))
x += 10.00
y += 10.00
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10.00))
x -= 10.00
for hits in range(3):
all_possible_hits.append((x + 10.00, y))
y -= 10.00
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.03)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
if snake_current_position in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
I don't really know why that happens, especially because it only happens sometimes and at different score amounts.
A few things here:
As the first comment pointed out, generally, you would not come to stackoverflow for debugging advice like this, at least without trying to debug on your own and posting the results with your question
Something you will need to keep in mind always is that directly comparing floating point values for equality is a big no-no in any programming (that is only one of the hundreds of articles explaining why, for a much more in depth and possibly over your head look, check out this). I believe that is directly where your problem lies in this case as well. By using snake_current_position in current_food_positions, you are likely asking for that comparison to be made between a bunch of floats as your have it written, and therefor failing the equality check when it looks like it should be true.
To find this out and debug your next program on your own, think about how debugging could be done. There are many tools but the simplest is sometimes just to print things. When taking a look myself, the first thing I did was add
print("snake:")
print(snake_pos_int)
print("food")
print(current_food_positions)
as well as turned the game speed down (sleep time up) so I could watch the output. This allowed me to get an idea if the values were sane, and also notice that the values were printing as floating point numbers with different amounts of rounding (10.0 vs 10.00) which is another indicator you definitely should not be directly comparing them.
This brings me to the "solution"
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10 != 0:
x = random.randint(x_min, x_max)
while y % 10 != 0:
y = random.randint(y_min, y_max)
self.food.setposition((x, y))
x += 10
y += 10
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10))
x -= 10
for hits in range(3):
all_possible_hits.append((x+10, y))
y -= 10
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.1)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
snake_pos_int = (round(int(snake_current_position[0])/10)*10, round(int(snake_current_position[1])/10)*10)
if snake_pos_int in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
You can note the main change: The previously floating point values are integers (I notice that you even cast the random ints for food to floats, do not do this if you do not need to, in your case this only hurt) and the snake head values are somewhat hacked to be integers (simply casting them to integers was not enough, as in another demonstration of why not to use floats here, at one point one got rounded down to 9, breaking the game, so I apply a rounding to the nearest 10 as well). THIS IS NOT AN IDEAL SOLUTION. It is meant to demonstrate that the problem is the use of floats and that you should be using integers for this all the way through.
With this hacky solution, I was able to run multiple games out to and past 25 score, so I believe that mostly fixes your main problem. I will leave it to you to fix the secondary but related issue that if your snake grows long enough to run into itself, the collision detection may not work consistently because of the same issue with comparing floats for equality as you do with if head_position in tail_positions:.
I m stuck with this DFS search for a Robo vac cleaner to clean the given grid. The Robo can move up, down, right, or left. The goal of the program is to find the next move.
Here is my python code, I m not sure what exactly I m doing wrong.
Based on the move returned the Robo will clean the room and calls this class with current position
'''
class RoboVac:
def __init__(self, config_list):
self.room_width, self.room_height = config_list[0]
self.pos = config_list[1]
self.block_list = config_list[2]
self.angle = 90
self.visited = []
self.block_size = 30
self.move = 0
self.max_x = self.room_width - 1
self.max_y = self.room_height - 1
self.is_move_back = False
# To store the new position
self.x = self.pos[0]
self.y = self.pos[1]
self.block_tiles_set = set()
for b in self.block_list:
for x in range(b[0], b[0] + b[2]):
for y in range(b[1], b[1] + b[3]):
self.block_tiles_set.add((x, y))
def visited_cell(self):
if (self.x, self.y) in self.visited:
return True
return False
def evaluate_new_position(self):
x = self.x
y = self.y
dir = self.move
# adjust based on direction only if new pos inside room
if dir == 0:
if y > 0:
y = y - 1
elif dir == 1:
if x < self.max_x:
x = x + 1
elif dir == 2:
if y < self.max_y:
y = y + 1
elif dir == 3:
if x > 0:
x = x - 1
if (x, y) == self.pos: # tried to go beyond room
return False
elif (x, y) in self.block_tiles_set:
return False
else:
return True
def find_next_move(self):
match self.angle:
case 0:
return 1
case 90:
return 0
case 180:
return 3
case 270:
return 2
return -1 #none
def move_left(self):
# to move left
self.angle = (self.angle + 90) % 360
def move_right(self):
if self.angle == 0:
self.angle = 270
self.angle = (self.angle - 90) % 360
def move_back(self):
self.move_right()
self.move_right()
def move_cell(self):
# find next move
self.move = self.find_next_move()
# check if it is safe to move
safe_move = self.evaluate_new_position()
if self.is_move_back:
self.move_back()
if safe_move:
return True
return False
def dfs(self):
if self.visited_cell():
self.move_back()
self.is_move_back = True
if self.move_cell():
self.visited.append(self.pos)
return self.move
self.move_left()
if self.move_cell():
self.visited.append(self.pos)
return self.move
self.move_left()
if self.move_cell():
self.visited.append(self.pos)
return self.move
self.move_left()
if self.move_cell():
self.visited.append(self.pos)
return self.move
self.dfs()
def get_next_move(self, current_pos): # called by PyGame code
# Return a direction for the vacuum to move
#mark current position visited
self.pos = current_pos
self.x = self.pos[0]
self.y = self.pos[1]
return self.dfs()
'''
I'm trying to code a little Jump´n´Run game in tkinter with canvas and so far its working pretty well, but I have a problem that I cant´t wrap my head around.
Look at these three pictures: on the first the collision works fine - I can jump from one paddle to the other.
On the second picture, you can see that whenever I get under the paddle it doesn't fall down and jumping up is also not possible, probably because i have self.y = 0 in the self.brick collision detection. How could I get this working such that even when its under the paddle it bounces off, because that's important, for example when I start to add the second line of paddles.
My Collision code:
def brick_hit(self, pos):
for brick_line in self.bricks:
for brick in brick_line:
brick_pos = self.gamerootCanvas.coords(brick.id)
try:
if pos[3] > brick_pos[1]:
if pos[2] > brick_pos[0] and pos[0] < brick_pos[2]:
return True
except:
continue
return False
My Full code:
def jump_and_run():
gameroot = Toplevel()
gameroot.title("Game Root")
gameroot.resizable(0, 0)
gameroot.wm_attributes("-topmost", 1)
gamerootCanvas = Canvas(gameroot, width=1800, height=800, bd=0, highlightthickness=0)
gameroot_Background = PhotoImage(file="jumpnrunbackground.png")
gamerootCanvas.create_image(500, 250, image=gameroot_Background)
gamerootCanvas.pack()
gamerootCanvas.update()
class Player:
def __init__(self, gamerootCanvas, bricks, color):
self.gamerootCanvas = gamerootCanvas
self.id = gamerootCanvas.create_rectangle(25,25,0,0, fill=color)
self.gamerootCanvas.move(self.id, 5, 650)
self.bricks = bricks
self.x = 0
self.y = 0
self.gravity = 0.1
self.gamerootCanvas_height = gamerootCanvas.winfo_height()
self.gamerootCanvas_width = gamerootCanvas.winfo_width()
self.gamerootCanvas.bind_all("<KeyPress-Right>", self.move_right)
self.gamerootCanvas.bind_all("<KeyRelease-Right>", self.move_right_stop)
self.gamerootCanvas.bind_all("<KeyPress-Left>", self.move_left)
self.gamerootCanvas.bind_all("<KeyRelease-Left>", self.move_left_stop)
self.gamerootCanvas.bind_all("<KeyPress-Up>", self.jump_)
self.gamerootCanvas.bind_all("<KeyRelease-Up>", self.jump_stop)
self.jump_counter = 0
self.move_counter = 0
def move_player(self):
self.gamerootCanvas.move(self.id, self.x, self.y)
pos = self.gamerootCanvas.coords(self.id)
self.y += self.gravity
if pos[0] <= 0:
self.x = 1
elif pos[2] >= self.gamerootCanvas_width:
self.x = -1
elif pos[1] <= 0:
self.y = 1
elif pos[3] >= self.gamerootCanvas_height:
self.y = 0
elif self.brick_hit(pos) == True:
self.y = 0
def move_right(self, evt):
self.x = 2
def move_right_stop(self, evt):
self.x = 0
def move_left(self, evt):
self.x = -2
def move_left_stop(self, evt):
self.x = 0
def jump_(self, evt):
if self.jump_counter < 2:
self.y = -6
self.jump_counter += 2
def jump_stop(self, evt):
self.y = 0
self.jump_counter = 0
def brick_hit(self, pos):
for brick_line in self.bricks:
for brick in brick_line:
brick_pos = self.gamerootCanvas.coords(brick.id)
try:
if pos[3] > brick_pos[1]:
if pos[2] > brick_pos[0] and pos[0] < brick_pos[2]:
return True
except:
continue
return False
class Bricks1:
def __init__(self, gamerootCanvas, color):
self.gamerootCanvas = gamerootCanvas
self.id = gamerootCanvas.create_rectangle(50, 15, 0, 0, fill=color, width=2)
self.gamerootCanvas.move(self.id, 5, 700)
def generate_bricks():
global bricks
bricks = []
for i in range(0, 1):
b = []
for j in range(0, 14):
Bricks_1 = Bricks1(gamerootCanvas, "Blue")
b.append(Bricks_1)
bricks.append(b)
for i in range(0, 1):
for j in range(0, 14):
gamerootCanvas.move(bricks[i][j].id, 158.2 * j, 40 * i)
generate_bricks()
player1 = Player(gamerootCanvas, bricks, "Red")
while True:
gameroot.update_idletasks()
player1.move_player()
gameroot.update()
gameroot.after(5)
play_gameloop_sound()
gameUI.mainloop()
You should offset yourself from the brick to prevent becoming stuck. When resting on a brick, you should also have an altered state to prevent jittering.
I'm trying to make a game like snake, where when you catch/eat the apple and the apple moves to a different spot in tkinter.
But I want to place the "apple", or in this case, a blue triangle, to a random place on the canvas,but the only command I can find is canvas.move(), which moves the widget a certain number of pixels in any direction. This can't fulfill what I need(I think, maybe there is a way around it?). Is there a way to place the blue triangle randomly on the canvas?
from Tkinter import *
import random
import time
class Ball:
def __init__(self, canvas,square):
self.square = square
self.canvas = canvas
self.id = canvas.create_oval(10,10,25,25,fill='red')
self.canvas.move(self.id,245,100)
self.text = self.canvas.create_text(10, 10, text='GAME OVER', font=('Courier', 80))
self.canvas.move(self.text, -7000, -7000)
starts = [-3,-2,-1,1,2,3]
self.x = random.choice(starts)
self.y = -30
def draw(self):
self.canvas.move(self.id,self.x,self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 4
if pos[3] >= self.canvas.winfo_height():
self.y = -4
if self.hit_square(pos) == True:
self.canvas.move(self.text,245,100)
time.sleep(2)
tk.destroy()
if pos[0] <= 0:
self.x = 4
if pos[2] >= self.canvas.winfo_width():
self.x = -4
def hit_square(self, pos):
square_pos = self.canvas.coords(self.square.id)
if pos[2] >= square_pos[0] and pos[0] <= square_pos[2]:
if pos[3] >= square_pos[1] and pos[3] <= square_pos[3]:
return True
return False
def stay(self):
self.x = 0
self.y = 0
class Square:
def __init__(self,canvas):
self.canvas = canvas
self.id = canvas.create_rectangle(15, 15, 30, 30,fill='green')
self.x = 0
self.y = 0
self.canvas.move(self.id, 200, 250)
self.canvas.bind_all('<KeyPress-Left>',self.left)
self.canvas.bind_all('<KeyPress-Right>', self.right)
self.canvas.bind_all('<KeyPress-Up>', self.up)
self.canvas.bind_all('<KeyPress-Down>', self.down)
def draw(self):
self.canvas.move(self.id,self.x,self.y)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas.winfo_width():
self.x = 0
if pos[1] <= 0:
self.y = 0
elif pos[3] >= self.canvas.winfo_height():
self.y = 0
def left(self, evt):
self.x = -2
self.y = 0
def right(self, evt):
self.x = 2
self.y = 0
def up(self, evt):
self.y = -2
self.x = 0
def down(self, evt):
self.y = 2
self.x = 0
class Triangle:
def __init__(self,canvas,square):
self.canvas = canvas
self.square = square
self.id = self.canvas.create_polygon(26.5,10,20,25,35,25,fill='blue')
self.canvas.move(self.id,random.randint(10,450),random.randint(10,380))
self.score = 0
def draw_score(self):
self.score_show = self.canvas.create_text(450, 20, text='score:' + str(self.score), font=('Arial', 20))
def hit_square(self):
pos = self.canvas.coords(self.id)
square_pos = self.canvas.coords(self.square.id)
if pos[2] >= square_pos[0] and pos[0] <= square_pos[2]:
if pos[3] >= square_pos[1] and pos[3] <= square_pos[3]:
self.teleport(pos)
def teleport(self, pos):
x = self.canvas.winfo_width()-pos[0]-10
y = self.canvas.winfo_height() - pos[1]-10
self.score += 1
self.canvas.move(self.id,)
tk = Tk()
tk.title("Run from the ball!")
tk.resizable(0,0)
tk.wm_attributes('-topmost',1)
canvas = Canvas(tk, width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
tk.update()
square = Square(canvas)
ball = Ball(canvas, square)
ball1 = Ball(canvas, square)
ball2 = Ball(canvas, square)
ball3 = Ball(canvas, square)
ball4 = Ball(canvas, square)
triangle = Triangle(canvas, square)
x = 0
while x < float('inf'):
ball.draw()
triangle.draw_score()
triangle.hit_square()
if x >= 10:
ball1.draw()
if x >= 20:
ball2.draw()
if x >= 30:
ball3.draw()
if x >= 40:
ball4.draw()
square.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
x += 0.01
Yes, I know that the score function is messed up, and that the one overlaps the zero. Could you guys help me with that too?
Is there a way to place the blue triangle randomly on the canvas?
Yes. the coords method can get you the current coordinates, but it also lets you change the coordinates to whatever you want.
self.canvas.coords(self.id, 36.5, 20, 30, 35, 45, 35)
I'm working on a 2D python game project for my CS class, and I've hit a bump, I don't know what the problem is:
The project is a large part of my grade, and up until now I've had an A+
This project is incredibly frustrating
NEW
ok so i've got everything working so far, except for some reason My protaganist() is stuck at the top left corner of the game screen !
Also, i need ideas on how to create a jump action
If anyone could help I would be incredibly grateful!
I am importing a game engine my teacher made available from his book website, but i it is too long for me to add but i will try to add some of it at the bottom
Here is all my code:
import gameEngine
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
sndAtk = pygame.mixer.Sound("OOT_AdultLink_Attack1.wav")
#goal is to create a game
#must have menu to start game
#menu should have a start and quit button.. start runs gaming operations and quit exits program
#sprites for character and enemies and bullets maybe, use one large image and simply move visibiliy
#this saves memory as 1 image is loaded instead of many
"""
class game(gameEngine.scene):
def __init__(self, scene):
self.background()
self.sprites["spawn.gif", "badguys.gif"]
"""
"""
protaganist is our hero sprite
should run left and right, jump left and right
and attack left and right...
I might add in the bow and jump attack
"""
class scrollinggrass(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("gamebackground.jpg")
self.rect.centerx = 20
self.rect.centery = 500
self.rect = self.image.get_rect()
self.dx = 10
self.dy = 0
self.checkKeys()
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
print("working")
self.forward(3)
run.play()
if keys[pygame.K_LEFT]:
self.forward(-3)
class hearts(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("heart.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.setPosition((550 , 30))
class badguy(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("badguy1.png")
self.rect = self.imageMaster.get_rect()
self.health = 2
self.DEAD = 1
self.state = 0
class protaganist(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.imageList = []
self.rect = self.imageMaster.get_rect()
self.STANDING = 0
self.RUNNING = 1
self.ATTACKING = 2
self.JUMPING = 3
self.DEAD = 10
self.imageFrame = 0
self.state = self.STANDING
self.hearts = 1
self.heartPts = self.hearts * 3
self.stats()
self.loadImages()
# self.image = self.imageList[0]
self.checkKeys()
def stats(self):
#sets it up so each heart is essentially 3 hit points
if self.heartPts >= 3:
self.hearts = 1
elif self.heartPts >= 6:
self.hearts = 2
elif self.heartPts == 9:
self.hearts = 3
elif self.heartPts > 9:
self.heartPts = 9
# changes state to dead if hp == 0
if self.heartPts == 0:
self.state = DEAD
def loadImages(self):
self.setPosition((320 , 380))
self.setImage("heroSTANDING.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.state = runRight
self.frame += 1
if self.frame >= len(self.imageList):
self.frame = 1
self.image = self.imageList[self.frame]
# self.image = self.image.get_rect()
# self.rect.center = (320, 240)
if keys[pygame.K_LEFT]:
self.state = 1
while keys[pygame.K_g]:
self.state = Attacking
sndAtk.play()
if self.state == self.DEAD:
self.image = self.deadImgList[0]
self.frame += 1
self.image = self.deadImgList[self.frame]
#self.image = self.image.get_rect()
#self.rect.center = (320, 240)
class game(gameEngine.Scene):
def __init__ (self):
gameEngine.Scene.__init__(self)
pygame.display.set_caption("Link's Mediocre Adventure")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pro = protaganist(self)
baddy = badguy(self)
baddy1 = badguy(self)
heart = hearts(self)
grass = scrollinggrass(self)
goodlySprites = self.makeSpriteGroup((grass, pro, heart))
baddySprites = self.makeSpriteGroup((baddy, baddy1))
# self.addSpriteGroup(goodlySprites)
self.addGroup((baddySprites))
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
baddy1.health -= 1
if baddy.health == 0:
baddy.reset()
elif baddy1.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collideGroup(baddySprites):
pro.heartPts -= 1
goodlySprites.update()
baddySprites.update()
goodlySprites.draw(screen)
baddySprites.draw(screen)
pygame.display.flip()
def main():
game.start()
if __name__ == "__main__":
game()
game engine
class SuperSprite(pygame.sprite.Sprite):
""" An enhanced Sprite class
expects a gameEngine.Scene class as its one parameter
Use methods to change image, direction, speed
Will automatically travel in direction and speed indicated
Automatically rotates to point in indicated direction
Five kinds of boundary collision
"""
def __init__(self, scene):
pygame.sprite.Sprite.__init__(self)
self.scene = scene
self.screen = scene.screen
#create constants
self.WRAP = 0
self.BOUNCE = 1
self.STOP = 2
self.HIDE = 3
self.CONTINUE = 4
#create a default text image as a placeholder
#This will usually be changed by a setImage call
self.font = pygame.font.Font("freesansbold.ttf", 30)
self.imageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF))
self.image = self.imageMaster
self.rect = self.image.get_rect()
#create properties
#most will be changed through method calls
self.x = 200
self.y = 200
self.dx = 0
self.dy = 0
self.dir = 0
self.rotation = 0
self.speed = 0
self.maxSpeed = 10
self.minSpeed = -3
self.boundAction = self.WRAP
self.pressed = False
self.oldCenter = (100, 100)
self.states = {}
self.currentState = "default"
def update(self):
self.oldCenter = self.rect.center
self.checkEvents()
self.__rotate()
self.__calcVector()
self.__calcPosition()
self.checkBounds()
self.rect.center = (self.x, self.y)
def checkEvents(self):
""" overwrite this method to add your own event code """
pass
def __rotate(self):
""" PRIVATE METHOD
change visual orientation based on
rotation property.
automatically called in update.
change rotation property directly or with
rotateBy(), setAngle() methods
"""
oldCenter = self.rect.center
self.oldCenter = oldCenter
self.image = pygame.transform.rotate(self.imageMaster, self.rotation)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def __calcVector(self):
""" calculates dx and dy based on speed, dir
automatically called in update
"""
theta = self.dir / 180.0 * math.pi
self.dx = math.cos(theta) * self.speed
self.dy = math.sin(theta) * self.speed
self.dy *= -1
def __calcPosition(self):
""" calculates the sprites position adding
dx and dy to x and y.
automatically called in update
"""
self.x += self.dx
self.y += self.dy
def checkBounds(self):
""" checks boundary and acts based on
self.BoundAction.
WRAP: wrap around screen (default)
BOUNCE: bounce off screen
STOP: stop at edge of screen
HIDE: move off stage and wait
CONTINUE: keep going at present course and speed
automatically called by update
"""
scrWidth = self.screen.get_width()
scrHeight = self.screen.get_height()
#create variables to simplify checking
offRight = offLeft = offTop = offBottom = offScreen = False
if self.x > scrWidth:
offRight = True
if self.x < 0:
offLeft = True
if self.y > scrHeight:
offBottom = True
if self.y < 0:
offTop = True
if offRight or offLeft or offTop or offBottom:
offScreen = True
if self.boundAction == self.WRAP:
if offRight:
self.x = 0
if offLeft:
self.x = scrWidth
if offBottom:
self.y = 0
if offTop:
self.y = scrHeight
elif self.boundAction == self.BOUNCE:
if offLeft or offRight:
self.dx *= -1
if offTop or offBottom:
self.dy *= -1
self.updateVector()
self.rotation = self.dir
elif self.boundAction == self.STOP:
if offScreen:
self.speed = 0
elif self.boundAction == self.HIDE:
if offScreen:
self.speed = 0
self.setPosition((-1000, -1000))
elif self.boundAction == self.CONTINUE:
pass
else:
# assume it's continue - keep going forever
pass
def setSpeed(self, speed):
""" immediately sets the objects speed to the
given value.
"""
self.speed = speed
def speedUp(self, amount):
""" changes speed by the given amount
Use a negative value to slow down
"""
self.speed += amount
if self.speed < self.minSpeed:
self.speed = self.minSpeed
if self.speed > self.maxSpeed:
self.speed = self.maxSpeed
def setAngle(self, dir):
""" sets both the direction of motion
and visual rotation to the given angle
If you want to set one or the other,
set them directly. Angle measured in degrees
"""
self.dir = dir
self.rotation = dir
def turnBy (self, amt):
""" turn by given number of degrees. Changes
both motion and visual rotation. Positive is
counter-clockwise, negative is clockwise
"""
self.dir += amt
if self.dir > 360:
self.dir = amt
if self.dir < 0:
self.dir = 360 - amt
self.rotation = self.dir
def rotateBy(self, amt):
""" change visual orientation by given
number of degrees. Does not change direction
of travel.
"""
self.rotation += amt
if self.rotation > 360:
self.rotation = amt
if self.rotation < 0:
self.rotation = 360 - amt
def setImage (self, image):
""" loads the given file name as the master image
default setting should be facing east. Image
will be rotated automatically """
self.imageMaster = pygame.image.load(image)
self.imageMaster = self.imageMaster.convert()
def setDX(self, dx):
""" changes dx value and updates vector """
self.dx = dx
self.updateVector()
def addDX(self, amt):
""" adds amt to dx, updates vector """
self.dx += amt
self.updateVector()
def setDY(self, dy):
""" changes dy value and updates vector """
self.dy = dy
self.updateVector()
def addDY(self, amt):
""" adds amt to dy and updates vector """
self.dy += amt
self.updateVector()
def setComponents(self, components):
""" expects (dx, dy) for components
change speed and angle according to dx, dy values """
(self.dx, self.dy) = components
self.updateVector()
def setBoundAction (self, action):
""" sets action for boundary. Values are
self.WRAP (wrap around edge - default)
self.BOUNCE (bounce off screen changing direction)
self.STOP (stop at edge of screen)
self.HIDE (move off-stage and stop)
self.CONTINUE (move on forever)
Any other value allows the sprite to move on forever
"""
self.boundAction = action
def setPosition (self, position):
""" place the sprite directly at the given position
expects an (x, y) tuple
"""
(self.x, self.y) = position
def moveBy (self, vector):
""" move the sprite by the (dx, dy) values in vector
automatically calls checkBounds. Doesn't change
speed or angle settings.
"""
(dx, dy) = vector
self.x += dx
self.y += dy
self.checkBounds()
def forward(self, amt):
""" move amt pixels in the current direction
of travel
"""
#calculate dx dy based on current direction
radians = self.dir * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.x += dx
self.y += dy
def addForce(self, amt, angle):
""" apply amt of thrust in angle.
change speed and dir accordingly
add a force straight down to simulate gravity
in rotation direction to simulate spacecraft thrust
in dir direction to accelerate forward
at an angle for retro-rockets, etc.
"""
#calculate dx dy based on angle
radians = angle * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.dx += dx
self.dy += dy
self.updateVector()
def updateVector(self):
#calculate new speed and angle based on dx, dy
#call this any time you change dx or dy
self.speed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
dy = self.dy * -1
dx = self.dx
radians = math.atan2(dy, dx)
self.dir = radians / math.pi * 180
def setSpeedLimits(self, max, min):
""" determines maximum and minimum
speeds you will allow through
speedUp() method. You can still
directly set any speed you want
with setSpeed() Default values:
max: 10
min: -3
"""
self.maxSpeed = max
self.minSpeed = min
def dataTrace(self):
""" utility method for debugging
print major properties
extend to add your own properties
"""
print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \
(self.x, self.y, self.speed, self.dir, self.dx, self.dy)
def mouseDown(self):
""" boolean function. Returns True if the mouse is
clicked over the sprite, False otherwise
"""
self.pressed = False
if pygame.mouse.get_pressed() == (1, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.pressed = True
return self.pressed
def clicked(self):
""" Boolean function. Returns True only if mouse
is pressed and released over sprite
"""
released = False
if self.pressed:
if pygame.mouse.get_pressed() == (0, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
released = True
return released
def collidesWith(self, target):
""" boolean function. Returns True if the sprite
is currently colliding with the target sprite,
False otherwise
"""
collision = False
if self.rect.colliderect(target.rect):
collision = True
return collision
def collidesGroup(self, target):
""" wrapper for pygame.sprite.collideany
simplifies checking sprite - group collisions
returns result of collision check (sprite from group
that was hit or None)
"""
collision = pygame.sprite.spritecollideany(self, target)
return collision
def distanceTo(self, point):
""" returns distance to any point in pixels
can be used in circular collision detection
"""
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dist = math.sqrt((dx * dx) + (dy * dy))
return dist
def dirTo(self, point):
""" returns direction (in degrees) to
a point """
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dy *= -1
radians = math.atan2(dy, dx)
dir = radians * 180 / math.pi
dir += 180
return dir
def drawTrace(self, color=(0x00, 0x00, 0x00)):
""" traces a line between previous position
and current position of object
"""
pygame.draw.line(self.scene.background, color, self.oldCenter,
self.rect.center, 3)
self.screen.blit(self.scene.background, (0, 0))
def addState(self, stateName, stateImageFile):
""" Creates a new sprite state with the associated name
and image. Useful to build multi-state sprites.
"""
#load the image
tempImage = pygame.image.load(stateImageFile)
tempImage.convert()
self.states[stateName] = tempImage
def setState(self, stateName):
""" attempts to set the sprite to the indicated state
(image)
"""
self.imageMaster = self.states[stateName]
self.rect = self.imageMaster.get_rect()
self.currentState = stateName
def getState(self):
""" returns the current state name
(default if no states have been explicitly set)
"""
return self.currentState
if pro.state == pro.ATTACKING:
if pro.collidesWith(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(baddySprites):
pro.heartPts -= 1
baddySprites is a sprite group, so I bet you have to use collidesGroup instead of collidesWith.
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesGroup(baddySprites):
pro.heartPts -= 1
But even if you do this, it seems like you'll still have problems. Namely, this code only ever deducts health from baddy and not baddy1. I'm assuming sprite groups support iteration. If so, you should perform collision detection independently on each enemy.
for enemy in baddySprites:
if pro.state == pro.ATTACKING:
if pro.collidesWith(enemy):
enemy.health -= 1
if enemy.health == 0:
enemy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(enemy):
pro.heartPts -= 1