How do I add a score tracker? - python

So I want it to count the score every time the snake eats a candy.
I haven't tried much, but I tried to find existing codes and adding them to mine but that just broke the game. I also tried to make my own score board by watching a tutorial, but I don't know where the code should go like at the beginning or end.
import pygame
import random
score = 0
welcome = ("Welcome to our snake game")
print(welcome)
class cube:
height = 20
w = 500
def __init__(movee,start,x=1,y=0,color=(0,0,0)):
movee.pos = start
movee.x = 1
movee.y = 0
movee.color = color
def move(movee, x, y):
movee.x = x
movee.y = y
movee.pos = (movee.pos[0] + movee.x, movee.pos[1] + movee.y)
def draw(movee, surface, eyes=False):
leng = movee.w // movee.height
i = movee.pos[0]
j = movee.pos[1]
pygame.draw.rect(surface, movee.color, (i*leng+1,j*leng+1, leng-2, leng-2))
class snake:
body = []
turns = {}
def __init__(movee, color, pos):
movee.color = color
movee.head = cube(pos)
movee.body.append(movee.head)
def move(movee):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
keys = pygame.key.get_pressed()
for key in keys:
if keys[pygame.K_LEFT]:
movee.x = -1
movee.y = 0
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_RIGHT]:
movee.x = 1
movee.y = 0
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_UP]:
movee.x = 0
movee.y = -1
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_DOWN]:
movee.x = 0
movee.y = 1
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
for i, c in enumerate(movee.body):
p = c.pos[:]
if p in movee.turns:
turn = movee.turns[p]
c.move(turn[0],turn[1])
if i == len(movee.body)-1:
movee.turns.pop(p)
else:
if c.x == -1 and c.pos[0] <= 0: c.pos = (c.height-1, c.pos[1])
elif c.x == 1 and c.pos[0] >= c.height-1: c.pos = (0,c.pos[1])
elif c.y == 1 and c.pos[1] >= c.height-1: c.pos = (c.pos[0], 0)
elif c.y == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.height-1)
else: c.move(c.x,c.y)
def add(movee):
tail = movee.body[-1]
dx, dy = tail.x, tail.y
if dx == 1 and dy == 0:
movee.body.append(cube((tail.pos[0]-1,tail.pos[1])))
elif dx == -1 and dy == 0:
movee.body.append(cube((tail.pos[0]+1,tail.pos[1])))
elif dx == 0 and dy == 1:
movee.body.append(cube((tail.pos[0],tail.pos[1]-1)))
elif dx == 0 and dy == -1:
movee.body.append(cube((tail.pos[0],tail.pos[1]+1)))
movee.body[-1].x = dx
movee.body[-1].y = dy
def draw(movee, surface):
for i, c in enumerate(movee.body):
if i ==0:
c.draw(surface, True)
else:
c.draw(surface)
def drawingAGrid(w, height, surface):
sizein = w // height
x = 0
y = 0
for l in range(height):
x = x + sizein
y = y + sizein
def redrawGrid(surface):
global height, width, s, snack
surface.fill((255,255,255))
s.draw(surface)
snack.draw(surface)
drawingAGrid(width, height, surface)
pygame.display.update()
def Candy(height, item):
positions = item.body
while True:
x = random.randrange(height)
y = random.randrange(height)
if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0:
continue
else:
break
return (x,y)
def gameloop():
global width, height, s, snack, x_pos, y_pos, reset
width = 500
height = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
snack = cube(Candy(height, s), color=(0, 0, 0))
flag = True
clock = pygame.time.Clock()
x_pos, y_pos = s.body[0].pos
while flag:
pygame.time.delay(50)
clock.tick(7)
s.move()
x, y = s.body[0].pos
if not -1 <= x - x_pos <= 1 or not -1 <= y - y_pos <= 1:
movee.reset((10,10))
x_pos, y_pos = s.body[0].pos
if s.body[0].pos == snack.pos:
s.add()
snack = cube(Candy(height, s), color=(0, 0, 0))
redrawGrid(win)
gameloop()
I just want like a scoreboard in any of the corners counting the score.

Use pygame.freetype to render text. e,g, crated a pygame.freetype.SysFont object:
import pygame.freetype
pygame.init()
font = pygame.freetype.SysFont('Times New Roman', 30)
The score is the number of body parts. Use str to convert a number to a text and .render() to render a text to a pygame.Surface object:
score = len(s.body)
text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30)
Define a margin to the border of the window, calculate the text position (e.g. bottom right) and .blit the text to the window surfrace:
margin = 10
text_pos = (width - text_rect.width - margin, width - text_rect.height - margin)
surface.blit(text_surf, text_pos)
Function redrawGrid:
def redrawGrid(surface):
global height, width, s, snack
surface.fill((255,255,255))
s.draw(surface)
snack.draw(surface)
drawingAGrid(width, height, surface)
score = len(s.body)
text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30)
margin = 10
text_pos = (width - text_rect.width - margin, width - text_rect.height - margin)
surface.blit(text_surf, text_pos)
pygame.display.update()

Related

Get isometric tile mouse selection in Pygame

