unity3d开启失败提示发着色器部分能有智能提示吗?

Unity3D 5 官方教程:标准着色器:内容和环境;金属与镜面工作流 - CSDN博客
Unity3D 5 官方教程:标准着色器:内容和环境;金属与镜面工作流
内容和环境
当在Unity中考虑光照,很方便将概念区分。被称为“内容”的是被光照和被渲染的物品;而环境,是场景中存在的,影响了被照射物体的光。
当光照一个物体时,搞清楚影响此物体的光源是哪个很重要。通常在场景中存在有向光源:已经放置在场景内的游戏对象。也有非有向光源,如反射和散射光。这些都会在物体的材质上产生影响,来形成摄像机看向物体表面时的最终效果。
可能被认为是“内容”的物体,也可以成为另一个物体的部分光照环境。一个好例子是,沙漠地表上的建筑。建筑可以从天空盒得到光照信息,并且可能接收到周围地面的反射光。
然而,可能有个角色站在建筑物的外墙附近。对于这个角色,建筑物是光照环境的一部分——它可能产生个阴影,它可能从外墙产生照在角色上的反射光,或者角色可能拥有反射部分,直接反射了建筑物自身。
默认的光照环境
At startup, Unity 5 shows an empty scene. This scene already has a default lighting context available with ambient, skydome-based reflections and a directional light. Any object placed in that scene should, by default, have all lighting it needs to look correct.
在启动时,Unity5 展示了一个空的场景。这个场景已经有一个默认光照可用:有环境,基于天空穹顶的反射和一个有向光。任何放置在场景中的物体,默认拥有让它看起来正确的所有光照。
添加一个球体到场景中,看看默认光照的效果。
添加的球体将使用默认的标准着色器。将摄像机聚焦到球体上会导致像这样的情况:
注意沿球体边缘的反射以及铭感的环境,从棕色(底下)到天空蓝(顶部)。默认地,在一个空场景中,所有光照环境起源自天空盒和一个有向光(场景默认添加的)。
当然,这是默认设置,在某些情形下,一个单一光源和天空反射并不足够。可以容易地添加光照和反射探针:
反射和光照探针如何工作的深入了解,请阅读文档中关于光照探针和反射探针部分。
一个天空盒,烘焙了的或者procedural?,可以是光照设置的一个完整部分。它可以被用来控制环境光和在物体上除了渲染天空之外的反射。Procedural天空盒也允许直接设置颜色,创建一个日轮来代替使用位图——更多信息可以在天空盒文档中找到。
虽然反射天空盒对于场景中的很多物体有用,特别是户外场景;也经常有需要变化物体使用的反射的情形——可能在户外场景中有暗的区域,如小巷或茂密森林,或有要求反射的内部区域来匹配每个空间(?)。
为了达到这些多变的反射要求,Unity有反射探针,允许在场景空间中的一个特定点取样环境,对任何邻近此点的物体作为环境光和反射源,而非默认的天空盒。反射探针可以放在场景中任何天空盒不充分或不合适的位置。
全局照明的概念是Unity5不可或缺的。包括标准着色器和Unity5的GI系统已被设计为彼此工作良好。GI系统关注于创建和追踪反射光,来自放射性材质的光,以及来自环境的光。
环境是图像整体外观极为重要的部分。在这个粒子里可以看到,环境中场景反射改变了,而内容和摄像机保持原样。
内容是用来描述场景中被渲染物体的术语。它们的外观:是光照环境作用在已被应用到这些物体上的材质的结果。
材质编辑器
当在使用了标准着色器的检视器里查看材质时,编辑器显示材质的所有参数,包括纹理、混合模式、遮罩以及第二贴图。浏览后可以看到哪些属性被使用以及预览材质。由于标准着色器是数据驱动的:Unity将仅仅使用:已经被用户为材质设置的配置所要求的着色器代码。换言之,如果一个材质的特征或纹理槽不被使用,它就不产生开销,并且着色器结合在场景后被优化。
Tip:可以使用Ctrl+左键在纹理片上来查看大的预览,这也可以分别检查颜色及alpha的内容!
如何创建一个材质
为了表示极大数量的材质类型,标准着色器考虑许多配置。值可以用纹理贴图或颜色选择器及滑块来设置。普通的UV贴图在配合纹理中被要求,来描述网格哪个部分对应纹理贴图的哪个部分。当在配合镜面和平滑贴图或者金属贴图使用时,标准着色器材质允许在相同网格有不同的材质属性。即,可以在一个网格创建橡胶,金属以及木头;考虑到在材质边缘间光滑边界和过渡,纹理的分辨率可以超过多边形拓扑;当然这意味着在工作流中更大的复杂度,但这将依赖于纹理创建的方式。
材质的纹理以两种方式之一创建——在如PS的2D图像编辑器中绘制并混合;或从3D包中渲染/烘焙,这也可以利用更高分辨率模型来生成除了albedo、镜面、以及其它贴图之外的法线贴图和遮挡图。
一般地,没有纹理贴图应当包含内在光照(阴影,高光,等等)。PBS的一个好处是:如果贴图已经包含光照信息,那么物体对(其它?)光的反应,将不可能存在。
金属 vs 镜面 工作流
两个工作流
当用标准着色器创建一个材质,有使用两种风格的选择:“标准”和“标准(镜面设置”。他们在采用的数据上存在如下不同:
标准:着色器曝光在“金属光泽”的数值,无论材质是否为金属的。在金属材质的情形下,Albedo色将控制镜面反射的颜色,大部分光会被以镜面反射的方式反射。非金属材质将会镜面反射与入射光相同的颜色,并会仅仅在看向表面时进行反射。
标准(镜面设置):对传统方法选择此着色器。一个镜面颜色被使用,来在材质上的控制颜色和镜面反射的强度。这使得 比如拥有与漫反射不同的颜色这种事成为可能。
使用以上方法之一,对大部分普通材质达成良好的表现效果是可能的;选择哪一个,取决于适宜你的艺术工作流的偏好。例如,下面分别是在标准和标准镜面工作流下创建的一个橡胶塑料材质:
随着材质的表面变得更平滑,菲涅尔效果在入射角可见,与观察者越来越明显有关。
第一幅图代表了金属工作流,我们设置了材质的Metallic为0(非金属)。第二个设置几乎一样,但我们设置Specular接近为黑(所以没有得到如金属镜面那样的反射)。
可能会有这些疑问:值如何得到的,什么是“接近黑”,以及到底是什么让草与铝不同?在基于物理着色的世界里,可以参考已知的真实世界材质。这些参考的一部分已经集成了,可以用来创建自己定制的材质。
本文已收录于以下专栏:
相关文章推荐
标准着色器的材质参数(一):渲染模式,反射率和透明度,镜面模式的镜面参数
刚开始也不知道什么是pbr (Physically Based Rendering)后来才发现这是一种新的渲染方式
与之对应的是材质是pbs(Physically Based Shader)
材质金属颜色
这篇在上一篇的基础上增加了对PBR的认识,主要包括了金属度和粗糙度(光滑度)的测试
unity里PBR流程,PBR材质属性具体分析
传统模型到PBR的流程,一些PBR制作转换工具介绍
以前这篇http...
点击这里下载
原文地址点击这里
首先着色器来自这里:/forum/read.php?tid=3162
没有账号的童鞋可以到这里下载这个着色器和脚本:http://pan.baidu....
镜面反射与观察视角的联系
系列6中我已经说明了材料表面的平整程度决定了镜面反射的明显与否,现实生活中找不到绝对平的物体表面,所以我们引入一个概念,每一种材料的表面的平整程度为Nshininess, ...
只需导入两张纹理图片,然后在Unity中做相应设置就可以了,果然这个软件很强大,都不带写代码的。做游戏特效越来越简单,是好事,还是坏事
在Unity中,我们在进行物体控制和交互过程中经常会涉及到动态改变Shader的情况,如将一个三维物体用网格显示、将三维物体的表现形式进行变更等等。
在Unity中主要通过MeshRenderer组...
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Unity3D教程:着色器 | Unity3D教程手册
当前位置 :
>> Unity3D教程:着色器
Unity3D教程:着色器
如下图所示,在Project面板中新建一个New Material.这样子就准备好了一个空白的着色器。它能支持的类型也就在Shader中全部显示出来。不得不说,很好很强大。
Unity3D教程:着色器
所有这些Pixel[像素]着色器,默认的Diffuse算是资源开销较小的渲染选择。随后那些带有凹凸贴图的着色器,其次就是Bumped Specular[凹凸高光],Parallax Specular[视差高光]类等开销大。所有基于顶点光照的着色器开销都比Pixel着色器的开销小。但是相对来说效果可能不是那么强大,毕竟打造好的视觉效果增大开销是在所难免。
着色器共有的主要属性:
Diffuse[散射]:定义对象的基本颜色,可以使用贴图或者简单的颜色选择器来控制散射,所有的着色器都带有某种类型的散射属性。
Bump and Parallax Bump[凹凸和视差凹凸]:名称中带有Bump或者Parallax的着色器都带有的一个属性,使得他们可以模拟对象的高度和深度。比如我们需要对对象的每一个伤痕或者细节进行建模,这样很耗费资源,此时聚划算的方法就是使用凹凸贴图来模拟。
Specular[高光]:带有Specular属性的着色器都可以使对象在光源照射下具有漂亮的光泽,注意这和反射是不同的。
下面,创建一个吧:
1:先把ToonShading
Unity3D教程:着色器
导入Project,在Hierarchy面板中创建一个Cube,此时看到的只是个纯色的方体。
Unity3D教程:着色器
2:在Project面板中创建一个Materials,将着色器的Shader选为:Toon—〉BasicOutLine。
Unity3D教程:着色器
3:点击上图中的Base(RGB)右侧的方框的Select按钮,选一个你喜欢的图片吧。下边的那一个ToonShader Cubemap的Select选toony lighing。这样子我们的着色器就可以使用了。预览一下:点击Preview那一栏的右边的原型按钮,可以变换模型哦。你可以对模型进行各个方向的拖拉查看。
Unity3D教程:着色器
Unity3D教程:着色器
4:把我们Project面板的着色器直接拖给场景的Cube物体。
Unity3D教程:着色器
我把outlineColor和outlinewidth都改一下。
Unity3D教程:着色器
Unity3D教程:着色器
关于天空盒[SkyBox]的着色器,就是Shader下的RenderFx/SkyBox。选择他以后你会看到下边列出了Front—Back—Left—Right—Up—Down的每个填充项。你用对应的天空贴图填充,就可以得到一个天空盒着色器了。
动态表面效果着色器脚本:
Unity3D教程:着色器
Shader "Effects/Plasma" {
Properties {
_MainTex ("Base (RGB)", RECT) = "white" {}
_ColorBand ("Base (RGB)", 2D) = "white" {}
SubShader {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
#pragma vertex vert_img
#pragma fragment fragSh
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform samplerRECT _MainT
uniform sampler2D _ColorB
float4 fragSh (v2f_img i) : COLOR
float2 ca = float2(0.2, 0.2);
float2 cb = float2(0.7, 0.9);
float da = distance(i.uv, ca);
float db = distance(i.uv, cb);
float c1 = sin(da * _CosTime.y * 16 + _Time.x);
float c2 = cos(i.uv.x * 8 + _Time.z);
float c3 = cos(db * 14) + _CosTime.x;
float p = (c1 + c2 + c3) / 3;
return tex2D(_ColorBand, float2(p, 0.5));
Fallback off
【上一篇】
【下一篇】
您可能还会对这些文章感兴趣!后使用快捷导航没有帐号?
 论坛入口:
  |   |    |   | 
技术贴:0经验上手手游Unity3D开发
iOS&Android&
Unity3D/2D&
  文 / 0!1!
  现在手游火的一塌糊涂,引擎也是层出不穷除了引领3D市场的Unity3D,独霸2D市场的Cocos2D-X之外,还有虚幻、Sphinx等,甚至搜狐也开发了国产的Genesis-3D引擎。
  文章适合人群:对Unity基础组件有一些了解的,想知道怎么在项目中具体应用各种组件。
  这篇文章以一个Asset Store上面的例子“Unity Projects Stealth”来讲解Unity的一些知识。所以可能你要对Unity一些概念有个了解。
  在此之前先说下本文运行项目时的环境:
  系统 :Window 7 X64
  Unity:4.3.3f1
  1、项目大致了解
  我们打开unity,新建项目,然后导入上面下载的&Unity Projects Stealth.unitypackage&。等导入完毕后就可以看到“Project”视图(如果你没打开,可以从菜单栏“Window”-“Project”打开)里面的结构如下:
image001.png (72.47 KB, 下载次数: 4)
16:39 上传
  里面每个目录大家都可以随便打开看看:
Animations 一些角色动画Animator 动画控制器,实际里面啥都没有-_-Audio音频文件Fonts字体Gizmos在scene视图用于调试的一些图标Materials材质Models模型Prefabs预设Scenes场景文件,实际里面啥都没有-_-Scripts脚本,实际里面啥都没有-_-Shaders着色器Textures一些贴图纹理Done这里面才包含了上面缺少的动画控制器、场景文件和脚本
  大家平常做项目也可以参考上面的做法,对不同的资源放不同目录进行归类整理,而且大家一看到这些文件夹就都知道里面有什么资源。可以减少很多沟通成本,也为自己查找资源带来便利。
  好了,如果没什么问题,点击播放按钮,应该就能运行项目了。
image002.png (716.33 KB, 下载次数: 6)
16:40 上传
  使用键盘的WASD或上下左右箭头就可以控制人物走动了。屏幕左下方也有文本给出其它操作的按键。比如&Z&可以打开开关等。
  2、碰撞器基础
  一般情况下,你想让物体直接有体积,能产生碰撞,那么你需要给这个物体增加一个碰撞器“Collider”。在Unity中,你只需要选择一个对象,然后点击菜单栏的”Component“-“Physics”,里面就有各种各样的碰撞器,你根据自己模型选择一个比较接近的就OK了。Unity里面的碰撞器区分为两种,一种为静态的,另一种动态的。
  静态的意思是这个碰撞器在游戏过程中不会发生位移、旋转、缩放。
  动态的意思则是说这个碰撞器可能会在游戏过程中发生位移、旋转、缩放。动态碰撞器有两种:
  一种是CharacterController。
  另一种是普通的碰撞器+刚体组件。
  第二种一定要增加刚体组件。不然可能会导致碰撞失效、性能开销增加。(比如Unity一个UI组件NGUI,它新版本的Panel会检测当前对象是否带了Rigibody,如果没有则自动增加一个。就是为了防止开发者做一些界面动画,忘记修改添加Rigibody,导致UI按钮点击失效。)
  3、控制角色
  控制一个角色在场景中运动,最简单的做法是把一个对象拖到场景中,然后根据按键。设置这个对象transform的position值。这样对象就可以在场景中运动起来。
  获取按键可以用Input.GetAxis方法。获取X和Z轴的按键分别可以使用:Input.GetAxis(&Horizontal&) 和 Input.GetAxis(&Vertical&)方法。“Horizontal”和“Vertical”其实是在“Edit”-“Project Settings”-“Input”里面配置的。
  那么我们这个例子是使用什么方法来让玩家角色运动的呢?
  先在“Hierarchy”视图(如果没有这个视图,可以在“Window”-“Hierarchy”打开它)中找到“char_ethan”对象,选中它。
image003.png (841.08 KB, 下载次数: 3)
16:41 上传
  我们先看看它的“Inspector”视图(如果没有可以在“Window”-“Inspector”打开它)。
image004.png (18.24 KB, 下载次数: 2)
16:41 上传
  注意这里除了有基本的Transform组件外,还有“Animator”,“Capsule Collider”,“Rigidbody”组件。这三个组件其实用于做可移动物体其实是黄金组合(当然也有用“Character Controller”的)。
  还有“Audio Source”、“Audio Listener”两个分别是播放声音和监听声音的组件。
  然后&Done Player Health (Script)&,&Done Player Inventory (Script)&,&Done Player Movement (Script)&分别是对角色做控制的脚本。下面我们简单说说每个组件的用途:
  A、Animator
  其实是Unity内置的一个动画控制器,原理是状态机。比如双击上面的“Animator”面板里面的“Controller”属性:
image005.png (10.56 KB, 下载次数: 3)
16:41 上传
  就会出现状态机的编辑窗口:
image006.png (55.76 KB, 下载次数: 3)
16:41 上传
  B、Capsule Collider
  胶囊体碰撞器。比较简单,没什么好说的。
  C、Rigidbody
  刚刚上面也说了,动态碰撞器的需要。
  关于A、B、C这几个组件上面就只简单介绍到这里,这里不展开讨论。如果有需要,我后面会再单独写文章介绍。
  下面开始看看控制代码部分。
  D、DonePlayerMovement
  这里有个“DonePlayerMovement(Script)”组件,从名字上看就知道应该是控制移动的(所以说命名很重要)。我们打开这个组件的源码,看看。
using UnityE&&
using System.C&&
&&
public class DonePlayerMovement : MonoBehaviour&&
{&&
& & public AudioClip shoutingC& && &// 玩家大喊的声音&&
& & public float turnSmoothing = 15f;& &// 用于玩家平滑转向的值&&
& & public float speedDampTime = 0.1f;&&// 用于控制从一个值变化到另一个的时间限制&&
& && &
& && &
& & private A& && && && && &
& & private DoneHashID& && && &&&// 保存各种动画状态的hash&&
& && &
& && &
& & void Awake ()&&
& & {&&
& && &&&anim = GetComponent&Animator&();&&
& && &&&hash = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent&DoneHashIDs&();&&
& && && &
& && &&&// Set the weight of the shouting layer to 1.&&
& && &&&anim.SetLayerWeight(1, 1f);&&
& & }&&
& && &
& && &
& & void FixedUpdate ()&&
& & {&&
& && &&&// Cache the inputs.&&
& && &&&float h = Input.GetAxis(&Horizontal&);&&
& && &&&float v = Input.GetAxis(&Vertical&);&&
& && &&&bool sneak = Input.GetButton(&Sneak&);&&
& && && &
& && &&&MovementManagement(h, v, sneak);&&
& & }&&
& && &
& && &
& & void Update ()&&
& & {&&
& && &&&// Cache the attention attracting input.&&
& && &&&bool shout = Input.GetButtonDown(&Attract&);&&
& && && &
& && &&&// Set the animator shouting parameter.&&
& && &&&anim.SetBool(hash.shoutingBool, shout);&&
& && && &
& && &&&AudioManagement(shout);&&
& & }&&
& && &
& && &
& & void MovementManagement (float horizontal, float vertical, bool sneaking)&&
& & {&&
& && &&&// Set the sneaking parameter to the sneak input.&&
& && &&&anim.SetBool(hash.sneakingBool, sneaking);&&
& && && &
& && &&&// If there is some axis input...&&
& && &&&if(horizontal != 0f || vertical != 0f)&&
& && &&&{&&
& && && && &// ... set the players rotation and set the speed parameter to 5.5f.&&
& && && && &Rotating(horizontal, vertical);&&
& && && && &anim.SetFloat(hash.speedFloat, 5.5f, speedDampTime, Time.deltaTime);&&
& && &&&}&&
& && &&&else&&
& && && && &// Otherwise set the speed parameter to 0.&&
& && && && &anim.SetFloat(hash.speedFloat, 0);&&
& & }&&
& && &
& && &
& & void Rotating (float horizontal, float vertical)&&
& & {&&
& && &&&// Create a new vector of the horizontal and vertical inputs.&&
& && &&&Vector3 targetDirection = new Vector3(horizontal, 0f, vertical);&&
& && && &
& && &&&// Create a rotation based on this new vector assuming that up is the global y axis.&&
& && &&&Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);&&
& && && &
& && &&&// Create a rotation that is an increment closer to the target rotation from the player's rotation.&&
& && &&&Quaternion newRotation = Quaternion.Lerp(rigidbody.rotation, targetRotation, turnSmoothing * Time.deltaTime);&&
& && && &
& && &&&// Change the players rotation to this new rotation.&&
& && &&&rigidbody.MoveRotation(newRotation);&&
& & }&&
& && &
& && &
& & void AudioManagement (bool shout)&&
& & {&&
& && &&&// If the player is currently in the run state...&&
& && &&&if(anim.GetCurrentAnimatorStateInfo(0).nameHash == hash.locomotionState)&&
& && &&&{&&
& && && && &// ... and if the footsteps are not playing...&&
& && && && &if(!audio.isPlaying)&&
& && && && && & // ... play them.&&
& && && && && & audio.Play();&&
& && &&&}&&
& && &&&else&&
& && && && &// Otherwise stop the footsteps.&&
& && && && &audio.Stop();&&
& && && &
& && &&&// If the shout input has been pressed...&&
& && &&&if(shout)&&
& && && && &// ... play the shouting clip where we are.&&
& && && && &AudioSource.PlayClipAtPoint(shoutingClip, transform.position);&&
& & }&&
}&&复制代码
  这个脚本整体来说:
  在Awake的时候,先找到脚本需要用到的Animator组件和DoneHashIds组件,然后缓存它们(在后面用到就不需要频繁查找了,节省CPU)。
  在FixedUpdate的时候,获取玩家按下的移动键,并处理移动、角色朝向问题。
  在Update的时候,获取玩家是否按下“Attract”键,并确定是否播放动画以及声音。
  OK,我们主要来看看MovementManagement这个方法:
void MovementManagement (float horizontal, float vertical, bool sneaking)&&
{&&
& & // Set the sneaking parameter to the sneak input.&&
& & anim.SetBool(hash.sneakingBool, sneaking);&&
& && &
& & // If there is some axis input...&&
& & if(horizontal != 0f || vertical != 0f)&&
& & {&&
& && &&&// ... set the players rotation and set the speed parameter to 5.5f.&&
& && &&&Rotating(horizontal, vertical);&&
& && &&&anim.SetFloat(hash.speedFloat, 5.5f, speedDampTime, Time.deltaTime);&&
& & }&&
& & else&&
& && &&&// Otherwise set the speed parameter to 0.&&
& && &&&anim.SetFloat(hash.speedFloat, 0);&&
}&&复制代码
  我们可以发现,除了Rotating方法,基本都是对anim做一些状态设置的方法。那么Rotating方法里面是什么呢?
void Rotating (float horizontal, float vertical)&&
{&&
& & // 创建一个Vector3用于保存输入的水平位移方向&&
& & Vector3 targetDirection = new Vector3(horizontal, 0f, vertical);&&
& && &
& & // 根据上面的方向计算这个方向指向的角度&&
& & Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);&&
& && &
& & // 创建一个从玩家当前方向旋转到目标方向的旋转增量&&
& & Quaternion newRotation = Quaternion.Lerp(rigidbody.rotation, targetRotation, turnSmoothing * Time.deltaTime);&&
& && &
& & // 修改玩家的方向&&
& & rigidbody.MoveRotation(newRotation);&&
}&&复制代码
  代码没有实现玩家的位置移动的代码,但是实际运行的时候却可以看到角色会移动。这是为什么?
  我们一起看到“Hierarchy”视图中的“char_ethan”。注意里面的Animator组件(它勾选了“Apply Root Motion”):
image008.png (13.92 KB, 下载次数: 2)
16:41 上传
  我们看到官方文档对于这个选项是这么介绍的:Root motion is the effect where an object's entire mesh moves away from its starting point but that motion is created by the animation itself rather than by changing the Transform position.
  这里大致的意思其实就是:这个选项会在由动画产生移动时,使得物体的mesh会偏离起点。那也就是物体的移动是做在动画中的,然后勾选Animator的ApplyRootMotion选项来实现的。
  关于角色控制中anim.SetFloat的方法的,这个涉及到动画混合,我们在这里暂时不讲。
  那么角色的移动、旋转就都讲了。接下来我们看看碰撞器。
  4、碰撞器及其使用
  前面我们说了碰撞器分静态和动态两种。
  那么角色“char_ethan”身上的肯定就是动态碰撞器了,注意到“char_ethan”对象有个“Capsule Collider”组件。这就是一个胶囊体碰撞器组件。然后角色还有个“rigidbody”组件,这个是刚体组件。也就是采用前面说的普通碰撞器+刚体组件。
  关于各种内置碰撞器的知识。大家不了解的可以看看官网(我不会告诉你我大部分U3D的知识都在官网学习的。)
  这里只要知道碰撞器一个重要的选项“Is Trigger”
image009.png (12.66 KB, 下载次数: 2)
16:41 上传
  只要两个相互碰撞的碰撞器,有任何一个勾选了IsTrigger,那么就会触发这两个碰撞器上面的OnTriggerEnter方法。如果两个相互碰撞的碰撞器,都没勾选IsTrigger,那么会触发这两个碰撞器上面的OnCollisionEnter,OnCollisionStay,OnCollisionExit方法。
  只有在不勾选IsTrigger的时候,才能使用Physics.Raycast进行射线检测。所以当你的碰撞方法没有调用的时候,请确保IsTrigger和方法是符合的。
  注意:这个游戏中玩家走路的时候不会穿墙,因为墙的碰撞器和玩家身上的碰撞器都没有勾选IsTrigger。
  5、怪物AI
  在“Hierarchy”视图中找到三个怪物“char_robotGuard_001”、“char_robotGuard_002”、“char_robotGuard_003”,这三个怪物上面的组件其实都是一样的。我们来看看“char_robotGuard_001”就可以了。看看怪物是怎么巡逻、然后发现敌人进行追踪的。
  怪物身上有如下组件:
image010.png (26.88 KB, 下载次数: 2)
16:41 上传
  A、Animator
  这里不再赘述。
  B、碰撞器
  两个碰撞器?为什么这里要用两个碰撞器?我们先看看两个碰撞器的属性:
image011.png (20.35 KB, 下载次数: 3)
16:41 上传
  一个是胶囊体,没有勾选IsTrigger。这个应该是配合走路,为了不让角色穿过建筑物的。
  第二个是球体碰撞器,勾选了IsTrigger。干嘛用的,别急,我们还是先来看那4个脚本吧。
  C、DoneEnemySight
  二话不说我们先上代码:
using UnityE&&
using System.C&&
&&
public class DoneEnemySight : MonoBehaviour&&
{&&
& & public float fieldOfViewAngle = 110f;& && && && && &// Number of degrees, centred on forward, for the enemy see.&&
& & public bool playerInS& && && && && && && && &&&// Whether or not the player is currently sighted.&&
& & public Vector3 personalLastS& && && && && & // Last place this enemy spotted the player.&&
& && &
& && &
& & private NavMeshA& && && && && && && && && &// Reference to the NavMeshAgent component.&&
& & private SphereC& && && && && && && && & // Reference to the sphere collider trigger component.&&
& & private A& && && && && && && && && && &// Reference to the Animator.&&
& & private DoneLastPlayerSighting lastPlayerS&&// Reference to last global sighting of the player.&&
& & private GameO& && && && && && && && &&&// Reference to the player.&&
& & private Animator playerA& && && && && && && && &// Reference to the player's animator component.&&
& & private DonePlayerHealth playerH& && && && &&&// Reference to the player's health script.&&
& & private DoneHashID& && && && && && && && && &// Reference to the HashIDs.&&
& & private Vector3 previousS& && && && && && & // Where the player was sighted last frame.&&
& && &
& && &
& & void Awake ()&&
& & {&&
& && &&&// Setting up the references.&&
& && &&&nav = GetComponent&NavMeshAgent&();&&
& && &&&col = GetComponent&SphereCollider&();&&
& && &&&anim = GetComponent&Animator&();&&
& && &&&lastPlayerSighting = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent&DoneLastPlayerSighting&();&&
& && &&&player = GameObject.FindGameObjectWithTag(DoneTags.player);&&
& && &&&playerAnim = player.GetComponent&Animator&();&&
& && &&&playerHealth = player.GetComponent&DonePlayerHealth&();&&
& && &&&hash = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent&DoneHashIDs&();&&
& && && &
& && &&&// Set the personal sighting and the previous sighting to the reset position.&&
& && &&&personalLastSighting = lastPlayerSighting.resetP&&
& && &&&previousSighting = lastPlayerSighting.resetP&&
& & }&&
& && &
& && &
& & void Update ()&&
& & {&&
& && &&&// If the last global sighting of the player has changed...&&
& && &&&if(lastPlayerSighting.position != previousSighting)&&
& && && && &// ... then update the personal sighting to be the same as the global sighting.&&
& && && && &personalLastSighting = lastPlayerSighting.&&
& && && &
& && &&&// Set the previous sighting to the be the sighting from this frame.&&
& && &&&previousSighting = lastPlayerSighting.&&
& && && &
& && &&&// If the player is alive...&&
& && &&&if(playerHealth.health & 0f)&&
& && && && &// ... set the animator parameter to whether the player is in sight or not.&&
& && && && &anim.SetBool(hash.playerInSightBool, playerInSight);&&
& && &&&else&&
& && && && &// ... set the animator parameter to false.&&
& && && && &anim.SetBool(hash.playerInSightBool, false);&&
& & }&&
& && &
&&
& & void OnTriggerStay (Collider other)&&
& & {&&
& && &&&// If the player has entered the trigger sphere...&&
& && &&&if(other.gameObject == player)&&
& && &&&{&&
& && && && &// By default the player is not in sight.&&
& && && && &playerInSight =&&
& && && && &&&
& && && && &// Create a vector from the enemy to the player and store the angle between it and forward.&&
& && && && &Vector3 direction = other.transform.position - transform.&&
& && && && &float angle = Vector3.Angle(direction, transform.forward);&&
& && && && &&&
& && && && &// If the angle between forward and where the player is, is less than half the angle of view...&&
& && && && &if(angle & fieldOfViewAngle * 0.5f)&&
& && && && &{&&
& && && && && & RaycastH&&
& && && && && && &
& && && && && & // ... and if a raycast towards the player hits something...&&
& && && && && & if(Physics.Raycast(transform.position + transform.up, direction.normalized, out hit, col.radius))&&
& && && && && & {&&
& && && && && && &&&// ... and if the raycast hits the player...&&
& && && && && && &&&if(hit.collider.gameObject == player)&&
& && && && && && &&&{&&
& && && && && && && && &// ... the player is in sight.&&
& && && && && && && && &playerInSight =&&
& && && && && && && && &&&
& && && && && && && && &// Set the last global sighting is the players current position.&&
& && && && && && && && &lastPlayerSighting.position = player.transform.&&
& && && && && && &&&}&&
& && && && && & }&&
& && && && &}&&
& && && && &&&
& && && && &// Store the name hashes of the current states.&&
& && && && &int playerLayerZeroStateHash = playerAnim.GetCurrentAnimatorStateInfo(0).nameH&&
& && && && &int playerLayerOneStateHash = playerAnim.GetCurrentAnimatorStateInfo(1).nameH&&
& && && && &&&
& && && && &// If the player is running or is attracting attention...&&
& && && && &if(playerLayerZeroStateHash == hash.locomotionState || playerLayerOneStateHash == hash.shoutState)&&
& && && && &{&&
& && && && && & // ... and if the player is within hearing range...&&
& && && && && & if(CalculatePathLength(player.transform.position) &= col.radius)&&
& && && && && && &&&// ... set the last personal sighting of the player to the player's current position.&&
& && && && && && &&&personalLastSighting = player.transform.&&
& && && && &}&&
& && &&&}&&
& & }&&
& && &
& && &
& & void OnTriggerExit (Collider other)&&
& & {&&
& && &&&// If the player leaves the trigger zone...&&
& && &&&if(other.gameObject == player)&&
& && && && &// ... the player is not in sight.&&
& && && && &playerInSight =&&
& & }&&
& && &
& && &
& & float CalculatePathLength (Vector3 targetPosition)&&
& & {&&
& && &&&// Create a path and set it based on a target position.&&
& && &&&NavMeshPath path = new NavMeshPath();&&
& && &&&if(nav.enabled)&&
& && && && &nav.CalculatePath(targetPosition, path);&&
& && && &
& && &&&// Create an array of points which is the length of the number of corners in the path + 2.&&
& && &&&Vector3 [] allWayPoints = new Vector3[path.corners.Length + 2];&&
& && && &
& && &&&// The first point is the enemy's position.&&
& && &&&allWayPoints[0] = transform.&&
& && && &
& && &&&// The last point is the target position.&&
& && &&&allWayPoints[allWayPoints.Length - 1] = targetP&&
& && && &
& && &&&// The points inbetween are the corners of the path.&&
& && &&&for(int i = 0; i & path.corners.L i++)&&
& && &&&{&&
& && && && &allWayPoints[i + 1] = path.corners[i];&&
& && &&&}&&
& && && &
& && &&&// Create a float to store the path length that is by default 0.&&
& && &&&float pathLength = 0;&&
& && && &
& && &&&// Increment the path length by an amount equal to the distance between each waypoint and the next.&&
& && &&&for(int i = 0; i & allWayPoints.Length - 1; i++)&&
& && &&&{&&
& && && && &pathLength += Vector3.Distance(allWayPoints[i], allWayPoints[i + 1]);&&
& && &&&}&&
& && && &
& && &&&return pathL&&
& & }&&
}&&复制代码
Awake主要是获取一些组件,在后面的逻辑中可以使用。Update检查玩家最后一次被发现的位置是否一致,不是则同步位置。
把playerInSight(是否发现玩家)这个状态设置到Animator的状态机里面。OnTriggerStay没错,前面说到的第二个碰撞器正是这里要用到的。第二个碰撞器勾选了IsTrigger,就是为了触发这个方法。
当玩家进入这个碰撞器(也就是怪物的视野)触发。
检查玩家是否在怪物前方,而且玩家和怪物中间没有遮挡物。那么设置playerInSight为true。OnTriggerExit当玩家离开怪物的视野(碰撞器)。那么设置playerInSight为false。CalculatePathLength计算怪物和玩家当前的距离。
  基本这个组件就是用于检测玩家是否在怪物视野内。然后设置玩家的一些位置信息,以及是否被发现的信息。
  我们接着看DoneEnemyAI这个组件
  D、DoneEnemyAI
  还是上代码:
using UnityE&&
using System.C&&
&&
public class DoneEnemyAI : MonoBehaviour&&
{&&
& & public float patrolSpeed = 2f;& && && && && && && && &&&// The nav mesh agent's speed when patrolling.&&
& & public float chaseSpeed = 5f;& && && && && && && && && &// The nav mesh agent's speed when chasing.&&
& & public float chaseWaitTime = 5f;& && && && && && && && &// The amount of time to wait when the last sighting is reached.&&
& & public float patrolWaitTime = 1f;& && && && && && && &&&// The amount of time to wait when the patrol way point is reached.&&
& & public Transform[] patrolWayP& && && && && && && &// An array of transforms for the patrol route.&&
& && &
& && &
& & private DoneEnemySight enemyS& && && && && && && & // Reference to the EnemySight script.&&
& & private NavMeshA& && && && && && && && && && & // Reference to the nav mesh agent.&&
& & private T& && && && && && && && && && & // Reference to the player's transform.&&
& & private DonePlayerHealth playerH& && && && && && &// Reference to the PlayerHealth script.&&
& & private DoneLastPlayerSighting lastPlayerS& && &// Reference to the last global sighting of the player.&&
& & private float chaseT& && && && && && && && && && & // A timer for the chaseWaitTime.&&
& & private float patrolT& && && && && && && && && && &// A timer for the patrolWaitTime.&&
& & private int wayPointI& && && && && && && && && && &// A counter for the way point array.&&
& && &
& && &
& & void Awake ()&&
& & {&&
& && &&&// Setting up the references.&&
& && &&&enemySight = GetComponent&DoneEnemySight&();&&
& && &&&nav = GetComponent&NavMeshAgent&();&&
& && &&&player = GameObject.FindGameObjectWithTag(DoneTags.player).&&
& && &&&playerHealth = player.GetComponent&DonePlayerHealth&();&&
& && &&&lastPlayerSighting = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent&DoneLastPlayerSighting&();&&
& & }&&
& && &
& && &
& & void Update ()&&
& & {&&
& && &&&// If the player is in sight and is alive...&&
& && &&&if(enemySight.playerInSight && playerHealth.health & 0f)&&
& && && && &// ... shoot.&&
& && && && &Shooting();&&
& && && &
& && &&&// If the player has been sighted and isn't dead...&&
& && &&&else if(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health & 0f)&&
& && && && &// ... chase.&&
& && && && &Chasing();&&
& && && &
& && &&&// Otherwise...&&
& && &&&else&&
& && && && &// ... patrol.&&
& && && && &Patrolling();&&
& & }&&
& && &
& && &
& & void Shooting ()&&
& & {&&
& && &&&// Stop the enemy where it is.&&
& && &&&nav.Stop();&&
& & }&&
& && &
& && &
& & void Chasing ()&&
& & {&&
& && &&&// Create a vector from the enemy to the last sighting of the player.&&
& && &&&Vector3 sightingDeltaPos = enemySight.personalLastSighting - transform.&&
& && && &
& && &&&// If the the last personal sighting of the player is not close...&&
& && &&&if(sightingDeltaPos.sqrMagnitude & 4f)&&
& && && && &// ... set the destination for the NavMeshAgent to the last personal sighting of the player.&&
& && && && &nav.destination = enemySight.personalLastS&&
& && && &
& && &&&// Set the appropriate speed for the NavMeshAgent.&&
& && &&&nav.speed = chaseS&&
& && && &
& && &&&// If near the last personal sighting...&&
& && &&&if(nav.remainingDistance & nav.stoppingDistance)&&
& && &&&{&&
& && && && &// ... increment the timer.&&
& && && && &chaseTimer += Time.deltaT&&
& && && && &&&
& && && && &// If the timer exceeds the wait time...&&
& && && && &if(chaseTimer &= chaseWaitTime)&&
& && && && &{&&
& && && && && & // ... reset last global sighting, the last personal sighting and the timer.&&
& && && && && & lastPlayerSighting.position = lastPlayerSighting.resetP&&
& && && && && & enemySight.personalLastSighting = lastPlayerSighting.resetP&&
& && && && && & chaseTimer = 0f;&&
& && && && &}&&
& && &&&}&&
& && &&&else&&
& && && && &// If not near the last sighting personal sighting of the player, reset the timer.&&
& && && && &chaseTimer = 0f;&&
& & }&&
&&
& && &
& & void Patrolling ()&&
& & {&&
& && &&&// Set an appropriate speed for the NavMeshAgent.&&
& && &&&nav.speed = patrolS&&
& && && &
& && &&&// If near the next waypoint or there is no destination...&&
& && &&&if(nav.destination == lastPlayerSighting.resetPosition || nav.remainingDistance & nav.stoppingDistance)&&
& && &&&{&&
& && && && &// ... increment the timer.&&
& && && && &patrolTimer += Time.deltaT&&
& && && && &&&
& && && && &// If the timer exceeds the wait time...&&
& && && && &if(patrolTimer &= patrolWaitTime)&&
& && && && &{&&
& && && && && & // ... increment the wayPointIndex.&&
& && && && && & if(wayPointIndex == patrolWayPoints.Length - 1)&&
& && && && && && &&&wayPointIndex = 0;&&
& && && && && & else&&
& && && && && && &&&wayPointIndex++;&&
& && && && && && &
& && && && && & // Reset the timer.&&
& && && && && & patrolTimer = 0;&&
& && && && &}&&
& && &&&}&&
& && &&&else&&
& && && && &// If not near a destination, reset the timer.&&
& && && && &patrolTimer = 0;&&
& && && &
& && &&&// Set the destination to the patrolWayPoint.&&
& && &&&nav.destination = patrolWayPoints[wayPointIndex].&&
& & }&&
}&&复制代码
  这个组件主要是:如果判断玩家在视野中,则对玩家进行射击。如果玩家最后一次被发现的点不是默认点,则进行追击。否则就进行巡逻。
  追击、巡逻,主要都是对NavMeshAgent进行设置来实现控制怪物的移动。关于NavMeshAgent我们以后再专门来讲解。
  而射击调用的Shooting方法里面只有一行代码:nav.Stop。那么射击具体在哪里实现的?接着看DoneEnemyShooting组件。
  E、DoneEnemyShooting
  看代码:
using UnityE&&
using System.C&&
&&
public class DoneEnemyShooting : MonoBehaviour&&
{&&
& & public float maximumDamage = 120f;& && && && && && &// The maximum potential damage per shot.&&
& & public float minimumDamage = 45f;& && && && && && & // The minimum potential damage per shot.&&
& & public AudioClip shotC& && && && && && && && &&&// An audio clip to play when a shot happens.&&
& & public float flashIntensity = 3f;& && && && && && & // The intensity of the light when the shot happens.&&
& & public float fadeSpeed = 10f;& && && && && && && &&&// How fast the light will fade after the shot.&&
& && &
& && &
& & private A& && && && && && && && && && &// Reference to the animator.&&
& & private DoneHashID& && && && && && && && && &// Reference to the HashIDs script.&&
& & private LineRenderer laserShotL& && && && && &&&// Reference to the laser shot line renderer.&&
& & private Light laserShotL& && && && && && && &&&// Reference to the laser shot light.&&
& & private SphereC& && && && && && && && & // Reference to the sphere collider.&&
& & private T& && && && && && && && && &// Reference to the player's transform.&&
& & private DonePlayerHealth playerH& && && && &&&// Reference to the player's health.&&
& & p& && && && && && && && && && &// A bool to say whether or not the enemy is currently shooting.&&
& & private float scaledD& && && && && && && && & // Amount of damage that is scaled by the distance from the player.&&
& && &
& && &
& & void Awake ()&&
& & {&&
& && &&&// Setting up the references.&&
& && &&&anim = GetComponent&Animator&();&&
& && &&&laserShotLine = GetComponentInChildren&LineRenderer&();&&
& && &&&laserShotLight = laserShotLine.gameObject.&&
& && &&&col = GetComponent&SphereCollider&();&&
& && &&&player = GameObject.FindGameObjectWithTag(DoneTags.player).&&
& && &&&playerHealth = player.gameObject.GetComponent&DonePlayerHealth&();&&
& && &&&hash = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent&DoneHashIDs&();&&
& && && &
& && &&&// The line renderer and light are off to start.&&
& && &&&laserShotLine.enabled =&&
& && &&&laserShotLight.intensity = 0f;&&
& && && &
& && &&&// The scaledDamage is the difference between the maximum and the minimum damage.&&
& && &&&scaledDamage = maximumDamage - minimumD&&
& & }&&
& && &
& && &
& & void Update ()&&
& & {&&
& && &&&// Cache the current value of the shot curve.&&
& && &&&float shot = anim.GetFloat(hash.shotFloat);&&
& && && &
& && &&&// If the shot curve is peaking and the enemy is not currently shooting...&&
& && &&&if(shot & 0.5f && !shooting)&&
& && && && &// ... shoot&&
& && && && &Shoot();&&
& && && &
& && &&&// If the shot curve is no longer peaking...&&
& && &&&if(shot & 0.5f)&&
& && &&&{&&
& && && && &// ... the enemy is no longer shooting and disable the line renderer.&&
& && && && &shooting =&&
& && && && &laserShotLine.enabled =&&
& && &&&}&&
& && && &
& && &&&// Fade the light out.&&
& && &&&laserShotLight.intensity = Mathf.Lerp(laserShotLight.intensity, 0f, fadeSpeed * Time.deltaTime);&&
& & }&&
& && &
& && &
& & void OnAnimatorIK (int layerIndex)&&
& & {&&
& && &&&// Cache the current value of the AimWeight curve.&&
& && &&&float aimWeight = anim.GetFloat(hash.aimWeightFloat);&&
& && && &
& && &&&// Set the IK position of the right hand to the player's centre.&&
& && &&&anim.SetIKPosition(AvatarIKGoal.RightHand, player.position + Vector3.up * 1.5f);&&
& && && &
& && &&&// Set the weight of the IK compared to animation to that of the curve.&&
& && &&&anim.SetIKPositionWeight(AvatarIKGoal.RightHand, aimWeight);&&
& & }&&
& && &
& && &
& & void Shoot ()&&
& & {&&
& && &&&// The enemy is shooting.&&
& && &&&shooting =&&
& && && &
& && &&&// The fractional distance from the player, 1 is next to the player, 0 is the player is at the extent of the sphere collider.&&
& && &&&float fractionalDistance = (col.radius - Vector3.Distance(transform.position, player.position)) / col.&&
& && &
& && &&&// The damage is the scaled damage, scaled by the fractional distance, plus the minimum damage.&&
& && &&&float damage = scaledDamage * fractionalDistance + minimumD&&
&&
& && &&&// The player takes damage.&&
& && &&&playerHealth.TakeDamage(damage);&&
& && && &
& && &&&// Display the shot effects.&&
& && &&&ShotEffects();&&
& & }&&
& && &
& && &
& & void ShotEffects ()&&
& & {&&
& && &&&// Set the initial position of the line renderer to the position of the muzzle.&&
& && &&&laserShotLine.SetPosition(0, laserShotLine.transform.position);&&
& && && &
& && &&&// Set the end position of the player's centre of mass.&&
& && &&&laserShotLine.SetPosition(1, player.position + Vector3.up * 1.5f);&&
& && && &
& && &&&// Turn on the line renderer.&&
& && &&&laserShotLine.enabled =&&
& && && &
& && &&&// Make the light flash.&&
& && &&&laserShotLight.intensity = flashI&&
& && && &
& && &&&// Play the gun shot clip at the position of the muzzle flare.&&
& && &&&AudioSource.PlayClipAtPoint(shotClip, laserShotLight.transform.position);&&
& & }&&
}&&复制代码
  Update的时候可以发现它检查动画的shotFloat属性。然后判断是否应该射击或取消射击。而射击的激光,这里可以看到是采用LineRenderer实现的。
  6、事件通知
  以上关于怪物的AI我们就讲完了。但这只是单个怪物的AI。实际这个游戏里面还有用于监控玩家的摄像头、还有红外线墙检测玩家是否通过。但是原理都一样。都是通过监听碰撞器的OnTriggerStay来设置玩家被发现的全局位置。也就是DoneLastPlayerSighting。
  个人关于这个demo代码设计的看法:
  我个人觉得这种做法不好。大家都自己去获取DoneLastPlayerSighting,然后自己检查里面的状态。然后还可以设置里面的状态。这样会导致程序变量跟踪困难。
  如果是我的话,我会把DoneLastPlayerSighting做成是一个消息中心。然后每个敌人订阅这个消息中心的消息(即玩家被发现的位置发生变化的消息),然后由消息中心通知所有订阅的对象。然后订阅的对象也可以提交自己发现的目标给消息中心,如果消息中心确认通过,再通知已经订阅的其它对象。这样做起来代码会更清晰,更容易维护。
  7、渲染特效
  A、雾效
  你可以通过点击Unity的菜单栏“Edit”-“Render Settings”。这时候在右边的Inspector面板可以通过勾选“fog”选项来开启雾效。“Fog Color”可以调整雾的颜色。
  B、游戏中监视器投射到地上的亮点
  在“Hiererchy”中找到监视器物体“prop_cctvCam_001”,你会发现它下面有个子物体“cam_frustum_collision”,它上面有个Light组件,Light上的Cookie属性就是产生亮点的贴图。
  C、激光墙的激光
  其实就是用了一个&Self-Illumin/Diffuse&的shader,这是一个自发光shader,你可以尝试一下,在相同的灯光条件下,采用自发光shader的物体总会比Diffuse的亮。
关注我们官方微信公众号
下载我们官方APP-游戏行
关注手游动态微信公众号
从LOLS7赛季中国队淘汰浅谈对国内电竞职业这款运营了11年的游戏,现在每天仍有超过4010月30日—11月5日共有39款游戏开测|GameR网易发布对“大逃杀”类游戏指导意见的执行电子竞技不需要“死亡宣告”写给策划看的最简版UE入门
微信扫一扫关注我们→

我要回帖

更多关于 unity3d弹出提示框 的文章

 

随机推荐