Python 实现平台类游戏添加跳跃功能


Posted in Python onMarch 27, 2020

 在本期使用 Python Pygame 模块编写视频游戏中,学会如何使用跳跃来对抗重力。

在本系列的前一篇文章 中,你已经模拟了重力。但现在,你需要赋予你的角色跳跃的能力来对抗重力。

跳跃是对重力作用的暂时延缓。在这一小段时间里,你是向上跳,而不是被重力拉着向下落。但你一旦到达了跳跃的最高点,重力就会重新发挥作用,将你拉回地面。

在代码中,这种变化被表示为变量。首先,你需要为玩家精灵建立一个变量,使得 Python 能够跟踪该精灵是否正在跳跃中。一旦玩家精灵开始跳跃,他就会再次受到重力的作用,并被拉回最近的物体。

设置跳跃状态变量

你需要为你的 Player 类添加两个新变量:

  • 一个是为了跟踪你的角色是否正在跳跃中,可通过你的玩家精灵是否站在坚实的地面来确定
  • 一个是为了将玩家带回地面

将如下两个变量添加到你的 Player 类中。在下方的代码中,注释前的部分用于提示上下文,因此只需要添加最后两行:           

self.movex = 0
  self.movey = 0
  self.frame = 0
  self.health = 10
  # 此处是重力相关变量
  self.collide_delta = 0
  self.jump_delta = 6

第一个变量 collide_delta 被设为 0 是因为在正常状态下,玩家精灵没有处在跳跃中的状态。另一个变量 jump_delta 被设为 6,是为了防止精灵在第一次进入游戏世界时就发生反弹(实际上就是跳跃)。当你完成了本篇文章的示例,尝试把该变量设为 0 看看会发生什么。

跳跃中的碰撞

如果你是跳到一个蹦床上,那你的跳跃一定非常优美。但是如果你是跳向一面墙会发生什么呢?(千万不要去尝试!)不管你的起跳多么令人印象深刻,当你撞到比你更大更硬的物体时,你都会立马停下。(LCTT 译注:原理参考动量守恒定律)

为了在你的视频游戏中模拟这一点,你需要在你的玩家精灵与地面等东西发生碰撞时,将 self.collide_delta 变量设为 0。如果你的 self.collide_delta 不是 0 而是其它的什么值,那么你的玩家就会发生跳跃,并且当你的玩家与墙或者地面发生碰撞时无法跳跃。

在你的 Player 类的 update 方法中,将地面碰撞相关代码块修改为如下所示:

ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
  self.movey = 0
  self.rect.y = worldy-ty-ty
  self.collide_delta = 0 # 停止跳跃
  if self.rect.y > g.rect.y:
  self.health -=1
  print(self.health)

这段代码块检查了地面精灵和玩家精灵之间发生的碰撞。当发生碰撞时,它会将玩家 Y 方向的坐标值设置为游戏窗口的高度减去一个瓷砖的高度再减去另一个瓷砖的高度。以此保证了玩家精灵是站在地面上,而不是嵌在地面里。同时它也将 self.collide_delta 设为 0,使得程序能够知道玩家未处在跳跃中。除此之外,它将 self.movey 设为 0,使得程序能够知道玩家当前未受到重力的牵引作用(这是游戏物理引擎的奇怪之处,一旦玩家落地,也就没有必要继续将玩家拉向地面)。

此处 if 语句用来检测玩家是否已经落到地面之下,如果是,那就扣除一点生命值作为惩罚。此处假定了你希望当你的玩家落到地图之外时失去生命值。这个设定不是必需的,它只是平台类游戏的一种惯例。更有可能的是,你希望这个事件能够触发另一些事件,或者说是一种能够让你的现实世界玩家沉迷于让精灵掉到屏幕之外的东西。一种简单的恢复方式是在玩家精灵掉落到地图之外时,将 self.rect.y 重新设置为 0,这样它就会在地图上方重新生成,并落到坚实的地面上。

撞向地面

模拟的重力使你玩家的 Y 坐标不断增大(LCTT 译注:此处原文中为 0,但在 Pygame 中越靠下方 Y 坐标应越大)。要实现跳跃,完成如下代码使你的玩家精灵离开地面,飞向空中。

