已归档 - Unity* 和英特尔® 实感™ 摄像头助力游戏开发

英特尔® 实感™ SDK 已停产。 不再继续提供支持或更新。

所有互动游戏都有游戏开发人员必须实施的功能:游戏对象应遵循物理定律,并与其他游戏对象发生接触;游戏对象应触发事件,比如播放声音和计分;以及响应用户的输入,比如操纵杆、鼠标和键盘。通常情况下,开发人员必须针对每个目标平台反复实施这种功能,过程非常耗时。为了减轻负担,开发人员可选择使用游戏引擎,后者包含可执行常规任务的不同函数,从而将更多精力集中于增强游戏创意。

Unity 技术的交叉平台游戏引擎Unity* 3D 是一款极具吸引力的解决方案。它的目标平台包括计算机、游戏控制台、移动设备和 web 浏览器。Unity 3D 还支持 C++、C#、Unity Script(类似于 JavaScript*),Boo 等不同的编程语言。

本文同时适于初学者和专家阅读。对于之前从未使用过 Unity 的开发人员,我们在本文的第一部分做了简要介绍。然后演示如何结合使用手部追踪和英特尔® 实感™ SDK ,以借助 C# 和英特尔实感 3D 摄像头开发一款小游戏。如欲加入游戏开发行列,您需要: 安装于计算机的 Visual Studio和 Unity,以及英特尔实感 SDK 和英特尔实感 3D 摄像头。

下载、安装和配置 Visual Studio*

您可以免费下载 Unity。Unity 基于 Microsoft Windows* OS 和 Apple Mac* OS X* 运行。安装 Unity 后,您应完成免费注册流程。

本文所显示的代码基于 C# 编程语言编写而成。IDE Unity 提供的默认交叉平台为MonoDevelop。如果您是 .NET 开发人员,肯定十分希望 Unity 和Microsoft Visual Studio能够完美地协同工作。第一步为连接 Unity 和 Visual Studio。为此,您需要从下拉菜单中选择 Edit -> Preferences…。将打开 Unity Preferences 对话框。选择左侧的 External Tools(图 1) 对于外部脚本编辑器,点击 ComboBox 中的 MonoDevelop,并使用Browse… 按钮查找 Visual Studio .exe 文件。使用默认设置安装 Visual Studio;程序的位置路径应为:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe.

这些是成功完成连接的全部必要步骤。


图 1. 连接 Visual Studio* 和 Unity*

环境

现在是配置 Unity 环境的部分特性的最佳时机。主屏幕包含多个不同的窗口。可手动选择设置或使用默认设置,查找最理想的安排。我们从右上角的 Layout 下拉菜单中选择常用设置 Tall


图 2. 选择窗口的位置

场景窗口和游戏窗口

第一个较大的场景窗口(图 3)主要用于定位以及布局您的游戏世界。以 3D 为标准实现内容可视化。通过拖放操作可轻松定位游戏对象。您可直接查看主屏幕上方的游戏选项卡。通过该游戏选项卡,可预览渲染后的游戏,显示在所需的平台上。


图 3. 场景窗口

图 4. 游戏窗口

层级窗口 (Hierarchy Window)

层级窗口(图 5)包含相关的内容对象,例如,游戏对象、摄像头视图、光照对象、UI 元素和音频。可在 "GameObject" 下的菜单中,或直接在窗口中使用 "Create" 创建新的内容对象。


图 5. 层级窗口

检查器窗口 (Inspector Window)

在层级窗口中选中内容对象后,检查器窗口将显示其属性(图 6)。通过添加 'add component' 按钮提供的组件,可进行功能扩展。例如,名为 rigid body 的组件,可为游戏对象提供重力。


图 6. 检查器窗口

项目窗口

项目窗口中排列了多个项目文件,比如游戏场景、游戏对象、音频、图片和脚本文件(图 7)。选择文件时,检查器显示相应的属性和预览信息。双击文件名称,可直接打开应用的脚本文件,及其开发环境。


图 7. 项目窗口

开发简单游戏

