《永不言弃 Give It Up》,这是一款极具虐心色彩的音乐题材闯关游戏。

这篇文章就来分析这款游戏原理,并用python写出来一个简易版。废话不多说,直接开始分析。

游戏元素,暂且把主角叫做MC, 障碍柱叫做柱子。下边开始冥想透视解析游戏大法,大法命名是我自己随便取的。想象去除游戏中的图片素材,去除背景和音乐,把MC看作画布中的一个矩形,把柱子也看做一个矩形。画布中只剩下了MC矩形和障碍物的矩形。想象到了吗?什么?没有?看下图

没错,这是已经做出来的游戏截图。看着很简单吧,是不是感觉跟之前的flappy bird 有点类似?那我告诉你,部分逻辑还确实有点类似。

不同的是,小鸟是自由落体,点击屏幕才会往上,而MC碰到柱子顶部会自己弹起来。当MC需要往上的时候,给MC一个向上的速度,随着MC的上升,这个速度逐渐减少直到0,MC开始下降。然后下降到一定高度再弹起。这样就模仿了MC的弹起的过程。

这个游戏的重点需要解决的问题就是当遇到障碍,怎样改变MC弹起的高度?

MC在降落到什么位置时开始弹起?

在相同的柱子高度怎么使柱子移动两格,而在后一个柱子比较高高时还保持一次一格的跳跃?

怎么判断MC死亡?

第一个问题:怎么改变高度?

前边提到,MC在弹起时会给一个初始速度和一个固定的加速度,初始速度依次减去加速度,直到为0,MC开始下落。那么想要改变MC弹起的高度,就把初始的速度给增大。这样还会有一个问题,速度增大了,如果加速度不变,那么速度减到0的时间就会变长,那么MC跳跃的间隔就会改变。

可以看到对比非常明显,第二张图因为跳的太高从而打乱了跳跃的节奏。

别忘了,这是一个音乐题材的跑酷游戏,踩点会给人一种节奏一致的快感,如果这个节奏断掉了,那么可玩性就大大降低了。所以在这里在改变初始速度的同时,也要改变这个加速度,让这个MC弹起的更快,这样也会给玩家一个给MC加弹跳力度的感觉。

第二个问题:MC 何时弹起?

给定一个基础的高度,如果MC 的最下端大于或等于这个高度,MC开始改变运动方向,即由下落转为上升。如果所有的柱子的高度是一定的,那么这个基础高度就不用变,那这个游戏也就不用玩了,玩家只用看着游戏就能到终点了。想要改变这个高度很简单,只看MC会落到哪个柱子上,把该柱子上方的高度,设为这个基础高度就可以了。

可以看到我用红点标出的高度,只要把红点的高度设置为MC下落的最低点,就可以了。

那么在什么时候重新设置这个高度呢?这里会引出另一个问题,什么时候移动柱子,达到MC往前跳的错觉呢?

只要在MC往上弹跳时,开始移动所有的柱子,移动的距离是一个柱子的宽度加一个柱子与柱子之间的宽度。当然这个移动的速度要比MC上升的速度快,不然MC都落下来了,柱子还没有移动。当柱子完成移动时,找到MC下方的柱子,把该柱子的上方高度设置成基础高度就行了。

第三个问题:如何判定该移动一格,还是移动两格?

通过判断当前柱子和下一个柱子的高度,如果当前柱子的高度小于后面的柱子,那玩家点屏幕时,增加MC上升的速度和加速度,柱子只移动一格。如果不小于,也就是大于或者等于后面的柱子高度,那就移动两格柱子。

上图中的左图的情况,当MC落地时,下一次应该改变速度,柱子移动一格。而中间图和右图两种情况不改变速度,柱子移动两格。当然右图这时候玩家是不应该跳的,跳了会死翘翘的。

第四个问题:如何判断MC死亡?

该跳不跳,撞柱子死亡

不该跳却跳,导致直接撞柱子死亡

跳到空地,死亡

总结就是与柱子碰撞就死亡,跳到空地就死亡。

主要的逻辑分析完毕,下边就开始撸代码

1. 定义MC类

