python游戏开发中的数学和物理--Apple的学习笔记

一,前言

最近看了《游戏开发中的数学和物理》,至于为什么突然要看这本书,只是看了北京卫视的<新闻大求真>里面正好说到光线闪烁后一帧帧看图片后,变成动画的效果。处于出于好奇,人家是什么做游戏的,因为我绘图或GUI引擎基本都知道原理,但是做不出游戏,所以我要探秘。我已经全部看完了,后面的章节只是快速过一下,前面的章节做了下实验。

二,游戏中的数学笔记

首先一个游戏中的运动要呈现的就是每一帧有图像变化。而每一帧可以理解为周期调用移动函数。我已经说明了框架哦。

2.1 第一章第一节 物体的匀速直线运动

  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的开发思路都是基于事件驱动后进行渲染,所以这方面的设计思想真的是一通百通呢~
按键直接控制小鸟直线运动的效果图

image.png

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()

四,总结

还真是学到不少,不看这本书的话,让我自己做我是想不到这些问题的。并且学习了些设计思路。有些东西自己去想这个实现思路会比较难,但是直接看过了解过,就简单了。这是否和做数学题一样,有时候没有解题思路,参考了别人的解题思路就会了。没事儿做看看闲书,就是就和知道,了解下方法论,需要用的时候可以知道去哪里找,不会没有方向,目的就达到了,哈哈~

你可能感兴趣的:(python游戏开发中的数学和物理--Apple的学习笔记)