20 世纪 70 年代早期,Atari 创始人 Nolan Bushnell 成功发布了一款名为 “Pong” 的游戏。游戏规则非常简单,与乒乓球类似:球在屏幕中来回移动。每名玩家控制一把垂直球拍。如果玩家没有成功回球,对手将获得一分。以下步骤将介绍如何在 Unity 中开发 Ping Pong 游戏。

通过 File -> New Project 创建新项目 PingPongUnity


图 8. 创建新项目

球拍

首先添加球拍,在层级窗口中点击 Create -> 3D Object -> Cube。如需改变球拍的形状和位置属性,您需要选择生成的立方体,并在检查器窗口中对其进行更改。我们将名称更改为 Paddle1,并在转换部分自定义它的形状。所需的 scale- 值包括: X: 0.5,Y:3,Z:1。将 Position 设置为: X: -9,Y: 1, Z: 0。现在,第一只球拍就完成了。在层级窗口中右击该球拍,然后在环境菜单中点击 Duplicate。这样可复制一把相同的球拍。现在,您只需将该复制品重命名为 Paddle2。然后将 Position。 的 X。 值改为 9。 即可。

为使球拍做出正确的物理行为,我们需要点击 “Add Components” 按钮,为每只球拍添加 RigidBody 组件。这样我们可在代码范围内移动球拍,使球拍和球发生碰撞。现在,球与球拍发生碰撞后,弹出了球场外。这不是我们想要的行为。为避免出现这种行为,我们需要为新增的 RigidBody 设置限制属性。球拍只允许上下移动,因此我们需要在冻结旋转部分选择 x、y 和 z,并在冻结位置只选择 x。


图 9. 生成的球拍

墙壁

打乒乓球时,我们需要两面墙壁:上壁和下壁,以便球反弹出去并留在球场内。因此,我们再次使用立方体。再次点击层级窗口中的 Create -> 3D Object -> Cube。命名为 WallBelow。墙壁的形状稍长一些,将 scale 值设置为: X: 23,Y: 1,Z: 1。位置的值设为: X: 0,Y: -5, Z: 0

生成第二面墙壁时,只需复制刚刚生成的墙壁,并将其命名为 WallAbove。现在将 Y-Position 更改为 7


图 10. 生成的墙壁

游戏最后,也是最重要的一个对象是:球。Unity 提供了一个符合形状的球。只需点击层级窗口中的 Create -> 3D Object -> Sphere。将名称更改为 Ball,并将Position 设置为 X: 0,Y: 1, Z: 0。如欲添加相应的物理属性,您需要使用 "Add Components" 添加 rigid body 组件,并删除属性选择 "Use gravity”。

图 11. 生成的球

调整摄像头视角,并增加亮度。

游戏的场景显示和游戏窗口仍然不够理想。我们需要更改摄像头设置。在层级窗口中,选择标配摄像头 Main Camera。然后将当前的 3D 视图改为 2D 视图。这一步骤在摄像头 Projection 属性中完成。将 Perspective (3D) 更改为 Orthographic (2D)。这样我们可获得正面的视图。并将新的 Size 属性更改为 10

如果场景窗口的显示仍然不够理想,您可以点击坐标轴旋转视图。


图 12. 点击坐标轴启用视图旋转功能。

这样,摄像头获得了比较理想的设置。但游戏对象仍然较暗。为增加亮度,需要切换至层级窗口,然后点击 Create -> Light -> Directional Light。现在,您可以看到,游戏窗口发生了变化。


图 13. 游戏窗口中的游戏

移动球

游戏场地已经准备好,但仍需一些动作。如果运行游戏,球落地,就可以了。我们需要编写几行 C# 代码,使球运动起来。为此,我们点击项目窗口中的 Create 按钮,创建名为 “Ball” 的 C# 脚本文件。然后,创建脚本文件和游戏对象之间的绑定。这一步骤的具体做法为:将文件拖放至层级或场景窗口中的游戏对象。选择球体对象后,检查器窗口将显示脚本属性。

