基于ARKit的Unity3d游戏开发--僵尸围城

基于ARKit的Unity3d游戏开发--僵尸围城
无可置疑的是,对绝大多数的中小游戏团队来说,目前和Unreal Engine4(虚幻4)已经成为3D游戏开发的首选商业引擎。因为Unity3d的简单易上手特性,强大的功能和丰富的游戏资源及扩展功能(通过Asset Store),很多初学者选择了从它开始进入游戏开发的世界。
然而遗憾的是,从2010年之后炙手可热的移动游戏(手游)开发如今已经成了超级红海,甚至是血海。如果大家只是纯粹的个人兴趣导向,那么还是可以学一学unity3d,然后试着做一两款自己喜欢的手游上架。但是如果从商业的角度考量,传统意义上的手游市场已经成了巨头的禁脔。作为小型团队或者独立游戏开发者,或许需要探索一条全新的道路。
苹果ARKit和Google ARCore的横空出世给我们照亮了前行的方向,虽然目前增强现实和虚拟现实技术都还相当不成熟,但在未知的蓝海中探索,总好过在红海中和一帮巨人搏命厮杀。
在本系列的教程中,我们将一起学习如何从零开始学习基于ARKit的Unity3d移动游戏开发。全系列的教程都是基于实战项目的,而且尽可能考虑到初学者可能会遇到的种种困难和障碍。
Part 1 开始前的准备
硬件设备:
首先要说明的是,既然本教程是基于ARKit的Unity3d移动游戏开发,那么有两大硬件设备是必不可少的。
1.苹果电脑或Wndows系统


2.iPhone或iPad pro
因为ARKit本身对运算性能的要求,因此需要A9或以上芯片,iPhone 6s之前的设备基本上都已经无法使用了。所以建议大家使用6s之后的iPhone,或者是iPad Pro。
下面列出了支持ARKit和Core ML的iOS设备清单:
        •        iPhone 6s and 6s Plus
        •        iPhone 7 and 7 Plus
        •        iPhone SE
        •        iPad Pro (9.7, 10.5 or 12.9) – 1代和2代
        •        iPad (2017)
        •        iPhone 8 and 8 Plus
        •        iPhone X

AR游戏开发和普通游戏开发最大的区别就是,模拟器对于AR游戏开发的作用微乎其微,绝大多数时候我们都需要随时通过iPhone或iPad设备进行测试,所以这个也是不能省的。
系统环境:
操作系统是macOS Sierra或High Sierra
特别说明一下,目前(2017年11月)有大量的第三方软件还不兼容High Sierra,因此个人目前使用的还是Sierra 10.12.6。建议大家如非必要,先不要着急升到High Sierra,可以等到明年初之后升级到新的操作系统。


开发工具:
Xcode
Xcode 9及以上版本是必不可少的了,本教程写作时所使用的版本是Xcode 9.1(9B55)
 

如果你还没有安装过Xcode,那么可以在App Store(Mac系统上的)上搜索Xcode并安装。
当然,还有另外一种方式可以安装Xcode,特别是后面需要用到最新beta版的Xcode时。
直接登陆开发者网站:developer.apple.com 
在输入开发者账号密码后,点击Downloads。没有Apple ID的童鞋需要通过注册登陆界面注册一个新的账号。
然后就可以下载最新的beta版本的Xcode了。
Unity3d
目前Unity最新版本为Unity2017.3,本教程写作时所使用的版本是Unity 2017.2,我们就需要下载旧版本的Unity,首先我们进入官网。
官网链接:https://unity3d.com/

 

选择个人版下载。

 

我们下载Unity2017.2需要将页面拉倒最下方,然后点击下载旧版本Unity

 

在旧版本页面下找到Unity2017.2,点击下载(Mac)。
 
Part 2- Unity和C#入门
Unity的功能很强大,而其使用界面也很容易上手。但即便如此,我们没有必要一上来就像教科书或者手册一样面面俱全,而是在实际开发项目的过程中来逐渐熟悉。
创建新项目
首次打开Unity 2017.2(或者其它版本),你会看到类似下面的界面:

 

你可以选择Skip跳过,也可以选择点击create one,注册一个Unity ID,然后使用自己的Unity ID来登录。
建议大家还是可以注册一个Unity ID的,因为后面在使用Asset Store(游戏资源商城)购买或下载游戏资源时,也还是需要用到的。另外如果你是Unity Plus或者Pro的用户,显然更应该使用Unity ID来登陆,以享受Plus和Pro用户的特权。


