Mask collisions doing funky stuff when i make the camera follow the player - python

I'm making a racing game with almost realistic cars in pygame. I made the cars succesfully and I want to make the car move a bit slower when it's not touching the drive way. For that, I want to use pygame mask collisions. Instead of using bounding boxes, they use the pixels and count them based on how many pixels of something are touching something. This all works correctly, but when I use the cam.follow_object(player) function, the game behaves very weird and there rise unexplainable bugs.
I included each individual picture and file, but I also included it on github because I know it's annoying to download all images one by one.
Assets:
app.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera, level
pygame.init()
# Basic Variables
screen_width = 1000
screen_height = 1000
fps = 80
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
cam = camera.Camera(1, screen)
#cam.zoom_game(1.55)
stage = "home"
# Loading Images
images = {
"start" : pygame.transform.scale(pygame.image.load("road_texture.png"), (200*cam.zoom, 75*cam.zoom)).convert_alpha(),
"bcg" : pygame.transform.scale(pygame.image.load("map2.png"), (1000*cam.zoom, 1000*cam.zoom)).convert_alpha(),
"car" : pygame.image.load("car.png").convert_alpha()
}
# Functionality Functions
def play_game():
global stage
stage = "game"
# Instansiating stuff
pos_save = pySave.Save_Manager("saved_info", "pos")
start_button = ui.Button((500,500), images["start"], play_game, text="startgame")
test_car = car.CarSprite(images["car"], 400, 400,[2, 2.3, 2.7], 0.013, rotations=360, camera=cam)
speedometer = ui.TextWithBackground((100,50), (100,950), images["start"])
level = level.Level(images["bcg"], test_car,cam,pos = (0,100))
# Groups and Lists
car_group = pygame.sprite.Group()
# Adding to Groups
car_group.add(test_car)
# Game Functions
def render():
if stage == "home":
screen.fill((255,255,255))
start_button.update("Start Game")
start_button.draw(screen)
elif stage == "game":
screen.fill((0,76,18))
level.update(screen)#screen.blit(images["bcg"], (0-cam.scroll[0],0-cam.scroll[1]))
car_group.update()
car_group.draw(screen)
#cam.follow(test_car, )
#speedometer.draw(screen)
speedometer.update(screen,round(test_car.speed*60,2) , " km/h")
#cam.zoom_game(2)
def collisions():
pass
run = True
while run:
clock.tick_busy_loop(80)
render()
collisions()
for event in pygame.event.get():
if event.type == QUIT:
run = False
pos_save.save("x", test_car.rect.x)
pos_save.save("y", test_car.rect.y)
pos_save.apply()
print(f"Quit with {round(clock.get_fps(), 2)} FPS")
quit()
pygame.display.update()
car.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, camera
class CarSprite( pygame.sprite.Sprite ):
def __init__( self, car_image, x, y, max_speed, accel,rot_speed=[1.8, 2.2, 3] ,rotations=360, camera="" ):
pygame.sprite.Sprite.__init__(self)
self.rotated_images = {}
self.min_angle = ( 360 / rotations )
for i in range( rotations ):
rotated_image = pygame.transform.rotozoom( pygame.transform.scale(car_image, (12*camera.zoom,25*camera.zoom)), 360-90-( i*self.min_angle ), 1 )
self.rotated_images[i*self.min_angle] = rotated_image
self.min_angle = math.radians( self.min_angle )
self.image = self.rotated_images[0]
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
self.reversing = False
self.heading = 0
self.speed = 0
self.velocity = pygame.math.Vector2( 0, 0 )
self.position = pygame.math.Vector2( x, y )
self.speed_hardening = 1
self.acc = False
self.steer_strenght_acc = rot_speed[0]
self.steer_strength_normal= rot_speed[1]
self.steer_strength_drift= rot_speed[2]
self.steer_strength = rot_speed[1]
self.drift_point = 0.00
self.accel = accel
self.max_speed = self.accel * 150
self.cam = camera
def turn( self, ori=1 ):
if self.speed > 0.1 or self.speed < 0. :
self.heading += math.radians( self.steer_strenght * ori )
image_index = int((self.heading + self.min_angle / 2) / self.min_angle) % len(self.rotated_images)
image = self.rotated_images[image_index]
if self.image is not image:
x,y = self.rect.center
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def accelerate( self):
self.speed += self.accel
def brake( self ):
if self.speed > 0:
self.speed -= self.accel * 3
if abs(self.speed) < 0.1:
self.speed = 0
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
def move(self):
keys = pygame.key.get_pressed()
if keys[K_w]:
self.accelerate()
if keys[K_s]:
self.brake()
if keys[K_a]:
self.turn(-1)
if keys[K_d]:
self.turn()
if keys[pygame.K_s] or keys [pygame.K_w]:
self.acc = True
else:
self.acc = False
def update( self ):
self.move()
self.speed_hardening = self.speed / 100
self.speed = round(self.speed, 3)
if self.acc:
self.steer_strenght = self.steer_strenght_acc
else:
self.steer_strenght = self.steer_strength_normal
if self.speed > self.max_speed and not pygame.key.get_pressed()[K_SPACE] and not self.drift_point > 0:
self.speed += self.accel / 4 - self.speed_hardening / 2
if self.speed > self.max_speed * 1.8:
self.speed = self.max_speed * 1.8
if self.speed < -self.max_speed / 4:
self.speed = -self.max_speed / 4
if not pygame.key.get_pressed()[K_SPACE]:
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
self.speed += self.drift_point
self.drift_point -= 0.0001
if self.drift_point < 0:
self.drift_point = 0
self.speed -= self.drift_point
else:
self.steer_strenght = self.steer_strength_drift
self.drift_point += 0.0001
if self.drift_point > self.accel / 1.5:
self.drift_point = self.accel / 1.5
if not self.acc and not self.speed < 0.04:
self.speed -= (self.accel / 2) + self.speed_hardening
if self.speed < 0.05:
self.speed = 0
self.position += self.velocity
self.rect.center = self.position
level.py
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera
screen_width = 1000
screen_height = 1000
class Level:
def __init__(self,image, car, camera, pos=(0,0)):
# Convert the images to a more suitable format for faster blitting
self.image = image.convert()
self.road = image
self.cam = camera
self.x,self.y = pos
self.bcg_mask = pygame.mask.from_surface(self.road)
self.car = car
self.get_car_mask()
def update(self, screen):
# Calculate the overlap between the car mask and the background mask
overlap = self.bcg_mask.overlap_mask(
self.car_mask,
(self.car.rect.x, self.car.rect.y)
)
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
def get_car_mask(self):
# Convert the car image to a more suitable format for faster blitting
carimg = self.car.image.convert()
carimg.set_colorkey((0,0,0))
self.carimg = carimg
self.car_mask = pygame.mask.from_surface(self.carimg)
camera.py:
import pygame
from pygame.locals import *
class Camera:
def __init__(self, speed, screen):
self.scroll = [5,5]
self.speed = speed
self.screen = screen
self.zoom = 1
def move_on_command(self):
keys = pygame.key.get_pressed()
if keys[K_UP]:
self.scroll[1] -= self.speed
if keys[K_DOWN]:
self.scroll[1] += self.speed
if keys[K_RIGHT]:
self.scroll[0] += self.speed
if keys[K_LEFT]:
self.scroll[0] -= self.speed
def follow(self, obj, speed=12):
#self.scroll[0], self.scroll[1] = obj.rect.x, obj.rect.y
if (obj.rect.x - self.scroll[0]) != self.screen.get_width()/2:
self.scroll[0] += ((obj.rect.x - (self.scroll[0] + self.screen.get_width()/2)))
if obj.rect.y - self.scroll[1] != self.screen.get_height()/2:
self.scroll[1] += ((obj.rect.y - (self.scroll[1] + self.screen.get_height()/2)))
def zoom_game(self, zoom):
self.zoom = zoom
ui.py :
import pygame
from pygame.locals import *
from random import choice, randint
class Button:
def __init__(self, pos, image, action, click_times=1, dissapear=True , text="", textcolor = (255,255,255), fontsize=30):
self.image = image
self.rect = self.image.get_rect(center=pos)
self.clicked = False
self.clicked_times = click_times
self.dissapear = dissapear
self.dont_draw = False
self.function = action
self.text= text
if not self.text == "":
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = textcolor
def update(self, var=""):
pressed = pygame.mouse.get_pressed()[0]
if not self.text == "":
self.text = self.font.render(f"{var}", 1, self.color)
self.text_rect = self.text.get_rect(center = self.rect.center)
if pressed and not self.clicked and not self.clicked_times <= 0 and self.rect.collidepoint(pygame.mouse.get_pos()):
self.function()
self.clicked = True
self.clicked_times -= 1
if not pressed:
self.clicked = False
if self.clicked_times <= 0:
if self.dissapear:
self.dont_draw = True
def draw(self, screen):
if not self.dont_draw:
screen.blit(self.image, (self.rect.x,self.rect.y))
screen.blit(self.text, (self.text_rect.x, self.text_rect.y))
class TextWithBackground:
def __init__(self, image_size, pos, image, fontsize=30, colour=(255, 255, 255)):
self.image = pygame.transform.scale(image, image_size)
self.rect = self.image.get_rect(center=pos)
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = colour
self.text = self.font.render("", 1, self.color)
self.other_text = None
self.other_rect = None
def update(self, screen, variable, othertext=""):
self.text = self.font.render(f"{variable}", 1, self.color)
self.text_rect = self.text.get_rect(center=self.rect.center)
if othertext:
self.other_text = self.font.render(f"{othertext}", 1, self.color)
self.other_rect = self.other_text.get_rect(topleft=(self.rect.center[0] - 5, self.rect.center[1] - 5))
screen.blit(self.image, (self.rect.x, self.rect.y))
screen.blit(self.text, (self.rect.x, self.rect.y))
if self.other_text:
screen.blit(self.other_text, (self.other_rect.x, self.other_rect.y))
Here you can find my images:
car.png
map2.png
road_texture.png