双击 C# 文件,自动启动您设置好的开发环境:MonoDevelop 或 Microsoft Visual Studio。生成的 C# 类将以标准结构开始。它包含 MonoBehavior 基本类,是 Unity API 的一个界面。“Start” 方法可用于一次性初始化和准备。游戏框架每秒处理几张图片,就好像您正在观看电视或手翻书。单张图片称为图画。绘制图画之前,每次需调用 “Update” 方法。我们将这种反复称之为游戏循环,这意味着其他游戏对象可在不考虑玩家动作的情况下行动。

在该游戏中,我们希望设置球落地的方向,在游戏开始前设置一次。“Start” 方法似乎非常可行。您可在表 1 中查找所需的代码。

using UnityEngine;

public class Ball : MonoBehaviour
{
    private float _x;
    private float _y;

    // Use this for initialization
    void Start ()
    {
       _x = GetRandomPositionValue();
       _y = GetRandomPositionValue();
        GetComponent<Rigidbody>().velocity = new Vector3(Random.Range(5,10) * _x, Random.Range(5,10) * _y, 0);
        
        Debug.Log("x: " + _x);
        Debug.Log("y: " + _y);
    }

    private float GetRandomPositionValue()
    {
        if (Random.Range(0, 2) == 0)
        {
            return -1;
        }
        return 1;
    }

    // Update is called once per frame
    void Update () 
    {
    }
}

表 1. 借助 C# 让球在一个方向下落

游戏对象的真正魅力隐藏于组件部分。有一款组件可实现所有游戏的逻辑。表 1 中的 C# 代码可通过 GetComponent 方法访问当前游戏对象的 Rigidbody 组件。Rigidbody 通过重力功能扩大游戏对象。设置速度属性时,会分配一个新的 Vector3 实例。它可定义当前重力的随机方向。

第二次游戏测试表明,球可随机朝不同的方向落下。但球碰到墙壁后,将沿着屏幕上行,我们不希望出现这种行为。它应该更像在墙壁之间反弹的橡皮球。

Physic Material 可帮助达到这种效果。我们点击项目窗口中的 Create 按钮,创建 Physic Material。将它重命名为 Bouncy。如需实现反弹行为,还要对属性做些调整。在检查器窗口中,将属性 Dynamic FrictionStatic Friction 的值设为 0Bounciness 设为 1Friction Combine 要求设为最小值,Bounce Combine 设为最大值。


图 14. 添加和设置 Physic Material

为使所有游戏对象使用我们生成的 Physic Material,必须设置通用物理设置。为此,点击菜单 Edit -> Project Settings -> Physics。将 Physic Material Bouncy 拖放至 Default material 属性。属性 Bounce Threshold 应设为 0.1。如果现在启动游戏,球的动作将与橡皮球一样。


图 15. 设置 PhysicsManager 中的 “Bouncy”

用手控制球拍

此款游戏即将完成。游戏对象已各就各位,球也处于运动之中。剩下的一点就是控制球拍的逻辑。为此,最佳的方法是使用创新型输入设备,即 3D 摄像头,该摄像头已普遍用于 Microsoft Xbox* 和 Kinect*。部分最新平板电脑和超极本™ 设备也配备了 3D 摄像头。如果没有 3D 摄像头,也可以使用这些经济型设备。开发游戏时,您需要下载免费的英特尔实感 SDK。安装后,您可以在英特尔实感 SDK 文件夹中查找到一些 Unity 示例。

图 16英特尔实感 SDK 示例

Unity 3D 支持第三方插件,并为英特尔实感 SDK 创建了一个这样的插件。如欲集成英特尔实感 SDK,必须将 SDK DLL 文件添加至项目。在 Unity 中选择 Assets -> Import package -> custom package.... 然后选择 RSUnityToolkit.unitypackage 文件,该文件位于 C:\Program files (x 86) \Intel\RSSDK\framework\Unity 路径下。将会弹出类似图 17 所示的对话框窗口。该对话框默认选择了所有文件。点击 None 清空选项,只选择插件目录。如果基于 64 位计算机开发游戏,您还需手动替换导入的 DLL 文件。这些文件位于下列目录:C:\Program Files (x86)\Intel\RSSDK\bin\x64