登陆成功后会看到如下界面:

 

点击右上角的New,就可以创建新的项目了。
在project name那里输入项目名称,比如HelloARKit,然后点击Location下面右侧的省略号,选择项目存放的位置,

 

注意到我们可以选择3D或者2D,这是因为Unity3d既可以用来开发3D游戏,也可以用来开发2D游戏,但作为增强现实游戏来说,显然是要选择默认的3D了。2D旁边还有一个Add Asset Package,这个项目中我们暂时不会用到,但是在后续的其它项目中可能会需要在创建时就选择游戏资源包。比如当我们已经在某个项目中导入了ARKit的插件之后,那么在创建新的基于ARKit的项目时,就可以直接通过这里勾选插件,而不再需要重新一步步来导入。

 

这里还有一个Enable Unity Analytics选项,这是Unity官方提供的新特性,可以帮忙分析用户行为,无需添加任何SDK。这里我们保持默认即可。
一切就绪后,点击Create project,我们的第一个项目就创建成功了。此时Unity会自动进入编辑器界面,如下所示:

 

大概的介绍一下,
A区是所谓的Hierarchy,这里会显示某个游戏场景中的所有游戏对象,而且不同的游戏对象之间存在父子从属关系。
B区是几个核心的视图区,比如Scene(场景)视图,Game(游戏)视图,Asset Store(游戏资源商城)视图,Animator(动画控制器)视图,等等。
C区和D区是Project视图,这里显示了项目中的所有游戏资源(不一定是当前场景中用到的)。
其中C区有点类似文件夹结构,而D区中则是具体的资源。
E区是Inspector视图,显示了游戏对象的各种组件及属性。
F区是工具栏,里面有各种有用的工具。
此外在Mac系统中,菜单栏是在系统最顶部的。
Unity的界面很丰富,但是这里不打算一次性灌输太多,不然也记不住。



 

在上一部分的学习中,我们重点是做了一些前期准备工作,从这一课开始,我们将开始学习如何使用Unity开发基于ARKit的移动游戏

创建新项目

打开Unity,点击右上角的New以创建新的项目。
 

然后在如图中所示的方框处填上项目名称(Project name),这里填的是ZoombieShooter,在红色箭头所示的地方选择项目存放的文件路径(Location),最后点击Create project按钮即可。

 

创建完项目后Unity会自动打开编辑器,如下图所示。

 

接下来让我们导入ARKit的插件。
点击Asset Store选项卡,然后在Search搜索栏中搜索ARKit,如下图所示。

 

点击Unity ARKit Plugin进入详情页,然后下载并导入该插件。如果看到类似下图的提示,直接点击导入即可。

 

 

导入成功后,可以在Project视图中看到插件的相关内容

在Examples目录中可以看到其中提供了多个示例项目,我们目前只需要用到UnityARKitScene,双击并打开该场景,如下图所示。

 

让我们把目光的焦点切换到Hierarchy视图中的游戏对象上:

 

其中Directiona llight的作用比较直接,也即模仿日常生活中的太阳光,可以让场景中的物体投射出阴影。
接下来是RandomCube,这个游戏对象并没有特别重要的作用,主要用来判断朝向。

接下来是比较重要的两个游戏对象,CameraParent和ARCameraManager,用来生成AR的效果。
HitCubeParent这个游戏对象也很重要,通常我们会把最主要的AR游戏对象放在这里。

 

然后是GeneratePlanes,用来生成示例场景中用来表示地面的蓝色方框。
而PointCloudParticleExample则是用来生成示例场景中的点云。

接下来让我们在设备上实际体验一下。

首先让我们从Hierarchy视图中删除ARKitControl,因为暂时不需要用到它。

使用Files-Save Scenes保存当前场景,然后从菜单中选择File -Build Settings,进入项目编译设置。

 

在上方的Scens In Build中勾选UnityARKitScene这个场景,然后在下方的Platform中选择iOS,然后点击Switch Platform切换平台设置。

 

等待操作完成后点击Player Settings,并进行以下项目的设置:
首先要设置Company Name和Product Name,在这里输入自己的工作室名称和产品名称即可,

 

接着要设置产品的Bundle Identifier(标识符),以及Camera Usage Description(摄像机使用描述)。

 