在你的 Player 类的 update 方法中,添加如下代码来暂时延缓重力的作用:

if self.collide_delta < 6 and self.jump_delta < 6:
  self.jump_delta = 6*2
  self.movey -= 33 # 跳跃的高度
  self.collide_delta += 6
  self.jump_delta += 6

根据此代码所示,跳跃使玩家精灵向空中移动了 33 个像素。此处是负 33 是因为在 Pygame 中,越小的数代表距离屏幕顶端越近。

不过此事件视条件而定,只有当 self.collide_delta 小于 6(缺省值定义在你 Player 类的 init 方法中)并且 self.jump_delta 也于 6 的时候才会发生。此条件能够保证直到玩家碰到一个平台,才能触发另一次跳跃。换言之,它能够阻止空中二段跳。

在某些特殊条件下,你可能不想阻止空中二段跳,或者说你允许玩家进行空中二段跳。举个栗子,如果玩家获得了某个战利品,那么在他被敌人攻击到之前,都能够拥有空中二段跳的能力。

当你完成本篇文章中的示例,尝试将 self.collide_deltaself.jump_delta 设置为 0,从而获得百分之百的几率触发空中二段跳。

在平台上着陆

目前你已经定义了在玩家精灵摔落地面时的抵抗重力条件,但此时你的游戏代码仍保持平台与地面置于不同的列表中(就像本文中做的很多其他选择一样,这个设定并不是必需的,你可以尝试将地面作为另一种平台)。为了允许玩家精灵站在平台之上,你必须像检测地面碰撞一样,检测玩家精灵与平台精灵之间的碰撞。将如下代码放于你的 update 方法中:

plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
  self.collide_delta = 0 # 跳跃结束
  self.movey = 0

但此处还有一点需要考虑:平台悬在空中,也就意味着玩家可以通过从上面或者从下面接触平台来与之互动。

确定平台如何与玩家互动取决于你,阻止玩家从下方到达平台也并不稀奇。将如下代码加到上方的代码块中,使得平台表现得像天花板或者说是藤架。只有在玩家精灵跳得比平台上沿更高时才能跳到平台上,但会阻止玩家从平台下方跳上来:        

if self.rect.y > p.rect.y:
  self.rect.y = p.rect.y+ty
  else:
  self.rect.y = p.rect.y-ty

此处 if 语句代码块的第一个子句阻止玩家精灵从平台正下方跳到平台上。如果它检测到玩家精灵的坐标比平台更大(在 Pygame 中,坐标更大意味着在屏幕的更下方),那么将玩家精灵新的 Y 坐标设置为当前平台的 Y 坐标加上一个瓷砖的高度。实际效果就是保证玩家精灵距离平台一个瓷砖的高度,防止其从下方穿过平台。

else 子句做了相反的事情。当程序运行到此处时,如果玩家精灵的 Y 坐标不比平台的更大,意味着玩家精灵是从空中落下(不论是由于玩家刚刚从此处生成,或者是玩家执行了跳跃)。在这种情况下,玩家精灵的 Y 坐标被设为平台的 Y 坐标减去一个瓷砖的高度(切记,在 Pygame 中更小的 Y 坐标代表在屏幕上的更高处)。这样就能保证玩家在平台上,除非他从平台上跳下来或者走下来。

你也可以尝试其他的方式来处理玩家与平台之间的互动。举个栗子,也许玩家精灵被设定为处在平台的“前面”,他能够无障碍地跳跃穿过平台并站在上面。或者你可以设计一种平台会减缓而又不完全阻止玩家的跳跃过程。甚至你可以通过将不同平台分到不同列表中来混合搭配使用。

触发一次跳跃

目前为此,你的代码已经模拟了所有必需的跳跃条件,但仍缺少一个跳跃触发器。你的玩家精灵的 self.jump_delta 初始值被设置为 6,只有当它比 6 小的时候才会触发更新跳跃的代码。

为跳跃变量设置一个新的设置方法,在你的 Player 类中创建一个 jump 方法,并将 self.jump_delta 设为小于 6 的值。通过使玩家精灵向空中移动 33 个像素,来暂时减缓重力的作用。

def jump(self,platform_list):
 self.jump_delta = 0

