用pygame做了一个2D横板格斗游戏。
代码是自己写,图片是网上找的,角色的动画是用开源软件LPC character gen0.9生成的。
就是幻影剑舞。当然这个图片仅仅用来做游戏开发学习,不商用!
背景图片是用下面这张图片利用移位和循环播放效果制作成动态的游戏背景,看起来像是不断有月球经过。
# 更新背景
background_x_1 -= background_speed
background_x_2 -= background_speed
if background_x_1 <= -background_width:
background_x_1 = background_width
if background_x_2 <= -background_width:
background_x_2 = background_width
# 绘制背景
win.blit(background_image, (background_x_1, 0))
win.blit(background_image, (background_x_2, 0))
有两个游戏音乐特效,一个是背景音乐,一个是游戏结束提示音。
一共两个类, fight类和Boss类。
class Boss:
def __init__(self, x, y, idle_images, walk_images, attack_images, defend_images=None):
self.x = x
self.y = y
self.idle_images = idle_images
self.walk_images = walk_images
self.attack_images = attack_images
self.defend_images = defend_images
self.current_images = idle_images
self.image_index = 0
self.image = self.current_images[self.image_index]
self.vel = 5
self.health = 100
self.is_jump = False
self.jump_count = 10
self.attacking = False
self.attack_start_time = 0
self.attack_duration = 300
self.attack_range = pygame.Rect(self.x, self.y, self.image.get_width() + 20, self.image.get_height())
self.attack_objects = []
self.state = FighterState.IDLE
self.animation_speed = 100
self.last_update = pygame.time.get_ticks()
self.is_defending = False # 新增防御状态标志
self.defend_images = defend_images
def draw(self, win):
if self.attacking and pygame.time.get_ticks() - self.attack_start_time < self.attack_duration:
win.blit(self.attack_images[self.image_index], (self.x, self.y))
elif self.is_defending and self.defend_images:
win.blit(self.defend_images[self.image_index], (self.x, self.y))
else:
win.blit(self.image, (self.x, self.y))
for obj in self.attack_objects:
obj.draw(win)
def move(self, keys):
if keys[pygame.K_LEFT] and self.x > 0:
self.x -= self.vel
self.state = FighterState.MOVING_LEFT
elif keys[pygame.K_RIGHT] and self.x < WIDTH - self.image.get_width():
self.x += self.vel
self.state = FighterState.MOVING_RIGHT
else:
self.state = FighterState.IDLE
if not self.is_jump:
if keys[pygame.K_UP]:
self.is_jump = True
self.state = FighterState.JUMPING
else:
if self.jump_count >= -10:
neg = 1
if self.jump_count < 0:
neg = -1
self.y -= (self.jump_count ** 2) * 0.5 * neg
self.jump_count -= 1
else:
self.is_jump = False
self.jump_count = 10
if keys[pygame.K_d]:
self.state = FighterState.DEFENDING
self.is_defending = True
if self.defend_images:
self.current_images = self.defend_images
else:
if not (keys[pygame.K_LEFT] or keys[pygame.K_RIGHT] or keys[pygame.K_UP]):
self.state = FighterState.IDLE
self.is_defending = False
self.current_images = self.idle_images
def attack(self, opponent):
if self.attacking and pygame.time.get_ticks() - self.attack_start_time < self.attack_duration:
if self.attack_range.colliderect(opponent.rect()) and opponent.is_defending == False:
opponent.health -= 10
def release_attack_object(self):
self.attacking = True
self.attack_start_time = pygame.time.get_ticks()
obj = AttackObject(self.x, self.y + self.image.get_height() // 2)
self.attack_objects.append(obj)
def rect(self):
return pygame.Rect(self.x, self.y, self.image.get_width(), self.image.get_height())
def update_attack_objects(self, opponent):
for obj in self.attack_objects[:]:
obj.move()
if obj.off_screen():
self.attack_objects.remove(obj)
elif obj.collide(opponent) and opponent.is_defending == False:
opponent.health -= 10
self.attack_objects.remove(obj)
def update_animation(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.animation_speed:
self.last_update = now
if self.state == FighterState.IDLE:
self.current_images = self.idle_images
elif self.state == FighterState.MOVING_LEFT or self.state == FighterState.MOVING_RIGHT:
self.current_images = self.walk_images
elif self.state == FighterState.ATTACKING:
self.current_images = self.attack_images
elif self.state == FighterState.DEFENDING and self.defend_images:
self.current_images = self.defend_images
self.image_index = (self.image_index + 1) % len(self.current_images)
self.image = self.current_images[self.image_index]
class Fighter:
def __init__(self, x, y, idle_images, walk_images, attack_images, defend_images=None):
self.x = x
self.y = y
self.idle_images = idle_images
self.walk_images = walk_images
self.attack_images = attack_images
self.defend_images = defend_images
self.current_images = idle_images
self.image_index = 0
self.image = self.current_images[self.image_index]
self.vel = 5
self.health = 100
self.is_jump = False
self.jump_count = 10
self.attacking = False
self.attack_start_time = 0
self.attack_duration = 300
self.attack_range = pygame.Rect(self.x, self.y, self.image.get_width() + 20, self.image.get_height())
self.attack_objects = []
self.state = FighterState.IDLE
self.animation_speed = 100
self.last_update = pygame.time.get_ticks()
self.is_defending = False # 新增防御状态标志
self.defend_images = defend_images
def draw(self, win):
if self.attacking and pygame.time.get_ticks() - self.attack_start_time < self.attack_duration:
win.blit(self.attack_images[self.image_index], (self.x, self.y))
elif self.is_defending and self.defend_images:
win.blit(self.defend_images[self.image_index], (self.x, self.y))
else:
win.blit(self.image, (self.x, self.y))
for obj in self.attack_objects:
obj.draw(win)
def move(self, keys):
if keys[pygame.K_LEFT] and self.x > 0:
self.x -= self.vel
self.state = FighterState.MOVING_LEFT
elif keys[pygame.K_RIGHT] and self.x < WIDTH - self.image.get_width():
self.x += self.vel
self.state = FighterState.MOVING_RIGHT
else:
self.state = FighterState.IDLE
if not self.is_jump:
if keys[pygame.K_UP]:
self.is_jump = True
self.state = FighterState.JUMPING
else:
if self.jump_count >= -10:
neg = 1
if self.jump_count < 0:
neg = -1
self.y -= (self.jump_count ** 2) * 0.5 * neg
self.jump_count -= 1
else:
self.is_jump = False
self.jump_count = 10
if keys[pygame.K_d]:
self.state = FighterState.DEFENDING
self.is_defending = True
if self.defend_images:
self.current_images = self.defend_images
else:
if not (keys[pygame.K_LEFT] or keys[pygame.K_RIGHT] or keys[pygame.K_UP]):
self.state = FighterState.IDLE
self.is_defending = False
self.current_images = self.idle_images
def attack(self, opponent):
if self.attacking and pygame.time.get_ticks() - self.attack_start_time < self.attack_duration:
if self.attack_range.colliderect(opponent.rect()) and opponent.is_defending == False:
opponent.health -= 10
def release_attack_object(self):
self.attacking = True
self.attack_start_time = pygame.time.get_ticks()
obj = fight1_AttackObject(self.x, self.y + self.image.get_height() // 4)
self.attack_objects.append(obj)
def rect(self):
return pygame.Rect(self.x, self.y, self.image.get_width(), self.image.get_height())
def update_attack_objects(self, opponent):
for obj in self.attack_objects[:]:
obj.move()
if obj.off_screen():
self.attack_objects.remove(obj)
elif obj.collide(opponent) and opponent.is_defending == False:
opponent.health -= 10
self.attack_objects.remove(obj)
def update_animation(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.animation_speed:
self.last_update = now
if self.state == FighterState.IDLE:
self.current_images = self.idle_images
elif self.state == FighterState.MOVING_LEFT or self.state == FighterState.MOVING_RIGHT:
self.current_images = self.walk_images
elif self.state == FighterState.ATTACKING:
self.current_images = self.attack_images
elif self.state == FighterState.DEFENDING and self.defend_images:
self.current_images = self.defend_images
self.image_index = (self.image_index + 1) % len(self.current_images)
self.image = self.current_images[self.image_index]
实现功能如下:
左边的角色1 可以 左右移动,上下跳跃(通过键盘上的方向键)。
左边的角色1 可以发动远程攻击,类似剑气一样, 攻击的时候会触发动画切换(其实就是切换图片)。
右边的角色2 可以发动冲击波攻击。
左边的角色1按下D键可以进入防御状态
防御状态下受到攻击生命值不会受到削减。
每个角色上面都有一个血条值,受到一次攻击会削减10%,任何一个角色的生命值为0时游戏结束。
# 游戏主循环
def main():
# 加载资源
fighter1_defend = [load_image("backgr/defend.png", alpha=True)]
background_image = load_image("backgr/backgr77.JPG")
background_width = background_image.get_width()
background_x_1 = 0
background_x_2 = background_width
background_speed = 2
fighter1_idle = [load_image("backgr/fighterd1.png", alpha=True)]
fighter1_walk = [load_image("backgr/left.PNG", alpha=True), load_image("backgr/right.PNG", alpha=True)]
fighter1_attack = [load_image("backgr/attack.png", alpha=True)]
fighter2_idle = [load_image("backgr/fighter2222.png", alpha=True)]
fighter2_walk = [load_image("backgr/evilrun.PNG", alpha=True), load_image("backgr/evilrun2.PNG", alpha=True)]
fighter2_attack = [load_image("backgr/fighter2_attack.png", alpha=True)]
# 创建角色
fighter1 = Fighter(100, 400, fighter1_idle, fighter1_walk, fighter1_attack, fighter1_defend)
fighter2 = Boss(700, 400, fighter2_idle, fighter2_walk, fighter2_attack)
# 字体设置
font = pygame.font.Font(None, 74)
# 游戏主循环
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
# 更新背景
background_x_1 -= background_speed
background_x_2 -= background_speed
if background_x_1 <= -background_width:
background_x_1 = background_width
if background_x_2 <= -background_width:
background_x_2 = background_width
# 绘制背景
win.blit(background_image, (background_x_1, 0))
win.blit(background_image, (background_x_2, 0))
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
# fighter1.attacking = True
fighter1.release_attack_object()
if event.key == pygame.K_RETURN:
fighter2.release_attack_object()
# 获取按键状态
keys = pygame.key.get_pressed()
# 角色移动和攻击
fighter1.move(keys)
fighter1.attack(fighter2)
fighter2.attack(fighter1)
fighter1.update_attack_objects(fighter2)
fighter2.update_attack_objects(fighter1)
# 更新动画
fighter1.update_animation()
fighter2.update_animation()
# 绘制角色
fighter1.draw(win)
fighter2.draw(win)
# 绘制生命值条
pygame.draw.rect(win, RED, (10, 10, 100, 20))
pygame.draw.rect(win, YELLOW, (10, 10, fighter1.health, 20))
pygame.draw.rect(win, RED, (WIDTH - 110, 10, 100, 20))
pygame.draw.rect(win, YELLOW, (WIDTH - 110, 10, fighter2.health, 20))
# 检查游戏结束
if fighter1.health <= 0 or fighter2.health <= 0:
text = font.render("Game Over", True, RED)
game_over_sound.play()
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
win.blit(text, text_rect)
# 更新显示
pygame.display.update()
# 退出 Pygame
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()