尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Unity农场模拟Demo开发全流程:从资源导入到交互系统实现

Unity农场模拟Demo开发全流程:从资源导入到交互系统实现 1. 项目概述与核心价值最近在社区里看到不少朋友对Unity Asset Store上那款“3D Farm Pack”资源包很感兴趣也看到很多人在搜索“Unity Demo-3DFarm详解”相关的教程。作为一个在Unity项目里摸爬滚打多年的老鸟我决定结合这个资源包从头到尾、掰开揉碎地聊聊如何基于它搭建一个完整的、可交互的3D农场Demo。这不仅仅是展示一个资源包怎么用更是想通过这个具体的案例把Unity项目从资源导入、场景搭建、到核心交互逻辑实现、再到性能优化的完整工作流给大家讲透。无论你是刚入门Unity的新手想找个具体的项目练手还是有一定经验的中级开发者希望学习如何高效整合第三方资源并构建完整游戏逻辑这个系列内容都能给你带来实实在在的参考价值。“3D Farm Pack”是一个风格化、低多边形的农场环境资源包包含了谷仓、风车、水井、各种蔬菜作物、围栏、道具等模块化资产。它的价值在于提供了一个风格统一、即拿即用的美术基础让我们能快速搭建出视觉观感不错的农场场景从而将开发重心集中在游戏玩法和交互逻辑的实现上。在接下来的内容里我会假设我们已经获取了这个资源包并以此为基础深入探讨如何构建一个包含种植、浇水、收获等核心循环的3D农场模拟Demo。2. 项目初始化与资源导入规范2.1 工程创建与基础设置启动Unity Hub创建一个新的3D项目Core或URP模板均可本示例以URP为例因其在现代项目中的通用性更高。项目创建后第一件事不是急着导入资源而是先规划好工程目录结构。一个清晰的结构是项目可维护性的基石。我通常会在Assets目录下创建如下文件夹_ThirdParty存放所有从Asset Store购买或下载的第三方资源包如“3D Farm Pack”。这样做可以清晰区分自有资产和外部资产未来更新或替换资源包时不会影响自己的代码。Art存放项目自有的美术资源可进一步细分为Models、Textures、Materials、Shaders等。Scripts所有C#脚本的归宿按功能模块分文件夹如Core、FarmSystem、UI、Utilities。Scenes存放游戏场景。Prefabs存放预制体同样可以按功能分子文件夹。Settings存放各种配置资产如URP Asset、Input System Actions、Addressable Groups设置等。Audio音效和音乐。创建好目录后通过Unity编辑器菜单栏的Window Rendering Render Pipeline Converter将项目材质和光照设置转换为URP如果创建的是URP项目此步可省略但需检查转换是否完全。然后将下载好的“3D Farm Pack.unitypackage”文件直接拖入Unity的Project窗口并选择导入到Assets/_ThirdParty/3D Farm Pack目录下。注意导入第三方资源包时务必勾选“Import into a folder named after the package”选项如果Unity版本支持或者手动指定到我们预设的_ThirdParty目录下。这能有效避免资源文件散落各处造成后期管理的噩梦。2.2 资源审查与预处理资源导入后不要立刻开始搭建场景。花15-30分钟仔细浏览导入的资源内容至关重要。打开_ThirdParty/3D Farm Pack文件夹你会看到通常包含Models、Textures、Materials、Prefabs等子文件夹。模型检查逐一查看关键模型如Barn, Windmill, Well, Crops。在Inspector面板关注Mesh Import Settings检查Read/Write Enabled。对于运行时不需要通过脚本修改顶点数据的静态模型务必取消勾选此项这能节省大量运行时内存。我们的农场建筑、道具基本都是静态的所以应该关掉。网格压缩Mesh Compression设置为Medium或High可以在几乎不影响视觉质量的前提下减小构建体积。法线Normals确认导入设置正确如果模型自带法线贴图通常选择Calculate或Import。材质与着色器适配由于我们使用的是URP资源包自带的材质球可能基于Built-in Render Pipeline。Unity在导入时通常会尝试自动转换但转换结果需要人工复核。选中几个关键材质的材质球检查其Shader是否为Universal Render Pipeline/...开头。如果仍然是Standard或Standard (Specular setup)则需要手动替换。可以批量选中所有材质在Inspector顶部尝试使用Edit Render Pipeline Universal Render Pipeline Upgrade Project Materials to URP Materials进行批量升级。实操心得批量升级后务必抽样检查一些复杂材质比如带有透明通道、视差效果的材质的表现有时需要手动调整一些参数如平滑度、法线强度来达到最佳效果。纹理优化检查关键纹理如Albedo贴图、法线贴图的导入设置。对于不需要透明通道的Albedo贴图将Texture Type设为Default并关闭Alpha Is Transparency。根据纹理在游戏中的最大显示尺寸合理设置Max Size例如场景背景纹理可以设为1024道具纹理设为512或256。启用Generate Mip Maps以获得更好的远处渲染性能和抗锯齿效果。对于法线贴图务必将其Texture Type设置为Normal Map这样Unity才能正确解码和使用它。3. 场景搭建与光照烘焙实战3.1 地形塑造与布局规划首先删除场景中默认的Main Camera和Directional Light我们将完全自定义。在Hierarchy中创建一个空对象命名为_FarmScene作为所有场景对象的根节点便于整体管理。创建地形使用Unity的Terrain工具GameObject 3D Object Terrain创建一块基础地形。将其置于_FarmScene下重命名为FarmTerrain。根据你设想的农场大小在Terrain组件中调整Terrain Width和Length例如250x250。使用地形塑造工具简单地刷出一些平缓的起伏模拟农田的轻微坡度。关键技巧在绘制地形纹理前先使用Set Height工具将一大片区域整平作为核心的耕种区。布局与摆放现在从_ThirdParty/3D Farm Pack/Prefabs文件夹中将主要的建筑预制体如谷仓、风车、水井、房屋拖入场景。遵循一些基础的美术原则层次感将大型建筑谷仓放在靠后的位置中小型建筑和树木放在中景农田和道路放在前景。视觉引导利用道路、围栏的线条将玩家的视线引导向场景的兴趣中心比如谷仓前的空地。自然错落摆放树木、石块、灌木等自然道具时避免完全均匀的网格状分布使用不同的旋转和轻微的位置偏移来营造自然感。利用预制体对于需要大量重复的对象如围栏、某一种树木先摆放好一个并调整到满意状态然后将其拖回Project窗口创建为新的预制体再使用复制或Prefab Variant进行批量放置效率极高。3.2 光照与环境设置光照是场景氛围的魔法师。我们采用静态光照烘焙Baked GI来获得高质量、高性能的光影效果。光源配置创建一个新的Directional Light重命名为Sun作为主光源。调整其旋转例如X50, Y30模拟下午时分的阳光角度。在Light组件中设置Mode为Mixed。这样它既能烘焙静态物体的光影到光照贴图又能为动态物体如未来要添加的玩家角色提供实时照明。适当调整Color为偏暖的黄色例如#FFE6C2Intensity设为1.2左右以增强风格化农场的温馨感。创建一个Reflection Probe反射探头覆盖主要建筑区域并设置其Type为Baked用于烘焙环境反射信息让材质尤其是金属、水面看起来更真实。环境光与天空盒打开Window Rendering Lighting设置Unity 2022可能在Window Rendering Environment。在Environment标签页下为Skybox Material指定一个URP友好的天空盒材质例如从Unity的Samples中导入一个或使用简单的渐变天空盒。调整Environment Lighting的Source为SkyboxIntensity Multiplier可以稍微调高如1.2让场景整体更明亮。光照烘焙参数与执行在Lighting窗口的Lightmapping Settings中选择Lightmapper为Progressive GPU (Preview)它的烘焙速度通常远快于CPU。Lightmap Resolution决定了光照贴图的精细度对于我们的农场场景设为40-50texels per unit是一个不错的起点。Lightmap Padding防止贴图边缘渗色保持默认或设为4-8。最重要的一步将场景中所有静态的、不会移动的物体地形、建筑、树木、岩石的Static复选框勾选上至少勾选Contribute GI和Reflection Probe Static。这告诉Unity这些物体参与光照烘焙。点击Generate Lighting按钮开始烘焙。这个过程可能需要几分钟到几十分钟取决于场景复杂度和电脑性能。烘焙完成后你会看到场景中的静态物体拥有了非常柔和、真实的光影和间接光照效果性能开销也降到了最低。4. 核心农场交互系统实现4.1 可耕种地块系统农场游戏的核心是土地。我们将创建一个FarmPlot耕种地块系统。创建地块预制体在场景中创建一个Plane重命名为FarmPlot缩放至合适大小例如2x2单位。这代表一块独立的可耕种土地。为其创建一个材质球使用URP Lit Shader赋予它一个泥土的Albedo贴图资源包内通常提供。添加一个Box Collider并勾选Is Trigger用于检测玩家的交互如点击、进入。创建一个C#脚本FarmPlot.cs将其挂载到该GameObject上。然后将这个配置好的FarmPlot对象拖入Project窗口创建为一个预制体并删除场景中的实例。FarmPlot脚本逻辑详解using UnityEngine; using System.Collections; // 如果需要使用协程 public class FarmPlot : MonoBehaviour { public enum PlotState { Empty, Planted, Growing, ReadyToHarvest, Withered } public PlotState currentState PlotState.Empty; [Header(Growth Settings)] public float growthTimeSeconds 60.0f; // 从种植到成熟的总时间 public float waterLevel 0f; // 当前水分值 public float maxWaterLevel 100f; public float waterDecayRatePerSecond 1.0f; // 每秒水分流失 [Header(Visual References)] public GameObject cropModel; // 当前种植的作物模型动态加载或子物体 public ParticleSystem waterParticleEffect; // 浇水粒子效果 public MeshRenderer groundRenderer; // 地块本身的渲染器用于改变颜色 private float growthProgress 0f; // 0 到 1 private Coroutine growthCoroutine; void Start() { UpdateVisualState(); } void Update() { // 如果地块已种植模拟水分流失 if (currentState PlotState.Planted || currentState PlotState.Growing) { waterLevel Mathf.Max(0, waterLevel - waterDecayRatePerSecond * Time.deltaTime); if (waterLevel 0 currentState ! PlotState.Withered) { SetWithered(); } } } // 玩家交互尝试种植 public bool TryPlantCrop(GameObject cropPrefab) { if (currentState ! PlotState.Empty) return false; // 实例化作物模型作为地块的子物体 if (cropPrefab ! null) { GameObject newCrop Instantiate(cropPrefab, transform.position, Quaternion.identity, transform); cropModel newCrop; // 初始化为幼苗状态可以缩放为0 newCrop.transform.localScale Vector3.zero; } currentState PlotState.Planted; waterLevel maxWaterLevel * 0.5f; // 初始给予一半水分 growthProgress 0f; UpdateVisualState(); // 开始生长协程 if (growthCoroutine ! null) StopCoroutine(growthCoroutine); growthCoroutine StartCoroutine(GrowthRoutine()); return true; } // 玩家交互浇水 public void WaterPlot(float amount) { if (currentState PlotState.Withered || currentState PlotState.ReadyToHarvest) return; waterLevel Mathf.Min(maxWaterLevel, waterLevel amount); if (waterParticleEffect ! null) waterParticleEffect.Play(); // 如果是从枯萎状态浇水可以恢复生长此处简化逻辑 if (currentState PlotState.Withered waterLevel maxWaterLevel * 0.3f) { currentState PlotState.Growing; UpdateVisualState(); } } // 玩家交互收获 public bool TryHarvest(out int yieldAmount) { yieldAmount 0; if (currentState ! PlotState.ReadyToHarvest) return false; // 根据作物类型计算产量这里简化假设固定值 yieldAmount 3; // 销毁作物模型 if (cropModel ! null) Destroy(cropModel); cropModel null; currentState PlotState.Empty; growthProgress 0f; UpdateVisualState(); return true; } IEnumerator GrowthRoutine() { currentState PlotState.Growing; UpdateVisualState(); while (growthProgress 1f) { // 生长速度受水分影响 float growthRate waterLevel maxWaterLevel * 0.2f ? 1.0f : 0.5f; // 水分不足则生长减慢 growthProgress (Time.deltaTime / growthTimeSeconds) * growthRate; growthProgress Mathf.Clamp01(growthProgress); // 更新作物模型视觉例如缩放、颜色 if (cropModel ! null) { // 简单的线性缩放模拟生长 cropModel.transform.localScale Vector3.one * growthProgress; } yield return null; // 等待下一帧 } // 生长完成 currentState PlotState.ReadyToHarvest; UpdateVisualState(); growthCoroutine null; } void SetWithered() { currentState PlotState.Withered; if (growthCoroutine ! null) { StopCoroutine(growthCoroutine); growthCoroutine null; } UpdateVisualState(); } void UpdateVisualState() { // 根据状态改变地块颜色或材质 if (groundRenderer ! null) { Color stateColor Color.gray; switch (currentState) { case PlotState.Empty: stateColor new Color(0.6f, 0.4f, 0.2f); break; // 泥土色 case PlotState.Planted: case PlotState.Growing: stateColor new Color(0.4f, 0.6f, 0.2f); break; // 湿润的绿色 case PlotState.ReadyToHarvest: stateColor Color.yellow; break; case PlotState.Withered: stateColor Color.gray; break; } groundRenderer.material.color stateColor; } } // 在Unity编辑器中可视化调试信息 void OnDrawGizmosSelected() { Gizmos.color Color.green; Gizmos.DrawWireCube(transform.position Vector3.up * 0.5f, new Vector3(2, 1, 2)); } }这个脚本是地块系统的核心它管理了状态机、生长逻辑、水分系统和视觉反馈。注意事项协程GrowthRoutine中使用了while循环和yield return null这是一种常见的每帧更新逻辑的方式但要确保在对象被禁用或销毁时停止协程我们在SetWithered和可能的地方做了处理。4.2 玩家交互与物品系统有了地块我们需要让玩家能与它互动。玩家控制器创建一个简单的第三人称或上帝视角的控制器。这里以简单的点击交互为例。创建一个PlayerController脚本挂载到代表玩家的物体或主摄像机上。using UnityEngine; using UnityEngine.EventSystems; // 用于检查UI点击 public class PlayerController : MonoBehaviour { public Camera playerCamera; public LayerMask farmPlotLayer; // 专门用于地块的Layer public GameObject waterCanEffect; // 浇水壶粒子效果预制体 public Inventory inventory; // 背包系统引用 void Update() { if (Input.GetMouseButtonDown(0)) // 左键点击 { // 检查是否点击在UI上避免穿透 if (EventSystem.current.IsPointerOverGameObject()) return; Ray ray playerCamera.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, 100f, farmPlotLayer)) { FarmPlot plot hit.collider.GetComponentFarmPlot(); if (plot ! null) { HandlePlotInteraction(plot, hit.point); } } } } void HandlePlotInteraction(FarmPlot plot, Vector3 hitPoint) { // 这里可以扩展为根据玩家当前手持工具种子、水壶进行不同操作 // 示例如果手持水壶则浇水 if (inventory ! null inventory.currentTool Inventory.Tool.WateringCan) { plot.WaterPlot(30f); // 浇水30单位 // 实例化浇水效果 if (waterCanEffect ! null) { Instantiate(waterCanEffect, hitPoint Vector3.up * 0.2f, Quaternion.identity); } } // 示例如果手持种子且地块为空则种植 else if (inventory ! null inventory.currentTool Inventory.Tool.Seeds inventory.seedCount 0) { if (plot.TryPlantCrop(inventory.GetCurrentCropPrefab())) { inventory.seedCount--; // 更新UI } } // 示例如果什么都没拿尝试收获 else if (inventory ! null inventory.currentTool Inventory.Tool.None) { int yield; if (plot.TryHarvest(out yield)) { inventory.AddItem(Wheat, yield); // 假设收获小麦 // 更新UI } } } }简易库存系统创建一个Inventory脚本管理玩家的工具、种子和收获物。using System.Collections.Generic; using UnityEngine; public class Inventory : MonoBehaviour { public enum Tool { None, WateringCan, Seeds, Hoe } public Tool currentTool Tool.None; public int seedCount 10; public GameObject[] cropPrefabs; // 不同作物的预制体数组 private Dictionarystring, int harvestedItems new Dictionarystring, int(); public GameObject GetCurrentCropPrefab() { // 简化逻辑返回第一种作物预制体 if (cropPrefabs.Length 0) return cropPrefabs[0]; return null; } public void AddItem(string itemName, int amount) { if (harvestedItems.ContainsKey(itemName)) harvestedItems[itemName] amount; else harvestedItems.Add(itemName, amount); Debug.Log($获得 {amount} 个 {itemName} 当前总数{harvestedItems[itemName]}); } public void SwitchTool(Tool newTool) { currentTool newTool; // 这里可以触发切换工具的动画或UI反馈 } }4.3 UI界面与反馈使用Unity的UGUI或新的UI Toolkit创建游戏界面。创建基础UI在场景中创建Canvas设置Render Mode为Screen Space - Overlay。工具选择栏创建一组按钮代表锄头、水壶、种子袋。为每个按钮添加Button组件并编写点击事件调用Inventory的SwitchTool方法。状态显示创建Text组件用于显示当前选中的工具、种子数量、以及收获物品的数量。在PlayerController和Inventory中相应逻辑处更新这些Text的文本内容。地块状态提示当鼠标悬停或点击地块时可以通过一个世界空间的UIWorld Space Canvas或屏幕上方飘字显示该地块的当前状态如“水分65%”、“生长进度42%”。这需要修改FarmPlot脚本在OnMouseEnter或通过PlayerController的射线检测来触发UI显示。5. 性能优化与高级功能探讨5.1 针对农场Demo的性能调优当你的农场有几十甚至上百块地时性能问题就会显现。以下是几个关键的优化方向绘制调用优化静态合批确保所有静态的、使用相同材质的物体如大量相同的围栏、石头都标记为StaticUnity会自动对它们进行静态合批大幅减少Draw Calls。动态合批对于少量、简单的动态物体比如同一种作物如果它们使用相同的材质且顶点数足够少Unity可能会进行动态合批。确保它们的缩放比例一致非统一缩放会破坏合批。GPU Instancing对于大量相同的物体如同一种作物模型即使它们是动态的也可以利用GPU Instancing。在作物材质的Inspector中勾选Enable GPU Instancing。然后在实例化作物模型的代码中确保使用MaterialPropertyBlock来传递每实例不同的数据如生长阶段对应的颜色或缩放而不是创建新的材质实例。脚本效率优化避免每帧查找不要在Update里使用GameObject.Find或GetComponent。在Start或Awake中缓存引用。使用协程代替计时器我们的生长逻辑已经使用了协程这比在Update中用Time.deltaTime累加更清晰且可以方便地暂停。对象池管理作物当作物被收获和重新种植时频繁的Instantiate和Destroy会产生GC垃圾回收压力。实现一个简单的对象池来复用作物模型。public class CropObjectPool : MonoBehaviour { public GameObject cropPrefab; public int poolSize 20; private QueueGameObject pool new QueueGameObject(); void Start() { for (int i 0; i poolSize; i) { GameObject obj Instantiate(cropPrefab); obj.SetActive(false); pool.Enqueue(obj); } } public GameObject GetCrop() { if (pool.Count 0) { GameObject obj pool.Dequeue(); obj.SetActive(true); return obj; } else { // 池空了动态扩展或返回null GameObject obj Instantiate(cropPrefab); return obj; } } public void ReturnCrop(GameObject obj) { obj.SetActive(false); pool.Enqueue(obj); } }然后在FarmPlot脚本中不再直接Destroy作物而是调用ReturnCrop种植时从池中GetCrop。LOD与视锥体剔除对于复杂的模型如风车、谷仓可以设置LOD Group在远处使用低模。Unity的视锥体剔除会自动进行确保相机看不到的物体不被渲染。5.2 可扩展功能设想基础系统完成后可以考虑添加更多深度内容作物多样性系统扩展Inventory和FarmPlot支持多种种子小麦、玉米、胡萝卜每种作物有不同的生长时间、水分需求、收获价值和模型。昼夜循环与天气系统使用Unity的Lighting设置动态调整方向光旋转和强度来模拟昼夜。添加一个天气管理器控制降雨自动为所有地块增加水分、干旱增加水分蒸发速度。经济与任务系统添加一个商店UI玩家可以出售收获的作物换取金币再用金币购买更多种子或升级工具。设计简单的每日任务或成长任务如“收获10个小麦”。数据持久化使用PlayerPrefs简单或JSON/Binary文件序列化复杂来保存游戏数据如地块状态、背包物品、金币数量。在GameManager的OnApplicationQuit和OnApplicationPause中触发保存。使用Addressable进行资源管理当作物种类、建筑皮肤很多时可以使用Unity的Addressable系统来异步加载资源避免初始加载卡顿并方便进行热更新即“华佗热更新”等热词关联的技术。将不同的作物预制体标记为Addressable通过标签或key来动态加载。6. 构建发布与常见问题排查6.1 平台构建设置以构建PC单机版为例打开File Build Settings。将当前场景拖入Scenes In Build列表。选择目标平台为PC, Mac Linux Standalone在Target Platform中选择你的操作系统。点击Player Settings...按钮在Inspector中Company Name和Product Name填写你的信息。Default Icon设置游戏图标。在Resolution and Presentation中可以设置默认窗口大小和是否全屏。对于WebGL如果发布网页版需要关注WebGL平台特有的设置如Compression Format推荐Brotli、Memory Size根据项目复杂度调整初始可设512MB。WebGL初始化慢常与代码体积和资源加载有关需用Addressable分包并启用压缩。回到Build Settings点击Build选择输出文件夹开始构建。6.2 常见问题与解决方案实录在开发过程中你几乎一定会遇到下面这些问题问题现象可能原因排查步骤与解决方案场景中模型显示为紫色粉红色材质球丢失或Shader不兼容。1. 检查模型Inspector中的Materials列表看材质是否显示为“Missing”。2. 如果是Missing重新指定正确的材质球。3. 如果材质存在但仍为紫色检查材质球使用的Shader是否与当前渲染管线URP/HDRP兼容。需手动替换为URP对应的Lit Shader。点击地块没有反应1. 地块没有Collider。2. Collider不是Trigger。3. 射线检测的Layer设置不对。4. 点击被UI阻挡。1. 确认FarmPlot预制体上有Box Collider组件。2. 确认Is Trigger已勾选。3. 在PlayerController中确认farmPlotLayer变量在Inspector中被正确设置为地块所在的Layer。4. 在PlayerController的点击代码中已通过EventSystem.current.IsPointerOverGameObject()排除了UI点击。作物生长协程不工作或异常1. 协程未正确启动或停止。2. GameObject被禁用或销毁导致协程中断。3. 时间计算逻辑错误。1. 使用StartCoroutine启动协程并保存Coroutine引用以便在需要时StopCoroutine。2. 确保在OnDisable或OnDestroy方法中停止所有运行的协程。3. 在GrowthRoutine中使用Time.deltaTime进行与帧率无关的时间累加确保生长速度稳定。游戏打包后找不到资源如材质变紫资源未被包含在构建中。1. 确保所有使用的材质、纹理、预制体都被放置在Resources文件夹内或者被标记为Addressable并正确配置了构建组。2. 对于常规资源只要被场景引用或Resources文件夹内默认会被打包。检查构建日志看是否有“missing reference”警告。在编辑器运行正常打包后UI错位或点击无效Canvas缩放模式或屏幕适配问题。1. 检查Canvas的Canvas Scaler组件。对于PC项目UI Scale Mode通常设为Scale With Screen SizeReference Resolution设为1080p1920x1080。2. 确保所有UI元素的锚点Anchors设置正确能适应不同分辨率。大量地块导致帧率下降1. Draw Calls过高。2. 每块地的Update中有昂贵操作。3. 未使用对象池GC频繁。1. 使用Frame Debugger工具查看Draw Calls通过合批、GPU Instancing优化。2. 优化FarmPlot.Update中的逻辑例如将水分流失计算改为按固定时间间隔如每2秒进行一次而不是每帧。3. 实现作物模型的对象池如5.1节所述。我个人在实现这个Demo过程中的一个深刻体会是前期花时间设计一个清晰的数据结构和状态机如FarmPlot的PlotState远比后期在混乱的逻辑里打补丁要高效得多。当你想增加“肥料系统”、“病虫害系统”时一个良好的状态机基础会让你轻松地插入新的状态和转换逻辑。另外不要过早追求视觉完美先用Cube和Sphere把核心交互循环种植-生长-收获跑通然后再用“3D Farm Pack”这样的美术资源进行“皮肤”替换这样能确保你的游戏首先是“可玩的”其次是“好看的”。最后性能优化是一个持续的过程边开发边用Profiler观察特别是关注GC Alloc和Draw Calls及时发现瓶颈避免项目后期积重难返。
返回列表