不管你相信与否,这就是 jump 方法的全部。剩余的部分在 update 方法中,你已经在前面实现了相关代码。

要使你游戏中的跳跃功能生效,还有最后一件事情要做。如果你想不起来是什么,运行游戏并观察跳跃是如何生效的。

问题就在于你的主循环中没有调用 jump 方法。先前你已经为该方法创建了一个按键占位符,现在,跳跃键所做的就是将 jump 打印到终端。

调用 jump 方法

在你的主循环中,将上方向键的效果从打印一条调试语句,改为调用 jump 方法。

注意此处,与 update 方法类似,jump 方法也需要检测碰撞,因此你需要告诉它使用哪个 plat_list。           

if event.key == pygame.K_UP or event.key == ord('w'):
  player.jump(plat_list)

如果你倾向于使用空格键作为跳跃键,使用 pygame.K_SPACE 替代 pygame.K_UP 作为按键。另一种选择,你可以同时使用两种方式(使用单独的 if 语句),给玩家多一种选择。

现在来尝试你的游戏吧!在下一篇文章中,你将让你的游戏卷动起来。

Python 实现平台类游戏添加跳跃功能

以下是目前为止的所有代码:

#!/usr/bin/env python3
# draw a world
# add a player and player control
# add player movement
# add enemy and basic collision
# add platform
# add gravity
# add jumping
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
import pygame
import sys
import os
'''
Objects
'''
class Platform(pygame.sprite.Sprite):
 # x 坐标,y 坐标,图像宽度,图像高度,图像文件
 def __init__(self,xloc,yloc,imgw,imgh,img):
 pygame.sprite.Sprite.__init__(self)
 self.image = pygame.image.load(os.path.join('images',img)).convert()
 self.image.convert_alpha()
 self.rect = self.image.get_rect()
 self.rect.y = yloc
 self.rect.x = xloc