class Ball(pygame.sprite.Sprite):

def __init__(self, position, disk_group):

pygame.sprite.Sprite.__init__(self)

self.disk_group = disk_group

self.rect = pygame.Rect(*position, BALL_SIZE, BALL_SIZE)

self.is_up = False # MC上升或下落

self.init_speed() # 初始化速度

self.move_disk = False # 移动托盘

self.min_height = BASE_HEIGHT # 设置下落的最小高度

self.can_jump = False # 是否可以跳

self.step = 1 # 托盘移动步长

self.change_up_speed = False # 是否改变初始速度

self.move_speed = (DISK_SIZE[0] + DISK_GAP_WIDTH) / 10 # 托盘移动速度

self.move_width = (DISK_SIZE[0] + DISK_GAP_WIDTH) * self.step # 托盘移动距离

self.current_disk_index = 0 # 当前托盘的下标

def init_speed(self, up_speed=1): # 初始化速度

self.up_speed = INIT_SPEED * up_speed

self.init_a_speed(up_speed)

self.down_speed = 0

def init_a_speed(self, a_speed=1): # 初始化加速度

self.a_speed = A_SPEED * FPS / 1000 * a_speed

def change_speed(self, step, change_up_speed): # 更改速度

self.step = step

self.change_up_speed = change_up_speed

def update(self):

if self.is_up:

# 上升速度越来越小

self.up_speed -= self.a_speed

self.rect.top -= self.up_speed

# 上升速度小于等于0, 改为下降状态

if self.up_speed <= 0:

self.down()

else:

# 下降速度越来越大

self.down_speed += self.a_speed

self.rect.bottom += self.down_speed

if self.rect.bottom >= self.min_height - 1:

self.rect.bottom = self.min_height - 1

self.up()

if not self.next_disk: # 游戏胜利

return 1

if not self.current_disk.show: # 跳到空地

return 2

def up(self):

self.can_jump = False

if self.change_up_speed: # 如果要改变速度, 就改变速度

self.change_up_speed = False

self.init_speed(SPEED)

else:

self.init_speed()

self.is_up = True

self.move_disk = True

def down(self):

self.init_speed()

if self.min_height - self.rect.bottom >= BALL_SIZE * 1.2:

self.init_a_speed(SPEED)

self.is_up = False

self.can_jump = True

@property

def current_disk(self):

try:

return self.disk_group.sprites()[self.current_disk_index]

except:

return None

@property

def next_disk(self):

try:

return self.disk_group.sprites()[self.current_disk_index + 1]

except:

return None

def set_min_height(self): # 设置最小高度

self.min_height = self.current_disk.rect.top

def draw(self, screen): # 画精灵

for disk in self.disk_group:

disk.draw(screen)

pygame.draw.rect(screen, (255, 255, 255), self.rect)

update函数MC一直弹跳的主要逻辑,当上升速度小于0,开始下降,当下降到最小的高度开始上升。up函数中调用了改变速度的代码,当上升时,需要改变速度,就把初始速度和加速度都乘以一定的倍数。

2. 柱子类

class Disk(pygame.sprite.Sprite):

def __init__(self, position, height, level, show=True):

pygame.sprite.Sprite.__init__(self)

self.rect = pygame.Rect(*position, DISK_SIZE[0], height)

self.height = height

self.level = level

self.show = show

def draw(self, screen):

if not self.show:

return

pygame.draw.rect(screen, (255, 255, 255), self.rect, 1)

定义柱子的level和show,用来判断柱子的高度和是否需要显示。

3. 初始化游戏和精灵

def init_game():

pygame.init()

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

pygame.display.set_caption('Give it up!')

return screen

def init_disk_sptite(FIRST_DISK_POSITION):

disk_group = pygame.sprite.Group()

disk_group.add(Disk(FIRST_DISK_POSITION, DISK_HEIGHT, 0))

for index, i in enumerate(DISK_LIST):

if i == -1:

show = False

else:

show = True

if i <= 0:

height = DISK_HEIGHT

else:

height = DISK_INCREMENT * i + DISK_HEIGHT

