├── C# ├── 匿名方法和闭包 ├── 多继承和多重继承 ├── 链表问题 ├── 委托的使用 ├── Csharp属性访问器 ├── 虚方法,抽象类和接口的区别 ├── 委托和事件 ├── 委托delegate ├── unity委托 └── c#基础 ├── 常用功能 ├── objCache对于Hierachy ├── Unity3d缓存图片 ├── unity小型游戏寻路算法 ├── AStar原理 ├── Unity场景加载进度条 ├── Profile性能优化基础 └── MiniMap小地图 ├── README.md ├── 包设计packet ├── Shader ├── 计算机图形学-三大测试 ├── Shader之闪光Logo ├── Shader屏幕后处理(2) ├── UV之tilling和offset ├── 顶点变换 ├── shader计算像素点距离 ├── Shader之UV ├── 屏幕后处理Camera的Pipeline ├── Shader基础光照 ├── UV动画 ├── SpriteOutline Shader ├── 渲染管线流程 ├── Shader-新手引导(椭圆遮罩) ├── Shader之遮罩层 ├── Shader之透明度混合 ├── Shader流光效果 ├── Shader之顶点着色器 ├── shader扭曲 ├── Shader屏幕后处理 ├── Shader之着色器内置属性 ├── Outline Shader初步 ├── Shader之RenderType ├── shader空间变换 ├── Shader屏幕坐标ComputeScreenPos ├── 高斯模糊的实现 ├── Shader读取纹理贴图 ├── Shader透明效果 ├── Shader绘制基本图形 ├── Shader读取2D纹理贴图 ├── Shader消融 └── Shader之纯着色器 ├── Unity ├── RawImage ├── UI │ ├── 屏幕自适应 │ ├── UGUI拖拽 │ ├── UGUI事件系统 │ ├── UGUI渲染层级 │ └── unity的UI渲染机制 ├── Ray │ ├── Camera和Ray │ └── 射线Ray的原理 ├── Animation配置 ├── 物理组件 │ ├── Camera看向目标物体 │ ├── Rigidbody之velocity和Addforce │ ├── camera控制 │ ├── Transform组件 │ ├── Collider和Trigger │ ├── Rigidbody移动 │ ├── UGUI血条跟随角色 │ ├── 2d游戏碰撞检测 │ ├── CharacterController │ ├── 物理组件Collider │ └── Camera相机跟随问题 ├── 向量 ├── Ray射线API ├── AssetBundle │ ├── Unity5如何进行AsasertBundle打包 │ ├── AssertBundle资源加载框架设计 │ ├── AssetBundle资源加载详解 │ ├── AssetBundle之打包Prefab │ ├── AssetBundle加载 │ ├── AssetManager(2) │ ├── AssertBundle基本理论 │ └── AssetBundleManager管理 ├── 生命周期 │ ├── Unity中Update和FixUpdate和LaaterUpdate │ └── Awake()和Start区别 ├── AudioManager ├── SerializeField的用法 ├── Xml存储 ├── UnityEditor编辑器 ├── 四元数Quaternion └── 协程 │ ├── 协程原理分析 │ └── 协程工作流程 ├── 音频 ├── 游戏架构设计 ├── Unity缓存池 ├── Unity的UI层级管理框架 ├── unity自定义消息通知组件 ├── SaveSystem ├── 游戏UI框架设计思路 ├── Unity游戏客户端通用框架设计 ├── 行为树设计 ├── XML序列化 ├── unity框架设计(二) ├── UI框架设计 ├── unity游戏音效 ├── Unity脚本加载和编译 ├── Unity工具类系列之对象池 ├── UI结构设计 ├── Unity事件管理器 ├── unity游戏架构设计之对象管理 ├── unity基础 ├── 资源加载 ├── Unity消息机制 ├── 任务系统架构 └── Unity基于事件机制的消息系统 ├── 网络通信 ├── Unity的Socket网络连接模块 ├── Token设计JWT ├── 网络消息包Packet封装 ├── protobuf实现传输 └── Socket粘包和分包 ├── 设计模式 ├── 外观模式 ├── 桥接模式 ├── 备忘录模式 ├── 代理模式 ├── 对象池模式 ├── 原型模式 ├── Unity设计模式 ├── 策略模式 ├── 装饰模式 ├── 命令模式 ├── 模版方法模式 ├── 状态机模式 ├── 责任链模式 └── 建造者模式 ├── 设计模式和处理请求 ├── 动画导入设置 ├── Unity旋转 ├── 常用设计模式梳理 ├── Json ├── vector.forward和ransform.forward ├── Lambda表达式 ├── 单例模式 ├── 网络输水模型 ├── Shader之Sprite Outline ├── LICENSE ├── UnityInspecotr编辑器扩展 ├── Shade之屏幕后处理(1) └── RPG_角色属性设计 /C#/匿名方法和闭包: -------------------------------------------------------------------------------- 1 | 外部变量: 2 | 3 | 4 | 捕获的外部变量: 5 | 6 | 7 | -------------------------------------------------------------------------------- /C#/多继承和多重继承: -------------------------------------------------------------------------------- 1 | 多继承和多重继承: 一字之差,谬以千里。 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /常用功能/objCache对于Hierachy: -------------------------------------------------------------------------------- 1 | ObjCache遍历Hierachy,有助于再次获取物体 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity_Interview_Analysis 2 | Unity3D开发面试的刷题和记录 3 | -------------------------------------------------------------------------------- /包设计packet: -------------------------------------------------------------------------------- 1 | //数据包 2 | 3 | public class Package 4 | { 5 | 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /Shader/计算机图形学-三大测试: -------------------------------------------------------------------------------- 1 | Alpha测试: 符合条件的Alpha像素显示出来,不符合的去掉。 2 | 3 | Stencil模板测试:符合条件的通过,不符合条件的像素丢弃掉。 4 | 5 | -------------------------------------------------------------------------------- /Unity/RawImage: -------------------------------------------------------------------------------- 1 | RawImage组建主要用来显示非交互的图像控件,一般用于装饰或者图标。 2 | 【RawImage支持任何类型的纹理,但Image控件只支持Sprite类型的纹理】 3 | 4 | 5 | -------------------------------------------------------------------------------- /Shader/Shader之闪光Logo: -------------------------------------------------------------------------------- 1 | 透明度混合实现闪光: 2 | 3 | 4 | 5 | https://blog.csdn.net/candycat1992/article/details/25720243 6 | -------------------------------------------------------------------------------- /Shader/Shader屏幕后处理(2): -------------------------------------------------------------------------------- 1 | 屏幕后处理的技术手段: 2 | 1)利用卷积进行图像的边缘检测 3 | 2)高斯模糊 4 | 3)Bloom效果 5 | 4)运动模糊 6 | 5)径向模糊【Radial Blur】 7 | 8 | -------------------------------------------------------------------------------- /Unity/UI/屏幕自适应: -------------------------------------------------------------------------------- 1 | 1)UI控件锚点自适应 2 | 3 | 2)Canvas自适应,Scale with screen size,根据屏幕需要设置ReferenceResolution的值 4 | 5 | 3)视口自适应 6 | -------------------------------------------------------------------------------- /Shader/UV之tilling和offset: -------------------------------------------------------------------------------- 1 | offset:贴图的起始位置,取值范围[0,1] 2 | 3 | tilling:从offset位置开始平铺图片,超过部分(即空白处),则按照比例生成新的区域拼接到空白处。取值[-1,1] 4 | 5 | -------------------------------------------------------------------------------- /Unity/Ray/Camera和Ray: -------------------------------------------------------------------------------- 1 | Unity中把射线碰到的店(hit)转换为非主摄像机的屏幕坐标?? 2 | 1) 3 | public Camera myCamera; 4 | myCamera.WorldToScreenPoint(hit); 5 | 6 | -------------------------------------------------------------------------------- /Unity/Animation配置: -------------------------------------------------------------------------------- 1 | Animation之HasExitTime: 2 | 勾选项:有退出时间,即当前动画要播放完毕后才能向下一步动画 3 | 不勾选:无退出时间,即只要满足条件即可立即跳转 4 | 5 | 一般不要勾选HasExitTime 6 | 7 | 8 | -------------------------------------------------------------------------------- /Unity/UI/UGUI拖拽: -------------------------------------------------------------------------------- 1 | 最简洁版本: 2 | public class Drag : MonoBehaviour,IDragHandler,IPointerDownHandler 3 | { 4 | 5 | 6 | 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /音频: -------------------------------------------------------------------------------- 1 | Unity一共支持4种音频格式文件: 2 | 3 | aiff:短音频,打斗音效 4 | wav:短音频,打斗音效 5 | mp3:长音频,背景音乐 6 | ogg:长音频,背景音乐 7 | 8 | Unity外部加载音频,从unity5.4开始就不支持MP3外部加载的格式了,目前支持OGG,wav 9 | -------------------------------------------------------------------------------- /Shader/顶点变换: -------------------------------------------------------------------------------- 1 | 顶点换换的目的: 2 | 3 | 本地坐标系 4 | | 5 | 世界坐标系 6 | | 7 | 相机坐标系 8 | |—————————————— 透视投影 裁剪空间 屏幕坐标系 9 | |——————————————正交投影 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /游戏架构设计/Unity缓存池: -------------------------------------------------------------------------------- 1 | Unity缓存池: 2 | 3 | 缓存池,顾名思义就是为了缓存而存在的。 4 | 优点:使得游戏的运行更加流畅 5 | 缺点:占用部分内存 6 | 7 | 8 | 常使用PoolManager插件,但是会导致代码较为庞大 9 | 10 | 预加载和对象缓冲池技术: 11 | -------------------------------------------------------------------------------- /Shader/shader计算像素点距离: -------------------------------------------------------------------------------- 1 | Shader "Custom/SetColorInFrag"{ 2 | 3 | Properties{ 4 | _ZeroPoint("ZeroPoint",Vector) = (0,0,0,0) 5 | _Distance() 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /网络通信/Unity的Socket网络连接模块: -------------------------------------------------------------------------------- 1 | https://blog.csdn.net/claine/article/details/52374546 2 | 3 | 4 | 启动Socket框架,主要通过ip,port异步连接。 5 | 6 | 7 | 成功,则new线程,开始接受数据。 8 | 异常:超时,失败处理 9 | -------------------------------------------------------------------------------- /网络通信/Token设计JWT: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | //头部Token 5 | public class Header_Token 6 | { 7 | public long expirty; 8 | public byte type; 9 | public long issureId; 10 | } 11 | -------------------------------------------------------------------------------- /Unity/物理组件/Camera看向目标物体: -------------------------------------------------------------------------------- 1 | 相机Camera中,LookAt是直接看过去,缺少旋转过程。 2 | 3 | void CameraRotateToLookAt(Transform target,float rotateSpeed) 4 | { 5 | //取得目标物体相对相机的法向量 6 | 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Unity/物理组件/Rigidbody之velocity和Addforce: -------------------------------------------------------------------------------- 1 | Rigidbody.Velocity: 2 | 3 | 瞬间为物体添加一个恒定的速度,将物体提升到该速度。 4 | 5 | Rigidbody.addForce: 6 | 7 | 瞬间给物体一个力 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /常用功能/Unity3d缓存图片: -------------------------------------------------------------------------------- 1 | 在某种情况下,在网络上下载图片的时候,由于网络等原加载图片会很慢,就需要将图片缓存到本地。这样在读取本地图片会很快。 2 | 在网上也搜索了一些方法主要原理就是查找本地是否有这个文件, 3 | 然后决定是去网上下载,还是本地加载。 4 | 这里主要用到的方法就是读写本地文件和网上下载文件。 5 | 6 | 7 | 主要是利用MD5去鉴定本地文件中是否存在文件。 8 | -------------------------------------------------------------------------------- /设计模式/外观模式: -------------------------------------------------------------------------------- 1 | 外观模式:Facade Pattern:隐藏系统的复杂性,向现有系统添加接口,来隐藏系统复杂性。向客户端提供一个可以访问的接口。 2 | 这种模式设计一个单一的类,此类向客户端请求的简化方法和对现有系统的方法的委托调用。 3 | 4 | 5 | 外观角色:Facade 6 | 客户端通过操作外观角色达到控制子系统的目的。 7 | 子系统角色:SubSystem 8 | 表示系统的子系统模块 9 | 10 | -------------------------------------------------------------------------------- /C#/链表问题: -------------------------------------------------------------------------------- 1 | 链表问题的解决思路: 2 | 1)创建一个虚拟头结点(主要用于头结点可能被改变的场景之下),以便链接到节点之中 3 | 4 | 5 | 快慢指针: 6 | 7 | 一般思路: 8 | 快慢指针定义两根指针,移动速度一快一慢。这个差值找到链表相应的节点。 9 | 10 | 快指针先走N步,然后再快指针和慢执着呢一起走,当快指针走完全程,慢指针就是倒数第N个。 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /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/屏幕后处理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 | -------------------------------------------------------------------------------- /常用功能/unity小型游戏寻路算法: -------------------------------------------------------------------------------- 1 | 1)随机寻路算法: 2 | 当NPC不管是遇到障碍物还是遇到了边界(利用碰撞检测),都会随机选取一个前进的方向,继续行走 3 | 4 | 2)跟踪算法: 5 | 当游戏中的主角进入到NPC 的“警戒区域”后,游戏的AI 可轻易获得目标的位置,然后控制NPC 对象移向被跟踪的对象 6 | 7 | 3)闪避算法: 8 | 和跟踪算法完全相反,也就是当游戏中的主角进入到NPC 的“警戒区域”后,主角可以去追着NPC跑 9 | 10 | 11 | -------------------------------------------------------------------------------- /设计模式/桥接模式: -------------------------------------------------------------------------------- 1 | 将抽象和实现分离,使得二者独立变化。 2 | 3 | 多人角色的武器系统,武器装备搭配需奥组装。 4 | 5 | 将不同的武器声明为一个枚举类型,然后写一个武器接口,同时写一个角色抽象接口包含一个武器成员变量, 6 | 一个攻击的通用方法,然后让enemy和Solider分别继承自角色抽象接口,利用switch判断武器枚举类型,进行相应操作。 7 | 8 | 虽然处理方便,但是武器越多修改时越困难,利用桥接模式解决问题。 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Shader/Shader基础光照: -------------------------------------------------------------------------------- 1 | 光纤与物体相交的结果: 2 | 1)散射(scattering) 3 | 2) 吸收(absorption) 4 | 5 | 散射:只去改变光线的方向,但不改变光线的密度和颜色。 6 | 7 | 两种方向:内部和外部,对应折射和反射。 8 | 折射:散射到物体内部,用漫反射(diffuse)模型来计算 9 | 反射:散射的物体外部,用高光反射(specular)模型来计算 10 | 11 | 吸收:只去改变光线的密度和颜色,但是步改变光线的方向 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /常用功能/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 | -------------------------------------------------------------------------------- /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的UI层级管理框架: -------------------------------------------------------------------------------- 1 | UI框架的设计是任何游戏都需要做的事情,其中事件管理器(EventManager)是比较常用的UI和逻辑分离的方法。 2 | 3 | 通过注册,绑定,分发事件来控制UI界面或者游戏场景的逻辑处理。 4 | 5 | 6 | UI和逻辑分离: 7 | 8 | 最基本的需求,不希望日后游戏更换UI需要到处修改逻辑,想要的是UI界面的负责展示。 9 | 10 | 一个UIManager来定义UI变化的逻辑,UI变化由事件驱动,所以UI界面 + UIManager + 事件分发器 就可以了。 11 | 以后换UI只需要修改UI界面就可以了。 12 | -------------------------------------------------------------------------------- /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/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/渲染管线流程: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /设计模式和处理请求: -------------------------------------------------------------------------------- 1 | 责任链模式,命令模式,中介模式和观察者模式,用于处理请求发送者和接受者之间不同的连接方式。 2 | 3 | 1)责任链按照顺序将请求动态传递给一系列的潜在接受者,直到其中一名接受者对请求进行处理。 4 | 2)命令在发送者和请求者之间建立单向连接 5 | 3)中介者清除了发送者和请求者之间的直接连接,强制他们通过一个中介对象进行间接沟通 6 | 4)观察者允许接受者动态订阅或者取消接收请求 7 | 8 | 9 | 中介者和观察者之间很难区别。。 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /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之遮罩层: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /动画导入设置: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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自定义消息通知组件: -------------------------------------------------------------------------------- 1 | 客户端消息机制是十分广泛的。 2 | 3 | using System; 4 | public class Notification 5 | { 6 | public Enum Type{set;get;} 7 | 8 | public Object[] Params{set;get;} 9 | 10 | public Object Target{set;get;} 11 | 12 | public Notification(){} 13 | } 14 | 15 | 16 | https://www.cnblogs.com/monkeycoder/articles/6860132.html 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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旋转: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /游戏架构设计/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /常用设计模式梳理: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /游戏架构设计/游戏UI框架设计思路: -------------------------------------------------------------------------------- 1 | UI窗体功能设计分布: 2 | 1)窗体自动加载管理 3 | 2)窗体生命周期管理 4 | 3)模态窗体实现 5 | 4)资源管理化 6 | 5)缓存UI窗体 7 | 6)窗体层级管理 8 | 7)消息中心 9 | 8)日志调试 10 | 11 | UI框架核心设计: 12 | 13 | 根据UI框架的核心设计理念,定义三个核心功能: 14 | 1)UI窗体的自动加载 15 | 2)缓存UI窗体 16 | 3)窗体生命周期(状态)管理 17 | 18 | UI框架设计的目的: 完成和具体游戏逻辑无关的底层事物。 19 | 20 | 四个核心类: 21 | 1)BaseUIForms: 基础UI窗体脚本 22 | 2)UIManager: UI窗体管理器脚本 23 | 3)UIType:窗体类型 【引用窗体的重要属性:枚举类型】 24 | 4)系统定义类 【框架使用的枚举类型,委托事件,系统常量 25 | -------------------------------------------------------------------------------- /游戏架构设计/Unity游戏客户端通用框架设计: -------------------------------------------------------------------------------- 1 | 1)UI框架(UGUI + MVC) 2 | 2)消息框架 3 | 3)网络层框架(Socket + Protobuf) 4 | 4)表格数据(Protobuf) 5 | 5)资源管理(Unity5.x的AssertBundle方案) 6 | 6)热更新框架(toLua) 7 | 8 | 9 | 10 | 11 | 编写UI框架的意义: 12 | 1)打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager 13 | 2)功能逻辑分散化,每个页面维护自身逻辑,一坨框架便于多人协同开发,不用关心跳转和显示关闭等细节 14 | 3)通用性框架能够做到简单代码复用和“项目经验”积累 15 | 16 | 17 | 18 | 泰斗破坏神2商业实战班 第1期: 19 | 海洋老师:战斗相关开发 20 | 肖红老师:UI框架开发及网络 21 | 卡卡大神:带高性能java服务器加盟泰斗2 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/物理组件/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/物理组件/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /设计模式/备忘录模式: -------------------------------------------------------------------------------- 1 | 行为设计模式: 2 | 备忘录模式,作为一种对象的行为模式。 3 | 4 | 备忘录本质:保存和恢复状态。 5 | 6 | 不破坏封装的前提下,捕获一个对象的内部状态,并且在对象之外保存这个状态, 7 | 这样可以将对象恢复(undo/rollback)到原先的状态。 8 | 9 | 实际应用场景就是: 10 | 游戏存档。 11 | (1.保存/恢复数据的相关状态 12 | 2.提供一个可回滚的操作 13 | ) 14 | 注意事项: 15 | 1.符合迪米特法则,增加备忘录管理类 16 | 2.为节约内存,使用原型 + 备忘录 模式 17 | 18 | Originator(原发器): 19 | 普通类,创建备忘录,并且存储当前内部状态,也可以使用备忘录恢复内部状态。 20 | 一般需要保存内部状态的类设计为原发器。 21 | 22 | Memento(备忘录): 23 | 存储原发器的内部状态(根绝原发器来决定保存哪些内部状态) 24 | 25 | 26 | Caretaker(负责人): 27 | 28 | 29 | 30 | 31 | 32 | 成就系统: 33 | 34 | 保存每一项成绩的最佳纪录,并且不需要保留多个版本,运用备忘录模式。 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /游戏架构设计/行为树设计: -------------------------------------------------------------------------------- 1 | 行为树的实现:主要依赖节点来实现。 2 | 3 | 行为树主要由几大部分组成: 4 | 1)Composites 组合节点 5 | 2) Decorator 装饰节点 6 | 3) Actions 行为节点 7 | 4) Conditinals 条件节点 8 | 9 | 首先,是行为节点基类: 10 | [System.Serializable] 11 | public abstract class Node 12 | { 13 | public delegate NodeStates NodeReture(); 14 | 15 | protected NodeStates nodeState; 16 | 17 | public NodeStates nodeState 18 | { 19 | get; 20 | 21 | } 22 | 23 | public Node(){} 24 | 25 | public abstract NodeStates Evaluate(); 26 | } 27 | 28 | public class Selector : Node 29 | { 30 | 31 | 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /设计模式/代理模式: -------------------------------------------------------------------------------- 1 | 代理模式大多数用来优化资源的加载: 2 | 1)当资源已经加载过时,使用原来的资源 3 | 2)当资源没有加载时,开始加载 4 | 所有过程由代理模式控制 5 | 6 | abstract class Component 7 | { 8 | public abstract void Add(Component c); //增加成员 9 | public abstract void Remove(Component c);//删除成员 10 | public abstract Component GetChild(int i);//获取成员 11 | public abstract void Operation(); //业务方法 12 | 13 | 14 | } 15 | 16 | public class Leaf : Component 17 | { 18 | public override void Add(Component c) 19 | { 20 | //异常处理或者错误提示 21 | } 22 | 23 | 24 | 25 | 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /游戏架构设计/XML序列化: -------------------------------------------------------------------------------- 1 | namespace common.xml 2 | { 3 | public abstract class DataBaseEntry 4 | { 5 | [XmlIgnore] 6 | public ID DataBaseID{get;set;} 7 | 8 | [XmlIgnore] 9 | public DataBase DataBase{get;set;} 10 | 11 | 12 | 13 | } 14 | 15 | 16 | } 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Xml存储地图信息: 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | namespace class XmlSerialization 36 | { 37 | 38 | #region 对象和XML字符串转换 39 | public static string Serialize(Type type,object obj,string xmlRootName) 40 | { 41 | 42 | } 43 | 44 | 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /游戏架构设计/unity框架设计(二): -------------------------------------------------------------------------------- 1 | 游戏本身业务仅仅提供抽象层从而提高业务逻辑的独立性和可维护行。 2 | 框架部分提供项目的基础设置: 3 | 1)资源管理 4 | 2)网络通信 5 | 3)UI框架 6 | 4)消息管理 7 | 5)场景管理 8 | 6)数据解析和存取 9 | 10 | (一)资源管理: 11 | 12 | 资源管理模块负责按照场景的划分:将所有游戏资源打包到AssertBundle之中,并且在游戏中动态加载 13 | 打包前将对应的资源索引文件和二进制文件(AB)放入StreamingAssertsPath下。 14 | 游戏初次运行将所有资源拷贝到可读写路径PersistentDataPath 15 | 16 | 17 | 更新阶段从服务器下载更新配置文件并根据本地资源的MD5更新资源文件,场景载入阶段异步加载场景需要的AssetBundle,资源 18 | 加载时根据资源索引文件加载资源,离开场景时卸载相关AssetBundle 19 | 20 | ResourceManager: 21 | 22 | 23 | 需要研究: 24 | 大型优秀源码的工作: 25 | 26 | https://blog.csdn.net/u013108312/article/category/6669595/2? 27 | 28 | https://blog.csdn.net/aawoe/article/details/80903614 29 | -------------------------------------------------------------------------------- /网络通信/网络消息包Packet封装: -------------------------------------------------------------------------------- 1 | 网络消息报: 2 | 3 | Packet: 4 | 5 | public class Packet : IDisposable 6 | { 7 | public int ptr; //数据读写的游标指针 8 | public int length; //所占位数 9 | public byte[] data; //字节数组,读写数据都在这 10 | 11 | public Packet(byte[] array) : this(array,array.Length) 12 | {} 13 | 14 | public Packet(byte[] array,int size) 15 | { 16 | ptr = 0; 17 | data = array; 18 | length = size << 3; 19 | } 20 | 21 | 22 | //每个数组类型的写入都是讲一个字节按照指定位数从某个位置开始写 23 | public void WriteByteAtPtr(byte value,int bits) 24 | { 25 | if(bits > 0) 26 | { 27 | 28 | } 29 | 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /设计模式/对象池模式: -------------------------------------------------------------------------------- 1 | 对象池模式是一种创建型设计模式。 2 | 持有偶一个初始化好的对象集合,将对象提供给调用者。 3 | 4 | 对象池的目的: 5 | 减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用。 6 | 7 | 一个对象池的创建涉及多种角色: 8 | 1)Reusable可重用的对象 9 | 2)Client调用者 10 | 3)ReusablePool可重用的对象的池 11 | 12 | Resuable: 13 | 1)创建成本较大,比如线程或者数据库连接 14 | 2)被ResablePool持有 15 | 3)被Client消费者使用,使用完备返回到ReusablePool 16 | 17 | ReusablePool: 18 | 1)维护一定数量的Reusable,提供给客户端使用 19 | 2)提供aquire或者obtain等方法,便客户端请求Reusable 20 | 3)提供recycle或者release等方法,便于客户端使用完毕后,将Reusable对象返回 21 | 22 | Client: 23 | 请求ResuablePool或者Resuable对象 24 | 25 | 池的大小选择: 26 | 1)通常情况下,我们需要控制对象池的大小 27 | 2)如果对象池没有限制,可能导致对象池持有过多的闲置对象,增加内存的占用 28 | 3)如果对象池闲置过小,没有可用的对象时,会造成之前对象池无可用的对象时,再次请求出现的问题 29 | 4)对象池的大小选取应该结合具体的使用场景,结合数据(触发池中无可用对象的频率)分析来确定。 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /设计模式/原型模式: -------------------------------------------------------------------------------- 1 | Prototype原型设计模式:用于创建重复的对象,同时还需要保证性能,这种设计模式属于对象创建型模式,提供了一种创建对象的最佳方式。 2 | 3 | 实现一个原型接口,用于创建当前对象的克隆。 4 | 5 | 原型模式就是使用原型指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 6 | 简而言之,就是从一个对象再次创建另外一个可定制对象,而且不需要直到任何的创建细节。 7 | 8 | 比如:地图中很多怪物之间具有共通性,这些怪物都是以基类怪物为父本克隆的子类物体。 9 | 10 | 此处以武器为例子: 11 | 12 | public abstract class Sword 13 | { 14 | protected string Name; 15 | protected int Atk; 16 | protected int Speed; 17 | 18 | public Sword() 19 | { 20 | Name = _Name; 21 | Atk = _Atk; 22 | Speed = _Speed; 23 | 24 | 25 | } 26 | 27 | public abstract Sword Clone(); 28 | 29 | 30 | 31 | 32 | } 33 | 34 | 35 | 36 | 37 | 使用场景: 38 | 1)类初始化较多资源 39 | 2)构造函数比较复杂 40 | 3)循环体中产生大量对象 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/高斯模糊的实现: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /游戏架构设计/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /游戏架构设计/unity游戏音效: -------------------------------------------------------------------------------- 1 | Unity3D游戏引擎一共支持4个音乐格式的文件: 2 | 3 |   .AIFF 适用于较短的音乐文件可用作游戏打斗音效 4 | 5 |   .WAV 适用于较短的音乐文件可用作游戏打斗音效 6 | 7 |   .MP3 适用于较长的音乐文件可用作游戏背景音乐 8 | 9 |   .OGG 适用于较长的音乐文件可用作游戏背景音乐 10 | 11 | 12 | 1,游戏音乐:适用较长的音乐,如背景音乐。 13 | 14 | 2,游戏音效:适用较短的音乐,如游戏出大招的音效。 15 | 16 | =========================================== 17 | Unity优化中对游戏性能优化,声音优化非常关键。 18 | 19 | 20 | 了解Audio Clips、Audio Listeners、AudioSources的基本操作: 21 | 22 | 23 | AudioClip设置: 24 | 取消Preload Audio Data(预加载音频数据)选项。然后把Override for Android选项勾上,并且设置Load Type为Streaming。 25 | 这样做可以很好的优化游戏音效的占用 26 | 27 | 28 | 音频可视化: 29 | 30 | https://blog.csdn.net/noEnoughChief/article/details/82984635 31 | 32 | public interface IAudio{ 33 | AudioSource GetAudioSource(); 34 | void OnPlayVideo(AudioClip clip); 35 | } 36 | 37 | public class AudioManager{ 38 | 39 | } 40 | -------------------------------------------------------------------------------- /单例模式: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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脚本加载和编译: -------------------------------------------------------------------------------- 1 | 一.C#作为以Assembly【汇编集】为基本单元,dll就是一个assembly,dll之间有加载以来顺序 2 | 3 | Assets/*.dll 4 | Stamdard Assets/*.cs -> Assembly-Csharp-First-Pass.dll 5 | Assets/*.cs -> Assembly-Csharp.dll 6 | Standard Assets/Editor/*.cs -> Assembly-Csharp-First-pass-editor.dll 7 | /Editor/*.cs -> Assembly-Csharp-Editor.dll 8 | 9 | 10 | 11 | 12 | 二、脚本编译顺序 13 | Unity保留了一些项目文件夹名称来标识其内的内容具有一些特殊目的。而其中一些文件夹会影响脚本编译的编译顺序。这些文件夹名称是: 14 | 15 | Assets 16 | Editor 17 | Editor default resources 18 | Gizmos 19 | Plugins 20 | Resources 21 | Standard Assets 22 | StreamingAssets 23 | 24 | 脚本编译有四个不同的阶段。处于哪个阶段编译由其父目录确定。 25 | 在脚本必须引用其他脚本中定义的类容的情况下,编译的顺序就非常重要了。基本规则是, 26 | 在当前阶段之后编译的任何东西都引用不到,在当前阶段之前或早期的阶段编译的任何内容都可以完全引用。 27 | 28 | 编译阶段如下: 29 | 30 | 阶段1:编译Standard Assets、Plugin中的运行时脚本 31 | 阶段2:编译Standard Assets、Plugin中的Editor脚本 32 | 阶段3:编译其他不在Editor目录下的脚本 33 | 阶段4:编译Editor目录下的脚本 34 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /网络输水模型: -------------------------------------------------------------------------------- 1 | 互联网情况错综你复杂,数据包需要经过无数网络设备,延时无法避免。 2 | 由于TCP协议在网络层约定保证数据包的顺序切不会丢失。 3 | 所以,需要一个容器去存储数据。 4 | 5 | 查看网络通信可知,网络数据在传输过程中会出现缓存现象。 6 | 简单来说:就是连续发送N个数据包。【粘包】 7 | 8 | 对于接收端来说,也不能很好保证每次都接收到1个完整的数据包,很多时候是X,Y个数据包。 9 | 10 | 在回到输水模型,容器等待水的到来。 11 | 12 | 现在存在超时时间,即每次等待水到来有一个最大时间, 13 | 即:超过这个时间之后,及时没有得到一滴水,则处理这个水桶。 14 | 所以,得到水的理论值是大于等于0,小于水桶容量。 15 | 16 | 代码分析: 17 | bytep[ buffer = new byte[MAX_LEN], 18 | 即数据包读取缓冲区。 19 | read():使用buffer读数据流, 20 | len:实际独处的数据长度,len <= MAX_LEN,len>=0(等于0不处理,等于-1视为连接断开) 21 | 22 | buffer缓冲区,里面存储len长度可用数据。 23 | 需要做的就是根据协议结构,将buffer传话为遵循协议的数据包packet,交由后面的业务逻辑代码处理。 24 | 25 | SO。定义逻辑层通信协议。 26 | byteHeader:包头,用于验证数据包合法性验证。 27 | 2byte的数据包长【一般4byte,即一个int】 28 | 剩下内容为可变长度的数据包体 29 | 30 | 31 | 32 | 拿到buffer,存在分包,粘包和增速宝。 33 | 实际上,获得packet时我们仅仅需要知道数据包packet的真实长度。即2byte,转为short后假设为PACKET_LEN, 34 | 然后秩序拆分和等待 PACKET_LEN长度的byte即可。 35 | 36 | ByteBuffer:底层还是 固定长度的byte[],仅仅是封装了byte操作的快捷方式。 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /游戏架构设计/Unity工具类系列之对象池: -------------------------------------------------------------------------------- 1 | ##游戏开发的过程当中首当其冲最重要的是: UI。## 2 | 3 | 工具类之对象池: 4 | 5 | 游戏UI中产生大量相同类型的物体时。如果每次创建完成都删除掉,会造成频繁的资源回收[GC]。 6 | 7 | 此时,对象池就是一种良好的设计模式,利于优化。 8 | 9 | 10 | 对象池的核心思想是:预先初始化一组可重用的实体,而不是按需销毁然后重建 11 | 12 | 13 | 我们利用对象池的核心思想就是“当我们第一次实例化好多个Item物体后,下一次打开如果界面更新就没必要再 14 | 创建第二次重复的资源物体了”,因此如果这个界面关闭的时候应该把资源临时存放在一个“池”里面。 15 | 16 | pool: 17 | --item 18 | --item 19 | --item 20 | 21 | 外表看来,就是资源保存在池中,外部表现就是item物体修改了父物体,然后关闭激活。 22 | 23 | 打开界面的时候,因为池中有资源,跳过读取资源实例化的过程。【资源加载和实例化是消耗极大性能的】 24 | 25 | /*这里有必要强调的是,我们没必要单独去做第一次对象池的实例化,我们只需要知道,这个物体是重复的,所以这个物体我们都在 26 | 对象池中去取。而对象池也只关注,当别人调用了我的创建实例函数,我必须返回给它一个创建好的物体,而池子里面有 27 | 没有资源让对象池自己判断,如果没有则创建,如果有则直接拿出来给它,这种设计方法叫做空池触发*/ 28 | 29 | 30 | 对象池存储对象【一种或者多种】: 31 | 32 | 33 | 多种: 34 | 35 | 思路: 36 | 将Queue 转换为Dictionary> 处理作为存储对象的功能。同时, 37 | 我们需要对象池可以识别多种对象,所以加入 Dictionary类型的变量存储物体Tag. 38 | 39 | 查阅:https://www.bilibili.com/read/cv54304 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/协程/协程原理分析: -------------------------------------------------------------------------------- 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/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 | -------------------------------------------------------------------------------- /游戏架构设计/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