Skip to content

Instantly share code, notes, and snippets.

@horstjens
Last active August 11, 2022 09:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save horstjens/134cb708fda4ea34733c6a20586cc644 to your computer and use it in GitHub Desktop.
Save horstjens/134cb708fda4ea34733c6a20586cc644 to your computer and use it in GitHub Desktop.
pygame platformer ( data folder not included)
#!/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