light 光 光源类型及属性介绍 8
shader 阴影 8
light 性能 9
camera组件 10
正交摄像机 11
透视摄像机 12
摄像机空间 13
渲染到纹理14
摄像机的性能优化 15
地形terrain 16
粒子系统 19
程序中控制粒子系统20
屏幕特性 22
Mesh:推荐转化成fbx格式导入unity 使用
mesh主要信息:
mesh renderer
bones 换装
物理系统 Physics
碰撞的基本原理
产生碰撞的必要因
常见包围盒collider
unity的包围盒collider
碰撞检测体
character collider人物角色控制器,
碰撞检测体分类:
Trigger:触发器,Collider的trigger属性勾选上就成为一个无阻挡碰撞,也叫触发器
Physics rayCast : 射线检测,无限长
physics 物理系统的性能
控制角色在真实环境中行走
音频
视频
录音
动画Animation 40
Animator 42
light 光
1.光源类型 7
directional light 直线光,无位置,有方向,无衰减,多用于做太阳光
point light:点光,有位置,有方向,有衰减,球形扩散, 如灯泡
spot light 聚光灯,有位置,有方向,衰减方向是沿着椎体的
2.ligh的属性
color, Intensity 强度 shadow type 是否产生阴影
Culling Mask 光照可以照亮的层,不在这里的层所对应的物体无光照效果
Mode (Baking ) 光源类型
Realtime 实时光源
baked 烘焙光源,意思是烘焙是起效,在运行时只是一张烘焙出的贴图,
一个untiy项目一般只有 一盏实时光源,其他都是烘焙光源
shadow type 阴影类型 hard shadows(硬件光照,阴影边缘有锯齿) soft shadows(软件光源,阴影边缘已经过模糊处理,是光滑,开销很大,不建议是移动端使用)
Render Mode 渲染模型
Important 像素光,(计算每一个像素的光照,开销很大,)
NotImportant 顶点光。(只计算每一个顶点的光照,相对像素光,计算强度小很多)
渲染模型是用模型每一个像素的法线和光的方向做一个几何运算来决定点的颜色
Culling Mask 裁剪 ,列表中勾选所有需要光照的layer,不勾选的layer 没有光照
离线计算:场景的光照属性存储到一张图上,运行时贴上图即可,是一种烘焙技术
实时计算: 实时计算的光照,烘焙是不起效果
---------------------------
阴影
产生的因素
1,添加产生阴影的游戏物体。并勾选其meshRenderer 的cast shadows 为on,表示会产生阴影
2.光源,如directional light
3. 光源设置属性shadow type 为hard shadows 或者soft shadows
4.选择接受阴影的物体,mesh renderer的receive shadows勾选
5.project setting ->quality 的shadow distance 在距离以后就不在产生阴影
6.project setting ->quality 的shadow 阴影类型的总开关,必选选择为hard shadows 或者soft shadows
7.project setting ->quality 的shadow Cascades 选择的值越大,表示阴影渲染需要的内存越大
阴影实现原理: 在unity中采用shadermap的方式,所有的阴影计算是依赖于一张贴图,就是把所有的阴影渲染到一张贴图,但是近处的阴影的清晰度要求比远处阴影的清晰度,就是近处阴影要使用高像素贴图,
这是就使用到参数shadow cascades
-----------------
light 性能,如何提升light 性能
1.尽量少使用实时光照(directional light),最多一盏太阳光
2.利用好用离线烘焙,不占draw call 是把光照的阴影存放在一张贴图上。就是光照在美术阶段制作到物体上。
3. 尽量不用spotlight ,因为聚光灯开销很大
4.实时光照,尽量只使用顶点光,就是rendermode 选择NotImportant。
5.实时光照,控制像素的光的数量,在project setting ->quality 的 pixel light count 设置为1或者0.
6.谨慎使用实时阴影,必须使用时shadow type 选择 hard shaoows
7.减少影子距离project setting ->quality 的shaow distance
8.利用好culling mask ,不需要的层,不要勾选,不勾选的层,不会进行光照计算,
9.降低shadow cascades ,在project setting ->quality 的shadow Cascades,尽量选择低一点的值
4.游戏中大多数物体的光照情况是固定的,所以需充分利用unity的烘焙技术,就其做成烘焙光,即baked光照。
-----------------
camera组件
属性clear flags 图形渲染之前需要清除上一次渲染的结果,clear flag就是清除过程的设置
1.skybox 清除后,使用skybox 作为新的渲染的背景
2.solid color 清除后,使用一个颜色作为新的渲染的背景
3. Depth only。屏幕上的像素存储数据包括深度和颜色,depth意思就是清除像素的深度,保留像素的颜色,多用于非主相机,
效果就是叠加新的渲染到主相机上
4.Donit clear 不清除
属性culling mask 同shadow的culling mask ,不在列表内的layer不会渲染到当前相机
属性Depth
属性Rendering path 渲染途径,用默认值就可
属性occlusion culling 是否开启占据剪裁,先对空间预先计算,学名遮挡剔除,意思是完全可不到的东西可以不渲染。在移动平台上效率很低,不建议开启
属性HDR是否使用hDR效果,用于屏幕特效
---------------------
正交摄像机
camera 的属性Projection 选择Orthographic camera是一个正交摄像机, 正交摄像机时2d的。平面正交的投影,用于2d游戏,无进大远小的效果
属性Size 可以看见的视口的大小
属性Cliping planes near 最近可视的物体距离 far 最远可视的物体距离
属性viewport rect 相机渲染的画面在屏幕中所处的位置,w为0.5就表示只渲染一半
透视摄像机
camera 的属性Projection 选择Perspective是一个透视摄像机。反应的是正常的3d世界
属性Field of View 透视摄像机的视椎体的大小。默认值是60
---------------------------------------------------------------------
camera 的摄像机空间
摄像机空间 定义:已摄像机为原点的坐标空间
范围: x y 均为0~1 z为物体距离摄像机的z项距离
获取: camera.worldToViewportPoint(获取物体在屏幕空间中的坐标)
作用: 得到的值,如果x y 不在0~1之间说明物体是不可见的,可以不渲染,从而节约内存空间
获取物体在摄像机的屏幕位置 camera。worldToScreenPoint
世界坐标到摄像机空间的转化矩阵 camera.worldtoCameraMatrix worldtoCameraMatrix *世界坐标的值 = 摄像机空间的值 等价于worldToViewportPoint
-------------------------
渲染到纹理:
概念: 把摄像机看到的东西渲染到一张贴图上,然后把贴图贴到某一个物体上,也就是某个物体上显示的问题是动态的,是绑定了某个摄像机的渲染纹理
做法: 摄像机camera 添加render texture 作为他的target texture 。然后把render texture 贴到某一个物体上即可
camere 渲染到纹理上,就不能再渲染到屏幕上,就是说camera 添加了render texture 后 它就无法在屏幕上显示,所以 添加render texture 不能做主相机
作用: 赛车游戏,后视镜看到的情景, 小地图
有一定开销,绘制的工作量会加大,渲染效率会受影响,所以不建议在移动平台使用
--------------------------------------
摄像机的性能优化
1.摄像机的视锥角 filedofview 越小越好
2.注意远裁剪面,缩小摄像机的可视范围,就是说cliping planes 的far 值要小一点,远景可以使用贴图表示,也可以使用雾等效果
3.在移动平台尽量确保可视的顶点数不要大约10万,在game --- stats 的verts
4. 移动平台尽量不要用渲染到纹理 render texture
5.移动平台尽量不要使用occlication culling 遮挡剔除
6.摄像机空间的剪裁。不在视角范围内可以停止运行
7.利用culling mask 不需要渲染的layer 不要勾线
8.camera.layercullDistances 对需要的layer按距离进行裁剪
------------------------
地形terrain
与模型做地表的区别:
可以以直接拉出地形,山谷,与模型搭建地表的区别是,更适合比较随机的,开销小,动态生成的,可以unity中随时编辑,支持纹理绘制,适用于大的野外场景。但不适合移动设备
存储的东西:1)把所有高度信息转化为256的黑白贴图。 2)每一个点上被画了那些贴图,贴图存储的是alpha 信息和颜色信息,
地形相关接口
获取地形Terragin:GetComponent<Terrain>();
获取当前地形的某一个位置的高度值 terrain.SampleHeight(vector3)
TerrainData td = terrain.terrainData 包括地形的高度图信息,绘制地形是的地表细节图Textures 在某一点的alpha 值
td.getHeight(250,250) terrain的width height均在terrain的setting中设置
-----------------------
粒子系统 pairtical system 只能stop 不能删除
partical system 发射器
属性:
duration 从发射一次粒子完到下一次发射的时间间隔。发射一次粒子的时间间隔
start lifttime: 一个粒子从发出到消失的时长
start delay 延迟发射时间
start speed
start size 开始的大小
start color
start rotation 开始的旋转
Gravity Velocity: 开始的重力加速度
Simulation Space 发生的空间 local 局部,移动发射器,已发射的粒子也移动
World 世界空间,移动发射器,已发射的粒子不会跟着移动,因为粒子的位置是世界坐标
play on Awake awake时就开始发射
Max particles 最大粒子数
----------------
Emassion 粒子喷射的另一种方式,就是第几秒喷射几颗, 是时间跟粒子个数的列表
Rate over Time 每秒发射的个位
Bursts 每秒发射增加的个数
--Shape 粒子发射区域的形状
Velocity over lifetime 随着粒子的生命时间变化粒子的加速度
Collision 粒子发射出来是否碰撞。性能消耗超大
sub Emitters 粒子发出来会产生新的发射器,效果比如烟花等
Texture sheet Anination 粒子的序列帧动画,可以给粒子给定贴图
Renderer粒子额渲染器类型
Render Mode 值 billboard:永远朝向自己。无论相机如何选择,看到的永远是粒子的正面
mesh 喷出的粒子是mesh
sorting Fudge 透明材质的渲染先后顺序。 正数,表示渲染是会在靠上的层
cast shadows 是否产生阴影,性能消耗很大,选off
Receice shadows 是否接受阴影,性能消耗很大,选off
--------
粒子系统操作代码
ParticalSystem ps = GetComponent<ParticalSystem>();
播放 ps.play();
停止播放 ps.stop()
是否停止播放:ps.isPlaying()
获取render ParticalSystemRenderer render = GetComponent<ParticalSystemRenderer>();
贴图的宽度 render.sharedMaterial.mainTexture.width
粒子系统在手机上的性能
1.很影响手机性能。表现在cpu很耗
2.全屏发射器数量<30
3.全屏粒子数量<200
4.不在粒子中发射模型。就是render mode 不能选择mesh
5.谨慎使用sub emitters 子发射器
6.关掉粒子系统阴影(render中cast shadows和receive shadows)和碰撞(colllition)
7.粒子系统的内部是可以合批。draw Call一般是1,2,3
-------------------------
屏幕特性:
存在于屏幕上的特性,是针对渲染到屏幕上的最后一帧的图像单独在图像范围内所做的屏幕上的特效
说白了就是渲染后屏幕上的图像在图形范围能的特效,比如变红。变模糊等
实现原理:把当前渲染的图像先渲染到纹理,就等于这一张图像先渲染到一张贴图上,然后拿到贴图,对贴图进行图像空间的处理,最后把处理后的图像给帧缓存并显示到屏幕上
所以添加屏幕特效,比没有屏幕特性要多一些渲染的操作,不如多一次绘制
在移动平台不建议使用
内置的屏幕特效 挂载在camera 组件下,
组件报名 Image Effect Post Processing
HDR 在unity中组件 Image Effect -- color Adjustments --- Tonemapping 来实现 将过亮或者过暗的细节在贴图表示出来,做法是压缩不主要的颜色,
Antialiasing 反走样 图像的边角会变得更光滑,消灭图片的锯齿
Blur 模糊, 高斯模糊
Bloom 在室内看到一束光从窗户外射进来,光的边缘会柔柔的,其他东西会比较效果,暖光的霓虹灯散射的样子 强烈的光在空气中散射的效果
Glow 与bloom类似。是一种更强烈光的散射,更加强烈的自发光 加 自发光
SSAO (Screen Space Ambient Occlusion)临近的连个面的交接出会产生一定的阴影。屏幕空间的环境遮挡,会在交接面产生阴影,物体更真实。
视觉系统的特定:51号公路
基础渲染管线
shader
matrial:
shader的写法
顶点 mesh triangles normals
顶点数据如何构成三角形?索引
----------------------------------------------------------
视觉系统的特定:51号公路
1.阴影和光照会让物体具有立体感
2.平行线汇集成一个点
3.物体的大小随着距离增加而减少。越远的物体看起来越小
4.物体会有重叠,但人的视觉无法穿透
-----------------------
基础渲染管线
1.索引顺序不同,构成的三角形不同
2。DX默认顺时针为正向, OPENGL默认逆时针为正向
3.背面剔除需要用到索引顺序
4.索引可以减少内存
5.索引是简单正数,低端设备只能支持65535个索引数据
--------------------
shader:GPU上绘制(渲染)程序,又名渲染器,重要流程是渲染管线(固定的大规模的渲染)
主流渲染图形软件接口:openGL,等
----------------
matrial:使用shader的包装,是对各种不同shader+不同的参数+不同的图片,等效果呈现
获取:
getComponent<Renderen>().material 是获取这个组件的material。是对预制体的material的copy
getComponent<Renderen>().sharedMaterial是预制体的material。是共享的资源
同样的东西还有mesh 。sharedmesh
-------------------------------------
shader的写法,
写法1: surface shader:unity提供一种简化的支持光照的shader,固定了光照算法,只需要配置相应的参数即可
写法2: vertex & frag shader 自己控制整个渲染工程
写法3: fixed pipeline 固定管线
shader中多个subshader 就是多个渲染渠道,哪一个符合当前显卡就用那个,如果都不行就用最后面的FallBack
-----------------------------------------
顶点
MeshFilter mf = this.getComponent<MeshFilter>();
Mesh rf = mf.sharedMesh;
rf.vertices;
rf.normal
rf.uv
-------------------------
unity 顶点vertices, uv 与图片贴图
mesh 就是组成3d物体的三角形们。这些三角形有顶点组成,三角形大小由顶点位置决定
贴图的原点在左下角,uv是贴图的坐标,是贴图和顶点的对应关系
triangles是mesh网格的三角形索引数组,一个三角形有三个点,
normals是mesh网格的法线数组,每一个顶点都有一个法线
顶点:
包含的数据有:空间坐标,法线,颜色,纹理坐标及其他自定义信息
顶点,法线, uv的数据是一一对应的。
将顶点数据存入顶点缓存区的容器数组中,然后GPU从顶点缓冲区中获取数据
使用过程
1.创建缓冲区
2.绑定顶点数据
3.上传顶点数据
顶点数据如何构成三角形?
索引:用于标记三角形,一个顶点一个索引,相当于给顶点编号,三个索引构成一个三角型
索引个数/3 = 模型三角形个数(面数)
索引数据的缓冲区叫索引缓冲区
索引注意事项:
1.索引的顺序不同,构成的三角形不同,CPU可以跟索引顺序确保不渲染三角形的背面
2.DX(DorectX)是unity的图形函数库,默认顺时针为正向。OpenGL默认逆时针为正向
3.背面剔除需要用到索引顺序
4.所以可以减少内存
5.索引是简单整数
--------------------------
顶点着色:
屏幕特效:
原理:对最后渲染到屏幕上的一帧图片,单纯的在图片的范围内所做的屏幕上的特效。
Mesh:推荐转化成fbx格式导入unity 使用
mesh主要信息:
1.顶点和索引
2.顶点数 面数
顶点元素信息组成:位置, 颜色 法线切线 uv1,uv2
影响顶点数量的元素: 法线normals 光滑组 Smoothingangle
mesh Filter 用于存储mesh数据
mesh Renderer: 用于渲染mesh
mesh renderer
可以获取的信息
bounds:包围盒
sharedMesh : 是使用同一个mesh数据的gameobject的公共mesh,修改后会影响所有的mesh 同时会在编译状态上修改硬盘上的这个mesh
mesh 是sharedmesh的一个copy。只会影响当前物体
同理sharedMaterial 和material也是如此
skinned Mesh Render 有蒙皮的mesh:是把一个蒙皮加动画赋值给一个骨骼,就让骨骼动起来
bones 换装
方式1:模型整体换装
方式2:模型挂接(游戏人物换武器)
方式3: 贴图置换(换衣服)
方式4: 基于相同的骨骼的模型替换(人的变身)
物理系统 Physics
碰撞的基本原理:
创景管理器:就是把场景划分成无数个八叉树的区域,碰撞检测是只用检测物体所在的子区域的物体。这样就可以让效率比较低
碰撞检测本质:就是检测模型a上的所有线 和模型b的所有线是否想交,所有碰撞检测就是检测线段的想交查询
碰撞检测简化: 给gameobject 添加一个包围盒Collider,然后检测包围盒之间是否想交,想交就会产生碰撞
产生碰撞的必要因素:
1.碰撞检测体rigidboby
2.包围盒区域collider
常见包围盒collider
1.球Sphere
2.AABB 轴对称的立方体盒子。包围盒不能旋转
3.OBB 包围盒可以旋转,是AABB的升级版
4.KDOP: 比OBB还精细的包围盒,unity不支持
unity的包围盒collider
1.sphereCollider 圆形
2.box Collider
3.Capsule Collider 胶囊体,拉伸后用于给人添加碰撞体
4.Character Controller 角色控制器,类似于capsule collider的形状,自身是一个碰撞检测器
5.Mesh Collider 比较复杂,效率低下
碰撞检测体
1 刚体 Rigid body:物体的形状不会影响物体的物理属性,在运行到包围盒区域内会产生碰撞
rigid body 有重力 gravity
character collider人物角色控制器,
即是包围盒collider 也是碰撞检测体,它的包围盒是capsule Collider
character Collider 与rigidbody 相比较,作为碰撞检测体有哪些好处?
rigidbody 除了碰撞检测的功能,还有其他物理特性,比如重力,所以比character collider计算会更加复杂,效率会变低
所以在仅仅只是做碰撞检测体时使用character collider 更高效
碰撞检测体分类:
dynamic collider
static collider: 标记为static,添加collider ,没有rigidboby为静态collider。所有的静态collider会在游戏初始化时进行合并,在游戏中一般不能移动,
如果改变了static collider的位置,会重新计算所有的static collider。造成很大的性能问题。
标记为static 添加collider 有rigidbody就不在是static collider。不会再游戏初始化时合并
Trigger:触发器
Collider的trigger属性勾选上就成为一个无阻挡碰撞,也叫触发器
进入/穿出会触发OnTriggerEnter/OnTriggerOut函数。
Physics rayCast : 射线检测,无限长
SphereCase :圆形射线,发射出去的是圆形小球
CapsuleCase 胶囊体赊销
lineCase 线段测试,有长度限制
被检测的collider必须在发现射线的发射器之外
physics 物理系统的性能
1、物理系统检测在移动端的性能不佳,尽量不用rigidbody
2. 调低 flexed TimeStep的值。默认0.02s 就是1s计算50次。一般调成0.5s
3.尽量使用简单的碰撞体collider。必然box collider capsule collider sphere collider 不使用 mesh collider 、terriain collider particie collider(粒子系统的collider)
4.禁止出现static collider
5.尽量少用rigidbody
6. 自己模拟一些简单的物理行为,如重力,只有在必要的时候才用physics CapsuleCase
控制角色在真实环境中行走
1.地面需要有collider
2.角色使用charactor collider
3.角色每个uppdate中计算位移量(需要获取摄像机坐标系下鼠标移动方向,世界坐标的转化)
public float speed = 1;
public static Vector3 camDelta = Vector3.left;
private Vector3 startpos;
update()
{
if(Input.GetMouseButton(0))
{
if(startpos == vector3.left)
{
startpos = Input.mousePosition;
}
else {
camDelta = Input.mousePosition = startpos;
}
} else if(Input.GetMouseButtonUp(0))
{
camDelta = vector3.zemo;
startpos = vector3.left;
}
}
4.对角色的charactor collider应用位移量(要保持与地表至少。保证行走在地表之上)
update(){
Vector3 worldYDirection = Camera.Main.transform.TransformDirection(vector3.up);
Vector3 groundYDirection = new Vector3(worldYDirection.x,0,worldYDirection.z).normalized* camDelta.y;
Vector3 worldXDirection = Camera.Main.transform.TransformDirection(vector3.right);
Vector3 groundXDirection = new Vector3(worldXDirection.x,0,worldXDirection.z).normalized* camDelta.x;
vector3 direction = (worldXDirection + worldXDirection).normalized;
Vector3 motion = direction * speed *Time.deltaTime;
motion.y = -1000;
CharacterController cc = GetComponent<CharacterController>();
cc.Move(motion);
--------------------
音乐 Audio Listener Audio Source
使用音乐三要素 接受者 ,音频来源 AudioClip
1.接收者listener(相当于耳朵)一般放在Camera上,使用组件Audio Listener 而且一个场景只能有一个listener
2.音频来源source (发声音的物体)Audio Source
Audio Source 属性介绍
1.AudioClip 拖入音频文件
2.3D sount settings 3d 音效设置
属性Deppler Level 多普勒定律:越音声音越大的,效果强度设置
多普勒定律具体体现Max Distance曲线,可以自己设置
代码控制声音
AudioSource source
source.play();
source.pause();
source.stop();
source.isPlaying()
source.volume= 10;
--------------
音频数据:用AudioClip存储
AudioClip保存的信息有 channel声道数量 frequency 采样率 short 存储的采样数据
动态在代码中生成音乐 并播放
create 接口参数: 参数1 名称 参数2 数据大小 参数3 通道数量 参数4采样率 参数45 是否是流式数据,web数据一般是流式数据
short[] data = null;
AudioClip clip = AudioClip.Create(“”,sizeof(HostData),2,1600,false);
clip.setData(data,0);
audioSource.clip = ac;
audioSource.play();
支持的视频格式 mov mpg mpeg mp4 avi asf
untiy 编辑器查看视频需要安装插件QuickTime
pc上使用视频,在pc上视频就是一个texture, 使用MovieTexture来播放和停止
ios/Androil 等移动平台: 会挂起u3d当前进程,调用的是操作系统的播放器进行播放,播放结束才回到游戏,所以只能是全屏播放。靠的是本地的系统播放器播放的
播放接口:
Handheld.playFullScreenMovie("名称",背景色,控制模式(FullScreenMovieControlMode.cancelOnInput),缩放模式(ScaleMode.scaleAndCrop);
视频存放地址: StreamingAssets(在移动平台的只读区域)。所以只能播放已存在视频,不能动态生成或者从网络下载
支持的视频格式: ios: mov mp4 mpv 3gp androld:mp4 3gp
-------------------------------------
unity 动画系统Animation
动画系统分为 4.0之前的legacy 和4.0之后是mechanim
动画片段都是Alimationclip
外部文件格式 fbx obj max 等格式,推荐使用fbx
内部
legacy 依赖于Animation 组件 。加入属性Animiaitons数组的动画就可以播放
mechanim 依赖于组件Animator组件,导入animator
Fbx 文件的主要属性
model 模型
Animations :有动画片段列表clip
Wrap Mode播放模式 :Loop 循环
Culling Type 选择Based on Renderers 意思是不渲染的时候动画不播放
--------------------
legacyde Animation 使用
1.拖入模型,去掉默认导入Animator 添加组件Animation
2、在Rig 属性(model右侧)的Animation Type 设置为Legacy
3.给Animation 添加动画片段(从fbx中拖入动画片段即可)
代码控制Animation播放动画
Animation animation = GetComponent<Animation>();
update(){
if(Input.GetKeyUp(KeyCode.A))
{
animation.Play("动作1名称")
} else if(Input.GetKeyUp(KeyCode.B))
{
animation.Play("动作2名称")
}
}
-------------------------------
Animator 4.0之后的动画系统,动画片段切换的时候会自动加入动作融合,让过渡自然
1.拖入动画,导入组件Animator
Controller:新的自己创建的动画状态机
Avatar 就是Fbx 修改rig 为genaric 或者humanoid后自动出出现的avatar 拖到这里即可
动画状态机:就是根据条件切换不同动作
创建方式: project中右键Create --- Animator Controller (同cs文件)
双击编辑状态机:
进入Animator窗口
Any state 任意状态 Entry 进入 exit 出去
1.修改动画fbx的Rig(在model后面)的Animation type为Generic(通用类型。效率高一点) 或者Humanoid(人形动画)
2.从动画fbx拖入需要的动画片段
3。animator窗口 点击动画,然后右键选择make Transition ,在直接拉到下一个动画,就可以添加动画片段间连接
右键选择Set as layer default state 设置进入后默认播放的动画片段
4.animator串口,选中一条连接,在右侧的Inspector窗口的Conditionas 添夹动画转化的条件
-----------------------------------
Animator 动作状态机
Animator窗口
1.动画片段切换
方式1: 在Animator 窗口处理
1.在左上角的Paramaters(Layers后面)添加参数
2.在右侧Inspector 的codititons 根据参数添加转化条件
代码控制 Animator的参数Paramaters
Animator animator = GetComponent<Animator>();
update(){
if(Input.GetKeyUp(KeyCode.A))
{
animator.SetBoll("参数a名",值);
} else if(Input.GetKeyUp(KeyCode.B))
{
animator.SetBoll("参数b名",值);
}
}
2.动画片段切换 方式2.不添加动画片段连线,直接通过代码控制
Animator animator = GetComponent<Animator>();
update(){
if(Input.GetKeyUp(KeyCode.A))
{
animator.CrossFade("要切到的动画片段的名称",过渡阶段时间长度(0,完全不融合 1 完全融合),播放动画的层,(默认是-1),从一下段动作的什么时候开始播放(0开头,1结尾))
animator.CrossFade("wake",1,-1,0);
}
直接代码控制的好处:animator就没有连线,管理起来比较简洁
--------------------------
Animator 动作融合
发生融合的情况:1.不同状态之间的动作融合(关键点:融合的时间长度,发生动作如何的时候下一个动作的播放时间)
2.身体不同部位之间的融合(比如射击的动作,关键点:不同部位之间的layer的融合,
不同部位融合的做法:
1.在Layer(Paramaters左侧)添加新layer)
2.每一个layer 添加一个mask ,方法是在project中右键Create --- Animator mask(Avatar Mask 中红色代表不起作用的层,绿色代表起作用的层,根据需要等新的avator mask 就行身体部位的改成绿色即可)
3.把Avatar Mask 赋值给layer 的Mask属性
4.还可以修改layer 的weight为1.
--------------------------
Animator 动作重定向(换装)多个模型,同一套骨骼,就可以公用同一个动作
AB 是不同的骨骼,比如人。人的变身,不同的骨骼,复用同一个动作
解决方法:unity 提供了一套标准骨骼,然A与标准骨骼关联。B与标准骨骼关联。标准骨骼作为AB直接的桥梁。就可以让AB公用同一套动作
举例说明animatior的动作重定向
在untiy中只有人形动作才能进行重定向
1.把A B对应fbx 的Rig(Model 右侧)的Animation type设置为Humanoid(人形动画)。
在设置rig 的Avator Definition 设置为Create form this model (avator 就是unity内置的标准人形骨骼)
然后rig 中点击 Configure配置avator(弹出弹框选择 选择do not save)
2.A B 的连个模型的 animator的 Controller属性设置为同一个,就可以共用一套animator controller了,动作就会一致
如果不需要动作重定向,就选择rig的Animation type为genaral即可
------------------------
Animator 根骨骼动画
问题:美术或者程序单独展现动作都会造成的逻辑位置跟骨骼位置不一致的问题
解决方案:美术直接在max里面为根节点制作点位移动画,并同步到游戏里的逻辑位置的方案,学名跟骨骼动画
区分:跟骨骼动画和非跟骨骼动画如何区分?
默认从fbx 导入的动画都是跟骨骼动画,区别跟骨骼动画和非跟骨骼动画的地方是
fbx 的Animations(rig 右侧)的Root Transform Rotation/Postion(y)/Postion(XZ) 的Base Info Pose 勾选上表示非跟骨骼动画,
Root Transform PostitionY选择based upon为Original 最后点击Apply,非跟骨骼动画完成一次动作后,逻辑位置在还在原来的位置,所以就会导致,第一次动作,会先回到原来的位置然后再执行
Root Transform Rotation 下 Bake Into Pose的意思是,跟骨骼的旋转是否烘焙到pose中,如果勾选上,表示跟骨骼的旋转只会影响到跟骨骼自身,而不影响它的逻辑位置,所以勾选上就是非跟骨骼动画
使用跟骨骼动画的总开关:
Animator 的属性 Apply Root Motion 勾选上表示使用跟骨骼动画
代码控制跟骨骼动画的位置
系统函数OnAnimatorMove 中可以实时得到每次逻辑位置受跟骨骼位置影响后的逻辑位置的真实位置,然后可以对逻辑位置做修改
OnAnimatorMove()
{
Animator animator = GetComponent<Animator>();
Vector3 pos = transform.position;
transform.postion = pos + Vector3.up;
}
--------------------------------
Animator 特性-
动作mask: 禁用某些骨骼对动作的影响
动作mask 做法
1.fbx 的Animations 的Mask开关
2. mask打开后出现的属性humandoid(人形模板)中把不需要的关节标红,(默认绿色,红色表示mask。不会有动作)
3.mask打开后出现的属性Transform也可以实现Animator mask 效果
-----------------
Animator 特性 - 动作event
动作event 是动作系统跟代码之间的接口
动作的播放进度与其他逻辑代码的同步,常用于表现技能动作中的特效音效同步
做法: 给动作打点,在点上添加event 。在代码中给event写函数
在fbx的Animations 的Events 点击打点 (就是添加Animation event 会调出Edit Animation Event 窗口,
需要先motion拖拽到需要打点的动画帧)在里面输入(Animator 所在物体的cs脚本的)function 方法名,
其他的(float. int. string.Object)都是参数,参数只能4选1
------------
animator -event 使用
播放特效
void PalyEffect(string path)
{
GameObject go = AssetDatabase.LoadAssetPath(string.format("Assets/(0).prefab",path), typeof(GameObject)) as GameObject;
GameObject effect = GameObject.Instantiate(go);
effect.transform.parent = gameObject.transform;
effect.transform.localPosition = vector3.zero;
effect.transform.localRotation = vector3.identify;
effect.transform.localScale = vector3.one;
}
播放音频
AudioSource as = getComponent<AudioSource>();
void PalyAudio(string path)
{
as.stop();
AudioClip ac = AssetDatabase.LoadAssetPath(string.format("Assets/(0).prefab",path),
typeof(AudioClip)) as AudioClip;
as.clip = ac; as.Play();
}
下一步在fbx 的Animations 的Event 打点,上面的播放音频,播放特效就是事件名称即可,
--------------
animation与animator 性能
尽可能使用animation,少用animator
1.在移动端animator是很大的性能瓶颈,因为animator的状态机操作维护,动作融合,跟骨骼计算等都很耗费cpu。所以非必要不要使用animitor
手机端使用animator的经验之谈
1.限制animator的数量,超过30个,对cpu的压力很明显.建议可以部分使用animation
2。不适用更骨骼动画,就关闭跟骨骼动画
3.限制骨骼数量不能超过30跟(animations 下的mask 属性里可以数到骨骼数)
3.animator 无论是否看到,一般都在计算,要让animator看不到就不计算,就让Animator的属性Culling Mode 选择为Cull Completely(不能选always aniamate),就可以在屏幕之外不会计算,
如果有从屏幕外到屏幕内的动作,那它的animator 的Culling Mode 需要选择always Animato
4.控制动作的帧数,尽量小
5.尽量fbx的rig使用 Gemeric 最好不要使用Humanoid(humanoid会涉及动作的重定向)
---------------------------------------------
Animation View 动画编辑器 位置 Window--Animation --Animation
用于边界Animation的所有属性
MeshRender MT
---------------
寻路:
概念:空间内,给定物体一个起点,一个终点。然后获取这两点之间一条物体可以走的路径
寻路算法: 2d 场景中:
方法1:A*算法,首先把场景划分成规则的格子(比如64*64等),给点两点。这两点可定属于某两个格子上,
使用广度优先的遍历,从当前格周围的四个方向或者八个方向开始依次去探测,永远保存当前离目的地最短代价最小的路径。探测到终点是停止,
这样就可以得到从当前点到终点的最短路径,效率不是很高,
方法2:Navigation Mesh: 导航网格,是对A*算法的扩展,与A*的区别是场景不在划分为格子而且场景的遮挡之类的,划分为不规则的相邻的三角型mesh,然后基于三角形之间的广度优先遍历,不扩展到三维的寻路。
Naviagtion Mesh: 位置WI
能够被计算到导航网格中的物体object都必须是静态的。
1.所有计算的object 的属性static 选择为Navigation Static
2. 可走的objct 的Navigaiton -- object 的Navagtion Area 选择Walkable
不可走的objct的Navigation--object的Navagtion Area 选择 Not Walkable
3.选中所有有navigation 的object 点击bake 下的bake按钮生成导航网格navigaiton mesh
4. 导航网格参数设置
在Navigation的Bake 下 Agent Ridius: 寻路物体的半径
Agent Height :寻路物体的高度
Max Slope: 最大坡度
Step Height
修改完成后从新bake 生成新的navigation
navigtionmesh 寻路
方法1:
1.在需要寻路的物体,比如人身上加组件Nav Mesh Agent
2.代码设置nav mesh Agent 的目标点就可以自动寻址
NavMeshAgent agent = Transform.GetComponent<NavMeshAgent>();
agent.destination = new vector3(0,0,0);
方法2:Navigation
NavMesh.CalCulatePath(原始的位置,目标点位置,可以走的路线层layer,NavMeshPath(输出参数,返回的是一个可以走的路线对应点的集合))
寻路和碰撞检测
有时阻挡也是控制物体移动的方式
静态阻挡: 1,NavMesh 导航网格方式实现寻路
动态阻挡 ,单独使用nav mesh 无法寻路(会直接忽略动态阻挡(非static),然后添加包围盒Capsule Collider 和RigidBody,会直接被动态阻挡挡住,从而停止,无法继续走
需要动态阻挡,从而规划新的路线,需要使用Navmesh obstacle
nav mesh 的layer 与const
nav Mesh的Areas 中默认定义了Walkable; NotWalkable; Jump
定义了Not Walkable的代价是无穷大的,所以寻址的时候就不会走
可以在Areas 添加需要Layer ,然后把对应的物体的Navigation的Object 下的Navigation Area 选择为对应的层,就会根据层的代价
等到不同的寻址路径。从而得到难易程度不同的线路
具体做法: 给有时可走有时不可走的物体添加一个layer 比如Door
当它可以走时在添加Nav Mesh Agent的人上修改Nav Mesh Agent 的Area mask 中勾选上door。当它不可走时去掉door即可
-------------------------
navmesh obstacle 导航网格 中的动态阻挡,指的是动态生成的object 无法阻挡
解决办法:
方法1.动态物体object 添加包围盒collider 和righdbody 让寻路非动态物体阻挡而停止
方法2:使用组件navmesh obstacle来解决动态阻挡的问题,在需要动态阻挡后,选择新的道路
动态添加的obj 添加组件Nav Mesh obstacle 勾选属性carve则每一帧都会程序重新计算网格。但是性能低下
----------------
控制角色在真实环境中寻路
NavMeshAgent agent = Transform.GetComponent<NavMeshAgent>();
1.获取点击的点
if(Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RayCastInfo Info;
if(Physics.rayCast(ray,out info))
{
agent.destination = info.point;
if(agent.pathStatus == NavMeshPathStatus.PathInvalid)
{
return;
}
}
协程:逻辑并行, 帧并行
for(int i= 0;i<100;i++)
{
DOSIMTHING()
Yeild return null;
}
携程:C#有一个迭代器,就是把一个复杂的操作分不到每一帧中执行,实际就是update ,好处是每次运行都能记住上次运行的位置,
携程是单线程。是逻辑上的并行,不会阻塞主线程,是在主线程的每一帧上分布执行
携程:是假的多线程,是在主线程的多个帧的并行
携程常用的coroutine
yield return null;
yield return new waitForEndFrame();
yield return new waitForFiexUpdate();
yield return new waitForSeconds(2);
yield return www new www(网址)
代码示例
WWW www = new WWW(http:
yield return www;
Debug.Log(www.text);
yield return StartCoroutine(down());
示例代码: 先现在,在计算,最后打印
IEnumerator Down(){
WWW www = new WWW(http:
yield return www;
}
IEnumerator Caculate(int times){
int a= times + 5;
yield return null;
}
IEnumerator other(){
yield return null;
}
调用:会依次执行down -》 Caculate -》other
IEnumerator work()){
yield return StartCoroutine(down());
yield return StartCoroutine(Caculate(3));
yield return StartCoroutine(other());
}
携程
1.最简单的携程也需要2帧执行
IEnumerator work()){
yield return null;
}
yield return null;
yield return new waitForEndFrame();
在yield return null 和yield return new waitForEndFrame(); 之间存在update操作
尽量避免无谓的多帧执行
IEnumerator Down(){
if(!needdown)
{
yield return null
}
else
{
WWW www = new WWW(http:
yield return www;
}
}
正确做法是在外部片段,携程中只做下载方法,正确做法如下
void dd()
{
if(!needdown)
{
work();
}
else{
yield return StartCoroutine(down());
}
}
void word(){
}
IEnumerator Down(){
WWW www = new WWW(http:
yield return www;
work();
}
携程的应用
1.定时器:
void hh()
{
yield return new waitForSeconds(2);
work();
}
2.每个三秒运行一次work
void hh1()
{
while(true){
yield return new waitForSeconds(3);
work();
}
}
3.先运行一次work。然后每个三秒运行一次work
void hh2()
{
while(true){
work();
yield return new waitForSeconds(3);
}
}