I'm not managing to get this math correct, and it's a little bit difficult to explain in words. I have managed to create a isometric grid, which you can select the tiles with the mouse perfectly, and I have managed to implement a camera movement using wasd keys and still get the tiles correctly selected, but there is a slightly bug which I can not figure out where is coming from.
This is what happens, but only sometimes, depend where the camera offset is:
when this happens, it is only on the x axis, and not in every tile.
I'm almost giving up on this cause I can't find the bug, thought of posting here to see if anyone had similar problem.
import time
import pygame
import sys
import math
from os import path
from settings import *
from sprites import *
# ------------------------- SETTINGS ---------------------------- #
# COLORS (r, g, b)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (40, 40, 40)
LIGHTGREY = (100, 100, 100)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
# game settings
WIDTH = 1024
HEIGHT = 768
FPS = 60
title = "Isometric-Based game"
BGCOLOUR = DARKGREY
TILE_X = 80
TILE_Y = 40
WORLD_X, WORLD_Y = 14, 10
ORIGIN_X, ORIGIN_Y = 5, 1
# Debug
pygame.init()
font = pygame.font.Font(None, 25)
CAMERA_SPEED = 300
def get_info(info_list):
display_surface = pygame.display.get_surface()
for i, key in enumerate(info_list):
text = font.render(str(key) + " : " + str(info_list[key]), True, (255, 255, 255), (0, 0, 0))
text_rect = text.get_rect()
text_rect.y = 20 * i
display_surface.blit(text, text_rect)
# ------------------------- SPRITES ---------------------------- #
class Camera:
def __init__(self, game, x, y):
self.game = game
self.x, self.y = self.game.to_screen(x, y)
self.vx, self.vy = 0, 0
def update(self):
self.get_keys()
self.x += self.vx * self.game.dt
self.y += self.vy * self.game.dt
def get_keys(self):
self.vx, self.vy = 0, 0
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.vy = -CAMERA_SPEED
if keys[pygame.K_s]:
self.vy = CAMERA_SPEED
if keys[pygame.K_a]:
self.vx = -CAMERA_SPEED
if keys[pygame.K_d]:
self.vx = CAMERA_SPEED
if self.vx != 0 and self.vy != 0:
self.vx *= 1.0
self.vy *= 0.50
class MouseSelection:
def __init__(self, game, image):
self.game = game
self.image = image
def update(self):
# get mouse x and y
self.mouse_x, self.mouse_y = pygame.mouse.get_pos()
# get the mouse offset position inside the tile
self.offset_x, self.offset_y = self.mouse_x % TILE_X, self.mouse_y % TILE_Y
self.offset_x += self.game.scroll_x % TILE_X # Add camera scroll to offset
self.offset_y += self.game.scroll_y % TILE_Y
# get the cell number
self.cell_x, self.cell_y = (self.mouse_x // TILE_X), (self.mouse_y // TILE_Y)
self.cell_x += int((self.game.scroll_x // TILE_X)) # Add camera scroll to cell
self.cell_y += int((self.game.scroll_y // TILE_Y))
# get the selected cell in iso grid
self.selected_x = (self.cell_y - ORIGIN_Y) + (self.cell_x - ORIGIN_X)
self.selected_y = (self.cell_y - ORIGIN_Y) - (self.cell_x - ORIGIN_X)
# height and width of a quarter of a tile, select the corner of the tile to nodge to a direction
h, w = TILE_Y / 2, TILE_X / 2
if self.offset_y < (h / w) * (w - self.offset_x):
self.selected_x -= 1
if self.offset_y > (h / w) * self.offset_x + h:
self.selected_y += 1
if self.offset_y < (h / w) * self.offset_x - h:
self.selected_y -= 1
if self.offset_y > (h / w) * (2 * w - self.offset_x) + h:
self.selected_x += 1
# translate the selected cell to world coordinate
self.selectedWorld_x, self.selectedWorld_y = self.game.to_screen(self.selected_x, self.selected_y)
def draw(self):
# Draw the selected tile with the camera scroll offset
self.game.screen.blit(self.image, (self.selectedWorld_x - self.game.scroll_x,
self.selectedWorld_y - self.game.scroll_y))
class SpriteSheet:
def __init__(self, image):
self.image = image
self.frames = []
def get_image(self):
for row in range(2):
for col in range(4):
if row == 0:
image = pygame.Surface((TILE_Y, TILE_Y / 2)).convert_alpha()
image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y))
image = pygame.transform.scale(image, (TILE_X, TILE_Y))
else:
image = pygame.Surface((TILE_Y, TILE_Y)).convert_alpha()
image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y * 2))
image = pygame.transform.scale(image, (TILE_X, TILE_Y * 2))
image.set_colorkey(WHITE)
self.frames.append(image)
return self.frames
# ------------------------- GAME LOOP ---------------------------- #
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(title)
self.clock = pygame.time.Clock()
pygame.key.set_repeat(400, 100)
self.debug = {}
self.sprite_sheet_image = pygame.image.load("isometric_whitebg - Copy.png")
self.index = 1
self.scroll_x, self.scroll_y = 0, 0
def new(self):
# initialize all variables and do all the setup for a new game
self.sprite_sheet = SpriteSheet(self.sprite_sheet_image)
self.tile_selected = self.sprite_sheet.get_image()[0]
self.tiles = self.sprite_sheet.get_image()
self.mouse_selection = MouseSelection(self, self.tile_selected)
self.camera = Camera(self, 1, 1)
def run(self):
# game loop - set self.playing = False to end the game
self.playing = True
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.update()
self.draw()
def quit(self):
pygame.quit()
sys.exit()
def update(self):
# update portion of the game loop
self.camera.update()
self.mouse_selection.update()
self.mx, self.my = pygame.mouse.get_pos()
# -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
if self.camera.x - self.scroll_x != WIDTH / 2:
self.scroll_x += (self.camera.x - (self.scroll_x + WIDTH / 2)) / 10
if self.camera.y - self.scroll_y != HEIGHT / 2:
self.scroll_y += (self.camera.y - (self.scroll_y + HEIGHT / 2)) / 10
# -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
self.debug_info()
def to_screen(self, x, y):
screen_x = (ORIGIN_X * TILE_X) + (x - y) * (TILE_X / 2)
screen_y = (ORIGIN_Y * TILE_Y) + (x + y) * (TILE_Y / 2)
return screen_x, screen_y
def draw_world(self):
for y in range(WORLD_Y):
for x in range(WORLD_X):
vWorld_x, vWorld_y = self.to_screen(x, y)
# Invisible tile
if self.index == 0:
self.screen.blit(self.tiles[1], (vWorld_x, vWorld_y))
# Grass
elif self.index == 1:
self.screen.blit(self.tiles[2], (vWorld_x - self.scroll_x, vWorld_y - self.scroll_y))
def draw(self):
self.screen.fill(BGCOLOUR)
self.draw_world()
self.mouse_selection.draw()
get_info(self.debug)
pygame.display.flip()
def debug_info(self):
self.debug["FPS"] = int(self.clock.get_fps())
self.debug["Cell"] = self.mouse_selection.cell_x, self.mouse_selection.cell_y
self.debug["Selected"] = int(self.mouse_selection.selected_x), int(self.mouse_selection.selected_y)
self.debug["Scroll"] = int(self.scroll_x), int(self.scroll_y)
self.debug["Mouse"] = int(self.mx), int(self.my)
self.debug["Mouse_offset"] = int(self.mouse_selection.offset_x), int(self.mouse_selection.offset_y)
def events(self):
# catch all events here
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
pass
game = Game()
while True:
game.new()
game.run()
Define the corner points of the map:
map_outline = [
pygame.math.Vector2(left_x, left_y),
pygame.math.Vector2(top_x, top_y),
pygame.math.Vector2(right_x, right_y,
pygame.math.Vector2(bottom_x, bottom_y)
]
With this information you can calculate the x and y axis of the map:
origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows
You can use the x-axis and the y-axis to calculate a point in the map as a function of the row and column:
def transform(p, mat2x2):
x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
return pygame.math.Vector2(x, y)
p_position = transform((column + 0.5, row + 0.5), (x_axis, y_axis)) + origin
If you want to get the row and column depending on the mouse cursor, you need to do the opposite. You need to calculate the inverse 2x2 matrix from the x and y axis. Using the inverse matrix, you can calculate the row and column as a function of a point on the map:
def inverseMat2x2(m):
a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
det = 1 / (a*d - b*c)
return [(d*det, -b*det), (-c*det, a*det)]
m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
Also see PyGameExamplesAndAnswers - Isometric
Minimal example:
replit.com/#Rabbid76/Pygame-IsometircMap
import pygame
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
colors = {'g': (40, 128, 40), 'd': (90, 60, 40)}
tilemap = [
'gdddg',
'dgddd',
'ggddg',
'ggddg',
'ddddg',
'dgggd'
]
columns, rows = len(tilemap[0]), len(tilemap)
isometric_tiles = {}
for key, color in colors.items():
tile_surf = pygame.Surface((50, 50), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
tile_size = (isometric_size, isometric_size//2)
def tileRect(column, row, tile_size):
x = (column + row) * tile_size[0] // 2
y = ((columns - column - 1) + row) * tile_size[1] // 2
return pygame.Rect(x, y, *tile_size)
game_map = pygame.Surface(((columns+rows) * isometric_size // 2, (columns+rows) * isometric_size // 4), pygame.SRCALPHA)
for column in range(columns):
for row in range(rows):
tile_surf = isometric_tiles[tilemap[row][column]]
tile_rect = tileRect(column, row, tile_size)
game_map.blit(tile_surf, tile_rect)
map_rect = game_map.get_rect(center = window.get_rect().center)
map_outline = [
pygame.math.Vector2(0, columns * isometric_size / 4),
pygame.math.Vector2(columns * isometric_size / 2, 0),
pygame.math.Vector2((columns+rows) * isometric_size // 2, rows * isometric_size / 4),
pygame.math.Vector2(rows * isometric_size / 2, (columns+rows) * isometric_size // 4)
]
for pt in map_outline:
pt += map_rect.topleft
origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows
def inverseMat2x2(m):
a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
det = 1 / (a*d - b*c)
return [(d*det, -b*det), (-c*det, a*det)]
point_to_grid = inverseMat2x2((x_axis, y_axis))
def transform(p, mat2x2):
x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
return pygame.math.Vector2(x, y)
font = pygame.font.SysFont(None, 30)
textO = font.render("O", True, (255, 255, 255))
textX = font.render("X", True, (255, 0, 0))
textY = font.render("Y", True, (0, 255, 0))
p_col, p_row = 2, 2
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a and p_col > 0:
p_col -= 1
if event.key == pygame.K_d and p_col < columns-1:
p_col += 1
if event.key == pygame.K_w and p_row > 0:
p_row -= 1
if event.key == pygame.K_s and p_row < rows-1:
p_row += 1
p_position = transform((p_col + 0.5, p_row + 0.5), (x_axis, y_axis)) + origin
m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
window.fill((0, 0, 0))
window.blit(game_map, map_rect)
pygame.draw.lines(window, (164, 164, 164), True, map_outline, 3)
pygame.draw.line(window, (255, 0, 0), origin, origin+x_axis, 3)
pygame.draw.line(window, (0, 255, 0), origin, origin+y_axis, 3)
pygame.draw.circle(window, (255, 255, 255), origin, 5)
pygame.draw.circle(window, (255, 0, 0), origin+x_axis, 5)
pygame.draw.circle(window, (0, 255, 0), origin+y_axis, 5)
window.blit(textO, textO.get_rect(topright = origin+(-5, 5)))
window.blit(textX, textX.get_rect(bottomright = origin+x_axis+(-5, -5)))
window.blit(textY, textX.get_rect(topright = origin+y_axis+(-5, 5)))
pygame.draw.ellipse(window, (255, 255, 0), (p_position[0]-16, p_position[1]-8, 32, 16))
if 0 <= m_grid_pos[0] < columns and 0 <= m_grid_pos[1] < rows:
tile_rect = tileRect(m_col, m_row, tile_size).move(map_rect.topleft)
pts = [tile_rect.midleft, tile_rect.midtop, tile_rect.midright, tile_rect.midbottom]
pygame.draw.lines(window, (255, 255, 255), True, pts, 4)
pygame.display.update()
pygame.quit()
exit()

Problem with speed unstability of enemies [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Problem with Pygame movement acceleration, platformer game
(1 answer)
Closed 1 year ago.
I tried to make the enemies(yellow box) follow the player(red box) with a constant velocity from all directions.
The problem of my code is the speed of the enemy from x+(Right) and y+(Bottom) axis to the player is slower than x-(Left) and y-(Above).
Image: https://i.stack.imgur.com/qJNp7.png
I think the problem is in here. I can't find it. It may be somewhere else.
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
print(enemy_types.rect.center)
x,y = enemy_types.rect.center
x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = (x,y)
Here is all of my code.
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 8 21:56:06 2021
#author: Toon
"""
import pygame
import cv2
import numpy as np
import math
import random
import time
dt = 0.01
window = (1280, 720)
win = pygame.display.set_mode(window)
background = pygame.Surface(window)
pygame.display.set_caption("First Game")
run = True
class playerIO():
def __init__(self):
self.name = 'Player'
self.width = 30
self.height = 30
self.speed = 1000
self.color = (255,0,0)
self.HPwidth = 50
self.HPheight = 8
self.HPoffset = 10
self.MAXHP = 1000
self.HP = self.MAXHP
self.HPregeneration_per_sec = 5
self.MANAwidth = 50
self.MANAheight = 8
self.MANAoffset = 10
self.MAXMANA = 3000
self.MANA = self.MAXMANA
self.MANAregeneration_per_sec = 10
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=(80, 80))
self.bullet_delay = [0, 0, 0]
def draw(self):
self.HPbar_high = self.rect.y-self.HPheight-self.HPoffset
self.MANAbar_high = self.HPbar_high-self.MANAheight-self.MANAoffset
pygame.draw.rect(win, self.color, self.rect)
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth, self.HPheight))
pygame.draw.rect(win, (0,255,0), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth*self.HP/self.MAXHP, self.HPheight))
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.MANAwidth/2, self.MANAbar_high, self.MANAwidth, self.MANAheight))
pygame.draw.rect(win, (0,0,255), (self.rect.x + self.width/2 - self.MANAwidth/2, self.MANAbar_high, self.MANAwidth*self.MANA/self.MAXMANA, self.MANAheight))
class BulletIO():
def __init__(self):
self.MANA_usage = 5
self.zeta = 0
self.width = 15
self.height = 15
self.speed = 1600
self.damage = 8
self.color = (0,255,0)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 0.2 #shot delay
def variant(self,X,Y, Zeta):
self.rect.x = X
self.rect.y = Y
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class LaserIO():
def __init__(self):
self.MANA_usage = 10
self.zeta = 0
self.width = 8
self.height = 8
self.speed = 500
self.damage = 20
self.color = (0,0,160)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 0 #shot delay
def variant(self,X,Y, Zeta):
self.rect.x = X
self.rect.y = Y
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class LaserBeamIO():
def __init__(self):
self.MANA_usage_per_sec = 1000
self.zeta = 0
self.width = 8
self.height = 8
self.speed = 500
self.damage = 20
self.color = (0,160,160)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 5 #shot delay
self.charge_time = 0
self.beam_distance = 60
self.max_charge_time = 10
self.charge_size_per_sec = 20
self.charge_damage_per_sec = 100
def charge_beam(self, player, Zeta):
self.charge_time+=dt
self.width += self.charge_size_per_sec * dt
self.height += self.charge_size_per_sec * dt
self.damage += self.charge_damage_per_sec * dt
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.rect.center = (player[0] + self.beam_distance * math.cos(Zeta), player[1] + self.beam_distance * math.sin(Zeta))
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class EnemyIO():
def __init__(self):
self.name = 'enemy'
self.width = 15
self.height = 15
self.zeta = 0
self.speed = 200
self.color = (255,255,0)
self.MAXHP = 16
self.regeneration = 0.2
self.HPwidth = 20
self.HPheight = 5
self.HPoffset = 8
self.HP = self.MAXHP
self.damage = 10
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=(20, 20))
def randompos(self, spawn_offset = 200):
while 1 :
try:
rand_pole = random.randint(1,4)
if rand_pole == 1 or rand_pole == 2:
self.rect.x = random.randint(FirstPlayer.rect.x+1+spawn_offset, window[0]-self.width)
else:
self.rect.x = random.randint(1,FirstPlayer.rect.x-spawn_offset)
if rand_pole == 1 or rand_pole == 4:
self.rect.y = random.randint(FirstPlayer.rect.y+1+spawn_offset, window[1]-self.height)
else:
self.rect.x = random.randint(1,FirstPlayer.rect.y-spawn_offset)
return 0
except:
pass
def draw(self):
self.HPbar_high = self.rect.y-self.HPheight-self.HPoffset
pygame.draw.rect(win, self.color, self.rect)
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth, self.HPheight))
pygame.draw.rect(win, (0,255,0), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth*self.HP/self.MAXHP, self.HPheight))
def direction(mouse, obj):
#y = 1 clockwise
zeta = math.atan2((mouse[1]-obj[1]),(mouse[0]-obj[0]))
return zeta
def enemy_spawn(Enemy_max = 30, slow_rate = 2):
num = len(Global_Obj[2])
if num < Enemy_max:
# random spawn enemy 1/100
rand = random.randint(1, int((num+1)*slow_rate))
if rand == 1:
ene = EnemyIO()
ene.randompos()
Global_Obj[2].append(ene)
#Global_Obj[0] are player obj type, Global_Obj[1] are bullet obj type, Global_Obj[2] are enemy obj type, Global_Obj[3] are beam obj type
Global_Obj = [[],[],[],[]]
FirstPlayer = playerIO()
Global_Obj[0].append(FirstPlayer)
win.blit(background,(0, 0))
mouse_pos = (-1,-1)
beam_load = False
pygame.init()
# define the RGB value for white,
# green, blue colour .
text_color = (0, 0, 0)
white = (255, 255, 255)
# set the pygame window name
pygame.display.set_caption('Game')
# create a font object.
# 1st parameter is the font file
# which is present in pygame.
# 2nd parameter is size of the font
font = pygame.font.Font('freesansbold.ttf', 32)
# create a text surface object,
# on which text is drawn on it.
text = font.render('Game start', True, text_color)
# create a rectangular object for the
# text surface object
textRect = text.get_rect()
# set the center of the rectangular object.
textRect.center = (window[0] // 2, window[1] // 2)
inteface_run = True
# infinite loop
while inteface_run:
# completely fill the surface object
# with white color
win.fill(white)
# copying the text surface object
# to the display surface object
# at the center coordinate.
win.blit(text, textRect)
# iterate over the list of Event objects
# that was returned by pygame.event.get() method.
for event in pygame.event.get():
if pygame.mouse.get_pressed()[0] or event.type == pygame.QUIT:
inteface_run = False
# Draws the surface object to the screen.
pygame.display.update()
pygame.time.delay(1000)
font = pygame.font.Font('freesansbold.ttf', 16)
end_time = 300 # sec
t0= time.time()
while run:
pygame.display.flip()
pygame.time.delay(int(dt*1000))
# player move event
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
run = False
if keys[pygame.K_SPACE] :
FirstPlayer.rect.x, FirstPlayer.rect.y = (80,80)
if (keys[pygame.K_a] or keys[pygame.K_LEFT]):
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x - FirstPlayer.speed * dt, FirstPlayer.rect.y)
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x + FirstPlayer.speed * dt, FirstPlayer.rect.y)
if keys[pygame.K_w] or keys[pygame.K_UP]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x, FirstPlayer.rect.y - FirstPlayer.speed * dt)
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x, FirstPlayer.rect.y + FirstPlayer.speed * dt)
if FirstPlayer.rect.x < 0:
FirstPlayer.rect.x = 0
if FirstPlayer.rect.x+FirstPlayer.width > window[0]:
FirstPlayer.rect.x = window[0] - FirstPlayer.width
if FirstPlayer.rect.y < 0:
FirstPlayer.rect.y = 0
if FirstPlayer.rect.y+FirstPlayer.height > window[1]:
FirstPlayer.rect.y = window[1] - FirstPlayer.height
#player HP regen
if FirstPlayer.HP < FirstPlayer.MAXHP:
FirstPlayer.HP +=FirstPlayer.HPregeneration_per_sec*dt
if FirstPlayer.HP > FirstPlayer.MAXHP:
FirstPlayer.HP = FirstPlayer.MAXHP
#player MANA regen
if FirstPlayer.MANA < FirstPlayer.MAXMANA:
FirstPlayer.MANA +=FirstPlayer.MANAregeneration_per_sec*dt
if FirstPlayer.MANA > FirstPlayer.MAXMANA:
FirstPlayer.MANA = FirstPlayer.MAXMANA
#bullet reload time
for i in range(len(FirstPlayer.bullet_delay)):
if FirstPlayer.bullet_delay[i]>0:
FirstPlayer.bullet_delay[i]-= dt
if FirstPlayer.bullet_delay[i]<0:
FirstPlayer.bullet_delay[i] =0
# bullet click event
mouse_pos = pygame.mouse.get_pos()
if pygame.mouse.get_pressed()[0] and FirstPlayer.MANA >= BulletIO().MANA_usage and FirstPlayer.bullet_delay[0]==0:
bullet = BulletIO()
FirstPlayer.MANA -= bullet.MANA_usage
zeta = direction(mouse_pos, FirstPlayer.rect.center)
bullet.variant(*FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[0] = bullet.reload_delay
Global_Obj[1].append(bullet)
if pygame.mouse.get_pressed()[2] and FirstPlayer.MANA >= LaserIO().MANA_usage and FirstPlayer.bullet_delay[1]==0:
laser = LaserIO()
FirstPlayer.MANA -= laser.MANA_usage
zeta = direction(mouse_pos, FirstPlayer.rect.center)
laser.variant( *FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[1] = laser.reload_delay
Global_Obj[1].append(laser)
# Global_Obj[1] bullet obj type movement
for bullet_type in Global_Obj[1]:
bullet_type.rect.x += dt * bullet_type.speed * math.cos(bullet_type.zeta)
bullet_type.rect.y += dt * bullet_type.speed * math.sin(bullet_type.zeta)
if bullet_type.rect.x > window[0] or bullet_type.rect.x < 0 or bullet_type.rect.y > window[1] or bullet_type.rect.y < 0:
Global_Obj[1].remove(bullet_type)
# Global_Obj[3] beam obj type movement
for bullet_type in Global_Obj[3]:
bullet_type.rect.x += dt * bullet_type.speed * math.cos(bullet_type.zeta)
bullet_type.rect.y += dt * bullet_type.speed * math.sin(bullet_type.zeta)
if bullet_type.rect.x > window[0] or bullet_type.rect.x < 0 or bullet_type.rect.y > window[1] or bullet_type.rect.y < 0:
Global_Obj[3].remove(bullet_type)
# enemy
enemy_spawn()
# Global_Obj[2] enemy obj type movement
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
print(enemy_types.rect.center)
x,y = enemy_types.rect.center
x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = (x,y)
# enemy-player damage check
for enemy_type in Global_Obj[2]:
if FirstPlayer.rect.colliderect(enemy_type.rect):
FirstPlayer.HP -= enemy_type.damage
Global_Obj[2].remove(enemy_type)
# bullet - enemy damage check
for bullet_type in Global_Obj[1]:
for enemy_type in Global_Obj[2]:
if bullet_type.rect.colliderect(enemy_type.rect):
enemy_type.HP -= bullet_type.damage
if enemy_type.HP<=0:
Global_Obj[2].remove(enemy_type)
Global_Obj[1].remove(bullet_type)
break
# beam - enemy damage check
for bullet_type in Global_Obj[3]:
for enemy_type in Global_Obj[2]:
if bullet_type.rect.colliderect(enemy_type.rect):
enemy_type.HP -= bullet_type.damage
if enemy_type.HP<=0:
Global_Obj[2].remove(enemy_type)
#end game
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 2 and FirstPlayer.bullet_delay[2]==0: # left mouse button
beam = LaserBeamIO()
Global_Obj[3].append(beam)
beam_load = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 2 and beam_load:
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[2] = beam.reload_delay
beam_load = False
if beam_load:
if FirstPlayer.MANA <= beam.MANA_usage_per_sec * dt:
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[2] = beam.reload_delay
beam_load = False
else:
FirstPlayer.MANA -= beam.MANA_usage_per_sec * dt
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
if FirstPlayer.HP <= 0:
run = False
te = int(time.time() - t0)
if te >= end_time:
run = False
# draw obj
win.blit(background,(0, 0))
for obj_types in Global_Obj:
for obj in obj_types:
obj.draw()
text = font.render(f'timer:{te}/{end_time} sec', True, white)
# create a rectangular object for the
# text surface object
textRect = text.get_rect()
# set the center of the rectangular object.
textRect.center = (window[0] // 2, window[1]-40)
win.blit(text, textRect)
pygame.quit()
Feel free to give me any suggestions to improve my code. I'm very new to Pygame.
Your enemies do move at the same speed in all directions, which can be measured by adding a few lines:
# Global_Obj[2] enemy obj type movement
for enemy_types in Global_Obj[2]:
zetas = math.dist(enemy_types.rect.center, FirstPlayer.rect.center)
x, y = enemy_types.rect.center
dx = dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
dy = dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
x += dx
y += dy
print((dx ** 2 + dy ** 2) ** 0.5)
enemy_types.rect.center = (x, y)
The code consistently prints out 2.
I suspect that it has something to do with the amount of time it takes to evaluate negative/positive calculations, which can be measured using the time.perf_counter() method.
Tip: Your
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
can be replaced with
zetas = math.dist(enemy_types.rect.center, FirstPlayer.rect.center)
a much more efficient method.
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data:
The coordinates for Rect objects are all integers. [...]
When you do
enemy_types.rect.center = (x,y)
it is the same as you would do:
enemy_types.rect.center = (int(x), int(y))
The fraction component of the coordinate get lost. This causes that the movement to the left and to the top is faster than to the right and to the bottom.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location of the rectangle:
class EnemyIO():
def __init__(self):
# [...]
self.x, self.y = self.rect.center
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
enemy_types.x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
enemy_types.y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = round(enemy_types.x), round(enemy_types.y)

Python treating all instances of an object as the same

I'm making a game with pygame and pymunk as a physics engine. I'm trying to kill a bullet whenever it hits a player or goes past its lifetime.
When I tried to space.remove(self.shape) and the second bullet hits the player, it gives me an "AssertionError: shape not in space, already removed. I simply changed it to teleport the bullets away, and then learned of the real error.
When I have more than one bullet in the space and a bullet hits the enemy player, all the current bullets teleport away, which means that when I tried to remove one bullet, it called the remove on all the bullets and thats why I had the initial error.
However the problem still remains that one bullet is being treated as every bullet.
Why is something that should be a non-static variable being called as a static variable?
I even tried to use deepcopy to see if that fixed it, but to no avail
This is my chunk of code, apologies since I don't know what is needed to understand it.
The key parts are most likely the Bullet class, the shoot() function in the Player class, and the drawBulletCollision() function
# PyGame template.
# Import modules.
import sys, random, math, time, copy
from typing import List
import pygame
from pygame.locals import *
from pygame import mixer
import pymunk
import pymunk.pygame_util
from pymunk.shapes import Segment
from pymunk.vec2d import Vec2d
pygame.mixer.pre_init(44110, -16, 2, 512)
mixer.init()
# Set up the window.
width, height = 1440, 640
screen = pygame.display.set_mode((width, height))
bg = pygame.image.load("space.png")
def draw_bg():
screen.blit(bg, (0, 0))
#load sounds
#death_fx = pygame.mixer.Sound("")
#death_fx.set_volume(0.25)
shoot_fx = mixer.Sound("shot.wav")
shoot_fx.set_volume(0.25)
#mixer.music.load("video.mp3")
#mixer.music.play()
#time.sleep(2)
#mixer.music.stop()
#gun_mode_fx = pygame.mixer.Sound("")
#gun_mode_fx.set_volume(0.25)
#thrust_mode_fx = pygame.mixer.Sound("")
#thrust_mode_fx.set_volume(0.25)
collision_fx = mixer.Sound("thump.wav")
collision_fx.set_volume(0.25)
ship_group = pygame.sprite.Group()
space = pymunk.Space()
space.gravity = 0, 0
space.damping = 0.6
draw_options = pymunk.pygame_util.DrawOptions(screen)
bulletList = []
playerList = []
environmentList = []
arbiterList = []
b0 = space.static_body
segmentBot = pymunk.Segment(b0, (0,height), (width, height), 4)
segmentTop = pymunk.Segment(b0, (0,0), (width, 0), 4)
segmentLef = pymunk.Segment(b0, (width,0), (width, height), 4)
segmentRit = pymunk.Segment(b0, (0,0), (0, height), 4)
walls = [segmentBot,segmentLef,segmentRit,segmentTop]
for i in walls:
i.elasticity = 1
i.friction = 0.5
i.color = (255,255,255,255)
environmentList.append(i)
class Player(object):
radius = 30
def __init__(self, position, space, color):
self.body = pymunk.Body(mass=5,moment=10)
self.mode = 0 # 0 is gun, 1 is thrust, ? 2 is shield
self.body.position = position
self.shape = pymunk.Circle(self.body, radius = self.radius)
#self.image
#self.shape.friction = 0.9
self.shape.elasticity= 0.2
space.add(self.body,self.shape)
self.angleGun = 0
self.angleThrust = 0
self.health = 100
self.speed = 500
self.gearAngle = 0
self.turningSpeed = 5
self.shape.body.damping = 1000
self.cooldown = 0
self.fireRate = 30
self.shape.collision_type = 1
self.shape.color = color
playerList.append(self)
def force(self,force):
self.shape.body.apply_force_at_local_point(force,(0,0))
def rocketForce(self):
radians = self.angleThrust * math.pi/180
self.shape.body.apply_force_at_local_point((-self.speed * math.cos(radians),-self.speed * math.sin(radians)),(0,0))
def draw(self):
gear = pygame.image.load("gear.png")
gearBox = gear.get_rect(center=self.shape.body.position)
gearRotated = pygame.transform.rotate(gear, self.gearAngle)
#gearRotated.rect.center=self.shape.body.position
x,y = self.shape.body.position
radianGun = self.angleGun * math.pi/180
radianThrust = self.angleThrust * math.pi/180
radiyus = 30 *(100-self.health)/100
screen.blit(gearRotated,gearBox)
self.gearAngle += 1
if radiyus == 30:
radiyus = 32
pygame.draw.circle(screen,self.shape.color,self.shape.body.position,radiyus,0)
pygame.draw.circle(screen,(0,0,0),self.shape.body.position,radiyus,0)
pygame.draw.line(
screen,(0,255,0),
(self.radius * math.cos(radianGun) * 1.5 + x,self.radius * math.sin(radianGun) * 1.5 + y),
(x,y), 5
)
pygame.draw.line(
screen,(200,200,0),
(self.radius * math.cos(radianThrust) * 1.5 + x,self.radius * math.sin(radianThrust) * 1.5 + y),
(x,y), 5
)
#more
def targetAngleGun(self,tAngle):
tempTAngle = tAngle - self.angleGun
tempTAngle = tempTAngle % 360
if(tempTAngle < 180 and not tempTAngle == 0):
self.angleGun -= self.turningSpeed
elif(tempTAngle >= 180 and not tempTAngle == 0):
self.angleGun += self.turningSpeed
self.angleGun = self.angleGun % 360
#print(tAngle, "target Angle")
#print(self.angleGun, "selfangleGun")
#print(tempTAngle, "tempTAngle")
def targetAngleThrust(self,tAngle):
tempTAngle = tAngle - self.angleThrust
tempTAngle = tempTAngle % 360
if(tempTAngle < 180 and not tempTAngle == 0):
self.angleThrust -= self.turningSpeed
elif(tempTAngle >= 180 and not tempTAngle == 0):
self.angleThrust += self.turningSpeed
self.angleThrust = self.angleThrust % 360
#print(tAngle, "target Angle")
#print(self.angleThrust, "selfangleGun")
#print(tempTAngle, "tempTAngle")
def targetAngle(self,tAngle):
if(self.mode == 0):
self.targetAngleGun(tAngle)
elif(self.mode == 1):
self.targetAngleThrust(tAngle)
def shoot(self):
if(self.cooldown == self.fireRate):
x,y = self.shape.body.position
radianGun = self.angleGun * math.pi/180
spawnSpot = (self.radius * math.cos(radianGun) * 1.5 + x,self.radius * math.sin(radianGun)*1.5+y)
self.shape.body.apply_impulse_at_local_point((-20 * math.cos(radianGun),-20 * math.sin(radianGun)),(0,0))
print(spawnSpot)
bT = Bullet(spawnSpot, 5, 50,self.shape.color)
b = copy.deepcopy(bT)
bulletList.append(b)
space.add(b.shape,b.shape.body)
b.getShot(self.angleGun)
self.cooldown = 0
print('pew')
shoot_fx.play()
# HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEREEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
def tick(self):
self.draw()
if(self.cooldown < self.fireRate):
self.cooldown += 1
#for o in playerList:
# c = self.shape.shapes_collide(o.shape)
# if(len(c.points)>0):
# self.damage(c.points[0].distance/10)
for o in bulletList:
c = self.shape.shapes_collide(o.shape)
#print(c)
for o in walls:
c = self.shape.shapes_collide(o)
if(len(c.points)>0):
self.damage(c.points[0].distance * 3)
def damage(self, damage):
self.health -= abs(damage)
if self.health < 0:
self.health = 0
#maybe make it part of the player class
def drawWallCollision(arbiter, space, data):
for c in arbiter.contact_point_set.points:
r = max(3, abs(c.distance * 5))
r = int(r)
p = tuple(map(int, c.point_a))
pygame.draw.circle(data["surface"], pygame.Color("red"), p, r, 0)
print('magnitude', math.sqrt(arbiter.total_impulse[0]**2 + arbiter.total_impulse[1]**2))
#print('position', p)
#print(data)
print("its all arbitrary")
s1, s2 = arbiter.shapes
collision_fx.play()
def drawBulletCollision(arbiter, space, data):
s1, s2 = arbiter.shapes
for c in arbiter.contact_point_set.points:
magnitude = math.sqrt(arbiter.total_impulse[0]**2 + arbiter.total_impulse[1]**2)
for p in playerList:
avr = ((c.point_a[0] + c.point_b[0])/2, (c.point_a[1] + c.point_b[1])/2)
distance = (math.sqrt((avr[0] - p.shape.body.position[0]) **2 + (avr[1] - p.shape.body.position[1]) **2 ))
if(distance < Bullet.explosionRadius + Player.radius):
if not(s1.color == s2.color):
p.damage(magnitude)
for b in bulletList:
avr = ((c.point_a[0] + c.point_b[0])/2, (c.point_a[1] + c.point_b[1])/2)
distance = (math.sqrt((avr[0] - p.shape.body.position[0]) **2 + (avr[1] - p.shape.body.position[1]) **2 ))
if(distance < Bullet.explosionRadius + Player.radius):
if not(s1.color == s2.color):
b.damage(magnitude)
pygame.draw.circle(data["surface"], pygame.Color("red"), tuple(map(int, c.point_a)), 10, 0)
print('magnitude', magnitude)
#print('position', p)
#print(data)
print("its all arbitrary")
def drawArbitraryCollision(arbiter, space, data):
collision_fx.play()
class Ship(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("gear.png")
self.rect = self.image.get_rect()
self.rect.center = [x, y]
def rotate(self):
self.image = pygame.transform.rotate(self.image,1)
class Bullet(object):
damage = 2
explosionRadius = 5
def __init__(self, position, size, speed,color):
pts = [(-size, -size), (size, -size), (size, size), (-size, size)]
self.body = copy.deepcopy(pymunk.Body(mass=0.1,moment=1))
self.shape = copy.deepcopy(pymunk.Poly(self.body, pts))
self.shape.body.position = position
self.shape.friction = 0.5
self.shape.elasticity = 1
self.shape.color = color
self.speed = speed
self.size = size
self.shape.collision_type = 2
#space.add(self.body,self.shape)
#bulletList.append(self)
self.lifetime = 0
def getShot(self,angle):
radians = angle * math.pi/180
self.shape.body.apply_impulse_at_local_point((self.speed * math.cos(radians),self.speed * math.sin(radians)),(0,0))
def tick(self):
self.lifetime += 1
if(self.lifetime > 300):
self.shape.body.position = (10000,30)
def damage(self, damage):
self.lifetime = 300
#VELOCITY OF BULLET STARTS WITH VELOCITY OF PLAYER
#MAKE VOLUME OF SOUND DEPEND ON THE IMPULSE FOR THE IMPACTS
#error on purpose so you notice this
#INSTANCES NOT WORKING????
def runPyGame():
# Initialise PyGame.
pygame.init()
# Set up the clock. This will tick every frame and thus maintain a relatively constant framerate. Hopefully.
fps = 60.0
fpsClock = pygame.time.Clock()
running = True
font = pygame.font.SysFont("Arial", 16)
p1 = Player((240,240),space,(132, 66, 245,255))
p2 = Player((1200,400),space,(47, 247, 184,255))
space.add(segmentBot,segmentTop,segmentLef,segmentRit)
# Main game loop.
ch = space.add_collision_handler(1, 0)
ch.data["surface"] = screen
ch.post_solve = drawWallCollision
ch = space.add_collision_handler(1, 2)
ch.data["surface"] = screen
ch.post_solve = drawBulletCollision
ch = space.add_collision_handler(0, 2)
ch.data["surface"] = screen
ch.post_solve = drawArbitraryCollision
dt = 1/fps # dt is the time since last frame.
while True: # Loop forever!
keys = pygame.key.get_pressed()
for event in pygame.event.get():
# We need to handle these events. Initially the only one you'll want to care
# about is the QUIT event, because if you don't handle it, your game will crash
# whenever someone tries to exit.
if event.type == QUIT:
pygame.quit() # Opposite of pygame.init
sys.exit() # Not including this line crashes the script on Windows.
if event.type == KEYDOWN:
if event.key == pygame.K_s:
p1.mode = -(p1.mode - 0.5) + 0.5
print(p1.mode)
if (event.key == pygame.K_k and p1.mode == 0):
p1.shoot()
if event.key == pygame.K_KP_5:
p2.mode = -(p2.mode - 0.5) + 0.5
print(p2.mode)
if (event.key == pygame.K_m and p2.mode == 0):
p2.shoot()
#b = Bullet((200,200),51,51)
if(keys[K_w]):
p1.targetAngle(90)
if(keys[K_q]):
p1.targetAngle(45)
if(keys[K_a]):
p1.targetAngle(0)
if(keys[K_z]):
p1.targetAngle(315)
if(keys[K_x]):
p1.targetAngle(270)
if(keys[K_c]):
p1.targetAngle(225)
if(keys[K_d]):
p1.targetAngle(180)
if(keys[K_e]):
p1.targetAngle(135)
if(keys[K_k] and p1.mode == 1):
p1.rocketForce()
if(keys[K_KP_8]):
p2.targetAngle(90)
if(keys[K_KP_7]):
p2.targetAngle(45)
if(keys[K_KP_4]):
p2.targetAngle(0)
if(keys[K_KP_1]):
p2.targetAngle(315)
if(keys[K_KP_2]):
p2.targetAngle(270)
if(keys[K_KP_3]):
p2.targetAngle(225)
if(keys[K_KP_6]):
p2.targetAngle(180)
if(keys[K_KP_9]):
p2.targetAngle(135)
if(keys[K_m] and p2.mode == 1):
p2.rocketForce()
# Handle other events as you wish.
screen.fill((250, 250, 250)) # Fill the screen with black.
# Redraw screen here.
### Draw stuff
draw_bg()
space.debug_draw(draw_options)
for i in playerList:
i.tick()
screen.blit(
font.render("P1 Health: " + str(p1.health), True, pygame.Color("white")),
(50, 10),
)
screen.blit(
font.render("P2 Health: " + str(p2.health), True, pygame.Color("white")),
(50, 30),
)
for i in bulletList:
i.tick()
ship_group.draw(screen)
# Flip the display so that the things we drew actually show up.
pygame.display.update()
dt = fpsClock.tick(fps)
space.step(0.01)
pygame.display.update()
runPyGame()
I cant point to the exact error since the code is quite long and depends on files I dont have. But here is a general advice for troubleshooting:
Try to give a name to each shape when you create them, and then print it out. Also print out the name of each shape that you add or remove from the space. This should show which shape you are actually removing and will probably make it easy to understand whats wrong.
For example:
...
self.shape = pymunk.Circle(self.body, radius = self.radius)
self.shape.name = "circle 1"
print("Created", self.shape.name)
...
print("Adding", self.shape.name)
space.add(self.body,self.shape)
...
(Note that you need to reset the name of shapes you copy, since otherwise the copy will have the same name.)

How to create multiple objects of a class with unique name values gathered from a list in a csv file

I'm new to programming and I'm struggling to figure out how to get this to work. I'm trying to build a simple game called "Pet pandemic" and you basically just press start and the "pets" marked by different colored dots roam around on a grid in the window. Some of the pets spawn in with the "virus". While roaming the pets will see if they are within a certain distance from one another and if so they will transmit the virus to the other who doesn't have it already, in turn updating the status of that pet to have the "virus" and there dot image will update to be red. The game will run until every "pet" has the "virus". I'm struggling to implement a way to spawn the "pets" with unique names like "Alex" or "Doug". I would like to use a list of names I have that's in a .csv file for this.
Thank you for your time.
Here are the files:
https://drive.google.com/drive/folders/1tjEY6Eo1nKKtEr0st1CUFkcnBvvbiMn1?usp=sharing
import pygame
import random
import math
import time
# Window settings
WIDTH = 960
HEIGHT = 660
TITLE = "PET PANDEMIC"
FPS = 60
#create window
pygame.init()
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption(TITLE)
clock = pygame.time.Clock()
#Game Stages
START = 0
PLAYING = 1
END = 2
#Define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (132, 201, 217)
# Load fonts
title_font = default_font = pygame.font.Font("fonts/recharge bd.ttf", 60)
default_font = pygame.font.Font(None, 55)
# Load images
has_v_img = pygame.image.load("red_dot.png").convert_alpha()
no_v_img = pygame.image.load("grey_dot.png").convert_alpha()
no_v_mask_img = pygame.image.load("black_dot.png").convert_alpha()
#Game classes
class Pet(pygame.sprite.Sprite):
def __init__(self, name, x, y):
super().__init__()
self.wearing_mask = random.randint(0, 1) # 0 = no, 1 = yes.
self.has_virus = random.randint(0, 6) # 6 = Virus
if self.has_virus == 6:
self.image = has_v_img
elif self.wearing_mask != 1 and self.has_virus != 6:
self.image = no_v_mask_img
else:
self.image = no_v_img
self.rect = self.image.get_rect()
self.rect.center = x, y
self.name = name
self.dir = random.randint(0, 3)
self.is_alive = True
self.print_name()
def print_name(self):
amnt_w_virus = 0
if self.has_virus == 6:
print("Spawned {} ".format(self.name) + "With The Virus!")
amnt_w_virus += 1
else:
print("Spawned {}.".format(self.name))
return amnt_w_virus
def check_distance(self, other):
distance = math.sqrt(((self.x - other.x) ** 2) + ((self.y - other.y) ** 2))
if distance < 6 and self.has_virus == 6:
other.has_virus = 6
print(self.name + " Was In Range Of " + other.name + " And Gave Them The Virus.")
amnt_w_virus += 1
else:
return amnt_w_virus
def roam(self):
while game_running:
if self.is_alive == True:
time.sleep(1.5)
self.dir = random.randint(0, 3)
self.move()
def move(self):
if self.dir == 0:
self.rect.y += 1
elif self.dir == 1:
self.rect.x += 1
elif self.dir == 2:
self.rect.y -= 1
elif self.dir == 3:
self.rect.x -= 1
if self.rect.left < 0:
self.rect.left = 0
elif self.rect.top > HEIGHT:
self.rect.top = HEIGHT
elif self.rect.right > WIDTH:
self.rect.right = WIDTH
print(self.name + " Moved Forward In Direction " + str(self.dir) + " (" + str(self.x) + "," + str(self.y) + ").")
class Players(pygame.sprite.Group):
def __init__(self, *sprites):
super().__init__(*sprites)
def update(self, *args):
super().update(*args)
self.check_distance()
self.roam()
# Setup
def new_game():
global players
#Random Spawn Location Inside Window
start_x = random.randint(0,WIDTH)
start_y = random.randint(0,HEIGHT)
#Open List Of Names To Use For The Pets
f = open("names.csv", "r")
reader = csv.reader(f)
name = []
for row in reader:
name.append(row)
pet = {}
#Here Im trying to spawn the pets and assign them unique names from the list but this code is all messed up
for item in name:
name = '{}'.format(item)
pet[name] = pet.get(name, Pet(name=name))
pets = Pet([name], start_x, start_y)
#making players = a sprite group of all the pets
players = Players(pets)
#trying to calculate the number of pets without the virus
players.amount_alive = len(players) - amnt_w_virus
print(players.amnt_alive)
#grid overlay
def draw_grid(width, height, scale):
for x in range(0, WIDTH, scale):
pygame.draw.line(screen, WHITE, [x, 0], [x, HEIGHT], 1)
for y in range(0, HEIGHT, scale):
pygame.draw.line(screen, WHITE, [0, y], [WIDTH, y], 1)
def display_stats():
infected_text = default_font.render("Infected: " + str(amnt_w_virus), True, WHITE)
rect = infected_text.get_rect()
rect.top = 20
rect.left = 20
screen.blit(infected_text, rect)
non_infected_text = default_font.render("Non-Infected: " + str(players.amount_alive), True, WHITE)
rect = non_infected_text.get_rect()
rect.top = 20
rect.right = WIDTH - 20
screen.blit(non_infected_text, rect)
time_text = default_font.render("Time: " + str(time.time), True, WHITE)
rect = time_text.get_rect()
rect.bottom = HEIGHT - 20
rect.left = 20
screen.blit(time_text, rect)
def start_screen():
screen.fill(BLACK)
title_text = title_font.render(TITLE, True, WHITE)
rect = title_text.get_rect()
rect.centerx = WIDTH // 2
rect.bottom = HEIGHT // 2 - 15
screen.blit(title_text, rect)
sub_text = default_font.render("Press Any Key To Start The Spread", True, WHITE)
rect = sub_text.get_rect()
rect.centerx = WIDTH // 2
rect.top = HEIGHT // 2 + 15
screen.blit(sub_text, rect)
def end_screen():
end_text = default_font.render("GAME OVER, EVERYONE IS INFECTED.", True, WHITE)
rect = end_text.get_rect()
rect.centerx = WIDTH // 2
rect.centery = HEIGHT // 2
screen.blit(end_text, rect)
# Game loop
new_game()
stage = START
running = True
while running:
# Input handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if stage == START:
stage = PLAYING
elif stage == END:
if event.key == pygame.K_r:
new_game()
stage = START
# Game logic
if stage != START:
players.update()
if len(players) == 0:
stage = END
# Drawing Code
screen.fill(BLUE)
draw_grid(960, 660, 20)
players.draw(screen)
display_stats()
pygame.display.flip()
if stage == START:
start_screen()
elif stage == END:
end_screen()
# Update screen
pygame.display.update()
# Limit refresh rate of game loop
clock.tick(FPS)
# Close window and quit
pygame.quit() ```
You are overwriting the object called name:
for item in name:
name = '{}'.format(item) ### BAD BAD BAD BAD BAD
pet[name] = pet.get(name, Pet(name=name))
pets = Pet([name], start_x, start_y)
#making players = a sprite group of all the pets
players = Players(pets)
If you actually wanted to do that then you need to preserve the items in name by converting it to a list.
for item in list(name):
name = '{}'.format(item)
pet[name] = pet.get(name, Pet(name=name))
pets = Pet([name], start_x, start_y)
#making players = a sprite group of all the pets
players = Players(pets)
I'm sure that you didn't actually want to do that though so just use a different variable name (e.g. _name).
for item in name:
_name = '{}'.format(item)
pet[_name] = pet.get(_name, Pet(name=_name))
pets = Pet([_name], start_x, start_y)
#making players = a sprite group of all the pets
players = Players(pets)

How to fix 'apples' sometimes not appearing in my version of Snake? [duplicate]

I'm currently in the process of creating a Snake game and I want to create a food generator that generates an apple every 10 seconds based on my in-game timer. The timer counts down from 60 to 0(when the game ends) and I want an new apple to generate every 10 seconds, keeping the old one even if it hasn't been eaten. I don't know how to approach this and could use some help. Here is my full program.
Edit: this is a beginner Computer Science school project so the more basic the better.
import random
import pygame
pygame.init()
#---------------------------------------#
#
# window properties #
width = 640 #
height = 480
game_window=pygame.display.set_mode((width,height))
black = ( 0, 0, 0) #
#---------------------------------------#
# snake's properties
outline=0
body_size = 9
head_size = 10
apple_size = 8
speed_x = 8
speed_y = 8
dir_x = 0
dir_y = -speed_y
segx = [int(width/2.)]*3
segy = [height, height + speed_y, height + 2*speed_y]
segments = len(segx)
apple_counter = 0
grid_step = 8
regular_font = pygame.font.SysFont("Andina",18)
blue = [11, 90, 220]
clock = pygame.time.Clock()
time = 60
fps = 25
time = time + 1.0/fps
text = regular_font.render("Time from start: "+str(int(time)), 1, blue)
text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue)
apple_x = random.randrange(0, 640, grid_step)
apple_y = random.randrange(0, 480, grid_step)
apple_colour = (255,0,0)
def redraw_game_window():
game_window.fill(black)
for i in range(segments):
segment_colour = (random.randint(1,50),random.randint(100,150),random.randint(1,50))
head_colour = (random.randint(180,220),random.randint(180,220),random.randint(1,15))
apple_colour = (255,0,0)
pygame.draw.circle(game_window, segment_colour, (segx[i], segy[i]), body_size, outline)
pygame.draw.circle(game_window, head_colour, (segx[0], segy[0]), head_size, outline)
game_window.blit(text, (530, 20))
game_window.blit(text2, (30, 20))
pygame.draw.circle(game_window, apple_colour, (apple_x, apple_y), apple_size, outline)
pygame.display.update()
exit_flag = False
print "Use the arrows and the space bar."
print "Hit ESC to end the program."
########################################################## TIMER/CONTROLS
while exit_flag == False:
redraw_game_window()
clock.tick(fps)
time = time - 1.00/fps
text = regular_font.render("Time: "+str(int(time)), 1, blue)
text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue)
if time < 0.1:
print "Game Over"
exit_flag = True
pygame.event.get()
keys = pygame.key.get_pressed()
if time ==
if keys[pygame.K_ESCAPE]:
exit_flag = True
if keys[pygame.K_LEFT] and dir_x != speed_x:
dir_x = -speed_x
dir_y = 0
if keys[pygame.K_RIGHT] and dir_x != -speed_x:
dir_x = speed_x
dir_y = 0
if keys[pygame.K_UP] and dir_y != speed_x:
dir_x = 0
dir_y = -speed_y
if keys[pygame.K_DOWN] and dir_y != -speed_x:
dir_x = 0
dir_y = speed_y
############################################################ SNAKE MOVEMENT
for i in range(segments-1,0,-1):
segx[i]=segx[i-1]
segy[i]=segy[i-1]
segx[0] = segx[0] + dir_x
segy[0] = segy[0] + dir_y
############################################################ COLLISION
for i in range(segments-1, 3, -1):
if segments > 3:
if segx[0] == segx[i] and segy[0] == segy[i]:
print "You have collided into yourself, Game Over."
exit_flag = True
############################################################# BORDERS
if segx[0] > 640 or segx[0] < 0:
print "Game Over, you left the borders."
break
if segy[0] > 480 or segy[0] < 0:
print "Game Over, you left the borders."
break
############################################################# APPLE DETECT
for i in range (0 , 13):
if segx[0] == apple_x + i and segy[0] == apple_y + i:
segments = segments + 1
segx.append(segx[-1])
segy.append(segy[-1])
apple_counter = apple_counter + 1
if segx[0] == apple_x - i and segy[0] == apple_y - i:
segments = segments + 1
segx.append(segx[-1])
segy.append(segy[-1])
apple_counter = apple_counter + 1
#############################################################
pygame.quit()
You either
A) use pygame.time.set_timer to call a function every 10 seconds to spawn food, and every 60 seconds to end the round.
or
B) compare get_ticks()
def new_round():
last_apple = pygame.time.get_ticks() + 10*1000
while true:
now = pygame.time.get_ticks()
if now - last_apple >= 1000:
spawn_apple()
last_apple = now
if now - round_start >= 60*1000:
round_end()

Categories

Resources