在Unity中使用UFPS创建第一人称射击游戏

Unity Asset Store资源商店中总有很多功能强大的插件让开发者事半功倍,例如UFPS : Ultimate
FPS因其具备的平滑控制、流畅实时生成的相机和武器动作的功能,而受到广大开发者的欢迎。本文由Unity大中华区技术经理马瑞为大家介绍一下UFPS :
Ultimate FPS这款插件对创建第一人称射击游戏的贡献。

UFPS: Ultimate FPS是什么?

UFPS,即 Ultimate FPS,是由Opsive(FPS)
开发的模板项目,这个项目对初级或中级开发者来说是必不可少的,因为它可以帮助您提高FPS游戏运行效率。它是Unity Asset
Store中维护时间最长的资源之一, 在整个生命周期中,得到了来自开发者的诸多好评。已经有很多游戏使用了这个模板,例如:

Time Rifters

Slender: The Arrival

Rambo (the mobile game)

Shark Attack Deathmatch 2

Gone Home

Tacoma

Grave

Reflections

Ascend

Surviving Pangea

强大的功能

UFPS: Ultimate FPS有一些很有用的功能:

基于物理的动画 — 让流畅的枪击和相机抖动等动作更加真实

鼠标平滑和加速 — 您可以选择不同的鼠标移动模式,这样在为不同类型的角色,如机甲,士兵等开发角色控制器时,可能会很方便。

支持本地Oculus VR — 在日益增长的VR市场环境下,开发者将VR结合进来是被很多人看好的。

拥有机械动画播放器的全身感知 —
含有一个全身模型和动画。生成地形(Terrain)或可步行空间后,您可以指定曲面的材质,并将不同的声效和粒子分配给不同的材质。

手榴弹,爆炸系统和掩护(Cover)支持 — 大多数现代FPS游戏允许玩家使用手榴弹。您可以借助UFPS创造全新爆炸型手榴弹并且在躲在墙后以免受伤。

具有方向攻击指示器和血液飞溅效果的HUD — 当玩家受到伤害时,这个HUD会变得“血腥”。此外,箭头可以帮助我们辨别我们在哪个方向被攻击。

地震、冲击波、Boss挑战和相机反馈 —
UFPS为开发者提供了一套漂亮的相机抖动。他们被某些事件触发,例如:玩家从高处跌落,或者附近有一个强烈的爆炸/地震。

库存和物品捡起系统。

高级表面碰撞系统 — 如果您射击沙子或草地,会发现有不同的粒子产生。

Spawnpoint系统与智能障碍检测。

基于键盘,鼠标或触摸屏的UFPS输入管理器 — 允许在运行时重新绑定控制器。

拆除系统 — 玩家销毁的对象(Object)将被新对象(Object)替换。

交互系统 —门,平台,触发器,开关,抓取和投掷东西的交互。

表面系统允许通用和强大的物理模拟

高级移动平台支持

慢动作模式 — 像Max Payne游戏中让时间变慢

支持Unity Pro专业版图像FX

支持反欺诈工具箱ObscuedTypes

完整且良好注释的C#代码

100多页详细的在线学习手册

下载地址:

https://www.assetstore.unity3d.com/en/#!/content/2943

演示场景

下面我们介绍几个演示场景,包括简单场景和完整项目,以便更清晰地了解UFPS的功能。

Clean Scene

这个场景不太复杂,含有地形(Terrain)和第一视角摄像机控制器。您可以尝试跌落悬崖,看看相机如何对这个事件做出反应。

** **

Demo Scene 1

这个场景包含很多例子。第一个例子演示了在使用UFPS时可以实现多少不同的效果。您可以轻松实现西部牛仔风格的游戏、现代FPS、太空宇航员、炮塔、狙击步枪、甚至控制机甲!
如下图所示。

第二个示例为您展示了可以实现的不同的相机抖动,比如地震,Boss挑战,进攻的炮兵和撞毁的飞机。它们其中的任何一个事件将带来不同的相机抖动。您可以在后面即将介绍的Sky
City演示中了解到:当您在玩家的附近投掷手榴弹时,看到类似的效果如下图所示。

