├── C# ├── Csharp属性访问器 ├── c#基础 ├── unity委托 ├── 匿名方法和闭包 ├── 多继承和多重继承 ├── 委托delegate ├── 委托和事件 ├── 委托的使用 ├── 虚方法,抽象类和接口的区别 └── 链表问题 ├── Json ├── LICENSE ├── Lambda表达式 ├── README.md ├── RPG_角色属性设计 ├── Shader ├── Outline Shader初步 ├── Shader-新手引导(椭圆遮罩) ├── Shader之RenderType ├── Shader之UV ├── Shader之着色器内置属性 ├── Shader之纯着色器 ├── Shader之透明度混合 ├── Shader之遮罩层 ├── Shader之闪光Logo ├── Shader之顶点着色器 ├── Shader基础光照 ├── Shader屏幕后处理 ├── Shader屏幕后处理(2) ├── Shader屏幕坐标ComputeScreenPos ├── Shader流光效果 ├── Shader消融 ├── Shader绘制基本图形 ├── Shader读取2D纹理贴图 ├── Shader读取纹理贴图 ├── Shader透明效果 ├── SpriteOutline Shader ├── UV之tilling和offset ├── UV动画 ├── shader扭曲 ├── shader空间变换 ├── shader计算像素点距离 ├── 屏幕后处理Camera的Pipeline ├── 渲染管线流程 ├── 计算机图形学-三大测试 ├── 顶点变换 └── 高斯模糊的实现 ├── Shader之Sprite Outline ├── Shade之屏幕后处理(1) ├── Unity ├── Animation配置 ├── AssetBundle │ ├── AssertBundle基本理论 │ ├── AssertBundle资源加载框架设计 │ ├── AssetBundleManager管理 │ ├── AssetBundle之打包Prefab │ ├── AssetBundle加载 │ ├── AssetBundle资源加载详解 │ ├── AssetManager(2) │ └── Unity5如何进行AsasertBundle打包 ├── AudioManager ├── RawImage ├── Ray │ ├── Camera和Ray │ └── 射线Ray的原理 ├── Ray射线API ├── SerializeField的用法 ├── UI │ ├── UGUI事件系统 │ ├── UGUI拖拽 │ ├── UGUI渲染层级 │ ├── unity的UI渲染机制 │ └── 屏幕自适应 ├── UnityEditor编辑器 ├── Xml存储 ├── 协程 │ ├── 协程原理分析 │ └── 协程工作流程 ├── 向量 ├── 四元数Quaternion ├── 物理组件 │ ├── 2d游戏碰撞检测 │ ├── Camera相机跟随问题 │ ├── Camera看向目标物体 │ ├── CharacterController │ ├── Collider和Trigger │ ├── Rigidbody之velocity和Addforce │ ├── Rigidbody移动 │ ├── Transform组件 │ ├── UGUI血条跟随角色 │ ├── camera控制 │ └── 物理组件Collider └── 生命周期 │ ├── Awake()和Start区别 │ └── Unity中Update和FixUpdate和LaaterUpdate ├── UnityInspecotr编辑器扩展 ├── Unity旋转 ├── vector.forward和ransform.forward ├── 动画导入设置 ├── 包设计packet ├── 单例模式 ├── 常用功能 ├── AStar原理 ├── MiniMap小地图 ├── Profile性能优化基础 ├── Unity3d缓存图片 ├── Unity场景加载进度条 ├── objCache对于Hierachy └── unity小型游戏寻路算法 ├── 常用设计模式梳理 ├── 游戏架构设计 ├── SaveSystem ├── UI框架设计 ├── UI结构设计 ├── Unity事件管理器 ├── Unity基于事件机制的消息系统 ├── Unity工具类系列之对象池 ├── Unity消息机制 ├── Unity游戏客户端通用框架设计 ├── Unity的UI层级管理框架 ├── Unity缓存池 ├── Unity脚本加载和编译 ├── XML序列化 ├── unity基础 ├── unity框架设计(二) ├── unity游戏架构设计之对象管理 ├── unity游戏音效 ├── unity自定义消息通知组件 ├── 任务系统架构 ├── 游戏UI框架设计思路 ├── 行为树设计 └── 资源加载 ├── 网络输水模型 ├── 网络通信 ├── Socket粘包和分包 ├── Token设计JWT ├── Unity的Socket网络连接模块 ├── protobuf实现传输 └── 网络消息包Packet封装 ├── 设计模式 ├── Unity设计模式 ├── 代理模式 ├── 原型模式 ├── 命令模式 ├── 备忘录模式 ├── 外观模式 ├── 对象池模式 ├── 建造者模式 ├── 桥接模式 ├── 模版方法模式 ├── 状态机模式 ├── 策略模式 ├── 装饰模式 └── 责任链模式 ├── 设计模式和处理请求 └── 音频 /C#/Csharp属性访问器: -------------------------------------------------------------------------------- 1 | 类成员包括变量和方法,希望其他类访问成员变量,则定义为共有public,那么这个成员变量就可随意访问,但是不利于数据安全性。 2 | 3 | c#通过属性特性读取和写入字段【成员变量】,而非直接读取和写入,借此提供对类中字段的保护。 4 | 5 | 属性作为c#面相对象技术中封装性的体现。 6 | 7 | 属性和字段的区别: 8 | 1)属性作为逻辑字段,是字段的扩展,不占用实际的内存 9 | 2)属性可以被其他类访问,而非public字段不能直接访问 10 | 3)属性可以对接受的数据在范围上做限定,但是字段不能 11 | 12 | 使用属性的情况: 13 | 1)要求字段只读或只写 14 | 2)需要限制字段取值范围 15 | 3)在改变一个字段的值时希望改变对象的其他状态 16 | 17 | 18 | 使用字段的情况: 19 | 1)允许自由读写 20 | 2)取值范围只受到数据类型约束而无其他任何限制 21 | 3)值变动不引发类中其他成员的变化 22 | 23 | 24 | 设计字段是为了便于内部方法使用,而尽量与外界隔绝; 25 | 设计属性考虑的是方便外界的使用,但是不让外界知道的数据一律不给。 26 | -------------------------------------------------------------------------------- /C#/c#基础: -------------------------------------------------------------------------------- 1 | 1.简述值类型和引用类型的区别: 2 | 1)值累心光存储虎仔内存栈中,引用类型数据存储在内存堆中。而内存单元中存储的是堆中存放的地址。 3 | 2)值类型存取快,引用类型存取慢 4 | 3)值类型表示实际存储的数据,引用类型表示指向存储在内存堆中的数据的指针和引用 5 | 4)栈的内存是自动释放,堆内存中是.Net中会由GC来自动释放 6 | 5)值类型继承自System.ValueType,引用类型继承自System.Object 7 | 8 | 2.描述Interface和抽象类之间的不同 9 | 语法不同: 10 | 1.抽象类中可以有字段,接口没有 11 | 2.抽象类中可以有实现成员,接口只能包含抽象成员 12 | 3.抽象类中所有成员修饰符都是可以使用,接口中所有的成员都是对外的,所以不需要修饰符修饰。 13 | 用法不容: 14 | 1.抽象类是概念的抽象,接口关注行为 15 | 2.抽象类的子类和父类的关系是泛化关系,实现类和接口之间关系是实现关系 16 | 3.一个类只能继承一个类,但是可以实现多个接口 17 | 18 | 19 | 3.c#中List,Hashtable,Dictionary区别? 20 | List是对数组的封装,当List对象的Item元素数量超过了Capacity,List对象重新生成一块 21 | 大小是原来Capacity的两倍内存空间,然后将当前所有Item元素以及待添加的元素复制到新的内存空间。 22 | 频繁的添加数据消耗大,但数据查询速度快,内存连续。 23 | 24 | 25 | Hashtable、Dictionary它们的内存是用key值根据hash算法算出来的,它们的内存区域是不连续的, 26 | 所以在遍历的时候难免会对内存进行换页操作这样遍历的时间就长; 27 | 28 | Dictionary:1、类型安全,对值类型没有装箱拆箱操作; 29 | 2、在单条数据的读取上速度快; 30 | 3、数据的存放是有序的 31 | Hashtable:线程安全,适合多线程,无序; 32 | 33 | 简述StringBuilder和String的区别? 34 | string是字符串常量 35 | stringBuffer是字符串常量,线程安全 36 | stringBuilder是字符串变量,线程不安全 37 | 38 | string类型是不可变的对象,当每次对string进行改变时都需要生成一个新的string对象, 39 | 然后将指针指向一个新的对象,如果在一个循环里面,不断改变一个对象,就要不断的生成新的对象, 40 | 所以效率很低。建议不断更改string对象的地方不要使用string类型。 41 | stringBuilder对象是对象在做字符串连接操作时在原来的字符串上进行修改,改善了性能。连接操作 42 | 频繁是,使用stringBuilder对象。 43 | 44 | 45 | 索引器与属性的区别: 46 | 1)索引器以函数签名方式this来标识,而属性采用名称来标识,名称可以任意 47 | 2.)索引器可以重载,而属性不能重载 48 | 3)索引器不能以static来声明,而属性可以 49 | 50 | 51 | 52 | c#解释代码编译: 53 | 54 | 1.通过c#编译器将源代码编译为托管代码 55 | 2.将创建的代码组合到程序集合 56 | 3.加载公共语言运行时CLR 57 | 4.通过CLR执行程序集 58 | 59 | 类型安全之拆箱和装箱: 60 | 引用类型之间的类型转换 61 | 62 | 将一个对象转化为任何基类类型。 63 | 转换时,将等号左边的和等号右边的类型进行转换。 64 | 如果左边是基类,则安全, 65 | 否则编译异常,必须显示转换 66 | Object a = new Manager()  正确, Object是基类 67 | 68 | 装箱的过程: 69 | int x = 1023; 70 | object o = x;  //装箱 71 | 72 | 执行第一句,托管堆没有任何东西。栈上有一个整型变量。第二局就是装箱,因为Object是引用类型,必须指向堆上的某个对象,而x是值类型,没有堆上的对应对象。 73 | 所以需要使用装箱,在堆上创建x. 74 | 75 | 装箱的步骤: 76 | 1.分配内存。 这个例子需要一个整型变量,加上托管堆上所有对象都有的两个 77 | 额外成员【类型对象指针和同步块索引】。类型对象指针指向int类型对象。 78 | 2.值类型的变量赋值到新分匹配的堆内存 79 | 3.返回对象的地址 80 | 81 | 82 | 拆箱的过程: 83 | 拆箱不需要额外的内存。 84 | int i = 1; 85 | object o = i; 86 | var j = (byte)o; 87 | 拆箱步骤: 88 | 1.如果已经装箱实例为Null,抛出NullReference异常 89 | 2.对象不是null但是类型不是原先未装箱的值类型,则抛出InvalidCast异常 90 | 3.获取已经装箱实例当中值类型字段的地址 91 | 4.创建新的值类型变量,其值使用第三步获取到的值 92 | 93 | 94 | 类(class)和结构(struct)的区别?结构对象可能分配在堆上吗?何时考虑使用结构体? 95 | 类和结构是c#两个最主要的研究对象。 96 | 1.结构是值类型,继承自 System.ValueType,而类是引用类型 97 | 2.值类型不能继承,所以结构struct不能继承 98 | 3.结构struct作为值类型,但是结构中可以存在引用类型 99 | 【但是如果struct中包含引用类型,建议使用类】 100 | 结构体如果包含引用类型,则部分分配在堆上 101 | 102 | 结构: 103 | 结构作为值类型, 104 | 结构是值类型,new运算符和其他引用类型的工作方式不同。 105 | new运算符并不分配堆中内存,只是调用相应的构造函数。 106 | 107 | 结构中可以存在构造函数,但是结构内部不能存在无参构造函数。 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /C#/unity委托: -------------------------------------------------------------------------------- 1 | Unity中事件和委托大多数时候和事件相关。 2 | 3 | 比如:当前需要设计一款计时器(Timer)功能,拥有基础事件:开始计时、暂停计时,停止计时【计时器事件】 4 | 5 | 6 | using UnityEngine; 7 | using System.Collections; 8 | /// 9 | ///计时器 10 | /// 11 | public class Timer : MonoBehaviour { 12 | public delegate void MyEventHandler(float currentTime); 13 | #region 计时器的三种基础事件 14 | 15 | public static event MyEventHandler onTimerStart; 16 | public static event MyEventHandler onTimerPause; 17 | public static event MyEventHandler onTimerStoped; 18 | 19 | #endregion 20 | private bool isStarted=false; 21 | public bool IsStarted{ 22 | get{ 23 | return isStarted; 24 | } 25 | } 26 | private bool isStoped = true; 27 | public bool IsStoped{ 28 | get{ 29 | return isStoped; 30 | } 31 | } 32 | private float totalTime = 0; 33 | // Update is called once per frame 34 | void Update () { 35 | 36 | //空格键当作“开始/暂停”键 37 | if (Input.GetKeyDown (KeyCode.Space)) { 38 | OnChangeState (); 39 | } 40 | //回车键当作“停止”键 41 | if (Input.GetKeyDown (KeyCode.Return)) { 42 | OnSetStop (); 43 | } 44 | 45 | if (isStarted) { 46 | isStoped = false; 47 | totalTime += Time.deltaTime; 48 | } 49 | } 50 | void OnChangeState(){ 51 | var _startState = !isStarted; 52 | isStarted = _startState; 53 | if (isStarted) { 54 | //检查onTimerStart是否为空,防止报空 (废话了。。。下面不做赘述) 55 | if (onTimerStart != null) { 56 | onTimerStart (totalTime); 57 | } else { 58 | Debug.Log ("onTimerStart is Empty"); 59 | } 60 | } else { 61 | if (onTimerPause != null) { 62 | onTimerPause (totalTime); 63 | } else { 64 | Debug.Log ("onTimerPause is Empty"); 65 | } 66 | } 67 | } 68 | void OnSetStop(){ 69 | if (onTimerStoped != null) { 70 | onTimerStoped (totalTime); 71 | } else { 72 | Debug.Log ("onTimerStoped is Empty"); 73 | } 74 | isStarted = false; 75 | isStoped = true; 76 | totalTime = 0; 77 | } 78 | } 79 | 80 | 81 | https://blog.csdn.net/GhostOrange/article/details/53150383 82 | -------------------------------------------------------------------------------- /C#/匿名方法和闭包: -------------------------------------------------------------------------------- 1 | 外部变量: 2 | 3 | 4 | 捕获的外部变量: 5 | 6 | 7 | -------------------------------------------------------------------------------- /C#/多继承和多重继承: -------------------------------------------------------------------------------- 1 | 多继承和多重继承: 一字之差,谬以千里。 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /C#/委托delegate: -------------------------------------------------------------------------------- 1 | 委托的本质: 2 | 1)声明委托: 3 | public delegate void MyDelegate(string s); 4 | 2)ILSpy反编译: 5 | public classauto ansi sealed MyDelegate : MulticastDelegate 6 | 自动生成了类,可见委托是一种数据类型。sealed表明是封闭类,不可继承 7 | 8 | 9 | 创建委托变量的本质: 10 | 1)既然=委托是类,并且不是静态类,想要使用,就必须new 11 | 12 | public delegate void MyDelegate(string s); 13 | 14 | public static void main(string[] args){ 15 | 16 | MyDelegate delegate = new MyDelegate(MyDelegate_2); //创建委托变量 17 | 18 | MyDelegate_2 delegate2 = MyDelegate_2; 19 | 20 | } 21 | 22 | static void MyDelegate_2(){ 23 | Console.write(""hhhh); 24 | } 25 | 26 | 27 | 调用委托变量和本质: 28 | 29 | 1)给委托变量加()即可调用 30 | 31 | MyDelegate delegate = new MyDelegate(MyDelegate_2); 32 | delegate(); 33 | 34 | 35 | 委托和匿名方法: 36 | 37 | 如果委托指向的方法知识临时使用,就不需要声明了,直接使用匿名方法。 38 | 39 | private delegate void MyDelegateHandler(string name); 40 | 41 | private void MyDelegate(string name,MyDelegateHandler mydelegateHandler) 42 | { 43 | mydelegateHandler(name); 44 | } 45 | 46 | MyDelegate delegate = new MyDelegate(delegate(string name){ 47 | Console.WriteLine(); 48 | }); 49 | 50 | 51 | 委托好处: 52 | 1)逻辑分离,解除耦合 53 | 2)对修改封闭,对扩展开放 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /C#/委托和事件: -------------------------------------------------------------------------------- 1 | 委托和事件没有可比性,主要在于; 2 | 1)委托作为数据类型,事件是对象【理解为对委托变量的封装】 3 | 4 | 委托对象【委托方法实现的事件】和【标准的event方式实现】的事件的区别: 5 | 6 | 事件内部是委托实现 7 | 8 | 三种实现事件方式的区别: 9 | 1)直接使用委托实现 10 | 2)用私有委托+共有方法模拟事件 11 | 3)直接event事件 12 | 13 | 14 | 15 | 对于事件来说,外部只能“注册自己” : += 16 | 注销自己“”: -= 17 | 外部不可以注销其他的注册这,外界不可主动触发事件 18 | 如果使用Delegate就没有办法进行这样的控制 19 | 所以诞生了事件 20 | 21 | 事件是用来阉割委托实例的。事件只能add、remove自己,不能赋值。事件只能+=、-=,不能=、不能外部触发事件。 22 | 23 | 1、委托的作用: 24 | 25 | 占位,在不知道将来要执行的方法的具体代码时,可以先用一个委托变量来代替方法调用(委托的返回值,参数列表要确定)。在实际调用之前,需要为委托赋值,否则为null。 26 | 27 | 2、事件的作用: 28 | 29 | 事件的作用与委托变量一样,只是功能上比委托变量有更多的限制。(比如:1.只能通过+=或-=来绑定方法(事件处理程序)2.只能在类内部调用(触发)事件。) 30 | 31 | 3、在自定义控件(自己编写控件的时候,会大量用到.编写控件的时候,会写一些事件。但是当这些事件 被触发以后,具体执行的那些事件处理程序是编写控件的人没法确定的。这个时候只能通过事件来占位(调用),具体调用的是哪个方法,由使用控件的人来决定(Click+=new 委托(方法名);)) 32 | 33 | 先有观察者模式,再有委托事件这一技术。它有一个前后顺序,如果你要彻底学会委托事件,那么就要先学会观察者模式,还要了解观察者模式的缺陷。 34 | 35 | 观察者模式的缺陷: 36 | 37 | 观察者模式是对主题对象和观察者对象进行解耦,使双方都依赖与抽象,而不是依赖于对方的具体对象, 38 | 、使双方的变化都不会影响到对方的具体对象。当多个对象需要根据一个对象的状态发生相应的改变或操作时, 39 | 可使用观察者模式。 40 | 41 | 42 | 43 | 猫和人抓老鼠: 44 | 1)老鼠是主体对象 45 | 2)猫和人是观察者 46 | 47 | https://blog.csdn.net/q493201681/article/details/82352616 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /C#/委托的使用: -------------------------------------------------------------------------------- 1 | 向函数传参: 2 | 3 | void Start(){ 4 | Print("xxx"); 5 | } 6 | 7 | void Print(string name){ 8 | base.print(name); 9 | } 10 | 11 | 传统的传递参数的方式 12 | 13 | c#委托的方便在于只需要告诉某个函数的名称就可以实现调用。 14 | 15 | 委托其实 实质就是 事件和Observer观察者模式 16 | 17 | class X 18 | { 19 | delegate void Helper(); 20 | 21 | void A() 22 | { 23 | Print("A"); 24 | } 25 | 26 | void B() 27 | { 28 | Print(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /C#/虚方法,抽象类和接口的区别: -------------------------------------------------------------------------------- 1 | 虚方法和普通方法其实差不多。 2 | 3 | 虚方法(virtual)可以实例化调用,也可以被继承类重写(Override) 4 | 5 | 它的作用是当实例类型从继承类转换为基类时,类中方法仍然是继承类中的实现。 6 | 7 | 8 | 如果不使用虚方法而使用普通方法隐藏基类方法,那么当实例类型转换时,编程了基类的实现。 9 | 10 | 抽象类(抽象方法)和接口类似,继承类都需要实现它们声明的函数。 11 | 抽象方法声明使用,是必须被派生类覆写的方法,抽象类就是用来被继承的;可以看成是没有实现体的虚方法;如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法;抽象类不能有实体的。 12 | 13 | 相同点: 14 | (1) 都可以被继承 15 | (2) 都不能被实例化 16 | (3) 都可以包含方法声明 17 | (4) 派生类必须实现未实现的方法 18 | 19 | 不同点: 20 | (1) 抽象基类可以定义字段、属性、方法实现;接口只能定义属性、索引器、事件、和方法声明,不包含字段以及方法实现。 21 | (2) 抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。 22 | (3) 接口可以被多重实现,抽象类只能被单一继承 23 | (4) 抽象类的成员可以具有访问级别,而接口的成员全部public级别 24 | (5) 抽象的子类可以选择性实现其基类的抽象方法,而接口的子类必须实现 25 | (6) 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法 26 | (7) 接口可以用于支持回调,而继承并不具备这个特点 27 | (8) 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的 28 | (9) 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法 29 | -------------------------------------------------------------------------------- /C#/链表问题: -------------------------------------------------------------------------------- 1 | 链表问题的解决思路: 2 | 1)创建一个虚拟头结点(主要用于头结点可能被改变的场景之下),以便链接到节点之中 3 | 4 | 5 | 快慢指针: 6 | 7 | 一般思路: 8 | 快慢指针定义两根指针,移动速度一快一慢。这个差值找到链表相应的节点。 9 | 10 | 快指针先走N步,然后再快指针和慢执着呢一起走,当快指针走完全程,慢指针就是倒数第N个。 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Json: -------------------------------------------------------------------------------- 1 | 2 | 需要对存储的类进行序列化: 3 | [System.Serializable] 4 | public class Item 5 | { 6 | public string itemName; 7 | public string itemDesc; 8 | public int itemID; 9 | public int goldValue; 10 | public float power; 11 | 12 | } 13 | 14 | 需要进行J序列化:使用LitJson 15 | 16 | public static class JsonUtil 17 | { 18 | 19 | public static string ToJson(T obj) 20 | { 21 | 22 | 23 | } 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 provencesl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lambda表达式: -------------------------------------------------------------------------------- 1 | Lambda表达式: 2 | 3 | 4 | 5 | 1)表达式Lambda: 6 | 7 | 泛型集合List中的Find()用于查找集合中符合指定条件的元素。 8 | 相比foreach便利元素,使用Find()查找,代码更加简洁。 9 | 10 | 函数原型:public T Find(Predicate match) 11 | 其中,Predicate为C#定义号的委托,原型如下: 12 | public delegate bool Predicate(T)(T obj) 13 | 14 | 15 | 16 | List.Find()的入参match,就是一个返回值为Bool的,入参为T类型函数。 17 | 18 | List list = new List(); 19 | 20 | //Person p = list.Find(); 21 | 22 | //匿名函数 23 | Person p = list.Find(delegate(Person s){ 24 | return s.Name.Equals("wang"); 25 | }) 26 | 27 | //Lambda表达式 28 | Person p = list.Find(s=>{ 29 | return s.Name.Equals("赵") 30 | }); 31 | //Lambda表达式简洁写法 32 | Person p = list.Find(s=>s.Name.Equals("赵")); 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity_Interview_Analysis 2 | Unity3D开发面试的刷题和记录 3 | -------------------------------------------------------------------------------- /RPG_角色属性设计: -------------------------------------------------------------------------------- 1 | 属性通常分为: 2 | 1)基础属性 BaseAttribute 3 | 2)附加属性 AdditiveAttribute 4 | 3)当前属性 CurrentAttribute 5 | 4)升级属性 GrowthAttribute 6 | 5)总属性 SumAttribute 7 | 8 | 9 | [System.Serializable] 10 | public class BaseAttribute 11 | { 12 | //等级 13 | public int LV; 14 | /// 15 | ///血量,魔量 16 | /// 17 | public float HP, MP; 18 | /// 19 | ///攻击力, 防御力, 力量, 智力, 敏捷, 体力 20 | /// 21 | public float Attack, Defend, Strong, Intelligence, Agility, Power; 22 | /// 23 | /// 攻击速度,散步速度,跑步速度,后退速度,转身移动速度,转身速度,攻击范围,经验值,暴击概率,暴击伤害百分比; 24 | /// 25 | public float AttackSpeed, MoveSpeed, RotationSpeed, AttackRange, Exp, CriticalRate, CriticalDamage; 26 | /// 27 | /// 每秒血量回复,每秒魔量回复 28 | /// 29 | public float HPRecoverPerSecond, MPRecoverPerSecond; 30 | } 31 | 32 | [System.Serializable] 33 | public class AdditionAttribute 34 | { 35 | /// 36 | ///血量,魔量 37 | /// 38 | public float HP, MP; 39 | /// 40 | ///攻击力, 防御力, 力量, 智力, 敏捷, 体力 41 | /// 42 | public float Attack, Defend, Strong, Intelligence, Agility, Power; 43 | /// 44 | /// 攻击速度,散步速度,跑步速度,后退速度,转身移动速度,转身速度,攻击范围,暴击概率,暴击伤害百分比;//暴击概率和攻击范围仅由装备影响。 45 | /// 46 | public float AttackSpeed, MoveSpeed, RotationSpeed, AttackRange, CriticalRate,CriticalDamage; 47 | 48 | /// 49 | /// 每秒血量回复,每秒魔量回复 50 | /// 51 | public float HPRecoverPerSecond, MPRecoverPerSecond; 52 | } 53 | 54 | [System.Serializable] 55 | public class CurAttribute 56 | { 57 | /// 58 | ///血量,魔量 59 | /// 60 | public float CurHP, CurMP; 61 | /// 62 | ///攻击力, 防御力, 力量, 智力, 敏捷, 体力 63 | /// 64 | public float CurAttack, CurDefend, CurStrong, CurIntelligence, CurAgility, CurPower; 65 | /// 66 | /// 当前攻击速度,当前散步速度,当前跑步速度,当前后退速度,当前转身移动速度,当前转身速度,当前攻击范围,当前暴击概率, 当前暴击伤害百分比; 67 | /// 68 | public float CurAttackSpeed, CurMoveSpeed, CurRotationSpeed, CurAttackRange, CurCriticalRate, CurCriticalDamage; 69 | 70 | /// 71 | /// 每秒血量回复,每秒魔量回复 72 | /// 73 | public float HPRecoverPerSecond, MPRecoverPerSecond; 74 | } 75 | 76 | [System.Serializable] 77 | public class SumAttribute 78 | { 79 | /// 80 | ///血量,魔量 81 | /// 82 | public float HP, MP; 83 | /// 84 | ///攻击力, 防御力, 力量, 智力, 敏捷, 体力 85 | /// 86 | public float Attack, Defend, Strong, Intelligence, Agility, Power; 87 | /// 88 | /// 攻击速度,移动速度,转身速度,攻击范围,暴击概率,暴击伤害百分比; 89 | /// 90 | public float AttackSpeed, MoveSpeed, RotationSpeed, AttackRange, CriticalRate, CriticalDamage; 91 | 92 | /// 93 | /// 每秒血量回复,每秒魔量回复 94 | /// 95 | public float HPRecoverPerSecond, MPRecoverPerSecond; 96 | } 97 | 98 | [System.Serializable] 99 | public class AttributeGrowth 100 | { 101 | /// 102 | ///血量,魔量 103 | /// 104 | public float HP, MP; 105 | /// 106 | ///攻击力, 防御力, 力量, 智力, 敏捷, 体力 107 | /// 108 | public float Attack, Defend, Strong, Intelligence, Agility, Power; 109 | } 110 | 111 | 112 | 还需要定义一些单独存在的量:比如最高等级,最大经验值、当前等级所需经验,如果有各种动画状态,还需要单独定义步行速度(WalkSpeed)、跑步速度(RunSpeed)、左右转身时向左或向右移动的速度(TurnSpeed),或者还可以定义后退时的移动速度(BackwardSpeed),看个人需求。 113 | 114 | 115 | ==========================分割线=========================== 116 | 117 | 以上都只是属性的定义:还需要设计属性和角色的动态变化。 118 | 其实核心就是 接受属性的变化罢了。而接受属性之前,这些值在不同模块之间传递。 119 | 120 | 121 | 122 | 123 | 124 | 任务: 125 | 设计角色属性和属性编辑器 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /Shader/Outline Shader初步: -------------------------------------------------------------------------------- 1 | 三种实现方式: 2 | 1)基于Sobel和色彩(Sobel Color)的实现,基于Sobel和深度纹理的实现(Sobel Depth)和基于Laplacian的深度加上法线(Laplacian Depth and Normal)的实现。 3 | 4 | 5 | 边缘检测都是在Pixel(Fragment) Shader实现,Vertex Shader不需要处理。 6 | 7 | 8 | Sobel Color: 9 | 给模型上不同颜色的边界都描边,2D中较为常见。 10 | 11 | 12 | 13 | 描边效果 由简入深来说: 14 | 15 | 1)实现思路: 判断像素周围是否有透明度为0的值,如果有则表明像素处于边缘位置。 16 | 17 | Tags{ 18 | "Queue" = "Transparent" 19 | } 20 | Blend SrcAlpha OneMinusSrcAlpha 21 | 22 | 主要处理的就是片元着色器。 23 | 24 | fixed frag(v2f i):SV_Target 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Shader/Shader-新手引导(椭圆遮罩): -------------------------------------------------------------------------------- 1 | 实现思路: 2 | 3 | 遮挡点击可以利用Image组件的RaycastTarget.只有透明部分让鼠标可以穿透,就可以复写Image的IsRaycastLocationValid方法.计算是否点击在椭圆内。 4 | 5 | //对图片进行Mask处理,只有椭圆部分 6 | Shader "Unlit/Transparent Colored Mask UnRound" 7 | { 8 | 9 | 10 | 11 | 12 | 13 | } 14 | 15 | 参考链接:https://blog.csdn.net/thinbug/article/details/87168796 16 | -------------------------------------------------------------------------------- /Shader/Shader之RenderType: -------------------------------------------------------------------------------- 1 | Opaque: 用于大多数着色器(法线着色器、自发光着色器、反射着色器以及地形的着色器)。 2 | Transparent:用于半透明着色器(透明着色器、粒子着色器、字体着色器、地形额外通道的着色器)。 3 | TransparentCutout: 蒙皮透明着色器(Transparent Cutout,两个通道的植被着色器)。 4 | Background: Skybox shaders. 天空盒着色器。 5 | Overlay: GUITexture, Halo, Flare shaders. 光晕着色器、闪光着色器。 6 | TreeOpaque: terrain engine tree bark. 地形引擎中的树皮。 7 | TreeTransparentCutout: terrain engine tree leaves. 地形引擎中的树叶。 8 | TreeBillboard: terrain engine billboarded trees. 地形引擎中的广告牌树。 9 | Grass: terrain engine grass. 地形引擎中的草。 10 | GrassBillboard: terrain engine billboarded grass. 地形引擎何中的广告牌草。 11 | -------------------------------------------------------------------------------- /Shader/Shader之UV: -------------------------------------------------------------------------------- 1 | UV是什么? 2 | 对于三维模型来说,最重要的坐标系统,一个是顶点的位置(X,Y,Z)的坐标,另外一个就是UV坐标。 3 | 4 | UV,其实就是贴图映射到模型表面。完整的说,就是UVW。 5 | U代表显示器水平,V代表显示器垂直方向的坐标。取值范围 0 ~ 1 6 | 7 | UV就是将图像上每一个点精确对应到模型物体的表面. 在点与点之间的间隙位置由软件进行图像光滑插值处理. 这就是所谓的UV贴图. 8 | -------------------------------------------------------------------------------- /Shader/Shader之着色器内置属性: -------------------------------------------------------------------------------- 1 | [HideInInspector] 2 | 3 | [NoScaleOffset] 4 | 隐藏纹理数据格式(3D/2D/Cube)的倾斜、偏移字段:Tilling或Ofset 5 | 6 | [Normal] 7 | 8 | [PerRendererData]:纹理数据格式(3D/2D/Cube) 9 | 目标的数据MaterialPropertyBlock类被初始化的实例值。 10 | 主要在从脚本动态设置纹理数据时才被使用。 11 | 12 | 13 | 14 | 滑动条 15 | [PowerSlider]:数值在一定范围内移动 16 | PowerSlider() 17 | 18 | 示例: 19 | Properties 20 | { 21 | [PowerSlider(0.5)]_PosX("PositionX",Range(0.01,1)) = 0 22 | _PosY("positionY",Range(0.01,1)) = 0 23 | [PowerSlider(2.0)]_PosZ("PositionZ",Range(0.01,1)) = 0 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Shader/Shader之纯着色器: -------------------------------------------------------------------------------- 1 | 着色器:其实就是描述顶点和像素特征的程序。 2 | 3 | 整个着色器渲染流程: 4 | 5 | Mesh Info 6 | (Vertices,UVs) 7 | || 8 | Vertex Shader 9 | (Places Vertices On the Screen):场景中绘制顶点空间 10 | || 11 | Pixel Shader 12 | (Render Mesh):在空间中描绘Mesh 13 | 14 | 顶点着色器每个顶点运行一次,并且设置对象在屏幕上的位置。还准备了像素着色器接下来要使用的数据(如UV) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Shader "ShaderTutorial/BaseColor" // Shader在面板上显示的名称 31 | { 32 | /* 33 | 材质面板属性 34 | */ 35 | Properties 36 | { 37 | /* 38 | 材质面板属性定义,格式:变量名("<显示变量名>",变量类型)=<缺省变量值> 39 | */ 40 | _BaseColor("BaseColor",Color) = (1,1,1,1) 41 | 42 | } 43 | 44 | /* 45 | 子着色器 46 | */ 47 | SubShader 48 | { 49 | Tags { "RenderType" = "Opaque" } // 标签,这里"RenderType"标明渲染物体类型为"Opaque",非透明物体 50 | LOD 100 // shader LOD(level of detail ) ,可以通过这个对画质进行分档 51 | 52 | Pass 53 | { 54 | 55 | CGPROGRAM // CG 代码段的开始标志 56 | #pragma vertex vert // 预编译指令,指示顶点着色器(vertex)使用名称为vert的函数 57 | #pragma fragment frag // 预编译指令,告诉编译器,片元着色器(frament)使用名称为frag的函数 58 | 59 | #include "UnityCG.cginc" // 预编译指令,引用文件"UnityCG.cginc",这里包含一些预定义的一些变量和函数 60 | 61 | 62 | /* 63 | 变量声明,前面Properties中定义的,暴露给编辑器材质面板的变量, 64 | 必须在这里再次声明,因为这里才是真正的变量声明位置 65 | */ 66 | half4 _BaseColor; 67 | 68 | 69 | /* 70 | 结构体,这个结构体是顶点着色器的输入数据, 71 | 是从应用阶段(cpu)传输给顶点处理阶段(gpu)的顶点属性, 72 | 因此这里命名为appdata,其他任意名称也ok 73 | */ 74 | struct appdata 75 | { 76 | /* 77 | 这里变量声明后加的":POSITION",是语义说明, 78 | 用来告诉GPU,顶点的坐标放在positionOS这个变量里。 79 | */ 80 | float4 positionOS : POSITION; 81 | }; 82 | 83 | /* 84 | 结构体,这个结构体是顶点着色器的输出以及片元着色器的输入,因此命名为v2f(vector to fragment ),其他任意名称也ok 85 | */ 86 | struct v2f 87 | { 88 | float4 positionCS : SV_POSITION; // 这里的语义说明":SV_POSITION",是告诉GPU,把光栅化阶段之后的裁剪空间顶点坐标放在positionCS这个变量里。 89 | }; 90 | 91 | /* 92 | 顶点着色器函数,每个模型顶点需要调用一次该顶点着色器, 以appdata作为输入,v2f作为输出 93 | */ 94 | v2f vert (appdata v) 95 | { 96 | v2f o; 97 | 98 | /* 99 | 使用UnityCG.cginc中定义的函数,UnityObjectToClipPos(), 100 | 把模型空间的顶点坐标转换到裁剪空间 101 | */ 102 | o.positionCS = UnityObjectToClipPos(v.positionOS); 103 | /* 104 | 上述函数的实现如下, 105 | 实际上就是把顶点坐标先从模型空间转换到世界空间(乘以模型空间到世界空间的变换矩阵-unity_ObjectToWorld), 106 | 然后从世界空间转换到齐次裁剪空间(世界空间坐标,乘以观察投影矩阵-UNITY_MATRIX_VP)。 107 | 108 | 这里需要注意的是, 109 | 很多教程中会告诉你使用UNITY_MATRIX_MVP矩阵直接乘以模型空间坐标, 110 | 这种方式虽然结果上是正确的, 111 | 但是因为UNITY_MATRIX_MPV矩阵实际上是UNITY_MATIRX_VP和unity_ObjectToWrold相乘的预编译指令, 112 | 因此直接使用MVP矩阵会导致运算为4x4矩阵与4x4矩阵的相乘结果再乘以四维向量, 113 | 运算次数远多于4x4矩阵乘以两次四维向量。 114 | 115 | 因此建议使用unity内置的UnityObjectToClipPos方法或者下面的实现,进行空间变换,可以得到更好的性能 116 | 117 | o.positionCS = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(v.positionOS.xyz, 1.0))); 118 | 119 | */ 120 | return o; 121 | } 122 | /* 123 | 片元着色器函数,以v2f作为输入,这里v2f,是经过光栅化之后的插值过的v2f。 124 | 每一个片元(类似于屏幕像素,只是此时还没有输送给屏幕颜色缓冲)都需要执行一次片元着色器 125 | */ 126 | half4 frag (v2f i) : SV_Target 127 | { 128 | /* 129 | "SV_Target"同样是语义说明,用来标记返回类型half4, 130 | 说明这里返回结果保存到SV_Target0(这里0省掉了), 131 | 指向的颜色缓冲区的对应位置 132 | */ 133 | 134 | /* 135 | 这里的返回值使用了half4,而不是常见的fixed4格式, 136 | 因为目前的几乎所有显卡都不再支持fixed4类型格式, 137 | 而编译器会内部把fixed 类型 转为 half 类型。 138 | 139 | 但是除非你非常了解unity shader编译器在目标平台的编译结果, 140 | 建议使用half4代替fixed4类型。 141 | */ 142 | 143 | half4 col = _BaseColor.rgba; 144 | /* 145 | 把我们定义的变量类型_BaseColor赋值给col,并返回。 146 | 这里".rgba"的写法或者".xyzw"是CG或者HLSL语言对向量的特有操作, 147 | 表示把向量的指定分量(例如 .r .xy .xx .a 等 赋值给对应变量) 148 | */ 149 | return col; 150 | } 151 | ENDCG // CG代码段的结束标志 152 | } 153 | } 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /Shader/Shader之透明度混合: -------------------------------------------------------------------------------- 1 | 通常来说,渲染半透明物体,使用透明度混合。 2 | 3 | 4 | 需要注意的几点: 5 | 1)设置标签:Tags{ "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} 6 | 将物体放入渲染队列 7 | 8 | 2.关闭深度写入:ZWrite Off 9 | 具体原因见下面 10 | 3.Blend混合命令:Blend SrcAlpha OneMinusSrcAlpha 11 | 12 | 意思是:最终颜色 = 源颜色(该像素颜色) * SrcAlpha + 目标颜色(颜色缓冲中的颜色) * (1 - SrcAlpha) 13 | -------------------------------------------------------------------------------- /Shader/Shader之遮罩层: -------------------------------------------------------------------------------- 1 | Shader遮罩层: 2 | 3 | Shader "ShaderDemo/MaskLayer" 4 | { 5 | Properties 6 | { 7 | _MainTex("Base(RGB)",2D) = "white"{} 8 | _MaskLayer("Culling Mask",2D) = "white"{} 9 | _Cutoff("Alpha cutoff",Range(0,1)) = 0 10 | } 11 | 12 | SubShader 13 | { 14 | 15 | } 16 | 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Shader/Shader之闪光Logo: -------------------------------------------------------------------------------- 1 | 透明度混合实现闪光: 2 | 3 | 4 | 5 | https://blog.csdn.net/candycat1992/article/details/25720243 6 | -------------------------------------------------------------------------------- /Shader/Shader之顶点着色器: -------------------------------------------------------------------------------- 1 | 顶点着色器输入: 2 | 3 | POSITION[n]:位置 4 | TEXCOORD[n]:纹理坐标 5 | 6 | 7 | 顶点着色器输出: 8 | POSITION[n]:位置 9 | FOG[n]:雾融合值 10 | COLOR[n]:顶点颜色 11 | TEXCOOR[]n:顶点纹理坐标 12 | 13 | 14 | 15 | struct adddata 16 | { 17 | float4 vertex:POSITION; //顶点坐标 18 | float2 uv: TEXCOORD0; //纹理uv坐标系的第0个集合 19 | } 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Shader/Shader基础光照: -------------------------------------------------------------------------------- 1 | 光纤与物体相交的结果: 2 | 1)散射(scattering) 3 | 2) 吸收(absorption) 4 | 5 | 散射:只去改变光线的方向,但不改变光线的密度和颜色。 6 | 7 | 两种方向:内部和外部,对应折射和反射。 8 | 折射:散射到物体内部,用漫反射(diffuse)模型来计算 9 | 反射:散射的物体外部,用高光反射(specular)模型来计算 10 | 11 | 吸收:只去改变光线的密度和颜色,但是步改变光线的方向 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Shader/Shader屏幕后处理: -------------------------------------------------------------------------------- 1 | Shader屏幕后处理: 2 | 3 | 屏幕后处理,通常指的是在渲染完整场景得到屏幕图像后,再对这个图像进行一系列操作,实现各种屏幕特效。 4 | 可以为游戏画面添加更多艺术效果。可实现低级的边缘检测、高斯模糊、Bloom效果(让屏幕中较亮的区域扩散到周围的区域中)、低级的运动模糊等效果。 5 | 6 | 复变函数中的卷积知识应用于边缘检测和高斯模糊: 7 | 8 | 使用GrabPass或者OnRenderImage函数来获取当前屏幕图像。Unity会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中, 9 | 下面我们可在自定义的Pass中把它们当成普通的纹理来处理。 10 | 11 | 12 | 使用深度和法线纹理来实现屏幕特效。 13 | 14 | 15 | 在顶点着色器中计算从切线空间到世界空间的变换矩阵,并把它传递个片元着色器。 16 | 变换矩阵的计算可以有顶点的切线、副切线和法线在世界空间下的表示来得到。 17 | 最后我们只需要在片元着色器中把法线纹理中的法线方向从切线空间变换到世界空间下即可。 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Shader/Shader屏幕后处理(2): -------------------------------------------------------------------------------- 1 | 屏幕后处理的技术手段: 2 | 1)利用卷积进行图像的边缘检测 3 | 2)高斯模糊 4 | 3)Bloom效果 5 | 4)运动模糊 6 | 5)径向模糊【Radial Blur】 7 | 8 | -------------------------------------------------------------------------------- /Shader/Shader屏幕坐标ComputeScreenPos: -------------------------------------------------------------------------------- 1 | 屏幕坐标通过ComputerScreenPos获取: 2 | 3 | ComputeScreenPos在UnityCG.cgine中被定义 4 | 首先,在顶点着色器中将ComputeScreenPos的结果保存在输出结构体之中。 5 | 然后再片元着色器中进行一个齐次除法运算后得到视口空间中的坐标。 6 | 7 | 8 | 9 | struct vertOut 10 | { 11 | float4 pos:SV_POSITION; 12 | float4 scrPos:TEXCOORD0; 13 | } 14 | 15 | vertOut vert(appdata_base v) 16 | { 17 | vertOut o; 18 | o.pos = mul(UNITY_MATRIX_MVP,v.vertex); 19 | //第一步:把ComputeScrenPos的结果保存到scrPos之中 20 | o.scrPos = ComputeScreenPos(o.pos); 21 | return o; 22 | } 23 | 24 | fixed4 frag(vertOut i):SV_Target 25 | { 26 | //第二步:用scrPos.xy除以scrPos.w得到视口空间的坐标 27 | float2 wcoord = (i.scrPos.xy/i.scrPos.w); 28 | return fixed4(wcoord,0.0,1.0); 29 | } 30 | 31 | 参考链接: 32 | https://www.bbsmax.com/A/kmzLbmbG5G/ 33 | -------------------------------------------------------------------------------- /Shader/Shader流光效果: -------------------------------------------------------------------------------- 1 | 游戏Logo看到流光效果,实现方案即为:带有光条的图片UV根据时间进行移动,然后和原图进行叠加实现。 2 | 3 | 4 | 基础版本: 5 | 6 | Shader "UICamera/ImageFlashEffect" 7 | { 8 | Properties 9 | { 10 | _MainTex("Main Texture",2D) = "white"{} 11 | _LightTex("Login Texture",2D) = "white"{} 12 | 13 | 14 | 15 | 16 | 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Shader/Shader消融: -------------------------------------------------------------------------------- 1 | 主要是利用自己的规则决定是否剔除某像素点,既然针对像素点,自然需要处理片元着色器,{俗称为像素着色器(Pixel Shader)}。 2 | 3 | 4 | 最初的片元着色器/顶点着色器,属于UNLIT Shader[无光照着色器] 5 | 6 | 7 | 8 | 9 | 10 | 11 | 顶点/片元着色器渲染流程图: 12 | 13 | 1)网格数据 + 常数属性数据 14 | 15 | 常数属性数据包括: 资源,脚本,动画数据 16 | 网格数据包括:Mesh 17 | 通常使用结构体Struct把网格数据直接给顶点函数 18 | 19 | 20 | 2)顶点函数 21 | 22 | 顶点Vertex函数 23 | 用来构建对象。输入参数是网格数据,输出的是顶点到片元结构体 24 | Vertex To Fragment,简称v2f 25 | 26 | //构建对象,输入appdata,即组织好的网格数据 27 | //输入的顶点数据需要从对象空间转到屏幕空间 28 | 29 | //使用UnityObjectToClipPos函数方式直接处理顶点(vertex)信息。调用这个方法一般需要引入UnityCG.cginc预定义文件,通过#include “UnityCG.cginc”实现 30 | v2f vert(appdata v) 31 | { 32 | v2f o; 33 | //操纵对象的结构 34 | o.vertex = UnityObjectToClipPos(v.vertex); 35 | o.uv = TRANSFORM_TEX(v.uv,_MainTex); 36 | return o; 37 | 38 | } 39 | 40 | 3)顶点到片元结构体 41 | 42 | 43 | 44 | 45 | 46 | 47 | Shader"Unity Shader/Simple Shader"{ 48 | 49 | SubShader{ 50 | 51 | } 52 | 53 | Pass{ 54 | 55 | //CG语句 56 | CGPROGRAM 57 | 58 | //告诉unity函数vert包含顶点着色器 59 | #pragma vertex vert 60 | 61 | //告诉unity 函数frag包含片元着色器 62 | #pragma fragment frag 63 | 64 | 65 | float vert(float4 v:POSITION) : SV_POSITION{ 66 | 67 | return mul(UNITY_MATRIX_MVP,v); 68 | } 69 | 70 | float frag() : SV_Target{ 71 | //返回一个表示白色的fixed4类型变量,(R,G,B,A) 72 | return fixed4(1.0,1.0,1.0,1.0); 73 | } 74 | 75 | ENDCG 76 | 77 | } 78 | 79 | 80 | 81 | 82 | 83 | } 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 消融原理:[Discard] 102 | 1)使用噪声和透明度测试、 103 | 从噪声图中读取某个通道的值。 104 | 然后使用该值进行透明度测试。\ 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ///////开放变量 115 | 116 | Shader "Custom/Dissolve Shader" 117 | { 118 | Properties 119 | { 120 | _NoiseTex("Noise",2D)= "white"{} //Noise噪声贴图 121 | _RampTex("Ramp",2D) = "black"{} //渐变贴图 122 | _Dissolve("Dissolve",Range(0,1)) = 0 //消融贴图 123 | _Emission("Emission",float) = 1 //自发光贴图 124 | 125 | 126 | 127 | 128 | } 129 | 130 | } 131 | 132 | 133 | 134 | ///////设置渲染标签 135 | SubShader 136 | { 137 | Tags 138 | { 139 | "RenderType" = "Opaque" 140 | "Queue" = "AlphaTest" 141 | } 142 | 143 | LOD 200 144 | } 145 | //模型为透明,渲染类型Opaque 146 | //通过剔除渲染的方式实现消融,把渲染队列放在AlphaTest之中 147 | 148 | 149 | 150 | 151 | 核心代码: 152 | fixed cutout = text2D(_NoiseTex,i,uvNoiseTex).r; 153 | cli(cutout - _Threshold); 154 | 155 | 主要是利用自己的规则决定是否剔除某像素点,既然针对像素点,自然需要处理片元着色器。 156 | 提取像素需要使用UnityShder的clip() . [clip如果输入向量的任何元素小于0,则丢弃当前像素] 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /Shader/Shader绘制基本图形: -------------------------------------------------------------------------------- 1 | 模板: 2 | Draw.Shader 3 | 4 | 5 | 6 | Shader "Custom/Draw" 7 | { 8 | Properties 9 | { 10 | 11 | } 12 | SubShader 13 | { 14 | 15 | Pass 16 | { 17 | CGPROGRAM 18 | #pragma vertex vert 19 | #pragma fragment frag 20 | 21 | #include "UnityCG.cginc" 22 | 23 | struct appdata 24 | { 25 | float4 vertex : POSITION; 26 | }; 27 | 28 | struct v2f 29 | { 30 | float4 vertex : SV_POSITION; 31 | }; 32 | 33 | 34 | v2f vert (appdata v) 35 | { 36 | v2f o; 37 | o.vertex = UnityObjectToClipPos(v.vertex); 38 | return o; 39 | } 40 | 41 | fixed4 frag (v2f i) : SV_Target 42 | { 43 | return fixed4(1,1,1,1); 44 | 45 | } 46 | ENDCG 47 | } 48 | } 49 | } 50 | 51 | //////////////////////////////////// 52 | 53 | Shader代码编写: 54 | 1)绘制点或园形: 55 | 点可以认为是只有一个像素的圆形,所以我们统一认为是绘制圆形。为了在屏幕上绘制圆形,我们需要知道圆形的位置和半径。 56 | 57 | 58 | 在 Properties中添加要绘制的点位置信息和半径。 59 | _Point1("Point1",vector) = (100,100,0,0) 60 | _Point2("Point2",vector) = (200,200,0,0) 61 | 62 | SubShader中Pass添加: 63 | 64 | float4 _Point1; 65 | float4 _Point2; 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Shader/Shader读取2D纹理贴图: -------------------------------------------------------------------------------- 1 | Shader "ShaderTutorial/BaseTexture" // 名称改为BaseTexture 2 | { 3 | 4 | Properties 5 | { 6 | 7 | _BaseColor("BaseColor",Color) = (1,1,1,1) 8 | 9 | /* 10 | 定义一个2D纹理属性,缺省状态是一个白色的纹理 11 | */ 12 | _BaseMap("BaseMap",2D) = "White"{} 13 | 14 | } 15 | 16 | 17 | SubShader 18 | { 19 | Tags { "RenderType" = "Opaque" } 20 | LOD 100 21 | 22 | Pass 23 | { 24 | 25 | CGPROGRAM 26 | #pragma vertex vert 27 | #pragma fragment frag 28 | 29 | #include "UnityCG.cginc" 30 | 31 | 32 | 33 | half4 _BaseColor; 34 | sampler2D _BaseMap; // 声明2D纹理采样器 35 | float4 _BaseMap_ST; // 纹理对应的缩放和偏移参数,xy为缩放,zw为偏移 格式:纹理名_ST; 36 | 37 | 38 | struct appdata 39 | { 40 | float4 positionOS : POSITION; 41 | 42 | /* 43 | 语义TEXCOORD0,告诉GPU把顶点的第一套UV坐标填入texcoord变量中, 44 | */ 45 | float2 texcoord:TEXCOORD0; 46 | }; 47 | 48 | 49 | struct v2f 50 | { 51 | float4 positionCS : SV_POSITION; 52 | 53 | /* 54 | 这里的语义TEXCOORD0,TEXCOORD1,TEXCOORD2等, 55 | 虽然和appdata中的名称一样,但是意义不同, 56 | 这里是用来标记一个通用功能的向量,以便在光栅化中进行插值, 57 | 只是这里刚好用来保存第一套顶点UV坐标 58 | */ 59 | float2 texcoord:TEXCOORD0; 60 | }; 61 | 62 | 63 | v2f vert(appdata v) 64 | { 65 | v2f o; 66 | 67 | 68 | o.positionCS = UnityObjectToClipPos(v.positionOS); 69 | 70 | o.texcoord = TRANSFORM_TEX(v.texcoord, _BaseMap); 71 | /* 72 | 上述函数TRANSFORM_TEX定义如下: 73 | 74 | // Transforms 2D UV by scale/bias property 75 | #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw) 76 | 77 | 用来把顶点uv坐标进行偏移缩放操作,等价于: 78 | o.texcoord = v.texcoord.xy*_BaseMap_ST.xy+_BaseMap_ST.zw; 79 | */ 80 | 81 | return o; 82 | } 83 | 84 | half4 frag(v2f i) : SV_Target 85 | { 86 | /* 87 | tex2D是纹理采样函数,这里是使用坐标 i.texcoord 采样纹理_BaseMap, 88 | 获得texcoord处的颜色值 89 | */ 90 | half4 baseColor = tex2D(_BaseMap, i.texcoord); 91 | 92 | half4 col = half4 (_BaseColor.rgb*baseColor.rgb,1.0); 93 | /* 94 | 把我们定义的变量类型_BaseColor.rgb乘以纹理采样结果赋值给col,并返回。 95 | */ 96 | return col; 97 | } 98 | ENDCG 99 | } 100 | } 101 | } 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Shader/Shader读取纹理贴图: -------------------------------------------------------------------------------- 1 | Shader "Shader/BaseTexture" 2 | { 3 | Properties 4 | { 5 | _BaseColor("BaseColor",color) = (1,1,1,1) 6 | 7 | //定义2D纹理属性 8 | _BaseMap("BaseMap",2D) = "white"{} 9 | 10 | } 11 | 12 | 13 | SubShader 14 | { 15 | Tags{"RenderType"="Opaque"} 16 | LOD 100 17 | 18 | Pass 19 | { 20 | CGPROGRAM 21 | #pragma vertex vert 22 | #pragma fragment frag 23 | 24 | #include "UnityEngine.cgine" 25 | 26 | half4 _BaseColor; 27 | sampler2D _BaseMap; //声明2D纹理采样器 28 | float4 _BaseMap_ST; //纹理对应的缩放和偏移参数:xy为缩放,zw为偏移, 格式:纹理名_ST 29 | 30 | 31 | 32 | 33 | } 34 | 35 | 36 | 37 | } 38 | 39 | 40 | } 41 | 42 | 参考链接;https://blog.csdn.net/u013746357/article/details/92570102 43 | 44 | -------------------------------------------------------------------------------- /Shader/Shader透明效果: -------------------------------------------------------------------------------- 1 | 透明效果主要有两种实现方式: 2 | 1)透明度测试【无法实现真正的半透明效果】 3 | 2)透明度混合 4 | 5 | 透明度测试和透明度混合: 6 | 7 | 8 | 透明度测试:1)只有一个片元的透明度不满足条件(通常是小于某个阙值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理, 9 | 也不会对颜色缓冲产生任何影响。否则会被当做不透明处理,进行深度测试,深度写入等。所以透明度测试是不需要关掉深度写入的。 10 | 除了会舍弃片元,其实和不透明物体渲染差不多。 11 | 12 | 透明度混合: 13 | 这种方法可以得到真正的半透效果。它会使用当前片元的透明度作为混合因子,与颜色缓冲中的颜色进行混合,得到新的颜色。这里需要注意, 14 | 透明度混合需要关闭深度写入。但没有关闭深度测试,所以当混合渲染一个片元时,它还是会比较它的深度和深度缓冲中的深度, 15 | 如果它的深度值离摄像机更远,则不再进行混合操作。所以当不透明物体在透明物体前面时,先渲染了不透明物体,它仍可以遮挡主透明物体。 16 | 所以对于透明度混合来说,深度缓冲只是可读的。 17 | 18 | Shader 渲染顺序的问题: 19 | 20 | 一般渲染引擎内部会有机制: 21 | 1)先渲染所有不透明物体,并且开启深度测试和写入 22 | 2)把半透明的物体按照离摄像机远近顺序,按照从后往前顺序渲染,开启深度测试,关闭深度写入。 23 | 24 | 25 | 渲染队列,SubShader的Queue标签决定渲染顺序。 26 | 27 | Background: 28 | Geometry: 29 | AlphaTest: 30 | Transparent: 31 | Overlay: 32 | 33 | 34 | //透明度测试 35 | SubShader 36 | { 37 | Tags{"Queue" = "AlphaTest"} 38 | Pass{} 39 | 40 | } 41 | 42 | //透明度混合 43 | SubShader 44 | { 45 | Tags{"Queue" = "Transparent"} 46 | Pass 47 | { 48 | ZWrite Off 49 | } 50 | } 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Shader/SpriteOutline Shader: -------------------------------------------------------------------------------- 1 | 2 | 3 | Shader "Sprites/Outline" 4 | { 5 | Properties 6 | { 7 | [PerRenderData]_MainTex("Sprite Texture",2D) = "white"{} 8 | _Color("Tint",Color) = (1,1,1,1) 9 | [MaterialToggle]PixelSnap("Pixel snap",Float) = 0 10 | 11 | 12 | 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Shader/UV之tilling和offset: -------------------------------------------------------------------------------- 1 | offset:贴图的起始位置,取值范围[0,1] 2 | 3 | tilling:从offset位置开始平铺图片,超过部分(即空白处),则按照比例生成新的区域拼接到空白处。取值[-1,1] 4 | 5 | -------------------------------------------------------------------------------- /Shader/UV动画: -------------------------------------------------------------------------------- 1 | Unity: 2 | 3 | _Time: float4 从场景加载开始经历的时间 4 | _SinTime:float4 时间的正弦值 5 | _CosTime:float4 t是时间的余弦值 6 | 7 | 8 | Properties 9 | { 10 | _Color("Color",Color) = (1,1,1,1) 11 | _MainTex("Sequence Image",2D) = “white”{} 12 | _Speed("Speed",Range(1,100)) = 50 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Shader/shader扭曲: -------------------------------------------------------------------------------- 1 | Unity2d Shader : 2 | 3 | 1)UI Shader 4 | 2)Screen Shader 屏幕后处理 5 | 3)Sprite Shader 精灵着色器 6 | 7 | 8 | 9 | 扭曲: 扰动一个物体的UV坐标,表现效果几位扰动效果,比如:火焰扭曲,水波等 10 | 11 | 1)Shader中需要当前渲染的纹理 12 | 2)给物体渲染并且使用当前纹理,用屏幕坐标作为UV坐标加上扰动值 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Shader 2D 扭曲 21 | 22 | fixed frag(v2f i):SV_Target 23 | { 24 | float2 uv = i.uv; 25 | 26 | //uv扭曲代码 27 | 28 | 29 | 30 | } 31 | 32 | UV扰动:是对一张噪声纹理采用加上偏移值,同时让噪声纹理做UV移动。达到每次采用不同来实现动态的效果。 33 | 34 | 35 | -------------------------------------------------------------------------------- /Shader/shader空间变换: -------------------------------------------------------------------------------- 1 | 建模是在模型空间进行,模型自带的坐标均为模型空间下的表示。 2 | 3 | 当模型被放到世界坐标系之中时,表达某个模型的位置使用的是世界空间下来的坐标。 4 | 所以,模型上对应的某个点,必须相应的转化为世界坐标下的坐标。 5 | 6 | 7 | 从模型空间到世界空间的变换叫做模型变换。 8 | 9 | 模型空间的坐标由Renderer直接提供,作为顶点着色器的输入,语义为POSITION, 10 | 如: 11 | struct appdata 12 | { 13 | float4 vertex:POSITION; 14 | } 15 | 16 | 能看到的渲染图像均是通过摄像机得到的,为了方便后续裁剪、投影灯操作,将模型从模型空间变换到 17 | 世界空间之后,需要将其转换到观察空间。所谓观察空间,就是以摄像机位置为原点。 18 | 19 | 从世界空间到观察空间的变换叫做观察变换[视图变换]。 20 | 21 | 22 | 23 | (2)屏幕空间 24 | 25 | 经过裁剪操作之后,将裁剪空间的坐标投影到屏幕空间。 26 | 需要分割为两个步骤: 27 | 1)齐次除法: 28 | 使用xyz分别处以w分量,得到NDC(归一化设备坐标),经过这一步,能够看到坐标点变成一个 29 | xy边长为1,z为-1到1的立方体。 30 | 2)屏幕映射: 31 | 使用NDC坐标和屏幕长宽像素直接映射,得到屏幕空间的xy坐标。注意,屏幕空间没有深度,但是屏幕空间下的坐标 32 | 33 | 34 | 35 | 参考链接: 36 | https://my.oschina.net/u/918889/blog/1858627 37 | -------------------------------------------------------------------------------- /Shader/shader计算像素点距离: -------------------------------------------------------------------------------- 1 | Shader "Custom/SetColorInFrag"{ 2 | 3 | Properties{ 4 | _ZeroPoint("ZeroPoint",Vector) = (0,0,0,0) 5 | _Distance() 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /Shader/屏幕后处理Camera的Pipeline: -------------------------------------------------------------------------------- 1 | 1)Pipeline中的深度数据: 2 | 3 | 深度是渲染管线当中的重要概念,控制这三维世界中物体渲染的遮挡关系。 4 | 5 | 以传统的forward render为例,三角形提交drawcall,顶点进入vertex shader, 6 | 经过model-view-projection matrix,变换到齐次裁剪空间坐标系, 7 | 通过透视出发,进入pixel Shader。 8 | 9 | 10 | -------------------------------------------------------------------------------- /Shader/渲染管线流程: -------------------------------------------------------------------------------- 1 | Start---- 2 | 3 | 顶点-模型空间 ---- 4 | 5 | (顶点着色器) 6 | 顶点转换(模型空间-》齐次裁剪空间) 7 | (自定义操作) ---- 8 | 9 | 顶点裁剪(视锥体裁剪) 10 | 背面剔除(CULL) 11 | 图元装配 12 | 光栅化片段------ 13 | 14 | 片段着色器 15 | 模板擦拭 (Stencil Test) 16 | 深度测试(Depth Test) 17 | 透明测试(Alpha Test) 18 | 混合测试(Blend Test) 19 | 更新帧缓存中颜色值 20 | 21 | ----End 22 | 23 | -------------------------------------------------------------------------------- /Shader/计算机图形学-三大测试: -------------------------------------------------------------------------------- 1 | Alpha测试: 符合条件的Alpha像素显示出来,不符合的去掉。 2 | 3 | Stencil模板测试:符合条件的通过,不符合条件的像素丢弃掉。 4 | 5 | -------------------------------------------------------------------------------- /Shader/顶点变换: -------------------------------------------------------------------------------- 1 | 顶点换换的目的: 2 | 3 | 本地坐标系 4 | | 5 | 世界坐标系 6 | | 7 | 相机坐标系 8 | |—————————————— 透视投影 裁剪空间 屏幕坐标系 9 | |——————————————正交投影 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Shader/高斯模糊的实现: -------------------------------------------------------------------------------- 1 | 高斯模糊的实现: 2 | 3 | 思路:GrabPass实现毛玻璃[性能消耗太大,不适合在移动端使用] 4 | 5 | 从FrameBuffer抓取物体所在位置屏幕中将要绘制的内容,将其填充进入一个临时的texture, 6 | 以供同一个Shader中气候的Pass使用。 7 | 8 | Shader声明的属性: 9 | 10 | 11 | Properties 12 | { 13 | _BumpAmt("Distortion",range(0,2)) = 1 14 | TintAmt("Tint Amount",Range(0,1)) = 0.1 15 | _TintColor("Tint Color",Color)) = (1,1,1,1) 16 | _MainTex("Tint Texture(RGB)",2D) = "white"{} 17 | _BumpMap("Normalmap",2D) = "bump"{} 18 | _BlurAmt("Blur",Range(0,10)) = 1 19 | 20 | 21 | } 22 | 23 | 24 | 25 | 移动端使用CommandBuffer: 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 | 1)通道0:降低采样通道 51 | 2)通道1:垂直方向模糊处理通道 52 | 3)通道2:水平方向模糊处理通道 53 | 54 | 55 | -------------------------------------------------------------------------------- /Shader之Sprite Outline: -------------------------------------------------------------------------------- 1 | Shader "Unit/InnerSpriteOutline" 2 | { 3 | Properties 4 | { 5 | _MainTex("Texture",2D) = "white"{} 6 | 7 | _OutlineColor("Outline Color",Color) = (1,1,1,1) 8 | } 9 | 10 | SubShader 11 | { 12 | Tags 13 | { 14 | "RenderType" = "Transparent" 15 | } 16 | 17 | Pass 18 | { 19 | CGPROGRAM 20 | #pragma vertex vert 21 | #pragma fragment frag 22 | 23 | 24 | 25 | #include "UnityCG.cgine" 26 | 27 | struct appdata 28 | { 29 | float4 vertex:POSITION; 30 | float2 uv:TEXCORRD0; 31 | } 32 | 33 | struct v2f 34 | { 35 | float2 uv:TEXCOORD0; 36 | FLOAT4 VERTEX: SV_POSITION; 37 | 38 | } 39 | 40 | sampler2D _Maintex; 41 | float4 _MainTex_ST; 42 | float4 _MainTex_TexelSize; 43 | 44 | float4 _OutlineColor; 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | } 53 | 54 | 55 | 56 | } 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Shade之屏幕后处理(1): -------------------------------------------------------------------------------- 1 | 双摄像机分别渲染彩色主角和黑白环境: 2 | 3 | 渲染环境的camera: 4 | 5 | 获得摄像机画面之后再套上灰度公式化成黑白色 6 | 7 | Shader "Myuse/FogGreyShader" 8 | { 9 | Properties 10 | { 11 | _MainTex("Texture",2D) = "white"{} 12 | 13 | } 14 | 15 | SubShader 16 | { 17 | Tags 18 | { 19 | "RenderType" = "Opaque" 20 | } 21 | LOD 100 22 | 23 | Pass 24 | { 25 | CGPROGRAM 26 | #pragma vertex vert 27 | #pragma fragment frag 28 | 29 | 30 | #include "UnityCG.cginc" 31 | struct appdata 32 | { 33 | float4 vertex:POSITION; 34 | float2 uv:TEXCOORD0; 35 | }; 36 | 37 | struct v2f 38 | { 39 | float2 uv:TEXCOORD0; 40 | float4 vertex:SV_POSITION; 41 | float4 scrPos:TEXCOORD1; 42 | }; 43 | 44 | sampler2D _MainTex; 45 | float4 _MainTex_ST; 46 | 47 | v2f vert(appdata v) 48 | { 49 | v2f o; 50 | o.vertex = UnityObjectToClipPos(v.vertex); 51 | o.uv = TRANSFORM_TEX(v.uv,_MainTex); 52 | o.scrPos = ComputeScreenPos(o.vertex); 53 | return o; 54 | } 55 | 56 | fixed frag(v2f i):SV_Target 57 | { 58 | //灰度grey 59 | fixed selfColor = tex2Dproj(_MainTex,i.scrPos); //原色 60 | float gray = (selfColor.r * 30 + selfColor.g * 59 + selfColor.b * 11 + 50)/ (100); //灰度转化公式 61 | fixed4 c = fixed4(gray,gray,gray,1); 62 | return c; 63 | } 64 | 65 | ENDCG 66 | } 67 | 68 | } 69 | } 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Unity/Animation配置: -------------------------------------------------------------------------------- 1 | Animation之HasExitTime: 2 | 勾选项:有退出时间,即当前动画要播放完毕后才能向下一步动画 3 | 不勾选:无退出时间,即只要满足条件即可立即跳转 4 | 5 | 一般不要勾选HasExitTime 6 | 7 | 8 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssertBundle基本理论: -------------------------------------------------------------------------------- 1 | 1)最简单的AssertBundle打包方式: 2 | 3 | 4 | Unity需要加载模型经常需要AssertBundle。 5 | 6 | 在Unity中载入模型的最简单的方式就是:直接将模型文件放到工程目录下,然后拖进场景之中。 7 | 但是我们需要在程序运行时将网络或者任意目录下的模型加载到场景之中,name我们只能自己编写解析模型的脚本。 8 | 9 | Unity没有提供加载模型的API,或者是没有开放给用户 。。。 10 | 11 | Unity中模型文件大多是.prefab,加载prefab的方式只有两个: 12 | 1)Resource.Load,需要将prefab放到Resource目录下 13 | 2)AssertDatabase.LoadAssertAtPath ,文件必须放在Asserts目录下,即网络和其他文件夹下prefab无法加载 14 | 15 | 16 | 这时,assertBundle发挥除了很大用处。通过WWW加载AssertBundle。 17 | 接下来的问题就是如何打包AssertBundle, 18 | 19 | 利用 20 | public static AssetBundleManifest BuildAssetBundles 21 | (string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform); 22 | 23 | 24 | 在Editor文件夹下创建脚本,放入脚本,这时才会出现AssertBundle选项 25 | 26 | 27 | 28 | AssertBundle功能开发好后,怎么用呢??? 29 | 30 | 31 | AssertBundle需要对资源进行分组: 32 | 1)按逻辑实体进行分组: 33 | 一个UI界面或者所有UI界面一个包(这个界面里面的贴图和布局信息一个包) 34 | 一个角色或者所有角色一个包(这个角色里面的模型和动画一个包) 35 | 所有的场景所共享的部分一个包(包括贴图和模型) 36 | 37 | 2)按资源类型分组: 38 | 所有声音资源打成一个包 39 | 所有shader打成一个包 40 | 所有模型打成一个包 41 | 所有材质打成一个包 42 | 3)按照使用分组: 43 | 把需要同时加载的资源或者某一时间内使用的所有资源打成一个包。 44 | 可以按照关卡分,一个关卡所需要的所有资源包括角色、贴图、声音等打成一个包。 45 | 也可以按照场景分,一个场景所需要的资源一个包。 46 | 47 | 解包: 48 | 49 | IEnumerator LoadBundle(string url) 50 | { 51 | WWW www = new WWW(url); 52 | yield return www; 53 | 54 | if(www.error != null) 55 | { 56 | Debug.LogError(); 57 | yield break; 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | 65 | https://blog.csdn.net/puss0/article/details/79690523 66 | 67 | https://blog.csdn.net/puss0/article/details/79684185 68 | https://blog.csdn.net/puss0/article/details/79681013 69 | 70 | 微夏博客网 71 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssertBundle资源加载框架设计: -------------------------------------------------------------------------------- 1 | 目的: 2 | 1)便于增量更新功能的实现 3 | 2)降低安装包大小,unity不支持过滤到Resources目录下的不用的文件 4 | 3)用AB包加快资源文件读取速度 5 | 6 | 生成AssertBundle文件的基本流程: 7 | 1)所有需要打包成为AB包的资源放到BundleResources目录下,按照一定规则存放 8 | 2)便利BundleResources目录,。根据一定规则,给不同的文件或者目录孵育一个AssertLabels 9 | 3)打包,输出目录为StreamingAsserts 10 | 4)打包,遍历后生成的manifest信息,获取每个AB包的包名,根据依赖的AB包的HASH值,输出到一个JSON文件 11 | 12 | https://blog.csdn.net/niuzb/article/details/52848398 13 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssetBundleManager管理: -------------------------------------------------------------------------------- 1 | public class AssetBundleManager 2 | { 3 | public static AssetBundleManager Instance 4 | { 5 | get 6 | { 7 | if(_instance == null) 8 | _instance = new AssetBundleManager(); 9 | return _instance; 10 | } 11 | 12 | private static AssetBundleManager _instance = null; 13 | 14 | private AssetBundleManiest maniest = null; 15 | 16 | //存储待加载的AssetBundle集合 17 | private Dictionary dicAssetBundles = new Dictionary(); 18 | 19 | 20 | //fileName:Assets全路径,比如:Assets/Prefab/**.prefab 21 | public AssetBundle GetAssetBundle(string filePath) 22 | { 23 | AssetBundle ab = null; 24 | dicAssetBundle.TryGetValue(AssetsNameToBundleName(filePath)); 25 | return ab; 26 | 27 | } 28 | 29 | private string AssetsNameToBundleName(string file) 30 | { 31 | string f = file.Replace('/','.'); 32 | f = f.ToLower(); 33 | f += ".assetbundle"; 34 | return f; 35 | } 36 | 37 | public IEnumerator LoadAsync(string fileName) 38 | { 39 | string bundleName = AssetsNameToBundleName(filename); 40 | if(dicAssetBundle.ContainsKey(bundleName)) 41 | { 42 | yield break; 43 | } 44 | 45 | string[] dependence = manifest.GetAllDependencies(bundleName); 46 | for(int i = 0;i < dependence.Length;i++) 47 | { 48 | yield return LoadInternalAsync(dependence[i]); 49 | 50 | } 51 | 52 | yield return LoadInternalAsync(bundleName); 53 | 54 | 55 | } 56 | 57 | public void Unload(string fileName,bool force = false) 58 | { 59 | string bundleName = AssetsNameToBundleName(fileName); 60 | AssetBundle ab = null; 61 | if(dicAssetBundle.TryGetValue(bundleName,out ab) == false) 62 | return; 63 | 64 | if(ab == null) 65 | return; 66 | 67 | 68 | 69 | } 70 | 71 | //从缓存中加载 72 | private AssetBundle LoadInternal(string bundleName) 73 | { 74 | if(dicAssetBundle.ContainsKey(bundleName)) 75 | { 76 | return dicAssetBundle[bundleName]; 77 | } 78 | 79 | //同步加载 80 | AssetBundle bundle = AssetBundle.LoadFromFile(bundleName); 81 | dicAssetBundle.Add(bundleName,bundle); 82 | return bundle; 83 | 84 | } 85 | 86 | 87 | private IEnumerator LoadInternalAsync(string bundleName) 88 | { 89 | if(dicAssetBundle.ContainsKey(bundleName)) 90 | yield break; 91 | 92 | //异步加载 93 | AssetBundleCreateRequest req = AssetBundle.LoadFromileAsync() 94 | yield return req; 95 | 96 | dicAssetBundle.Add(bundleName,req.assetBundle); 97 | 98 | 99 | } 100 | 101 | private void Release() 102 | { 103 | foreach(var pair in dicAssetBundle) 104 | { 105 | var bundle = pair.Value; 106 | if(bundle != null) 107 | { 108 | //卸载资源 109 | bundle.Unload(true); 110 | bundle = null; 111 | } 112 | 113 | dicAssetBundle.Clear(); 114 | } 115 | 116 | } 117 | 118 | 119 | private string MainifestFilePath() 120 | { 121 | return Application.dataPath + "/StreamingAssets"; 122 | } 123 | 124 | 125 | public void UnLodUnUsedAssets() 126 | { 127 | Resources.UnLoadUnUsedAssets(); 128 | System.GC.Collect(); 129 | } 130 | 131 | 132 | 133 | 134 | public void LoadManifest() 135 | { 136 | AssetBundle bundle = AssetBundle.LoadFromFile(MainifestilePath()); 137 | bundle.LoadAsset("AssetBundleManiest") 138 | } 139 | 140 | //AssetBundle动态加载避免使用WWW 141 | private string BundleNameToBundlePath(string bundleFileName) 142 | { 143 | return System.IO.Path.Combine(Application.dataPath + "/StreamingAssets/",bundleFileName); 144 | } 145 | 146 | 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssetBundle之打包Prefab: -------------------------------------------------------------------------------- 1 | 将多个游戏对象(Prefab)或者二进制文件封装到AssetBundle中,提供封装和解包方法。 2 | 3 | 4 | 5 | 1)预设: 6 | 7 | Assetbundle可以将Prefab封装成AssetBundle,因为Prefab可以将游戏对象 8 | ,游戏脚本,材质封装在一起,当从服务器上将Assetbundle下载后直接Instantiate可以放入游戏。 9 | 10 | Prefab打包技巧: 11 | Prefab打包和关联的Prefab数量是无关的。 12 | 13 | 14 | 1)AssetBundle打包: 15 | 无论是模型FBX资源还是UI资源,最好先放在Prefab,然后再做成AssetBundle。 16 | 以模型来说, 17 | AssetBundle中可以存放多个模型。 18 | 比如:两个完全一样的模型,但是身上绑定的脚本不一样,需要两个模型放在不同Prefab。 19 | 20 | 将这两个Prefab打包,两个相同的Prefab放在一起打包会占用1M空间, 21 | 而且将他们分别打包会占用1+1 =2 M空间。Preefab打包时会将模型身上的所有材质,贴图和脚本全部包含进去。 22 | 23 | 将相同的模型尽量打包再一起,公用一套资源文件。 24 | 所以,将不同模型尽量分开打包,相同模型具有不同脚本、组件的话将他们放在不同prefab之中, 25 | 最后将Prefab打包进AssetBundle。 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssetBundle加载: -------------------------------------------------------------------------------- 1 | AssetBundle资源管理功能: 2 | 1)读取AssetBundle配置表 3 | 2) 设置中间类进行引用计数 4 | 3)根据路径加载AssetBundle 5 | 4) 根据路径卸载AssetBundle 6 | 5)为ResourceManager提供加载中间类,根据中间类释放资源 7 | 8 | 9 | 资源具有三种状态: 10 | 1)未加载 11 | 2)已加载 12 | 3)可释放 13 | 14 | 接下来要使用的资源就必须要加载到池中。接下来很长时间不需要使用的资源则彻底释放。 15 | 确保内存在可控范围。 16 | 17 | 18 | 设计: 19 | 为了达到这种目的,划分为三大模块去做。 20 | 1)最基础的资源加载和池 21 | 2)资源加载的自动记录过程 22 | 3)资源加载的动态加载和释放 23 | 24 | 25 | 26 | 27 | 28 | public class Test : MonoBehaviour 29 | { 30 | void Start() 31 | { 32 | AssetBundle obj = AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/test.cube"); 33 | if(obj == null) 34 | { 35 | Debug.Log("加载失败"); 36 | return; 37 | } 38 | else 39 | { 40 | GameObject a = obj.LoadAsset("cube"); //加载资源包文件 41 | Instantiate(a); //实例化读取对象 42 | obj.Unload(false); //shi8fang读取的对象,如果为true则释放全部资源 43 | } 44 | } 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssetBundle资源加载详解: -------------------------------------------------------------------------------- 1 | 1)AsyncLoading: 内存加载-异步加载 2 | 3 | 从内存加载: 4 | AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path1)) 5 | 6 | 7 | 2) SyncLoading:内存加载-同步加载(无需协程) 8 | 从内存中加载: 无需使用协程,直接完成资源加载 9 | 10 | AssetBundle.LoadFromMemory(File.ReadAllBytes(path1)) 11 | 12 | 3)LoadFromFile:本地加载 13 | AssetBundle.LoadFromMemory(File.ReadAllBytes(path1)) 14 | 15 | 4)LoadFromWWW:从本地/服务器加载 16 | AssetBundle.LoadFromMemory(File.ReadAllBytes(path1)); 17 | 18 | 19 | -------------------------------------------------------------------------------- /Unity/AssetBundle/AssetManager(2): -------------------------------------------------------------------------------- 1 | public struct AssetCiteData 2 | { 3 | public AssetBundle AssetObj; 4 | public int iCiteCount; 5 | public bool bLoadFinish; 6 | 7 | static public AssetCiteData Default() 8 | { 9 | AssetCiteData Asset; 10 | Asset.AssetObj = null; 11 | Asset.iCiteCount = 0; 12 | Asset.bLoadFinish = false; 13 | return Asset; 14 | } 15 | 16 | } 17 | 18 | 19 | 20 | public class AssetManager : MonoBehaviour 21 | { 22 | private AssetBundle _MainAssetBundle; 23 | private AssetBundleManiest _MainAssetBundleManiest; 24 | 25 | private Dictionary dicCiteData = new Dictionary(); 26 | 27 | 28 | //引用对象信息 29 | private AssetCiteData GetAssetCiteIno(string path) 30 | { 31 | if(dicCiteData.ContainsKey(path)) 32 | return dicCiteData[path]; 33 | return dicCiteData.Add(path,data); 34 | } 35 | 36 | //在缓存中增加对象信息 37 | private void AddAssetCiteData(string path,AssetCiteData data) 38 | { 39 | if(!dicCiteData.ContainsKey(path)) 40 | dicCiteData.Add(path,data); 41 | 42 | } 43 | 44 | //引用对象信息是否存在 45 | private bool HasAssetCite(string path) 46 | { 47 | return dicCiteData.ContainsKey(path); 48 | } 49 | 50 | 51 | //加载资源 52 | private Object LoadAsset(string path,Type type,bool is_dependencies = false) 53 | { 54 | path = path.Replace(".","_"); 55 | AssetBundle ab = null; 56 | 57 | 58 | 59 | } 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Unity/AssetBundle/Unity5如何进行AsasertBundle打包: -------------------------------------------------------------------------------- 1 | 开始创建一个AssetBundle,从项目文件夹中选择一个想要的自愿的对象, 2 | 3 | 在Inspector检查器的底部是AssetBundle菜单, 4 | 5 | 1)从第一个下拉框中选择或者设置AssetBundles的名称, 6 | 2)第二个下案例框是附加选项。 7 | 8 | 9 | 如果资源是第一次打包,请不要设置第二个附加选项,否则打包报错。 10 | 11 | 默认情况下,AssetBundle名称设置为None,这意味着资源不能进行AssetBundle打包。 12 | 如果还没有定义包,单机new.输入包名。这样可以创建一个或者多个AssetBundle 13 | -------------------------------------------------------------------------------- /Unity/AudioManager: -------------------------------------------------------------------------------- 1 | 面向接口编程本质上还是面向对象编程。接口本质上还是一种类(对象),只不过抽象程度更高而已。 2 | 3 | 4 | 5 | 面向接口编程: 6 | public interface IAudio 7 | { 8 | AudioSource GetAudioSource(); 9 | 10 | void OnPlayAudio(); 11 | 12 | void OnPlayAudio(int index); 13 | 14 | void OnPlayAudio(string aduioName); 15 | 16 | void OnPlauyAudio(AudioClip clip); 17 | 18 | void AddAudioClips(AudioClip[] clips); 19 | 20 | void RemoveAudioClips(AudioClip[] clips); 21 | 22 | } 23 | 24 | 25 | AudioManager: 26 | 27 | public class AudioManager : IAudio,MonoBehaviour 28 | { 29 | private AudioSource audioSource; 30 | 31 | 32 | private List audioPool = new List<>(); 33 | 34 | //TODO 35 | } 36 | -------------------------------------------------------------------------------- /Unity/RawImage: -------------------------------------------------------------------------------- 1 | RawImage组建主要用来显示非交互的图像控件,一般用于装饰或者图标。 2 | 【RawImage支持任何类型的纹理,但Image控件只支持Sprite类型的纹理】 3 | 4 | 5 | -------------------------------------------------------------------------------- /Unity/Ray/Camera和Ray: -------------------------------------------------------------------------------- 1 | Unity中把射线碰到的店(hit)转换为非主摄像机的屏幕坐标?? 2 | 1) 3 | public Camera myCamera; 4 | myCamera.WorldToScreenPoint(hit); 5 | 6 | -------------------------------------------------------------------------------- /Unity/Ray/射线Ray的原理: -------------------------------------------------------------------------------- 1 | 射线检测故名就是通过射线去检测是否和碰撞器产生了交集,和碰撞器与碰撞器发生交集一样,会返回一个真。 2 | 3 | 射线的用法很多:比如检测是否跳跃,通过向地面投射射线控制在地面时候可以跳起。 4 | 5 |         射击游戏中可以通过定长射线去判断目标物体是否被击中,等 6 | 7 | 主要用到的工具类是: 8 | 9 | Physics 10 | RaycastHit 光线投射碰撞 11 | 12 | 13 | Unity中用于检测鼠标点在屏幕上提供了一个控制方案。 14 | 具体点在unity三维世界坐标的哪个点。 15 | 通过鼠标点击屏幕,由屏幕点向unity三维直接发射一条射线。 16 | 17 | 当检测到碰撞物体后,会返回被碰撞物体的所有信息,以及交点信息等等… 18 | 19 | 射线检测即对一个碰撞器进行检测,如果碰撞到碰撞器,则返回true,否则返回false,这个检测是一条射线, 20 | 这条射线由我们自己设置 。一般在Update函数配合if()来使用。 21 | 22 | RaycastHit hit 此为射线碰撞到的物体碰撞器, 23 | 以out hit传入 当射线碰撞到物体时,返回true,hit则是对应的碰撞物体的碰撞器 24 | 可以通过hit来找到碰撞到的物体对象信息。 25 | 26 | 27 | //1. 射线开始的位置 28 | //2. 射线的方向 29 | //3. 碰撞信息 30 | //4. 射线的最大距离 31 | //5. 层级 32 | Physics.Raycast (transform.position, transform.forward, out hit, 2, _layerMask) 33 | 34 | 应用的场景之一: 35 | 1)通过射线使得物体移动: 36 | 通过鼠标点击的方法,获取Ray类型对象 37 | —ray = Camera.main.ScreenPointToRay(Input.mousePosition); 38 | 2)然后根据Ray类型对象做碰撞检测(Plane),得到RaycastHit _hit 39 | if(Physics.Raycast(_ray,out _hit)) 40 | 3)_hit.point就是需要移动的地方,此时我们使用transform.LookAt()看向他,然后设置transform.forward()移动到该点。 41 | 注意参数是vector3.forward而非transform.forward,两者调用区别很大 42 | -------------------------------------------------------------------------------- /Unity/Ray射线API: -------------------------------------------------------------------------------- 1 | Ray: 2 | 3 | //返回一条射线Ray从摄像机到屏幕指定的一个点 4 | Ray Camera.main.ScreenPointToRay(Vector3 pos); 5 | 6 | 7 | 8 | 9 | 10 | RaycastHit: 用于存储发射射线后产生的碰撞信息。 11 | 常用的成员变量如下:Collider和射线发生碰撞的碰撞器 12 | 13 | distanace:从射线七点到射线和碰撞器交点的距离 14 | normal:射线射向平面的法向量 15 | point:射线和碰撞器交点的坐标:Vector3对象 16 | 17 | 通常需要提前声明RaycastHit对象 18 | 19 | -------------------------------------------------------------------------------- /Unity/SerializeField的用法: -------------------------------------------------------------------------------- 1 | 如果a是公有的序列化变量。 2 | (1)如果你想要在面板中看到变量a,那么用: 3 | public int a; 4 | (2)如果你不想在面板中看到变量a,那么用: 5 | [HideInInspector] 6 | public int a; 7 | 这样a可以在程序中被代码赋值,但不会在面板中看到,也不能手动设置赋值。 8 | 9 | 10 | 如果a是私有的序列化变量,你想在面板中读取并赋值,那么用: 11 | [SerializeField] 12 | private int a; 13 | 14 | 如果a是私有的序列化变量,你想在面板中读取,但是不赋值,那么用: 15 | [HideInInspector][SerializedField] 16 | private int a; 17 | public int b 18 | { 19 | get{return a;} 20 | } 21 | 22 | 23 | 如果a是私有序列化变量,你不想在面板中做任何操作(不想看到,也不想写),但是想要在程序中给它赋值,那么用。 24 | [HideInInspector][SerializedField] 25 | private int a; 26 | public int b 27 | { 28 | get{return a;} 29 | set{a = value;} 30 | } 31 | -------------------------------------------------------------------------------- /Unity/UI/UGUI事件系统: -------------------------------------------------------------------------------- 1 | Unity UGUI的可触发事件系统: 2 | 3 | PointerEnter: 4 | PointerExit: 5 | PointerDown: 6 | PointUp: 7 | PointClick: 8 | Drag: 9 | Drop: 10 | Scroll: 11 | UpdateSelected: 12 | Select: 13 | Deselect: 14 | Move: 15 | 16 | (1)继承基础接口实现: 17 | 18 | 创建ClickObject脚本,继承MonoBehaviour和PointerClickHander 19 | 20 | public ClickObject : MonoBehaviour,IPointerClickHandelr{ 21 | 22 | void Start(){ 23 | 24 | } 25 | 26 | void Update(){ 27 | 28 | } 29 | 30 | public void OnPointerClick(PointerEventData eventData){ 31 | Debug.Log("click"); 32 | } 33 | 34 | } 35 | 36 | 2)实现public void OnPointerClick(PointerEventData eventData) 37 | 3)创建名为 Panel_IPointer的空白对象,并且把ClickObject脚本挂在对象上 38 | 4)启动。点击Panel_IPointer对象,Console输出 39 | 40 | 41 | 42 | (2)Unity3D编辑器操作设置实现: 43 | Button提供OnClick操作,实现事件 44 | 45 | (3) 46 | 47 | 步骤一:创建c#脚本,添加OnTestClick() 48 | 步骤二:创建Empty对象,接受和响应点击事件,创建名为Panel的UI对象,于触发点击事件。 49 | 步骤三:Panel对象添加EventTrigger组件," Add New" -> 选择" PointerClick"。 50 | 将Empty对象拖拽到触发者位置。然后点击"No Function"选择我们写在Test脚本中的OnTestClick事件。 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Unity/UI/UGUI拖拽: -------------------------------------------------------------------------------- 1 | 最简洁版本: 2 | public class Drag : MonoBehaviour,IDragHandler,IPointerDownHandler 3 | { 4 | 5 | 6 | 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /Unity/UI/UGUI渲染层级: -------------------------------------------------------------------------------- 1 | Unity中的渲染顺序自上而下大致分为三层。 分别是Camera - > Sorting Layer - > Sorting order 2 | 最高层为Camera层,可以在Camera的depth那里设置,设置之后,图形的渲染顺序就是先绘制depth低的相机下的物体, 3 | 再绘制depth高的相机下的物体,也就是说,depth高的相机会覆盖depth低的相机(具体的覆盖关系有don't clear, solid color等等几种) 4 | 5 | 6 | UGUI渲染模式:Canvas -- Render Mode:ScreenSpace-Overlay 7 | 8 | 此模式下不依赖摄像机Camera的渲染 9 | 10 | Hierachy下存在各层级遮挡, 11 | 12 | 13 | 14 | 15 | ①不同Camera的Depth。(大在前,小在后) 16 | ②同Camera的SortingLayer。(下在前,上在后) 17 | ③同SortingLayer下的Order in Layer。(大在前,小在后) 18 | ④同Order in Layer下的Z轴。(小在前,大在后) 19 | 20 | 改变控件之间的层级关系 21 | (1)同一canvas下: 22 | 改变控件transform的SiblingIndex, 23 | transform.GetSiblingIndex(); 24 | transform.SetSiblingIndex(int index); //index值越大,越后渲染,层级越大,越显示在前面 25 | 26 | (2)不同Canvas下: 27 | 设置Canvas下的Sort Order //Sort Order值越大,越后渲染,层级越大,越显示在前面 28 | 渲染顺序与hierarchy面板里物体的摆放顺序也有关 29 | 面板里越靠上的物体越先被渲染,越后被渲染的显示在越前面。 30 | 31 | 32 | 注意: 33 | 34 | 如果是多个Canvas的渲染先后顺序 http://blog.csdn.net/huutu/article/details/43636241 35 | 调Canvas下面有一个Sort Order值,默认为0,越大越在后面。 36 | 37 | 创建任意UGUI元素时自动生成一个Canvas物体,Canvas下的所有物体从上往下渲染,即排在下面的会遮盖排上面的。同理,子元素会覆盖父元素。 38 | 39 | 在游戏运行中如何修改UGUI的显示层级? 40 | 在代码中调整该元素的层级位:使用RectTransform类的函数。 41 | 42 | SetAsFirstSibling:移动到所有兄弟节点的第一个位置(Hierarchy同级最上面,先渲染,显示在最下面) 43 | SetAsLastSibling:移动到所有兄弟节点的最后一个位置(Hierarchy同级最下面,后渲染,显示在最上面) 44 | GetSiblingIndex:获得该元素在当前兄弟节点层级的位置 45 | SetSiblingIndex:设置该元素在当前兄弟节点层级的位置 46 | 47 | 48 | 49 | 50 | Canvas的渲染模式【Render Mode】:ScreenSpace Overlay,ScreenSpace Camera,World Space 51 | ScreenSpace-Overlay: 52 | 1.OverLay模式,永远覆盖在其他物体之上。出现在最上面。不受摄像机的Depth值影响 53 | 2.有多个摄像机时,由摄像机的Depth值决定 54 | 3.只有一个摄像机时,由距离和方向决定World模式和Camera模式、它们的渲染结果 可前、可后、可穿插。 55 | ScreenSpace-Camera: 56 | 1.遵循刷油漆规则 57 | 2.依次由Render Camera的Depth值、Sorting Layer先后顺序、Order in Layer值决定 58 | Render Camera不同时,由Render Camera的Depth决定 59 | Render Camera相同时,由Sorting Layer先后顺序决定 60 | Render Camera相同时,Sorting Layer相同,由Order in Layer值决定。 61 | 62 | -------------------------------------------------------------------------------- /Unity/UI/unity的UI渲染机制: -------------------------------------------------------------------------------- 1 | . Canvas[画布]是一个控制一组被渲染UI元素的组件, 所有的UI元素必须是Canvas的子物体, 同一个 2 | 场景中允许存在多个Canvas, 但是使用UI元素必须少有一个可用的 Canvas组件。如果场景中没有 3 | Canvas, 创建一个UI元素, 新的Canvas组件就会被创建, 该UI元素会附加为 Canvas的子物体. 4 | 5 | 2. 每一个Canvas都有一些可选的Render Modes (渲染模式), 6 | Screen Space - Overlay 是最常见的渲染模式, 也是Canvas默认的渲染模式 7 | Screen space - Overlay 被选中时, UI会覆盖整个场景 所有的UI元素会在场景最上层渲染输出 8 | 在这个模式下 UI 会自动填充整个场景. 同时也会随着场景视图大小变化而变化。 9 | 值得注意的是 Canvas 会影响Rect Transform组件, 整个 Rect Transform 不可编辑。 10 | Canvas会修改Rect Transform 的数值来自动填充整个屏幕。 11 | 在这个模式下有个 Pixel Perfect 选项 Pixel Perfect 选中后, UI元素渲染输出时, 会自动吸附 12 | 到最近的像素点。在某些情况下会增强UI的展示效果。 13 | 14 | Screen Space - Camera 和 Screen Space - Overlay 十分相似, 只不过它是由场景中指定的摄像 15 | 机渲染输出。这会使Camera相关的设定作用于UI元素上。最常使用的场景是使用透明摄像机对UI进行 16 | 渲染输出, 得到景深的效果。 17 | 在这个模式下 Canvas 会自动填充相机输出视图, 并且根据摄像机输出视图变化而变化, 在这个模式下 18 | Canvas 组件同样会影响 Rect Transform, 这个模式下有几个选项, 包括Pixel Perfect,作用与 19 | Screen Space - Overlay 相同。Render Camera 用来设置用于渲染这个 Canvas上UI 元素的摄像机. 20 | 如果被设置为None, Canvas 就会选择 Screen Space - Overlay 的设置来渲染这个 Canvas, 21 | 当 Screen Space - Camera 选中且富于摄像机对象时。 UI 会被移至相机的视椎体中, 并改变自身 22 | 大小适应填充。这使得UI能够根据场景视图中游戏对象进行渲染。 为了控制场景中UI元素坐标位置, 23 | 使用Plane Distance 向远或靠近相机, 这时 Canvas 和 UI 会根据与相机的距离自动改变大小,适应 24 | 视椎体。 值得注意的是 Plane Distance 的数值应在相机渲染范围内, 当使用 Screen Space - Camera 25 | Canvas可以被场景中任意摄像机渲染, 为了使Canvas 及其内容与场景中其他 Canvas 区分隔离开, 26 | 可以通过设置摄像机的 Clear Flag Culling Mask Depth 属性 27 | 28 | World Space 渲染模式在世界坐标系下渲染UI, UI元素可以是场景中静态的对象 也可以是动态的对象 29 | 例如对话框, 标签等在场景中跟随游戏对象运动, 首先要了解的是在 World Space 渲染模式下 Canvas 30 | 不再影响 Rect Transform , Canvas 可以位于世界坐标系下任意位置, 明白同一场景下允许多个Canvas 31 | 后, 给世界空间坐标系的UI元素创建Canvas的做法就十分常见了。 32 | Event Camera 是接收事件必须具备的, 确定哪个摄像机来检测例如UI上点击等事件, 大多数情况下, 当 33 | World Space 选中时,Event Camera 会是当前渲染场景的摄像机. Receives Events 选项设置这个 34 | Canvas 中的UI元素是否接收事件 比如点击或悬停。 禁用这个选项则该 Canvas 下的所有子UI元素都将 35 | 忽略事件检测, Sorting Layer 和 Order Inlayer 用于控制场景中 Canvas 之间渲染的顺序 36 | Sorting Layer 和 Order InLayer 在 Canvas 中可以进行设置 包含 World Space Screen Space 37 | 38 | 3. Canvas 中UI元素会以自顶向下的方式进行渲染, 第一个对象会渲染在底层 , 改变UI元素渲染的顺序, 39 | 只需简单的改变UI元素在Hierarchy 面板的顺序 40 | -------------------------------------------------------------------------------- /Unity/UI/屏幕自适应: -------------------------------------------------------------------------------- 1 | 1)UI控件锚点自适应 2 | 3 | 2)Canvas自适应,Scale with screen size,根据屏幕需要设置ReferenceResolution的值 4 | 5 | 3)视口自适应 6 | -------------------------------------------------------------------------------- /Unity/UnityEditor编辑器: -------------------------------------------------------------------------------- 1 | UnityEditor自制编辑器(一)Gizmos 2 | 3 | Unity场景中绘制符号,团和区块来标记区域。比如:重生点,死亡区块或 生怪区。 4 | 这些区域只会在编辑时看到。 5 | 6 | 绘制OnDrawGizmos,不会编译到游戏中。 7 | 8 | OnDrawGizmos():不论有无选中都会绘制 9 | OnDrawGizmosSelected():只有被选中才会绘制 10 | 11 | 12 | using UnityEngine; 13 | 14 | public class DrawGizmo : MonoBehaviour 15 | { 16 | void OnDrawGizmos() 17 | { 18 | Gizmos.color = new Color(1f,1f,0f,1f); 19 | Gizmos.DrawWireCube(this.transform.position,Vector3.one*0.9f); 20 | 21 | Gizmos.color = new Color(1f,1f,0f,3f); 22 | Gizmos.DrawCube(this.transform.position,Vector3.one* 0.9f); 23 | } 24 | 25 | } 26 | 27 | 28 | 29 | UnityEditor自制编辑器(二)Attribute 30 | 31 | Inspector直接改变数值: public 32 | 33 | 修改用Attribute 34 | 35 | [HideInSpector] 36 | public float strength; 37 | [SerializedField] private状态也能看得到 38 | 39 | 40 | 摄像机追踪中OnDrawGizmos 41 | 42 | UnityEditor编辑器(三)Custom Inspector 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 | -------------------------------------------------------------------------------- /Unity/Xml存储: -------------------------------------------------------------------------------- 1 | Xml存储任务:Quest 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | using System.Xml.Serialization; 20 | 21 | public class QuestNode 22 | { 23 | [XmlElementAttribute("NodeTitle")] 24 | public string NodeTitle; 25 | 26 | [XmlEelmentAttribute("NodeText")] 27 | public string NodeText; 28 | 29 | [XmlElementAttribute("NodeImage")] 30 | public string NodeImage; 31 | 32 | 33 | 34 | 35 | } 36 | 37 | Quest: 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | public void WriteToXML() 50 | { 51 | string path = Application.persistentDataPath + “/Load.xml”; 52 | 53 | XmlDocument doc = new XmlDocument(); 54 | if(!System.IO.File.Exists(Path)) 55 | { 56 | XmlDeclaration declaration = doc.CreateXmlDeclaration(""); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Unity/协程/协程原理分析: -------------------------------------------------------------------------------- 1 | 工作中协程的作用大多为两点: 2 | 1)延时(等待)一段时间执行代码 3 | 2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。 4 | 5 | 很多初学者,都会下意识地觉得协程是异步执行的,都会觉得协程是C# 线程的替代品,是Unity不使用线程的解决方案。 6 | 7 | 所以首先,请你牢记:协程不是线程,也不是异步执行的。 8 | 协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。 9 | 使用协程你不用考虑同步和锁的问题。 10 | 11 | Unity协程执行原理: 12 | UnityGems.com给出了协程的定义: 13 | A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done. 14 | 即协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。 15 | 16 | Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足),但也有写特例: 17 | 18 | 协程和Update其实一样,都是对Unity的每一帧进行处理的函数【如果有的话】 19 | 20 | 如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足,就会协程方法的后面代码。还可以发现:如果在一个对象的前期调用协程,协程会立即运行到第一个 yield return 语句处,如果是 yield return null ,就会在同一帧再次被唤醒。如果没有考虑这个细节就会出现一些奇怪的问题『1』。 21 | 结论都是从UnityGems.com 上得来的,经过下面的验证发现与实际不符,D.S.Qiu用的是Unity 4.3.4f1 进行测试的。经过测试验证,协程至少是每帧的LateUpdate()后去运行。 22 | 23 | MonoBehaviour.enabled = false 协程会照常运行,但 gameObject.SetActive(false) 后协程却全部停止 24 | 25 | 参考:https://dsqiu.iteye.com/blog/2029701 26 | -------------------------------------------------------------------------------- /Unity/协程/协程工作流程: -------------------------------------------------------------------------------- 1 | yield的返回值: 2 | 3 | yield返回如果是空,【c#是yield return null】,coroutine会被终止。 4 | 控制权交回调用的位置,就像coroutine结束了一样——事实上它还处于活动状态。 5 | 下一帧 coroutine会自动在yield的位置执行,直到Coroutine结束为止。 6 | 7 | yield返回值非空: 8 | 9 | 常见的有: 10 | yield return new WaitForEndOfFrame();//等到该帧结束 11 | yield return new WaitforSeconds(xxx);//等xxx秒 12 | yield return new WaitForFixedUpdate();//等物理帧结束 13 | yield return www;//这里www是一个WWW对象 14 | --------------------- 15 | 16 | 总提来说,只要返回的值非空,yield 语句的作用就是让coroutine在这里暂停,主线程继续,直到yield的条件得到满足,再继续执行yield后面的代码。 17 | 18 | 有了这个认识,其实上面的四种情况都是一样的。 19 | 前三个分别是等到该帧结束、等xxx秒、等物理帧结束,其实都是满足了 new WaitForXXX的这个条件。而最后一个www,则是等www加载完成,也就是www可以访问了,条件满足了。 20 | 21 | 另外需要注意的是,每个开启的coroutine会在堆中分配一个对象,并且会一直存在直到结尾。在周期性的函数中(例如Update,FixedUpdate, LateUpdate)开启coroutine一定要警惕,因为你可能会平行运行数以千计的coroutine,占据大量内存空间。帧率会直降,Unity也可能会因为CPU和内存负荷而崩溃掉。通常这种情况下可以用一个布尔变量来避免灾难发生。 22 | --------------------- 23 | 24 | 25 | 一个协程的执行可以在任何地方用yield语句来暂停,yield return的值决定了什么时候协程恢复执行。 26 | 协程在协调在几帧中执行的操作时有极大的用处.协程几乎没有任何性能开销。 27 | StartCoroutine一般都会立即返回,然而你也可以获得返回结果的值。但是这一步会等到协程结束执行才能生效。 28 | 29 | 30 | public class MainTest : MonoBehaviour{ 31 | 32 | void Start(){ 33 | Debug.Log("start"); 34 | StartCroutine(Test()); 35 | Debug.Log("start2"); 36 | } 37 | 38 | IEnumerator Test(){ 39 | Debug.Log("test1"); 40 | yield return null; 41 | Debug.Log("test"); 42 | } 43 | 44 | } 45 | 运行结果: 46 | start1 test1 start2 test2 47 | 48 | 49 | 这下就一目了然了,当StartCoroutine刚调用的时候,可以理解为正常的函数调用,然后接着看调用的函数里面。 50 | 51 | 当被调用函数执行到yield return null;(暂停协程,等待下一帧继续执行)时,根据Unity解释协同程序就会被暂停, 52 | 其实我个人认为他这个解释不够精确,先返回开始协程的地方,然后再暂停协程。也就是先通知调用处,“你先走吧,不用管我”,然后再暂停协程。。 53 | 54 | public class MainTets : Monobehaviour{ 55 | 56 | void Start(){ 57 | Debug.Log("start1"); 58 | StartCoroutine(Test()); 59 | Debug.Log("start2"); 60 | } 61 | 62 | IEnumerator Test(){ 63 | Debug.Log("test1"); 64 | yield return new WaitForSeconds(3); 65 | Debug.Log("test2"); 66 | } 67 | 68 | } 69 | 70 | 执行结果: 71 | start1 test1 start2 test2 [等待3s才打印出来] 72 | 73 | 验证了“yield return的值决定了什么时候协程恢复执行”这句。 74 | 协程->协同程序->其实就是协同两个任务, 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Unity/向量: -------------------------------------------------------------------------------- 1 | 1)点积和叉积: 2 | 3 | Vector3.Dot(a,b):表示两个向量的点积 4 | Vector3.Cross(a,b)表示两向量的叉积 5 | 6 | 点积的计算方式: 7 | a。b = |a|*|b|*COS (A,B表示两个向量的夹角) 8 | 9 | 应用: 10 | 1)通过点积计算两个向量的夹角 11 | 2)通过点积判断当前物体是否朝向另外的物体。 12 | 13 | 14 | 15 | 参考文章: 16 | 17 | https://www.cnblogs.com/Kprogram/p/4135952.html 18 | -------------------------------------------------------------------------------- /Unity/四元数Quaternion: -------------------------------------------------------------------------------- 1 | 四元数:Quaternion中存放了 x,y,z,w四个数据成员。 2 | 3 | Quaternion类属性: 4 | 5 | eulerAngles--欧拉角、 6 | Vector3 eulerAngles 7 | { 8 | get; 9 | set; 10 | } 11 | 12 | 13 | using UnityEngine; 14 | using System.Collections; 15 | 16 | public class EulerAngler_ts : MonoBehaviour { 17 | public Transform A, B; 18 | Quaternion rotations = Quaternion.identity; 19 | Vector3 eulerAngle = Vector3.zero; 20 | float speed = 10.0f; 21 | float tSpeed = 0.0f; 22 | 23 | // Use this for initialization 24 | void Start () { 25 | 26 | } 27 | 28 | // Update is called once per frame 29 | void Update () { 30 | tSpeed += speed * Time.deltaTime; 31 | //第一种方式:将Quaternion实例对象赋值给transform的rotation 32 | rotations.eulerAngles = new Vector3(0.0f, tSpeed, 0.0f); 33 | A.rotation = rotations; 34 | //第二种方式:将三位向量代表的欧拉角直接赋值给transform的eulerAngle 35 | B.eulerAngles = new Vector3(0.0f, tSpeed, 0.0f); 36 | } 37 | } 38 | 39 | 40 | 41 | Unity中四元数API: 42 | 43 | 44 | 45 | 46 | 47 | 48 | (1)把角度转化为相应四元数: 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Unity/物理组件/2d游戏碰撞检测: -------------------------------------------------------------------------------- 1 | 1)圆形碰撞体: 2 | 3 | 圆形碰撞体作为最简单的碰撞体,计算速度最快。算法容易理解。 4 | 5 | 6 | 1 bool IsHit(double x1, double y1, double x2, double y2, double r1, double r2) 7 | 2 { 8 | 3 //勾股定理 9 | 4 return ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) < (r1+r2)*(r1+r2); 10 | 5 } 11 | 12 | 这种碰撞体拥有最简单的计算过程,在运动速度够快(或形状够圆)的情况下也不会有太大的违和感;在弹幕游戏和射击游戏以及大量其它游戏中应用广泛。 13 | 14 | 15 | 16 | 17 | 2)轴对齐包围盒,Axis-Aligned Bounding Box(AABB) 长方形 18 | 19 | 包围盒,我们可以很方便的通过min点和MAX点(见右上方蓝色方块)的位置来完全确定这样一个盒子,同时通过一些比较大小的操作就可以确定两个盒子是否相交。 20 | 21 | 22 | 3)逐像素碰撞检测 23 | 24 | 4)方向包围盒 25 | 26 | 27 | -------------------------------------------------------------------------------- /Unity/物理组件/Camera相机跟随问题: -------------------------------------------------------------------------------- 1 | Camera较为常见的功能就是: 相机跟随人物移动。 2 | 3 | 第一种: 4 | 在Unity的坐标系中,将摄像机固定在主角头部上边靠后位置。主角移动,相机跟随人物移动。 5 | 即:摄像机相对主角,总是在Vector3.forward方向上靠后一些,在Vector3.up方向片上一些。 6 | 同时为了摄像机移动更加平滑,避免出现掉帧现象,引入插值函数Vector3.Lerp(),使得摄像机的移动圆滑。 7 | 8 | public class ThirdPersonCamera : MonoBehaviour 9 | { 10 | public float distanceAway = 1.7f; 11 | public float distanceUp = 1.3f; 12 | public float smooth = 2f; 13 | 14 | private Vector3 m_TargetPosition; 15 | private Transform follow; 16 | 17 | private void Start() 18 | { 19 | follow = GameObject.FindWithTag("Player").transform; 20 | } 21 | 22 | void LateUpdate() 23 | { 24 | 25 | } 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 相机跟随移动的过程中,常常遇到刷新频率不同导致物体抖动。 解决问题的最好做法是: 34 | (1)当角色的移动和旋转在Update中,把所有相机的移动旋转放在LateUpdate()中 35 | (2)为了在相机追踪角色位置之前,确保角色已经完成移动和旋转 36 | 37 | 38 | 39 | 40 | 41 | public class CameraFollowPlayer : MonoBehaviour 42 | { 43 | //玩家位置 44 | public Transform player; 45 | //玩家和相机的偏移 46 | private Vector3 pos; 47 | 48 | public float scrollSpeed = 1; 49 | public float rotateSpeed = 1; 50 | private bool isRightMouseDown; 51 | 52 | private void 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Unity/物理组件/Camera看向目标物体: -------------------------------------------------------------------------------- 1 | 相机Camera中,LookAt是直接看过去,缺少旋转过程。 2 | 3 | void CameraRotateToLookAt(Transform target,float rotateSpeed) 4 | { 5 | //取得目标物体相对相机的法向量 6 | 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Unity/物理组件/CharacterController: -------------------------------------------------------------------------------- 1 | MyCharacterController_1: 2 | 3 | public class MyCharacterController : MonoBehaviour 4 | { 5 | public float speed = 6f; 6 | public float jumpSpeed = 8f; 7 | 8 | public CharacterController controller; 9 | 10 | private Vector3 moveDir = Vector3.zero; 11 | 12 | void Start() 13 | { 14 | controller = GetComponent(); 15 | 16 | } 17 | 18 | void Update() 19 | { 20 | if(controller.isGrounded) 21 | { 22 | 23 | 24 | } 25 | } 26 | 27 | 28 | } 29 | 30 | 31 | ============================================================== 32 | 33 | MyCharacterController_2: 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Unity/物理组件/Collider和Trigger: -------------------------------------------------------------------------------- 1 | 理解Collider和Trigger,就必须对这两者都有清晰的认识。 2 | 3 | Collider:碰撞器 4 | 5 | Collider: 6 | Static Collider 静态碰撞器 7 | Rigidbody Collider 刚体碰撞器【特指附加刚体和碰撞器的游戏对象】 8 | Kinematic Rigidbody Collider 运动学刚体碰撞器 9 | 10 | 11 | 碰撞产生的条件: 12 | 1)两个碰撞器,且至少一个是刚体碰撞器【不含运动学刚体碰撞器】 13 | 14 | 触发产生的条件: 15 | 1)两个碰撞器,且至少有一个是刚体碰撞器【包含运动学碰撞器】 16 | 2)至少一个勾选iTrigger 17 | 3)必须是具有刚体的一方去碰撞【没有刚体的碰撞具有刚体的静止物体,不会反应,因为刚体休眠】 18 | 19 | 角色控制器Character Controller,相当于刚体碰撞器 20 | 21 | 控制器不会对施加自身的力做出反应,也不会自动推开其他刚体。 22 | -------------------------------------------------------------------------------- /Unity/物理组件/Rigidbody之velocity和Addforce: -------------------------------------------------------------------------------- 1 | Rigidbody.Velocity: 2 | 3 | 瞬间为物体添加一个恒定的速度,将物体提升到该速度。 4 | 5 | Rigidbody.addForce: 6 | 7 | 瞬间给物体一个力 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Unity/物理组件/Rigidbody移动: -------------------------------------------------------------------------------- 1 | Rigidbody进行移动的基础方法: 2 | 1)Position 3 | 2) Velocity 4 | 3) AddFoce 5 | 4) MovePositon 6 | 7 | Position: 8 | rigidbody.position = Vector3.forward * 0.1f; 9 | 10 | //不适用transform.position,因为transform.position是瞬间移动坐标 11 | 12 | Velcity: 13 | rigidbody.velocity = Vector3.forward * 0.1f; 14 | 15 | 16 | AddForce: 17 | rigidbody.AddForce(Vector3.forward * 0.1f); 18 | ForceMode.Force: 持续的 ,考虑质量 19 | Impulse.Force:瞬间的,考虑质量 20 | Acceleration.Force:持续的,无视重量 21 | VelocityChange.Force:瞬间的,无视重量 22 | 23 | -------------------------------------------------------------------------------- /Unity/物理组件/Transform组件: -------------------------------------------------------------------------------- 1 | 1)Transform组件变量 2 | 3 | 属性: 4 | position 5 | localPosition 6 | eulerAngles 7 | localEulerAngles 8 | rotation 9 | parent 10 | root 11 | 12 | 13 | 旋转欧拉角: eulerAngles 14 | 15 | x,y,z角代表Z轴旋转z度,绕x轴旋转x度,绕Y轴旋转y度。 16 | 17 | 仅使用这个变量读取和设置角度到绝对值。 18 | 当超过角度360度,将失败。使用Transfom.Rotate()替代。 19 | 20 | 21 | 22 | 旋转角度: 23 | Transform.rotation 旋转角度 24 | 25 | 旋转一个物体,使用Transform.Rotate, 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 说明: 41 | -------------------------------------------------------------------------------- /Unity/物理组件/UGUI血条跟随角色: -------------------------------------------------------------------------------- 1 | 1)把Canvas设置成Worldspace模式,放到角色下,变成子对象,自然就跟着角色移动了。 2 | 3 | 问题:角色旋转,血条也会跟着旋转,即便通过Update函数,修正rotation,强行等于Camera的rotation,也会出现摆动的现象。 4 | 5 | 2)把角色的Position投射到屏幕上,转化为屏幕坐标,再通过计算角色到Camera的距离,算出比例系数,再放大缩小血条。 6 | 7 | 问题:血条会把别的角色挡住 8 | 9 | 3)设置Canvas为相机模式,计算角色屏幕坐标,再用角色屏幕坐标计算血条世界坐标…… 10 | 11 | 第一步:创建血条UI 12 | 第二步:创建角色物体,挂载脚本 13 | 第三步:获得角色位置坐标,转换为屏幕z坐标 14 | Vector3 pt = Camera.main.WorldToScreenPoint(new Vector3(this.transform.position.x, this.transform.position.y + 1, this.transform.position.z)) 15 | 第四步:设置Ui坐标,设置屏幕坐标转换为世界坐标 16 | 17 | -------------------------------------------------------------------------------- /Unity/物理组件/camera控制: -------------------------------------------------------------------------------- 1 | Camera相机控制的核心在于 向量。 2 | 3 | 向量旋转 = 四元数 * 向量 4 | 5 | 初始值(0,0,1)的向量: 6 | private Vector3 targetPos = Vector3.forward; 7 | 8 | 9 | Quaternion.LookRotation(): 10 | 参考世界坐标轴,将坐标放入世界坐标系中。 11 | 12 | 效果: 13 | 敌人Enemy一直注视目标点Target 14 | Quaternion.LookRotation(Target.position - Enemy.position); 15 | 16 | 参考文献: 17 | https://www.iteye.com/blog/hypercube-2320466 18 | -------------------------------------------------------------------------------- /Unity/物理组件/物理组件Collider: -------------------------------------------------------------------------------- 1 | 无论碰撞的两个物体是利用碰撞器,还是利用触发器,都必须带有碰撞器组件【Collider】。其中一个物体必须带有RigidBody刚体。 2 | 3 | 简单来说:碰撞发生必须有两个必要条件: 4 | 1)双方物体必须有一个是刚体 5 | 2)两个物体比须有Collider 6 | *静态碰撞 只有一个有刚体的碰撞,如果没有刚体的物体去碰撞带有刚体的物体时,有的时候会检测不到,因为刚体休眠的原因。 7 | 动态碰撞 两个都有刚体的碰撞 8 | *运动学碰撞 两个都必须有刚体,而且有一个是运动学刚体,这时两个物体碰撞的时候,运动学刚体不会受到影响。 9 | 10 | 11 | 1)碰撞器是一群组件,它包含了很多种类,比如:Box Collider,Capsule Collider等,这些碰撞器应用的场合不同, 12 | 但都必须加到GameObjecet身上。 13 | 2)所谓触发器,只需要在检视面板中的碰 14 | 15 | 16 | 17 | 18 | 撞器组件中勾选IsTrigger属性选择框。 19 | 20 | 触发信息检测: 21 | 1)MonoBehaviour:OnTriggerEnter(Collider other):进入触发器 22 | 2)MonoBehaviour:OnTriggerExit(Collider other):退出触发器 23 | 3)MonoBehaviour:OnTriggerStat(Collider other):逗留触发器 24 | 25 | 碰撞器: 26 | 1)MonoBehaviour:OnCollisionEnter(Collision collisionInfo):进入碰撞器 27 | 2)MonoBehaviour:OnCollisionExit(Collision collisionInfo):退出碰撞器 28 | 3)MonoBehaviour:OnCollisionStay(Collision collisionInfo):逗留碰撞器 29 | 30 | 31 | -------------------------------------------------------------------------------- /Unity/生命周期/Awake()和Start区别: -------------------------------------------------------------------------------- 1 | Awake()在Monobehaviour创建后立刻被调用,Start在MonoBehaviour创建后在该帧 2 | update前执行。 3 | 4 | void Awake() 5 | { 6 | //初始化函数,主要用来创建变量 7 | //不可执行协同程序 8 | } 9 | 10 | void Start() 11 | { 12 | 13 | } 14 | 15 | Awake方法的调用顺序在object之间时随机的 16 | 当所有对象的Awake方法都调用完成后,才会执行第一个Start方法 17 | Awake方法就像构造函数一样 18 | Awake方法不论脚本是否启用,都会调用 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Unity/生命周期/Unity中Update和FixUpdate和LaaterUpdate: -------------------------------------------------------------------------------- 1 | FixedUpdate () 和 Update () : 2 | 3 | Monobehaviour激活后,在每一帧被调用,实现更新。 4 | 5 | Update()每一帧的时间不固定,即第一帧与第二帧的时间t1和第三帧与第四帧的时间t2不一定相同。FixedUpdate()每帧与每帧之间相差的时间是固定的. 6 | 7 | FixUpdate是按照帧进行更新的。 8 | 9 | 通常 10 | FixedUpdate() 11 | { 12 | i++; 13 | time += Time.deltaTime; 14 | Debug.Log(“这是第”); 15 | } 16 | -------------------------------------------------------------------------------- /UnityInspecotr编辑器扩展: -------------------------------------------------------------------------------- 1 | 1)Inspector扩展 2 | (1)目标类 挂在Inspector面板 3 | (2)绘制类:继承editor 4 | 5 | 6 | 绘制方式(1): 7 | 8 | [CustomEditor(typeof(Inspecotr))] 9 | public class InspectorExampleEditor : Editor 10 | { 11 | 12 | 13 | 14 | 15 | } 16 | 17 | 18 | 19 | ============== 分隔符 ================= 20 | 21 | public class Hero:MonoBehaviour 22 | { 23 | public int health = 0; 24 | public int maxHealth = 100; 25 | public bool withShield = 0; 26 | public int maxShield = 0; 27 | 28 | 29 | 30 | 31 | } 32 | 33 | Editor: 34 | 35 | {SerializedProperty和SerializedObject提供通用方式编辑对象属性的类,处理UI和预制体} 36 | 37 | public class HeroEditor : Editor 38 | { 39 | private SerilizedProperty health; 40 | private SerializedProperty maxHealth; 41 | private SerializedProperty withShield; 42 | private SerializedProperty shield; 43 | 44 | 45 | private void OnEnable() 46 | { 47 | health = serializedObject.FindProperty("health"); 48 | maxHealth = serializedObject.FindProperty("maxHealth"); 49 | withShield = serializedObject.FindProperty("withShield"); 50 | shield = serializedObject.FindProperty("shield"); 51 | } 52 | 53 | 54 | private override void OnInspectorGUI() 55 | { 56 | 57 | } 58 | 59 | 60 | 61 | 62 | 63 | 64 | } 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Unity旋转: -------------------------------------------------------------------------------- 1 | 1)方便的旋转到指定角度的便捷方法 2 | 3 | (1)Mathf.LerpAngle() 4 | (2)Quaternion.LookRotation() 5 | (3)Quaternion.RotateTowards() 6 | (4)Vector3.RotateTowards() 7 | (5)Quaternion.Slerp() 8 | 9 | 10 | 2)瞬间全部旋转到指定角度的便捷方法 11 | 12 | (1)使用Quaternion.AngleAxis 13 | (2)使用Quaternion.Euler 14 | (3)使用Quaternion.eulerAngles 15 | (4)使用transform.eulerAngles 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /vector.forward和ransform.forward: -------------------------------------------------------------------------------- 1 | 物体朝自己的正前方运动: 2 | transform.translate(Vector3.forward*Time.deltaTime) 3 | 4 | 朝向: 5 | 6 | //transform.Translate()中使用,如果不表明坐标系,则为物体的局部坐标,即物体的正前方 7 | Vector.forward:(0,0,1) 8 | //Vecctor3.right:(1,0,0) 9 | //Vector3.Up:(0,1,0) 10 | 11 | 使用Transform.translate()时,Vector3.forward和Transform.forward不同。 12 | 推荐使用: 13 | transform.translate(Vecotr3.forward,Space.self); 沿着自身坐标系前进 14 | transform.translate(Vector3.forward,Space.world); 沿着世界坐标系前进 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /动画导入设置: -------------------------------------------------------------------------------- 1 | Legacy:早起动画设置,不支持Animator,只可以使用Animation播放 2 | Generic:支持人性,非人形Model 3 | Humanoid:只支持人形Model 4 | 5 | 6 | 7 | 动画类型:Animation Type: 8 | Generic:点击后生成一个动画和骨骼映射(Humanoid一样) 9 | 10 | Avatar Definition:骨骼映射 11 | Create From This Model:使用此Model创建骨架 12 | Copy From Other Avatar:使用其他骨骼 13 | Optimize(优化) Game Objects:是否优化游戏物体,在发布游戏时勾选 14 | -------------------------------------------------------------------------------- /包设计packet: -------------------------------------------------------------------------------- 1 | //数据包 2 | 3 | public class Package 4 | { 5 | 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /单例模式: -------------------------------------------------------------------------------- 1 | 懒汉模式:[检测无时才实例化,即外部什么时候调用什么时候new] 2 | 1)提供私有化构造函数 3 | 2)自行实例化 4 | 3)提供唯一实例,并且对外提供全局静态接口 5 | 6 | public class Singleton 7 | { 8 | private static Singleton _instance = null; 9 | 10 | private Singleton() 11 | {} 12 | 13 | public static Singleton GetInstance() 14 | { 15 | if(_instance == null) 16 | instance = new Singleton(); 17 | return _instance; 18 | } 19 | 20 | 21 | } 22 | 23 | //饿汉模式:类加载时才实例化,并且创建单例对象 24 | 25 | 1)类内部预先实例化唯一实例 26 | 2)对外提供唯一访问接口(静态方法) 27 | 3)私有构造函数 28 | 29 | public class Singleton 30 | { 31 | private readonly static Singleton Instance = new Singleton(); 32 | 33 | private Singleton() 34 | {} 35 | 36 | public static Singleton GetInstance() 37 | { 38 | return Instance; 39 | } 40 | 41 | 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /常用功能/AStar原理: -------------------------------------------------------------------------------- 1 | 需要两个列表:(Open和Close列表) 2 | 3 | 1)Open列表:记录所有被考虑用来寻找最短路径的格子 4 | 2) Closed列表: 记录不会再被考虑的格子 5 | 6 | 第一步,再Closed列表当中添加当前位置(这个点被称为点“A”) 7 | 第二步,将所有当前位置相邻的可通行格子添加到Open列表 8 | 9 | 引入概念:节点Node和代价Cost 10 | 11 | Node:每个格子都可以称之为节点 12 | Cost:描述角色移动到某个节点是所走的距离 13 | 14 | 15 | -------------------------------------------------------------------------------- /常用功能/MiniMap小地图: -------------------------------------------------------------------------------- 1 | MiniMap: 2 | 3 | /////////////////////////////////////// 4 | using UnityEngine; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine.UI; 8 | using UnityEngine.EventSystems; 9 | 10 | public class MiniMapView : MonoBehaviour 11 | { 12 | private static MiniMapView instance; 13 | public static MiniMapView Instance 14 | { 15 | get 16 | { 17 | return instance; 18 | } 19 | } 20 | 21 | public MiniMapController controller; 22 | 23 | private MiniMapPoolManager poolManager; 24 | 25 | [HideInInspector] 26 | public Transform elementIconParent; 27 | 28 | public Dictionary elementIconDict; 29 | 30 | public Vector3 mapSize = new Vector3( 30f, 1, 30f ); 31 | 32 | public Vector2 miniMapSize; 33 | 34 | void Awake() 35 | { 36 | instance = this; 37 | 38 | controller = new MiniMapController(); 39 | 40 | poolManager = GetComponent(); 41 | 42 | miniMapSize = transform.GetComponent().sizeDelta; 43 | 44 | elementIconDict = new Dictionary(); 45 | 46 | elementIconParent = transform.Find( "Mask/ElementParent" ).transform; 47 | 48 | controller.OnCreate(); 49 | 50 | } 51 | 52 | public void CreateElementIcon( int id, MiniMapElementIconType iconType ) 53 | { 54 | GameObject elementIcon = null; 55 | elementIcon = poolManager.TakeObject( iconType ); 56 | elementIcon.SetActive( true ); 57 | elementIconDict.Add( id, elementIcon.transform ); 58 | } 59 | 60 | public void UpdateElementIcon( int id, MiniMapElementIconType iconType ) 61 | { 62 | if ( !elementIconDict.ContainsKey( id ) ) 63 | { 64 | return; 65 | } 66 | 67 | GameObject newIcon = null; 68 | newIcon = poolManager.TakeObject( iconType ); 69 | newIcon.transform.localPosition = elementIconDict[id].localPosition; 70 | newIcon.SetActive( true ); 71 | 72 | poolManager.ReleaseObject( controller.elementDataDict[id], elementIconDict[id].gameObject ); 73 | controller.elementDataDict[id] = iconType; 74 | elementIconDict[id] = newIcon.transform; 75 | } 76 | 77 | public void DestroyElementIcon( int id ) 78 | { 79 | if ( elementIconDict.ContainsKey( id ) ) 80 | { 81 | poolManager.ReleaseObject( controller.elementDataDict[id], elementIconDict[id].gameObject ); 82 | elementIconDict.Remove( id ); 83 | } 84 | } 85 | 86 | public void MoveElementIcon( int id, Vector2 vec ) 87 | { 88 | if ( elementIconDict.ContainsKey( id ) ) 89 | { 90 | SetElementIconPosition( elementIconDict[id], vec ); 91 | } 92 | } 93 | 94 | private void SetElementIconPosition( Transform elementIcon, Vector2 vec ) 95 | { 96 | elementIcon.localPosition = new Vector2( miniMapSize.x * vec.x / mapSize.x, miniMapSize.y * vec.y / mapSize.z ); 97 | } 98 | 99 | 100 | void Destroy() 101 | { 102 | controller.OnDestroy(); 103 | } 104 | } 105 | 106 | 107 | 108 | 109 | 110 | ////////////// 111 | Camera: 112 | 113 | 114 | using UnityEngine; 115 | using UnityEngine.EventSystems; 116 | using DG.Tweening; 117 | 118 | public class MiniMapCameraManager : MonoBehaviour, IDragHandler, IPointerDownHandler{ 119 | 120 | private Transform point; 121 | 122 | private Vector3 cameraOffset; 123 | 124 | private Vector2 pointOffset; 125 | 126 | private Vector3 mapSize; 127 | 128 | private Vector2 miniMapSize; 129 | 130 | private PolygonCollider2D polygonCollider2D; 131 | 132 | void Start() 133 | { 134 | point = transform.Find( "Mask/Bg/Point" ); 135 | polygonCollider2D = transform.Find( "Mask/Bg" ).GetComponent(); 136 | 137 | mapSize = MiniMapView.Instance.mapSize; 138 | miniMapSize = MiniMapView.Instance.miniMapSize; 139 | 140 | cameraOffset = Camera.main.transform.position; 141 | 142 | InitPointPosition( cameraOffset ); 143 | } 144 | 145 | public void OnPointerDown( PointerEventData eventData ) 146 | { 147 | if ( !polygonCollider2D.OverlapPoint( eventData.position ) ) 148 | { 149 | return; 150 | } 151 | 152 | point.position = eventData.position; 153 | 154 | SetCameraPosition( point.transform.localPosition ); 155 | } 156 | 157 | public void OnDrag( PointerEventData eventData ) 158 | { 159 | if ( !polygonCollider2D.OverlapPoint( eventData.position ) ) 160 | { 161 | return; 162 | } 163 | 164 | if (Vector2.Distance(point.position, eventData.position) < 10) 165 | { 166 | return; 167 | } 168 | 169 | point.position = eventData.position; 170 | 171 | SetCameraPosition( point.transform.localPosition ); 172 | } 173 | 174 | Tweener tweener; 175 | void SetCameraPosition(Vector2 vec) 176 | { 177 | vec = vec - pointOffset; 178 | Vector3 targetPosition = new Vector3( mapSize.x * vec.x / miniMapSize.x, 0, mapSize.z * vec.y / miniMapSize.y ) + cameraOffset; 179 | if ( tweener != null && tweener.IsPlaying() ) 180 | { 181 | tweener.Kill( false ); 182 | } 183 | 184 | tweener = DOTween.To( () => Camera.main.transform.position, value => Camera.main.transform.position = value, targetPosition, 0.3f ).SetEase(Ease.OutQuad); 185 | 186 | } 187 | 188 | void InitPointPosition( Vector3 vec ) 189 | { 190 | Vector3 position = Vector3.zero; 191 | 192 | RaycastHit hit; 193 | Vector2 v = new Vector2( Screen.width / 2, Screen.height / 2 ); 194 | if ( Physics.Raycast( Camera.main.ScreenPointToRay( v ), out hit ) ) 195 | { 196 | position = hit.point; 197 | } 198 | 199 | Vector2 targetPosition = new Vector2( position.x * miniMapSize.x / mapSize.x, position.z * miniMapSize.y / mapSize.z ); 200 | point.localPosition = targetPosition; 201 | pointOffset = targetPosition; 202 | } 203 | 204 | } 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /常用功能/Profile性能优化基础: -------------------------------------------------------------------------------- 1 | Profile介绍: 2 | 3 | 4 | CPU: 5 | 6 | A. WaitForTargetFPS: 7 | Vsync(垂直同步)功能所,即显示当前帧的CPU等待时间 8 | B. Overhead: 9 | Profiler总体时间-所有单项的记录时间总和。用于记录尚不明确的时间消耗,以帮助进一步完善Profiler的统计。 10 | C. Physics.Simulate: 11 | 当前帧物理模拟的CPU占用时间。 12 | D. Camera.Render: 13 | 相机渲染准备工作的CPU占用量 14 | E. RenderTexture.SetActive: 15 | 设置RenderTexture操作. 16 | 底层实现:1.比对当前帧与前一帧的ColorSurface和DepthSurface. 17 | 2.如果这两个Buffer一致则不生成新的RT,否则则生成新的RT,并设置与之相对应的Viewport和空间转换矩阵. 18 | F. Monobehaviour.OnMouse_ : 19 | 用于检测鼠标的输入消息接收和反馈,主要包括:SendMouseEvents和DoSendMouseEvents。(只要Edtor开起来,这个就会存在) 20 | G. HandleUtility.SetViewInfo: 21 | 仅用于Editor中,作用是将GUI和Editor中的显示看起来与发布版本的显示一致。 22 | H. GUI.Repaint: 23 | GUI的重绘(说明在有使用原生的OnGUI) 24 | I. Event.Internal_MakeMasterEventCurrent: 25 | 负责GUI的消息传送 26 | J. Cleanup Unused Cached Data: 27 | 清空无用的缓存数据,主要包括RenderBuffer的垃圾回收和TextRendering的垃圾回收。 28 | 1.RenderTexture.GarbageCollectTemporary:存在于RenderBuffer的垃圾回收中,清除临时的FreeTexture. 29 | 2.TextRendering.Cleanup:TextMesh的垃圾回收操作 30 | K. Application.Integrate Assets in Background: 31 | 遍历预加载的线程队列并完成加载,同时,完成纹理的加载、Substance的Update等. 32 | L. Application.LoadLevelAsync Integrate: 33 | 加载场景的CPU占用,通常如果此项时间长的话70%的可能是Texture过长导致. 34 | M. UnloadScene: 35 | 卸载场景中的GameObjects、Component和GameManager,一般用在切换场景时. 36 | N. CollectGameObjectObjects: 37 | 执行上面M项的同时,会将场景中的GameObject和Component聚集到一个Array中.然后执行下面的Destroy. 38 | O. Destroy: 39 | 删除GameObject和Component的CPU占用. 40 | P. AssetBundle.LoadAsync Integrate: 41 | 多线程加载AwakeQueue中的内容,即多线程执行资源的AwakeFromLoad函数. 42 | Q. Loading.AwakeFromLoad: 43 | 在资源被加载后调用,对每种资源进行与其对应用处理. 44 | 45 | GPU: 46 | 47 | A. Device.Present: 48 | device.PresentFrame的耗时显示,该选项出现在发布版本中. 49 | B. Graphics.PresentAndSync: 50 | GPU上的显示和垂直同步耗时.该选项出现在发布版本中. 51 | C. Mesh.DrawVBO: 52 | GPU中关于Mesh的Vertex Buffer Object的渲染耗时. 53 | D. Shader.Parse: 54 | 资源加入后引擎对Shader的解析过程. 55 | E. Shader.CreateGPUProgram: 56 | 根据当前设备支持的图形库来建立GPU工程. 57 | 58 | 59 | 60 | Memory Profiler: 61 | 62 | A. Used Total: 63 | 当前帧的Unity内存、Mono内存、GfxDriver内存、Profiler内存的总和. 64 | B. Reserved Total: 65 | 系统在当前帧的申请内存. 66 | C. Total System Memory Usage: 67 | 当前帧的虚拟内存使用量.(通常是我们当前使用内存的1.5~3倍) 68 | D. GameObjects in Scene: 69 | 当前帧场景中的GameObject数量. 70 | E. Total Objects in Scene: 71 | 当前帧场景中的Object数量(除GameObject外,还有Component等). 72 | F. Total Object Count: 73 | Object数据 + Asset数量. 74 | 75 | Detail Memory Profiler: 76 | 77 | A. Assets: 78 | Texture2d:记录当前帧内存中所使用的纹理资源情况,包括各种GameObject的纹理、天空盒纹理以及场景中所用的Lightmap资源. 79 | B. Scene Memory: 80 | 记录当前场景中各个方面的内存占用情况,包括GameObject、所用资源、各种组件以及GameManager等(天般情况通过AssetBundle加载的不会显示在这里). 81 | C. Other: 82 | ManagedHeap.UseSize:代码在运行时造成的堆内存分配,表示上次GC到目前为止所分配的堆内存量. 83 | SerializedFile(3): 84 | WebStream:这个是由WWW来进行加载的内存占用. 85 | System.ExecutableAndDlls:不同平台和不同硬件得到的值会不一样。 86 | 87 | 优化的关键点: 88 | 89 | A. CPU-GC Allow: 90 | 关注原则:1.检测任何一次性内存分配大于2KB的选项 2.检测每帧都具有20B以上内存分配的选项. 91 | B. Time ms: 92 | 记录游戏运行时每帧CPU占用(特别注意占用5ms以上的). 93 | C. Memory Profiler-Other: 94 | 1.ManagedHeap.UsedSize: 移动游戏建议不要超过20MB. 95 | 2.SerializedFile: 通过异步加载(LoadFromCache、WWW等)的时候留下的序列化文件,可监视是否被卸载. 96 | 3.WebStream: 通过异步WWW下载的资源文件在内存中的解压版本,比SerializedFile大几倍或几十倍,重点监视.**** 97 | D. Memory Profiler-Assets: 98 | 1.Texture2D: 重点检查是否有重复资源和超大Memory是否需要压缩等. 99 | 2.AnimationClip: 重点检查是否有重复资源. 100 | 3.Mesh: 重点检查是否有重复资源. 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /常用功能/Unity3d缓存图片: -------------------------------------------------------------------------------- 1 | 在某种情况下,在网络上下载图片的时候,由于网络等原加载图片会很慢,就需要将图片缓存到本地。这样在读取本地图片会很快。 2 | 在网上也搜索了一些方法主要原理就是查找本地是否有这个文件, 3 | 然后决定是去网上下载,还是本地加载。 4 | 这里主要用到的方法就是读写本地文件和网上下载文件。 5 | 6 | 7 | 主要是利用MD5去鉴定本地文件中是否存在文件。 8 | -------------------------------------------------------------------------------- /常用功能/Unity场景加载进度条: -------------------------------------------------------------------------------- 1 | 实现思路: 2 | 1)加载器加载场景 3 | 2)加载进度通知UI 4 | 3)实时更新UI进度条 5 | 6 | 场景加载器: 7 | SceneLoader.cs: 8 | 9 | *********************** 10 | 11 | using UnityEngine; 12 | using System.Collections; 13 | using UnityEngine.SceneManagement; 14 | 15 | public class SceneLoader : MonoBehaviour 16 | { 17 | //场景名称 18 | public string sceneName; 19 | //进度条时间接受者 20 | public Transform progressReceiver; 21 | //加载进度 22 | private float _progress = 0; 23 | 24 | //异步加载信息 25 | private AsyncOperation async; 26 | 27 | public void Load() 28 | { 29 | 30 | } 31 | 32 | 33 | public void LoadScene(string sceneName) 34 | { 35 | 36 | } 37 | 38 | 39 | public bool isLoading{ 40 | get{return async != null;} 41 | } 42 | 43 | public float progress{ 44 | get{return _progress;}; 45 | } 46 | 47 | public void Update(){ 48 | if(async == null){ 49 | enabled = false; 50 | } 51 | else 52 | { 53 | //加载进度值范围为0 - 0.9,这里做处理 54 | float progreee = async.progress; 55 | progress = progress < 0.9f ? progress * (10 /9f) : 1; 56 | 57 | _progress = progress; 58 | if(progressReceiver != null) 59 | { 60 | progressReceiver.SendMessage(); 61 | } 62 | 63 | 64 | 65 | } 66 | 67 | } 68 | 69 | } 70 | 71 | 72 | 73 | 74 | 进度条: 75 | ProgressBar.cs: 76 | 77 | public class Progress : MonoBehaviour,IOnProgress{ 78 | 79 | //进度条 80 | private Slider slider; 81 | //进度文本 82 | private Text text; 83 | 84 | #if UNITY_EDITOR 85 | private void Reset(){ 86 | slider = GetComponentInChildren; 87 | text = GetComponentInChilder(); 88 | } 89 | 90 | #endif 91 | 92 | ///进度条更新事件 93 | public void OnProgress(float progress) 94 | { 95 | if(slider != null){ 96 | if(slider != null){ 97 | slider.value = progress; 98 | 99 | } 100 | if(text != null){ 101 | text.text = (int)(progress * 100) +"%; 102 | } 103 | 104 | } 105 | } 106 | 107 | 108 | 109 | } 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 进度更新接口: 121 | IOnProgress.cs: 122 | 123 | public interface IOnProgress{ 124 | 125 | /// 进度更新事件 126 | /// 进度百分比 127 | void OnProgress(float progress); 128 | 129 | } 130 | 131 | 132 | 通常情况下, 133 | 同步加载场景会导致线程阻塞。即在当前场景A当中,Unity切换场景,销毁A 134 | 利用异步加载场景 135 | 136 | 137 | 138 | 参考资料: 139 | 140 | 141 | 142 | 【Unityu场景加载进度条】:https://segmentfault.com/a/1190000015302921 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /常用功能/objCache对于Hierachy: -------------------------------------------------------------------------------- 1 | ObjCache遍历Hierachy,有助于再次获取物体 2 | -------------------------------------------------------------------------------- /常用功能/unity小型游戏寻路算法: -------------------------------------------------------------------------------- 1 | 1)随机寻路算法: 2 | 当NPC不管是遇到障碍物还是遇到了边界(利用碰撞检测),都会随机选取一个前进的方向,继续行走 3 | 4 | 2)跟踪算法: 5 | 当游戏中的主角进入到NPC 的“警戒区域”后,游戏的AI 可轻易获得目标的位置,然后控制NPC 对象移向被跟踪的对象 6 | 7 | 3)闪避算法: 8 | 和跟踪算法完全相反,也就是当游戏中的主角进入到NPC 的“警戒区域”后,主角可以去追着NPC跑 9 | 10 | 11 | -------------------------------------------------------------------------------- /常用设计模式梳理: -------------------------------------------------------------------------------- 1 | 创建型设计模式: 2 | 1)工厂模式: 3 | ---简单工厂模式 4 | ---工厂方法模式 5 | 2)建造者模式 6 | 7 | 创建型设计模式总结: 8 | 一:创建型设计模式对类的实例化过程进行了抽象,能够将对象的创建和对象的使用过程分离。 9 | 10 | 11 | 12 | 13 | 结构型设计模式总结: 14 | 1)装饰模式 15 | 2)组合模式 16 | 3) 外观模式 17 | 4)适配器模式 18 | 19 | 结构型:描述类和对象之间进行有效的组织,使用继承关系组织各类。 20 | 21 | 22 | 23 | 24 | 行为型设计模式总结: 25 | 1)中介模式 26 | 2)职责链模式 27 | 3)观察者模式 28 | 4)策略模式 29 | 5)模版方法模式 30 | 31 | 行为型设计模式总结: 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /游戏架构设计/SaveSystem: -------------------------------------------------------------------------------- 1 | public enum SaveType 2 | { 3 | XML, 4 | JSON, 5 | BINARY, 6 | CSV 7 | 8 | } 9 | 10 | public class SaveData 11 | { 12 | public int id; 13 | 14 | 15 | } 16 | 17 | public interface ISaveAndLoad() 18 | { 19 | public void Save(); 20 | public void Load(); 21 | public void UnLoad(); 22 | public void Delete(); 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /游戏架构设计/UI框架设计: -------------------------------------------------------------------------------- 1 | UI框架的设计: 2 | 3 | UI框架用于管理场景中的所有面板,控制面板之间的切换,加快开发进度,提高代码质量。【主要在于管理面板】 4 | 5 | 实现思路: 6 | 7 | 1)进入状态:界面第一次动态加载被使用【】 8 | 2)暂停状态:切换到其他界面的时候 9 | 3)继续状态:重新回到界面的时候 10 | 4)退出状态:界面不显示的时候 11 | 12 | 实现步骤: 13 | 1)使用JSON保存面板路径,枚举保存面板类型 14 | 2)根据界面的四种状态,创建UI基类BasePanel,场景界面继承该类。将四种状态写作虚方法, 15 | 分别为OnEnter(),OnPause(),OnResume(),OnExit() 16 | 3)通过管理类UIManager,解析JSON,管理UI界面的加载和切换。方便调用,做成单例模式, 17 | 分别从两个字典保存从JSON读取面板信息和已动态加载实例化的面板,通过栈实现面板之间的切换。 18 | 19 | 20 | 缺点明显,使用栈用来存储场景中依次打开的界面,只能够依次从栈顶界面开始关闭。 21 | 22 | OnEnter()->OnPause()->OnResume()->OnExit() 23 | 24 | UI框架类图: 25 | UIPanelType :存储面板路径和类型 26 | UIManager : 27 | panelPathDict:Dictionary 28 | panelDict:Dictionary 29 | panelStack:Stack 30 | 31 | BasePanel: 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /游戏架构设计/UI结构设计: -------------------------------------------------------------------------------- 1 | public class BasePanel : mMonoBehaviour 2 | { 3 | private CanvasGroup canvasGroup; 4 | private Button btn; 5 | 6 | public void Awake() 7 | { 8 | } 9 | 10 | 11 | private Button FindCloseBtn(string childName) 12 | { 13 | Button closeBtn = null; 14 | foreach(var item in GetComponentsInChildren