需要注意的是Camera Usage Description可以使用默认的描述,也可以更改为自己希望显示的描述。

全部设置完成后,点击Build And Run按钮,

 

在弹出的对话框中输入产品名称,然后点击Save即可。

编译完成后会自动打开Xcode,这个时候会看到红色的错误提示,然后要设置Team信息,如下图所示。

 

需要注意的是,如果在Team设置处找不到相关的信息,那么需要添加自己的苹果开发者账号。

然后选择自己的苹果设备,点击工具栏上的编译运行按钮即可。


 

 
 

接下来让我给场景中添加一些游戏对象。
首先让我从Hierarchy中删除RandomCube这个游戏对象,因为我不需要用到它。
接下来我们可以禁用GeneratePlanes,具体操作方法是在Hierarchy视图中选中GeneratePlanes,然在Inspector面板中取消勾选。

 

同样的,我们可以禁用PointCloudParticleExample,具体操作和禁用GeneratePlanes类似。

 

清理工作完成了,接下来让我们添加所需要的游戏资源。

在Unity中打开AssetStore,然后搜索abandoned house,并选择FREE ONLY,找到Abandoned buildings这个资源包。

 

下载并导入该资源包。

接下来在AssetStore中搜索zombie,还是选择FREE ONLY,并找到下面这个资源。
 

下载并导入该资源包即可。

接下来对资源做一个小小的整理。
在Unity的Project视图中右键单击Assets,选择Create-Folder,创建一个新的文件夹,把它命名为Components。
然后把刚才下载导入的游戏资源Buildings和Zombie两个文件夹拖到Components中。

现在我们可以在游戏场景中使用刚才所下载的游戏资源了。

在Project视图中找到Components-Buildings-Prefab,找到ruined_house这个预设体。
在Hierarchy视图中删除HItCubeParent的子对象HitCube,然后将ruined_house这个预设体拖到Hierarchy视图中,使其成为HitCubeParent的子对象。
在Hierarchy视图中保持选中ruined_house,然后在Inspector视图中删除Animator这个组件。

 

然后点击Add Component,添加一个新的Unity AR Hit Test Example组件,如下图所示。

 

然后将ruined_house这个游戏对象拖到Unity AR Hit Test Example组件的Hit Transform属性处,如下图所示。

 
然后让我们更改ruined_house游戏对象的大小,调整Transform中Scale的数据为6,6,6,将房子整体放大6倍,如下图所示。

 

为了让场景更加真实,我们需要给房子添加一些阴影。

在Hierarchy视图中选择Directional Light,在Inspector面板中更改Shadow Type为Soft Shadows,如下图所示。

 

不过现在阴影只是在房子里面有,在房子外面则没有,这是因为我们没有添加游戏场景中的固定地面,因此我们需要修补这个问题
在Project视图的搜索栏中输入shadowplane,并找到shadowPlanePrefab这个预设体。

 

将其拖到Hierarchy视图中,使其成为ruined_house的子对象,如下图。

 


在Hierarchy视图中保持选中shadowPlanePrefab,然后调整Transform中的Scale为100,1,100,如下图。

 

可以看到此时的阴影比较符合要求了。

接下来我们将把游戏中的敌人添加到游戏中,在Project视图中找到Aseets-Components-Zombie-Model中的z@walk,并将其拖到Hierarchy视图中。

接下来使用移动工具将敌人移动到合适的位置,如下图。
 

接下来我们将给这个敌人添加动画。
在Unity中有两种方式给游戏对象添加对象,分别是Legacy 和使用Animator的方式(又被称为Mecanim动画系统)。
对于游戏角色来说,目前我们基本上都会使用Animator。
在Hierarchy视图中选中z@walk游戏对象,在Inspector中可以看到有一个Animator组件。

 


此时Animator组件中的Controller属性是None,也就是还没有创建合适的Animator。
因此我们要在Unity的Project视图中右键单击,选择Create-Animator Controller,
将其更名为zombieController,双击将其打开,在Unity界面的中间部分会打开一个Animator视图,如下图所示。

 

