一,前言
最近看了《游戏开发中的数学和物理》,至于为什么突然要看这本书,只是看了北京卫视的<新闻大求真>里面正好说到光线闪烁后一帧帧看图片后,变成动画的效果。处于出于好奇,人家是什么做游戏的,因为我绘图或GUI引擎基本都知道原理,但是做不出游戏,所以我要探秘。我已经全部看完了,后面的章节只是快速过一下,前面的章节做了下实验。
二,游戏中的数学笔记
首先一个游戏中的运动要呈现的就是每一帧有图像变化。而每一帧可以理解为周期调用移动函数。我已经说明了框架哦。
2.1 第一章第一节 物体的匀速直线运动
- 关于匀速直线运动的物体碰壁回弹
a.关于移动物体,比如从左边出发,移动到右边,遇到右边边框,这个向右碰到判断的位置是viewWidth-objWidth。为什么要减去objWidth呢!原因就是物体坐标从左上角开始。这个依据view和obj的坐标系,设计代码的时候注意即可。
b.碰到后可能obj面积有超过view边框,所以要重置位置值。比如x=viewWidth-objWidth。
c.关于回弹就是速度使用负值。
2.2 关于按键控制匀速直线运动
这个看起来和自动每一帧运动类似,只是周期调用前判断上下左右按键是否按下。没想要里面也有技巧,就是2个按键一起按下后的效果,比如向右和向上一起按下后,它的运动方向变成x和y都增加,最后是斜向上方向运行。它的速度会变成原来的倍。这是勾股定理,因为匀速运动速度和距离成正比。我同样是周期运行,1帧的时间,一个上一个右2个按钮按下后,物体运动的距离是一个斜边s。斜边s的长度比a和b直角边长要长吧,长就说明速度不同了。
所以解决方法就是判断按右键后,继续判断是否上或下键也同时按下了,此时移动的距离向右增加的少些,变成增加v/1.414。
2.3 第一章第二节 物体的各种方向运动
三角函数中几乎都试用弧度,很少使用角度,主要是关系到微积分的一些问题。所以游戏中将角的单位统一为弧度。此案例中让我领悟了沿不同角度移动,其实就是要修改x和y移动增加的delta,而只要delta的数学关系满足数学公式,那么物体移动的效果就满足数学公式。突然感觉这个积分的处理有点像,积分将断拆分为小段进行累加,而此运动可以看做一帧帧中物体的一点点的变动。
def __init__(self):
#...略
self.mult = 3 # 调速倍数
self.angle = math.pi/6.0 # 移动角度
self.moveSpeedX = self.mult * math.cos(self.angle) # x移动增量
self.moveSpeedY = self.mult * math.sin(self.angle) # y移动增量
def birdUpdate(self):
# 直线移动
self.birdX += self.moveSpeedX
self.birdY += self.moveSpeedY
#self.moveSpeedY += 1 # 重力加速度
# 移动到边框则重中心点开始,每次角度添加30度的方向开始直线移动
if( self.birdX <= 0 or self.birdX >= view_w - self.birdrect.width or self.birdY <= 0 or self.birdY >= view_h - self.birdrect.height):
# 重置物体位置到中心点
self.birdX = (view_w - self.birdrect.width)/2.0
self.birdY = (view_h - self.birdrect.height)/2.0
# 角度增加30度
self.angle += 2.0 * math.pi/10.0
# 角度值控制在2 pi内
if(self.angle > 2.0 * math.pi):
self.angle -= 2.0 *math.pi
# delta移动增量按角度更新
self.moveSpeedX = self.mult * math.cos(self.angle)
self.moveSpeedY = self.mult * math.sin(self.angle)
2.4 第一章第5节 圆周运动
之前小节有写重力加速度,就是速度每帧也累加,这个比较简单。另外一节就是说随机值,里面提到c语言的rand是均匀分布的随机数,不是正态分布的随机数。并且给了没有详细解释的正态分布随机值的算法,所以我没有去尝试。接着说到了圆周运动,此时我发现和速度没有关系了,直接计算x和y的位置了。不是都在速度上做文章吗?
def birdUpdate_cycle(self):
# 圆周移动
self.birdX = 60 * math.cos(self.angle) + (view_w - self.birdrect.width)/2.0
self.birdY = 60 * math.sin(self.angle) + (view_h - self.birdrect.height)/2.0
self.angle += 2.0*math.pi/120.0
我改成和速度相关,运行结果非预期,它不是在图像中心点开始运动的,另外关于这个r*cos(x)中的r取值也不好定义了。虽然运动起来也是一个圆。主要原因我想了下就是这个r是围绕图像中心为圆心。而不是运动的物体中每个点的圆心。所以次例子中再用delta增量的方式不太好。直接对obj对象进行处理,在定位方面会比较好。
def birdUpdate_cycle(self):
# 圆周移动
# self.birdX = 60 * math.cos(self.angle) + (view_w - self.birdrect.width)/2.0
# self.birdY = 60 * math.sin(self.angle) + (view_h - self.birdrect.height)/2.0
# self.angle += 2.0*math.pi/120.0
self.birdX += self.moveSpeedX
self.birdY += self.moveSpeedY
self.moveSpeedX = 5 * math.cos(self.angle)
self.moveSpeedY = 5 * math.sin(self.angle)
self.angle += 2.0 * math.pi / 120.0
三,学以致用
理论学习中物体的运动,只是对移动对象的局部操作,想要运用到游戏中,那么我需要用一个框架来实现下。本书自带的example已经下载不到了,而且很古老。所以我可以选择在之前的guilite中实现,可以选择在SDL2框架实现,但是我想了下,我期望再简单点,目的是要验证书中描述的物体运动。于是我找了个更简单的框架,就在python的pygame上实现吧。小游戏就是通过上下左右按键来控制小鸟的直线运动。框架我是参考网上其它博主分享的,结合自己的按键验证场景进行的修改。至于pygame的api一学就会,基于游戏或GUI的开发思路都是基于事件驱动后进行渲染,所以这方面的设计思想真的是一通百通呢~
按键直接控制小鸟直线运动的效果图
python小游戏源码:
'''
******************************************************************************
| Project : AppleGame
| Description : pygame project
| CPU and Compiler : win10 python 3.7
| R E V I S I O N H I S T O R Y
|-------------------------------------------------------------------------------
| Date Version Author Description
| ---------- -------- ------ ---------------------------------------------
| 2020-04-29 01.00.00 AppleCai First version
******************************************************************************
'''
import pygame
import sys
import random
view_w = 628
view_h = 393
class Bird(object):
"""定义一个鸟类"""
def __init__(self):
"""定义初始化方法"""
# 定义鸟的3种状态列表
self.birdStatus = pygame.image.load("bird.png")
self.birdrect = self.birdStatus.get_rect() # 获取矩形区域
self.birdX = 10 # 鸟所在X轴坐标,即是向右飞行的速度
self.birdY = 30 # 鸟所在Y轴坐标,即上下飞行高度
self.bLeftKey = False
self.bRightKey = False
self.bUpKey = False
self.bDownKey = False
self.moveSpeed = 10 # 移动速度
def birdUpdate(self):
# 左键操作
if(self.bLeftKey):
if(self.bUpKey or self.bDownKey):
self.birdX -= self.moveSpeed/1.414
else:
self.birdX -= self.moveSpeed
if(self.birdX < 0):
self.birdX = 0
# 右键操作
if(self.bRightKey):
if(self.bUpKey or self.bDownKey):
self.birdX += self.moveSpeed/1.414
#self.birdX += self.moveSpeed
else:
self.birdX += self.moveSpeed
if(self.birdX >= view_w - self.birdrect.width):
self.birdX = view_w -self.birdrect.width
# 上键操作
if(self.bUpKey):
if(self.bLeftKey or self.bRightKey):
self.birdY -= self.moveSpeed/1.414
#self.birdY -= self.moveSpeed
else:
self.birdY -= self.moveSpeed
if(self.birdY < 0):
self.birdY = 0
# 下键操作
if(self.bDownKey):
if (self.bLeftKey or self.bRightKey):
self.birdY += self.moveSpeed/1.414
else:
self.birdY += self.moveSpeed
if(self.birdY >= view_h - self.birdrect.height):
self.birdY = view_h -self.birdrect.height
def createMap():
"""定义创建地图的方法"""
screen.fill((255, 255, 255)) # 填充颜色
screen.blit(background, (0, 0)) # 填入到背景
# 显示小鸟,设置小鸟的坐标
screen.blit(Bird.birdStatus, (Bird.birdX, Bird.birdY))
Bird.birdUpdate() # 小鸟移动
# 更新显示
pygame.display.update()
def check_keydown_events(event, rocket):
"""响应按键"""
if event.key == pygame.K_RIGHT:
rocket.bRightKey = True
elif event.key == pygame.K_LEFT:
rocket.bLeftKey = True
elif event.key == pygame.K_UP:
rocket.bUpKey = True
elif event.key == pygame.K_DOWN:
rocket.bDownKey = True
def check_keyup_events(event, rocket):
"""响应松开"""
if event.key == pygame.K_RIGHT:
rocket.bRightKey = False
elif event.key == pygame.K_LEFT:
rocket.bLeftKey = False
elif event.key == pygame.K_UP:
rocket.bUpKey = False
elif event.key == pygame.K_DOWN:
rocket.bDownKey = False
if __name__ == '__main__':
"""主程序"""
pygame.init() # 初始化pygame
pygame.display.set_caption("AppleCai's pygame")
size = view_w, view_h # 设置窗口
screen = pygame.display.set_mode(size) # 显示窗口
clock = pygame.time.Clock() # 设置时钟
Bird = Bird() # 实例化鸟类
background = pygame.image.load("background.png") # 加载背景图片
while True:
clock.tick(60) # 每秒执行60次
# 轮询事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, Bird)
elif event.type == pygame.KEYUP:
check_keyup_events(event, Bird)
createMap() # 创建地图
pygame.quit()
四,总结
还真是学到不少,不看这本书的话,让我自己做我是想不到这些问题的。并且学习了些设计思路。有些东西自己去想这个实现思路会比较难,但是直接看过了解过,就简单了。这是否和做数学题一样,有时候没有解题思路,参考了别人的解题思路就会了。没事儿做看看闲书,就是就和知道,了解下方法论,需要用的时候可以知道去哪里找,不会没有方向,目的就达到了,哈哈~