本章目标

完成 Unity-BattleStar的Audio系统

最终效果展示:视频地址

一、导入资源文件

文件下载:地址

1、导入Package

2、运行_Scenes里面的BattleStar_GameScene场景,观察

二、概要

1、BGM位于玩家对象,即摄像头下,Play On Awake、Loop

2、在同一个Audio Source—Audio Clip上动态切换音乐,需:

Assets新建Resources文件夹,将音乐放入其中,代码使用Resources.Load方法,动态更换Audio Clip

3、3D音效:

a、Audio Source组件—Spatial Blend设置为1开启3D音效

b、3D Sound Settings—Volume Rolloff设置为Custome Rolloff等

c、3D Sound Settings—Doppler Level设置为0避免Audio Source快速移动,Audio Listener听到的失真

三、注意事项

1、 一个场景只能有一个Audio Listener

2、用代码切换动画时,我们要注意Unity Animation默认播放动画应该空,否则即使写了改变播放动画,也不会执行我们写的程序,Unity会执行默认动画的播放

3、关于机器人不射击的原因:

原代码发射射线检测玩家是用的如下代码

Physics.Raycast((transform.localPosition + new Vector3(0, 1.3f, 0)), transform.forward, out hit, 10);

我们通过在Scene视图会发现,某些机器人Z向(即前向)坐标轴并不是指向身体正前方,因此当机器人面对玩家时,往往射线检测的方向为另一方向,造成无法检测到玩家的现象

我们调整方向代码,改为如下所示。方向Z轴加上1.3是因为要跟起点等高,避免射线向上或向下倾斜

Physics.Raycast((transform.localPosition + new Vector3(0, 1.3f, 0)),(playerTransform.position-(transform.position+new Vector3(0,1.3f,0))), out hit, 15);

四、Audio系统控制策略

1、Assets新建Resources文件夹,将Packages里Audios的音频压缩包解压到里面去

2、删除GunWithHand的默认播放动画

3、给WeaponMainMesh、HealthPackage和每个Robot添加AudioSource组件,并设置为3D音效模式

4、我们分别给这几个C#脚本设置:

Gun:

1)、当我们击中Robot时,Robot会调用BulletHit的音效,若没击中,则在Gun代码中调用GunFire音效

2)、当更换弹药时,播放ReloadBullet音效,更改动画播放速度,使之与声音相匹配

AnimationState.speed调整动画播放速度

using UnityEngine;
using System.Collections;public class ExampleScript : MonoBehaviour
{public Animation anim;void Start(){// Walk at double speedanim["Walk"].speed = 2.0f;}
}

3)、可在没子弹时开火,此时只播放FireWithoutBullet音效

GunModelTrigger:当捡到枪支时,播放GetGun音效

HealthPackage:当捡到血包时,播放HealthPackage音效

Player:当玩家受伤时,播放PlayerGetHurt音效

Robot:当机器人射击时,播放RobotHit音效

五、代码展示

PS:有的代码执行完毕后就要销毁自身物体,我们可使其先GetComponent<MeshRenderer>().enabled = false;隐藏显示,Invoke()一段时间执行完我们想要的命令后再进行销毁

我们仅将最复杂的Gun、Robot代码展示出来,其余代码读者根据本文描述自行思考

Gun