对于Animator的使用,有一个所谓“状态机”的概念,
在绝大多数情况下,我们都需要随着游戏环境的变化让游戏对象在不同的动画状态之间进行切换。举个简单的例子,在某个游戏中,当我们按下键盘上的空格键,或者是游戏控制器上的某个特定按键时,玩家所控制的游戏角色会从行走动画切换到跳跃动画。即便某个游戏对象只有一个对应的Animation Clip,我们也需要让其归于一个Animator Controller的控制下。
Animator Controller使用State Machine(状态机)来管理游戏对象的不同动画状态及其之间的过渡。这个名词听起来有点可怕,但实际上很好理解。它可以看做某种类型的流程图,或是使用Unity内置的可视化编程语言所编写的小程序。我们可以在Animator视图中创建、浏览和修改Animator Controller的结构。
关于理论的部分暂不多讲,我们来通过具体的操作来熟悉如何设置Animator。
在Project视图中找到Assets-Components-Models-z@walk,将其展开,可以看到里面有一个名为walk的动画片段,也就是带一个播放按钮标志的文件,如下图所示。

 

Mecanim动画系统中的一个核心概念是Animation Clips(动画片段),其中包含了丰富的动画信息,如特定的对象将如何更改其位置、旋转及其它属性。每个动画片段都可被视为一个简单的线性记录。

 


Unity支持使用第三方软件所创建的动画片段,如Max或Maya,或是使用动作捕捉设备及软件所获取的动画片段。
在Project视图中选中walk动画片段,在Unity右侧的Inspector视图下方可以看到该动画的预览,点击播放按钮就可以预览动画。

 

把walk这个动画片段拖动到Animator视图中,会看到在Entry和walk之间自动创建了一个关联。

 

然后在Hierarchy视图中选择z@walk游戏对象,在Inspector视图的Animator组件中,在Controller属性处点击右侧的小圆圈,并选择刚刚创建的zombieController。
点击工具栏上的播放按钮,可以看到敌人的动画只播放了一次就停止了,这个问题需要修复。

 

在Project视图中找到刚才的walk动画片段,然后点击Inspector视图右上角的Edit按钮,勾选Loop Time选项,然后点击Apply按钮即可。

 


此时再次点击预览,会发现敌人角色的动画是循环播放的了。

 


注意一个小细节,刚才我们是在Game视图中查看的,现在则切换到了Scene视图中查看。

在上一课的内容中,我们往场景中添加了敌人,同时还设置了它的动画控制器。但是奇怪的是虽然僵尸敌人已经在循环播放动画了,但是我们也在预览场景中看到僵尸并没有真正的向前走动。因此,在这一课的内容中我们将让敌人在场景中正常行走。为了实现这一点,我们将需要借助代码的神奇魔力。在Hierarchy视图中选择z@walk游戏对象,在Inspector视图中点击Add Component,选择New Script,

 

输入名称EnemyMovement,然后点击Create and Add即可。

 

创建完成后右键单击该组件,选择Edit Script,从而在代码编辑器中打开该文件。当然,也可以双击打开。
 


默认情况下,我们使用的是MonoDevelop作为代码编辑器。
Unity提供了丰富的API可供调用,而事实上我们不太可能也没有必要记住所有的这些API。
一个常用的方法就是在浏览器中打开官方的文档备用:
https://docs.unity3d.com/ScriptReference/index.html
比如这里我们需要让敌人角色在场景中行走,那么显然要考虑更改其位置信息,而角色位置信息对应的其实就是Transform属性,那么我们需要在API文档中搜索Transform,
可以看到,API文档中不但给出了Transform的详细描述,还给出了示例代码。

 

在相关页面向下滚动,并找到Public Methods部分,里面有一个Translate方法,旁边的介绍是“让游戏对象的transform沿着translation的方向和距离移动”,显然这正是我们所需要的方法。点击Translate进入方法的详细介绍,会找到一段示例代码。

 

我们可以拷贝以上示例代码中用红色圈出的那一行代码,也就是

 

transform.Translate(Vector3.forward * Time.deltaTime);
然后把它粘贴到EnemyMovement.cs的Update方法中,此时其中的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyMovement : MonoBehaviour {

        // Use this for initialization
        void Start () {
                
        }
        
        // Update is called once per frame
        void Update () {
                
                transform.Translate(Vector3.forward * Time.deltaTime);
        }
}


