前面完成了飞机和坦克两个经典游戏的基本功能,想挑战一下魂斗罗?这里的难点感觉是玩家的上下左右移动和变换动作,还有跳跃、趴下、射击等,尤其是跳到地面下的各个台阶部分,有点复杂。
下面开始我的魂斗罗1.0的挑战,看看能不能实现。
一、建立一个配置文件Config.py。
这一次将代码想模块化管理,将所有的全局常量、变量都统一归口到这个配置文件中,方便调用。
网络上找到的背景地图图片高度为240像素,这里将游戏窗口设定为720像素高,地图放大3倍。
# Config.py
import pygame
class Constant:
WIDTH = 1200
HEIGHT = 720
FPS = 60
MAP_SCALE = 3
class Variable:
all_sprites = pygame.sprite.Group()
game_start = True
二、建立主循环文件Contra.py
文件主要包括键盘控制、游戏窗口、游戏时钟设定以及主循环
# Contra.py
import sys
import pygame
from Config import Constant, Variable
def control():
for event in pygame.event.get():
if event.type == pygame.QUIT:
Variable.game_start = False
class Main:
def __init__(self):
pygame.init()
self.game_window = pygame.display.set_mode((Constant.WIDTH, Constant.HEIGHT))
self.clock = pygame.time.Clock()
def game_loop(self):
while Variable.game_start:
control()
self.clock.tick(Constant.FPS)
pygame.display.set_caption(f'魂斗罗 1.0 {self.clock.get_fps():.2f}')
pygame.display.update()
pygame.quit()
sys.exit()
if __name__ == '__main__':
main = Main()
main.game_loop()
三、建立ContraMap.py文件:
1、先载入背景地图
素材一共有8个不同阶段地图,按照stage1-8的方式命名,建立一个image_list列表。每通过一关,增加stage值进入下一关地图。
# ContraMap.py
import os
import pygame
from Config import Constant, Variable
class Stage(pygame.sprite.Sprite):
def __init__(self, order):
pygame.sprite.Sprite.__init__(self)
self.image_list = []
self.order = order
for i in range(1, 9):
image = pygame.image.load(os.path.join('image', 'map', 'stage' + str(i) + '.png'))
rect = image.get_rect()
image = pygame.transform.scale(image, (rect.width * Constant.MAP_SCALE, rect.height * Constant.MAP_SCALE))
self.image_list.append(image)
self.image = self.image_list[self.order]
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 0
self.speed = 0
2、地图移动
魂斗罗的背景地图移动相对复杂一点,不是简单的重复移动。
素材中只有stage3是纵向移动的,其它是横向移动的,这里先不管stage3 ,先考虑横向移动方式。
横向移动方式设计为三步:先是一段英文介绍快速左移,然后等待玩家选择界面;玩家选择完后地图再次快速移动进入游戏地图开始正常游戏,此时地图停止移动,等待玩家移动到游戏窗口中间以后再进行移动,先实现前面两步。
在配置文件Config.py中增加一个变量step来控制地图移动,定义update函数实现移动。
def update(self):
if self.order == 2:
print('纵向地图')
else:
if Variable.step == 0 and self.rect.x >= -Constant.WIDTH:
self.rect.x -= 10
if Variable.step == 1 and self.rect.x > -Constant.WIDTH * 2:
self.rect.x -= 10
if self.rect.x == -2400:
Variable.step = 2
if Variable.step == 2:
self.rect.x -= self.speed
3、在ContraMap.py中定义地图载入方法
首先需要在配置文件变量中增加map_switch = True和stage = 1两个变量,用来控制地图载入及地图索引,定义new_stage方法。
def new_stage():
if Variable.switch:
stage_map = Stage(Variable.stage - 1)
Variable.all_sprites.add(stage_map)
Variable.switch = False
修改control方法,这里省略一下,在玩家选择界面直接Enter实现游戏进入。
def control():
for event in pygame.event.get():
if event.type == pygame.QUIT:
Variable.game_start = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
Variable.step = 1
在主循环中通过all_sprites绘制及调用update函数实现移动,这样就实现了地图载入到进入游戏的一个简单过程。
下面准备主角Bill。
四、建立ContraBill.py文件,魂斗罗主角BILL:
1、归纳整理素材图片
这个是网络找到的主角BILL的各动作分解图!
把各个动作进行归纳整理,先搞出第一关的部分吧,第一关的动作一共归纳了9种状态,每种状态里面都通过PS给做成了6种动作分解图片,有的是动作分解,有的就是一样的图片复制一下,比如stand状态,就是6个一样的图片,只是为了方便图片的导入。下面没有归纳到文件夹的图片是其它关使用的,暂时先放着吧。
英文都是百度的大概意思!
2、建立ContraBill.py文件
图片的载入采用了两层for循环,一层是遍历image_dict状态字典的键,根据键来载入对应的图片,一层是前面PS做好的动作分解,因为都做成了6张图片,这里代码就简单了不少,直接一个for i in range(6)就搞定了。
# ContraBill.py
import os
import pygame
from Constant import Constant
class Bill(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_dict = {
'be_hit': [],
'jump': [],
'oblique_down': [],
'oblique_up': [],
'run': [],
'shoot': [],
'stand': [],
'up': []
}
for i in range(6):
for key in self.image_dict:
image = pygame.image.load(os.path.join('image', 'bill', str(key), str(key) + str(i + 1) + '.png'))
rect = image.get_rect()
image_scale = pygame.transform.scale(
image, (rect.width * Constant.MAP_SCALE, rect.height * Constant.MAP_SCALE))
self.image_dict[key].append(image_scale)
self.image_order = 0
self.image_type = 'stand'
self.image = self.image_dict[self.image_type][self.image_order]
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 250
self.direction = 'right'
def update(self):
self.image = self.image_dict[self.image_type][self.image_order]
再定义一个玩家Bill出现的时间及方法。 同样增加一个player_switch,在实例化玩家Bill后关闭。防止重复载入。
def new_player():
bill = Bill()
if Variable.step == 2 and Variable.player_switch:
Variable.all_sprites.add(bill)
Variable.player_switch = False
在主循中调用方法,可以看到玩家按照预定的步骤出现在屏幕上。这样写的玩家产生方法,不知道这样写会不会更麻烦呢?
3、玩家移动:
先简单写一下玩家的左右移动以及方向的改变,在update函数中增加按键监测。
def update(self):
self.image = self.image_dict[self.image_type][self.image_order]
if self.direction == 'left':
self.image = pygame.transform.flip(self.image, True, False)
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_a]:
self.direction = 'left'
self.image_type = 'run'
self.rect.x -= 2
elif key_pressed[pygame.K_d]:
self.direction = 'right'
self.image_type = 'run'
self.rect.x += 2
现在Bill可以左右简单移动。
五、现阶段各部分完整代码:
1、Contra.py现阶段代码:
# Contra.py
import sys
import pygame
import ContraBill
import ContraMap
from Config import Constant, Variable
def control():
for event in pygame.event.get():
if event.type == pygame.QUIT:
Variable.game_start = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
Variable.step = 1
class Main:
def __init__(self):
pygame.init()
self.game_window = pygame.display.set_mode((Constant.WIDTH, Constant.HEIGHT))
self.clock = pygame.time.Clock()
def game_loop(self):
while Variable.game_start:
control()
if Variable.stage == 1:
ContraMap.new_stage()
ContraBill.new_player()
if Variable.stage == 2:
pass
if Variable.stage == 3:
pass
Variable.all_sprites.draw(self.game_window)
Variable.all_sprites.update()
print(Variable.all_sprites)
self.clock.tick(Constant.FPS)
pygame.display.set_caption(f'魂斗罗 1.0 {self.clock.get_fps():.2f}')
pygame.display.update()
pygame.quit()
sys.exit()
if __name__ == '__main__':
main = Main()
main.game_loop()
2、ContraMap.py现阶段代码:
# ContraMap.py
import os
import pygame
from Config import Constant, Variable
class StageMap(pygame.sprite.Sprite):
def __init__(self, order):
pygame.sprite.Sprite.__init__(self)
self.image_list = []
self.order = order
for i in range(1, 9):
image = pygame.image.load(os.path.join('image', 'map', 'stage' + str(i) + '.png'))
rect = image.get_rect()
image = pygame.transform.scale(image, (rect.width * Constant.MAP_SCALE, rect.height * Constant.MAP_SCALE))
self.image_list.append(image)
self.image = self.image_list[self.order]
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 0
self.speed = 0
def update(self):
if self.order == 2:
print('纵向地图')
else:
if Variable.step == 0 and self.rect.x >= -Constant.WIDTH:
self.rect.x -= 10
if Variable.step == 1 and self.rect.x > -Constant.WIDTH * 2:
self.rect.x -= 10
if self.rect.x == -2400:
Variable.step = 2
if Variable.step == 2:
self.rect.x -= self.speed
def new_stage():
if Variable.map_switch:
stage_map = StageMap(Variable.stage - 1)
Variable.all_sprites.add(stage_map)
Variable.map_switch = False
3、ContraBill.py现阶段代码:
# ContraBill.py
import os
import pygame
from Config import Constant, Variable
class Bill(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_dict = {
'be_hit': [],
'jump': [],
'oblique_down': [],
'oblique_up': [],
'run': [],
'shoot': [],
'stand': [],
'up': []
}
for i in range(6):
for key in self.image_dict:
image = pygame.image.load(os.path.join('image', 'bill', str(key), str(key) + str(i + 1) + '.png'))
rect = image.get_rect()
image_scale = pygame.transform.scale(
image, (rect.width * Constant.MAP_SCALE, rect.height * Constant.MAP_SCALE))
self.image_dict[key].append(image_scale)
self.image_order = 0
self.image_type = 'stand'
self.image = self.image_dict[self.image_type][self.image_order]
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 250
self.direction = 'right'
def update(self):
self.image = self.image_dict[self.image_type][self.image_order]
if self.direction == 'left':
self.image = pygame.transform.flip(self.image, True, False)
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_a]:
self.direction = 'left'
self.image_type = 'run'
self.rect.x -= 2
elif key_pressed[pygame.K_d]:
self.direction = 'right'
self.image_type = 'run'
self.rect.x += 2
def new_player():
bill = Bill()
if Variable.step == 2 and Variable.player_switch:
Variable.all_sprites.add(bill)
Variable.player_switch = False
4、Config.py现阶段代码:
# Config.py
import pygame
class Constant:
WIDTH = 1200
HEIGHT = 720
FPS = 60
MAP_SCALE = 3
class Variable:
all_sprites = pygame.sprite.Group()
game_start = True
map_switch = True
player_switch = True
stage = 1
step = 0