See PyGame collision with masks. You need to calculate the offset between the car and the map when you get the overlap area of the masks:
class Level:
# [...]
def update(self, screen):
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Calculate the overlap between the car mask and the background mask
offset = (self.car.rect.x - self.x, self.car.rect.y - self.y)
overlap = self.bcg_mask.overlap_mask(self.car_mask, offset)
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())

Related

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)

Enemy doesn't follow player (pygame) [duplicate]

This question already has answers here:
How to make smooth movement in pygame
(2 answers)
How can i make a block follow another block in pygame [duplicate]
(2 answers)
Closed 2 years ago.
So I followed the answers in another question asked on StackOverflow but it seems that I have missed something. I went ahead after reading the answer and copied the code and adjusted it to my variables and class names.
The following is the error code that Idle gives me:
Traceback (most recent call last):
File "D:\Programme (x86)\Python\Games\Zombie Game\Zombie Game_Test1.py", line 133, in <module>
Zombie.move_towards_Char(Char)
TypeError: move_towards_Char() missing 1 required positional argument: 'Char'
This is where I looked:
How to make an enemy follow the player in pygame?
import pygame
import turtle
import time
import math
import random
import sys
import os
pygame.init()
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
BGColor = (96,128,56)
ZColor = (225,0,0)
PColor = (0,0,255)
MOVE = 2.5
size = (1920, 1080)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zombie Game")
class Char(pygame.sprite.Sprite):
def __init__(self, color, pos, radius, width):
super().__init__()
self.image = pygame.Surface([radius*2, radius*2])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.circle(self.image, color, [radius, radius], radius, width)
self.rect = self.image.get_rect()
def moveRightP(self, pixels):
self.rect.x += pixels
pass
def moveLeftP(self, pixels):
self.rect.x -= pixels
pass
def moveUpP(self, pixels):
self.rect.y -= pixels
pass
def moveDownP(self, pixels):
self.rect.y += pixels
pass
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.rect.center = pos
def move_towards_Char(self2, Char):
dx, dy = self2.rect.x - Char.rect.x, self2.rect.y - Char.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self2.rect.x += dx * self2.speed
self2.rect.y += dy * self2.speed
def moveRightZ(self2, pixels):
self2.rect.x += pixels
pass
def moveLeftZ(self2, pixels):
self2.rect.x -= pixels
pass
def moveUpZ(self2, pixels):
self2.rect.y -= pixels
pass
def moveDownZ(self2, pixels):
self2.rect.y += pixels
pass
all_sprites_list = pygame.sprite.Group()
playerChar = Char(PColor, [0, 0], 15, 0)
playerChar.rect.x = 960
playerChar.rect.y = 505
all_sprites_list.add(playerChar)
carryOn = True
clock = pygame.time.Clock()
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = pygame.time.get_ticks() + 10000
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = 10000
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
for zombie in zombie_list:
screen.blit(zombie.image,zombie.rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
The major issue is, that you do the zombie movement calculations with integral data types. If the movement of a zombie is 1 pixel and the movement is diagonal, then the x and y component of the movement is < 1. Using an integral data type, this may results in 0 movement, because of truncating to int. Note the members of pygame.Rect are integral values.
You've to switch to floating point values to solve the issue. Use pygame.math.Vector2 to do the calculations.
Add a member pos of type Vector2 to the class Zombie which stores the floating point position of the zombie:
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.speed = 1
self2.pos = pygame.Vector2(pos[0], pos[1])
# [...]
Add a new method draw to the class Zombie, which draws (blit) a zombie at the position pos:
class Zombie(pygame.sprite.Sprite):
# [...]
def draw(self2):
self2.rect.center = (int(round(self2.pos.x)), int(round(self2.pos.y)))
screen.blit(self2.image, self2.rect)
Do the calculation of the movement of the zombie based on Vector2. Ensure that the distance between the player and the zombie is greater than 0 and that the zombie does not step over of the position of the player (min(len, self2.speed)):
class Zombie(pygame.sprite.Sprite):
# [...]
def move_towards_Char(self2, Char):
deltaVec = pygame.Vector2(Char.rect.center) - self2.pos
len = deltaVec.length()
if len > 0:
self2.pos += deltaVec/len * min(len, self2.speed)
Call the methods move_towards_Char and draw for each zombie, in the main loop of the application:
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
# update all the positions of the zombies
for zombie in zombie_list:
zombie.move_towards_Char(playerChar)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
# draw all the zombies
for zombie in zombie_list:
zombie.draw()
pygame.display.flip()
clock.tick(60)
L{Zombie.move_towards_Char} is a self method. You need to create object of Zombie class passing the required args mentioned in L{Zombie.init}.
Something like below:
zm = Zombie(color=<color>, pos=<pos>, radius=<radius>, width=<width>)
zm.move_towards_Char(Char)

Python/ Pygame : 2d angular momentum / inertia

my first ever Python program has hit a block I don't think I have the knowledge to solve myself.
It's a controllable spaceship on a 2d surface, I want to add momentum / inertia
I have it so the ship keeps travelling on the vector it previously was, when the engine is stopped.
However I can only get it to 'snap' to the new vector it rotates to face instantly.
What I want to happen is that inertia vector slowly aligns with the new pointing vector as it accelerates- like rotational acceleration? ( I'm not too hot on the math ) - I can rotate the inertia vector , but I would need to compare it somehow with the new pointing vector , and modify it based upon their difference?
if anyone could advise as to how I might start to approach this, that would be great - I suspect I coming at this from completely the wrong way.
Heres some of the code ( be gentle please!)
the sprite used is this : - ship.png
import pygame
import sys
from math import sin, cos, pi, atan2
from pygame.locals import *
import random
from random import randint
from pygame.math import Vector2
import operator
"""solar system generator"""
"""set screen size and center and some global namespace colors for ease of use"""
globalalpha = 255
screenx = int(1200)
screeny = int(700)
centerx = int(screenx / 2)
centery = int(screeny / 2)
center = (centerx, centery)
black = ( 0, 0, 0)
white = (255, 255, 255)
red = (209, 2, 22)
TRANSPARENT = (255,0,255)
numstars = 150
DISPLAYSURF = pygame.display.set_mode((screenx, screeny), 0, 32)
clock = pygame.time.Clock()
globaltimefactor = 1
shipimage = pygame.image.load('ship.png').convert()
DISPLAYSURF.fill(black)
screen_rect = DISPLAYSURF.get_rect()
class Playership(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.imageorig = pygame.image.load('ship.png').convert_alpha()
self.startpos = (screen_rect.center)
self.image = self.imageorig.copy()
self.rect = self.imageorig.get_rect(center=self.startpos)
self.angle = 0
self.currentposx = 600
self.currentposy = 350
self.tuplepos = (self.currentposx, self.currentposy)
self.speed = 1
self.rotatespeed = 1.5
self.initialvec = (600, 0)
self.destination = 0
self.anglechange = 0
self.currentspeed = 0
self.maxspeed = 5
self.engineon = False
self.newvec = (600, 0)
self.newdestination = 0
self.acceleration = 0.015
self.inertiaspeed = 0
self.transitionalvec = self.initialvec
def get_angleafterstopping(self):
newvec = self.initialvec
self.newvec = newvec
def get_destinationafterstopping(self):
x_dist = self.newvec[0] - self.tuplepos[0]
y_dist = self.newvec[1] - self.tuplepos[1]
self.newdestination = atan2(-y_dist, x_dist) % (2 * pi)
def get_destination(self):
x_dist = self.initialvec[0] - self.tuplepos[0]
y_dist = self.initialvec[1] - self.tuplepos[1]
self.destination = atan2(-y_dist, x_dist) % (2 * pi)
def moveship(self):
if self.engineon is True:
self.currentspeed = self.currentspeed + self.acceleration
if self.currentspeed > self.maxspeed:
self.currentspeed = self.maxspeed
elif self.currentspeed < 0:
self.currentspeed = 0
self.inertiaspeed = self.currentspeed
elif self.engineon is False:
self.currentposx = self.currentposx + (cos(self.newdestination) * self.inertiaspeed * globaltimefactor)
self.currentposy = self.currentposy - (sin(self.newdestination) * self.inertiaspeed * globaltimefactor)
self.tuplepos = (self.currentposx, self.currentposy)
self.rect.center = self.tuplepos
return
self.get_destination()
self.currentposx = self.currentposx + (cos(self.destination) * self.currentspeed * globaltimefactor)
self.currentposy = self.currentposy - (sin(self.destination) * self.currentspeed * globaltimefactor)
self.tuplepos = (self.currentposx, self.currentposy)
self.rect.center = self.tuplepos
def rotateship(self, rotation):
self.anglechange = self.anglechange - (rotation * self.rotatespeed * globaltimefactor)
self.angle += (rotation * self.rotatespeed * globaltimefactor)
self.image = pygame.transform.rotate(self.imageorig, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
initialvec = self.tuplepos + Vector2(0, -600).rotate(self.anglechange * globaltimefactor)
initialvec = int(initialvec.x), int(initialvec.y)
self.initialvec = initialvec
myship = Playership()
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(myship)
firsttimedone = False
def main():
done = False
while not done:
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
myship.rotateship(1)
if keys_pressed[pygame.K_RIGHT]:
myship.rotateship(-1)
if keys_pressed[pygame.K_UP]:
myship.engineon = True
myship.moveship()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
myship.engineon = False
myship.currentspeed = 0
myship.get_angleafterstopping()
myship.get_destinationafterstopping()
DISPLAYSURF.fill(black)
all_sprites_list.update()
all_sprites_list.draw(DISPLAYSURF)
pygame.draw.line(DISPLAYSURF, white, (myship.tuplepos), (myship.initialvec))
pygame.draw.line(DISPLAYSURF, red, (myship.tuplepos), (myship.newvec))
pygame.display.flip()
if myship.engineon is False:
myship.moveship()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
if __name__ == '__main__':
pygame.init()
main()
pygame.quit(); sys.exit();
EDIT :
I fixed it : just required a better understanding of vectors
ship starts off with acceleration and velocity both stated as vectors.
self.position = vec(screenx / 2, screeny / 2)
self.vel = vec(0, 0)
self.acceleration = vec(0, -0.2) # The acceleration vec points upwards from the starting ship position
rotating the ship rotates that vector in place
self.acceleration.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.imageorig, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
accelerating is this :
self.vel += self.acceleration * self.enginepower * globaltimefactor
updating position :
self.position += self.vel
self.rect.center = self.position
I was making it harder than it needed to be, velocity needed to be constant until acted upon by the rotated acceleration vector. I didn't know how to add vectors together etc.
I fixed it : just required a better understanding of vectors
ship starts off with acceleration and velocity both stated as vectors.
self.position = vec(screenx / 2, screeny / 2)
self.vel = vec(0, 0)
self.acceleration = vec(0, -0.2) # The acceleration vec points upwards from the starting ship position
rotating the ship rotates that vector in place
self.acceleration.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.imageorig, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
accelerating is this :
self.vel += self.acceleration * self.enginepower * globaltimefactor
updating position :
self.position += self.vel
self.rect.center = self.position
I was making it harder than it needed to be, velocity needed to be constant until acted upon by the rotated acceleration vector. I didn't know how to add vectors together etc.

Python game keys are not responding

I am trying to implement a Python game (aliens.py from the PyGame package) but when I run it, I can't move my player or do any shooting...
I run it on Mac OS X, python3 and have a french keyboard. Could this have anything to do the fact that it does not take any of my keyboard commands?
On the screen of my terminal I see:
^[ (when I press esc),
^[[D (when I press the left arrow),
^[[C (when I press the right arrow),
^[[A (when I press the upwards arrow),
^[[B (when I press the downwards arrow
...
Is this normal? It does not help replacing K_RIGHT with ^[[C.
#!/usr/bin/env python
import random, os.path
#import basic pygame modules
import pygame
from pygame.locals import *
#see if we can load more than standard BMP
if not pygame.image.get_extended():
raise SystemExit("Sorry, extended image module required")
#game constants
MAX_SHOTS = 2 #most player bullets onscreen
ALIEN_ODDS = 22 #ances a new alien appears
BOMB_ODDS = 60 #chances a new bomb will drop
ALIEN_RELOAD = 12 #frames between new aliens
SCREENRECT = Rect(0, 0, 940, 480)
SCORE = 0
main_dir = os.path.split(os.path.abspath(__file__))[0]
def load_image(file):
"loads an image, prepares it for play"
file = os.path.join(main_dir, 'data', file)
try:
surface = pygame.image.load(file)
except pygame.error:
raise SystemExit('Could not load image "%s" %s'%(file, pygame.get_error()))
return surface.convert()
def load_images(*files):
imgs = []
for file in files:
imgs.append(load_image(file))
return imgs
class dummysound:
def play(self): pass
def load_sound(file):
if not pygame.mixer: return dummysound()
file = os.path.join(main_dir, 'data', file)
try:
sound = pygame.mixer.Sound(file)
return sound
except pygame.error:
print ('Warning, unable to load, %s' % file)
return dummysound()
# each type of game object gets an init and an
# update function. the update function is called
# once per frame, and it is when each object should
# change it's current position and state. the Player
# object actually gets a "move" function instead of
# update, since it is passed extra information about
# the keyboard
class Player(pygame.sprite.Sprite):
speed = 10
bounce = 24
gun_offset = -11
images = []
def __init__(self):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
self.reloading = 0
self.origtop = self.rect.top
self.facing = -1
def move(self, direction):
if direction: self.facing = direction
self.rect.move_ip(direction*self.speed, 0)
self.rect = self.rect.clamp(SCREENRECT)
if direction < 0:
self.image = self.images[0]
elif direction > 0:
self.image = self.images[1]
self.rect.top = self.origtop - (self.rect.left//self.bounce%2)
def gunpos(self):
pos = self.facing*self.gun_offset + self.rect.centerx
return pos, self.rect.top
class Alien(pygame.sprite.Sprite):
speed = 13
animcycle = 12
images = []
def __init__(self):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.facing = random.choice((-1,1)) * Alien.speed
self.frame = 0
if self.facing < 0:
self.rect.right = SCREENRECT.right
def update(self):
self.rect.move_ip(self.facing, 0)
if not SCREENRECT.contains(self.rect):
self.facing = -self.facing;
self.rect.top = self.rect.bottom + 1
self.rect = self.rect.clamp(SCREENRECT)
self.frame = self.frame + 1
self.image = self.images[self.frame//self.animcycle%3]
class Explosion(pygame.sprite.Sprite):
defaultlife = 12
animcycle = 3
images = []
def __init__(self, actor):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(center=actor.rect.center)
self.life = self.defaultlife
def update(self):
self.life = self.life - 1
self.image = self.images[self.life//self.animcycle%2]
if self.life <= 0: self.kill()
class Shot(pygame.sprite.Sprite):
speed = -11
images = []
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=pos)
def update(self):
self.rect.move_ip(0, self.speed)
if self.rect.top <= 0:
self.kill()
class Bomb(pygame.sprite.Sprite):
speed = 9
images = []
def __init__(self, alien):
pygame.sprite.Sprite.__init__(self, self.containers)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=
alien.rect.move(0,5).midbottom)
def update(self):
self.rect.move_ip(0, self.speed)
if self.rect.bottom >= 470:
Explosion(self)
self.kill()
class Score(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.font = pygame.font.Font(None, 20)
self.font.set_italic(1)
self.color = Color('white')
self.lastscore = -1
self.update()
self.rect = self.image.get_rect().move(10, 450)
def update(self):
if SCORE != self.lastscore:
self.lastscore = SCORE
msg = "Score: %d" % SCORE
self.image = self.font.render(msg, 0, self.color)
def main(winstyle = 0):
# Initialize pygame
pygame.init()
if pygame.mixer and not pygame.mixer.get_init():
print ('Warning, no sound')
pygame.mixer = None
# Set the display mode
winstyle = 0 # |FULLSCREEN
bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32)
screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth)
#Load images, assign to sprite classes
#(do this before the classes are used, after screen setup)
img = load_image('player1.gif')
Player.images = [img, pygame.transform.flip(img, 1, 0)]
img = load_image('explosion1.gif')
Explosion.images = [img, pygame.transform.flip(img, 1, 1)]
Alien.images = load_images('alien1.gif', 'alien2.gif', 'alien3.gif')
Bomb.images = [load_image('bomb.gif')]
Shot.images = [load_image('shot.gif')]
#decorate the game window
icon = pygame.transform.scale(Alien.images[0], (32, 32))
pygame.display.set_icon(icon)
pygame.display.set_caption('Pygame Aliens')
pygame.mouse.set_visible(0)
#create the background, tile the bgd image
bgdtile = load_image('background.gif')
background = pygame.Surface(SCREENRECT.size)
for x in range(0, SCREENRECT.width, bgdtile.get_width()):
background.blit(bgdtile, (x, 0))
screen.blit(background, (0,0))
pygame.display.flip()
#load the sound effects
boom_sound = load_sound('boom.wav')
shoot_sound = load_sound('car_door.wav')
if pygame.mixer:
music = os.path.join(main_dir, 'data', 'house_lo.wav')
pygame.mixer.music.load(music)
pygame.mixer.music.play(-1)
# Initialize Game Groups
aliens = pygame.sprite.Group()
shots = pygame.sprite.Group()
bombs = pygame.sprite.Group()
all = pygame.sprite.RenderUpdates()
lastalien = pygame.sprite.GroupSingle()
#assign default groups to each sprite class
Player.containers = all
Alien.containers = aliens, all, lastalien
Shot.containers = shots, all
Bomb.containers = bombs, all
Explosion.containers = all
Score.containers = all
#Create Some Starting Values
global score
alienreload = ALIEN_RELOAD
kills = 0
clock = pygame.time.Clock()
#initialize our starting sprites
global SCORE
player = Player()
Alien() #note, this 'lives' because it goes into a sprite group
if pygame.font:
all.add(Score())
while player.alive():
#get input
for event in pygame.event.get():
if event.type == QUIT or \
(event.type == KEYDOWN and event.key == K_ESCAPE):
return
keystate = pygame.key.get_pressed()
# clear/erase the last drawn sprites
all.clear(screen, background)
#update all the sprites
all.update()
#handle player input
direction = keystate[K_RIGHT] - keystate[K_LEFT]
player.move(direction)
firing = keystate[K_SPACE]
if not player.reloading and firing and len(shots) < MAX_SHOTS:
Shot(player.gunpos())
shoot_sound.play()
player.reloading = firing
# Create new alien
if alienreload:
alienreload = alienreload - 1
elif not int(random.random() * ALIEN_ODDS):
Alien()
alienreload = ALIEN_RELOAD
# Drop bombs
if lastalien and not int(random.random() * BOMB_ODDS):
Bomb(lastalien.sprite)
# Detect collisions
for alien in pygame.sprite.spritecollide(player, aliens, 1):
boom_sound.play()
Explosion(alien)
Explosion(player)
SCORE = SCORE + 1
player.kill()
for alien in pygame.sprite.groupcollide(shots, aliens, 1, 1).keys():
boom_sound.play()
Explosion(alien)
SCORE = SCORE + 1
for bomb in pygame.sprite.spritecollide(player, bombs, 1):
boom_sound.play()
Explosion(player)
Explosion(bomb)
player.kill()
#draw the scene
dirty = all.draw(screen)
pygame.display.update(dirty)
#cap the framerate
clock.tick(40)
if pygame.mixer:
pygame.mixer.music.fadeout(1000)
pygame.time.wait(1000)
pygame.quit()
#call the "main" function if running this script
if __name__ == '__main__': main()
Event KEYDOWN sends values unicode, key, mod and you can display it to see what codes (numeric values) uses your keyboard.
if event.type == KEYDOWN;
print('key:', event.key)
print('unicode:', event.uniconde)
print('mod:', event.mod)
And then you can use them to test your keys and move objects.
It is surely due to keyboard keys problem , just go to this documentation and find out proper keyword for your respective keyboard button:
"https://www.pygame.org/docs/ref/key.html"

Python Game problems

I'm working on a 2D python game project for my CS class, and I've hit a bump, I don't know what the problem is:
The project is a large part of my grade, and up until now I've had an A+
This project is incredibly frustrating
NEW
ok so i've got everything working so far, except for some reason My protaganist() is stuck at the top left corner of the game screen !
Also, i need ideas on how to create a jump action
If anyone could help I would be incredibly grateful!
I am importing a game engine my teacher made available from his book website, but i it is too long for me to add but i will try to add some of it at the bottom
Here is all my code:
import gameEngine
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
sndAtk = pygame.mixer.Sound("OOT_AdultLink_Attack1.wav")
#goal is to create a game
#must have menu to start game
#menu should have a start and quit button.. start runs gaming operations and quit exits program
#sprites for character and enemies and bullets maybe, use one large image and simply move visibiliy
#this saves memory as 1 image is loaded instead of many
"""
class game(gameEngine.scene):
def __init__(self, scene):
self.background()
self.sprites["spawn.gif", "badguys.gif"]
"""
"""
protaganist is our hero sprite
should run left and right, jump left and right
and attack left and right...
I might add in the bow and jump attack
"""
class scrollinggrass(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("gamebackground.jpg")
self.rect.centerx = 20
self.rect.centery = 500
self.rect = self.image.get_rect()
self.dx = 10
self.dy = 0
self.checkKeys()
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
print("working")
self.forward(3)
run.play()
if keys[pygame.K_LEFT]:
self.forward(-3)
class hearts(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("heart.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.setPosition((550 , 30))
class badguy(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("badguy1.png")
self.rect = self.imageMaster.get_rect()
self.health = 2
self.DEAD = 1
self.state = 0
class protaganist(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.imageList = []
self.rect = self.imageMaster.get_rect()
self.STANDING = 0
self.RUNNING = 1
self.ATTACKING = 2
self.JUMPING = 3
self.DEAD = 10
self.imageFrame = 0
self.state = self.STANDING
self.hearts = 1
self.heartPts = self.hearts * 3
self.stats()
self.loadImages()
# self.image = self.imageList[0]
self.checkKeys()
def stats(self):
#sets it up so each heart is essentially 3 hit points
if self.heartPts >= 3:
self.hearts = 1
elif self.heartPts >= 6:
self.hearts = 2
elif self.heartPts == 9:
self.hearts = 3
elif self.heartPts > 9:
self.heartPts = 9
# changes state to dead if hp == 0
if self.heartPts == 0:
self.state = DEAD
def loadImages(self):
self.setPosition((320 , 380))
self.setImage("heroSTANDING.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.state = runRight
self.frame += 1
if self.frame >= len(self.imageList):
self.frame = 1
self.image = self.imageList[self.frame]
# self.image = self.image.get_rect()
# self.rect.center = (320, 240)
if keys[pygame.K_LEFT]:
self.state = 1
while keys[pygame.K_g]:
self.state = Attacking
sndAtk.play()
if self.state == self.DEAD:
self.image = self.deadImgList[0]
self.frame += 1
self.image = self.deadImgList[self.frame]
#self.image = self.image.get_rect()
#self.rect.center = (320, 240)
class game(gameEngine.Scene):
def __init__ (self):
gameEngine.Scene.__init__(self)
pygame.display.set_caption("Link's Mediocre Adventure")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pro = protaganist(self)
baddy = badguy(self)
baddy1 = badguy(self)
heart = hearts(self)
grass = scrollinggrass(self)
goodlySprites = self.makeSpriteGroup((grass, pro, heart))
baddySprites = self.makeSpriteGroup((baddy, baddy1))
# self.addSpriteGroup(goodlySprites)
self.addGroup((baddySprites))
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
baddy1.health -= 1
if baddy.health == 0:
baddy.reset()
elif baddy1.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collideGroup(baddySprites):
pro.heartPts -= 1
goodlySprites.update()
baddySprites.update()
goodlySprites.draw(screen)
baddySprites.draw(screen)
pygame.display.flip()
def main():
game.start()
if __name__ == "__main__":
game()
game engine
class SuperSprite(pygame.sprite.Sprite):
""" An enhanced Sprite class
expects a gameEngine.Scene class as its one parameter
Use methods to change image, direction, speed
Will automatically travel in direction and speed indicated
Automatically rotates to point in indicated direction
Five kinds of boundary collision
"""
def __init__(self, scene):
pygame.sprite.Sprite.__init__(self)
self.scene = scene
self.screen = scene.screen
#create constants
self.WRAP = 0
self.BOUNCE = 1
self.STOP = 2
self.HIDE = 3
self.CONTINUE = 4
#create a default text image as a placeholder
#This will usually be changed by a setImage call
self.font = pygame.font.Font("freesansbold.ttf", 30)
self.imageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF))
self.image = self.imageMaster
self.rect = self.image.get_rect()
#create properties
#most will be changed through method calls
self.x = 200
self.y = 200
self.dx = 0
self.dy = 0
self.dir = 0
self.rotation = 0
self.speed = 0
self.maxSpeed = 10
self.minSpeed = -3
self.boundAction = self.WRAP
self.pressed = False
self.oldCenter = (100, 100)
self.states = {}
self.currentState = "default"
def update(self):
self.oldCenter = self.rect.center
self.checkEvents()
self.__rotate()
self.__calcVector()
self.__calcPosition()
self.checkBounds()
self.rect.center = (self.x, self.y)
def checkEvents(self):
""" overwrite this method to add your own event code """
pass
def __rotate(self):
""" PRIVATE METHOD
change visual orientation based on
rotation property.
automatically called in update.
change rotation property directly or with
rotateBy(), setAngle() methods
"""
oldCenter = self.rect.center
self.oldCenter = oldCenter
self.image = pygame.transform.rotate(self.imageMaster, self.rotation)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def __calcVector(self):
""" calculates dx and dy based on speed, dir
automatically called in update
"""
theta = self.dir / 180.0 * math.pi
self.dx = math.cos(theta) * self.speed
self.dy = math.sin(theta) * self.speed
self.dy *= -1
def __calcPosition(self):
""" calculates the sprites position adding
dx and dy to x and y.
automatically called in update
"""
self.x += self.dx
self.y += self.dy
def checkBounds(self):
""" checks boundary and acts based on
self.BoundAction.
WRAP: wrap around screen (default)
BOUNCE: bounce off screen
STOP: stop at edge of screen
HIDE: move off stage and wait
CONTINUE: keep going at present course and speed
automatically called by update
"""
scrWidth = self.screen.get_width()
scrHeight = self.screen.get_height()
#create variables to simplify checking
offRight = offLeft = offTop = offBottom = offScreen = False
if self.x > scrWidth:
offRight = True
if self.x < 0:
offLeft = True
if self.y > scrHeight:
offBottom = True
if self.y < 0:
offTop = True
if offRight or offLeft or offTop or offBottom:
offScreen = True
if self.boundAction == self.WRAP:
if offRight:
self.x = 0
if offLeft:
self.x = scrWidth
if offBottom:
self.y = 0
if offTop:
self.y = scrHeight
elif self.boundAction == self.BOUNCE:
if offLeft or offRight:
self.dx *= -1
if offTop or offBottom:
self.dy *= -1
self.updateVector()
self.rotation = self.dir
elif self.boundAction == self.STOP:
if offScreen:
self.speed = 0
elif self.boundAction == self.HIDE:
if offScreen:
self.speed = 0
self.setPosition((-1000, -1000))
elif self.boundAction == self.CONTINUE:
pass
else:
# assume it's continue - keep going forever
pass
def setSpeed(self, speed):
""" immediately sets the objects speed to the
given value.
"""
self.speed = speed
def speedUp(self, amount):
""" changes speed by the given amount
Use a negative value to slow down
"""
self.speed += amount
if self.speed < self.minSpeed:
self.speed = self.minSpeed
if self.speed > self.maxSpeed:
self.speed = self.maxSpeed
def setAngle(self, dir):
""" sets both the direction of motion
and visual rotation to the given angle
If you want to set one or the other,
set them directly. Angle measured in degrees
"""
self.dir = dir
self.rotation = dir
def turnBy (self, amt):
""" turn by given number of degrees. Changes
both motion and visual rotation. Positive is
counter-clockwise, negative is clockwise
"""
self.dir += amt
if self.dir > 360:
self.dir = amt
if self.dir < 0:
self.dir = 360 - amt
self.rotation = self.dir
def rotateBy(self, amt):
""" change visual orientation by given
number of degrees. Does not change direction
of travel.
"""
self.rotation += amt
if self.rotation > 360:
self.rotation = amt
if self.rotation < 0:
self.rotation = 360 - amt
def setImage (self, image):
""" loads the given file name as the master image
default setting should be facing east. Image
will be rotated automatically """
self.imageMaster = pygame.image.load(image)
self.imageMaster = self.imageMaster.convert()
def setDX(self, dx):
""" changes dx value and updates vector """
self.dx = dx
self.updateVector()
def addDX(self, amt):
""" adds amt to dx, updates vector """
self.dx += amt
self.updateVector()
def setDY(self, dy):
""" changes dy value and updates vector """
self.dy = dy
self.updateVector()
def addDY(self, amt):
""" adds amt to dy and updates vector """
self.dy += amt
self.updateVector()
def setComponents(self, components):
""" expects (dx, dy) for components
change speed and angle according to dx, dy values """
(self.dx, self.dy) = components
self.updateVector()
def setBoundAction (self, action):
""" sets action for boundary. Values are
self.WRAP (wrap around edge - default)
self.BOUNCE (bounce off screen changing direction)
self.STOP (stop at edge of screen)
self.HIDE (move off-stage and stop)
self.CONTINUE (move on forever)
Any other value allows the sprite to move on forever
"""
self.boundAction = action
def setPosition (self, position):
""" place the sprite directly at the given position
expects an (x, y) tuple
"""
(self.x, self.y) = position
def moveBy (self, vector):
""" move the sprite by the (dx, dy) values in vector
automatically calls checkBounds. Doesn't change
speed or angle settings.
"""
(dx, dy) = vector
self.x += dx
self.y += dy
self.checkBounds()
def forward(self, amt):
""" move amt pixels in the current direction
of travel
"""
#calculate dx dy based on current direction
radians = self.dir * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.x += dx
self.y += dy
def addForce(self, amt, angle):
""" apply amt of thrust in angle.
change speed and dir accordingly
add a force straight down to simulate gravity
in rotation direction to simulate spacecraft thrust
in dir direction to accelerate forward
at an angle for retro-rockets, etc.
"""
#calculate dx dy based on angle
radians = angle * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.dx += dx
self.dy += dy
self.updateVector()
def updateVector(self):
#calculate new speed and angle based on dx, dy
#call this any time you change dx or dy
self.speed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
dy = self.dy * -1
dx = self.dx
radians = math.atan2(dy, dx)
self.dir = radians / math.pi * 180
def setSpeedLimits(self, max, min):
""" determines maximum and minimum
speeds you will allow through
speedUp() method. You can still
directly set any speed you want
with setSpeed() Default values:
max: 10
min: -3
"""
self.maxSpeed = max
self.minSpeed = min
def dataTrace(self):
""" utility method for debugging
print major properties
extend to add your own properties
"""
print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \
(self.x, self.y, self.speed, self.dir, self.dx, self.dy)
def mouseDown(self):
""" boolean function. Returns True if the mouse is
clicked over the sprite, False otherwise
"""
self.pressed = False
if pygame.mouse.get_pressed() == (1, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.pressed = True
return self.pressed
def clicked(self):
""" Boolean function. Returns True only if mouse
is pressed and released over sprite
"""
released = False
if self.pressed:
if pygame.mouse.get_pressed() == (0, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
released = True
return released
def collidesWith(self, target):
""" boolean function. Returns True if the sprite
is currently colliding with the target sprite,
False otherwise
"""
collision = False
if self.rect.colliderect(target.rect):
collision = True
return collision
def collidesGroup(self, target):
""" wrapper for pygame.sprite.collideany
simplifies checking sprite - group collisions
returns result of collision check (sprite from group
that was hit or None)
"""
collision = pygame.sprite.spritecollideany(self, target)
return collision
def distanceTo(self, point):
""" returns distance to any point in pixels
can be used in circular collision detection
"""
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dist = math.sqrt((dx * dx) + (dy * dy))
return dist
def dirTo(self, point):
""" returns direction (in degrees) to
a point """
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dy *= -1
radians = math.atan2(dy, dx)
dir = radians * 180 / math.pi
dir += 180
return dir
def drawTrace(self, color=(0x00, 0x00, 0x00)):
""" traces a line between previous position
and current position of object
"""
pygame.draw.line(self.scene.background, color, self.oldCenter,
self.rect.center, 3)
self.screen.blit(self.scene.background, (0, 0))
def addState(self, stateName, stateImageFile):
""" Creates a new sprite state with the associated name
and image. Useful to build multi-state sprites.
"""
#load the image
tempImage = pygame.image.load(stateImageFile)
tempImage.convert()
self.states[stateName] = tempImage
def setState(self, stateName):
""" attempts to set the sprite to the indicated state
(image)
"""
self.imageMaster = self.states[stateName]
self.rect = self.imageMaster.get_rect()
self.currentState = stateName
def getState(self):
""" returns the current state name
(default if no states have been explicitly set)
"""
return self.currentState
if pro.state == pro.ATTACKING:
if pro.collidesWith(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(baddySprites):
pro.heartPts -= 1
baddySprites is a sprite group, so I bet you have to use collidesGroup instead of collidesWith.
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesGroup(baddySprites):
pro.heartPts -= 1
But even if you do this, it seems like you'll still have problems. Namely, this code only ever deducts health from baddy and not baddy1. I'm assuming sprite groups support iteration. If so, you should perform collision detection independently on each enemy.
for enemy in baddySprites:
if pro.state == pro.ATTACKING:
if pro.collidesWith(enemy):
enemy.health -= 1
if enemy.health == 0:
enemy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(enemy):
pro.heartPts -= 1

Categories

Resources