图 17使用 "import package" 将英特尔实感 SDK 集成至 Unity*

集成后,现在可开始开发球拍逻辑了。我们需要另外一个 C# 文件,可通过点击项目窗口中的 Create 按钮生成。该文件名称为 Paddle。游戏过程中只需生成一个常见实例即可响应两只球拍。这一点非常重要,因为英特尔实感 3D 摄像头只能连接一个实例,无法连接多个。因此,我们在层级窗口中将新文件拖放至 "main camera" 游戏对象,以实现连接。

接下来,双击 Paddles.cs 文件。即使文件连接了主摄像头,仍然需要球拍游戏对象。Unity 中多个游戏对象之间的连接通过属性就可实现。在 C#,您需要使用 “get” 和 “set” 宣布其他属性,但在 Unity 中不需要。我们需要做的只是针对每支球拍创建类型 GameObject 的公共变量,如表 2 所示。现在,可将游戏对象拖放至创建好的球拍属性,以实现连接。

public class Paddles : MonoBehaviour
{
    public GameObject Paddle1;
    public GameObject Paddle2;
..
Listing 2 – Self written properties


图 18. 借助 Unity* 宣布面向 Paddles.cs 文件的游戏对象

英特尔实感 SDK 提供类 PXCMSenseManager,可实现与摄像头的永久连接。该启动函数只能从 Unity 引擎中调用一次,因此是一般实例准备的理想选择。这是我们在启动函数中初始化 PXCMSenseManager 类的原因。为此,我们首先为其提供 QueryHand 模块,这样有助于我们读取手势。如欲识别面部表情和语音,需采用其他模块。摄像头的实例在启动函数外以 _pxcmSenseManager 字段的形式宣布。代码如表 3 所示。

public class Paddles : MonoBehaviour
{
    public GameObject Paddle1;
    public GameObject Paddle2;

    private PXCMSenseManager _pxcmSenseManager;
    private PXCMHandModule _pxcmHandModule;

    // Use this for initialization
    private void Start()
    {
        _pxcmSenseManager = PXCMSenseManager.CreateInstance();

        if (_pxcmSenseManager == null)
        {
            Debug.LogError("SenseManager Initialization Failed");
        }
        else
        {
            pxcmStatus pxcmResult = _pxcmSenseManager.EnableHand();
            if (pxcmResult != pxcmStatus.PXCM_STATUS_NO_ERROR)
            {
                Debug.LogError("EnableHand: " + pxcmResult);
            }
            else
            {
                _pxcmHandModule = _pxcmSenseManager.QueryHand();
                _pxcmSenseManager.Init();

                PXCMHandConfiguration configuration = _pxcmHandModule.CreateActiveConfiguration();
                configuration.EnableAllGestures();
                configuration.ApplyChanges();
                configuration.Dispose();
            }
        }
    }
...

表 3. 与英特尔实感 3D 摄像头的永久连接

采用更新函数(游戏循环)可直接查询最新的摄像头数据,我们可因此确认摄像头是否捕捉到了手部。左手用于控制左球拍,右手控制右球拍。因此,您需要将计算后的数值分别分配给各自的球拍。因为逻辑非常相似,因此我们创建私有函数 MoveBall。

如果玩家退出或重新启动游戏,需要断开摄像头的直接实例。Unity 引擎可自动调用的函数 OnDisable 非常适用于这种情况。

   // Update is called once per frame
    private void Update()
    {
        if (_pxcmSenseManager == null)
        {
            return;
        }

        _pxcmSenseManager.AcquireFrame(false, 0);

        _pxcmHandModule = _pxcmSenseManager.QueryHand();
        PXCMHandData handData = _pxcmHandModule.CreateOutput();
        handData.Update();

        MoveBall(handData, PXCMHandData.AccessOrderType.ACCESS_ORDER_LEFT_HANDS, Paddle1);
        MoveBall(handData, PXCMHandData.AccessOrderType.ACCESS_ORDER_RIGHT_HANDS, Paddle2);

        _pxcmSenseManager.ReleaseFrame();
    }

