我的世界python写游戏_用python写游戏之 Give it up

《永不言弃 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

初来乍到,请多关照。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值