每种类型的游戏需要的鼠标控制方式略有不同。例如使用手枪相对于使用狙击步枪时,可能想要实现不同的效果。从下面的例子中您可以了解到如何实现不同的鼠标模式,并在游戏视图中看到不同的效果。

您可以改变持有的武器风格,比如Old School和Modern Shooter等,如下图所示。

** **

Demo Scene 2

这个场景的目的是展示基于物理的摄像机动画如何对不同的事件作出反应,如掉下屋顶,从高处跳跃等。我们很期待看到当玩家附近发生爆炸以及当玩家用他的头撞到墙上等情况发生时,相机会作何反应。

** **

Demo Scene 3

这个演示场景看起来更像一个完整的项目。您可以收集武器,用步枪、手枪或者替换其他武器进行射击,爆炸的对象可能会造成损害,并降低您的生命值。您还可以在场景周围抓取和移动对象,销毁一些对象
。当您进入白色气泡,就会切换为慢动作模式,时间将减慢几秒钟,产生Matrix/Max Payne效果。

** **

Sky City

最后的演示是一个完整的项目。在这个场景中,你将围绕建筑物攻击敌人的塔楼。玩家可以藏在墙后面躲避子弹。
您可以在猎枪,步枪,手枪和手榴弹之间切换,体会到在一个完整的FPS游戏中,不同的系统是如何协同工作的。

总结

如果想要建立第一视角射击游戏,UFPS是一个非常好的起点。它的一系列有用的元素可以被用来构建游戏,例如将UFPS作为坚实的框架,并添加游戏特定的逻辑和资源后,就可以变成一个完整的游戏。不过请记住,插件本身不是最终产品,您仍然需要在资源和代码方面之下一些功夫,来完善您的游戏。想要了解更多技术相关内容,请访问Unity官方中文社区(forum.china.unity3d.com)。

Strumpy-Shader-Editor入门教程

对于3D游戏来说,有很多绚丽的效果,都是靠shader(着色器)来实现的。不过很多朋友估计都不会编写shader,阿赵我自己也只是看了个入门,明白了它的原理,很多具体的效果都写不出来的。这次来介绍一个Unity3D的第三方shader编辑器:Strumpy。这个编辑器是完全可视化编辑,使用起来相对简单一点。

先来看看我们这次需要做的例子:

我们需要使用Strumpy,编辑出这样一个shader:包括了漫反射通道、法线通道、高光通道,以及在模型的边缘运动的光效。

很显然,Unity3D自带的shader没有能直接实现这样的功能的,最多也只能使用法线和高光通道而已。

由于这个例子稍微复杂,所以我们先来做一个更简单的例子,来熟悉一下strumpy的界面和基本操作。

首先肯定是要先下载Strumpy插件了。在Asset Store里面有,免费下载的。我这里使用的是4.0a版本。

下载完之后导入,会看到多了一个选项:

选择之后打开了Strumpy的编辑窗口:

Flie的功能很直观,新建,读取,保存和导出的功能,我们可以先看看读取功能

插件自带了一些已经编辑好的范例,有兴趣的朋友可以逐个看看,会有很大的收益。

然后我们来正式开始做这个简单的例子,我们准备了一张带有透明通道的贴图(拿斩首大刀的阿赵),接下来我们做一个shader,把这张贴图显示在一个面片上面,使它实现漫反射通道和透明通道的显示。

我们新建一个着色器编辑:

注意看,新建时,在Settings的标签是红色的,然后Shader
Name也是红色的,这是提醒我们,每一个shader必须要先有一个名称,而这个名称就是以后你在材质栏里面选择的材质名称了。比如你可以用“myshader/test”,这样test材质会出现在myshader下面。

输入材质名词之后,我们先来Inputs里面,新建一个输入。这个输入,就是我们平常在Unity自带的材质球里面看到的输入通道了。假如我们需要它能调节颜色,那么就要新建一个颜色的输入,假如需要调用贴图,就要新建一个贴图的输入。这里我们新建一个Unity预设的MainTexture。熟悉用脚本替换材质贴图的朋友估计很熟悉这个标签的含义了。

新建之后,我们可以看到,出现了一个贴图选择的通道,就像我们平常操作的自带材质球一样。

接下来我们会新建一些节点,然后对他们进行编辑。

创建节点的方式有两种:

第一种是在Nodes里面选择相应的节点:

第二种是在节点编辑的窗口鼠标右键单击,选择相应的节点。

我个人比较习惯第二种方法。

我们选择了一个Sampler2D的输入

刚生成的时候,这个节点是红色的,因为我们没有指定输入的来源。想起刚才我们新建的Input了吧?那个MainTexture指定在这里。

在选中该节点的情况下,来到Node的标签,会看到提示错误了

我们选择_MainTex

这时候,节点就不再是红色了,而是出现了两项输出,分别是2D采样和UV信息。节点名称里面,也会相应的显示出_MainTex,也就是我们刚才新建的Input的名称。

接下来我们新建一个Tex2D方法节点。

然后像上图一样,用鼠标把他们之间连起来。

这时候,把我们预先准备好的贴图指定在贴图通道里面

点击预览窗口的Update Preview,会看到预览的物体上面出现了我们的贴图

为了便于观察,我们选择一个片面模型。这时候,漫反射通道已经完成了,接下来我们继续做透明通道。

如上图一样,把A连接到Alpha通道。

然后来到Settings,按上图设置一下。

打开背景显示,会看到透明通道已经生效了。

在完成了以上的小例子之后,我们正式来解释一下Strumpy各个部分的意思吧。

在master里面,分别是各个通道的最终输出。

节点是通过有方向性的线条来连接的,分为输入端和输出端,上图是一个单向输入输出的例子。

再来是一个运算的例子,Add是相加的操作,这里是两个输入端进入了相加,然后输出一个结果。

在这里要说明一下的是,这些操作很大一部分都是数学运算的方法,比如加减乘除、sin、cos之类,各位在学习之前最好要先理解他们的意思。比如相加就是互相叠加,两张图相加会整张图都变得更亮。相乘是波峰波谷的叠加,两张图相乘,会使亮的地方更亮,暗的地方更暗。其他的方法请各位自行百度去查阅了,就不一一说明。

再来看看设置的选项

如果曾经自己写过shader的朋友,对于这些选项应该是很熟悉的。比如很多朋友问的双面显示,其实就是把CullMode选择为Off就行。

介绍完基本功能,我们正式的来做这次的目标例子了。

这里我们准备了一个模型。

模型带有了漫反射贴图和法线贴图。

首先新建一个着色器编辑,然后给shader起名为:myShader

按照第一个例子的操作,我们完成了漫反射通道。

为了应用在我们的模型上,我们需要先生成一个shader。选择导出(Export As)

给shader起一个文件名。这个名称没有太大的意义。

导出之后,我们来到材质球选择的地方,会看到了我们新建的myShader,选择它。

然后指定漫反射贴图,我们的模型变成了上图的效果。

接下来,我们在Inputs里面新建一个凹凸贴图的输入

然后完成法线通道节点的编辑。由于需要使用法线贴图,所以需要加入一个UnPackNormal的节点。

这时候,我们会发现输入通道处多了Normalmap通道,我们把法线贴图赋予上去。模型变成上图的效果。

我们再新建一个颜色输入,作为高光的颜色。

完成高光通道和光泽强度的节点编辑。

这里我用了一个Multiply(相乘),目的是让光泽强度范围的对比度更大,显得高光会更尖锐一点。高光颜色直接连接到Specular通道。

现在我们的模型已经拥有了一定的质感了。由于模型原来是没有法线贴图的,我拿了漫反射贴图来直接转换,所以效果差了点,有兴趣的朋友可以自己做法线贴图增强效果。

接下来做有动画效果的光。

我们新建了几个输入:发光颜色(_LightColor),一个发光颜色的遮罩贴图(_Light),一个浮点(_dir)作为光运动的方向,一个范围选择(_RimPow)作为发光强度的控制。

这张是发光遮罩贴图,是一张黑白的梯度图。

这一个步骤的节点编辑有点复杂,基本的思路是将颜色和遮罩贴图混合在一起,并用时间控制UV动画:

1、遮罩贴图我用了ViewDirection和Fresnel结合控制显示方向,并用_RimPow作为显示方向的次幂控制显示强度。

2、为了让光会随着时间自己做动画,我是用来Time和_dir相乘,这样可以控制动画的速度和方向。