class Player(pygame.sprite.Sprite):
 '''
 生成一个玩家
 '''
 def __init__(self):
 pygame.sprite.Sprite.__init__(self)
 self.movex = 0
 self.movey = 0
 self.frame = 0
 self.health = 10
 self.collide_delta = 0
 self.jump_delta = 6
 self.score = 1
 self.images = []
 for i in range(1,9):
  img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
  img.convert_alpha()
  img.set_colorkey(ALPHA)
  self.images.append(img)
  self.image = self.images[0]
  self.rect = self.image.get_rect()
 def jump(self,platform_list):
 self.jump_delta = 0
 def gravity(self):
 self.movey += 3.2 # how fast player falls
 
 if self.rect.y > worldy and self.movey >= 0:
  self.movey = 0
  self.rect.y = worldy-ty
 
 def control(self,x,y):
 '''
 控制玩家移动
 '''
 self.movex += x
 self.movey += y
 
 def update(self):
 '''
 更新精灵位置
 '''
 
 self.rect.x = self.rect.x + self.movex
 self.rect.y = self.rect.y + self.movey
 # 向左移动
 if self.movex < 0:
  self.frame += 1
  if self.frame > ani*3:
  self.frame = 0
  self.image = self.images[self.frame//ani]
 # 向右移动
 if self.movex > 0:
  self.frame += 1
  if self.frame > ani*3:
  self.frame = 0
  self.image = self.images[(self.frame//ani)+4]
 # 碰撞
 enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
 for enemy in enemy_hit_list:
  self.health -= 1
  #print(self.health)
 plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
  self.collide_delta = 0 # stop jumping
  self.movey = 0
  if self.rect.y > p.rect.y:
  self.rect.y = p.rect.y+ty
  else:
  self.rect.y = p.rect.y-ty
  
 ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
  self.movey = 0
  self.rect.y = worldy-ty-ty
  self.collide_delta = 0 # stop jumping
  if self.rect.y > g.rect.y:
  self.health -=1
  print(self.health)
  
 if self.collide_delta < 6 and self.jump_delta < 6:
  self.jump_delta = 6*2
  self.movey -= 33 # how high to jump
  self.collide_delta += 6
  self.jump_delta += 6
  
class Enemy(pygame.sprite.Sprite):
 '''
 生成一个敌人
 '''
 def __init__(self,x,y,img):
 pygame.sprite.Sprite.__init__(self)
 self.image = pygame.image.load(os.path.join('images',img))
 self.movey = 0
 #self.image.convert_alpha()
 #self.image.set_colorkey(ALPHA)
 self.rect = self.image.get_rect()
 self.rect.x = x
 self.rect.y = y
 self.counter = 0
  
 def move(self):
 '''
 敌人移动
 '''
 distance = 80
 speed = 8
 self.movey += 3.2
 
 if self.counter >= 0 and self.counter <= distance:
  self.rect.x += speed
 elif self.counter >= distance and self.counter <= distance*2:
  self.rect.x -= speed
 else:
  self.counter = 0
 
 self.counter += 1
 if not self.rect.y >= worldy-ty-ty:
  self.rect.y += self.movey
 plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
  self.movey = 0
  if self.rect.y > p.rect.y:
  self.rect.y = p.rect.y+ty
  else:
  self.rect.y = p.rect.y-ty
 ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
  self.rect.y = worldy-ty-ty
 
class Level():
 def bad(lvl,eloc):
 if lvl == 1:
  enemy = Enemy(eloc[0],eloc[1],'yeti.png') # 生成敌人
  enemy_list = pygame.sprite.Group() # 创建敌人组
  enemy_list.add(enemy)  # 将敌人添加到敌人组
  
 if lvl == 2:
  print("Level " + str(lvl) )
 return enemy_list
 def loot(lvl,lloc):
 print(lvl)
 def ground(lvl,gloc,tx,ty):
 ground_list = pygame.sprite.Group()
 i=0
 if lvl == 1:
  while i < len(gloc):
  ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
  ground_list.add(ground)
  i=i+1
 if lvl == 2:
  print("Level " + str(lvl) )
 return ground_list
 def platform(lvl,tx,ty):
 plat_list = pygame.sprite.Group()
 ploc = []
 i=0
 if lvl == 1:
  ploc.append((0,worldy-ty-128,3))
  ploc.append((300,worldy-ty-256,3))
  ploc.append((500,worldy-ty-128,4))
  while i < len(ploc):
  j=0
  while j <= ploc[i][2]:
   plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
   plat_list.add(plat)
   j=j+1
  print('run' + str(i) + str(ploc[i]))
  i=i+1
 if lvl == 2:
  print("Level " + str(lvl) )
 return plat_list
'''
Setup
'''
worldx = 960
worldy = 720
fps = 40 # 帧率
ani = 4 # 动画循环
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)
ALPHA = (0,255,0)
world = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # 生成玩家
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
jump = -24
eloc = []
eloc = [200,20]
gloc = []
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
tx = 64 # 瓷砖尺寸
ty = 64 # 瓷砖尺寸
i=0
while i <= (worldx/tx)+tx:
 gloc.append(i*tx)
 i=i+1
enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,gloc,tx,ty )
plat_list = Level.platform( 1,tx,ty )
'''
主循环
'''
while main == True:
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
  pygame.quit(); sys.exit()
  main = False
 if event.type == pygame.KEYDOWN:
  if event.key == pygame.K_LEFT or event.key == ord('a'):
  print("LEFT")
  player.control(-steps,0)
  if event.key == pygame.K_RIGHT or event.key == ord('d'):
  print("RIGHT")
  player.control(steps,0)
  if event.key == pygame.K_UP or event.key == ord('w'):
  print('jump')
 if event.type == pygame.KEYUP:
  if event.key == pygame.K_LEFT or event.key == ord('a'):
  player.control(steps,0)
  if event.key == pygame.K_RIGHT or event.key == ord('d'):
  player.control(-steps,0)
  if event.key == pygame.K_UP or event.key == ord('w'):
  player.jump(plat_list)
  if event.key == ord('q'):
  pygame.quit()
  sys.exit()
  main = False
# world.fill(BLACK)
 world.blit(backdrop, backdropbox)
 player.gravity() # 检查重力
 player.update()
 player_list.draw(world) # 刷新玩家位置
 enemy_list.draw(world) # 刷新敌人
 ground_list.draw(world) # 刷新地面
 plat_list.draw(world) # 刷新平台
 for e in enemy_list:
 e.move()
 pygame.display.flip()
 clock.tick(fps)

总结

到此这篇关于Python 实现平台类游戏添加跳跃功能的文章就介绍到这了,更多相关python 平台类游戏 跳跃内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中尝试多线程编程的一个简明例子
Apr 07 Python
详解Python中DOM方法的动态性
Apr 11 Python
Python中关于使用模块的基础知识
May 24 Python
浅谈Python生成器generator之next和send的运行流程(详解)
May 08 Python
Django 权限认证(根据不同的用户,设置不同的显示和访问权限)
Jul 24 Python
python实现图片插入文字
Nov 26 Python
TensorFlow tf.nn.max_pool实现池化操作方式
Jan 04 Python
Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)
Feb 17 Python
Python pandas对excel的操作实现示例
Jul 21 Python
python logging模块的使用详解
Oct 23 Python
Python中过滤字符串列表的方法
Dec 22 Python
PyTorch 如何设置随机数种子使结果可复现
May 12 Python
django配置app中的静态文件步骤
Mar 27 #Python
使用卷积神经网络(CNN)做人脸识别的示例代码
Mar 27 #Python
django实现HttpResponse返回json数据为中文
Mar 27 #Python
python对XML文件的操作实现代码
Mar 27 #Python
Python Socketserver实现FTP文件上传下载代码实例
Mar 27 #Python
使用python从三个角度解决josephus问题的方法
Mar 27 #Python
解决django接口无法通过ip进行访问的问题
Mar 27 #Python
You might like
dedecms防止FCK乱格式化你的代码的修改方法
2007/03/17 PHP
yii2中的rules 自定义验证规则详解
2016/04/19 PHP
利用laravel+ajax实现文件上传功能方法示例
2017/08/13 PHP
Jquery Ajax请求代码(2)
2011/01/07 Javascript
javascript 进阶篇1 正则表达式,cookie管理,userData
2012/03/14 Javascript
jQuery实现点击行选中或取消CheckBox的方法
2016/08/01 Javascript
Ajax基础知识详解
2017/02/17 Javascript
Three.js利用orbit controls插件(轨道控制)控制模型交互动作详解
2017/09/25 Javascript
js获取 gif 的帧数的代码实例
2019/09/10 Javascript
vue input标签通用指令校验的实现
2019/11/05 Javascript
基于JavaScript判断两个对象内容是否相等
2020/01/10 Javascript
Vue实现简易购物车页面
2020/12/30 Vue.js
[01:08]2014DOTA2展望TI 剑指西雅图LGD战队专访
2014/06/30 DOTA
[01:21]DOTA2新纪元-7.0新版本即将开启!
2016/12/11 DOTA
Python multiprocessing.Manager介绍和实例(进程间共享数据)
2014/11/21 Python
基于Python代码编辑器的选用(详解)
2017/09/13 Python
Python解析命令行读取参数之argparse模块
2019/07/26 Python
Python json模块与jsonpath模块区别详解
2020/03/05 Python
Python多线程thread及模块使用实例
2020/04/28 Python
Python filter过滤器原理及实例应用
2020/08/18 Python
python rsa-oaep加密的示例代码
2020/09/23 Python
html5-websocket基于远程方法调用的数据交互实现
2012/12/04 HTML / CSS
东南亚旅游平台:The Trip Guru
2018/01/01 全球购物
ECCO爱步官方旗舰店:丹麦鞋履品牌
2018/01/02 全球购物
万宝龙英国官网:Montblanc手表、书写工具、皮革和珠宝
2018/10/16 全球购物
农贸市场管理制度
2014/01/31 职场文书
管理部副部长岗位职责范文
2014/03/09 职场文书
端午节活动总结
2014/08/26 职场文书
2014司机年终工作总结
2014/12/05 职场文书
董事长岗位职责
2015/02/13 职场文书
2016新年年会主持词
2015/07/06 职场文书
大学运动会通讯稿
2015/07/18 职场文书
2015年学校管理工作总结
2015/07/20 职场文书
《水上飞机》教学反思
2016/02/20 职场文书
2019最新校园运动会广播稿!
2019/06/28 职场文书
告诉你创业计划书的8个实用技巧
2019/07/12 职场文书