disk_group.add(

Disk((FIRST_DISK_POSITION[0] + (DISK_SIZE[0] + DISK_GAP_WIDTH) *

(index + 1), FIRST_DISK_POSITION[1] - height + DISK_HEIGHT),

height, i, show))

return disk_group

4. 监控事件

def press(ball):

if not ball.next_disk:

return

for event in pygame.event.get():

if event.type == pygame.QUIT: # 点击关闭按钮退出

pygame.quit()

sys.exit()

if event.type == pygame.KEYDOWN:

if event.key == 32 and ball.can_jump:

if ball.current_disk.level < ball.next_disk.level:

step = 1

change_speed = True

else:

step = 2

change_speed = False

ball.change_speed(step, change_speed)

ball.move_width = (DISK_SIZE[0] + DISK_GAP_WIDTH) * ball.step

当按下空格键时,判断当前与下一柱子的高度,来定义下一次柱子移动的步长。

5. 移动柱子

def move_disk(ball):

if ball.move_disk: # 可以移动柱子

speed = ball.move_speed * ball.step # 柱子移动的速度

for disk in ball.disk_group: # 移动所有柱子

disk.rect.left -= speed if ball.move_width > speed else ball.move_width

if disk.rect.right < 0: # 删除超过屏幕的柱子

ball.disk_group.remove(disk)

ball.current_disk_index -= 1

ball.move_width -= speed # 柱子移动的宽度还剩多少

if ball.move_width < speed: # 柱子移动完成

ball.current_disk_index += ball.step

ball.step = 1

ball.move_disk = False

ball.move_width = (DISK_SIZE[0] + DISK_GAP_WIDTH) * ball.step

ball.set_min_height() # 设置基础高度

6.开始结束游戏

def start_or_end_game(screen, ball, win, clock):

while True:

if win == 1:

text = 'You Win'

elif win == 2:

text = 'Press Enter To Restart!'

else:

text = 'Press Space To start!'

font_size = 32

font = pygame.font.SysFont('arial', font_size)

font_width, font_height = font.size(text)

screen.blit(font.render(text, True, (255, 255, 255)),

((SCREEN_WIDTH - font_width) / 2,

(SCREEN_HEIGHT - font_height) / 2.5))

ball.draw(screen)

for event in pygame.event.get():

if event.type == pygame.QUIT: # 点击关闭按钮退出

pygame.quit()

sys.exit()

if event.type == pygame.KEYDOWN:

if event.key == 13 and win == 2:

return

if event.key == 32 and win == 0:

return

# 更新画布

pygame.display.update()

clock.tick(FPS)

7. 主函数

def main():

screen = init_game()

disk_group = init_disk_sptite(FIRST_DISK_POSITION)

ball = Ball(BALL_POSITION, disk_group)

clock = pygame.time.Clock()

win = 0

start_or_end_game(screen, ball, win, clock)

while True:

screen.fill((0, 0, 0))

move_disk(ball) # 移动柱子

press(ball) # 监控按键

win = ball.update() # 移动MC

if win is not None:

break

if ball.next_disk:

if pygame.sprite.spritecollide(ball, ball.disk_group,

False): # 判断是否撞柱子

win = 2

break

ball.draw(screen)

# 更新画布

pygame.display.update()

clock.tick(FPS)

start_or_end_game(screen, ball, win, clock)

if __name__ == "__main__":

while True:

main()

运行效果

欸,您且稍等,故事还没结束。之前写的几款游戏,我只分析了玩法,做了简易版的游戏,并没有加入游戏素材。而这款游戏,没有音乐和音效,感觉少点什么,没内味儿,所以我去找了一些图片和音效,加了进去之后,画风就变成了下面这样。

源码已上传至Github:打代码的shy:用python写游戏系列​github.com

初来乍到,请多关照。

