I am learning pygame. I wanted to make the camera follows the player so that the player is in the middle of the screen all the time. Even though I move the camera about the same vector, the player moves much faster and runs off the screen. Thanks in advance for any attempts to help. I am posting the whole code, but the problem probably lies in one of the functions: player.move(), level.move_camera() or level.render().
import pygame
from pygame.math import Vector2
#####SETTINGS#####
HEIGHT = 1080
WIDTH = 1920
TICKRATE = 60
BLOCK_SIZE = 60
GRAVITY = 18
##################
class Level():
def __init__(self, file):
self.file = file
self.blocks = [] # list of tills
self.map = self.load_from_file()
self.camera_position = Vector2(WIDTH / 2, 0)
# loading map
def load_from_file(self):
map = []
file = open(self.file + '.txt', 'r')
data = file.read()
file.close()
data = data.split('\n')
for x in data:
map.append(list(x))
return map
def move_camera(self, player):
self.camera_position.x += player.shift.x
def render(self, screen):
self.blocks = []
y = 0
for row in self.map:
x = 0
for block in row:
if block != '0':
self.blocks.append(pygame.Rect(x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
if block == '1':
pygame.draw.rect(screen, (56,24,0), (x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
elif block == '2':
pygame.draw.rect(screen, (18,115,81), (x * BLOCK_SIZE - self.camera_position.x, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
x += 1
y += 1
class Player():
def __init__(self, x, y, color):
self.position = Vector2(x, y)
self.shift = Vector2(0, 0)
self.jump = Vector2(0, 0)
self.color = color
self.rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
self.go_left = False
self.go_right = False
self.go_up = False
self.collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
# moving the player object
def move(self, level):
self.shift = Vector2(0, GRAVITY)
self.position.y = int(self.position.y)
# left / right section
if self.go_left:
self.go_left = False
self.collisions = test_collisions(pygame.Rect(self.position.x - 1, self.position.y, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if not self.collisions['left']:
self.shift += Vector2(-10, 0)
if self.go_right:
self.go_right = False
self.collisions = test_collisions(pygame.Rect(self.position.x + 1, self.position.y, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if not self.collisions['right']:
self.shift += Vector2(10, 0)
# gravity section
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y + GRAVITY, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.collisions['bottom']:
self.shift -= Vector2(0, GRAVITY)
if self.position.y % BLOCK_SIZE > 0:
self.position.y += BLOCK_SIZE - (self.position.y % BLOCK_SIZE)
# jump section
if self.go_up:
self.go_up = False
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y + GRAVITY, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.collisions['bottom']:
self.jump = Vector2(0, -80)
self.collisions = test_collisions(pygame.Rect(self.position.x, self.position.y - 1, BLOCK_SIZE, BLOCK_SIZE), level.blocks)
if self.jump.y > GRAVITY or self.collisions['top']:
self.jump = Vector2(0, 0)
else:
self.jump *= 0.9
self.shift += self.jump
# new position
self.position += self.shift
self.rect = pygame.Rect(self.position.x, self.position.y, BLOCK_SIZE, BLOCK_SIZE)
# checking if player is death
def check_death(self):
if self.position.y > HEIGHT:
new_game()
# render player
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
# render section
def render(screen, player, level):
screen.fill((49, 113, 181))
level.render(screen)
player.render(screen)
pygame.display.update()
# keyboard handling
def handle_events(player):
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
pygame.quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
player.go_left = True
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
player.go_right = True
if keys[pygame.K_w] or keys[pygame.K_UP] or keys[pygame.K_SPACE]:
player.go_up = True
# test direction of collison (rects have to overlap)
def test_collisions(object, rects):
collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
for rect in rects:
if object.colliderect(rect):
if object.x <= rect.x:
collisions['right'] = True
if object.x >= rect.x:
collisions['left'] = True
if object.y >= rect.y:
collisions['top'] = True
if object.y <= rect.y:
collisions['bottom'] = True
return(collisions)
# game loop
def main_loop(run, screen, level, player):
clock = pygame.time.Clock()
while run:
clock.tick(TICKRATE)
handle_events(player)
player.move(level)
player.check_death()
level.move_camera(player)
render(screen, player, level)
# init new game
def new_game():
run = True
screen = pygame.display.set_mode((WIDTH, HEIGHT))
level = Level('assets/level_two')
level.camera_position = Vector2(0, 0)
player = Player(WIDTH / 2, 0, (255,255,0))
main_loop(run, screen, level, player)
# launch new game on start
if __name__ == "__main__":
new_game()
You are not taking into account the players movement on itself, you only apply it to the blocks. For it to always be in the centre, create a direction vector towards the centre from the player and add that to the player position like this:
class Player():
#...
def move(self, level):
#...
#either this
self.position.x += ((WIDTH / 2) - self.position.x)
#or if you want a camera following with a delay effect
delay = 5
self.position.x += ((WIDTH / 2) - self.position.x) / delay
Related
This question already has answers here:
Subsurface ValueError: 'subsurface rectangle outside surface area'
(1 answer)
Subsurface rect outside of surface area when using get_clip() in Pygame?
(1 answer)
How can I crop an image with Pygame?
(4 answers)
Closed 2 months ago.
I am currently working on a game on Pygame in Python. It is a player vs player battle game. When the program is run the value error subsurface rectangle outside surface area is shown. I searched about the error but could not understand about it as I am new to Pygame.
My program-
import pygame
from Players import Characters
pygame.init()
#Game window
swidth = 1000
sheight= 600
s=pygame.display.set_mode((swidth, sheight))
pygame.display.set_caption("Brawler")
#Framerate
clock = pygame.time.Clock()
FPS = 60
#Game variables
count=3
last_count_update=pygame.time.get_ticks()
score = [0, 0]#player scores. [P1, P2]
round_over = False
round_cooldown = 2000
#Player variables
Knight_size = 278
Knight_scale = 4
Knight_offset = [72, 56]
Knight_data = [Knight_size, Knight_scale, Knight_offset]
Assassin_size = 288
Assassin_scale = 3
Assassin_offset = [112, 107]
Assassin_data =[ Assassin_size, Assassin_scale, Assassin_offset]
#load background image
bg_image = pygame.image.load("G1/Assets/Bg.png").convert_alpha()
#load spritesheets
Knight_sheet = pygame.image.load("G1/Assets/Knight/Sprites.png").convert_alpha()
Assassin_sheet = pygame.image.load("G1/Assets/Assassin/Sprites.png").convert_alpha()
#load vicory image
victory_img = pygame.image.load("G1/Assets/victory.png").convert_alpha()
#define number of steps in each animation
Knight_steps=[8, 8, 20, 8, 11, 19, 10, 6, 13]
Assassin_steps=[8, 8, 6, 6, 8, 18, 7, 6, 19]
#Font
count_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 80)
score_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 30)
#Text
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
s.blit(img, (x, y))
#Background
def draw_bg():
scaled_bg = pygame.transform.scale(bg_image, (swidth, sheight))
s.blit(scaled_bg, (0, 0))
#Health bars
red=(255, 0, 0)
yellow=(255, 255, 0)
white=(255, 255, 255)
def draw_health_bar(health, x, y):
ratio = health / 100
pygame.draw.rect(s, white, (x - 2, y - 2, 404, 34))
pygame.draw.rect(s, red, (x, y, 400, 30))
pygame.draw.rect(s, yellow, (x, y, 400 * ratio, 30))
#Player avatars
Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
Assassin=Characters(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)
#game loop
run = True
while run:
clock.tick(FPS)
#Display background
draw_bg()
#Display health bar
draw_health_bar(Knight.health, 20, 20)
draw_health_bar(Assassin.health, 580, 20)
draw_text("P1: " + str(score[0]), score_font, red, 20, 60)
draw_text("P2: " + str(score[1]), score_font, red, 580, 60)
#countdown
if intro_count <= 0:
#Move fighters
Knight.move(swidth, sheight, s, Assassin, round_over)
Assassin.move(swidth, sheight, s, Knight, round_over)
else:
#Display count timer
draw_text(str(intro_count), count_font, red, swidth/2, sheight/3)
#update count timer
if (pygame.time.get_ticks()-last_count_update)>= 1000:
intro_count -= 1
last_count_update = pygame.time.get_ticks()
#Update fighters
Knight.update()
Assassin.update()
#Display fighters
Knight.draw(s)
Assassin.draw(s)
#check for player defeat
if round_over == False:
if Knight.alive == False:
score[1] += 1
round_over = True
round_over_time = pygame.time.get_ticks()
elif Assassin.alive == False:
score[0] += 1
round_over = True
round_over_time = pygame.time.get_ticks()
else:
#display victory image
s.blit(victory_img, (360, 150))
if pygame.time.get_ticks()-round_over_time>round_cooldown:
round_over = False
intro_count = 3
Knight= Knight(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
Assassin= Assassin(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)
#event handler
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.update()
#exit pygame
pygame.quit()`
My Module-
import pygame
class Characters():
def __init__(self, player, x, y, flip, data, sheet, steps):
self.player = player
self.size = data[0]
self.image_scale = data[1]
self.offset = data[2]
self.flip = flip
self.animation_list = self.load_images(sheet, steps)
self.action = 0#0:idle #1:run #2:jump #3:attack1 #4: attack2 #5:hit #6:death
self.frame_index = 0
self.image = self.animation_list[self.action][self.frame_index]
self.update_time = pygame.time.get_ticks()
self.rect = pygame.Rect((x, y, 80, 180))
self.vel_y = 0
self.running = False
self.jump = False
self.attacking = False
self.attack_type = 0
self.attack_cooldown = 0
self.hit = False
self.health = 100
self.alive = True
def load_images(self, sheet, steps):
#extract images from spritesheet
animation_list = []
for y, animation in enumerate(steps):
temp_img_list = []
for x in range(animation):
temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
temp_img_list.append(pygame.transform.scale(temp_img, (self.size * self.image_scale, self.size * self.image_scale)))
animation_list.append(temp_img_list)
return animation_list
def move(self, screen_width, screen_height, surface, target, round_over):
SPEED = 10
GRAVITY = 2
dx = 0
dy = 0
self.running = False
self.attack_type = 0
#get keypresses
key = pygame.key.get_pressed()
#can only perform other actions if not currently attacking
if self.attacking == False and self.alive == True and round_over == False:
#check player 1 controls
if self.player == 1:
#movement
if key[pygame.K_a]:
dx = -SPEED
self.running = True
if key[pygame.K_d]:
dx = SPEED
self.running = True
#jump
if key[pygame.K_w] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack
if key[pygame.K_r] or key[pygame.K_t]:
self.attack(target)
#determine which attack type was used
if key[pygame.K_r]:
self.attack_type = 1
if key[pygame.K_t]:
self.attack_type = 2
#check player 2 controls
if self.player == 2:
#movement
if key[pygame.K_LEFT]:
dx = -SPEED
self.running = True
if key[pygame.K_RIGHT]:
dx = SPEED
self.running = True
#jump
if key[pygame.K_UP] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack
if key[pygame.K_KP1] or key[pygame.K_KP2]:
self.attack(target)
#determine which attack type was used
if key[pygame.K_KP1]:
self.attack_type = 1
if key[pygame.K_KP2]:
self.attack_type = 2
#apply gravity
self.vel_y += GRAVITY
dy += self.vel_y
#ensure player stays on screen
if self.rect.left + dx < 0:
dx = -self.rect.left
if self.rect.right + dx > screen_width:
dx = screen_width - self.rect.right
if self.rect.bottom + dy > screen_height - 220:
self.vel_y = 0
self.jump = False
dy = screen_height - 220 - self.rect.bottom
#ensure players face each other
if target.rect.centerx > self.rect.centerx:
self.flip = False
else:
self.flip = True
#apply attack cooldown
if self.attack_cooldown > 0:
self.attack_cooldown -= 1
#update player position
self.rect.x += dx
self.rect.y += dy
#handle animation updates
def update(self):
#check what action the player is performing
if self.health <= 0:
self.health = 0
self.alive = False
self.update_action(6)#6:death
elif self.hit == True:
self.update_action(5)#5:hit
elif self.attacking == True:
if self.attack_type == 1:
self.update_action(3)#3:attack1
elif self.attack_type == 2:
self.update_action(4)#4:attack2
elif self.jump == True:
self.update_action(2)#2:jump
elif self.running == True:
self.update_action(1)#1:run
else:
self.update_action(0)#0:idle
animation_cooldown = 50
#update image
self.image = self.animation_list[self.action][self.frame_index]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.frame_index += 1
self.update_time = pygame.time.get_ticks()
#check if the animation has finished
if self.frame_index >= len(self.animation_list[self.action]):
#if the player is dead then end the animation
if self.alive == False:
self.frame_index = len(self.animation_list[self.action]) - 1
else:
self.frame_index = 0
#check if an attack was executed
if self.action == 3 or self.action == 4:
self.attacking = False
self.attack_cooldown = 20
#check if damage was taken
if self.action == 5:
self.hit = False
#if the player was in the middle of an attack, then the attack is stopped
self.attacking = False
self.attack_cooldown = 20
def attack(self, target):
if self.attack_cooldown == 0:
#execute attack
self.attacking = True
self.attack_sound.play()
attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect):
target.health -= 10
target.hit = True
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))
The error occurred-
File "c:\Users\User\Desktop\G1\Battle game V1.py", line 73, in <module>
Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
File "c:\Users\User\Desktop\G1\Players.py", line 10, in __init__
self.animation_list = self.load_images(sheet, steps)
File "c:\Users\User\Desktop\G1\Players.py", line 33, in load_images
temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
ValueError: subsurface rectangle outside surface area
This question already exists:
in my shooting game, my players are positioned on top of my head (player) which is very abnormal and also once the game starts, they start shooting [closed]
Closed 6 months ago.
i have earlier tried to format my question properly but i hope this one is better. am creating a shooter game from https://github.com/russs123/Shooter. however my problem is that my enemies are stacked vertically upwards in a straight line when i run my game and the enemies don't start off at their predefined location. below is the full code i wrote. perhaps if you can run it, it will be better understood.
import pygame, sys
from player import Player
import os
import random
import csv
pygame.init()
screen_width = 600
scroll_thresh = 200
screen_scroll = 0
bg_scroll = 0
screen_height = int(screen_width * 0.8)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Shooter Game")
clock = pygame.time.Clock()
fps = 60
# define game variables
GRAVITY = 0.75
level = 1
ROWS = 16
COLS = 150
TILE_SIZE = screen_height // ROWS
TILE_TYPES = 21
img_list = []
for x in range(TILE_TYPES):
img = pygame.image.load(f"img/tile/{x}.png")
img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
img_list.append(img)
# color variables
bg = (144, 201, 120)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
# define player action variables
moving_left = False
moving_right = False
shoot = False
grenade = False
grenade_thrown = False
# load up images
pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
sky_cloud_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
bullet_img = pygame.image.load("img/icons/bullet.png").convert_alpha()
grenade_img = pygame.image.load("img/icons/grenade.png").convert_alpha()
# pick up boxes
health_box_img = pygame.image.load("img/icons/health_box.png").convert_alpha()
ammo_box_img = pygame.image.load("img/icons/ammo_box.png").convert_alpha()
grenade_box_img = pygame.image.load("img/icons/grenade_box.png").convert_alpha()
# name_dt = type(name)
item_boxes = {
"Health": health_box_img,
"Ammo": ammo_box_img,
"Grenade": grenade_img,
}
# define fonts
font = pygame.font.SysFont("Futura", 30)
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_bg():
screen.fill(bg)
screen.blit(sky_cloud_img, (0, 0))
screen.blit(mountain_img, (0, screen_height - mountain_img.get_height() - 300))
screen.blit(pine1_img, (0, screen_height - pine1_img.get_height() - 150))
screen.blit(pine2_img, (0, screen_height - pine2_img.get_height()))
class Soldier(pygame.sprite.Sprite):
def __init__(self, char_type, x, y, scale, speed, ammo, grenades):
pygame.sprite.Sprite.__init__(self)
self.alive = True
self.char_type = char_type
self.speed = speed
self.ammo = ammo
self.start_ammo = ammo
self.shoot_cooldown = 0
self.grenades = grenades
self.health = 100
self.max_health = self.health
self.direction = 1
self.vel_y = 0
self.jump = False
self.in_air = True
self.flip = False
self.animation_list = []
self.frame_index = 0
self.action = 0
# ai specific variables
self.move_counter = 0
self.vision = pygame.Rect(0, 0, 150, 20)
self.idling = 0
self.idling_counter = 0
self.update_time = pygame.time.get_ticks()
# load all images for the players
animation_types = ["idle", "run", "jump", "death"]
for animation in animation_types:
temp_list = []
# reset temporary list of images
# count number of files in the folder
num_of_frames = len(os.listdir(f"img/{self.char_type}/{animation}"))
for i in range(num_of_frames):
img = pygame.image.load(f"img/{self.char_type}/{animation}/{i}.png").convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.width = self.image.get_width()
self.height = self.image.get_height()
def update(self):
self.update_animation()
self.check_alive()
# update cooldown
if self.shoot_cooldown > 0:
self.shoot_cooldown -= 1
def move(self, moving_left, moving_right):
screen_scroll = 0
dx = 0
dy = 0
if moving_left:
dx = -self.speed
self.flip = True
self.direction = -1
if moving_right:
dx = self.speed
self.flip = False
self.direction = 1
if self.jump == True and self.in_air == False:
self.vel_y = -11
self.jump = False
self.in_air = True
# apply gravity
self.vel_y += GRAVITY
if self.vel_y > 10:
self.vel_y
dy += self.vel_y
# check for collision
for tile in world.obstacle_list:
# check collision in the x direction
if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
dx = 0
# check for collision in the y direction
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
# check if below the ground, i.e jumping
if self.vel_y < 0:
self.vel_y = 0
dy = tile[1].bottom - self.rect.top
# check if above the ground, ie falling
elif self.vel_y >= 0:
self.vel_y = 0
self.in_air = False
dy = tile[1].top - self.rect.bottom
self.rect.x += dx
self.rect.y += dy
# update scroll based on player position
if self.char_type == "player":
if self.rect.right > screen_width - scroll_thresh or self.rect.left < scroll_thresh:
self.rect.x = -dx
screen_scroll = -dx
return screen_scroll
def shoot(self):
if self.shoot_cooldown == 0 and self.ammo > 0:
self.shoot_cooldown = 20
bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery,
self.direction)
bullet_group.add(bullet)
# reduce ammo after each shot
self.ammo -= 1
def ai(self):
if self.alive and player.alive:
if self.idling == False and random.randint(1, 200) == 1:
self.update_action(0)
self.idling = True
self.idling_counter = 50
# check if ai is near the player
if self.vision.colliderect(player.rect):
# stop running and face the player
self.update_action(0)
self.shoot()
else:
if self.idling == False:
if self.direction == 1:
ai_moving_right = True
else:
ai_moving_right = False
ai_moving_left = not ai_moving_right
self.move(ai_moving_left, ai_moving_right)
self.update_action(1)
self.move_counter += 1
# update ai vision as the enemy moves
self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery)
if self.move_counter > TILE_SIZE:
self.direction *= -1
self.move_counter *= -1
else:
self.idling_counter -= 1
if self.idling_counter <= 0:
self.idling = False
# scroll
self.rect.x = screen_scroll
def update_animation(self):
ANIMATION_COOLDOWN = 100
# update image depending on current index
self.image = self.animation_list[self.action][self.frame_index]
# check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
# if animation has run out then restart to the first
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 3:
self.frame_index = len(self.animation_list[self.action]) - 1
else:
self.frame_index = 0
def check_alive(self):
if self.health <= 0:
self.health = 0
self.speed = 0
self.alive = False
self.update_action(3)
def update_action(self, new_action):
# check if new action is different from the previous one
if new_action != self.action:
self.action = new_action
# update animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def draw(self):
screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)
class HealthBar():
def __init__(self, health, x, y, max_health):
self.x = x
self.y = y
self.health = health
self.max_health = max_health
def draw(self, health):
self.health = health
pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24))
pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20))
ratio = self.health / self.max_health
pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20))
class World():
def __init__(self):
self.obstacle_list = []
def process_data(self, data):
for y, row in enumerate(data):
for x, tile in enumerate(row):
if tile >= 0:
img = img_list[tile]
img_rect = img.get_rect()
img_rect.x = x * TILE_SIZE
img_rect.y = y * TILE_SIZE
tile_data = (img, img_rect)
if 0 <= tile <= 8:
self.obstacle_list.append(tile_data)
elif 9 <= tile <= 10:
# water
water = Water(img, x * TILE_SIZE, y * TILE_SIZE, )
water_group.add(water)
pass
elif 11 <= tile <= 14:
# decoration
decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE)
decoration_group.add(decoration)
elif tile == 15:
# create a player
player = Soldier("player", x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 20, 5)
health_bar = HealthBar(10, 10, player.health, player.health)
elif tile == 16:
# create enemy
enemy = Soldier("enemy", x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 0)
enemy_group.add(enemy)
elif tile == 17:
# create ammo box
item_box = ItemBox("Ammo", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 18:
# create grenade box
item_box = ItemBox("Grenade", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 19:
# create health box
item_box = ItemBox("Health", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 20:
# create exit point
exit = Exit(img, x * TILE_SIZE, y * TILE_SIZE, )
exit_group.add(exit)
return player, health_bar
def draw(self):
for tile in self.obstacle_list:
tile[1][0] += screen_scroll
screen.blit(tile[0], tile[1])
class Decoration(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
self.rect.x += screen_scroll
class Water(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
self.rect.x += screen_scroll
class Exit(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
class ItemBox(pygame.sprite.Sprite):
def __init__(self, item_type, x, y):
pygame.sprite.Sprite.__init__(self)
self.item_type = item_type
self.image = item_boxes[self.item_type]
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
# scroll
self.rect.x += screen_scroll
# check if player has picked up the box
if pygame.sprite.collide_rect(self, player):
# check what kind of box it was
if self.item_type == "Health":
player.health += 25
if player.health > player.max_health:
player.health = player.max_health
elif self.item_type == "Ammo":
player.ammo += 15
elif self.item_type == "Grenade":
player.grenades += 3
# delete the item box
self.kill()
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 10
self.image = bullet_img
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = direction
def update(self):
self.rect.x += self.direction * self.speed
# check if bullet has left the screen
if self.rect.right < 0 and self.rect.left > screen_width:
self.kill()
# check for collision with level
for tile in world.obstacle_list:
if tile[1].colliderect(self.rect):
self.kill()
# check collision with characters
if pygame.sprite.spritecollide(player, bullet_group, False):
if player.alive:
player.health -= 5
self.kill()
for enemy in enemy_group:
if pygame.sprite.spritecollide(enemy, bullet_group, False):
if enemy.alive:
enemy.health -= 25
self.kill()
class Grenade(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.timer = 100
self.vel_y = -11
self.speed = 7
self.image = grenade_img
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = direction
self.width = self.image.get_width()
self.height = self.image.get_height()
def update(self):
self.vel_y += GRAVITY
dx = self.direction * self.speed
dy = self.vel_y
# check for collision with level
for tile in world.obstacle_list:
# check if grenade has hit a wall
if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
self.direction *= -1
dx = self.direction * self.speed
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
self.speed = 0
# check if below the ground, i.e thrown
if self.vel_y < 0:
self.y = 0
dy = tile[1].bottom - self.rect.top
# check if above the ground, ie falling
elif self.vel_y >= 0:
self.vel_y = 0
dy = tile[1].top - self.rect.bottom
# update grenade position
self.rect.x += dx
self.rect.y += dy
# explosion countdown
self.timer -= 1
if self.timer <= 0:
self.kill()
explosion = Explosion(self.rect.x, self.rect.y, 0.5)
explosion_group.add(explosion)
# do damage to anyone nearby
if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and \
abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2:
player.health -= 50
for enemy in enemy_group:
if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \
abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2:
enemy.health -= 50
class Explosion(pygame.sprite.Sprite):
def __init__(self, x, y, scale):
pygame.sprite.Sprite.__init__(self)
self.images = []
for num in range(1, 6):
img = pygame.image.load(f"img/explosion/exp{num}.png").convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width() * scale), (int(img.get_height() * scale))))
self.images.append(img)
self.frame_index = 0
self.image = self.images[self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.counter = 0
def update(self):
EXPLOSION_SPEED = 4
# update explosion animation
self.counter += 1
if self.counter >= EXPLOSION_SPEED:
self.counter = 0
self.frame_index += 1
# if animation is complete, then delete the explosion
if self.frame_index >= len(self.images):
self.kill()
else:
self.image = self.images[self.frame_index]
# create sprite groups
enemy_group = pygame.sprite.Group()
bullet_group = pygame.sprite.Group()
grenade_group = pygame.sprite.Group()
explosion_group = pygame.sprite.Group()
item_box_group = pygame.sprite.Group()
decoration_group = pygame.sprite.Group()
water_group = pygame.sprite.Group()
exit_group = pygame.sprite.Group()
# create empty tile list
world_data = []
for row in range(ROWS):
r = [-1] * COLS
world_data.append(r)
# load in level data and create world
with open(f"level{level}_data.csv", newline="") as csvfile:
reader = csv.reader(csvfile, delimiter=",")
for x, row in enumerate(reader):
for y, tile in enumerate(row):
world_data[x][y] = int(tile)
print(world_data)
world = World()
player, health_bar = world.process_data(world_data)
run = True
while run:
clock.tick(fps)
draw_bg()
world.draw()
health_bar.draw(player.health)
# show ammo
draw_text("AMMO:", font, WHITE, 10, 35)
for x in range(player.ammo):
screen.blit(bullet_img, (90 + (x * 10), 40))
# show grenade
draw_text("GRENADES: ", font, WHITE, 10, 65)
for x in range(player.grenades):
screen.blit(grenade_img, (130 + (x * 15), 66))
player.update()
player.draw()
for enemy in enemy_group:
enemy.ai()
enemy.update()
enemy.draw()
# update and draw groups
bullet_group.update()
grenade_group.update()
explosion_group.update()
item_box_group.update()
decoration_group.update()
water_group.update()
exit_group.update()
bullet_group.draw(screen)
grenade_group.draw(screen)
explosion_group.draw(screen)
item_box_group.draw(screen)
decoration_group.draw(screen)
water_group.draw(screen)
exit_group.draw(screen)
# update player actions, 1 is run, 0 = idle, 2 = in air
if player.alive:
# shoot bullet
if shoot:
player.shoot()
elif grenade and grenade_thrown == False and player.grenades > 0:
grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction),
player.rect.top, player.direction)
grenade_group.add(grenade)
# reduce grenades
player.grenades -= 1
grenade_thrown = True
if player.in_air:
player.update_action(2)
elif moving_left or moving_right:
player.update_action(1)
else:
player.update_action(0)
screen_scroll = player.move(moving_left, moving_right)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# keyboard presses
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
moving_left = True
if event.key == pygame.K_d:
moving_right = True
if event.key == pygame.K_SPACE:
shoot = True
if event.key == pygame.K_q:
grenade = True
if event.key == pygame.K_w and player.alive:
player.jump = True
if event.key == pygame.K_ESCAPE:
run = False
# keyboard button released
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
moving_left = False
if event.key == pygame.K_d:
moving_right = False
if event.key == pygame.K_SPACE:
shoot = False
if event.key == pygame.K_q:
grenade = False
grenade_thrown = False
pygame.display.update()
pygame.quit()
i have looked at this code for days but cant seem to find out the problem exactly. so, the question once again is how to properly arrange the enemies on the screen at their various predefined positions and not directly on top of my player.
I'm learning pygame and I have an issue with detection of rect's collisions. I'm using colliderect() function now but it works only when rects overlap, and the question is "How to detect even edges collisions?". General comments about the whole are welcome. First post btw.
Here is my code:
import pygame
#####SETTINGS#####
HEIGHT = 1080
WIDTH = 1920
BLOCK_SIZE = 60
##################
class Level():
def __init__(self, file):
self.file = file
self.blocks = []
self.map = self.load_from_file()
def load_from_file(self):
map = []
file = open(self.file + '.txt', 'r')
data = file.read()
file.close()
data = data.split('\n')
for x in data:
map.append(list(x))
return map
def render(self, screen):
self.blocks = []
y = 0
for row in self.map:
x = 0
for block in row:
if block != '0':
self.blocks.append(pygame.Rect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
if block == '1':
pygame.draw.rect(screen, (56,24,0), (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
elif block == '2':
pygame.draw.rect(screen, (18,115,81), (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
x += 1
y += 1
class Player():
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
self.rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
self.go_left = False
self.go_right = False
self.go_up = False
self.go_down = False
self.collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
def move(self):
self.collisions = test_collisions(self.rect, level.blocks)
if self.go_left and not self.collisions['left']:
self.x -= 10
self.go_left = False
if self.go_right and not self.collisions['right']:
self.x += 10
self.go_right = False
if self.go_up and not self.collisions['top']:
self.y -= 10
self.go_up = False
if self.go_down and not self.collisions['bottom']:
self.y += 10
self.go_down = False
self.rect = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
def render(screen, player, level):
screen.fill((49, 113, 181))
level.render(screen)
player.render(screen)
pygame.display.update()
def handle_events(player):
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
pygame.quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
player.go_left = True
if keys[pygame.K_d]:
player.go_right = True
if keys[pygame.K_w]:
player.go_up = True
if keys[pygame.K_s]:
player.go_down = True
def test_collisions(object, rects):
collisions = {'left' : False, 'right' : False, 'top' : False, 'bottom' : False}
for rect in rects:
if object.colliderect(rect):
if object.x <= rect.x:
collisions['right'] = True
if object.x >= rect.x:
collisions['left'] = True
if object.y >= rect.y:
collisions['top'] = True
if object.y <= rect.y:
collisions['bottom'] = True
return(collisions)
def main_loop():
clock = pygame.time.Clock()
while run:
clock.tick(60)
handle_events(player)
player.move()
render(screen, player, level)
if __name__ == "__main__":
run = True
screen = pygame.display.set_mode((WIDTH, HEIGHT))
level = Level('assets/level_one')
player = Player(0,0,(255,255,0))
main_loop()
And here is level_one.txt file content:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000002222222222200000
00000000000000000000000000000000
00000000000000000000000000000000
00022222222200000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
22222222222222222222222222222222
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
You can enlarge the rectangles for the collision detection with [pygame.Rect.inflate`](builtins.TypeError: argument 1 must be pygame.Surface, not function). e.g.:
if object.colliderect(rect.inflate(1, 1)):
# [...]
I would like to create a game in python but I need a thought provoking impulse, how I can draw a point, which draws a line behind him/a trail, so far, so good, I have no idea how to make my point not just moving in the 4 directions, I want him to move forward on his own and the user should steer left and right.
Missing is:
• The trail of my point (later I have to check, if another sprite touches it)
• The "curvy" moving
My current code:
import pygame
import os
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height / 2) - 4)
w_center = ((width / 2) - 4)
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load(os.path.join("assets", "point.png"))
self.x = (width / 2)
self.y = (height / 2)
self.speed = 5
self.direction = 3 # 1:north ; 2:east ; 3:south ; 4:west
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_DOWN]:
self.direction = 3
elif key[pygame.K_UP]:
self.direction = 1
if key[pygame.K_RIGHT]:
self.direction = 2
elif key[pygame.K_LEFT]:
self.direction = 4
def move(self):
if self.direction == 1:
self.y -= self.speed
if self.direction == 2:
self.x += self.speed
if self.direction == 3:
self.y += self.speed
if self.direction == 4:
self.x -= self.speed
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
def main():
point = Point()
clock = pygame.time.Clock()
background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((0, 0, 0))
screen.blit(background, (0, 0))
point.draw(screen)
pygame.display.update()
clock.tick(40)
if __name__ == '__main__':
main()
Please assist
Two things:
Use a list to store the trail. It's just a list of previous positions.
Use the arrow keys to adjust speed, not direction
I also added a few lines of code so the dot stays on the screen. The dot.png is just a black dot 20x20 pixels.
Here's the updated code:
import pygame
import os
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height/2) - 4)
w_center = ((width/2) - 4)
trail=[None]*50 # trail has 50 dots
trailimage = pygame.image.load('dot.png')
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('dot.png')
self.x = (width/2)
self.y = (height/2)
self.speed = {'x':0, 'y':0}
self.direction = 3 # 1north ; 2east ; 3south ; 4west
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_DOWN]:
self.speed['y']+=0.25
elif key[pygame.K_UP]:
self.speed['y']-=0.25
if key[pygame.K_RIGHT]:
self.speed['x']+=0.25
elif key[pygame.K_LEFT]:
self.speed['x']-=0.25
def move(self):
self.y += self.speed['y']
self.x += self.speed['x']
# wrap to other side of screen
if self.x > width: self.x = (self.x - width)
elif self.x < 0: self.x = (width + self.x)
if self.y > height: self.y = (self.y - height)
elif self.y < 0: self.y = (height + self.y)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
def main():
point = Point()
clock = pygame.time.Clock()
#background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((200, 200, 200))
#screen.blit(background, (0, 0))
for d in trail:
if d: screen.blit(trailimage, d)
del trail[0] # remove last point in trail
trail.append((point.x,point.y)) # append this position
point.draw(screen)
pygame.display.update()
clock.tick(40)
if __name__ == '__main__':
main()
To make the controls more like CurveFever, I updated the code so the left \ right keys adjust the travel direction (in degrees). The speed is constant.
Here is the updated code:
import pygame
import os
import math
pygame.init()
width, height = 970, 970
screen = pygame.display.set_mode((width, height))
h_center = ((height/2) - 4)
w_center = ((width/2) - 4)
trail=[None]*50 # trail has 50 dots
trailimage = pygame.image.load('dot.png')
speed = 8 # constant
class Point(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('dot.png')
self.x = (width/2)
self.y = (height/2)
self.speed = {'x':0, 'y':0}
self.deg = -90 # up, direction in degrees
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 1
if key[pygame.K_RIGHT]:
self.deg+=2
elif key[pygame.K_LEFT]:
self.deg-=2
self.speed['x'] = speed*math.cos(math.radians(self.deg))
self.speed['y'] = speed*math.sin(math.radians(self.deg))
def move(self):
self.y += self.speed['y']
self.x += self.speed['x']
# wrap to other side of screen
if self.x > width: self.x = (self.x - width)
elif self.x < 0: self.x = (width + self.x)
if self.y > height: self.y = (self.y - height)
elif self.y < 0: self.y = (height + self.y)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
TrailTrim = False # set True for constant trail length
def main():
point = Point()
clock = pygame.time.Clock()
#background = pygame.image.load('backgroundborder.png').convert()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
point.handle_keys()
point.move()
screen.fill((200, 200, 200)) # clear screen
#screen.blit(background, (0, 0))
for d in trail:
if d: screen.blit(trailimage, d)
if (TrailTrim): del trail[0] # delete trail end
trail.append((point.x,point.y)) # add current postiion
point.draw(screen) # draw current point
pygame.display.update()
clock.tick(40) # 40 FPS
if __name__ == '__main__':
main()
I'm trying to make my tanks shoot, and I did all the code I think I should have done but I don't know why the tanks aren't shooting anything.
import pygame, assetloader
from pygame.locals import *
import random, time, math
import pygame
GRAD = math.pi/180
blue = (0, 0, 255)
wallRects = []
bullets = []
maze = [[] for i in range(25)]
assetloader.set_asset_path("assets/")
I defined the Bullet Class here:
def calculate_dir_with_angle(angle):
direction = [0, 0]
if (angle > 0 and angle < 180) or (angle > -360 and angle < -180):
direction[0] = -1
elif (angle > -180 and angle < 0) or (angle > 180 and angle < 360):
direction[0] = 1
elif (angle > -90 and angle < 90) or (angle > 270 and anlge < 360):
direction[1] = -1
elif (angle > 90 and angle < 270) or (angle > -270 and angle < -90):
direction[1] = 1
return direction
class Bullet:
def __init__(self, pos, r, angle):
self.x = pos[0]
self.y = pos[1]
self.r = r
self.counter = 50
direction = calculate_dir_with_angle(angle)
self.vel = [direction[0] * 2, direction[1] * 2]
def draw(self, screen):
self.x = int(self.x)
self.y = int(self.y)
pygame.draw.circle(screen, (25, 25, 25), (self.x, self.y), (self.r))
def move(self):
self.x += self.vel[0]
self.y += self.vel[1]
self.rect = pygame.Rect(self.x-self.r, self.y - self.r, 2 * self.r, 2 * self.r)
for wr in wallRects:
if self.rect.centery >= wr.top and self.rect.centery <= wr.bottom:
if self.rect.left <= wr.right and self.rect.left > wr.left:
self.vel[0] = -self.vel[0]
self.x = wr.right + self.r + 1
self.rect.x = wr.right + 1
elif self.rect.right >= wr.left and self.rect.right < wr.right:
self.vel[0] = -self.vel[0]
self.x = wr.left + self.r - 1
self.rect.x = wr.left - 2 * self.r - 1
if self.rect.centerx >= wr.left and self.rect.centerx <= wr.right:
if self.rect.top <= wr.bottom and self.rect.top > wr.top:
self.vel[1] = -self.vel[1]
self.y = wr.bottom + self.r + 1
self.rect.y = wr.bottom + 1
elif self.rect.bottom >= wr.top and self.rect.bottom < wr.bottom:
self.vel[1] = -self.vel[1]
self.y = wr.top - self.r - 1
self.rect.y = wr.top - 2 * self.r - 1
if self.counter > 0:
self.counter -= 1
def generateRandomPosition():
row = random.randint(1, 23)
col = random.randint(1, 23)
while maze[row][col-1] != 0 or maze[row][col] != 0 or maze[row][col+1] != 0:
row = random.randint(1, 23)
col = random.randint(1, 23)
return row, col
Player 1:
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = assetloader.load_image("Tank.png", -1)
self.rect.x = x
self.rect.y = y
self.rect.clamp_ip(screen.get_rect())
self.rows = pos[0]
self.cols = pos[1]
self.x = self.cols * gsize
self.y = self.rows * gsize
self.orig_image, self.orig_rect = assetloader.load_image("Tank.png", -1)
self.orig_rect.x = self.x
self.orig_rect.y = self.y
self.orig_gun_pos = self.orig_rect.midtop
self.ammo = 5
def checkCollisions(self):
for b in bullets:
if b.counter <= 0:
if b.rect.colliderect(self.orig_rect):
self.dead = True
def calculate_gun_pos(self):
self.orig_gun_pos = self.orig_rect.midtop
new_y = self.orig_gun_pos[1] - self.orig_rect.centery
new_x = self.orig_gun_pos[0] - self.orig_rect.centerx
rads = self.dir * GRAD
gun_x = (new_y * math.sin(rads)) + (new_x * math.cos(rads)) + (self.orig_rect.centerx)
gun_y = (new_y * math.cos(rads)) - (new_x * math.sin(rads)) + (self.orig_rect.centery)
self.gun_pos = (gun_x, gun_y)
def shoot(self):
if self.ammo > 0:
self.calculate_gun_pos()
b = Bullet(self.gun_pos, 3, self.dir)
bullets.append(b)
self.ammo -= 1
def draw(self, screen):
image = pygame.transform.rotate(self.image, self.dir)
screen.blit(image, self.rect)
def update(self):
oldCenter = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = oldCenter
screen_rect = screen.get_rect()
keys = pygame.key.get_pressed()
if keys[K_m]:
p.shoot()
if not screen_rect.contains(self.rect):
self.rect.clamp_ip(screen_rect)
Calling the functions:
size = width, height = 500, 400
gsize = 25
start_x, start_y = 0, 0
bgColor = 255, 255, 255
pygame.init()
screen = pygame.display.set_mode(size)#, pygame.FULLSCREEN)
pygame.display.set_caption("Sample Sprite")
clock = pygame.time.Clock()
p = Player(width/2, height/4, (3,4))
coll_font = pygame.font.Font(None, 30)
going = True
while going:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
going = False
elif event.type == KEYDOWN:
if event.key == K_m:
p.shoot()
for b in bullets:
b.move()
p.update()
screen.fill(bgColor)
p.draw(screen)
pygame.display.flip()
pygame.quit()
How would I call the bullet to actually appear and fire because I have the Bullet class which gets called within the Player class in def shoot(self) so does anyone have an idea why the bullets aren't appearing?
I usually add bullets in this way: I pass the group that contains all sprites and the bullet group to the player instance and add new bullets to these group in the player's handle_event method.
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
screen_rect = screen.get_rect()
FONT = pg.font.Font(None, 24)
BG_COLOR = pg.Color('gray12')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(
BULLET_IMAGE, pg.Color('aquamarine1'), [(0, 0), (20, 5), (0, 11)])
PLAYER_IMAGE = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(
PLAYER_IMAGE, pg.Color('dodgerblue1'), [(0, 0), (50, 15), (0, 30)])
class Player(pg.sprite.Sprite):
def __init__(self, pos, all_sprites, bullet_group):
super().__init__()
self.image = PLAYER_IMAGE
self.orig_image = self.image # Needed to preserve image quality.
self.rect = self.image.get_rect(center=(pos))
self.pos = Vector2(pos)
self.vel = Vector2(1, 0)
self.angle = 0
self.angle_speed = 0
self.all_sprites = all_sprites
self.bullet_group = bullet_group
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullet_group.
if event.button == 1:
bullet = Bullet(self.pos, self.angle)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
elif event.type == pg.KEYDOWN:
# Rotate self by setting the .angle_speed.
if event.key in (pg.K_a, pg.K_LEFT):
self.angle_speed = -3
elif event.key in (pg.K_d, pg.K_RIGHT):
self.angle_speed = 3
elif event.type == pg.KEYUP:
if event.key in (pg.K_a, pg.K_LEFT):
self.angle_speed = 0
elif event.key in (pg.K_d, pg.K_RIGHT):
self.angle_speed = 0
def update(self):
self.pos += self.vel
self.rect.center = self.pos
if self.angle_speed != 0:
self.rotate()
def rotate(self):
# Update the angle and the velocity vector.
self.angle += self.angle_speed
self.vel.rotate_ip(self.angle_speed)
# Rotate the image and get a new rect with the previous center.
self.image = pg.transform.rotozoom(self.orig_image, -self.angle, 1)
self.rect = self.image.get_rect(center=self.rect.center)
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, angle):
super().__init__()
self.image = pg.transform.rotate(BULLET_IMAGE, -angle)
self.rect = self.image.get_rect(center=pos)
# To apply an offset (40 pixels) to the start position,
# create another vector and rotate it as well.
offset = Vector2(40, 0).rotate(angle)
# Add the offset vector to the position vector (the center).
self.pos = Vector2(pos) + offset
# Rotate the start velocity vector (9, 0) by the angle.
self.vel = Vector2(9, 0).rotate(angle)
def update(self):
# Add the velocity to the pos to move the sprite.
self.pos += self.vel
self.rect.center = self.pos # Update the rect as well.
# Remove bullets outside of the screen area.
if not screen_rect.contains(self.rect):
self.kill()
def main():
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
# Bullets will be added to this group.
bullet_group = pg.sprite.Group()
# Pass the bullet group to the player.
player = Player((300, 200), all_sprites, bullet_group)
all_sprites.add(player)
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
# Pass events to the player instance.
player.handle_event(event)
all_sprites.update()
screen.fill(BG_COLOR)
all_sprites.draw(screen)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()