超级玛丽的 python 实现

这篇博客详细介绍了如何使用Python的Pygame库实现超级玛丽游戏,包括角色的导入、落地跳跃移动、动作图片切换、背景滚动、项目重构、碰撞检测等关键步骤。作者分享了在实现过程中遇到的问题及解决方案,并提供了部分代码示例和资源链接。
摘要由CSDN通过智能技术生成

超级玛丽的 python 实现

在经过三四天的摸索,参考了Github上的一个大神的代码的前提下,也算是初步搭建起了自己的超级玛丽,下面就给大家分享一些自己踩的坑。

这里是Github上大神的代码,对超级玛丽的第一关进行了很好的还原。

推荐一下Github上一个pygame的游戏仓库

推荐一本《python和pygame游戏开发指南》,想要深入研究的朋友可以去翻阅一下

关于 pygame 模块可以查看官方文档,这里还推荐一下CSDN上的中译版,毕竟官网的配色比较靓眼。或者看我前一篇博客的大致整理。这里移步个人博客

在开始之前你需要:

  • 掌握 python 的基本语法
  • 熟悉 pygame 模块的基本使用

由于pygame游戏的基本入门在之前一篇博客中有见过这里就不再赘述

1. 画面和角色的导入

创建屏幕、从图片中导入Mario

# 屏幕创建和初始化参数 
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.rect = self.screen.get_rect()
pygame.display.set_caption(TITLE)
# 加载关卡图片
self.background = load_image('level.png')
self.back_rect = self.background.get_rect()
	# 这里载入图片需要乘上特定的系数来适配屏幕的尺寸
self.background = pygame.transform.scale(self.background,
                                     (int(self.back_rect.width * BACKGROUND_SIZE),
                                      int(self.back_rect.height * BACKGROUND_SIZE))).convert()
# 导入Mario
self.sheet = load_image('mario.png')
	# 这里由于Mario会有奔跑和跳跃的速度,所以需要导入一整张图片再裁剪使用。
self.load_from_sheet()
	# 初始化角色的一些基本常量
self.rect = self.image.get_rect()
self.pos = vec(WIDTH * 0.5, GROUND_HEIGHT - 70)
self.vel = vec(0, 0)
self.acc = vec(0, 0)

2. 角色的落地、跳跃和移动

在这之前要解决一下Mario如何才能站在我们定义的地面上

self.acc = vec(0, GRAVITY)
if GROUND_HEIGHT < self.mario.pos.y:
    # 如果Mario低于我们定义的地面,就之间将他的所有速度加速度都置零,之间放在我们的地面上
    # 如果速度和加速度不值零,可能会出现Mario卡在地面上抖动的情况,由于y值的不断变化
    self.mario.acc.y = 0
    self.mario.vel.y = 0
    self.mario.pos.y = self.ground_collide.rect.top
 self.mario.landing = True

