关于Unity中FPS第一人称射击类游戏制作(专题十)
当前Unity最新版本5.6.3f1,我使用的是5.5.1f1
场景搭建
1: 导入人物模型, 手持一把枪;
2: 导入碎片模型;
3: 创建一个平面;
4: 创建一个障碍物;
5: 导入人物模型;
6: 配置一个十字瞄准器, 设想机对准的中心就是瞄准的中心, 屏幕中心;
7: 配置一个第一人称的摄像机,做人的眼睛;
开枪射击
1: 鼠标左键按下开火;
2: 从摄像机位置开始,根据摄像机的正前方,在一定的射击距离内如果碰撞到了某个物体,那么就表示这个物体被子弹射击;
3: 播放射击的子弹碎片: 在射击碰撞点产生碎片物体,并给这些碎片物体一个反弹力(100)
方向是设计的反方向;
4: Physics.Raycast (起点, 发射方向向量, out hit, 最大有效距离(子弹的距离));
5: 播放开枪的声音
PlayOneShot 固定时间播完你的audio文件;
用PlayOneShot,就能够听到连续的枪声了;
人物的行走
1: 定义移动的速度;
2: 玩家在自己的坐标空间内向前(W),后(S), 左(A), 右(D);
3: 前后 Translate(0, 0, 距离, Space.Self);
4: 左右 Translate(距离, 0, 0, Space.Self);
5: 播放人物行走的脚步声;
枪的上下左右移动
1: 枪的左右瞄准;
(1)计算绕Y轴旋转的欧拉角 GetAxis(Mouse Y) * 移动的灵敏度(5);
(2)在转向的时候,角色也要跟着转,那么我们旋转角色就可以了, 摄像机也会跟着转;
修改角色的欧拉角;
(3) 给这个枪绕Y轴一个转动范围[-360, 360]
(4) Mathf.Clamp(value, min, max);
2: 枪的上下移动: localEulerAngles
(1)枪的上下移动,角色不动,所以不能作用到角色上;
(2)计算绕X轴旋转的欧拉角 GetAxis(Mouse X) * 移动的灵敏度(5);
(3)只作用在瞄准上(eye),所以修改摄像机的相对欧拉角;
(4)同时手中的枪也要移动对应的欧拉角(right hand);
(5)抬头低头有一个范围,我们在[-45, 45]的范围内;
FPS第一人称射击类游戏实例
场景搭建
1.创建Unity项目工程和文件目录,保存场景
2.导入人物模型和子弹碎片的资源包charactor.unitypackage(第74)
3.创建一个平面plane,X和Z拉长10倍,把主角模型Assets\Prefabs\person拉近Hierarchy视图中
4.把走路的声音Step.mp3,射击的声音Shot.mp3,平面贴图Ground.jpg,瞄准的准心贴图Crosshairs.png(第74)导入Resources文件夹
5.把Crosshairs.png直接拖进Scene视图的平面plane上,自动帮我们生成了平面的材质并关联
6.创建一个cube,放大4倍,放在主角模型的正前方,Z设置为20
7.配置一个十字瞄准器,UI和摄像机是成比例对应的,所以摄像机对准的中心就是等下Crosshairs瞄准的中心,屏幕中心,右键---->UI---->Image,命名为Crosshairs,再把Crosshairs.png的Texture Type设置为Sprite(2D and UI)
8.把Crosshairs.png拖进Crosshairs节点的Image组件的Source Image属性中,调整Crosshairs的position为(0,0,0),屏幕正中央,Set Native Size
9.配置一个第一人称的摄像机,做人的眼睛。就是把Main Camera拖到person下作为子节点重命名为eye,这样可以跟随主角移动。然后把摄像机的position设置为(0,1.5,0),正好在人物头部
开始射击
10.创建一个脚本open_fire,挂载在person节点下,里面实现开枪的逻辑。
a.查看虚拟按键Edit---->Project Settings---->Input---->Fire1---->mouse 0,可以用Fire1代替mouse 0
b.枪口喷火的特效如果喷的很分散的话,可以设置FireEffect---->Particle System---->Shape---->Shape---->类型设置为Mesh
c.FPS游戏通常不会有真的子弹在天空中飞来飞去的,一般是用射线实现,有真的子弹也没关系。
打开open_fire.cs
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class open_fire : MonoBehaviour {
- private ParticleSystem fire_effect;//特效节点person---->RightHand---->FireEffect
- public Transform eye;//摄像机节点person---->eye
- public GameObject piece;//碎片节点Prefabs文件夹---->piece
- //开枪的声音Resources文件夹---->Shot,给person节点加一个AudioSource组件,关联Shot,再把person节点拖进shoot_audio
- //AudioSource组件的Play On Awake打钩去掉
- public AudioSource shoot_audio;
- // Use this for initialization
- void Start () {
- //获得开枪的特效节点的特效组件
- this.fire_effect = this.transform.Find("RightHand/Pistol/FireEffect").GetComponent<ParticleSystem>();
- }
- //生成枪打到障碍物的碎片特效
- void play_pieces_effect(Vector3 w_pos) {
- for (int i = ; i < ; i++) {
- GameObject p = GameObject.Instantiate(this.piece);
- p.transform.position = w_pos;//坐标设置在碰撞点处
- p.GetComponent<Rigidbody>().AddForce(-this.eye.forward * 100f);//撞到后给一个反弹力,方向和摄像机Z轴方向相反
- // Component.Destroy(p, 0.3f);//在0.3s后删除该组件
- // MonoBehaviour.Destroy(p, 0.3f);//在0.3s后删除该组件,MonoBehaviour继承自Component
- //Destroy(p, 0.3f);//在0.3s后删除节点,调用静态函数Destroy
- GameObject.Destroy(p, 0.3f);//在0.3s后删除节点
- }
- }
- //枪发射子弹
- void shoot_fire() {
- // 播放枪发射子弹时枪口的火焰
- this.fire_effect.Play();
- // end
- // 播放开枪的声音
- // this.shoot_audio.Play();//用这个播的比较慢,因为shot声音前面一小段没声音
- this.shoot_audio.PlayOneShot(this.shoot_audio.clip);//用这个播的速度比较快,固定时间内播完
- // end
- // 当前这个枪打到了哪个物体
- // 从这个eye的位置,沿着eye的前方方向,发射一条射线,看谁被命中了
- // 子弹是有有效的射击距离的,所以,我们要传入子弹的最大距离
- RaycastHit hit; // 命中的对象
- //从this.eye.position出发,向this.eye.forward(Z轴方向)发射一条射线,撞到hit,射线最大距离100米,最后再out带出hit命中对象
- if (Physics.Raycast(this.eye.position, this.eye.forward, out hit, ))
- {
- // 枪打到了哪个障碍物,打印出障碍物的碰撞器的节点的名字
- Debug.Log(hit.collider.gameObject.name);
- // .....其他的游戏逻辑
- // end
- // 播放这个碎片特效,传入碰撞点世界坐标hit.point
- this.play_pieces_effect(hit.point);
- // end
- }
- // end
- }
- // Update is called once per frame
- void Update () {
- //鼠标左键按下,在Edit---->Project Settings---->Input里面有配置Fire1就是鼠标左键mouse 0
- if (Input.GetButtonDown("Fire1")) {
- this.shoot_fire();
- }
- }
- }
人物的行走
11.创建一个脚本fps_ctrl,挂载在person节点下,里面实现控制人物行走的逻辑。
a.因为第10步的时候,person已经挂载了一个AudioSource组件,里面放的是开枪的声音,所以我们如果要再加一个AudioSource组件,放走路的声音,会导致随机选取两个声音之中的一个声音调用
解决方法是,可以在person的子节点下面挂载一个AudioSource组件,关联走路的声音,这样节点不同,关联的AudioSource组件也不同
打开fps_ctrl.cs
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class fps_ctrl : MonoBehaviour {
- public float move_speed = 8.0f;//定义人物移动的速度
- //开枪的声音Resources文件夹---->Step,给person的子节点加一个AudioSource组件,关联Step,再把person的这个子节点拖进walk_audio
- //AudioSource组件的Play On Awake打钩去掉
- public AudioSource walk_audio = null;
- // Use this for initialization
- void Start () {
- }
- // 单独分支出一个update函数用来做移动,W(上)S(下)A(左)D(右)
- bool walk_update() {
- if (Input.GetKey(KeyCode.W)){//GetKey一直按下,一直触发,GetKeyDown是按下再弹起来一次,触发一次
- float distance = this.move_speed * Time.deltaTime;//1秒移动的距离
- this.transform.Translate(, , distance, Space.Self);//沿着自己的空间,也就是本地坐标移动,不是世界坐标
- return true;
- }
- if (Input.GetKey(KeyCode.S)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(, , -distance, Space.Self);
- return true;
- }
- if (Input.GetKey(KeyCode.A)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(-distance, , , Space.Self);
- return true;
- }
- if (Input.GetKey(KeyCode.D)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(distance, , , Space.Self);
- return true;
- }
- return false;
- }
- // Update is called once per frame
- void Update () {
- // 如果有移动,就播放行走的声音
- if (this.walk_update()) {
- if (!this.walk_audio.isPlaying) {//正在播放就不要再播了,减少调用
- this.walk_audio.Play();//不是正在播放就播一下
- }
- }
- }
- }
枪的上下左右移动
12.打开脚本fps_ctrl,写好枪的上下左右移动的逻辑,就是鼠标转动的时候,摄像机也会跟着转动,整个画面就跟着转动
a.限制值的范围的函数Mathf.Clamp(value, min, max);
b.父节点控制世界的欧拉角的时候,子节点控制本地的欧拉角,让引擎帮我们把本地转换为世界,不会乱
打开脚本fps_ctrl
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class fps_ctrl : MonoBehaviour {
- public float move_speed = 8.0f;//定义人物移动的速度
- //开枪的声音Resources文件夹---->Step,给person的子节点加一个AudioSource组件,关联Step,再把person的这个子节点拖进walk_audio
- //AudioSource组件的Play On Awake打钩去掉
- public AudioSource walk_audio = null;
- float rot_y = 0.0f;//绕主角自身y轴总共旋转了多少
- float rot_x = 0.0f;//绕主角自身x轴总共旋转了多少
- public Transform eye = null;//获得摄像机节点
- public Transform right_hand = null;//握枪的手的节点
- public float sensibility = 5.0f; //灵敏度,鼠标移动多少像素和旋转转动多少角度的单位比例
- // Use this for initialization
- void Start () {
- }
- // 单独分支出一个update函数用来做移动,W(上)S(下)A(左)D(右)
- bool walk_update() {
- if (Input.GetKey(KeyCode.W)){//GetKey一直按下,一直触发,GetKeyDown是按下再弹起来一次,触发一次
- float distance = this.move_speed * Time.deltaTime;//1秒移动的距离
- this.transform.Translate(, , distance, Space.Self);//沿着自己的空间,也就是本地坐标移动,不是世界坐标
- return true;
- }
- if (Input.GetKey(KeyCode.S)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(, , -distance, Space.Self);
- return true;
- }
- if (Input.GetKey(KeyCode.A)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(-distance, , , Space.Self);
- return true;
- }
- if (Input.GetKey(KeyCode.D)) {
- float distance = this.move_speed * Time.deltaTime;
- this.transform.Translate(distance, , , Space.Self);
- return true;
- }
- return false;
- }
- // 单独分支出一个update函数用来做枪的上下左右转动
- void look_update() {
- // 左右移动,主角绕自身Y轴左右旋转,在Edit---->Project Settings---->Input里面有配置Mouse X,鼠标在屏幕的X轴上移动的量
- //两者相乘获得一个旋转的角度
- this.rot_y += (Input.GetAxis("Mouse X") * sensibility);
- // 给这个旋转一个范围[-360, 360],之间的用this.rot_y,小于-360返回-360,超过360返回360
- this.rot_y = Mathf.Clamp(this.rot_y, -, );
- //获得当前节点世界的欧拉角
- Vector3 e_rot = this.transform.eulerAngles;
- //设置当前节点的欧拉角的y为我们变化后的y
- e_rot.y = this.rot_y;
- //赋值给原欧拉角
- this.transform.eulerAngles = e_rot;
- // end
- // 上下旋转, 摄像机绕自身X轴上下旋转,在Edit---->Project Settings---->Input里面有配置Mouse Y,鼠标在屏幕的Y轴上移动的量
- //往上抬时旋转的角度为负,这样旋转的时候是逆时针,这样摄像机就向上抬
- this.rot_x -= (Input.GetAxis("Mouse Y") * sensibility);
- //范围[-45, 45],之间的用this.rot_y,小于-45返回-45,超过45返回45
- this.rot_x = Mathf.Clamp(this.rot_x, -, );
- //获得摄像机节点的本地的欧拉角,因为它是person的子节点,左右移动时person已经控制了世界的欧拉角,这里只能控制本地欧拉角
- //如果用世界的欧拉角还要叠加父亲的欧拉角的旋转,我认为用世界坐标会导致减去父节点的世界坐标后得到的原来的本地坐标乱了
- //用本地的欧拉角,让引擎帮我们转成世界的欧拉角,不会乱
- e_rot = this.eye.localEulerAngles;
- //设置当前节点的欧拉角的x为我们变化后的x
- e_rot.x = this.rot_x;
- //赋值给原欧拉角
- this.eye.localEulerAngles = e_rot;
- // end
- //握枪的手也要跟着摄像机转动
- e_rot = this.right_hand.localEulerAngles;
- e_rot.x = this.rot_x;
- this.right_hand.localEulerAngles = e_rot;
- // end
- }
- // Update is called once per frame
- void Update () {
- // 如果有移动,就播放行走的声音
- if (this.walk_update()) {
- if (!this.walk_audio.isPlaying) {//正在播放就不要再播了,减少调用
- this.walk_audio.Play();//不是正在播放就播一下
- }
- }
- this.look_update();
- }
- }
13.最终效果
关于Unity中FPS第一人称射击类游戏制作(专题十)的更多相关文章
- D3D游戏编程系列(六):自己动手编写第一人称射击游戏之第一人称视角的构建
说起第一人称射击游戏,不得不提第一人称视角啊,没有这个,那么这个第一就无从谈起啊,我作为一个观察者究竟如何在这个地图上顺利的移动和观察呢,那么,我们一起来研究下. 我们首先来看下CDXCamera类: ...
- [3D]第一人称相机类Camera
自己根据C++ D3D的源码改写一个相机类(第一人称). using System; using System.Collections.Generic; using System.Linq; usin ...
- 【转】265行JavaScript代码的第一人称3D H5游戏Demo
译文:http://blog.jobbole.com/70956/ 原文:http://www.playfuljs.com/a-first-person-engine-in-265-lines/ 这是 ...
- Unity中巧用协程和游戏对象的生命周期处理游戏重启的问题
主要用到协程(Coroutines)和游戏对象的生命周期(GameObject Lifecycle)基础知识,巧妙解决了游戏重启的问题. 关于协程,这里有篇文章我觉得写的非常好,理解起来也很容易.推荐 ...
- HTML5射击类游戏----【地球保卫战】
在线DEMO地址:打开: 游戏截图: 就不贴代码了, 因为代码太多了, 大概写一下这个游戏实现思路和一些实现: 游戏一共有三关, 每一关都有一个大Boss, Boss比较好杀,主要各种外星飞 ...
- 使用Unity中的Box Collider组件完成游戏场景中的碰撞检测功能
一.介绍 目的:通过Unity自带的组件完成游戏场景中的碰撞检测功能. 软件环境:Unity 2017.3.0f3 二.实现过程 1,在面板中点击Add Component按钮 2,添加Box Col ...
- 265行JavaScript代码的第一人称3D H5游戏Demo【个人总结1】
本文目的是分解前面的代码.其实,它得逻辑很清楚,只是对于我这种只是用过 Canvas 画线(用过 Fabric.js Canvas库)的人来说,这个还是很复杂的.我研究这个背景天空也是搞了一天,下面就 ...
- 怎样在Android开发中FPS游戏实现的两种方式比较
怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...
- 开发一个最简单的Cardboard虚拟现实应用(四)做一个Cardboard第一人称控制器
[开源互助-原创文章,转载请说明出处]第三帖中已经创建了一个cardboard自带的demo应用,但它是不能移动的,玩家只能站在原地,通过头部转动来观察四周,除此之外,玩家并没有更多的手段与游戏场景进 ...
随机推荐
- Linux系统性能监控之6个vmstat和6个iostat命令
这篇文章主要介绍一些Linux性能检测相关的命令. vmstat和iostat的两个命令可以运行在主流的Linux/Unix操作系统上. 如果vmstat和iostat命令不能再你的电脑上运行,请安装 ...
- Python安装pycurl失败,及解决办法
Centos安装pycurl centos 安装pycurl yum install python-devel curl-devel pip3 install pycurl Mac(老版本)安装pyc ...
- 【转】Linux mysql停止失败的解决办法 Stopping MySQL database server mysqld [fail]
root@ubuntu:/# /etc/init.d/mysql stop * Stopping MySQL database server mysqld [fail] root@ubuntu:/# ...
- decode和encode
作者:于洋链接:https://www.zhihu.com/question/23374078/answer/69732605来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...
- [转]Oracle SQL函数pivot、unpivot转置函数实现行转列、列转行
原文地址:http://blog.csdn.net/seandba/article/details/72730657 函数PIVOT.UNPIVOT转置函数实现行转列.列转行,效果如下图所示: 1.P ...
- Spring的Assert工具类的用法
简介 今天在看spring mvc源码时看到下面代码,感觉蛮有意思的,在这里记录下 Assert断言工具类,通常用于数据合法性检查,在JAVA编程中,通常会编写如下代码: if (name == nu ...
- 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0
从Excel中导入数据时,提示“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”的解决办法 操作系统:使用的是64位的Windows Server 2008 解决办法 ...
- Sphinx/Coreseek 4.1的安装流程
yum install mysql-devel libxml2-devel expat-devel cd /data/software/ wget http://www.coreseek.cn/upl ...
- 【Unity】制作简易定时器(Timer)
最近开始学习Unity,也想开始学习写一些简单的博客. 在网上学习了一些关于定时器的写法,在此简单总结一下,方便自己以后用到时查阅. 需求:制作定时器,运行3秒后执行第一次,之后每隔3秒执行一次操作. ...
- am335x uboot, kernel 编译
一.设置环境变量 // 写在家目录下面的 .bashrc 里面 export KERNEL_PATH=~/aplex/kernel3.2.0 // kernel 路径 export UBOOT_PAT ...