diff --git a/pythonProject1/source/__pycache__/constants.cpython-312.pyc b/pythonProject1/source/__pycache__/constants.cpython-312.pyc index f8b736d..23bfdf9 100644 Binary files a/pythonProject1/source/__pycache__/constants.cpython-312.pyc and b/pythonProject1/source/__pycache__/constants.cpython-312.pyc differ diff --git a/pythonProject1/source/components/__pycache__/box.cpython-312.pyc b/pythonProject1/source/components/__pycache__/box.cpython-312.pyc index df30de2..d5ef2b0 100644 Binary files a/pythonProject1/source/components/__pycache__/box.cpython-312.pyc and b/pythonProject1/source/components/__pycache__/box.cpython-312.pyc differ diff --git a/pythonProject1/source/components/__pycache__/brick.cpython-312.pyc b/pythonProject1/source/components/__pycache__/brick.cpython-312.pyc index cb96e72..6ac6ef3 100644 Binary files a/pythonProject1/source/components/__pycache__/brick.cpython-312.pyc and b/pythonProject1/source/components/__pycache__/brick.cpython-312.pyc differ diff --git a/pythonProject1/source/components/__pycache__/enemy.cpython-312.pyc b/pythonProject1/source/components/__pycache__/enemy.cpython-312.pyc index 309eb6e..50b6c4e 100644 Binary files a/pythonProject1/source/components/__pycache__/enemy.cpython-312.pyc and b/pythonProject1/source/components/__pycache__/enemy.cpython-312.pyc differ diff --git a/pythonProject1/source/components/__pycache__/powerup.cpython-312.pyc b/pythonProject1/source/components/__pycache__/powerup.cpython-312.pyc new file mode 100644 index 0000000..b04221d Binary files /dev/null and b/pythonProject1/source/components/__pycache__/powerup.cpython-312.pyc differ diff --git a/pythonProject1/source/components/__pycache__/stuff.cpython-312.pyc b/pythonProject1/source/components/__pycache__/stuff.cpython-312.pyc index 3100472..bc0bb79 100644 Binary files a/pythonProject1/source/components/__pycache__/stuff.cpython-312.pyc and b/pythonProject1/source/components/__pycache__/stuff.cpython-312.pyc differ diff --git a/pythonProject1/source/components/box.py b/pythonProject1/source/components/box.py index 177bc7d..9d07d8c 100644 --- a/pythonProject1/source/components/box.py +++ b/pythonProject1/source/components/box.py @@ -1,13 +1,17 @@ import pygame from .. import tools,setup from .. import constants as C +from .powerup import create_powerup + class Box(pygame.sprite.Sprite): - def __init__(self, x, y,box_type): + def __init__(self, x, y,box_type,group,name = 'box'): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.box_type = box_type + self.group = group + self.name = name self.frame_rects = [ (384,0,16,16), (400,0,16,16), @@ -24,3 +28,50 @@ class Box(pygame.sprite.Sprite): self.rect = self.image.get_rect() self.rect.x = self.x self.rect.y = self.y # 设置坐标与图片 + self.gravity = C.GRAVITY + + self.state = 'rest' + self.timer = 0 + + def update(self): + self.current_time = pygame.time.get_ticks() + self.handel_states() + + def handel_states(self): + if self.state == 'rest': + self.rest() + elif self.state == 'bumped': + self.bumped() + elif self.state == 'open': + self.open() + + + def rest(self): + frame_durations = [400,100,100,50] + if self.current_time - self.timer > frame_durations[self.frame_index]: + self.frame_index = (self.frame_index + 1) % 4 + self.timer = self.current_time + self.image = self.frames[self.frame_index] + + def go_bumped(self): + self.y_vel = -7 + self.state = 'bumped' + + def bumped(self): + self.rect.y += self.y_vel + self.y_vel += self.gravity + self.frame_index = 3 + self.image = self.frames[self.frame_index] + + if self.rect.y > self.y + 10 : # 如果大于初始位置回弹 + self.rect.y = self.y + self.state = 'open' + + if self.box_type == 1: + pass + else: + self.group.add(create_powerup(self.rect.centerx,self.rect.centery,self.box_type)) + + + def open(self): + pass diff --git a/pythonProject1/source/components/brick.py b/pythonProject1/source/components/brick.py index 50463ce..d3e17f1 100644 --- a/pythonProject1/source/components/brick.py +++ b/pythonProject1/source/components/brick.py @@ -1,13 +1,16 @@ import pygame from .. import tools,setup from .. import constants as C +from .powerup import create_powerup class Brick(pygame.sprite.Sprite): - def __init__(self, x, y,brick_type,color=None): + def __init__(self, x, y,brick_type,group,color=None,name = 'brick'): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y - self.type = brick_type + self.brick_type = brick_type + self.group = group + self.name = name bright_frame_rects = [(16,0,16,16),(48,0,16,16)] dark_frame_rects = [(16,32,16,16),(48,32,16,16)] # 抠图 @@ -25,3 +28,45 @@ class Brick(pygame.sprite.Sprite): self.rect = self.image.get_rect() self.rect.x = self.x self.rect.y = self.y # 设置坐标与图片 + self.state = 'rest' + self.gravity = C.GRAVITY + + + def update(self): + self.current_time = pygame.time.get_ticks() + self.handel_states() + + def handel_states(self): + if self.state == 'rest': + self.rest() + elif self.state == 'bumped': + self.bumped() + elif self.state == 'open': + self.open() + + + def rest(self): + pass + + def go_bumped(self): + self.y_vel = -7 + self.state = 'bumped' + + def bumped(self): + self.rect.y += self.y_vel + self.y_vel += self.gravity + + if self.rect.y > self.y + 10 : # 如果大于初始位置回弹 + self.rect.y = self.y + + if self.brick_type == 0: + self.state = 'rest' + elif self.brick_type == 1: + self.state = 'open' + else: + self.group.add(create_powerup(self.rect.centerx,self.rect.centery,self.brick_type)) + + def open(self): + self.frame_index = 1 + self.image = self.frames[self.frame_index] + diff --git a/pythonProject1/source/components/enemy.py b/pythonProject1/source/components/enemy.py index 70f9efe..27a0b3f 100644 --- a/pythonProject1/source/components/enemy.py +++ b/pythonProject1/source/components/enemy.py @@ -31,6 +31,10 @@ class Enemy(pygame.sprite.Sprite): self.rect.bottom = y_bottom self.timer = 0 + self.x_vel = -1 * C.ENEMY_SPEED if self.direction == 0 else C.ENEMY_SPEED + self.y_vel = 0 + self.gravity = C.GRAVITY + self.state = 'walk' def load_frames(self,frame_rects): for frame_rect in frame_rects: @@ -39,13 +43,101 @@ class Enemy(pygame.sprite.Sprite): self.left_frames.append(left_frame) self.right_frames.append(right_frame) - def update(self): + def update(self,level): self.current_time = pygame.time.get_ticks() + self.handle_states() + self.update_position(level) + + def handle_states(self): # 状态检测 + + if self.state == 'walk': + self.walk() + elif self.state == 'fall': + self.fall() + elif self.state == 'die': + self.die() + elif self.state == 'trampled': + self.trampled() + elif self.state == 'slide': + self.slide() + + if self.direction: + self.image = self.right_frames[self.frame_index] + else: + self.image = self.left_frames[self.frame_index] + + def walk(self): if self.current_time - self.timer > 125: self.frame_index = (self.frame_index + 1) % 2 self.image = self.frames[self.frame_index] self.timer = self.current_time + def fall(self): + if self.y_vel < 10: + self.y_vel += self.gravity + + def die(self): + self.rect.x += self.x_vel + self.rect.y += self.y_vel + self.y_vel += self.gravity + if self.rect.y > C.SCREEN_H: + self.kill() + + def trampled(self): + pass + + def slide(self): + pass + + def update_position(self,level): + self.rect.x += self.x_vel + self.check_x_collisions(level) + self.rect.y += self.y_vel + if self.state != 'die': + self.check_y_collisions(level) + + def check_x_collisions(self,level): + sprite = pygame.sprite.spritecollideany(self,level.ground_items_group) + if sprite: + if self.direction: # 向右 + self.direction = 0 + self.rect.right = sprite.rect.left + else: + self.direction = 1 + self.rect.left = sprite.rect.right + self.x_vel *= -1 + + if self.state == 'slide': + enemy = pygame.sprite.spritecollideany(self,level.enemy_group) + if enemy: + enemy.go_die(how='slided') + level.enemy_group.remove(enemy) + level.dying_group.add(enemy) + + def check_y_collisions(self,level): + check_group = pygame.sprite.Group(level.ground_items_group,level.box_group,level.brick_group) + sprite = pygame.sprite.spritecollideany(self,check_group) + if sprite: + if self.rect.top < sprite.rect.top: + self.rect.bottom = sprite.rect.top + self.y_vel = 0 + self.state = 'walk' + + level.check_will_fall(self) + + + def go_die(self,how): + self.death_timer = self.current_time + if how in ['bumped','slide']: + self.y_vel = -8 + self.gravity = 0.6 + self.state = 'die' + self.frame_index = 2 + elif how == 'trampled': + self.state = 'trampled' + + + class Goomba(Enemy): def __init__(self, x, y_bottom, direction, name, color): @@ -59,6 +151,14 @@ class Goomba(Enemy): Enemy.__init__(self,x,y_bottom,direction,name,frame_rects) + def trampled(self): + self.x_vel = 0 + self.frame_index = 2 + if self.death_timer == 0: + self.death_timer = self.current_time + if self.current_time - self.death_timer > 500: + self.kill() + class Koopa(Enemy): def __init__(self, x, y_bottom, direction, name, color): @@ -71,3 +171,11 @@ class Koopa(Enemy): frame_rects = dark_frame_rects Enemy.__init__(self,x, y_bottom, direction, name, frame_rects) + + + def trampled(self): + self.x_vel = 0 + self.frame_index = 2 # 缩头乌龟 + + def slide(self): + pass diff --git a/pythonProject1/source/components/powerup.py b/pythonProject1/source/components/powerup.py index e69de29..47deb3a 100644 --- a/pythonProject1/source/components/powerup.py +++ b/pythonProject1/source/components/powerup.py @@ -0,0 +1,27 @@ +import pygame +from .. import tools,setup +from .. import constants as C + + +def create_powerup(centerx,centery,type): # 根据状态出现道具 + """create powerup based on type and mario state""" + return Mushroom(centerx,centery) + +class PowerUp(pygame.sprite.Sprite): + def __init__(self,centerx,rentery,frame_rects): + pygame.sprite.Sprite.__init__(self) + + self.frames = [] + self.frame_index = 0 + for frame_rect in frame_rects: + self.frames.append(tools.get_image(setup.GRAPHICS['item_objects'],*frame_rect,(0,0,0),2.5)) + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = centerx + self.rect.centery = rentery + +class Mushroom(PowerUp): + def __init__(self,centerx,centery): + PowerUp.__init__(self,centerx,centery,[(0,0,16,16)]) + + diff --git a/pythonProject1/source/components/stuff.py b/pythonProject1/source/components/stuff.py index 2a4c633..d317af6 100644 --- a/pythonProject1/source/components/stuff.py +++ b/pythonProject1/source/components/stuff.py @@ -9,3 +9,8 @@ class Item(pygame.sprite.Sprite): self.rect.y = y self.name = name +class Checkpoint(Item): + def __init__(self,x,y,w,h,checkpoint_type,enemy_groupid=None,name='checkpoint'): + Item.__init__(self,x,y,w,h,name) + self.checkpoint_type = checkpoint_type + self.enemy_groupid = enemy_groupid \ No newline at end of file diff --git a/pythonProject1/source/constants.py b/pythonProject1/source/constants.py index 1cdfe7c..90165c0 100644 --- a/pythonProject1/source/constants.py +++ b/pythonProject1/source/constants.py @@ -6,6 +6,7 @@ BG_MULTI = 2.68 PLAYER_MULTI = 2.9 BRICK_MULTI = 2.69 ENEMY_MULTI = 2.5 +ENEMY_SPEED = 1 GRAVITY = 1.0 ANTI_GRAVITY = 0.3 diff --git a/pythonProject1/source/states/__pycache__/level.cpython-312.pyc b/pythonProject1/source/states/__pycache__/level.cpython-312.pyc index 43d1d7d..e7be3b9 100644 Binary files a/pythonProject1/source/states/__pycache__/level.cpython-312.pyc and b/pythonProject1/source/states/__pycache__/level.cpython-312.pyc differ diff --git a/pythonProject1/source/states/level.py b/pythonProject1/source/states/level.py index e14a223..b1c9563 100644 --- a/pythonProject1/source/states/level.py +++ b/pythonProject1/source/states/level.py @@ -19,6 +19,7 @@ class Level: self.setup_ground_items() self.setup_bricks_and_boxes() self.setup_enemies() + self.setup_checkpoints() def load_map_data(self): file_name = 'level_1.json' @@ -60,24 +61,37 @@ class Level: def setup_bricks_and_boxes(self): self.brick_group = pygame.sprite.Group() self.box_group = pygame.sprite.Group() + self.coin_group = pygame.sprite.Group() + self.powerup_group = pygame.sprite.Group() if'brick' in self.map_data: for brick_data in self.map_data['brick']: # 遍历得到x,y坐标以及type x,y = brick_data['x'],brick_data['y'] brick_type = brick_data['type'] - if 'brick_num' in brick_data: - #TODO Batch bricks - pass + if brick_type == 0: + if 'brick_num' in brick_data: + #TODO Batch bricks + pass + else: + self.brick_group.add(brick.Brick(x, y, brick_type,None)) + elif brick_type == 1: + self.brick_group.add(brick.Brick(x, y, brick_type, self.coin_group)) else: - self.brick_group.add(brick.Brick(x,y,brick_type)) + self.brick_group.add(brick.Brick(x, y, brick_type, self.powerup_group)) if 'box' in self.map_data: for box_data in self.map_data['box']: # 遍历得到x,y坐标以及type x, y = box_data['x'], box_data['y'] box_type = box_data['type'] - self.brick_group.add(box.Box(x, y, box_type)) + if box_type == 1: + self.box_group.add(box.Box(x, y, box_type, self.coin_group)) + else: + self.box_group.add(box.Box(x, y, box_type,self.powerup_group)) def setup_enemies(self): + self.dying_group = pygame.sprite.Group() + self.shell_group = pygame.sprite.Group() + self.enemy_group = pygame.sprite.Group() # 初始化 self.enemy_group_dict = {} for enemy_group_data in self.map_data['enemy']: # 字典存放 group = pygame.sprite.Group() @@ -86,6 +100,14 @@ class Level: group.add(enemy.create_enemy(enemy_data)) # 调用 ce self.enemy_group_dict[enemy_group_id] = group + def setup_checkpoints(self): + self.checkpoint_group = pygame.sprite.Group() + for item in self.map_data['checkpoint']: + x,y,w,h = item['x'],item['y'],item['width'],item['height'] + checkpoint_type = item['type'] + enemy_groupid = item.get('enemy_groupid') + self.checkpoint_group.add(stuff.Checkpoint(x,y,w,h,checkpoint_type,enemy_groupid)) + def update(self, surface, keys): @@ -100,13 +122,17 @@ class Level: else: self.update_player_position() + self.check_checkpoints() self.check_if_go_die() self.update_game_window() self.info.update() self.brick_group.update() self.box_group.update() - for enemy_group in self.enemy_group_dict.values(): - enemy_group.update() + self.enemy_group.update(self) + self.dying_group.update(self) + self.shell_group.update(self) + self.coin_group.update() + self.box_group.update() self.draw(surface) @@ -122,8 +148,11 @@ class Level: self.check_x_collisions() # y判定 - self.player.rect.y += self.player.y_vel - self.check_y_collisions() + if not self.player.dead: + self.player.rect.y += self.player.y_vel + self.check_y_collisions() + + def check_x_collisions(self): # x方向 check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group) @@ -131,12 +160,64 @@ class Level: if collided_sprite: self.adjust_player_x(collided_sprite) + enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group) + if enemy: + self.player.go_die() + + shell = pygame.sprite.spritecollideany(self.player, self.shell_group) # 碰撞检测 + if shell: + if shell.state == 'slide': + self.player.go_die() + else: + if self.player.rect.x < shell.rect.x: # 龟壳弹开方向以及速度 + shell.x_vel = 10 + shell.rect.x += 40 + shell.direction = 1 + else: + shell.x_vel = -10 + shell.rect.x -= 40 + shell.direction = 0 + shell.state = 'slide' + def check_y_collisions(self): # y方向 - check_group = pygame.sprite.Group(self.ground_items_group, self.brick_group,self.box_group) - collided_sprite = pygame.sprite.spritecollideany(self.player, check_group) # 检测碰撞 - if collided_sprite: - self.adjust_player_y( collided_sprite) + ground_item = pygame.sprite.spritecollideany(self.player,self.ground_items_group) + brick = pygame.sprite.spritecollideany(self.player, self.brick_group) + box = pygame.sprite.spritecollideany(self.player, self.box_group) + enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group) + + if brick and box: # 判断离哪个砖块进哪个顶起 + to_brick = abs(self.player.rect.centerx - brick.rect.centerx) + to_box = abs(self.player.rect.centerx - box.rect.centerx) + if to_brick > to_box: + brick = None + else: + box = None + + + if ground_item: + self.adjust_player_y(ground_item) + elif brick: + self.adjust_player_y(brick) + elif box: + self.adjust_player_y(box) + + elif enemy: + self.enemy_group.remove(enemy) + if enemy.name == 'koopa': + self.shell_group.add(enemy) # 龟壳状态进龟壳组 + else: + self.dying_group.add(enemy) # 由敌人组 → 死亡组 + + if self.player.y_vel < 0: + how = 'bumped' + else: + how = 'trampled' + self.player.state = 'jump' + self.player.rect.bottom = enemy.rect.top + self.player.y_vel = self.player.jump_vel * 0.8 # 力道 + enemy.go_die(how) + self.check_will_fall(self.player) # 坠落检测 def adjust_player_x(self,sprite): @@ -156,11 +237,18 @@ class Level: self.player.rect.top = sprite.rect.bottom self.player.state = 'fall' + if sprite.name == 'box': + if sprite.state == 'rest': + sprite.go_bumped() + if sprite.name == 'brick': + if sprite.state == 'rest': + sprite.go_bumped() + def check_will_fall(self,sprite): sprite.rect.y += 1 # 下落一个像素 check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group) # 检测是否碰撞 collided_sprite = pygame.sprite.spritecollideany(sprite,check_group) - if not collided_sprite and sprite.state != 'jump' : # 如果没有或者处于跳起则不坠落 + if not collided_sprite and sprite.state != 'jump': # 如果没有或者处于跳起则不坠落 sprite.state = 'fall' sprite.rect.y -= 1 @@ -175,12 +263,24 @@ class Level: self.game_ground.blit(self.player.image,self.player.rect) # 将背景与人物化境 self.brick_group.draw(self.game_ground) self.box_group.draw(self.game_ground) - for enemy_group in self.enemy_group_dict.values(): - enemy_group.draw(self.game_ground) + self.enemy_group.draw(self.game_ground) + self.dying_group.draw(self.game_ground) + self.shell_group.draw(self.game_ground) + self.coin_group.draw(self.game_ground) + self.powerup_group.draw(self.game_ground) surface.blit(self.game_ground,(0,0), self.game_window) # 渲染 self.info.draw(surface) + def check_checkpoints(self): + checkpoint = pygame.sprite.spritecollideany(self.player,self.checkpoint_group) + if checkpoint: + if checkpoint.checkpoint_type == 0: # checkpoint for enemy appearance # 如果检查点类型为0 释放对应敌人 + self.enemy_group.add(self.enemy_group_dict[str(checkpoint.enemy_groupid)]) # 一旦某个点被触发就把对应敌人放入此组 + checkpoint.kill() # 被触碰后消失 + + + def check_if_go_die(self): if self.player.rect.y > C.SCREEN_H: self.player.go_die() # 判断是否掉落屏幕外