我的世界python写游戏_用python写游戏之 Give it up相关推荐

  1. python pygame模块怎么写游戏_使用 Python 和 Pygame 模块构建一个游戏框架

    这系列的第一篇通过创建一个简单的骰子游戏来探究 Python.现在是来从零制作你自己的游戏的时间. 在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏 ...

  2. python用户界面游戏_用Python和Pygame写游戏-从入门到精通(实战二:恶搞俄罗斯方块2)...

    我们接着来做这个整死人不偿命的俄罗斯方块. 代码组织和名词约定 上一次我们稍微整理了一下游戏运行的框架,这里需要整理一下python代码的框架,一个典型的pygame脚本结构如下: 其中,lib为py ...

  3. 用python和pygame写游戏_用Python和Pygame写游戏-从入门到精通(6)

    掌握了小小的像素,我们可以使用更加复杂一点的东西了,对,就是图像,无数的像素的集合~还记得上次我们为了生成的一张图片,花了无数时间,还好一般游戏不会在游戏的过程中动态生成图像,都是将画好的作为资源封装 ...

  4. python抽奖游戏_利用Python写一个抽奖程序,解密游戏内抽奖的秘密

    原标题:利用Python写一个抽奖程序,解密游戏内抽奖的秘密 前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 极客 ...

  5. 用java写游戏_用java写的扫雷游戏

    扫雷不能不说一款非常经典的游戏,无聊时候可以打发时间,虽然玩了很久,但还不知道它是怎么写的,所以自己就尝试动手做了个. 众所周知,java的swing采用mvc模式,即模型-视图-控制器,所以如果真的 ...

  6. 弟子规python编程游戏_《Python游戏趣味编程》 第11章 消灭星星

    知乎视频​www.zhihu.com 图书简介可以看这里: 童晶:<Python游戏趣味编程>新书上架了​zhuanlan.zhihu.com 消灭星星是一款非常容易上瘾的消除类游戏,只需 ...

  7. python 图像识别游戏_基于Python的浏览器图像识别

    我想实现一个软件在21点计算卡,使用一些图像识别自动化的过程.但我不知道从哪里开始. 我认为问题可以分为以下几个步骤: 1-在游戏中从浏览器中获取图像(基本上是一个Adobe Flash游戏) 2-处 ...

  8. python html5游戏_【Python】Python制作塔防小游戏

    开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块. 相关文件 原理介绍 游戏规则简介: 玩家通过建造箭塔抵御敌人的进攻. 每隔一段时间,将会有一波 ...

  9. python 概率分布模型_使用python的概率模型进行公司估值

    python 概率分布模型 Note from Towards Data Science's editors: While we allow independent authors to publis ...

最新文章

  1. iOS关于自定义rightBarButtonItem
  2. ORA-01589: 要打开数据库则必须使用 RESETLOGS 或 NORESETLOGS 选项
  3. 操作系统/etc/hosts文件配置
  4. SAP Fiori UI上关于时区Timezone的一些问题和解决方案
  5. 转,jquery中attr和prop的区别
  6. YARN的服务库和事件库
  7. 数据结构-堆 C与C++的实现
  8. nginx 分别对各个域名跳转
  9. STM32调试MIPI RFFE协议
  10. 倒排索引的MapReduce实现
  11. 早间简评:黄金亚盘快速下跌   1300关口岌岌可危?
  12. 支付宝小程序设置服务器维护,支付宝小程序配置
  13. OpenSSL下载及使用(生成公钥 私钥)
  14. 道路覆盖 (二分答案+状压DP)
  15. 混合高斯背景建模算法GMM
  16. 微信读书十块钱一个月?分分钟搞定它!“白嫖”才是最香的!
  17. ios上webview与浏览器webview
  18. 《统计学》——思考题第四章数据的概括性度量(贾俊平)
  19. 对于anaconda安装的一个小感悟 。
  20. 篮球数据API接口 - 【篮球文字直播】API调用示例代码

热门文章

  1. 输出字母在字符串中位置索引 python
  2. 宜信智能监控平台建设实践|分享实录
  3. rk3568 android 11 默认壁纸
  4. 兮°Android下的屏幕适配问题的一点心得
  5. Java中long与float
  6. chromedriver.exe安装
  7. 聚焦医疗数字化,华为医疗物联网更懂智慧医疗
  8. 采购员的主要职责是什么?
  9. 图的计算(1):图的矩阵表示
  10. SketchUp 2019 建筑透视剖面图教程