






















创建于 2025年3月4日 17:24:35 · 约13,119字
| 游戏类型 | ||
|---|---|---|
| AVG | 文字冒险游戏 | Adventure Game |
| SLG | 模拟策略游戏 | Simulation Game |
| RTS | 及时战略游戏 | Real Time Strategy Game |
| MMO | 大型多人在线游戏 | Massive Multiplayer Online Game |
| MOBA | 多人在线战术竞技游戏 | Multiplayer Online Battle Area |
| TBS | 回合制策略游戏 | Turn Based Strategy Game |
| FTG | 格斗游戏 | Fighting Game |
| STG | 射击游戏 | Shooting Game |
| PZL | 益智游戏 | Puzzle Game |
| RCG | 竞速游戏 | Racing Game |
| SPT | 体育游戏 | Sports Game |
| CAG | 卡牌游戏 | Card Game |
| TAB | 桌面游戏 | Table Game |
| MSC | 音乐游戏 | Music Game |
| 游戏内名词 | |
|---|---|
| PVE | 玩家VS AI敌人 Player VS Environment |
| PVP | 玩家VS玩家 Player VS Player |
| HOT | 指持续性治疗效果 Heal Over Time |
| DOT | 持续性伤害效果 Damage Over Time |
| AOE | 法术场效果 Area of effect |
| AFK | 脱手操作,放置型 Away from keyboard |
| DD | 直接伤害,非持续性伤害 Direct Damage |
| DPS | 每秒造成伤害 Damage per second |
| BUFF | 主要指辅助类角色为别人施加的有益状态,通俗的说法就是“加状态” |
| NPC | Non Player Character(非玩家角色) |
| 运营术语 | ||
|---|---|---|
| ACU | Average Concurrent Users | 24小时内每小时平均在线用户 |
| DAU | Daily Active User | 日活跃用户量 |
| PCU | Peek Concurrent Users | 24小时内同时最高在线人数 |
| DNU | Daily New User | 单日新增用户 |
| URR | Users Retention Ratio | 用户留存率 |
| ARPU | Average Revenue Per User | 平均每个用户产生的收入 |
| ARPPU | Average Revenue Per Paying User | 平均每个付费用户产生的收入 |
| DCR | Daily Churn Rate | 日流失率 |
| CP | Content Provider | 内容提供商 |
| SP | Server Provider | 服务提供商 |
| QA | Quality Assesment | 质量测试 |
| QC | Quality Control | 质量控制 |
| CB | Close Beta | 封闭删档测试 |
| OB | Open Beta | 开放不删档测试 |
| 国产原创黑话 | |
|---|---|
| 大中小R | 什么都充,活动充,新手礼包充 |
| 洗人 | 把玩家从某个游戏拉到另一个游戏 |
| 马甲包 | 换皮,稍微改一点点美术变成另一个游戏 |
| 冲榜 | 冲击各种游戏榜单 |
| 起量 | 流量逐渐上升 |
| 爆量 | 流量短期突然上升 |
| 买量 | 通过花钱打广告获得流量和用户 |
| 换量 | 不用应用内的广告相互推广,我的点进你的,你的进我的 |
| 导量 | 通过渠道把用户引入游戏内 |
| 卖量 | 渠道把流量卖给应用商 |
| 七大类 | 广电总局规定的,七种审核类型。“不涉及政治、军事、民族、宗教等题材内容, 办理周期短且无故事情节或者情节简单的 消除类、跑酷类、飞行类、棋牌类、解谜类、体育类、音乐舞蹈类 |
| 互联网通用黑话 | 这个随便看看,反正遍地是 |
|---|---|
| 复盘 | 回忆总结之前的工作 |
| 闭环 | 一套成体系自洽的方案 |
| 脑暴 | 头脑风暴,点子idea |
| 痒点 | 没有也还行,有更好的东西 |
| 痛点 | 没有他不太行,但他还真没有的东西 |
| 爽点 | 成功给到了他想要的东西 |
| 差异化 | 要不同有区别 |
| 底层逻辑 | 深层原理 |
| 对/碰 | 讨论 |
| 对齐 | 意见统一 |
| 生态 | 某个领域圈子 |
| 颗粒度 | 细致程度 |
| 引爆点 | 突然让人关注的地方 |
| 背书 | 找有名的人,机构做担保,推荐 |
包管理的几种方式 #todo 包管理啊嗯
manifest.json定义自动安装Window->PackageManager中Add package from git URL填入对应的地址。 .dll 到Library里,在 Unity 编辑器里 Assets->ReimportAll或者删除Library重启 .npm install -g openupm-cli就可以使用openupm的命令了| 常用预处理器指令 | 描述 |
|---|---|
| UNITY_EDITOR | 脚本符号,用于从游戏代码中调用 Unity 编辑器脚本。 |
| UNITY_EDITOR_WIN | Windows 上编辑器代码的脚本符号。 |
| UNITY_EDITOR_OSX | Mac OS X 上编辑器代码的脚本符号。 |
| UNITY_EDITOR_LINUX | Linux 上编辑器代码的脚本符号。 |
| UNITY_STANDALONE_OSX | 脚本符号,用于编译或执行专门针对 Mac OS X 的代码(包括通用、PPC 和英特尔架构)。 |
| UNITY_STANDALONE_WIN | 脚本符号,用于专门为 Windows 独立应用程序编译/执行代码。 |
| UNITY_STANDALONE_LINUX | 脚本符号,用于专门为 Linux 独立应用程序编译/执行代码。 |
| UNITY_STANDALONE | 脚本符号,用于为任何独立平台(Mac OS X、Windows 或 Linux)编译/执行代码。 |
| UNITY_WII | 用于编译/执行 Wii 控制台代码的脚本符号。 |
| UNITY_IOS | 用于为 iOS 平台编译/执行代码的脚本符号。 |
| UNITY_IPHONE | 已弃用,改用 UNITY_IOS。 |
| UNITY_ANDROID | 安卓平台的脚本符号。 |
| UNITY_LUMIN | Magic Leap OS 平台的脚本符号。您也可以使用 PLATFORM_LUMIN。 |
| UNITY_TIZEN | Tizen 平台的脚本符号。 |
| UNITY_TVOS | Apple TV 平台的脚本符号。 |
| UNITY_WSA | 通用 Windows 平台的脚本符号。此外,在针对 .NET Core 编译 C# 文件并使用 .NET 脚本后端时,将定义 NETFX_CORE。 |
| UNITY_WSA_10_0 | 通用 Windows 平台的脚本符号。此外,在针对 .NET Core 编译 C# 文件时,还会定义 WINDOWS_UWP。 |
| UNITY_WEBGL | WebGL 的脚本符号。 |
| UNITY_FACEBOOK | Facebook 平台的脚本符号(WebGL 或 Windows 独立)。 |
| UNITY_ANALYTICS | 用于从游戏代码调用 Unity 分析方法的脚本符号,版本 5.2 及更高版本。 |
| UNITY_ASSERTIONS | 断言控制过程的脚本符号。 |
| UNITY_64 | 64 位平台的脚本符号。 |
IMGUI(Immediate Mode GUI即时模式图形用户界面)一般简称GUI。是一个代码驱动的UI系统,不要用它为玩家制作UI功能,GUI主要用于开发和调试阶段创建游戏内调试工具在运行时检查和调整游戏状态;制作Unity的拓展工具和编辑器UI。比如地图编辑器,技能编辑器,资源打包自定义等。
在 MonoBehaviour中有一个特殊的函数void OnGUI在里面写GUI相关的代码,类似于生命周期每帧执行,在OnDisable和LateUpdate之间执行,这意味着Inspector修改参数会有即时反馈。
Texture的缩放比例会始终保持一致,一般把这东西放出来都是为了编辑,权限public就行。 GUI的画布原点(0,0)是在左上角
所有控件必传的是位置信息和内容
Rect 参数来指定控件的位置和尺寸。Rect 包含 x 和 y 位置以及 width 和 height 尺寸GUIContent比如下面的btncontentGUIStyle,可以先声明一个GUIStyle对象,就可以编辑CSS啦😄(哈哈兜兜转转又回到web按钮只有在按钮内部进行一次按下和点击才算点击。只能按一次,但是RepeatButton可以长按一直触发(竟然用if来判断我为什么感觉这方式有点沙币
使用GUIStyle 中的 FixedWidth FixedHeight去修改图片的大小和响应区域,不要改Rect
Normal(false)/OnNormal(true)修改是否选中的状态,单选可以自己做出来,注意看在一个循环返回中怎么处理的
窗口必须 赋值 且 在函数中DragWindow才能拖动!!!DragWindow可以确定可以拖动的范围
Csharp
public class IMGUI : MonoBehaviour
{
// 测试文本
public GUIContent content;
public Rect rect;
public Rect rect1;
public Texture tex;
// 测试按钮
public Rect btnrect=new Rect(600,800,100,100);
public GUIContent btncontent;
// toggle选项框
private bool isSealed0 = false;
private bool isSealed1 = false;
private int isSealed3 = 1;
public GUIStyle style1;
// 文字和密码
public string inputString="请接收输入";
public string inputStringPasswd="输入密码";
// 滑动条
public float nowValue=5f,minVlaue=0f,maxVlaue=10f;
// 画个图
public Texture2D demoTexture;
// 多选工具栏(按钮)
public int toolbarIndex = 0;
private string[] toolbarNames=new string[]{"选项一","选项二","选项三"};
public int gridIndex = 0;
private string[] gridNames=new string[]{"选项一","选项二","选项三","选项2一","选项2二","选项2三"};
private string[] demogride =new string[]{"选项一","选项二","选项三","选项2一","选项2二","选项2三","选项3一","选项3二","选项3三","选项4一","选项4二","选项4三"};
// 遮罩窗口
public Rect groupos = new Rect(50, 660, 200, 200);
// 滑动条窗口
public Rect scPos=new Rect();
public Vector2 nowPos=new Vector2();
public Rect showPos=new Rect();
// 窗口相关
public int windowUid = 1;
public Rect windowPos=new Rect(50,800,200,200);
private void OnGUI()
{ //
GUI.Label(new Rect(0,0,100,20),"烤全羊啊嗯"); // 使用Label绘制
GUI.Label(rect,tex);
content.text = "打组一块显示";
content.image = tex;
content.tooltip = "中嘞!"; // 鼠标移动上去显示
GUI.Label(rect1,content); // 可以给GUI组件设置tooltip
// 鼠标移上去的时候就可以直接显示tooltip,默认是空字符串,他会一直打印很烦
if(GUI.tooltip!="") Debug.Log(GUI.tooltip);
btncontent.text = "按钮哎";
if (GUI.Button(btnrect, btncontent)) // 这按钮只能按一次,但是RepeatButton可以长按一直触发
{
print("你们这是什么按钮啊,你们这个按钮害人不浅!");
}
// toggle选项框
isSealed0 = GUI.Toggle(new Rect(0, 0, 100, 30),isSealed0, "效果开关0");
isSealed1 = GUI.Toggle(new Rect(300, 300, 100, 30),isSealed1, "效果开关1",style1); // rect 的宽高是toggle的响应位置
if(GUI.Toggle(new Rect(50, 80, 100, 30),isSealed3==1, "效果开关3-0"))isSealed3 = 1;
if(GUI.Toggle(new Rect(50, 100, 100, 30),isSealed3==2, "效果开关3-1"))isSealed3 = 2;
if(GUI.Toggle(new Rect(50, 120, 100, 30),isSealed3==3, "效果开关3-2"))isSealed3 = 3;
if(GUI.Toggle(new Rect(50, 140, 100, 30),isSealed3==4, "效果开关3-3"))isSealed3 = 4;
// 文字和密码
inputString = GUI.TextField(new Rect(50, 160, 100, 30), inputString);
inputStringPasswd = GUI.PasswordField(new Rect(50, 200, 100, 30), inputStringPasswd,'星');// 接收密码字段
// 滑动条
nowValue = GUI.HorizontalSlider(new Rect(50, 250, 100, 30), nowValue, minVlaue, maxVlaue);
// 直接画个图
// 后面是 ScaleToFit 计算宽高,等比例缩放不裁剪 StretchToFill 无视比例拉伸 ScaleAndCrop 等比例缩放会裁剪
// imageAspect 自定义缩放宽高比
GUI.DrawTexture(new Rect(50, 350, 100, 100),demoTexture,scaleMode:ScaleMode.StretchToFill,alphaBlend:false,imageAspect: 10);
GUI.Box(new Rect(50, 400, 100, 100),"这是个盒子,一个非常单纯的盒子");
// 多选工具栏(按钮)
toolbarIndex=GUI.Toolbar(new Rect(50, 500, 200, 50),toolbarIndex,toolbarNames); // 需要不断赋值,可以使用switch case赋值
gridIndex=GUI.SelectionGrid(new Rect(50, 560, 200, 50), gridIndex, gridNames,3); // 就是满了会自动换行
// 相当于html里的遮罩,只显示窗口位置
GUI.BeginGroup(groupos); // Rect
GUI.Button(new Rect(0, 0, 80, 60),"你们这个按钮也害人不浅啊");
GUI.EndGroup();
// Rect position组件的位置大小 Vector2 scrollPosition滑动到哪了 Rect viewRect 内容的范围大小
nowPos=GUI.BeginScrollView(scPos,nowPos,showPos);
GUI.SelectionGrid(new Rect(0, 0, 200, 50), 0, demogride,4); // 注意位置已经是相对位置了
GUI.EndScrollView();
// 窗口相关,必须 赋值 且 在函数中DragWindow才能拖动!!!
windowPos=GUI.Window(windowUid, windowPos, DrawWindow,"窗口标题"); // 会调用函数里面的东西进行绘制
GUI.ModalWindow(windowUid+1, windowPos, DrawWindow,"窗口标题"); // 相当于获取了焦点的窗口,相当于alert了
}
private void DrawWindow(int windowID) // 实际上这东西绑在了一个委托上
{
// 可以通过windowID来判断不同窗口的处理逻辑
GUI.RepeatButton(new Rect(10, 10, 40, 50), "按钮按钮");
if (windowUid == 1) // 给第一个窗口用的
{ // 允许拖动这一步也是必须的
GUI.DragWindow(new Rect(0,0,1000,20)); // 允许拖动哪一块范围,默认全能拖
}
}
}[ 📌Tip ]
GUI控制显示隐藏,可以写在一个脚本中,用变量控制启用;或者挂在对象上,控制脚本是否启用;或者控制对象是是否启用。
一般是控制对象,高级一点的UI框架都是这么封装的
GUILayout 可以用来写顶栏工具和Inspector不太适合直接写UI 设置全局颜色GUI.color然后再设置文本颜色GUI.contentColor或GUI.backgroundColor这两个颜色会相乘好怪,就这仨颜色。 颜色会跨脚本影响看执行顺序
右键创建GUI Skin 可以直接读取预设的主题,使用GUISkin skin来创建,赋值创建的GUISkin文件就可以了。绘制时使用了GUIStyle会优先使用指定的样式。
Csharp
private void OnGUI()
{
// 全局颜色
GUI.color = new Color(1f,0f,0f,1f); // RGBA颜色
GUI.Label(new Rect(150,100,100,100),"This is red text");
// GUI.color = Color.white; // 白色重置颜色
GUI.skin = null; // 规定接下来绘制使用GUISkin
// GUILayout 可以用来写顶栏工具和Inspector
GUILayout.Button("按钮1哦",GUILayout.ExpandWidth(false)); // GUILayout.Width(300);在此控件上失效
GUILayout.Button("按钮2哦");
GUILayout.Button("按钮3哦");
GUILayout.BeginVertical(); // 布局控制
GUILayout.EndVertical();
// GUILayoutOption提供的选项
// 控件的固定宽高
GUILayout.Width(300);
GUILayout.Height(200);
// 允许控件的最小宽高
GUILayout.MinWidth(50);
GUILayout.MinHeight(50);
// 允许控件的最大宽高
GUILayout.MaxWidth(100);
GUILayout.MaxHeight(100);
// 允许或禁止水平的样式
GUILayout.ExpandWidth(true); // 允许水平
GUILayout.ExpandHeight(false); // 禁止高度
}IMGUI缺点是 代码控制繁琐,运行时才能看效果,不能分辨率自适应。 在mono类加特性[ExecuteAlways]可以让UI所见即所得 (其他的脚本其实也能用
UI血条跟随屏幕动,使用IMGUI的小框架,因为屏幕的Y轴是从左下角算起的,而IMGUI原点是从右上角算的,注意怎么转换
Csharp
public Texture maxHpBG;
public Texture hpBG;
public Rect maxHpRect;
public Rect hpRect;
private void OnGUI()
{
Vector3 screenPos = Camera.main.WorldToScreenPoint(this.transform.position);
screenPos.y = Screen.height - screenPos.y; // 因为屏幕的Y轴是从左下角算起的,而IMGUI原点是从右上角算的,注意怎么转换
maxHpRect.x = screenPos.x - 50;
maxHpRect.y = screenPos.y - 50;
maxHpRect.width = 100;
maxHpRect.height = 15;
// 画血条背景
GUI.DrawTexture(maxHpRect, maxHpBG);
hpRect.x = screenPos.x - 50;
hpRect.y = screenPos.y - 50;
hpRect.width = (float)Hp / maxHP * 100f;
hpRect.height = 15;
// 画血条
GUI.DrawTexture(hpRect, hpBG);
}NGUI太老了,听劝学UGUI
Unity内置的右键Create->UI创建 一旦创建UI组件会自动创建Canvas和EventSystem。在 Game窗口->Stats(Statistics)->Batches就是DrawCall的数量。
Canvas 负责渲染所有的子UI,不在Canvas的UI渲染不了,场景上允许有多个Canvas以设置不同的渲染和分辨率适应,但是一般情况只有一个。参数如下:
RenderMode渲染模式
CanvasScaler 用于画布缩放分辨率自适应。注意他不负责位置由RectTransform去负责。选中指定Canvas之后在RectTransform中可以看到宽高和缩放系数,符合公式 宽高x缩放系数=分辨率
更改这个组件本质上来说就是屏幕分辨率和参考分辨率经过不同算法计算得出缩放系数然后参与RectTransform计算
UIScaleMode
GraphicRaycaster 检测UI输入事件的射线发射器,是通过图形不是通过刚体碰撞器完成的。屏幕空间摄像机模式下面参数才能起效
EventSystem 事件系统,管理输入事件都会被它轮询检测并分发给UI控件
StandaloneInputModule 一般不会进行修改,相当于UGUI和InputManager对接的模块。注意下面参数
RectTransform 继承了Transform每个UI组件上都有
Anchor锚点,默认为(0.5,0.5),PosX和PosY的距离是中心点Pivot相对于锚点Anchor的距离,左边的更改也是更改锚点的预设。
当我们进行分辨率位置自适应的时候,是以Anchor为坐标原点计算的。当Anchor被分开是一个范围的时候,LeftTopRightBottom是UI元素相对于父元素的margin距离,会跟随父矩形拉伸而拉伸,一般只有背景图对齐的时候才会用。
使用快捷设置的时候,按住Shift点击鼠标左键可以同时设置轴心点(相对自身矩形);按住Alt点击鼠标左键可以同时设置位置。
挂载到UI元素上的脚本使用this.transform as RectTransform 使用里氏替换原则父类装子类,类型转换一下就可以了。
Panel 此组件就挂载了一个Image,基本上就是分组用的,可以设置遮罩射线等。
Image 参数注意 RaycastTarget是否响应射线检测响应,会拦截射线后面的UI元素没有响应了。ImageType使用Sliced最好是在SpriteEditor中先规定九宫格的线,PixelsPerUnitMultiplier可以设置像素对应大小。Tiled经常会用来做填充。Filled经常用来做CD缓冲或者血条效果。
RawImage 一般用作大图,不需要打图集的图片,Texture支持的格式比较多。UVRect是控制图的偏移,只展示图性能消耗小
Text 字体过大RectTransform太小会直接消失,BestFit会自动调整字体大小,可以规定字体最大最小。注意 经常出现Text的RectTransform挡住按钮的情况,出问题检查一下。边缘线Outline和阴影Shadow组件可以添加效果。2020某版本后TMP是UGUI默认使用的文本组件
Button 挂了一个Button和Image,子对象是Text,如果按钮的图片上已经有字了可以删除Text子对象。修改button的图一般是拿Button的Image改Sprite
按钮的点击必须是在按钮内点击并抬起算一次。注意参数
Toggle 单选,多选控件。BackGround是未勾选的图片,勾选的图片是它的子对象CheckMark控制,更改这两个对象上的Image组件就可以。
用一个空对象(任何对对象都可以)挂载ToggleGroup,在Toggle的Group选择此Group可以达到在Group内单选的效果。
InputField 输入框,下面的对象Placeholder和Text,用来提示输入和存储输入的Text
Slider 滑动条下面的子对象有Background背景,Fill填充,Handle滑块 分别挂载了一个image组件组成的。分别绑定了连个React
ScrollBar 一般不单独用,会和ScrollView配合使用。自身挂载的Image是后面图片的背景,Handle是滑块的图。
可以让组件添加自定义的事件监听,比如给文本控件额外添加点击双击等。UGUI中提供了常用的事件接口。挂载的脚本类需要继承指定接口然后类中实现指定的方法,挂载的控件会自动调用,事件检测
PointerEnterHandler接口实现函数OnPointerEnter - 当指针进入对象时调用 (鼠标进入)移动设备上不存在IPointerExitHandler接口实现函数OnPointerExit - 当指针退出对象时调用 (鼠标离开)移动设备上不存在IPointerDownHandler接口实现函数OnPointerDown - 在对象上按下指针时调用 (按下)IPointerUpHandler接口实现函数OnPointerUp - 松开指针时调用(在指针正在点击的游戏对象上调用)(抬起)IPointerClickHandler接口实现函数OnPointerClick - 在同一对象上按下再松开指针时调用 (点击)IBeginDragHandler接口实现函数OnBeginDrag - 即将开始拖动时在拖动对象上调用 (开始拖拽)IDragHandler接口实现函数OnDrag - 发生拖动时在拖动对象上调用 (拖拽中)IEndDragHandler接口实现函数OnEndDrag - 拖动完成时在拖动对象上调用 (结束拖拽)PointerEventData注意的参数看代码。Csharp
public class Test1 : MonoBehaviour,IPointerClickHandler,IPointerEnterHandler,IPointerExitHandler // 继承事件接口
{
void Start()
{
//Image
Image image = GetComponent<Image>();
image.sprite = Resources.Load<Sprite>("t1"); // 从Resources中加载资源替换掉sprite
RectTransform rectTrans = this.transform as RectTransform;// 点进去看比transform多了多少属性
rectTrans.sizeDelta = new Vector2(80, rectTrans.sizeDelta.y); // 不能直接修改某一个属性,只能一次性赋值
// Text
TextMeshProUGUI t = GetComponent<TextMeshProUGUI>();
t.text = "test";
// Button
Button btn = GetComponent<Button>();
// 也可以RemoveListener(FunctionName)
// RemoveAllListener() 无法移除Editor中拖拽的
btn.onClick.AddListener(() => // 委托绑定
{
Debug.Log("click!");
});
// toggle
Toggle tog = GetComponent<Toggle>();
tog.isOn = true; // toggle是否被选中
tog.onValueChanged.AddListener((bool on)=> // 绑定事件响应
{
Debug.Log($"toggle changed to {on}");
});
// toggleGroup
ToggleGroup g = GetComponent<ToggleGroup>();
g.allowSwitchOff = false;
foreach (Toggle activeToggle in g.ActiveToggles()) // 获得备选中toggle
{
Text text = activeToggle.GetComponentInChildren<Text>();
Debug.Log($" toggle {activeToggle.name} label {text.text} on is {activeToggle.isOn}");
}
// inputField
InputField inf = this.GetComponent<InputField>();
print(inf.text);
inf.text ="112222222";
inf.onValueChanged.AddListener(delegate { print(inf.text); }); // 值改变的时候获取内容
inf.onEndEdit.AddListener(delegate { print($"结束输入 {inf.text}"); }); // 按回车结束输入的时候
// Slider
Slider sdr = this.GetComponent<Slider>();
print(sdr.value); // 获取slider当前的值
sdr.value = 0.5f; // 初始化一个值
sdr.onValueChanged.AddListener((v) => { print(v); }); // 接收一个float
// ScrollBar
Scrollbar scb = this.GetComponent<Scrollbar>();
scb.value = 1.0f;
scb.size = 0.2f;
scb.onValueChanged.AddListener((v) => { scb.value = v; }); // 监听事件
// ScrollView
ScrollRect srr = this.GetComponent<ScrollRect>();
srr.content.sizeDelta = new Vector2(200, 2/00); // 改变content大小,可以拖多少都是根绝它来
srr.normalizedPosition = new Vector2(0, 0.5f); // 不需要计算横着没拖,竖着拖了一半的效果
srr.onValueChanged.AddListener((xypos)=>{Debug.Log(xypos);}); // 传入的是Vector2,表示横x竖y拖动到了那个地方(百分比
// Dropdown
Dropdown dd = GetComponent<Dropdown>();
print(dd.value); // 当前选中了哪个
print(dd.options[dd.value].text); // 用options获取当前选中的元素
dd.options.Add(new Dropdown.OptionData() { text = "OP" }); // 通过代码去添加一个选项。可以传图片文字等
dd.onValueChanged.AddListener((int value) => { Debug.Log($"current choose index {value}"); });
}
// 事件接口
public void OnPointerClick(PointerEventData eventData)
{
print($"pointer info : " +
$"{eventData.position} " + // 屏幕座标系鼠标的位置
$"{eventData.pointerId} " + // 左中右哪个键点击
$"{eventData.pressPosition} " + // 屏幕座标系鼠标点击的位置
$"{eventData.delta} " + // 拖动增量
$"{eventData.clickTime} " + // 点击时系统时间
$"{eventData.clickCount}"); // 点了几次
print(eventData.pressEventCamera); // 最后一个点击事件关联的摄像机
print(eventData.enterEventCamera); // 最后一个鼠标进入事件关联的摄像机
Debug.Log("pointer click");
}
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("pointer enter");
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("pointer leave");
}
}#todo UGUI P16 P30 P32,有时间P25也做完,再有时间P27也做了 #todo UGUI P24 15min scroll三种模式,content和viewport的关系
图集 设置详细用法在基础SpriteAltas中,UGUI可以不打就能用,NGUI需要先打图集才能用Edit->PorjectSettings->Editor->SpritePacker,Enabled是运行和构建都打,EnabledForBuilds是只构建打。
创建一个SpriteAltas,因为是UI图集取消AllowRotation和TightPacking。相同图集的图堆叠,只会有一次DrawCall但是如果如果AABA这样堆叠打断了批处理,这样就多2次DrawCall,Text等也会打断,插入到中间且重叠会增加DrawCall
坐标转换 使用这函数可以制作摇杆RectTransformUtility.ScreenPointToLocalPointInRectangle,此函数的作用是把屏幕上的点转换为以某元素为原点的点,下例
遮罩 一般是和Image配合使用,在父对象上挂载Mask组件,想要被遮罩的Image勾上Maskable,子UI对象都会被遮罩。此时带Mask对象的Image组件关联的图片,透明的地方被遮罩,不透明的地方显示。
注意: RectMask是通过RectTransform矩形判断停留在应用阶段,消耗低但是只能遮罩RectTransform的矩形部分。
异形按钮 不规则的图片。
img.alphaHitTestMinimumThreshold = 0.1f; 效果就是此图片透明通道必须有值的才能响应检测,而且不会拦截射线世界物体检测 Camera+PhysicsRaycaster 组件可以 让UI事件系统可以作用到 3D 物体上,Unity 的 EventSystem 在每一帧都会收集输入事件(鼠标、触屏)它会从输入位置(比如鼠标点的位置 Input.mousePosition)通过摄像机发出一条 物理射线。这条射线会和场景中的 Collider 进行物理检测。如果射中了某个带有 Collider 的物体,就会把这个物体当成 UI 事件的接收者。当然可以用Physics.Raycast手动写,但是用Camera+PhysicsRaycaster之后就可以直接在物体上实现UI的回调函数或者挂UI组件用了
CanvasGroup 组件,给Panle控件挂载可以用来整体淡入淡出和禁用,主要参数Interactable是否启用不影响射线拦截 BlocksRaycasts 进行射线检测,如果不进行射线检测,那么如果后面有被遮挡的UI就可以接收到。IgnoreParentGroups忽略父级的CanvasGroup对自己的影响。
在UI元素的Inspector中,切换到LayoutProperties,里面的属性就是自动布局使用的属性不懂的问问GPT,其实不太重要,知道有影响就行。主要有两个VerticalLayoutGroup,HorizontalLayoutGroup组件参数差不多。
GridLayoutGroup是网格布局组件,说点和上面两个参数不一样的。ContentSizeFitter 内容大小自适应组件,自动调整RectTransform大小适应,一般在Text或者配合其他组件使用,比如搭配ScrollContentView固定宽度然后根据物品多少自动设置高度AspectRatioFitter这个了解就行,可以让元素自己根据宽或者高调整大小,可以自定义宽高比;也可以适应父对象。ViewContent下拉,在Content中使用ContentSizeFitter+LayoutGrop/GrideLayout动态下拉和伸缩 对照目录查漏补缺Fetching Title#hno5
[ 📝Note ] UI经验总结
UI动态生成的思路。一个经典的权衡问题,提示框弹出来的时候,用代码控制是一个按钮还是两个按钮,如果是一个按钮就居中两个按钮就排布;或者做两个面板,分别调用。
如果使用动态生成的思路,应该写一个按钮栏父对象+HorizontalLayout,然后写一个按钮类,传入指定的文字图片Action返回按钮Object挂到按钮栏上。
UI控件主要学习三个事情:UI控件使用,事件响应,分辨率自适应(大小 位置)
做UI其实就三件事,数据准备->拼界面->写逻辑
在UI中一般都是用一个单独的根对象避免和其他的对象混合管理增加管理难度,命名最好有规范,比如btnBegine一看就知道这是啥,写C#写多了直接大驼峰罢
先分层,业务逻辑和响应解耦
Uinty自带的编辑器的UI,可以凑活用,做地图技能编辑器什么的。
这里面的API就是在街面上点击按钮操作的API,
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup group, BuildTarget target); Editor中可以切换平台相当于File->BuildSettings->Platform->Android->SwitchPlatform。
Csharp
[MenuItem("Tools/MyCustom")] // 加在函数上创建自定义Editor菜单,至少有一个斜杠,只支持静态方法
EditorUtility.DisplayProgressBar("Title", "Message", progress); // 进度条,想要变化那就改变progress(float 0-1f)的值和 标题 信息
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("Title", "Message", "OK", "Cancel");
Object[] selectedAssets = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);// 获取在Project窗口中选中的资源
string assetPath = AssetDatabase.GetAssetPath(asset); // 获取 资源的路径 格式是 Asset/xxx/xxx
AssetDatabase.CopyAsset(assetPath, destinationPath); // Unity有自带的API可以在Asset中复制文件
AssetDatabase.Refresh(); // 刷新一下Editor界面#todo 没时间了,先跟着做用到啥算啥,有时间跟着这个教程摸个够用就行
广受好评的编辑器UI框架,感觉完全是UnityEditor上位替代,有时间再摸 Unity使用Odin完成编辑器开发 【基础知识篇 第一节】中文 分组 颜色 按钮 条件_哔哩哔哩_bilibili
热更新实际上是无感下载,不重新安装APP的情况下更新内容,重启程序虚拟机或者重启游戏才生效,而不是正在运行着就把内存中的资源替换掉了。一般也不会让Rsource加载一半老的一半新的,这样就乱了。
Unity笔记热更Part1:关于AB包与热更
这个人写的都不错。 手游或者需要联网热更的游戏一般不使用Resouce.Load,AB包就是干这个的,AB包不仅可以在本地还可以在网络上远程加载。文档可以花小半天先看一遍,更好理解在干什么虽然感觉翻译的有点依托。
目前已经不在维护官方建议用Addressables替代,但是公司还在用所以要学。安装看git repo版本太高报错就把里面的Test删掉。
没东西打包会报错,在资源的Inspector->底部AssetBundle,第一个是打指定包的包名,第二个是拓展名别动。三个页签栏
Configure: 查看所有待打包的文件,Inspect可以查看打好的文件的基本信息。
Build:
Build好的东西会放到AssetBundle(Assets同级)/{指定plantform文件夹}下,打包好的格式是 二进制文件+元信息文件(Assets.manifest),同时会生成一个文件夹同名的主包
同一AB包不能重复加载 否则报错,AB包异步加载完资源后会在下一帧渲染,卸载的时候如果选择不卸载资源,则只会卸载ab包但是已经实例化的资源仍旧会占内存
包间的依赖,某个包内的资源引用其他资源,他们都会被自动打包进这个包里。 如果手动指定了不同包,引用不到了会报错 需要把依赖的ab包先加载了或者把他俩移动到同一个ab包里。
利用主包可以获取包间依赖,挨个加载就是了,他没法获得某个资源依赖了几个包,只能从包级别获取,并且会自动递归返回完整的依赖列表,不需要手动处理多层包引用。
但是 注意AB包不会自动处理循环依赖! A依赖了B,B依赖了C,AC在ab1中,B在ab2中;这样就循环依赖了会报错
[ 📌Tip ]
在打AB包的时候不能把场景和非场景资源打在同一个AssetBundle里,会报警告。
最佳实践场景单独一个包,其他资源随便分包
Csharp
public class AssetBundleTest : MonoBehaviour
{
private AssetBundle ab;
void Start()
{
// 同步加载
ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/"+"testab");
AudioClip musica = ab.LoadAsset<AudioClip>("伍佰 & China Blue - 你是我的花朵"); // 使用泛型加载,如果使用名字加载可能会出现同名不同类型的冲突
musica = ab.LoadAsset("伍佰 & China Blue - 你是我的花朵",typeof(AudioClip)) as AudioClip; // 使用反射加载,用于在lua中不支持泛型的时候使用
AudioSource aus = this.GetComponent<AudioSource>();
aus.clip = musica;
aus.Play();
// 异步加载
StartCoroutine(LoadABAsync("testabasync", "Cube"));
// 包间依赖处理
GameObject sph = ab.LoadAsset<GameObject>("Sphere");
// 使用主包获取依赖先加载,不然就会丢失材质洋红色
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/"+"PC"); // 加载主包
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] strs = abManifest.GetAllDependencies("testab");
for (int i = 0; i < strs.Length; i++)
{
AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/"+strs[i]);
}
GameObject.Instantiate(sph); // 这样应该就不是红色的
}
IEnumerator LoadABAsync(string abname,string resname)
{
// 异步加载包
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + abname);
yield return abcr;
AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resname,typeof(GameObject)); // 加载资源,我估计后面正式用类型也得一块传进来
yield return abq;
GameObject.Instantiate(abq.asset as GameObject); // 把这东西拿出来
}
IEnumerator UnloadAB(AssetBundle ab_)
{
Debug.Log("卸载此包");
ab_.Unload(true); // 卸载指定的ab包
AssetBundle.UnloadAllAssetBundles(false); // 参数是卸载的时候是否把当前使用的资产也卸掉
yield break;
}
void Update()
{ if (Input.anyKeyDown)
{
Debug.Log("检测键盘");
StartCoroutine(UnloadAB(ab));
}
}
}客户端的AB包都放在两个路径,一个是streamingAssetsPath中的默认AB包资源(移动端只读),persistentDataPath有最新的(可读写)。在第一版发布的时候,会把AB包都打包进StreamingAssets,后续我可能在服务器上发布新资源,然后每次游戏启动检查对比。(我怎么感觉treamingAssetsPath和persistentDataPath设计的有点🦈臂
之后再整理一下,现在先放这,看希佬的代码,争取的是把长风的AB支持热更新
其实是个数据下载对比的活。 #todo 获取文件MD5 生成资源描述文件 名字 大小 md5。上传服务器,下载描述文件,对比本地,是否转存,哪些删除哪些需要下载哪些不需要下载,注意persistentDataPath和streamingAssetsPath
跟着长风学的,这设计真的有点东西。 #todo 把代码梳理出来,加载配置,资源划分,处理,写入二进制。加载AB,GC 资源管理 调用
基于AB包做的热更进一步封装,建议别跳过AB包因为公司用啥我用啥。
很流行的一个资源热更的插件,多看文档YooAsset超级详细。
都是TuYoo搞出来的,东西很好用。这里只跟进一个SpaceShooter的Case先把三板斧流程搞通。
在使用之前确保项目是ProjectSetting->Player->OtherSettings->Configuration : IL2CPP+NetFramework .NET Standard 一些反射、动态特性不完全支持 HybridCLR 的补丁机制;AllowHttp也要打开,否则可能出现下载资源不成功。。
YooAsset使用前需要添加openupm的源,然后在PackageManager中搜索安装,顺便导入SpaceShooter示例。YooAsset依赖于ScriptableBuildPipeline而不是自带的AB包打包管线,不从PackageManager里安装的话不会自己安装。第一次启动可能会莫名报错,重启编辑器就好了
此时在Editor上面就多了一个YooAsset的菜单选项。 AssetArt Scanner/Reporter 用来扫描美术资产生成引用报告,包含依赖检测孤立资源依赖冲突公共引用等(用到再整理具体用法
示例Boot中提供了四种模式
YooAsset是把包和Object的GUID打在一块了,而不是单纯的路径,所以当Miss的时候可以Fix一下
包层级分为Package/Group/Collectors,每个Collector会被打包成一个AB,它的PackRule可以决定按照类型资源目录等规则打成一个AB包。
AutoCollectShaders可以自动收集Shaders到单独的一个包里,上来基本都默认就行。 使用EnableAddress可以分名字打包,不开启默认是分类型打包
打包之前需要创建一个全局默认配置,右键可以直接创建,可以规定资源文件夹是什么几乎所有的参数都在文档上有。此时包已经被打好了,先打到Project/yoo下并且在StreamingAssets下有一份。
Pcakage Package_version.bytes这两个文件,通过文档的代码生成 Catalog相关的俩文件,然后才能使用host模式更新 StreamingAssets/yoo在这里放Pcakage的俩文件生成Catalog,才能用。Project/setting规定的目录,打包之后的包下载到persistentDataPath对应的app文件夹 #todo space shooter代码再看吧,弄完了他会给带注释的工程HybridCLR是做代码热更新的 多看官网文档HybridCLR虽然很多也很详细。原先是涂游FocusGame团队开源的huatuo,后来涂游把这个名字收回了,代码哲学就吸取旧框架的经验,重新实现一版出来叫wolong。看这个热更新需要了解Unity的跨平台实现C#的AOT,HybirdCLR使用的是IL2CPP补充元数据的技术,可以自动生成Link.xml。
要求必须安装Mac/Win平台的IL2CPP Support,win需要安装VS2019+,mac要求MacOS版本 >= 12 xcode版本 >= 13。
记得切换PlayerSetting,版本低于v4.0.0还要取消use internal GC。C++Configuration选择Debug方便调试,最后上线之前切换为Release。
按照官网安装就好了,PackageManager安装好包之后此时在Editor上面就多了一个HybridCLR的菜单选项,第一次需要使用Installer安装il2cpp_plus+hybirdclr,他都帮你在ProjectSetting中配好了,安装就可以。
此时项目目录下多了一个HybridCLRData文件夹,里面有帮你下载的2个包和把这两个包拼装起来符合你平台Editor的包。
如果没有对热更新代码进行混淆,经过ILSpy之类的反编译工具,可以轻易还原近乎源码的C#代码,目前的开源代码混淆工具中,只有Obfuz与Unity深度集成,并且完整支持HybridCLR。
AutoReferenced取消勾选,否则会被剔除报错。.
├── ConsoleToScreen.cs # 官网给的示例脚本内容,主要是接管打印日志
├── LoadDLL.cs # 用来加载DLL
├── MainScene.unity
└── hotreload_assmebly # 热更程序集文件夹
├── Hello.cs # 打印并初始化一个GameObject
├── Print.cs # 打印GameObject的名字
└── hotreload.asmdef # 热更程序集Project/HybridCLRData/HotUpdateDlls/TargetPlantform下找到你定义的热更Assmebly的DLLUnity大厂面试题xLua ILRuntime HybirdCLR热更方案比较
有个案例逮空做一做
lua热更就是三点 几个Lua热更新框架差异 - Bob的博客
如果是已有项目使用xlua,把官方Assets里面Plugins XLua两个文件夹粘贴进项目里,如果需要热补丁还需要Tools文件夹,纯lua项目不需要
github把官方项目复制下来,我使用的是M1的macos,需要编译出bundle文件,替换掉Plugins下面的xlua.bundle。
这是初始化XLua项目需要的目录,找时间看懂牢唐是怎么做的
C
Assets
├── Lua
│ ├── JsonUtility.lua # 封装的JSON序列化工具
│ ├── Main.lua # 入口文件
│ ├── Object.lua # 封装的OOP
│ └── SplitTools.lua # 字符串切分方法
└── Scripts
├── Main.cs # 游戏主入口
└── ProjectBase
├── ABMgr.cs # AB包管理器
├── Base
│ ├── BaseManager.cs #
│ ├── SingletonAutoMono.cs
│ └── SingletonMono.cs
└── LuaMgr.cs[ ⚠️Warning ]
Xlua和AB打包有冲突,需要先清Xlua的ClearGenerateCode再打包
交互和交互细节 luabehavior 哪些地方用点哪些地方用冒号调用是有说法的。 lua用起来有点阴间的
xLua/Assets/XLua/Doc/XLua教程.md at master · Tencent/xLua · GitHub
xLua/Assets/XLua/Doc/configure.md at master · Tencent/xLua · GitHub
看一眼最基本的使用收发请求判断响应等。 Unity 事件详解 - CatSevenMillion - 博客园
【Unity技术栈】《Unity3D网络游戏实战》笔记 | 聪头之家
唐老师网络19-40 61-68 网络实践1-8
实际上也可以算一种设计模式,我在设计模式里面实现过,这里以gmhevinci/UniFramework里面的状态机为例解析一下。
一般的单例模式可以直接public static Airplane Instance = new Airplane(); 但Unity中的单例模式不行,在资源或者全局的管理等情况下,一般初始化动作放在awake中做,而不是start,如果把这些逻辑放在 Start 中,可能会导致其他脚本在 Start 中访问 BGMusic.Instance 时,instance 还没有被赋值,从而引发空引用异常。在MonoBehaviour的子类中不能直接new,先声明变量在Awake的时候instance=this 如果想让他游戏运行自动挂载,那就在Instance获得的时候检查是不是空的,如果是的话创建一个空的GameObject然后挂上去,这样就不用自己在场景上创建对象了
过场景的时候单例MonoBehaviour的静态变量也是不会销毁的,在OnDestroy的时候赋值instance = null;销毁
在Unity游戏开发中滥用单例 单例之间互相依赖,耦合性过强,初始化顺序不可控容易爆炸,跨模块调用
(中文语音)Unity中的更好单例模式_哔哩哔哩_bilibili
Model:负责增删查改获取界面上需要的数据
View:负责获取控件,更新控件信息,挂载到场景上
Controller:负责业务逻辑处理。 界面事件监听,触发数据更新,触发界面更新
MVC有几个变种,基本上M和V是一定要有的,中间的事件控制解耦可以用不同的方式去做。
MVC还是会把data传递到View里面,MVP会切断View和Model的耦合,让Presenter处理一切。
类似于前端的双向绑定,让ViewModel和V进行双向数据绑定,更新VM等同于更新V。 umvvm 和 GitHub - vovgou/loxodon-framework: An MVVM & Databinding framework that can use C# and Lua to develop games
用EventCenter事件中心来分发消息
看石匠的GitHub - hadashiA/VContainer 从官网安装VContainer,
事件总线发布订阅框架,必须配合DI使用,Cysharp的三件套是可以一块配套使用的。
2019年UUG高川-浅谈Unity内存管理 也可以直接搜标题看文字版
c与cpp lua c# unity 高川_哔哩哔哩_bilibili
这里只有工具的基本使用。 UnityProfiler ProfileAnalyzer MemoryProfiler FrameDebugger
最佳实践
根据环境调整日志级别:
Csharp
#if DEVELOPMENT_BUILD || UNITY_EDITOR
.MinimumLevel.Debug()
#else
.MinimumLevel.Warning()
#endif在重要流程中添加日志点:
上不上报,性能影响等
做独立游戏够用了,看的是某咩的plus版,唐门!
跨平台打包和SDK接入
非一般的算法,是游戏领域常见的算法,有些是引擎组件的原理有些是特殊的功能,除非瓶颈一般自己不手搓。
最近邻
碰撞检测
服务端地图推送性能优化
此为Unity内置的寻路的算法原理
生物集群模拟
[ 📌Tip ] 系统自带的字体
Win: C:\Windows\Fonts
Mac: 系统内置只读字体 /System/Library/Fonts 系统用户共享字体/Library/Fonts 用户字体~/Library/Fonts
Unity支持ttf和otf(TrueType字体和OpenType字体)
文档 Window->TextMeshPro->ImportTextMeshProEssentialResources中可手动导入。 默认改字体很蠢,需要在软件里面改Editor\Data\Resources\PackageManager\BuiltInPackages\com.unity.ugui\Runtime\UI\Core\Text.cs找代码改。
使用TMP设置系统默认字体Edit->ProjectSettings->TextMeshPro->Settings->DefaultFontAsset选中你生成的字体这样全局就会有字体了
静态字符集,在Window-TextMeshPro->FontAssestCreater。用到静态字符集里没有的字会直接显示框。
使用动态字符集右键->TextMeshPro->CreateFontAssest,用到啥他自己会找,但是动态的性能会差一些。
TextMeshPro 的动态字体是通过图集的方式实现的,而它默认生成的图集是一张1024x1024的图, 集满了之后把没有的放到一个MissingCharacterList里,然后用方框代替。
找到生成的Font资产->Inspector->GenerationSettings->MultiAtlasTextures勾上他自己会使用多张图集。
使用静态字符集+设置Fallback为动态字体,在性能和字符之间找到一个平衡。
【保姆级教学】Unity TextMeshPro 位图字体(位图字体使用建议)_unity 位图字体-CSDN博客
TextLife
【Unity】精灵资产——轻松实现文字中插图片_哔哩哔哩_bilibili
[ 📌Tip ] 补间动画
冷知识: UI 动画里常说的 补间动画(tween animation)其实是"tween"的直译
在传统动画里,动画师会先画好关键帧(Keyframe),比如第 1 帧和第 20 帧。但中间的 2~19 帧不可能都由主动画师亲自画,于是交给助理画“过渡帧”,这些过渡帧就叫 in-between frames,简称 tween。
中文翻译过来就是 补间(补充关键帧之间的内容)
UI动画插件,在UnityStore搜索Dotween,添加到我的资源;然后在Window->PackageManager->MyAsset->搜索Dotween,安装引入。 【Unity教程搬运】使用Unity UniTask异步等待实现流畅的硬币收集动画DOTween_哔哩哔哩_bilibili
Tools->Demigiant->DoTweenUtilityPanel->
可视化状态机
用于更方便持久话数据的插件,配合牢唐的持久化的几个封装好的使用很不错。
依旧是安装找官网,先用学习版。从Window->EasySave3打开,Settings绝大部分需要改的你都能看懂,Types是支持保存的类型。AutoSave是挂载脚本到想要保存的物体上+场景单例ES3AutoSaveMgr管理,他会保存脚本组件和其他所有的属性。一般OnApplicationQuit里写保存Awake/Start的里写加载。 能够保存大多数的可序列化对象,自定义类型需要加Serializable。保存时同文件内键不允许重复,保存Object类型的时候必须要有EasySave3Manager
有了管理器我们就可以保存一些包含引用的对象,Load的时候实际上是先加载然后把这个GameObject赋值给引用,而不是赋值给现有的引用对象,所以可能引用现在持有的对象会被顶掉,如果找不到就会重新创建这个对象放上去。
如果想在原来的对象上加载数据应用更改,使用LoadInto方法。
场景上的对象和持久化的对象引用关系是EasySave3Manager保存的,所以切换场景的时候可能会丢失
[ ⚠️Warning ]
游戏内的GameObject以及材质等,要慎用Load,一般是保存数据然后LoadInto到新对象上。对复杂对象只保存路径或 ID,不保存直接引用,运行时再实例化
每个UnityEngine.Object在保存时会分配一个内部引用ID,这个refID用来标识这个对象在内存中的唯一性。如果这个Key从A保存,上次加载的时候是加载到B身上,那么再次加载这个Key不显式传递被加载的引用,那么数据就会跑到B身上而不是A。
如果已经引用乱了找EasySave3Manager清空对应的引用
AutoSave用法
为指定游戏对象添加ES3AutoSave脚本,在面板上勾选想要保存的数据,EasySave3Manager会自动保存你勾选的东西。自动保存支持的种类很多。
自己规定的字段必须要声明序列化支持,想被Unity序列化但是不想被ES3序列化的使用ES3NonSerializable,想被ES3序列化但是不想被Unity序列化的使用[ES3Serializable]。
在ES3窗口的AutoSave页中可以规定自动保存的全局设置,下面勾选自动保存的对象数据,不用一个一个挂载添加了。在一个Prefab上右键->EasySave3->EnableEasySaveForPrefabs这样对这个prefab的autosave操作就会应用到他所有的实例化对象上
先在AutoSave中把Save和Load设置为None不自动触发,代码中使用ES3AutoSaveMgr.Current.Save();(Load)控制全局的保存和加载。
利用这个我们可以做玩家自定义键盘位置的功能,对每个键盘点Transform进行AutoSave,下次打开加载。
Prefab的自动保存设置之后,子物体不能在Inspector中进行自己单独的配置,需要在AutoSave页的Scene中单独配置 Settings的行为:
开启加密不会影响之前已经保存的文件,再次加载还是加载未加密的会报错。
在static中声明全局的ES3Settings,可以更改的东西和面板上差不多一一对应。
保存加载的时候可以传入Path参数,使用自己制定的ES3文件,这样就可以制作多存档了。LoadInto("key0","my/path/es3save.es3",GameObject),不同文件相当于不同的命名空间了,Key可以重复了。
操作文件夹的时候一般习惯推荐以/结尾
Types页 如果发现有类型不支持保存,去Types看一眼是不是没有默认配置上,配置某类型实际上会自动生成一段支持类型的代码,等他重新编译完就好了
Csharp
class ES3SettingTest
{
public static ES3Settings es3setting = new ES3Settings();
}
[Serializable]
public class Skill
{
[ES3NonSerializable]
public string skill_name;
public int skill_atk;
}
public class TestES : MonoBehaviour
{
public int hp = 10;
public List<int> idlist = new List<int> { 1,2,3,4,5};
public Dictionary<string, int> id_res = new Dictionary<string, int>();
public List<Skill> skills = new List<Skill>();
void Start()
{
ES3.DirectoryExists("my/path/");// 判断文件夹是不是存在
ES3.DeleteDirectory("my/path/");// 文件夹 文件 Key都能删除
ES3.FileExists("SaveFile.es3");// 判断整个存档是不是存在
if (ES3.KeyExists("hp"))// 不传就是判断默认存档中的hp是不是存在
{
hp = ES3.Load<int>("hp",hp); // 泛型加载并给个默认值,非null和default的时候可以帮助省略泛型,直接推断
// 也可以加载之后as强转
}
ES3AutoSaveMgr.Current.Save();
ES3SettingTest.es3setting.encryptionType = ES3.EncryptionType.AES; // 和在面板上改一样,用的时候参数传递进去
ES3.CopyFile("SaveFile.es3","backup/SaveFile.es3"); // 也支持CopyDirectory,用来备份存档
}
void OnApplicationQuit()
{
ES3.Save("hp",this.hp); // 任意类型都可以Save(key,object),很强
}
}Cysharp开发的0内存分配代替Linq的库,不触发GC适合unity使用。
摄像机运动的插件
流畅的过场动画,Unity内置的插件。
强化版的动画机插件 【Lee哥】替换Unity内置动画!3D动画控制器 - Animancer Pro | Unity | 游戏开发 | 优秀工具_哔哩哔哩_bilibili
直接浏览器打开网页的话使用Application.OpenURL(inputStr); 。
嵌入试了好几个插件,还是这个3D WebView for Android™ (Web Browser)好用AssetsStore,说是for Android实际上都能用,本质上来说就是系统webview套壳。
提供了Canvas和Image的的Prefab,支持交互支持全端,有示例场景,Prefab拖进去用就可以了,里面封好了一个RawImage。虽然资料不多但是不会的去翻文档问AI基本都能满足需求
这东西实际上打包出来非常大,包了个Chrome进去多了三四百兆
Csharp
public async void ShowPage(string kw)
{
string url = $"https://www.baidu.com/s?wd=s{kw}";
introWebViewPrefab.gameObject.SetActive(true); // 不知道为什么必须要有
await introWebViewPrefab.WaitUntilInitialized(); // 必须await使用
introWebViewPrefab.WebView.LoadUrl(url); // 直接加载,可以和正常浏览器一样
}Unity的音频不支持热加载
音频插件,可以做类似于雨大小这种精细一些的音效控制 Wwise 终末地中的音频系统设计
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。