using System.Collections;
using UnityEngine;
using UnityEngine.UI;public class Gun : MonoBehaviour
{public Text bulletNumberText;//枪支Animation组件Animation gunAnimation;//主摄像机,用于Raycast射线检测Camera mainCamera;//开火粒子特效public ParticleSystem gunParticle;//开火声音AudioSource gunAudio;//子弹数量属性int gunBulletNumber;public int GunBulletNumber{set{gunBulletNumber = value;bulletNumberText.text = gunBulletNumber.ToString();}get{return gunBulletNumber;}}//控制开火属性。在没换弹完成前不允许开火。因此设置布尔变量,开完火后立即将允许开枪的变量设置为false,在换弹动画完成前不允许开火bool activeFire;public bool ActiveFire{set{activeFire = value;}get{return activeFire;}}private void Start(){gunAnimation = transform.GetComponent<Animation>();mainCamera = GameObject.Find("FirstPersonCharacter").GetComponent<Camera>();gunAudio = GetComponent<AudioSource>();GunBulletNumber = 25;ActiveFire = true;StartCoroutine(GunFire());StartCoroutine(GunReLoad());}IEnumerator GunFire(){while (true){if (Input.GetMouseButton(0)){if (activeFire){Fire();}}yield return null;    //一帧怎么能执行两次开火动画呢?两次开火之间要有一个时间差。但这儿即使不隔一帧也没关系,因为我们已设置了开火一次后延迟换弹时间才能进行下一次开火 }}void Fire(){if (GunBulletNumber > 0){activeFire = false;//发射射线检测是否打中机器人,是则调用机器人减血等动画Vector3 point = new Vector3(mainCamera.pixelWidth / 2, mainCamera.pixelHeight / 2, 0);Ray ray = mainCamera.ScreenPointToRay(point);RaycastHit hit;if (Physics.Raycast(ray, out hit)){if (hit.transform.name.Contains("Robot")){ChangeGunAnimation("Fire01");gunParticle.Play();GunBulletNumber--;//RobotGetHurt方法内有播放击中Robot的音效hit.transform.GetComponent<Robot>().RobotGetHurt();Invoke("ResumeFire", gunAnimation.GetClip("Fire01").length);}else{ChangeGunAnimation("Fire01");gunParticle.Play();gunAudio.clip = (AudioClip)Resources.Load("GunFire");GunBulletNumber--;gunAudio.Play();  //若没击中机器人,但击中了某碰撞器,播放开火声音Invoke("ResumeFire", gunAnimation.GetClip("Fire01").length);}  }else{ChangeGunAnimation("Fire01");gunParticle.Play();gunAudio.clip = (AudioClip)Resources.Load("GunFire");GunBulletNumber--;Invoke("ResumeFire", gunAnimation.GetClip("Fire01").length);gunAudio.Play();  //若什么都没击中,也播放开火声音}}else{activeFire = false;ChangeGunAnimation("Idle04");gunAnimation.Play();gunAudio.clip = (AudioClip)Resources.Load("FireWithoutBullet");gunAudio.Play();Invoke("ResumeFire", gunAnimation.GetClip("Idle04").length);}     }void ResumeFire(){ActiveFire = true;}void ChangeGunAnimation(string gunAnimationName){if (!gunAnimation.isPlaying)gunAnimation.Play(gunAnimationName);   //Animation的名字是string类型,Animation组件会直接调用内部这个名字的Animation动画}IEnumerator GunReLoad(){while (true){if (Input.GetKeyDown(KeyCode.R))ReLoadBullet();yield return null;}}void ReLoadBullet(){if (GunBulletNumber < 25){if (!gunAnimation.isPlaying){if (activeFire == true){activeFire = false;StartCoroutine(GunReloadAnimation());}}}}IEnumerator GunReloadAnimation(){if (!gunAnimation.isPlaying){gunAnimation["Reload01"].speed = 1.6f;gunAnimation.Play("Reload01");gunAudio.clip = (AudioClip)Resources.Load("ReloadBullet");Invoke("gunReload", 0.5f);yield return new WaitForSeconds(0.406f);GunBulletNumber = 25;ActiveFire = true;}}void gunReload(){gunAudio.Play();}
}

Robot

using System.Collections;
using UnityEngine;
using UnityEngine.AI;public class Robot : MonoBehaviour
{//玩家Transform组件[SerializeField] private Transform playerTransform;[SerializeField] private GameObject robotBullet;[SerializeField] private Transform healthImage;private Transform firePos;//用于控制机器人攻击间隔的布尔值private bool activeAttack;AudioSource AS;//机器人生命值private float robotHealth;public float RobotHealth{get{return robotHealth;}set{robotHealth = value;healthImage.localScale = new Vector3(value / 5, 1, 1);if (robotHealth == 0){RobotDie();}}}//Unity Start方法,详情可查询官方文档void Start(){firePos = transform.Find("FirePos").transform;activeAttack = true;RobotHealth = 5;AS = GetComponent<AudioSource>();StartCoroutine(RobotNavigation());}//机器人寻路的逻辑判定private IEnumerator RobotNavigation(){while (GetComponent<NavMeshAgent>().enabled && RobotHealth > 0){if (Vector3.Distance(playerTransform.position, transform.position) <= 10){StopNavigation();RaycastHit hit;transform.LookAt(playerTransform);Physics.Raycast((transform.localPosition + new Vector3(0, 1.3f, 0)),(playerTransform.position-(transform.position+new Vector3(0,1.3f,0))), out hit, 15);if (hit.transform.name == "FPSController"){if (activeAttack){//动画切换到AttackGetComponent<Animator>().SetTrigger("Attack");AS.clip = (AudioClip)Resources.Load("RobotHit");InstantiateBullet();AS.Play();//避免短间隔重复伤害activeAttack = false;//2S后允许下一次进攻Invoke("AttackPlayer", 2f);}}}else if (10 < Vector3.Distance(playerTransform.position, transform.position) && Vector3.Distance(playerTransform.position, transform.position) < 30){//机器人以玩家为目标进行寻路GetComponent<NavMeshAgent>().destination = playerTransform.position;//机器人保持面向玩家transform.LookAt(playerTransform);//继续寻路GetComponent<NavMeshAgent>().isStopped = false;//切换动画到WalkGetComponent<Animator>().SetBool("Walk", true);}else{StopNavigation();}yield return new WaitForEndOfFrame();}}private void InstantiateBullet(){//生成子弹飞向玩家Instantiate(robotBullet, firePos.position, firePos.rotation);}private void StopNavigation(){GetComponent<NavMeshAgent>().isStopped = true;GetComponent<Animator>().SetBool("Walk", false);}//NPC受到伤害public void RobotGetHurt(){RobotHealth -= 1;AS.clip = (AudioClip)Resources.Load("BulletHit");AS.Play();}//允许攻击private void AttackPlayer(){activeAttack = true;}//机器人死亡private void RobotDie(){//关闭碰撞体GetComponent<CapsuleCollider>().enabled = false;//关闭NavMeshAgent组件GetComponent<NavMeshAgent>().enabled = false;//机器人播放死亡动画GetComponent<Animator>().SetTrigger("Dead");//两秒后销毁机器人Invoke("DestroyRobot", 2);}//摧毁机器人private void DestroyRobot(){Destroy(gameObject);}
}