正如之前那一篇文章所说,角色的移动如果只是单纯的实现以像素为单位向左向右移动,无疑会很影响玩家的游戏体验正如以下[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-18TNEWIP-1588172494406)(https://i.loli.net/2020/04/29/tcpYBHFKq8ISP54.gif)]

可以明显感觉到两个方向的运动的不同体验,下面是两个方向的代码作为比对

keys = pygame.key.get_pressed()
	if keys[pygame.K_RIGHT]:
        # 向右
   	 	self.pos.x += 5 # ------------------------简单的改变位置
    elif keys[pygame.K_LEFT]:
        # 向左
       if self.vel.x < 0:
        	# 这里很细节的加入了一个转向的速度控制
            self.acc.x = -TURNAROUND
            if self.vel.x >= 0:  # ------------------------改变加速度来改变运动
                self.acc.x = -ACC
        # 这里加入了一个最大速度限制
    if abs(self.vel.x) < MAX_SPEED:
        self.vel.x += self.acc.x
    elif keys[pygame.K_LEFT]:
        self.vel.x = -MAX_SPEED
    elif keys[pygame.K_RIGHT]:
        self.vel.x = MAX_SPEED
    # 这里对加速度和速度进行计算得出位移并在下一帧时改变Mario的位置
    self.acc.x += self.vel.x * FRICTION
    # 同时还要引用一个 摩擦力 的概念,随着速度的增大而增大
    self.vel += self.acc
    self.pos += self.vel + 0.5 * self.acc
    self.rect.midbottom = self.pos

对于角色的跳跃,一定要对其状态加以限制,让其必须在 “落地” 的状态下才能开始跳跃,不然就会产生下面的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfPXR0yT-1588172494408)(https://i.loli.net/2020/04/29/kLSh19zaAJ5KptX.gif)]

为了避免这种情况,我们引入了一个self.landing状态,只有但其在为true的时候才能响应跳跃事件

if keys[pygame.K_SPACE]:
    if self.landing:
        # 这里跳跃的参数,只是给Mario一个向上的速度,类似于物理中的上抛运动
        self.vel.y = -JUMP

这里以上的所有大写常量参数都是定义在单独的配置文件中,方便修改。其参数的大小可以自行调节找出最合适的一组

对于这些运动的参数大家可以自己去调试调试,尝试一下不同的操作体验,也可以去文末的代码自寻

3. 角色的动作图片的切换

在提供的素材中是张表如图,我们需要自行裁剪下我们所需要的图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mtM5LLFY-1588172494410)(https://i.loli.net/2020/04/29/CRtDbnMAKFuGarN.png)]

这里可以在工具类中定义一个加载图片的方法

def load_image(filename):
    src = os.path.dirname(os.path.abspath(__file__))
    path = os.path.join(src, 'resources', 'graphics', filename)
    return pygame.image.load(path)

从sheet中裁切图片

# 裁切方法
def get_image(self, x, y, width, height):
    image = pg.Surface([width, height])
    rect = image.get_rect()
    image.blit(self.sheet, (0, 0), (x, y, width, height))
    image.set_colorkey(BLACK)
    image = pg.transform.scale(image,
                               (int(rect.width * MARIO_SIZE),
                                int(rect.height * MARIO_SIZE)))
    return image

# 裁切并加入容器
def load_from_sheet(self):
    self.right_frames = []
    self.left_frames = []

    self.right_frames.append(
        self.get_image(178, 32, 12, 16)) # 站立
    self.right_frames.append(
        self.get_image(80, 32, 15, 16))  # 跑 1
    self.right_frames.append(
        self.get_image(96, 32, 16, 16))	 # 跑 2
    self.right_frames.append(
        self.get_image(112, 32, 16, 16))  # 跑 3
    self.right_frames.append(
        self.get_image(144, 32, 16, 16))  # 跳 

    # 将向右的图片水平翻转就是向左的图片了
    for frame in self.right_frames:
        new_image = pg.transform.flip(frame, True, False)
        self.left_frames.append(new_image)

    # 最后全部加入容器方便我们之间通过下标
    self.frames = self.right_frames + self.left_frames

图片的切换,一开始我是采用一帧换一张图片的方向,下面是代码

# 定义一个方法来通过运动方向更换图片
def walk(self, facing):
    if facing == 'right':
        if self.image_index > 3:
            self.image_index = 0
     if facing == 'left':
        if self.image_index > 8:
            self.image_index = 5
        if self.image_index < 5:
            self.image_index = 5
      self.image_index += 1

但后来在运行的时候我发现了一个让人懵逼的效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-joJBm3k7-1588172494412)(https://i.loli.net/2020/04/29/3bdTG8tfQKvqLPE.gif)]

后来发现这是由于我的图片是每帧一换,快的让人反应不过来,产生了这种让人苦笑不得的效果。

后来在参考大神的源码的时候,发现了一种控制图片切换速度的方法。不得不说大神还是很细节的,引入的一些常量系数时间戳,通过Mario的移动速度来控制图片切换,让其更加自然平滑。下面是代码

    # 改进后的代码
    def walk(self, facing):
        if self.image_index == 0:
            self.image_index += 1
            # 加入一个时间戳
            self.walking_timer = pg.time.get_ticks()
        else:
            # 比较时间变化和当前的Mario的速度
            if (pg.time.get_ticks() - self.walking_timer >
                    self.calculate_animation_speed()):
                self.image_index += 1
                self.walking_timer = pg.time.get_ticks()
        
  • 14
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值