僵尸围城小游戏:基于ARKit的Unity3d游戏开发--僵尸围城 2024-05-06 11:41:52 0 0 基于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) using System.Collections.Generic; using UnityEngine; //import namespace using UnityEngine.UI; public class ShootEnemy : MonoBehaviour { //创建到Button对象的引用 public Button shootBtn; //创建到主摄像机的引用 public Camera fpsCam; //设置敌人每次受到伤害的数值 public float damage = 10f; //敌人受伤的粒子特效 public GameObject bloodEffect; //攻击的粒子特效 public GameObject shootingEffect; //添加的攻击力度 public int forceAdd = 300; //1.定义两个音源对象 AudioSource shootSound; AudioSource reloadSound; // Use this for initialization void Start () { //Debug.Log ("Activated!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //添加按钮的响应事件 shootBtn.onClick.AddListener (OnShoothttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //2.获取音源组件 AudioSource[] audios = GetComponents<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //3.设置音源 shootSound = audios [0]; reloadSound = audios [1]; } public void OnShoot(){ //4.播放音效 shootSound.Play(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Debug.Log ("shooting!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //定义一个RaycastHit类型变量,用于保存检测信息 RaycastHit hit; //判断是否检测到命中敌人 if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit)) { //获取所受攻击的敌人 Enemy target = hit.transform.GetComponent<Enemy>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //destroy enemy if (target != null) { //instantiate blood effect target.TakeDamage (damagehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //创建敌人受伤的粒子特效 GameObject bloodBurst = Instantiate (bloodEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (bloodBurst, 0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } else { //load shooting effect //如果没有击中敌人,则创建攻击时的粒子特效 GameObject shootingGo = Instantiate (shootingEffect, hit.point, Quaternion.LookRotation (hit.normal)); //0.2秒后销毁粒子特效 Destroy (shootingGo,0.2f); } //攻击敌人时添加一个额外的冲击力 if (hit.rigidbody != null) { hit.rigidbody.AddForce (-hit.normal * forceAddhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //输出所命中的对象名称 Debug.Log (hit.transform.namehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 还是按照注释行的数字编号来解释一下: 1.定义了两个音源对象,分别用在攻击敌人和重新装弹上 2.使用数组来获取音源组件,注意这里用的是GetComponents,而不是GetComponent,因而获取的是一个数组,而非单一组件对象。 3.分别设置两个音源对象 4.在射击时播放对应的音效。 好了,攻击敌人的音效已经添加了,接下来我们还将给敌人本身添加点音效。 在Unity编辑器的Project视图中找到_Prefabs中的zombieEnemy预设体,在Inspector视图中点击Add Component,并添加一个新的Audio Source组件。 将AudioClip属性设置为Project视图中Assets/Sounds/BloodSFX/Splat中的bloodfx1音效(或者其它你个人喜好的),同时注意取消勾选Play On Awake。 然后打开Enemy.cs这个文件,并更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy : MonoBehaviour { //设置敌人的生命值 public float health = 30f; //1.定义敌人受伤的音效 AudioSource bloodSound; // Use this for initialization void Start () { //2.获取音源 AudioSource[] audios = GetComponents<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //3.设置音效 bloodSound = audios[1]; } //敌人受到伤害后的处理 public void TakeDamage(float damage){ //4.播放音效 bloodSound.Play(); //敌人生命值减少特定的数值 health -= damage; //输出敌人生命值 print (healthhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //当敌人生命值变为0的时候,就死亡 if (health <= 0) { //Enemy Die Die (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } //敌人死亡 void Die(){ //5.在1秒钟后销毁敌人对象 Destroy (gameObject, 1fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } 这里所添加的几行代码跟刚才的完全类似,其作用如下: 1.定义敌人受伤的音效 2.获取音源组件的数组 3.设置敌人受伤的音效 4.播放音效。 回到Unity编辑器,点击工具栏上的Play按钮,就可以预览游戏效果了。 可以看到从视觉上没有什么变化,只是增加了跟敌人相关的特定音效。 首先我们要对场景做一些小的调整。 在Unity编辑器的Hierarchy视图中找到HItCubeParent下的ruined_house子对象,然后在Inspector视图中右键单击Unity AR Hit Test Example(Script)组件,选择Edit Script,并对其中的代码进行编辑。 当前的代码有一个小小的问题,特别是在Update方法中。当前的脚本会检测用户在屏幕上的触碰操作,并将其转换成一个新的坐标。但问题在于,当触碰游戏的UI按钮,比如射击按钮时,也会执行类似的操作,并将整个地图移动到新的坐标。这显然不是我们希望看到的。因此我们将更改UnityARHitTestExample.cs的代码如下: using System; using System.Collections.Generic; //1.导入UI事件系统 using UnityEngine.EventSystems; namespace UnityEngine.XR.iOS { public class UnityARHitTestExample : MonoBehaviour { public Transform m_HitTransform; bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes) { List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypeshttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> if (hitResults.Count > 0) { foreach (var hitResult in hitResults) { Debug.Log ("Got hit!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransformhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransformhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Debug.Log (string.Format ("x:{ 0:0.######} y:{ 1:0.######} z:{ 2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> return true; } } return false; } // Update is called once per frame void Update () { if (Input.touchCount > 0 && m_HitTransform != null) { var touch = Input.GetTouch(0https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //2.对此行代码进行调整,添加另外一个逻辑判断条件,也即UI系统没有进行交互 if ((touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved) && !EventSystem.current.IsPointerOverGameObject(0)) { var screenPosition = Camera.main.ScreenToViewportPoint(touch.positionhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ARPoint point = new ARPoint { x = screenPosition.x, y = screenPosition.y } ; // prioritize reults types ARHitTestResultType[] resultTypes = { ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent, // if you want to use infinite planes use this: //ARHitTestResultType.ARHitTestResultTypeExistingPlane, ARHitTestResultType.ARHitTestResultTypeHorizontalPlane, ARHitTestResultType.ARHitTestResultTypeFeaturePoint }; foreach (ARHitTestResultType resultType in resultTypes) { if (HitTestWithResultType (point, resultType)) { return; } } } } } } } 以上仅作了两处调整,按照数字编号解释一下: 1.导入了Unity的UI事件交互系统的 2.添加了一个判断条件,仅当UI事件交互系统没有响应时才会执行下面的操作。 通过这两处调整,就可以规避我们刚刚提到的问题。 回到Unity编辑器,接下来我们还希望实现当用户触碰Start Game按钮开启游戏后,可以删除UnityARHitTestExample脚本。因为当游戏正式开启后,我们不希望再实时更改ruined_house对象的位置,而希望它固定在某个位置。此外,在开启游戏后还需要做的就是禁用Start Game按钮,直到游戏重新开始。 为此,在Hierarchy视图中选择HItCubeParent下的ruined_house子对象,然后在Inspector视图中点击Add Component,创建一个新的脚本文件,将其命名为StartGame,并在MonoDevelop中将其打开。 更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //1.导入UI相关的命名空间 using UnityEngine.UI; //2.导入ARKit相关的命名空间 using UnityEngine.XR.iOS; public class StartGame : MonoBehaviour { //3.开始按钮 public Button startBtn; //4.创建到UnityARHitTestExample的引用 private UnityARHitTestExample unityARHitTestExample; //5.crosshair public Image crosshair; // Use this for initialization void Start () { //6.添加开始游戏按钮的事件响应机制 startBtn.onClick.AddListener (StartNewGamehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } void StartNewGame(){ //7.获取到UnityARHitTestExample的引用 unityARHitTestExample = GetComponent<UnityARHitTestExample> (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //8.删除到UnityARHitTestExample的引用 Destroy (unityARHitTestExamplehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //9.禁用开始游戏按钮 startBtn.gameObject.SetActive (falsehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //10.启用辅助瞄准 crosshair.gameObject.SetActive (truehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } 这里按照注释行的数字编号顺序来简单解释一下: 1.这里导入了和UI相关的命名空间 2.导入了和ARKit相关的命名空间 3.创建到开始按钮的引用 4.创建到UnityARHitTestExample的引用 5.创建到crosshair准星的引用 6.添加开始游戏按钮的事件响应机制 7.获取到UnityARHitTestExample的引用 8.删除到UnityARHitTestExample的引用 9.禁用开始游戏按钮 10.启用辅助瞄准 接下来回到Unity编辑器,做一些简单的设置。 首先在Hierarchy视图中的Canvas下找到Crosshair,并将其禁用,因为我们会在代码中将其启用。 接下来在Hierarchy视图中找到HitCubeParent下的ruined_house子对象,然后在Start Game(Script)组件处设置Start Btn和Crosshair如下。 点击Unity编辑器工具栏上的Play按钮,然后切换到Game视图。当我们按下Start Game按钮的时候,可以看到Inspector视图处的UnityARHitTestExample脚本对象已经没有了,Start Game按钮也消失了,而准星则出现在界面中间。如下图所示。 接下来我们要在iPhone设备上进行测试。 为此,从Unity菜单栏中选择F ile-Build Settings, 然后点击Player Settings, 首先更改设备朝向,在Resolution and Presentation部分的Default Orientation属性处,从下拉列表中选择Landscape Left,如图所示。 然后点击Build and Run按钮编译运行。 此时可能会提示你之前有同名的项目,选择Replace即可。 此时系统会自动在Xcode中打开生成的项目,当然首先会看到几个错误提示。 接下来要在Xcode的General- Signing中更改Team信息。 之前提到过,你需要注册一个苹果开发者账号,才能顺利进行下面的工作。 如果还没有注册过,需要在苹果官网注册:https://developer.apple.com/ 然后确保iPhone设备连接到电脑上,点击Xcode工具栏上的编译运行按钮(向右的三角)即可。 可以看到,在我们触碰Start Game之前,ruined_house模型在空间中的位置是会发生变化的。当触碰Start Game按钮后,房屋模型的空间位置就不变了,然后就会看到僵尸敌人从各个方向来袭。触碰屏幕右下角 可以开始攻击。我们可以借助准星的力量进行瞄准。当敌人受到攻击时,会受到一个向后的冲力,同时会播放音效和粒子特效。在遭到敌人攻击时,也会有对应的音效和屏幕特效。 需要特别说明的是,在手机上实际测试游戏的时候,最好是在户外。如果是在室内测试,那么建议将房屋模型和敌人的Transform比例进行相应的缩写。 实际测试之后,我们发现有一些小的地方可以继续优化。 首先,我们不太希望看到房屋底部的阴影,因此需要把HitCubeParent下的shadowPlanePrefab的位置稍微往上提一下。 此外,现在房屋的阴影有点太生硬了,因此需要更改阴影的力度。 在Hierarchy视图中选择Directional Light,然后在Inspector视图中将Light组建的Shadow Type下的Realtime Shadows的Strength属性调整到0.55左右。 最后在Hierarchy视图中找到CameraParent下的Main Camera的子对象weapon1,然后在Inspector视图中将Shoot Enemy(Script)组建中的Force Add属性值降低到150,从而让敌人受到的冲击力显得更为自然。 OK,现在我们的调整就基本到位了。 在本课的内容中,我们将继续对ShootEnemy.cs脚本中的代码进行一些优化和完善工作。首先给武器添加一些弹药。 在Unity编辑器的Hierarchy视图中找到Canvas对象,然后右键单击,选择UI-Image,添加一个新的 Image控件,将其命名为Weapon1。在Inspector视图中将其Source Image设置为UIMask。然后将其Rect Transform中的Width 和Height属性值更改为280和170,如图所示。 为了方便我们调整UI元素的视觉效果,接下来选择Canvas中的btn_StartGame控件,并将其先禁用。 然后回到刚才添加的Weapon1控件,在Hierarchy视图中选中该控件,右键单击添加一个Image子对象,将其命名为weapon1Image。设置其Source Image为LT Handgun1。然后将Rect Transform中的Width和Height更改为160和130(主观调整)。 然后回到刚才添加的Weapon1控件,在Hierarchy视图中选中该控件,右键单击添加一个Text子对象,将其命名为ammoText。在Inspector视图中将其默认的文本内容更改为20。 更改Font为Jupiter,调整字体大小到合适的程度,比如55. 更改Color的R,G,B,A为255,255,255,180。也就是不完全透明的白色。 此外还要调整Width和Height到160和100,并调整Pos X和Pos Y,使其处于手枪图片的右下。 调整完成后在Game视图中的预览效果如下。 然后在Hierarchy视图中右键单击ammoText,选择Duplicate命令从而复制一个新的文本对象,并将其更名为ammoText2。 在Inspector视图中将其Text属性设置为/100。 更改Color为纯白色,然后调整其位置和字体大小,直到在Game视图中看到类似下面的效果。 需要说明的是,对UI元素的视觉效果调整是非常主观的,因此最好有美术设计人员的协助,或是培养自己的审美。 设置完成后,在Hierarchy视图中选择Weapon1对象,然后在Rect Trans form中点击Anchors上面的小图标,注意要按住Alt键,然后选择Top Right的标志,如下图所示。 适当调整Pos X和Pos Y的数值,使其在Game视图中的显示类似下图: 然后我们再来添加其它武器。 在Hierarchy视图中选中Canvas对象,右键单击,选择UI-Image,添加一个新的Image对象,并将其命名为Grenade,并将其Source Image属性设置为UIMask。 调整Rect Transform中的Width和Height属性为150和150. 接着右键单击Grenade,选择UI-Image,为其添加一个新的Image子对象,设置其Source Image属性为LT Handgrenade 8。 选中Grenade对象,在Inspector视图中的Rect Transform中点击Anchors上面的小图标,注意要按住Alt键,然后选择Top Right的标志。这个操作和刚才调整手枪的UI元素位置类似。 然后调整手榴弹的图片位置到合适的地方。 在Hierarchy视图中选择Grenade对象,右键单击,选择Duplicate,并将其命名为HealthKit。 并将其下面Image子对象的Source Image设置为LT Healthpack. 然后调整其位置,直到看到类似下图的效果。 此时Hierarchy视图中Canvas下面新添加的几个对象的层级关系类似下图: 最后重新启用btn_StartGame对象即可。 在上边的内容中,我添加了弹药的UI元素。在这一次的内容中,我将添加相应的脚本。 打开Unity编辑器,在Hierarchy视图中选中CameraParent下面Main Camera的子对象weapon1,然后从Inspector视图中打开ShootEnemy脚本文件。 在Start方法的前面添加以下代码: //创建到弹药UI元素的引用 public Text ammo1Text; public Text ammo2Text; public int ammo1; public int ammo2; 以上我们创建了到弹药UI元素的引用,以及弹药的具体数量。 然后在Hierarchy视图中找到Canvas下的Weapon1的子对象ammoText和ammoText2,并将其分别拖动到Inspector视图中Shoot Enemy组件的Ammo1 Text和Ammo2 Text。如图所示: 接下来回到ShootEnemy脚本,在Start方法的最后添加以下代码: //设置弹药数量的初始值 ammo1 = 20; ammo2 = 100; 然后在OnShoot方法的最开始添加以下代码: //弹药数量减少 ammo1 -= 1; string ammo1String = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammo1String; ammo2 -= 1; string ammo2String = (ammo2).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo2Text.text = ammo2String; 回到Unity主编辑器,点击工具栏上的Play按钮预览游戏效果。 此时脚本起作用了,但是似乎少了点东西。是的,少了个/符号。 回到ShootEnemy.cs脚本文件,修改刚才的代码如下: //弹药数量减少 ammo1 -= 1; string ammo1String = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammo1String; ammo2 -= 1; string ammo2String = (ammo2).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo2Text.text = "/"+ ammo2String; 接下来测试,发现弹药数量竟然可以减少到负数,显然是不科学的。 回到ShootEnemy.cs脚本,修改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //import namespace using UnityEngine.UI; public class ShootEnemy : MonoBehaviour { //创建到Button对象的引用 public Button shootBtn; //创建到主摄像机的引用 public Camera fpsCam; //设置敌人每次受到伤害的数值 public float damage = 10f; //敌人受伤的粒子特效 public GameObject bloodEffect; //攻击的粒子特效 public GameObject shootingEffect; //添加的攻击力度 public int forceAdd = 300; //定义两个音源对象 AudioSource shootSound; AudioSource reloadSound; //创建到弹药UI元素的引用 public Text ammo1Text; public Text ammo2Text; public int ammo1; public int ammo2; private bool ammoIsEmpty; // Use this for initialization void Start () { //Debug.Log ("Activated!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //添加按钮的响应事件 shootBtn.onClick.AddListener (OnShoothttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //获取音源组件 AudioSource[] audios = GetComponents<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //设置音源 shootSound = audios [0]; reloadSound = audios [1]; //设置弹药数量的初始值 ammo1 = 20; ammo2 = 100; } public void OnShoot(){ //仅在ammoIsEmpty为真时才可执行逻辑判断中的操作 if (!ammoIsEmpty) { if (ammo1 == 1) { ammo1 = 21; } //弹药数量减少 ammo1 -= 1; string ammo1String = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammo1String; ammo2 -= 1; string ammo2String = (ammo2).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo2Text.text = "/"+ ammo2String; //如果弹药总数量为0,则设置ammoIsEmpty为true if (ammo2 == 0) { ammoIsEmpty = true; ammo1 = 0; string ammoTempString = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammoTempString; } //播放音效 shootSound.Play(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Debug.Log ("shooting!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //定义一个RaycastHit类型变量,用于保存检测信息 RaycastHit hit; //判断是否检测到命中敌人 if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit)) { //获取所受攻击的敌人 Enemy target = hit.transform.GetComponent<Enemy>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //destroy enemy if (target != null) { //instantiate blood effect target.TakeDamage (damagehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //创建敌人受伤的粒子特效 GameObject bloodBurst = Instantiate (bloodEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (bloodBurst, 0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } else { //load shooting effect //如果没有击中敌人,则创建攻击时的粒子特效 GameObject shootingGo = Instantiate (shootingEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (shootingGo,0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //攻击敌人时添加一个额外的冲击力 if (hit.rigidbody != null) { hit.rigidbody.AddForce (-hit.normal * forceAddhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //输出所命中的对象名称 Debug.Log (hit.transform.namehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } } 在以上的代码中,我们主要是添加了一个逻辑判断,使用布尔变量isAmmoEmpty来判断弹药总量是否没有减少到0.仅当有弹药时才会执行后面的操作。 修改完成后回到Unity主编辑器,点击Play按钮预览游戏效果,测试一下,直到弹药数量减少为0,看看是否一切正常。 我将在屏幕中添加武器。 为此准备了相关的资源,请从此处下载: 链接:https://pan.baidu.com/s/1c2ezd7a 密码:o2g6将下载的文件解压缩,并拖动到Unity编辑器的Project视图中的Assets文件夹中。在继续之前,要清理下我们的Assets文件组织,把之前创建的几个脚本文件全部拖动到_Scripts中。 打开WeaponPack文件夹,把其中的Pistol预设体拖动到Hierarchy视图中,使之成为CameraParent-Main Camera- weapon1的子对象,如图所示。 然后在Inspector视图中设置Pistol的Transform属性,如下图所示。 当然,以上的具体参数其实比较主观,只要最后在Game视图中显示的效果类似下图即可: 然后在Hierarchy视图中展开刚刚添加的Pistol预设体,直到找到最里层的polySurface8。 然后在Inspector视图中更改其材质属性,将Main Maps中的Albedo属性设置为hands 接下来在Hierarchy视图中继续在Pistol预设体的子对象中寻找到GunBody,如图 然后在Inspector视图中更改其材质属性,将Main Maps中的Albedo属性设置为m1911 此时Game视图的效果如图: 接下来我们要添加开火的效果。 在Hierarchy视图中,给Pistol对象添加一个子对象,并将其命名为MuzzleFlash。 然后从Project视图中找到Standard Assets-ParticleSystems-Prefabs,将Flare拖动为MuzzleFlash的子对象。 接下来记得在Inspector视图中重置MuzzleFlash和Flare的Transform位置信息。 在Hierarchy视图中,删除Flare对象的Sparks子对象。 此时在Scene视图中注意观察手枪的相关显示,为方便起见,甚至可以暂时关闭其它场景对象。 然后使用移动工具将MuzzleFlash调整到枪口附近的位置。 然后在Hierarchy视图中选中Flare,在Inspector视图中更改相关的属性: 1.将Duration调整为1. 注意,将其子对象Smoke的Duration也调整为1。 2.展开Emission属性 将Rate over Time更改为0,并增加一个Burst属性,设置如下图所示。 对Flare的子对象Smoke进行相似的设置。不同之处只是在Bursts处是从三个中删除下面的两个。 接下来让我们替换开火的效果。 在Project视图中从WeaponPack文件夹中找到muzzleFlash这个空的材质,将其纹理设置为MuzzleFlash,如图所示。 然后将muzzleFlash材质拖动到Hierarchy视图中的Flare对象上。 选中Flare的子对象Smoke,并在Transform中稍微调整下位置。并取消勾选Looping。 对Flare对象也要取消勾选Particle System中的Looping属性。 再次提醒,这里的很多参数设置是比较主观的,你可以调整到自己满意为止~ 在上边的内容中,我添加了武器开火的粒子特效。这次的内容相对比较简单,我将给所添加的粒子特效设置对应的脚本。 打开Unity,在Project视图中找到\Assets\_Scripts\ShootEnemy.cs脚本,并在MonoDevelop中将其打开。 更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //import namespace using UnityEngine.UI; public class ShootEnemy : MonoBehaviour { //创建到Button对象的引用 public Button shootBtn; //创建到主摄像机的引用 public Camera fpsCam; //设置敌人每次受到伤害的数值 public float damage = 10f; //敌人受伤的粒子特效 public GameObject bloodEffect; //攻击的粒子特效 public GameObject shootingEffect; //添加的攻击力度 public int forceAdd = 300; //定义两个音源对象 AudioSource shootSound; AudioSource reloadSound; //创建到弹药UI元素的引用 public Text ammo1Text; public Text ammo2Text; public int ammo1; public int ammo2; //判断弹药是否已空 private bool ammoIsEmpty; //1.创建到开火粒子系统的引用 public ParticleSystem muzzleFlash; // Use this for initialization void Start () { //Debug.Log ("Activated!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //添加按钮的响应事件 shootBtn.onClick.AddListener (OnShoothttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //获取音源组件 AudioSource[] audios = GetComponents<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //设置音源 shootSound = audios [0]; reloadSound = audios [1]; //设置弹药数量的初始值 ammo1 = 20; ammo2 = 100; } public void OnShoot(){ //仅在ammoIsEmpty为真时才可执行逻辑判断中的操作 if (!ammoIsEmpty) { if (ammo1 == 1) { ammo1 = 21; } //弹药数量减少 ammo1 -= 1; string ammo1String = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammo1String; ammo2 -= 1; string ammo2String = (ammo2).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo2Text.text = "/"+ ammo2String; //如果弹药总数量为0,则设置ammoIsEmpty为true if (ammo2 == 0) { ammoIsEmpty = true; ammo1 = 0; string ammoTempString = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammoTempString; } //播放音效 shootSound.Play(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Debug.Log ("shooting!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //定义一个RaycastHit类型变量,用于保存检测信息 RaycastHit hit; //判断是否检测到命中敌人 if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit)) { //获取所受攻击的敌人 Enemy target = hit.transform.GetComponent<Enemy>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //destroy enemy if (target != null) { //instantiate blood effect target.TakeDamage (damagehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //创建敌人受伤的粒子特效 GameObject bloodBurst = Instantiate (bloodEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (bloodBurst, 0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } else { //load shooting effect //如果没有击中敌人,则创建攻击时的粒子特效 GameObject shootingGo = Instantiate (shootingEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (shootingGo,0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //攻击敌人时添加一个额外的冲击力 if (hit.rigidbody != null) { hit.rigidbody.AddForce (-hit.normal * forceAddhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //输出所命中的对象名称 Debug.Log (hit.transform.namehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //2.播放开火的粒子特效 muzzleFlash.Play (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 以上我们只添加了两行代码,按照注释行的数字编号来解释一下: 1.创建了到开火粒子系统的引用 2.在满足逻辑条件的前提下播放开火的粒子特效。 接下来回到Unity编辑器,在Hierarchy视图中选中CameraParent下Main Camera的子对象weapon1,然后在Inspector视图中,将Shoot Enemy组件中的Muzzle Flash属性设置为Flare,如图所示。 点击工具栏上的Play按钮,预览一下游戏的运行效果。 感觉开火的颜色似乎有点不满意,让我们来调整一下。 在Project视图中找到WeaponPack文件夹中的muzzleFlash材质,然后在Inspector视图中点击Tint Color旁边的色彩拾取器,并适当调整一下其中的色彩。 大概调整到类似上图的程度就好了。当然,具体如何调整其实是比较主观的。调整完成后,点击Unity编辑器工具栏上的Play按钮,观察下效果。 我们将给武器添加idle、开火和装弹时的动画,让画面显得更为真实。 首先我们要手动创建一个idle动画。在Hierarchy视图中选中Pistol对象, 对于idle动画,我们只需要更改Transform中的Position Y即可。 在Unity编辑器的菜单栏中点击Window-,打开动画编辑器。 点击Create以创建一个新的Animation,并将其命名为idleAnimation。 需要注意的是,在点击Create之前,需要在Hierarchy视图中选中Pistol对象。 接下来我们只需要设置几个关键帧即可。 首先点击Animation面板左侧的Add Property按钮,并选择Position,如图所示。 添加完成后,展开Pistol:Position,如下图所示: 然后点击Animation面板左上的红色录制按钮开始录制动画。 在面板中间的时间轴的大概0.35秒处点击,然后点击左侧的Add Keyframe按钮添加一个关键帧, 然后将Position.y的数值更改为-0.176,如图所示。 最后将时间轴上另外一个关键帧拖动到1.10秒左右的位置,然后将Position.y的数值更改为-0.179,如图所示。 然后点击Animation面板工具栏上的播放按钮预览动画效果。 如果觉得还不错,那么可以点击红色录制按钮停止录制。 好了,这样我们的idleAnimation动画就录制完毕了。 接下来在Inspector视图中打开Pistol的Animator Controller, 此时在Animator视图中只有一个idleAnimation状态,如图。 此时点击工具栏上的Play按钮,可以看到动画在播放中。 但是如果触碰开火按钮,会发现仍然播放的是idle动画。 因此接下来我们将添加开火的动画状态和重新装弹的动画状态。 在Unity编辑器的Project视图中找到WeaponPack中的Pistol预设体并将其展开, 发现里面已经提供了Fire动画和Reload动画,如图所示。 将Fire动画和Reload动画拖动到Animator视图中。 右键单击Fire动画,选择Make Transition,并拖出一条线到idleAnimation. 接下来选中idleAnimation,使用同样的方式拖一条线到Reload动画, 不过此时我们还要从Reload动画托一条线到idleAnimation。 完成后的连线如下图所示。 好了,Animator的设置基本上完成了,待会儿我们会回来添加一些其它的设置。 接下来进入代码时间,在Project视图中的Assets-_Scripts中找到并打开ShootEnemy.cs脚本文件。 在Start方法的前面添加以下代码: //创建到手机对象的引用 public GameObject pistol; 然后回到Unity编辑器,选中CameraParent-Main Camera- weapon1对象, 然后在Inspector视图中设置Shoot Enemy组件的Pistol属性为Pistol对象。 然后回到ShootEnemy.cs脚本,在OnShoot方法的最后,紧接着muzzleFlash.Play();添加一行代码如下: //播放开火动画 pistol.GetComponent<Animator>().Play("Fire"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr>接下来回到Animator视图,选中从idleAnimation到Reload动画状态之间的连线, 然后取消勾选Has Exit Time,如图所示。 然后点击Unity编辑器工具栏上的Play按钮,来预览下游戏效果: 开火动画起作用了,但是似乎稍微有点慢。 回到Animator视图,选中Fire动画,将Inspector视图中的Speed更改为2. 再次预览,发现效果好一些了。 接下来让我们添加重新装弹的Reload动画。 首先回到Animator视图,在左侧切换到Parameters选项卡,点击+号, 选择Trigger类型,将其命名为reload。 然后选中从idleAnimation到Reload的连线,在Inspector视图中的Conditions处点击+号, 从而创建一个新的触发条件reload,如图所示。 Trigger类型的参数触发条件意味着,仅当reload条件满足时,才会播放相关的动画。 接下来回到ShootEnemy.cs,并更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //import namespace using UnityEngine.UI; public class ShootEnemy : MonoBehaviour { //创建到Button对象的引用 public Button shootBtn; //创建到主摄像机的引用 public Camera fpsCam; //设置敌人每次受到伤害的数值 public float damage = 10f; //敌人受伤的粒子特效 public GameObject bloodEffect; //攻击的粒子特效 public GameObject shootingEffect; //添加的攻击力度 public int forceAdd = 300; //定义两个音源对象 AudioSource shootSound; AudioSource reloadSound; //创建到弹药UI元素的引用 public Text ammo1Text; public Text ammo2Text; public int ammo1; public int ammo2; //判断弹药是否已空 private bool ammoIsEmpty; //创建到开火粒子系统的引用 public ParticleSystem muzzleFlash; //创建到手机对象的引用 public GameObject pistol; //1.重新装弹检查判断 private bool reloadCheck; // Use this for initialization void Start () { //Debug.Log ("Activated!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //添加按钮的响应事件 shootBtn.onClick.AddListener (OnShoothttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //获取音源组件 AudioSource[] audios = GetComponents<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //设置音源 shootSound = audios [0]; reloadSound = audios [1]; //设置弹药数量的初始值 ammo1 = 20; ammo2 = 100; //2.设置重新装弹检查的默认值为true reloadCheck = true; } //3.Coroutine方法 IEnumerator WaitForReload(){ yield return new WaitForSeconds (3fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> reloadCheck = true; } public void OnShoot(){ //4.仅在ammoIsEmpty和reloadCheck同时为真时才可执行逻辑判断中的操作 if (!ammoIsEmpty && reloadCheck) { if (ammo1 == 1) { ammo1 = 21; //5.触发重新装弹动画 pistol.GetComponent<Animator> ().SetTrigger ("reload"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> reloadCheck = false; //6.等待3秒 StartCoroutine (WaitForReload ()https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //7.播放重新装弹的音效 reloadSound.Play (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //弹药数量减少 ammo1 -= 1; string ammo1String = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammo1String; ammo2 -= 1; string ammo2String = (ammo2).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo2Text.text = "/"+ ammo2String; //如果弹药总数量为0,则设置ammoIsEmpty为true if (ammo2 == 0) { ammoIsEmpty = true; ammo1 = 0; string ammoTempString = (ammo1).ToString (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> ammo1Text.text = ammoTempString; } //播放音效 shootSound.Play(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Debug.Log ("shooting!"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //定义一个RaycastHit类型变量,用于保存检测信息 RaycastHit hit; //判断是否检测到命中敌人 if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit)) { //获取所受攻击的敌人 Enemy target = hit.transform.GetComponent<Enemy>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //destroy enemy if (target != null) { //instantiate blood effect target.TakeDamage (damagehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //创建敌人受伤的粒子特效 GameObject bloodBurst = Instantiate (bloodEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (bloodBurst, 0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } else { //load shooting effect //如果没有击中敌人,则创建攻击时的粒子特效 GameObject shootingGo = Instantiate (shootingEffect, hit.point, Quaternion.LookRotation (hit.normal)https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //0.2秒后销毁粒子特效 Destroy (shootingGo,0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //攻击敌人时添加一个额外的冲击力 if (hit.rigidbody != null) { hit.rigidbody.AddForce (-hit.normal * forceAddhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //输出所命中的对象名称 Debug.Log (hit.transform.namehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } //播放开火的粒子特效 muzzleFlash.Play (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //播放开火动画 pistol.GetComponent<Animator>().Play("Fire"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 所添加的新代码都已经添加了注释,这里就不再一一赘述了。 在最后测试之前,我们还有一处小小的修改, 在Hierarchy视图中找到CameraParent-Main Camera-weapon1-MuzzleFlash-Flare, 然后在Inspector视图中取消勾选Play On Awake. 全部完成后在编辑器上点击工具栏上的Play按钮,预览下游戏效果。 好了,本课的内容到此结束,我们下一课再见。 我们将让玩家可以在地面上捡起武器,然后开火。而不是一开始就已经一人一枪横扫千军了。 为此,首先我们要从Asset Store中下载一个武器资源。 在Asset Store中搜索pistol,然后选择FREE ONLY,从搜索结果中选择下图中的这个资源。 把下载后的资源拖入到Arts文件夹。打开[PBR]Makarov文件夹,然后将其中的预设体拖动到Hierarchy视图中,使其成为ruined_house的子对象,并更名为myWeapon。 接下来在Inspector视图中调整Transform中的相关属性如下(仅供参考): 接下来点击Inspector视图中的Add Component,给pistol对象添加一个Box Collider组件,然后设置碰撞体的大小,如图所示。 再次强调,这些数字是相对比较主观的,你觉得合适就行。 除了Box Collider,我们还需要继续给pickupWeapon对象添加一个Rigidbody组件,因为我们希望武器受到重力的影响。这样当游戏开始的时候,武器就会自动掉落到地面。 好了,接下来我们需要创建脚本,来处理拾取武器的操作。 在Hierarhcy视图中选中CameraParent-Main Camera,然后点击Inspector视图中的Add Component按钮,给它添加一个新的脚本组件,并将其命名为PickupWeapon。 在MonoDevelop中将其打开并更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; public class PickupWeapon : MonoBehaviour { // Use this for initialization void Start () { } //1.开始碰撞 void OnCollisionEnter(Collision col){ if (col.gameObject.name == "myWeapon") { Debug.Log ("Enter test"); } } //2.碰撞结束 void OnCollisionExit(Collision col){ if(col.gameObject.name == "myWeapon"{ Debug.Log("Exit test"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 这里我们只是添加了两个方法,而这两个方法在之前的课程中有提过,分别是用于处理碰撞开始和碰撞结束的事件。这里我们暂时只是在Console中输出相应的信息。 好了,接下来回到Unity编辑器,在Hierarchy视图中选中CameraParent-Main Camera,然后在Inspector视图中点击Add Component,添加一个Rigidbody组件,并取消勾选Use Gravity。此外在Rigidbody组件的Constraints属性处勾选所有的选项。 接下来可以测试一下,点击Unity编辑器上的Play按钮进行测试。 当我们在场景中拖动主摄像机到地面上的武器位置附近时,可以看到武器被撞飞了,同时在Console中输出了相应的提示。 不过看起来武器的重量太轻了点,让我们给它增加点重量。 在Hierarchy视图中选中HitCubeParent-ruined_house-myWeapon,然后在Inspector视图中将其Rigidbody组件的Mass属性设置为40. 再次运行测试,看起来差不多了~ 然后从这里下载本章所需的资源素材: 链接:百度网盘-链接不存在 密码:s6pz 将其中的文件解压缩,并将hand-sprite.png资源文件拖动到Unity编辑器的Project视图的Arts文件夹中。 选中该文件,然后在Inspector视图中点击Texture Type旁的下拉列表,然后选择Sprite(2D and UI) 然后别忘了点击右下角的Apply按钮 接下来在Hierarchy视图中选中Canvas,添加一个新的Button UI元素,将其命名为btn_Pickup,删除该按钮的文本子对象。 然后在Inspector视图中将Image组建下的Source Image属性设置为hand-sprite。 更改Rect Transform属性中的Width 和Height为150和150. 最后在默认情况下对其禁用,如图所示。 好了,现在可以回到我们的PickupWeapon.cs脚本,修改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; public class PickupWeapon : MonoBehaviour { //1.创建到拾取武器按钮的引用 public GameObject pickupBtn; // Use this for initialization void Start () { } //1.开始碰撞 void OnCollisionEnter(Collision col){ if (col.gameObject.name == "myWeapon") { // Debug.Log ("Enter test"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //2.启用拾取武器按钮 pickupBtn.gameObject.SetActive (true); } } //2.碰撞结束 void OnCollisionExit(Collision col){ if(col.gameObject.name == "myWeapon"){ // Debug.Log("Exit test"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //3.禁用拾取武器按钮 pickupBtn.gameObject.SetActive (falsehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 在以上代码中,按照注释行的数字编号来简单解释一下: 1.创建了到拾取武器按钮的引用。 2.当碰撞发生时,启用拾取武器按钮 3.当碰撞结束时,禁用拾取武器按钮。 回到Unity编辑器,在Hierarchy视图中选中CameraParent-Main Camera,然后在Inspector视图中将Pickup Weapon组件的Pickup Btn属性更改为btn_Pickup,如图所示。 好了,接下来我们完成一个之前别遗忘的工作,给准星创建一个引用。 回到PickupWeapon.cs,更改代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; public class PickupWeapon : MonoBehaviour { //创建到拾取武器按钮的引用 public GameObject pickupBtn; //1.创建到准星的引用 public GameObject crossHair; // Use this for initialization void Start () { } //开始碰撞 void OnCollisionEnter(Collision col){ if (col.gameObject.name == "myWeapon") { // Debug.Log ("Enter test"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //启用拾取武器按钮 pickupBtn.gameObject.SetActive (truehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //2.禁用准星 crossHair.gameObject.SetActive(false); } } //碰撞结束 void OnCollisionExit(Collision col){ if(col.gameObject.name == "myWeapon"){ // Debug.Log("Exit test"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //禁用拾取武器按钮 pickupBtn.gameObject.SetActive (falsehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //3.启用准星 crossHair.gameObject.SetActive(truehttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } } 按照注释行数字编号简单解释下: 1.创建了到准星UI控件对象的引用 2.当碰撞发生时,禁用准星 3.当碰撞结束时,启用准星 然后回到Unity编辑器,在Hierarchy视图中选中CameraParent-Main Camera,然后在Inspector视图中将Pickup Weapon组件的Cross Hair属性设置为Crosshiar UI元素,如图所示。 接下来点击工具栏上的Play按钮,预览下游戏效果。 到了这一步的,基本的操作已经完成了,让我们再做一些完善工作。 在Hierarchy视图中选中Canvas,然后在Inspector视图中点击Add Component,给其添加一个新的脚本组件,将其命名为WeaponPickup。在MonoDevelop中将其打开,并更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //1.导入UI相关的命名空间 using UnityEngine.UI; public class WeaponPickup : MonoBehaviour { //2.创建到拾取武器按钮的引用 public Button pickupBtn; //3.创建到武器的引用 public GameObject weapon1; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } 以上代码比较简单,这里就不再赘述了,大家直接看注释应该就可以明白。 回到Unity编辑器,首先在Hierarchy视图中找到CameraParent-Main Camera-weapon1,然后在默认状态下将其禁用。 然后在Hierarchy视图中选中Canvas,设置Weapon Pick Up组件的属性如图: 接下来回到WeaponPickup.cs脚本,更改其中的代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; //导入UI相关的命名空间 using UnityEngine.UI; public class WeaponPickup : MonoBehaviour { //创建到拾取武器按钮的引用 public Button pickupBtn; //创建到武器的引用 public GameObject weapon1; // Use this for initialization void Start () { //1.启用武器 pickupBtn.onClick.AddListener (EnableWeapon); } void EnableWeapon(){ weapon1.gameObject.SetActive (true); } // Update is called once per frame void Update () { } } 以上代码中,我们只是在用户按下拾取武器的按钮时启用武器。好了,接下来可以点击工具栏上的Play按钮预览游戏效果。 为了让游戏的效果更加接近真实,我们希望当手机射击的时候可以弹出弹壳。 在本课的内容中,我们将主要实现这一功能。 打开Unity,切换到Asset Store视图,在搜索栏中输入shell,选择FREE ONLY,然后下载并导入下图中的资源。 下载完成后,把Grenades_Ammo_FPS文件夹拖动到Project视图的Assets/Arts中。 从Grenades_AMMO_FPS的Prefabs文件夹中找到9x18这个预设体,在Inspector视图中,右键单击Transform,选择reset。调整其Scale比例,并设置Rotation值。 将其更命名为shell,然后拖动到Project视图的Assets/_Prefabs文件夹中。 然后从Project视图中找到并打开ShootEnemy.cs。 在Start方法之前添加一行代码: //创建到弹壳的引用 public GameObject shell; 这里我们创建了到弹壳对象的引用。 回到Unity编辑器,在Hierarchy视图中选中CameraParent-Main Camera-weapon1对象,然后在Inspector视图中将Shoot Enemy组件中的Shell属性设置为刚才创建的shell 预设体,如图所示。 然后在OnShoot方法中,紧接着播放开火动画的那行代码添加以下代码: //loading shell Vector3 position = GameObject.FindGameObjectWithTag ("positionPistol").transform.position; Quaternion rotation = Quaternion.Euler (0, 0, 0https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> Instantiate(shell,position,rotationhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr>这里我们获取了Tag标记为positionPistol的对象的位置,然后将rotation设置为0,并使用Instantiate方法生成弹壳对象。 接下来我们要设置这个Tag。在Hierarchy视图中选中CameraParent-Main Camera-weapon1-Pistol-All-GunAndRightArm-GunPosition,然后在Inspector视图中添加一个Tag。 创建完成后,将GunPostion的Tag设置为positionPistol。 好了,接下来点击Unity编辑器工具栏上的Play按钮预览游戏效果。 可以看到在Hierarchy视图中子弹壳已经生成了,但是在Game视图中还无法看到子弹壳的实体。这是因为它的位置在手枪的扳机处,而且保持不变。 为此,我们需要给弹壳添加物理机制。 从Project视图中的Assets/_Prefabs文件夹中找到shell这个预设体,然后在Inspector视图中做一些调整。 首先给它添加一个Rigidbody组件,然后将Mass设置为0.03,Drag设置为1,Angular Drag设置为1. 紧接着添加一个新的脚本组件,名为MoveShell.cs,在MonoDevelop中打开并编辑。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class MoveShell : MonoBehaviour { //创建到Rigidbody的引用 public Rigidbody rb; // Use this for initialization void Start () { //获取Rigidbody rb = GetComponent<Rigidbody> (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } // Update is called once per frame void Update () { //施加一个向右的力 rb.AddForce (transform.right * 0.05fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //施加一个向上的力 rb.AddForce (transform.up * 0.05fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } } 回到Unity编辑器,在Inspector视图中将Move Shell脚本组件中的Rb属性设置为Rigidbody,同时禁用Use Gravity,如图。 点击Unity编辑器上的Play按钮预览游戏,弹壳出来了,但是弹出的方向始终在一个方向,显然不符合常识。我们希望子弹有个随机的掉落方向,接下来将实现这一点。继续编辑MoveShell.cs脚本如下: using System.Collections; using System.Collections.Generic; using UnityEngine; public class MoveShell : MonoBehaviour { //创建到Rigidbody的引用 public Rigidbody rb; // Use this for initialization void Start () { //获取Rigidbody rb = GetComponent<Rigidbody> (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //1.添加随机旋转的协程 StartCoroutine("Rotate"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //2.添加恢复重力的协程 StartCoroutine("RecoverGravity"https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } // Update is called once per frame void Update () { //施加一个向右的力 rb.AddForce (transform.right * 0.05fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //施加一个向上的力 rb.AddForce (transform.up * 0.05fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> } IEnumerator Rotate(){ while (true) { //3.等待0.1秒 yield return new WaitForSeconds (0.1fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //4.随机旋转 transform.eulerAngles += new Vector3 (Random.Range (-360f, 360f), Random.Range (-360f, 360f),Random.Range(-360f,360f)); } } IEnumerator RecoverGravity(){ //5.等待0.2秒 yield return new WaitForSeconds (0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //6.恢复重力的影响 rb.useGravity = true; } } 按照注释行的数字编号简单解释一下: 1.添加了一个随机旋转的协程 2.添加了恢复重力作用的协程 3.等待0.1秒 4.在0.1秒后开始随机旋转 5.等待0.2秒 6.在0.2秒后开始恢复重力的作用 回到Unity编辑器,点击Play,可以预览下游戏效果。 接下来还有一个事情需要完成,那就是销毁弹壳,不然游戏场景中的弹壳会无穷无尽了。 继续回到刚才的MoveShell.cs脚本,在Start方法的最后添加一行代码: //3.在2秒后销毁弹壳 Destroy(gameObject,2.0fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr>最后给弹壳也添加一个音效。 在Project视图中选中shell预设体,然后点击Inspector视图中的Add Component,添加一个Audio Source组件。禁用Play On Awake,将AudioClip属性设置为bulletshells01,如图所示。 最后回到MoveShell.cs,在RecoverGravity方法的最后添加以下代码: //等待0.2秒 yield return new WaitForSeconds(0.2fhttps://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> //播放弹壳的音效 AudioSource shell = GetComponent<AudioSource>(https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr> shell.Play (https://blog.csdn.net/Batman1208/article/details/);%0D%0A%3Cbr>代码的作用很直白,首先要再等待0.2秒,然后播放弹壳的音效。 在Unity编辑器中切换到Asset Store,在搜索栏中�输入mode在Unity编辑器中切换到Asset Store,在搜索栏中 ONLY,然后下载和导入下面的这个资源。 接着在�输入table,仍然选择FREE ONLY,然后下载和导入下图的资源。 接着在�输入campfire,仍然选择FREE ONLY,然后下载和导入下图的资源。 资源下载导入成功后,接下来让我们把这些资源添加到场景中。 首先要添加的就是野外的篝火。 在刚刚导入的Campfire Pack资源包中打开DemoScene,从Hierarchy视图中选中Torch并复制该对象。 再次打开UnityARKitScene,将复制的Torch对象粘贴到Hierarchy视图中 接下来在Project视图中找到Campfire Pack-Model-model中的modelBonfire,将其拖动到Hierarchy视图中,使其成为HitCubeParent-ruined_house的子对象。然后使用移动工具将其拖动到房子外面的空地上。 将刚刚添加的Torch对象拖动为modelBonfire的子对象,并重置其Transform信息如下。 在粒子特效的属性中将起始大小调的小一点, 当然,还是那句话,具体的数值大小是很主观的,大家没必要完全照搬,觉得满意就行~ 接下来在Project视图中找到Campfire Pack-Model-model中 的modelTent,并将其拖动到Hierarchy视图,使其成为HitCubeParent-ruined_house的子对象。 在Inspector视图中调整其位置和大小,具体就不再赘述了,大家觉得合适就行。 接下来添加一个桌子,从刚刚下载的资源包中找到Wooden_table_and_chair-FBX下的table_and_chair预设体,把它拖动到Hierarchy视图,使其成为HitCubeParent-ruined_house的子对象 通过移动工具调整它的位置,放到合适的位置,主要是想让武器可以掉落在桌子上。 接下来选中table_and_chair的子对象chair,给其添加Rigidbody和Box Collider两个组件,对子对象table做同样的操作。 接下来适当调整桌椅的大小。 把武器拖动为table_and_chair的子对象,并调整其Box Collider的大小。 点击Play按钮,查看手枪是不是比较和谐的掉落在桌面上。如果不是,就需要我们稍微调整双方的Box Collider的大小和位置。 调整到位后,在Project视图中找到Grenades_Ammo_FPS/Prefabs中的手榴弹预设体,并拖动到Hierarchy视图中,让其成为table_and_chair的子对象 在Inspector视图中重置其position,具体的操作就不再赘述了,大家应该已经非常熟悉此类操作了。 把手榴弹对象拖动到桌面上,给其添加box collider和rigidbody组件,并适当调整下比例大小。 然后从Project视图中把另外一个手榴弹预设体拖动到Hierarchy 收藏(0)