3、为了让UV移动做动画,我用了UV_Pan。记得UV_Pan需要选择输入的轴,这里我选择了Y轴。

4、最后,我把发光颜色和遮罩运算的结果相乘,达到叠加波峰改变颜色的目的。

最后,发布shader,根据需要选择相应的遮罩贴图,指定发光的颜色、强度和方向,我们的例子就完成了。

补充说明几点:

1、可能很多功能自带的shader都有,不过有时候你就是会需要一些功能特殊一点的shader,具体需要什么输入通道,和怎样的操作,请根据实际情况考虑。

2、关于即时镜面反射的shader,估计很多朋友都很感兴趣。不过我这里不打算详细介绍,因为unity没有直接即时反射运算的shader。

简单说明一下其原理:

在自带的水面和愤怒机器人场景里面,我们都看到了类似镜面反射的效果,其实这都是一种欺骗。做法不算复杂,其实是根据了当前摄像机的位置,新建了一个相对角度的反射摄像机,并把反射摄像机看到的内容渲染成RenderTexture。最后把RenderTexture和位置矩阵输入到普通材质的贴图通道,达到好像即时反射的效果。水面的例子做法会傻一点,因为它是针对水面物体自己生成了一个反射摄像机,所以的反射效果只会对水面本身有效果,每个可以反射的面,都要单独生成。而愤怒机器人里面的例子会聪明很多,它会在摄像机的脚本里面根据预先选择的可反射的图层,统一生成了一张RenderTexture,并让所有可以反射的shader使用。

结合着即时反射这个例子,可以看出实际上很多特殊的效果,都不是单独的shader能直接实现的,还需要到其他的脚本去配合。

FingerGestures

FingerGestures是一款强大的手势识别插件,支持鼠标和触控。跨平台,最新的3.0版本支持自定义手势识别。

其中CustomGestures的识别还是挺准的。测试画个一笔五角星,能被容易的识别。

直接解压包,里面还有samples和对playerMaker拓展 2个子包。

二.导入Sample包

在搜索栏里输入 t:scene, 然后全选场景并拖入Build Setting里的Scenes in Build

把’Sample Browser’场景调整到第一个场景

也可在player setting里把屏幕设置为600x400