    private void MoveBall(PXCMHandData handData, PXCMHandData.AccessOrderType accessOrderType, GameObject gameObject)
    {
        PXCMHandData.IHand pxcmHandData;
        if (handData.QueryHandData(accessOrderType, 0, out pxcmHandData) == 
            pxcmStatus.PXCM_STATUS_NO_ERROR)
        {
            PXCMHandData.JointData jointData;
            if (pxcmHandData.QueryTrackedJoint(PXCMHandData.JointType.JOINT_CENTER, out jointData) ==
                pxcmStatus.PXCM_STATUS_NO_ERROR)
            {
                gameObject.GetComponent<Rigidbody>().velocity = new Vector3(-9, jointData.positionWorld.y*100f, 0);
            }
        }
    }

    private void OnDisable()
    {
        _pxcmHandModule.Dispose();
        _pxcmSenseManager.Dispose();
    }
}

表 4. 用手移动球拍

重启游戏

生成游戏对象,并编写逻辑代码后,现在可以测试游戏了。如果摄像头可在 1-5 米的范围实现手部识别,球拍将会移动。球碰到球拍后,将会反弹至对面。如果球离开球场,未触碰到球拍,玩家将无计可施。在这种情况下,如果球离开球场,应自动重启游戏。

只需几行代码就可实施 Ball.cs 文件的逻辑。如果球还在视线范围内,应检查 Update 函数。如果看不到球,Application.LoadLevel 函数将重启游戏,如 表 5 所示。

private bool _loaded;

// Update is called once per frame
void Update () 
{
    if (GetComponent<Renderer>().isVisible)
    {
        _loaded = true;
    }

    if (!GetComponent<Renderer>().isVisible && _loaded)
    {
        Application.LoadLevel(Application.loadedLevel);
    }
}

表 5. Ball.cs: 看不到球时重启游戏

发布游戏

第一版游戏已就绪,当然,我们希望同大家一起分享。菜单选项 File | Build Settings (Ctrg + Shift + B) 可打开 Build 对话框。您可以从包含众多选择的 Platform 框中选择用来运行游戏的平台。我们选择 Windows,并将 x86_64 设置为架构。点击 Build 即可启动编译器并生成面向 Windows 的.exe 文件。如欲玩游戏,您需要配备包含预安装驱动程序的英特尔实感 3D 摄像头。


图 19. 创建 .exe 文件发布游戏

现在轮到您了

我已经向大家演示了如何创建有趣的游戏,但还需做进一步改进。那么,向我展示您的学习成果吧。构建高分、声效和炫酷特效的游戏。请将视频上传至 YouTube*,然后通过电子邮件将创建好的游戏发送给我。非常高兴看到大家的创意。现在,请用英特尔实感 3D 摄像头尽享首款 Unity 游戏的乐趣吧。


图 20. 玩游戏
图 21. 游戏 UI

关于作者

Gregor Biswanger(微软 MVP,英特尔黑带开发人员, 英特尔软件创新者)是CleverSocial.de的创始人,兼自由讲师、顾问、培训师、作家和演讲者。他为大中型企业和机构提供关于软件架构、灵活流程、XAML、Web 应用和混合应用开发、云解决方案、内容营销和社交媒体营销方面的建议。

他在 video2brain 和 Microsoft 发布了一些关于上述主题的培训视频。他还是一名自由作家,为heise Developer撰写在线文章,并为商业类杂志撰写文章。他主笔撰写过 entwickler.press 出版的《交叉平台开发》一书。

他还经常参加国际会议,或在会议上发言。英特尔曾聘请他担任英特尔® 开发人员专区的技术顾问。此外,他还是 INdotNET (Ingolstaedter .NET Developers Group) 的负责人。点击此处,查找关于他的联系方式: https://about.me/gregor.biswanger

有关编译器优化的更完整信息,请参阅优化通知