《游戏编程算法与技巧》输入和声音篇

游戏输入

可分为两种:数字模拟

数字输入

只用两种状态:“按下”和“没有按”(例如键盘)

如何实现一直按着的判断? 同时跟踪上一帧和这一帧的状态,根据这两个状态来判断

上一帧状态 本帧状态 结论
释放 释放 一直释放
释放 按下 刚刚按下
按下 释放 刚刚释放
按下 按下 一直按下

伪代码:

 1enum KeyState {
 2    StillReleased,
 3    JustPressed,
 4    JustReleased,
 5    StillPressed
 6}
 7
 8lastState[256];      // 上一帧状态
 9currentState[256];   // 当前帧状态
10
11// 每帧更新状态
12void UpdateKeyboard() {
13    lastState = currentState;
14    currentState = GetKeyboardState();  // 获取当前状态
15}
16
17// 通过keyCode获取KeyState
18KeyState GetKeyState(int keyCode) {
19    if (lastState[keyCode])
20        if (currentState[keyCode])
21            return StillReleased;
22        else
23            return JustReleased;
24    else
25        if (currentState[keyCode])
26            return JustPressed;
27        else
28            return StillReleased;
29}

模拟输入

可返回某个数字的范围(如遥感)。但遥感的数值基本不会归零所以需要输入过滤,来消除偏差值。一般取遥感总值的10%为无效值。

遥感的范围为-32768 ~ 32768。最终遥感映射到0 ~ 1的伪代码为:

 1float deadZone = 3000   // 无效值
 2float maxValue = 32677  // 最大值
 3
 4Vector2 GetJoy() {
 5    Vector2 joy = GetJoystickInput() // 获取遥感的输入
 6    float length = joy.length() // 当前值长度
 7
 8    // 小于无效区域则没有输入
 9    if (length < deadZone) {
10        joy.x = 0
11        joy.y = 0
12    } else {
13        // 计算无效区到最大值之间的百分比
14        float pct = (length - deadZone) / (maxValue - deadZone);
15
16        // 正规化向量
17        joy = joy / length;
18        joy = joy * maxValue * pct;
19    }
20
21    return joy
22}

移动设备输入

触摸和手势

大多数移动游戏都是通过多点触摸来实现虚拟手柄。还有一些游戏利用手势操作。自定义手势会比较难实现。Rubine算法就是一种流行的实现自定义手势检测的算法

加速器

检测设备坐标系轴向上的加速度。总有一个常量添加到设备上:重力。意味着如果设备为空闲状态,加速器可以粗略地通过重力方向检测设备的朝向

陀螺仪

检测设备关于设备轴向的旋转

声音

声音同时播放的频道数量是有限的。所以声音应该有优先级数据

原始数据

又类似Audacity等工具创建的原始音频文件

  • 短音效一般存储为WAV格式或者其他无压缩文件格式
  • 长音效一般会采用压缩文件格式比如MP3或者OGG

这两种音效一般会分别用两种方式播放

  • 短音效(或常用音效)一般会直接加载到内存中缓存,需要时直接播放
  • 长音效一般会以流方式加载。就是播放的同时加载

播放场景

声音的播放场景其实有很多,不同游戏有不同的情况。一般来说分为几种

  • 短音效的事件触发播放(Unity的Animation中的事件元通道触发),比如脚步声
  • 过长动画播放(Unity中的Timeline制作)
  • 对话中的音频播放(一般会在对话树中抛出事件播放)
  • 背景持续音

3D声音

2D音效是指没有位置相关的音效。而3D音效是由随着监听者和发射者距离增大而衰减的方式。

对于3D声音监听者的摆放位置

第一人称游戏则很自然的跟随摄像机就能提供最好的效果。但是对于第三人称游戏放在摄像机位置会感觉很怪。放在人物位置如果一个声音在人物和摄像机直接则也会有问题。所以一个解决方式是放在摄像机和玩家之间,朝向跟随摄像机。具体还是得根据游戏类型来测试最优结果

数字信号处理

广义上讲,数字信号处理(DSP)是计算机中表示的信号。在音频领域中,其表示加载音频后再修改之后得到的不同效果。

为什么不把所有的效果离线处理,而要在运行时处理?

可以节省内存

数字信号处理效果

  • 高音偏移(多普勒偏移):通过调整牝鹿增加或减小音效的音高
  • 压缩机:缩小音量范围,导致很小的声音得到了加强,同时很大的声音得到了减小。
  • 低通滤波器:通过删减频率减小音量

多普勒效应

由于声波在空气中传播需要时间,所以发声者靠近时,意味着声波都比前一个要早到。导致了频率的增加,就会有更高音高。所有与波相关的情况都会有多普勒效应

声音遮挡

声音遮挡在声音不是直接由发射者传递到监听者的时候发生。声音遮挡主要就是低通滤波的结果,意味着高频率的声音的音量被移除了。这是因为低频率的音波比高频率的音波更容易传播。但是,声音遮挡的另一个输出就是整体音量的降低

相似但是不同的想法就是声音衍射。通过声音衍射,声音可能不再是直线传播的了,但是还是有可能穿透障碍物

检测遮挡和衍射的方法就是为发射者构造一系列指向监听者附近的弧形。如果没有一个能量到达,则就是遮挡。如果有一些能到达就是衍射。如果全部能到达就都不是。这个方法称之为Fresenl声学衍射