Unity-BattleStar丨11. 且听风吟:音乐与音效相关推荐

  1. 青春三部曲(《且听风吟》,《一九七三年的弹子球》,《寻羊冒险记》)--[日]村上春树...

    原本想把这三本书分开来着,但是细想了一下,还是和在一起好一些 村上春树的青春三部曲,<且听风吟>,<一九七三年的弹子球>,<寻羊冒险记> 我是先看了<寻羊冒险 ...

  2. 连载《一个程序猿的生命周期》-《发展篇》- 26.且听风吟,静待花开,慢慢走向人生赢家...

           昨天一早感觉天气很好,完全没有上班的意愿.上午赶到公司处理一些无关紧要的事儿,于是乎下午带着大宝去八大处爬山,在感受自然风光的同时想到一句话:且听风吟,静待花开.在人生过程中所做出的努力 ...

  3. 一直走,让心灵的窗户且听风吟

    初春,万物萌发,蛰虫涌动,前些日子,大地于悄无声息邂逅了一场春雨.微雨的天,及不上亮堂,也不算昏暗,乍暖还寒的时节,有些微凉吧.这个时候,只想静静的坐下,捧一掌春水,细听雨的情思,许自已一段柔和安静的 ...

  4. c语言中字母的定义,c语言字符串定义与初始化 - 且听风吟

    字符串的两种定义方式 char数组 char sa[] = "hello world"; char指针 char *sp = "hello world"; 这两 ...

  5. 且听风吟--十二月的总结

    学习态度:   没有吃苦不吃苦这一说.现在需要的是精英,而不是庸才.作为一名理科生.细心,理性,脑路清晰应该是最基本的.对待学习要真正的用心,要带心的努力.态度决定高度,明天第三次考核.以一个士气高昂 ...

  6. 听风的插件-正在战斗中

    文章目录 文件结构 界面 由上篇中的事件引出 事件处理 上篇我们跟插件打了个招呼,本篇我们要介绍一个简单的战斗统计插件,这个插件的主要功能是在玩家战斗开始发生时提示玩家正在处于战斗,并在战斗结束时显示 ...

  7. 【ARTS】01_06_左耳听风-20181217~1223

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...

  8. [更新]用最先进的理念打造最特别的串口调试工具--听风(原Wise#Pragma)

    软件主要界面 "听风"是一款独特的串口调试工具,主要有以下功能及特点: 1.能自动列举本机存在的串口,能显示串口详细描述及串口号,省去打开设备管理器查看串口的麻烦 2.能自动识别新 ...

  9. 【技术思路】极客时间-左耳听风-程序员攻略开篇

    2011 年,我在某篇技术博客找到了<程序员技术练级攻略>很多知识点都非常不错. 最近看到开了左耳听风这个专栏.作者又梳理了一遍知识点,这次更详细. 入门篇 编程语言:Python和Jav ...

最新文章

  1. PCL特征点与配准(1)
  2. linux多线程求和_谈一谈C++中的多线程(上)
  3. java中public_java中public class与class的区别详解
  4. Spring 的核心API
  5. Python爬取某旅游网站中的中国城市信息
  6. C++如何产生随机数
  7. oracle学习数据,Oracle从入门到精通的学习笔记
  8. List列表拒绝添加重复信息
  9. 微软开放Windows Phone 7中文开发中心
  10. 管理感悟:不要过度自我合理化
  11. 神朔 计算机联锁,计算机联锁系统集中操控方式在宁东铁路中的应用
  12. 普通IO口模拟实现SPI通信及应用解析
  13. vue3.0页面模板-示例
  14. 技术总监面试有什么技巧
  15. Glassfish的安装与使用
  16. MQ入门之看MQ项目源码 XXL-MQ
  17. 公司邮箱如何申请?如何申请公司邮箱?公司邮箱号码大全
  18. python爬取bilibili数据_BiliBili爬取数据简单分析
  19. HCNR200和HCNR201在电机驱动和电流回路中的应用
  20. uniapp开发app 调用支付宝人脸实名认证

热门文章

  1. 数组中的逆序对python
  2. c语言系统分工 ppt,C语言案例员工管理系统.ppt
  3. java快速排序,优化
  4. docker二进制安装mysql_docker安装mysql
  5. 令人感动的一个老公今生想和老婆做的45件事
  6. 解决smplayer中文字幕乱码
  7. 7,tcl双引号和花括号
  8. 小程序如何实现像淘宝那样错开排版
  9. Android踩坑小记:在onResume中申请权限
  10. 数据结构pintia_数据结构和算法(一)概述