保存后回到Unity编辑器,点击Play按钮预览效果,可以看到僵尸可以向前行走了。
但是此时它的移动速度有点太快了,让我们来做一下微调。
更改Update方法中刚才粘贴过去的那行代码,
transform.Translate(Vector3.forward * Time.deltaTime * 0.3f);
这里我们只是在最后乘以0.3f,从而降低僵尸的移动速度。
此外,我们还希望可以让僵尸始终朝着主摄像机的方向移动。
因此回到API在线文档,在Transform的Public Functions中有一个LookAt方法,通过链接进入该方法的详细描述和示例代码如下。

拷贝示例代码中Update方法中的这行代码,

 

transform.LookAt(target);
然后将其粘贴到EnemyMovement.cs中的Update方法中,
void Update () {
                transform.Translate(Vector3.forward * Time.deltaTime * 0.3f);
        transform.LookAt(target);
当然,可以看到这里的target使用红色标出,显然是有问题的。
更改这行代码,修改后的Update方法体代码如下:
void Update () {
                
        transform.Translate(Vector3.forward * Time.deltaTime * 0.3f);
        transform.LookAt(Camera.main.transform.position);
}


保存后回到Unity编辑器,点击Play按钮查看效果。可以看到代码起作用了,但是如果我们把主摄像机的位置移到半空中,那么僵尸也直接开始空中漫步了~

 

因此我们要继续完善代码。
回到API文档的Transform页面,在Variables中可以找到eulerAngles这个属性,它代表以degree为单位的Euler旋转角。关于什么是EulerAngles,这里暂时不做详细的解释,我们先了解下如何使用它来修复这个问题。
点击链接可以看到关于eulerangles的详细描述,

 

从中拷贝Update方法中的以下代码:
transform.eulerAngles = new Vector3(10, yRotation, 0);

将其粘贴到EnemyController.cs的Update方法中,
void Update () {
                
        transform.Translate(Vector3.forward * Time.deltaTime * 0.3f);
        transform.LookAt(Camera.main.transform.position);
        transform.eulerAngles = new Vector3(10, yRotation, 0);
        }

注意eulerAngles中Vector3的三个属性值其实就是Transform中Rotation的x,y,z值。我们需要限制僵尸在地面上移动,因此需要将Rotation中的x值和y值设置为0。
更改这行代码,更改后的Update方法体代码如下:

void Update () {
                
     transform.Translate(Vector3.forward * Time.deltaTime * 0.3f);
        transform.LookAt(Camera.main.transform.position);
        transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
        }
保存后回到Unity编辑器,点击Play按钮预览效果,发现无论怎么更改主摄像机在x轴和y轴的数值,都不会产生“僵尸上天”的怪异举动了~

在这一课的内容中,我们将给僵尸敌人添加碰撞检测机制。当敌人碰到Main Camera(也就是玩家)时,敌人将发起攻击。而为了实现这一点,我们需要添加碰撞检测。
在Unity编辑器的Hierarchy视图中选择z@walk游戏对象,然后在Inspector视图中点击Add Component,搜索Rigidbody,并将其添加到当前游戏对象上。

 

此时还需要再添加一个Box Collider组件,添加完成后的Inspector视图如下图:

 


在添加了Box Collider之后,可以看到僵尸敌人的周围出现一个绿色的盒子,接下来我们要设置Box Collider的大小。
 

点击Box Collider组件下面的Edit Collider,然后直接调整相关的大小,到觉得合适位置。
现在已经给敌人添加好了Box Collider,还需要给代表玩家的Main Camera添加碰撞机制。

在Hierarchy视图中选择Main Camera,然后点击Inspector视图中的Add Component,并添加一个Box Collider。
接下来我们还需要给敌人添加一个新的互动脚本,用来处理碰撞检测的相关事件。
在Hierarchy视图中选择z@walk游戏对象,然后点击Inspector视图中的Add Component,并添加一个新的脚本文件,将其命名为CollisionWithCamera。

 

我们将添加三个方法来实现碰撞检测。双击在MonoDevelop中打开该文件,并更改其代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionWithCamera : MonoBehaviour {

        // Use this for initialization
        void Start () {
                
        }
        
        // Update is called once per frame
        void Update () {
                
        }
        //碰撞开始

        void OnCollisionEnter (Collision col)
        {
                //判断碰撞体中是否有主摄像机
                if (col.gameObject.tag == "MainCamera") {
                
                        Debug.Log ("enter");
                }
        }

        //碰撞结束
        void OnCollisionExit(Collision col){

                //判断碰撞体中是否有主摄像机
                if (col.gameObject.tag == "MainCamera") {
                
                        Debug.Log ("exit");
                }
                
        }

        void Attack(){
                        
        }
}

这里我们添加了三个方法,分别是OnCollisionEnter,OnCollisionExit,以及Attack方法,其作用如下:
(1)OnCollisionEnter
该方法用来获取碰撞开始事件,并作出相应的反应。
在这段代码中我们获取了碰撞体的tag标志,如果该标志和MainCamera一致,则表明碰撞体的另一方是主摄像机。
如果是,则输出结果”enter”
(2)OnCollisionExit
该方法用来获取碰撞结束事件,并作出相应的反应。
在这段代码中我们获取了碰撞体的tag标志,如果该标志和MainCamera一致,则表明碰撞体的另一方是主摄像机。
如果是,则输出结果“exit”。
(3)Attack
该方法将在后面添加敌人的攻击行为。
注意:
以上的OnCollisionEnter和OnCollisionExit方法的首字母必须是大写,否则无法生效。
回到Unity的主编辑器,点击Play按钮来预览效果。
现在敌人将朝着主摄像机的方向行走,拖动主摄像机,当其和敌人碰在一起时,在Unity编辑器下方的Console视图中将输出enter,当碰撞结束时,将输出exit。

 

接下来让我们定义几个变量。
然后更改OnCollisionEnter和OnCollisionExit方法中的输出结果代码。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionWithCamera : MonoBehaviour {

        //1.敌人是否在场
        public bool zombieIsThere;
        //2.计时器
        float timer;
        //3.两次攻击之间的间隔
        int timeBetweenAttack;

        // Use this for initialization
        void Start () {

                //4.定义初始数值
                timeBetweenAttack = 2;
        }
        
        // Update is called once per frame
        void Update () {

                //5.获取系统时间
                timer += Time.deltaTime;
//                print (timer);

                //6.判断敌人是否在场,而且攻击间隔大于2秒
                if (zombieIsThere && timer >= timeBetweenAttack) {
                
                        //7.开始攻击动作
                        Attack ();
                }
        }
        //碰撞开始

     void OnCollisionEnter (Collision col)
        {
                //判断碰撞体中是否有主摄像机
                if (col.gameObject.tag == "MainCamera") {
                
//                        Debug.Log ("enter");
                        //8.确认敌人在现场
                        zombieIsThere = true;
                }
        }

        //碰撞结束
        void OnCollisionExit(Collision col){

                //判断碰撞体中是否有主摄像机
                if (col.gameObject.tag == "MainCamera") {
                
//                        Debug.Log ("exit");
                        //9.设置敌人不在现场
                        zombieIsThere = false;
                }
                
        }

        //攻击指令
        void Attack(){
        
                //10.恢复计时器为0
                timer = 0;
                //11.输出结果
                Debug.Log ("attack");
        }
}
下面按照数字编号来解释一下相关的代码:
(1)public bool zombieIsThere;
这里我们定义了一个public bool类型的变量,public表示其它类也可以访问这个变量,而bool则是变量的具体类型。
所谓的bool类型是一种逻辑判断类型,它只有两个数值,true或者false,代表“是”与“否”。
(2)float timer;
这里定义了一个float类型的变量,用来保存系统时间。
float是一种数据类型,用来保存浮点类的数据
(3)int timeBetweenAttack;
这里定义了一个int类型的变量,用来保存两次攻击之间的时间间隔。


(4)在Start方法中,我们定义了timeBetweenAttack的初始值。
Start()是Unity提供的一种事件函数,在场景启动的时候就会自动执行其中的代码。
(5)我们使用Time.deltaTime来获取系统时间
(6)这里使用了逻辑判断,&&是逻辑与的意思,表示符号左右的两个条件必须同时满足,才能执行后面的动作。

(7)调用攻击动作的方法。
(8)当碰撞开始时,把zombieIsThere这个bool类型的变量数值设置为true
(9)当碰撞结束时,把zombieIsThere这个bool类型的变量数值设置为false
(10)当攻击开始时,恢复计时器为0.
(11)输出结果,表示攻击开始。
接下来回到Unity的主编辑器,点击Play来预览游戏效果。
当敌人开始攻击时,可以看到Console视图中出现了attack提示。

 

这样,我们已经验证了游戏的基本逻辑。
接下来,我们让敌人在攻击时播放攻击的动画,而不仅仅在console中输出一个提示。
更改Attack方法的代码如下:

        //攻击指令
        void Attack(){
        
                //10.恢复计时器为0
                timer = 0;
                //11.输出结果
//                Debug.Log ("attack");

                //12.播放攻击动画
                GetComponent<Animator>().Play("attack");
        }

在编号12的代码中,我们使用GetComponent<Animator>函数获取了当前游戏对象的Animator组件,然后让其播放attack动画。
问题来了,我们在设计敌人的Animator动画控制器时,并没有添加attack动画。
因此让我们回到Unity编辑器,在Project视图中找到zombieController,双击打开Animator视图。

 
从Project视图中找到Assets-Components-Zombie-Model中的z@attack预设体,展开右三角,会看到里面有一个attack动画片段,将其拖动到Animator视图中。
右键单击attack动画片段,选择Make Transition,然后拖一条线到walk动画片段上。

 

因为我们希望当攻击动画播放结束时,僵尸敌人会回到walk这个动作状态上来。
在Unity编辑器中点击Play按钮,预览游戏效果,会看到如我们所设想的那样,当敌人碰到主摄像机并满足条件时,会开始播放攻击动画。而当我们移走主摄像机时,敌人又回到正常的行走动画。

在上的内容中,我们给僵尸敌人添加了碰撞检测机制。不过简单的攻击动画效果还是有点单独,我们希望当敌人攻击时,让画面变红,产生一种更紧张的氛围。
打开浏览器,在Google中搜搜bloody screen,在搜索结果上方点击Images切换到图片。从搜索的图片中选择一种你喜欢的效果就好了。当然,这里只是为了教程的展示需要,如果是开发商业项目,显然需要让美术团队成员来帮忙。
下载完成之后,把图片拖到Assets视图中。选中该图片,在Inspector中把Texture Type从Default更改为Sprite(2D and UI)。
 
然后点击Apply更改类型。

接下来在Hierarchy视图中右键单击,选择UI-Canvas。

 

当然,还有另外一种方式,在直接添加某种UI元素的时候,Unity会自动帮我们添加一个Canvas对象。

这里我们在Hierarchy视图中右键单击,选择UI-Image,
在Hierarchy视图中选择Image对象,在Inspector视图中点击Source Image,然后选择bloodyscreen。

 


接下来选择适配的设备分辨率,在Game视图的Free Aspect下拉列表中选择自己的设备分辨率,注意如果没有合适的选项,可以点击加号添加所需的分辨率。

 

继续在Game视图中,可以看到现在这个图片只覆盖了很小的范围。接下来让我们调整它的scale比例,直到覆盖整个界面。

 

接下来我们要做的就是在合适的时机开启或禁用这个UI。
从Unity的Project视图中找到CollisionWithCamera.cs,双击将其打开。
首先创建一个到bloodyScreen对象的引用,

        //受到攻击的画面效果
        public GameObject bloodyScreen;

然后回到Unity,在Hierarchy视图中选择z@walk,然后在Collision With Camera组件中将Blood Screen后的属性设置为Image对象。

 

然后回到代码编辑器,在Attack()的方法体重添加一行代码以激活bloodyScreen对象。

        //攻击指令
        void Attack(){
        
                //恢复计时器为0
                timer = 0;
                //输出结果
//                Debug.Log ("attack");

                //播放攻击动画
                GetComponent<Animator>().Play("attack");

                //激活bloodyScreen对象
                bloodyScreen.gameObject.SetActive(true);


        }

但是仅仅这样还不够,我们还需要在两秒之后将bloodyScreen重新禁用。
为了实现这一点,我们需要使用协程的方式来实现。
在Attack方法体中再添加一行代码:

        //攻击指令
        void Attack(){
        
                //恢复计时器为0
                timer = 0;
                //输出结果
//                Debug.Log ("attack");

                //播放攻击动画
                GetComponent<Animator>().Play("attack");

                //激活bloodyScreen对象
                bloodyScreen.gameObject.SetActive(true);

                //添加协程,在两秒后禁用bloodyScreen
                StartCoroutine(WaitTwoSeconds
());
        }

然后添加WaitTwoSeconds方法的实现代码如下:
        IEnumerator WaitTwoSeconds(){

                yield return new WaitForSeconds (2f);
                bloodyScreen.gameObject.SetActive (false);

        }


接下来我们在Unity中先默认禁用Image游戏对象,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Batman1208

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值