[三.基本识别

GestureRecognizers(检测用户输入并发送事件)](http://photo.blog.sina.com.cn/showpic.html#blogid=471132920101hlwt&url=http://s14.sinaimg.cn/orignal/47113292gd94359d9fddd)

场景里必须只有一个FingerGestures组件示例。它相当于Manager。

(1)直接新建GameObject起名Manager,并加FingerGestures脚本

(2)直接新建GameObject起名Gestures,并加TapRecognizer脚本

直接新建TapTutorial.cs 并放到Gestures上

Gestures里TapRecognizer面板下,点”Copy Event To Clipboard”
把对应代码拷贝到粘贴板上,并在TapTutorial脚本里粘贴代码。

当recognizer检测对应输入到后, 会在obj上发对应的SendMessage消息。但是SendMessage开销大
,可以自己实现开销更小的delegate-based events.

(5)打输出debug信息的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using UnityEngine;



using System.Collections;



publicclass TapTutorial : MonoBehaviour



 OnTap(TapGesture gesture)



  Debug.Log( "Tap gesture detected at " + gesture.Position +



            ". It was sent by " + gesture.Recognizer.name );

识别单击物体

(1)创建球,确保有collider或者trigger,位置(0,1,0),缩放(4,4,4)

(2)Gesture物体上加 ScreenRaycaster

当Ray Thickness为0时 可以是collider或者trigger,

当Ray Thickness不为0时 必须是collider, 可以模拟厚手指的操作。

(2) 脚本变为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;



using System.Collections;



public class TapTutorial : MonoBehaviour



OnTap( TapGesture gesture )



    ( gesture.Selection )



        Debug.Log( "Tapped object: " + gesture.Selection.name );



        Debug.Log( "No object was tapped at " + gesture.Position );

之后就能检测到点击物体了,并且被点击的圆球同样的收到同名事件

识别2手指Tap3下

设置 Required Finger Count为2

设置 Required Taps为3

如果是电脑运行,用鼠标左右键同时按模拟

四.FingerEventDetector

检测单手指的按下 松开 经过 移动 静止,与GesturesRecognizers类似,发消息,用ScreenRaycaster与物体互动。

FingerEventDetector是抽象类,各种finger event detectors继承自它。

也可以提供finger index跟踪某指定手指。

(1)新场景,加FingerGestures管理

(2)新物体起名FingerEvent,并加FingerDownDetector

(3)新建脚本FingerEventTutor.cs, 并从FingerDownDetector拷贝粘贴脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;



using System.Collections;



public class FingerEventTutor : MonoBehaviour



OnFingerDown(FingerDownEvent e)



Debug.Log( e.Finger + " Down at " + e.Position + " on object:" + e.Selection
);

点击运行,即可检测任何手指按下的事件。

每个手指事件都要添加对应的detector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using UnityEngine;



using System.Collections;



public class FingerEventTutor : MonoBehaviour



OnFingerDown(FingerDownEvent e)



Debug.Log( e.Finger + " Down at " + e.Position + " on object:" + e.Selection
);



OnFingerUp( FingerUpEvent e )



// time the finger has been held down before being released



    float elapsedTime = e.TimeHeldDown;



Debug.Log( e.Finger + " Up at " + e.Position + " on object:" + e.Selection
);

五.自定义手势识别

3.0后可以用PointCloudRecognizer来识别自定义手势。算法使用的是$P recognizer. 现在只支持single-
stroke(单手指单次画),将会支持multi-strokes.

PointCloudRecognizer将会对比一些gesture模板并返回最接近的 匹配图形,并返回分数和距离值。

PointCloud
gestures手势,和缩放以及画的方向无关。但是图形的旋转必须是固定的,例如一个正着摆放的三角形你必须画正的,你画一个倒着摆放的三角形它是不能被识别的。

绘制PointCloud

(1)在Assets栏下,创建 PointCloud Gesture Template,起名MyPCGesture

(2)点击Edit,绘制图形,然后Apply

使用PointCloud

(1)新场景,加FingerGestures管理

(2)创建新的物体起名Gestures

Gestures加 PointCloudRecognizer 组件。

Max Match Distance 用户画的图形distance必须在该值之下, 设定得越小,画得就必须越精确。

Sampling Distance 两个连续手指位置的最小间距。越小表示越精确,但更多的采样

Gesture Template List 要去匹配的图形库

MyPCGesture加至 Gesture Template List

(5) 创建PointCloudTutorial.cs脚本并添加至Gestures物体下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 using UnityEngine;



using System.Collections;



public class PointCloudTutorial : MonoBehaviour



     OnCustomGesture( PointCloudGesture gesture )



        Debug.Log( "Recognized custom gesture: " +
gesture.RecognizedTemplate.name +



            ", match score: " + gesture.MatchScore +



            ", match distance: " + gesture.MatchDistance );

PointCloudGesture. RecognizedTemplate 对应画出的图形模板

PointCloudGesture. MatchScore 匹配百分比,1代表完美匹配

PointCloudGesture. MatchDistance 与图形有多接近

当然也可以从代码绘制PointCloudGestureTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
 Awake()



    PointCloudGestureTemplate triangle =
ScriptableObject.CreateInstance<PointCloudGestureTemplate();



    triangle.name = "Triangle Gesture Template"



    triangle.BeginPoints();



    triangle.AddPoint( , , );



    triangle.AddPoint( , , );



    triangle.AddPoint( , , );



    triangle.AddPoint( , , );



    triangle.EndPoints();



    PointCloudGestureTemplate square =
ScriptableObject.CreateInstance<PointCloudGestureTemplate();



    square.name = "Square Gesture Template"



    square.BeginPoints();



    square.AddPoint( , , );



    square.AddPoint( , , );



    square.AddPoint( , , );



    square.AddPoint( , , );



    square.AddPoint( , , );



    square.EndPoints();



    PointCloudRegognizer recognizer =
gameObject.AddComponent<PointCloudRegognizer();



    recognizer.AddTemplate( triangle );



    recognizer.AddTemplate( square );

AddPoint的第一个参数代表第几画,但是现在只支持一笔画出来的图形,所以该值只填0。当EndPoints()被调用的时候,所有点都会被单位化至(0,1)的范围。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×