Last active
August 11, 2022 09:35
-
-
Save horstjens/134cb708fda4ea34733c6a20586cc644 to your computer and use it in GitHub Desktop.
pygame platformer ( data folder not included)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
pygame platform game | |
""" | |
# TODO : avoid climbing in block (when jumping) -> set middle point down near foot points | |
# TODO: fine-tune bounce on ceiling | |
import os | |
# | |
import pygame | |
import random | |
import xml.etree.ElementTree as et | |
main_dir = os.path.split(os.path.abspath(__file__))[0] | |
# 64x64 tiles ergibt für 1200x800 screensize: 18 x 12 | |
legend = {"#":"gras", | |
".":"nothing", | |
"c":"cannon", | |
"S":"snow", | |
"*":"powerup", | |
} | |
BLOCK_SIZE_X = 64 | |
BLOCK_SIZE_Y = 73 | |
GROUND="#S" | |
# between top platform and ceiling (I) must be 2 empty rows!! | |
LEVEL1 = """ | |
IIIIIIIIIIIIIIIIIIII | |
I...*.......*......I | |
I..................I | |
I.......##.....*...I | |
I............####..I | |
I..................I | |
I..SSS...###....##.I | |
I............##....I | |
I..####SSS.........I | |
I..*...............I | |
I.####......c......I | |
I........#####.....I | |
I...##.............I | |
I******************I | |
I******##**********I | |
I******************I | |
I***##*************I | |
I******************I | |
I******************I | |
ISSSSSSSSSSSSSSSSSSI | |
""" | |
# -------- helper functions ---- | |
# function to load state images and animation cycle times into a class | |
def put_states_into_class(class_name, state_name, cycle_time, *picture_names): | |
"""load picture from file or from Game.images into class""" | |
class_name.images[state_name] = [] | |
class_name.animation_speed[state_name] = cycle_time | |
for pic in picture_names: | |
if (pic.lower().endswith(".png") or | |
pic.lower().endswith(".gif") or | |
pic.lower().endswith(".jpg") or | |
pic.lower().endswith(".jpeg")): | |
# pic is a file | |
image = load_image(pic) | |
else: | |
image = load_from_atlas(pic) | |
class_name.images[state_name].append(image) | |
def load_from_atlas(imagename): | |
sheet, rect = Game.atlas[imagename] | |
surface = pygame.surface.Surface((rect.width, rect.height)) | |
# make black transparent | |
surface.set_colorkey((0,0,0)) | |
surface.convert_alpha() | |
surface.blit(Game.spritesheets[sheet],(0,0), rect) | |
return surface | |
# quick function to load an image | |
def load_image(name, invisible_color=None, target_size=None): | |
path = os.path.join(main_dir, Game.datafoldername, name) | |
#if name[-4:].lower() in (".png", ".gif"): | |
# image= pygame.image.load(path).convert_alpha() | |
#else: | |
image= pygame.image.load(path) | |
if target_size is not None: | |
image=pygame.transform.scale(image, target_size) | |
if invisible_color is not None: | |
image.set_colorkey(invisible_color) | |
image.convert_alpha() | |
return image | |
class Game: | |
#friction = {"#":0.9, | |
# "S":0.995, | |
# ".":1.0, | |
# "I":0.8, | |
# } | |
screensize = (800,600) | |
objects = [] | |
fps = 120 | |
gravity = pygame.math.Vector2(0, 1000) # 9.81 | |
level = None | |
datafoldername = "data" | |
camera = pygame.math.Vector2(0,0) | |
images={} | |
spritesheets = {} | |
atlas = {} # {name: (sheetname, pygame.Rect)} | |
grid = False | |
hudcolor1=(255,255,255) | |
hudcolor2=(128,128,128) | |
hudcolor3=(0,0,0) | |
iconsize1=(64,64) | |
iconsize2=(256,64) | |
fillcolor1=(255,255,255) | |
# parent game object class | |
class GameObject(pygame.sprite.Sprite): | |
images = {} # { state_name : [first_image, second_image, ....] } | |
animation_speed = {} # { state_name : cycle_time } | |
def __init__(self, image=None, pos=None, move=None,layer=5): # MUST have image and pos | |
super().__init__(self.groups) | |
if move is None: | |
move = pygame.math.Vector2(0,0) | |
self.move = move | |
self.add_cameravector = True | |
#self.groups = groups | |
self._layer = layer | |
#self._layer = 5 | |
if image is not None: | |
self.image = image | |
#print(self,"self.image not passed to super()__init__(") | |
self.rect = self.image.get_rect() | |
if pos is not None: | |
self.pos = pos # middle of rect! | |
#print(self,"self.pos not passed to super()__init__(") | |
self.rect.center = self.pos | |
# bounce on screen edge ? | |
self.area = pygame.Rect(self.pos.x - 200, self.pos.y - 200, 400, 400) | |
self.bounce_north = False | |
self.bounce_south = False | |
self.bounce_east = False | |
self.bounce_west = False | |
self.kill_north = False | |
self.kill_south = False | |
self.kill_east = False | |
self.kill_west = False | |
self.wrap_north = False | |
self.wrap_south = False | |
self.wrap_east = False | |
self.wrap_west = False | |
#def kill(self): | |
# super().__kill__() | |
def update(self, dt): | |
self.pos += self.move * dt | |
self.rect.center = self.pos | |
#self.rect.center = self.pos | |
# --- Area west ---- | |
if self.pos.x - self.rect.width /2 < self.area.left: | |
if self.bounce_west: | |
self.pos.x = self.area.left + self.rect.width / 2 | |
self.move.x *= -1 | |
if self.kill_west: | |
self.kill() | |
if self.wrap_west: | |
if self.pos.x + self.rect.width / 2 < self.area.left: | |
self.pos.x = self.area.right + self.rect.width /2 | |
# TODO: east, north, south | |
if self.bounce_east: | |
if self.pos.x + self.rect.width/2 > Game.screensize[0]: | |
self.pos.x = Game.screensize[0] - self.rect.width/2 | |
self.move.x *= -1 | |
if self.bounce_north: | |
if self.pos.y - self.rect.height/2 < 0: | |
self.pos.y = self.rect.height /2 | |
self.move.y *= -1 | |
if self.bounce_south: | |
if self.pos.y + self.rect.height/2 > Game.screensize[1]: | |
self.pos.y = Game.screensize[1] - self.rect.height/2 | |
self.move.y *= -1 | |
class Block(GameObject): | |
def __init__(self, image, pos=None): | |
super().__init__(image, pos) | |
self.friction = 1.0 # no friction | |
self.player_restrict_from_top = False | |
self.player_restrict_from_bottom = False | |
self.player_restrict_from_left = False | |
self.player_restrict_from_right = False | |
#class Air(Block): | |
# def __init__(self,pos): | |
# self.image = pygame.surface.Surface((BLOCK_SIZE_X,BLOCK_SIZE_Y)) | |
# self.image.set_colorkey((0,0,0)) | |
# self.image.convert_alpha() | |
# super().__init__(self.image, pos) | |
# self.friction = 1.0 # no friction | |
# self.char = "." | |
class Gras(Block): | |
def __init__(self, pos): | |
self.image = load_from_atlas("tileGrass") | |
super().__init__(self.image, pos) | |
self.friction = 0.9 | |
self.player_restrict_from_top = True | |
self.char = "#" | |
class Snow(Block): | |
def __init__(self, pos): | |
self.image = load_from_atlas("tileSnow") | |
super().__init__(self.image, pos) | |
self.friction = 0.995 | |
self.player_restrict_from_top = True | |
self.char = "S" | |
class Stone(Block): | |
def __init__(self, pos): | |
self.image = load_from_atlas("tileStone") | |
super().__init__(self.image, pos) | |
self.friction = 0.8 | |
self.char = "I" | |
self.player_restrict_from_top = True | |
self.player_restrict_from_bottom = True | |
self.player_restrict_from_left = True | |
self.player_restrict_from_right = True | |
class Player(GameObject): | |
def __init__(self, pos=None): | |
self.hp = 100 | |
self.hp_max = 100 | |
self.i = 0 # current image index | |
self.time = 0 # duration of existence | |
self.max_dash_ready = 4 | |
self.dash_ready = 0 | |
self.time_this_frame = 0 | |
self.dash_ready = 0 | |
self.dash_duration = 0.25 | |
self.dash_remaining = 0 | |
self.updraft_ready = 0 | |
self.max_updraft_ready = 4 | |
self.updraft_duration = 0.25 | |
self.updraft_remaining = 0 | |
self.updraft_speed = 1200 | |
self.on_ground = False | |
#self.ground = ".." # both feets are over air | |
self._layer = 7 | |
self.state = "stand" | |
self.old_state = "stand" | |
self.standing_state = "stand" | |
self.jump_speed = 800 # for jumping up | |
self.walk_speed = 300 | |
self.run_speed = 800 | |
self.effects = {}#{effect_name:remaining_duration} | |
self.friction = 1.0 # remain full speed - keine Abbremsung | |
#self.feet_delta_y = 0#25 # 10 pixel nach unten | |
#self.feet_delta_x = 50#20 # 10 pixel nach innen | |
self.image = Player.images[self.state][0] | |
self.rect = self.image.get_rect() | |
if pos is None: | |
# spawn in screen center | |
pos = pygame.math.Vector2(Game.screensize[0]/2,Game.screensize[1]/2) | |
#pos = pygame.math.Vector2(200,200) | |
super().__init__(self.image,pos) | |
self.left_feet_vector = pygame.math.Vector2(-self.rect.width/4, self.rect.height/2+5 ) | |
self.right_feet_vector = pygame.math.Vector2(self.rect.width/4, self.rect.height/2+5 ) | |
self.mid_vector = pygame.math.Vector2(0,0) | |
self.top_vector = pygame.math.Vector2(0,-self.rect.height/2) | |
self.left_top_vector = pygame.math.Vector2(-self.rect.width/2-5,-self.rect.height/2) | |
self.left_bottom_vector = pygame.math.Vector2(-self.rect.width/2-5,self.rect.height/2) | |
self.right_top_vector = pygame.math.Vector2(self.rect.width/2+5,-self.rect.height/2) | |
self.right_bottom_vector = pygame.math.Vector2(self.rect.width/2+5,self.rect.height/2) | |
def animate(self): | |
if len(self.images[self.state]) ==0: | |
return | |
# only change images if there is more than one image for this state | |
# ------ animation ----- | |
if self.time_this_frame > 1 /self.animation_speed[self.state]: | |
self.i += 1 | |
#print("i:",self.i) | |
#print("state:", self.state, "i:", self.i) | |
if self.i == len(self.images[self.state]): | |
self.i = 0 | |
self.image = self.images[self.state][self.i] | |
self.time_this_frame = 0 | |
def update(self, dt): | |
#effekte abklingen lassen | |
for effect, remaining_time in self.effects.items(): | |
remaining_time -= dt | |
remaining_time = max(0, remaining_time) | |
self.effects[effect] = remaining_time | |
if self.updraft_ready < self.max_updraft_ready: | |
self.updraft_ready += dt | |
if self.dash_ready < self.max_dash_ready: | |
self.dash_ready += dt | |
# ---- change state , fall down , jump , standing_state , etc | |
if self.on_ground: | |
# --- not flying, player walks on ground --- | |
if self.move.x < 0: | |
#self.state = "walk_left" | |
self.standing_state = "stand_left" | |
elif self.move.x > 0: | |
#self.state = "walk" | |
self.standing_state = "stand" | |
else: | |
self.state = self.standing_state | |
elif not self.on_ground: | |
# ------ flying ----- | |
self.friction = 1.0 | |
self.move += Game.gravity * dt | |
if self.move.y < 0: | |
self.state = "jump" | |
if self.move.x < 0: | |
self.state = "jump_left" | |
elif self.move.y > 0: | |
self.state = "fall" | |
if self.move.x < 0: | |
self.state = "fall_left" | |
else: | |
self.state = self.standing_state | |
# --------------------------- | |
# ---- increase time, check state_change --------- | |
self.time += dt # in seconds | |
self.time_this_frame += dt | |
if self.state != self.old_state: | |
self.i = 0 | |
self.time_this_frame = 0 | |
self.image = self.images[self.state][self.i] | |
# ---------- animate -------- | |
self.animate() | |
self.old_state = self.state | |
# ----- neighbor blocks ---- | |
#middle = self.pos | |
#x,y = int(middle.x/BLOCK_SIZE_X), int(middle.y/BLOCK_SIZE_Y) | |
# ------- check ground ----- | |
# in mainloop | |
# ------ moving ----- | |
#super().update(dt) | |
self.pos += self.move * dt | |
#self.rect.center = self.pos | |
def blocktouch(self, | |
#dt, | |
blocks_touching_left_feet, | |
blocks_touching_right_feet, | |
blocks_touching_middle, | |
blocks_touching_left_top, | |
blocks_touching_left_bottom, | |
blocks_touching_right_top, | |
blocks_touching_right_bottom, | |
blocks_touching_top): | |
# is called from main loop collision detection with: Game.player1.blocktouch(left_feet_blocks, right_feet_blocks, middle_blocks) | |
# | |
# ------ physic ------- | |
#print(blocks_touching_left_feet, blocks_touching_right_feet, blocks_touching_middle) | |
# check self.on_ground when falling down ----------- | |
if self.move.y >= 0: | |
if (len(blocks_touching_middle) == 0) and (len(blocks_touching_left_feet) > 0) or (len(blocks_touching_right_feet)>0): | |
self.on_ground = True | |
self.move.y = 0 | |
block_y = None | |
if len(blocks_touching_left_feet) > 0: | |
block_y = min([block.pos.y for block in blocks_touching_left_feet]) | |
self.friction = sum([block.friction for block in blocks_touching_left_feet]) / len(blocks_touching_left_feet) | |
else: | |
block_y = min([block.pos.y for block in blocks_touching_right_feet]) | |
self.friction = sum([block.friction for block in blocks_touching_right_feet]) / len(blocks_touching_right_feet) | |
self.pos.y = block_y -BLOCK_SIZE_Y/2-self.rect.height/2 | |
else: | |
self.on_ground = False | |
# check bottom when jumping up | |
elif self.move.y < 0: | |
if len(blocks_touching_top)>0: | |
block_y = max([block.pos.y for block in blocks_touching_top ]) | |
self.move.y = 0 | |
self.pos.y = block_y + BLOCK_SIZE_Y/2 + self.rect.height/2 + self.top_vector.y | |
# check ----------block left if moving left----------- | |
if self.move.x < 0: | |
if (len(blocks_touching_left_top)>0) or (len(blocks_touching_left_bottom)>0): | |
self.move.x = 0 | |
block_x = None | |
if len(blocks_touching_left_top) > 0: | |
block_x = max([block.pos.x for block in blocks_touching_left_top]) | |
else: | |
block_x = max([block.pos.x for block in blocks_touching_left_bottom]) | |
self.pos.x = block_x + BLOCK_SIZE_X/2 + self.rect.width / 2 | |
# check block right if moving right ------------ | |
elif self.move.x > 0: | |
if (len(blocks_touching_right_top)>0) or (len(blocks_touching_right_bottom)>0): | |
self.move.x = 0 | |
block_x = None | |
if len(blocks_touching_right_top) > 0: | |
block_x = min([block.pos.x for block in blocks_touching_right_top]) | |
else: | |
block_x = min([block.pos.x for block in blocks_touching_right_bottom]) | |
self.pos.x = block_x - BLOCK_SIZE_X/2 - self.rect.width / 2 | |
class Powerup(GameObject): | |
def __init__(self,pos,): | |
self.state = "rest_powerup" | |
self.image = self.images[self.state][0] | |
self.age = 0 | |
self.max_age = 8 | |
super().__init__(self.image,pos) | |
self.effect = random.choice(("damage","speed","health")) | |
def update(self, dt): | |
super().update(dt) | |
self.age =+dt | |
if self.age > self.max_age: | |
self.kill() | |
class Beam(GameObject): | |
def __init__(self, pos, target, shooter_move=pygame.Vector2(0,0), speed=200, max_age=3.5): | |
shooter_move= pygame.Vector2(shooter_move[0], shooter_move[1]) | |
self.pos = pygame.math.Vector2(pos[0], pos[1]) | |
self.target = pygame.math.Vector2(target[0], target[1]) | |
self.speed = speed | |
self.max_age = max_age | |
self.age = 0 | |
self.image = pygame.surface.Surface((50,10)) | |
#self.image.fill((0,0,128)) # dark green | |
pygame.draw.polygon(self.image, (0,255,0), [(0,5),(40,0),(50,5),(40,10)],0) | |
self.image.set_colorkey((0,0,0)) | |
self.image.convert_alpha() | |
distance = pygame.Vector2(self.target.x, self.target.y) - pygame.math.Vector2(self.pos.x, self.pos.y) | |
angle = distance.angle_to(pygame.Vector2(1,0)) | |
#print("distance:", distance, type(distance)) | |
#distance.normalize_ip() # has now lenght of 1 | |
self.image = pygame.transform.rotate(self.image, angle ) | |
# TODO: test sane parameters for player move speed so that it can safely added to shooter_move | |
#self.move = distance.normalize() * speed + pygame.Vector2(shooter_move.x,shooter_move.y) | |
self.move = distance.normalize() * speed #+ pygame.Vector2(shooter_move.x,shooter_move.y) | |
print(distance, angle, self.move ) | |
super().__init__(image=self.image, pos=self.pos, move=self.move) | |
self.rect = self.image.get_rect() | |
self.add_cameravector = True | |
def update(self, dt): | |
#print("update!!", self.pos, self.move) | |
self.age += dt | |
if self.age > self.max_age: | |
self.kill() | |
self.pos += self.move * dt | |
#print(self.pos, self.move) | |
self.rect.center = self.pos# + Game.camera | |
class Cannon(GameObject): | |
def __init__(self, pos, aim_at_player=True): | |
self.state = "rest_cannon" | |
self.image = self.images[self.state][0] # referring to Cannon.images["rest"][0] | |
self.age = 0 | |
self._layer = 7 | |
self.aim_at_player = aim_at_player | |
self.angle = 0 | |
super().__init__(self.image,pos) | |
def update(self, dt): | |
super().update(dt) | |
# rotation | |
if self.aim_at_player: | |
#center = self.rect.center | |
v = Game.player1.pos - self.pos | |
length, angle = v.as_polar() | |
self.image = self.images[self.state][0] # referring to Cannon.images["rest"][0] | |
self.image = pygame.transform.rotate(self.image, -angle) | |
self.rect = self.image.get_rect() | |
#self.rect = self.image.get_rect() | |
#self.rect.center = self.pos | |
class Textsprite(GameObject): | |
def __init__(self, pos, text):# | |
self.image= Game.font2.render(text, False,(255,255,255),(128,128,128)) | |
self.age = 0 | |
self.max_age = 3 | |
self.start_speed = 200 | |
self.text = text | |
move = pygame.math.Vector2(0, -self.start_speed) | |
super().__init__(self.image, pos, move) | |
def update(self,dt): | |
super().update(dt) | |
self.age +=dt | |
if self.age > self.max_age : | |
self.kill() | |
return | |
percent = self.age/self.max_age | |
self.move.y = -self.start_speed *(1-percent*2) | |
alpha = int(255-percent*255) | |
self.image= Game.font2.render(self.text, False,(255,255,255)) | |
self.image.set_alpha(alpha) | |
self.image.convert_alpha() | |
#self.image.set_alpha(255-percent*255) | |
class Particle(pygame.sprite.Sprite): | |
def __init__(self, pos): | |
super().__init__(self.groups) | |
self.age = 0 | |
self.add_cameravector = True | |
self._layer = 6 | |
self.image = pygame.surface.Surface((5,5)) | |
self.color = (255,0,0,255) | |
self.image.fill(self.color) | |
self.image.convert_alpha() | |
self.rect =self.image.get_rect() | |
self.pos = pos | |
self.max_age = random.uniform(0.5,2.5) | |
self.rect.center = pos | |
self.move = pygame.math.Vector2(random.uniform(25,250),0) | |
self.move.rotate_ip(random.uniform(0,360)) | |
def update(self, dt): | |
self.age += dt | |
if self.age > self.max_age: | |
self.kill() | |
self.pos += self.move * dt | |
self.rect.center = self.pos | |
#print(self.pos) | |
def create_hud(): | |
surface = pygame.surface.Surface(Game.screensize) | |
surface.set_colorkey((0,0,0)) | |
#-----game time top mid | |
#fontsurface = Game.font2.render( | |
# f"seconds played:{Game.game_time:.1f}", | |
# False, | |
# Game.hudcolor1, | |
# None) | |
#fontsurface.set_alpha(128) | |
#fontrect = fontsurface.get_rect() | |
#surface.blit(fontsurface,(Game.screensize[0]/2 - fontrect.width/2,0)) | |
# ============= bottom-right square icons ================ | |
# blitting from right to left ! | |
percents = [] | |
# updraft, dash | |
percents.append(Game.player1.updraft_ready / Game.player1.max_updraft_ready) | |
percents.append(Game.player1.dash_ready / Game.player1.max_dash_ready) | |
for i, icon in enumerate(Game.icons): | |
x=Game.screensize[0]-Game.iconsize1[0] * (i+1) # width | |
y=Game.screensize[1]-Game.iconsize1[1] # height | |
pygame.draw.rect( | |
surface, | |
Game.fillcolor1, | |
(x, # x of topleft corner | |
y+(1-percents[i])*Game.iconsize1[1], # y of topleft corner | |
Game.iconsize1[0], # rect width | |
percents[i]*Game.iconsize1[1]), # rect height | |
) | |
surface.blit(Game.icons[i],(x,y)) | |
#=============blit icon lower left corner=========== | |
# ---------- ui for player hp ----------- | |
x2=0 #Game.screensize[0]-(Game.screensize[0]) | |
y2=Game.screensize[1]-64 | |
füllbreite = 256-64 | |
hp_percent = Game.player1.hp / Game.player1.hp_max | |
# wenn hp = 0 .... rot = 255, grün = 0 | |
# wenn hp = full ... rot = 0, grün = 255 | |
farbe=( | |
int((1-hp_percent)*255), # rot | |
int(hp_percent*255) # green | |
,0) # blue | |
pygame.draw.rect(surface,farbe,(x2+64, y2, int(hp_percent*füllbreite), 64)) | |
surface.blit(Game.HPbar,(x2,y2)) | |
#--------------topleft effects------------ | |
y = 0 | |
for effects, remaining_time in Game.player1.effects.items(): | |
fontsurface = Game.font.render( | |
f"{effects}:{remaining_time:.1f}", | |
False, | |
Game.hudcolor2, | |
None) | |
fontsurface.set_alpha(128) | |
surface.blit(fontsurface,(0,y)) | |
y += fontsurface.get_rect().height | |
return surface | |
# here's the full code | |
def prepare(): | |
pygame.init() | |
Game.screen = pygame.display.set_mode(Game.screensize) | |
#pink_tank = load_image( | |
# name="tank.gif", | |
# invisible_color="#ff40dd", | |
# target_size = (128,128), | |
# ) | |
#tank = load_image("tank.gif") | |
background = load_image("volcano.jpg") #, invisible_color="#02325a") | |
# load icons from RIGHT to LEFT | |
Game.icons = [ | |
load_image("UPDRAFT.png", invisible_color="#ff40ff", target_size=Game.iconsize1), | |
load_image("DASH.png", invisible_color="#ff40ff", target_size=Game.iconsize1), | |
] | |
#Game.icon1 = load_image("DASH.png", invisible_color="#ff40ff", target_size=Game.iconsize1) | |
#Game.icon2 = load_image("UPDRAFT.png", invisible_color="#ff40ff", target_size=Game.iconsize1) | |
Game.HPbar = load_image("HP_BAR.png", invisible_color="#ff40ff", target_size=Game.iconsize2) | |
#tileset = load_image("blockPack_tilesheet.png") | |
#gras = pygame.Surface.subsurface(tileset, (256,26,64,64)) | |
#---------- load spritesheets ------ | |
#for name in ["blockPack_spritesheet.png", | |
# "player_tilesheet.png" | |
# ]: | |
# Game.spritesheets[name] = load_image(name) | |
# ----- xml spritesheet data ----- | |
for xmlname in ('blockPack_spritesheet.xml',"player_tilesheet.xml"): | |
tree = et.parse(os.path.join(Game.datafoldername,xmlname)) | |
root = tree.getroot() | |
if root.tag != "TextureAtlas": | |
raise ValueError(f"first element of {xmlname} is not a '<TextureAtlas>'") | |
sheetname = root.attrib["imagePath"] | |
# create entry into Game.spritesheets if necessary | |
if sheetname not in Game.spritesheets: | |
Game.spritesheets[sheetname] = load_image(sheetname) # joins with datafoldername | |
# create entries in Game.atlas: {name: (sheetname, pygame.Rect)} | |
for i in range(len(root)): | |
xmldata = root[i].attrib | |
xname = xmldata["name"] | |
if xname.lower().endswith(".png") or xname.lower().endswith(".gif"): | |
name = xname[:-4] # remove file extension # TODO: enforce lower() ? | |
else: | |
name = xname | |
Game.atlas[name]= (sheetname, | |
pygame.Rect( | |
int(xmldata["x"]), | |
int(xmldata["y"]), | |
int(xmldata["width"]), | |
int(xmldata["height"]))) | |
gras = load_from_atlas("tileGrass") | |
snow = load_from_atlas("tileSnow") | |
stone = load_from_atlas("tileStone") | |
#print("gras:", gras.get_rect()) | |
# ======= Cannon Sprite ===== | |
put_states_into_class(Cannon, "rest_cannon", 1, "cannon.png") # looking to east | |
#powerup sprite | |
put_states_into_class(Powerup, "rest_powerup", 1, "foliageBush_small") | |
# ========== PLAYER SPRITE ============= | |
put_states_into_class(Player, "stand", 1, "player_stand") | |
put_states_into_class(Player, "walk", 7, "player_walk1", "player_walk2") | |
put_states_into_class(Player, "run", 30, "player_walk1", "player_walk2") | |
put_states_into_class(Player, "idle", 1, "player_idle", "player_stand") | |
put_states_into_class(Player, "jump", 1, "player_jump") | |
put_states_into_class(Player, "fall", 1, "player_fall") | |
put_states_into_class(Player, "slide", 1, "player_slide") | |
# ---- create left-looking images for player ----- | |
# add statename_left to the Player.images.states | |
for key in list(Player.images.keys()): | |
new_key = key+"_left" | |
Player.images[new_key]= [] | |
for image in Player.images[key]: | |
Player.images[new_key].append( | |
pygame.transform.flip(image, True, False) | |
) | |
# ---- copy animation times ----- | |
for key in list(Player.animation_speed.keys()): | |
value = Player.animation_speed[key] | |
Player.animation_speed[key+"_left"] = value | |
Game.background = pygame.transform.scale(background, Game.screensize) | |
# ----- sprite groups (containers) ---- | |
Game.all_group = pygame.sprite.LayeredUpdates() # group using _layer, **layer | |
Game.fx_group = pygame.sprite.Group() # simple group | |
Game.player_group = pygame.sprite.Group() | |
Game.block_group = pygame.sprite.Group() | |
Game.power_group = pygame.sprite.Group() | |
# ----------- sprites ---------- | |
GameObject.groups = Game.all_group, | |
Player.groups = Game.player_group, | |
Particle.groups = Game.all_group, Game.fx_group | |
Powerup.groups = Game.all_group, Game.power_group | |
Block.groups = Game.all_group, Game.block_group | |
#Cannon is already in all_group because it's a GameObject | |
# -----------create 1 player-------------- | |
Game.player1 = Player() | |
#Cannon(pos=Game.player1.pos + pygame.math.Vector2(200,0)) | |
# create 10 tanks | |
#for _ in range(0): | |
# o = GameObject( | |
# image=gras, | |
# pos=pygame.math.Vector2(random.randint(0, Game.screensize[0]), | |
# random.randint(0, Game.screensize[1])), | |
# move=pygame.math.Vector2(random.uniform(-100,100), | |
# random.uniform(-100,100)), | |
# ) | |
# Game.objects.append(o) | |
# ---------- create platforms layer ---------- | |
# ---- (create cannons) --- | |
Game.level = [list(line) for line in LEVEL1.splitlines() if len(line.strip()) >0] | |
Game.platforms = pygame.surface.Surface( | |
(BLOCK_SIZE_X*len(Game.level[0]) , | |
BLOCK_SIZE_Y*len(Game.level))) | |
Game.platforms.set_colorkey((0,0,0)) # black is transparent | |
Game.platforms.convert_alpha() | |
# surface is black by default | |
for y, line in enumerate(Game.level): | |
for x, char in enumerate(line): | |
if char == "#": # platform | |
#Game.platforms.blit(gras, (x*BLOCK_SIZE_X,y*BLOCK_SIZE_Y)) | |
Gras(pos=pygame.math.Vector2(x*BLOCK_SIZE_X, y*BLOCK_SIZE_Y)) | |
if char == "S": # platform | |
#Game.platforms.blit(snow, (x*BLOCK_SIZE_X,y*BLOCK_SIZE_Y)) | |
Snow(pos=pygame.math.Vector2(x*BLOCK_SIZE_X, y*BLOCK_SIZE_Y)) | |
if char == "I": # wall | |
#Game.platforms.blit(stone, (x*BLOCK_SIZE_X,y*BLOCK_SIZE_Y)) | |
Stone(pos=pygame.math.Vector2(x*BLOCK_SIZE_X, y*BLOCK_SIZE_Y)) | |
if char == "c": # cannon | |
Cannon(pos=pygame.math.Vector2(x*BLOCK_SIZE_X, y*BLOCK_SIZE_Y)) | |
Game.level[y][x] = "." # write back empty space | |
if char == "*": # powerup | |
Powerup(pos=pygame.math.Vector2(x*BLOCK_SIZE_X, y*BLOCK_SIZE_Y)) | |
Game.level[y][x] = "." # write back | |
# ---------- create grid layer (toggle with key 'g') ----- | |
Game.gridlayer = pygame.surface.Surface( | |
(BLOCK_SIZE_X*len(Game.level[0]) , | |
BLOCK_SIZE_Y*len(Game.level))) | |
Game.gridlayer.set_colorkey((0,0,0)) # black is transparent | |
Game.gridlayer.convert_alpha() | |
Game.font = pygame.font.SysFont("sysfont", 24, ) | |
Game.font2 = pygame.font.SysFont("sysfont",48,) | |
for y, line in enumerate(Game.level): | |
for x, char in enumerate(line): | |
#print(x,y) | |
midx = x*BLOCK_SIZE_X+BLOCK_SIZE_X/2 | |
midy = y*BLOCK_SIZE_Y+BLOCK_SIZE_Y/2 | |
middle = pygame.math.Vector2(x*BLOCK_SIZE_X+BLOCK_SIZE_X/2, y*BLOCK_SIZE_Y+BLOCK_SIZE_Y/2) | |
pygame.draw.rect(Game.gridlayer, (255,0,0), (midx-BLOCK_SIZE_X/2,midy-BLOCK_SIZE_Y/2,BLOCK_SIZE_X,BLOCK_SIZE_Y),2) | |
pygame.draw.line(Game.gridlayer, (255,0,0), middle+pygame.math.Vector2(-5,-5), middle+pygame.math.Vector2(5,5),1) | |
pygame.draw.line(Game.gridlayer, (255,0,0), middle+pygame.math.Vector2(-5,5), middle+pygame.math.Vector2(5,-5),1) | |
fontsurface = Game.font.render(f" {x} / {y}", False,(1,1,1),(128,128,128)) | |
Game.gridlayer.blit(fontsurface, (midx-BLOCK_SIZE_X/2, midy)) | |
def main(): | |
clock = pygame.time.Clock() | |
pause = False | |
pause_time = 0 | |
Game.player1.move = pygame.math.Vector2(0,0) | |
Game.game_time = 0.0 | |
while True: | |
text = "fps: {:.2f} camera: {} player.pos {} mouse:{}".format( | |
clock.get_fps(), | |
Game.camera, | |
Game.player1.pos, | |
pygame.mouse.get_pos()) | |
pygame.display.set_caption(text) | |
milliseconds = clock.tick(Game.fps) | |
seconds = milliseconds / 1000 | |
Game.game_time+=seconds | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
return | |
if event.type == pygame.KEYDOWN: | |
if event.key == pygame.K_ESCAPE: | |
return | |
if event.key == pygame.K_s: | |
Game.player1.move = pygame.math.Vector2(0,0) | |
if event.key == pygame.K_p: | |
pause = not pause | |
if pause: | |
pause_time += seconds | |
seconds = 0 | |
if event.key == pygame.K_g: | |
Game.grid = not Game.grid | |
if event.key == pygame.K_LALT: | |
Game.player1.dash_ready = game_time + 3 # 3 seconds in the future | |
#-------jump --------- | |
if event.key == pygame.K_SPACE: | |
if Game.player1.on_ground: | |
Game.player1.move.y = -Game.player1.jump_speed | |
Game.player1.on_ground = False | |
# -------- tasten die gleichzeitig gedrückt sind ---- | |
pressed = pygame.key.get_pressed() | |
# ------------------- move camera ---------- | |
if pressed[pygame.K_RIGHT]: | |
Game.camera.x -= 100 * seconds | |
if pressed[pygame.K_LEFT]: | |
Game.camera.x += 100 * seconds | |
if pressed[pygame.K_UP]: | |
Game.camera.y += 100 * seconds | |
if pressed[pygame.K_DOWN]: | |
Game.camera.y -= 100 * seconds | |
# -------- particle test | |
#if pressed[pygame.K_x]: | |
# print(pygame.mouse.get_pos()) | |
if pygame.mouse.get_pressed()[0]: # left mouse button pressed | |
#Particle(pygame.mouse.get_pos()) | |
# beam test | |
Beam(pos=Game.player1.pos, | |
target= pygame.mouse.get_pos()-Game.camera, | |
shooter_move=Game.player1.move, | |
speed=200, | |
max_age=2.5, | |
) | |
if pygame.mouse.get_pressed()[2]: | |
print("dash") | |
# ------------------- move player ---------- | |
#Game.player1.move.x = 0 # reset move to 0 | |
#Game.player1.move.x *= Game.player1. | |
if Game.player1.move.x != 0 and Game.player1.on_ground: | |
if Game.player1.move.x < 0: | |
#friction = Game.friction[Game.player1.ground[0]] | |
if Game.player1.move.x < -Game.player1.walk_speed: | |
Game.player1.state = "slide_left" | |
else: | |
Game.player1.state = "stand_left" | |
else: | |
#friction = Game.friction[Game.player1.ground[1]] | |
if Game.player1.move.x > Game.player1.walk_speed: | |
Game.player1.state = "slide" | |
else: | |
Game.player1.state = "stand" | |
# slow down speed because of ground friction | |
Game.player1.move.x *= Game.player1.friction | |
if abs(Game.player1.move.x) < 10: | |
Game.player1.move.x = 0 | |
Game.player1.state=Game.player1.standing_state | |
#move left | |
if pressed[pygame.K_a] : | |
Game.player1.move.x = -Game.player1.walk_speed | |
if Game.player1.on_ground: | |
Game.player1.state = "walk_left" | |
if pressed[pygame.K_LSHIFT]: | |
Game.player1.move.x = -Game.player1.run_speed | |
Game.player1.state = "run_left" | |
#move right | |
if pressed[pygame.K_d] : | |
Game.player1.move.x = Game.player1.walk_speed | |
if Game.player1.on_ground: | |
Game.player1.state = "walk" | |
if pressed[pygame.K_LSHIFT]: | |
Game.player1.move.x = Game.player1.run_speed | |
Game.player1.state = "run" | |
#Updraft | |
if pressed[pygame.K_e] : | |
if Game.player1.updraft_ready > Game.player1.max_updraft_ready: | |
if Game.player1.on_ground: | |
Game.player1.on_ground=False | |
Game.player1.updraft_ready=0 | |
Game.player1.move.y = -Game.player1.updraft_speed | |
else: | |
Game.player1.updraft_ready=0 | |
Game.player1.move.y = -Game.player1.updraft_speed | |
if pressed[pygame.K_q]: | |
if Game.player1.dash_ready>Game.player1.max_dash_ready: | |
Game.player1.dash_ready = 0 | |
if pressed[pygame.K_z] : | |
if Game.player1.hp <= Game.player1.hp_max: | |
Game.player1.hp -=1 | |
if pressed[pygame.K_u] : | |
if Game.player1.hp <= Game.player1.hp_max: | |
Game.player1.hp +=1 | |
Game.player1.hp = min(Game.player1.hp, Game.player1.hp_max) | |
Game.player1.hp = max(Game.player1.hp, 0) | |
# ================ udpate everything ================= | |
#for o in Game.objects: | |
# o.update(milliseconds/1000) | |
# Game.screen.blit(o.image, o.pos) | |
# ------ update player ------ | |
old = pygame.math.Vector2(Game.player1.pos.x, Game.player1.pos.y) | |
Game.player1.update(milliseconds/1000) | |
#------------------- collision detection------------------- | |
# ---- collision beam and block ---- | |
#beam_touching_blocks = pygame.sprite.spritecollide() | |
# --------- collision detection player and powerup ---------- | |
#for p in Game.power_group: | |
# diffvector= p.pos - Game.player1.pos | |
# distance= diffvector.length() | |
# if distance<Game.player1.rect.width / 2: | |
# print (p.effect) | |
# for _ in range (20): | |
# Particle(pos=pygame.Vector2(p.pos.x, p.pos.y)) | |
# Textsprite(pos=pygame.Vector2(Game.player1.pos.x, | |
# Game.player1.pos.y-Game.player1.rect.height/2),text=p.effect) | |
# Game.player1.effects[p.effect]=4.0 | |
# p.kill() | |
powerups_touching_player = pygame.sprite.spritecollide(Game.player1, Game.power_group, False ) | |
for p in powerups_touching_player: | |
for _ in range(20): | |
Particle(pos=pygame.Vector2(p.pos.x, p.pos.y)) | |
Textsprite(pos=pygame.Vector2(Game.player1.pos.x, Game.player1.pos.y-Game.player1.rect.height/2), | |
text=p.effect) | |
Game.player1.effects[p.effect]=4.0 # TODO: different cooldown times for each effect -> dictinary in Game? or Effect class ? | |
p.kill() | |
# ---------- collision detection player and block -> set on_ground, friction ----- | |
playerblocks = pygame.sprite.spritecollide(Game.player1, Game.block_group,False ) | |
left_feet = Game.player1.rect.center + Game.player1.left_feet_vector | |
right_feet = Game.player1.rect.center + Game.player1.right_feet_vector | |
# ---while moving down --- | |
middle = Game.player1.rect.center + Game.player1.mid_vector | |
left_feet_blocks = [block for block in playerblocks if block.player_restrict_from_top and block.rect.collidepoint(left_feet) ] | |
right_feet_blocks = [block for block in playerblocks if block.player_restrict_from_top and block.rect.collidepoint(right_feet)] | |
middle_blocks = [block for block in playerblocks if block.player_restrict_from_top and block.rect.collidepoint(middle)] | |
# ---- while moving up --- | |
top = Game.player1.rect.center + Game.player1.top_vector | |
top_blocks = [block for block in playerblocks if block.player_restrict_from_bottom and block.rect.collidepoint(top) ] | |
# ---- while moving west --- | |
left_top = Game.player1.rect.center + Game.player1.left_top_vector | |
left_top_blocks = [block for block in playerblocks if block.player_restrict_from_right and block.rect.collidepoint(left_top)] | |
left_bottom = Game.player1.rect.center + Game.player1.left_bottom_vector | |
left_bottom_blocks = [block for block in playerblocks if block.player_restrict_from_right and block.rect.collidepoint(left_bottom)] | |
# ---- while moving east ---- | |
right_top = Game.player1.rect.center + Game.player1.right_top_vector | |
right_top_blocks = [block for block in playerblocks if block.player_restrict_from_left and block.rect.collidepoint(right_top)] | |
right_bottom = Game.player1.rect.center + Game.player1.right_bottom_vector | |
right_bottom_blocks = [block for block in playerblocks if block.player_restrict_from_left and block.rect.collidepoint(right_bottom)] | |
#print("collide:", "feet:", len(left_feet_blocks), len(right_feet_blocks),"middle:", len(middle_blocks)) | |
Game.player1.blocktouch( | |
#seconds, | |
left_feet_blocks, | |
right_feet_blocks, | |
middle_blocks, | |
left_top_blocks, | |
left_bottom_blocks, | |
right_top_blocks, | |
right_bottom_blocks, | |
top_blocks, | |
) | |
new = pygame.math.Vector2(Game.player1.pos.x, Game.player1.pos.y) | |
delta = new-old | |
Game.camera -= delta | |
Game.all_group.update(seconds) | |
for sprite in Game.all_group: | |
if sprite.add_cameravector: | |
sprite.rect.center = sprite.pos + Game.camera | |
#for block in playerblocks: | |
# if block.pos.y > Game.player1.pos.y: | |
# if block.player_restricted_from_top: | |
# Game.player1.on_ground = True | |
# Game.player1.pos.y = block.rect.top - Game.player1.rect.height /2 | |
# ================= blit everything =================== | |
# ---- delete everything, blit background- ----------- | |
Game.screen.blit(Game.background, (0, 0)) | |
# ---------- blit platforms ---------- | |
#Game.screen.blit(Game.platforms, (0+Game.camera.x,0+Game.camera.y)) | |
# ---- blit allgroup --- | |
Game.all_group.draw(Game.screen) | |
## --------- blit grid (if enabled) ------ | |
if Game.grid: | |
Game.screen.blit(Game.gridlayer, (-BLOCK_SIZE_X/2+Game.camera.x, -BLOCK_SIZE_Y/2+Game.camera.y)) | |
# ---- blit particles --- | |
#for s in Game.all_group: | |
# if s.add_cameravector: | |
# Game.screen.blit(s.image, (s.pos + Game.camera + pygame.math.Vector2(-s.rect.width/2,-s.rect.height/2))) | |
# else: | |
# Game.screen.blit(s.image, s.pos) # inestead of Game.all_group.draw(Game.screen) | |
##Game.player1.pos = Game.player1.pos + Game.camera | |
##Game.player1.rect.center = Game.player1.pos | |
Game.player1.rect.center = Game.player1.pos + Game.camera | |
# ---------- blit player ------------- | |
#Game.screen.blit(Game.player1.image, Game.player1.pos + pygame.math.Vector2(-Game.player1.rect.width/2, -Game.player1.rect.height/2)+Game.camera) | |
Game.player_group.draw(Game.screen) | |
Game.screen.blit(create_hud(),(0,0)) | |
# ------ flip the screen -------- | |
# pygame.diplay.flip() #??? | |
pygame.display.update() | |
if __name__ == "__main__": | |
prepare() | |
main() | |
pygame.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment