Unity程序员如何做移动端性能优化:原理与商业项目实战
·
Unity程序员如何做移动端性能优化:原理与商业项目实战
引言:移动端性能优化的特殊挑战
移动设备与PC平台有着本质的性能差异:有限的CPU/GPU性能、严格的功耗限制、不稳定的网络环境、多样化的硬件配置和操作系统版本。在移动端,性能优化不仅影响游戏体验,更直接影响电池寿命、设备发热和用户留存率。
商业项目关键指标:
- 帧率稳定性(60FPS/30FPS目标)
- 内存使用(iOS 1.8GB警告线,Android因设备而异)
- 耗电量(每小时的电池消耗百分比)
- 安装包大小(App Store 200MB蜂窝下载限制)
- 发热控制(设备表面温度不超过45℃)
第一部分:移动端性能分析工具链
1.1 Unity Profiler深度使用
关键Profiler模块:
// 自定义性能监控系统
public class MobilePerformanceMonitor : MonoBehaviour
{
private class FrameMetrics
{
public float FrameTime;
public float CPUTime;
public float GPUTime;
public int DrawCalls;
public int SetPassCalls;
public int Triangles;
public int Vertices;
public long MemoryAllocated;
public long MemoryReserved;
public int GCCollectCount;
}
private Queue<FrameMetrics> m_frameHistory = new Queue<FrameMetrics>();
private const int HISTORY_SIZE = 300; // 5秒数据(假设60FPS)
private float m_lastGCCollectTime = 0;
private int m_gcCollectCount = 0;
void Update()
{
CollectFrameMetrics();
AnalyzePerformance();
CheckCriticalIssues();
}
void CollectFrameMetrics()
{
var metrics = new FrameMetrics
{
FrameTime = Time.unscaledDeltaTime * 1000f, // 毫秒
MemoryAllocated = Profiler.GetTotalAllocatedMemoryLong() / 1024 / 1024, // MB
MemoryReserved = Profiler.GetTotalReservedMemoryLong() / 1024 / 1024,
DrawCalls = GetDrawCallCount(),
Triangles = GetTriangleCount(),
Vertices = GetVertexCount()
};
m_frameHistory.Enqueue(metrics);
if (m_frameHistory.Count > HISTORY_SIZE)
m_frameHistory.Dequeue();
}
void AnalyzePerformance()
{
if (m_frameHistory.Count < 10) return;
// 计算平均帧时间
float avgFrameTime = m_frameHistory.Average(m => m.FrameTime);
float fps = 1000f / avgFrameTime;
// 检测掉帧(帧时间超过阈值)
int droppedFrames = m_frameHistory.Count(m => m.FrameTime > 33.3f); // 低于30FPS
// 检测内存泄漏趋势
var oldest = m_frameHistory.First();
var newest = m_frameHistory.Last();
float memoryGrowth = newest.MemoryAllocated - oldest.MemoryAllocated;
// 生成报告
if (Time.frameCount % 60 == 0) // 每60帧报告一次
{
Debug.Log($"性能报告 - FPS: {fps:F1}, 掉帧: {droppedFrames}, " +
$"内存增长: {memoryGrowth:F1}MB");
}
}
void CheckCriticalIssues()
{
var current = m_frameHistory.LastOrDefault();
if (current == null) return;
// 关键问题检测
if (current.FrameTime > 50f) // 低于20FPS
{
Debug.LogWarning($"严重掉帧: {current.FrameTime:F1}ms");
LogDetailedAnalysis();
}
if (current.MemoryAllocated > GetMemoryWarningThreshold())
{
Debug.LogError($"内存过高: {current.MemoryAllocated}MB");
TriggerMemoryCleanup();
}
}
// 获取DrawCall等渲染数据(需要自定义实现)
int GetDrawCallCount()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
return UnityEditor.UnityStats.drawCalls;
#else
// 发布版本需要其他方式获取
return -1;
#endif
}
// 自定义性能采样标记
public class ScopedProfilerSample : IDisposable
{
private string m_name;
private System.Diagnostics.Stopwatch m_stopwatch;
public ScopedProfilerSample(string name)
{
m_name = name;
m_stopwatch = System.Diagnostics.Stopwatch.StartNew();
Profiler.BeginSample(name);
}
public void Dispose()
{
m_stopwatch.Stop();
Profiler.EndSample();
// 记录耗时(仅开发版本)
#if DEVELOPMENT_BUILD
if (m_stopwatch.ElapsedMilliseconds > 5) // 超过5ms警告
{
Debug.LogWarning($"性能警告 - {m_name}: {m_stopwatch.ElapsedMilliseconds}ms");
}
#endif
}
}
// 使用示例
void ProcessComplexLogic()
{
using (new ScopedProfilerSample("ProcessComplexLogic"))
{
// 复杂的逻辑处理
// ...
}
}
}
1.2 平台专属分析工具
iOS专用工具链:
// iOS性能监控集成
public class iOSPerformanceMonitor
{
#if UNITY_IOS && !UNITY_EDITOR
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern void StartPerfMonitoring();
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern double GetThermalState(); // 0-1,1表示过热
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern double GetBatteryLevel();
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern void LogMetalPerformance(string jsonData);
#endif
public void Initialize()
{
#if UNITY_IOS && !UNITY_EDITOR
StartPerfMonitoring();
#endif
}
public float GetThermalStatus()
{
#if UNITY_IOS && !UNITY_EDITOR
return (float)GetThermalState();
#else
return 0f;
#endif
}
// 热限制自适应
public void ApplyThermalThrottling()
{
float thermalState = GetThermalStatus();
if (thermalState > 0.7f) // 设备过热
{
// 降低渲染质量
QualitySettings.SetQualityLevel(0, true); // 最低画质
// 降低帧率
Application.targetFrameRate = 30;
// 减少粒子效果
ParticleSystem[] allParticles = GameObject.FindObjectsOfType<ParticleSystem>();
foreach (var ps in allParticles)
{
var emission = ps.emission;
emission.rateOverTimeMultiplier *= 0.5f;
}
Debug.Log("热限制触发:已降低渲染质量");
}
else if (thermalState > 0.3f) // 设备温热
{
// 适度降低质量
Application.targetFrameRate = 45;
}
}
}
Android专用工具链:
// Android性能监控
public class AndroidPerformanceMonitor
{
#if UNITY_ANDROID && !UNITY_EDITOR
private static AndroidJavaClass m_unityPlayer;
private static AndroidJavaObject m_currentActivity;
private static AndroidJavaObject m_powerManager;
static AndroidPerformanceMonitor()
{
m_unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
m_currentActivity = m_unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaClass powerManagerClass = new AndroidJavaClass("android.os.PowerManager");
m_powerManager = m_currentActivity.Call<AndroidJavaObject>("getSystemService", "power");
}
#endif
// 检查是否处于省电模式
public static bool IsPowerSaveMode()
{
#if UNITY_ANDROID && !UNITY_EDITOR
return m_powerManager.Call<bool>("isPowerSaveMode");
#else
return false;
#endif
}
// 获取可用内存
public static long GetAvailableMemory()
{
#if UNITY_ANDROID && !UNITY_EDITOR
AndroidJavaClass activityManagerClass = new AndroidJavaClass("android.app.ActivityManager");
AndroidJavaObject activityManager = m_currentActivity.Call<AndroidJavaObject>("getSystemService", "activity");
AndroidJavaObject memoryInfo = new AndroidJavaObject("android.app.ActivityManager$MemoryInfo");
activityManager.Call("getMemoryInfo", memoryInfo);
long availMem = memoryInfo.Get<long>("availMem");
return availMem / 1024 / 1024; // MB
#else
return 0;
#endif
}
// 设置CPU核心使用(大核/小核调度)
public static void SetCPUAffinity(bool useBigCoresOnly)
{
#if UNITY_ANDROID && !UNITY_EDITOR
try
{
AndroidJavaClass processClass = new AndroidJavaClass("android.os.Process");
int pid = processClass.CallStatic<int>("myPid");
// 获取CPU核心信息
string cpuInfo = ReadFile("/proc/cpuinfo");
// 解析核心信息并设置亲和性...
Debug.Log($"设置CPU亲和性,PID: {pid}");
}
catch (System.Exception e)
{
Debug.LogWarning($"设置CPU亲和性失败: {e.Message}");
}
#endif
}
#if UNITY_ANDROID && !UNITY_EDITOR
private static string ReadFile(string path)
{
using (AndroidJavaObject file = new AndroidJavaObject("java.io.File", path))
using (AndroidJavaObject fis = new AndroidJavaObject("java.io.FileInputStream", file))
using (AndroidJavaObject bis = new AndroidJavaObject("java.io.BufferedInputStream", fis))
{
byte[] buffer = new byte[1024];
System.Text.StringBuilder content = new System.Text.StringBuilder();
while (bis.Call<int>("read", buffer) != -1)
{
content.Append(System.Text.Encoding.UTF8.GetString(buffer));
}
return content.ToString();
}
}
#endif
}
第二部分:渲染性能优化
2.1 DrawCall优化策略
// 动态DrawCall合并系统
public class DynamicBatchingOptimizer : MonoBehaviour
{
[System.Serializable]
public class BatchableMaterial
{
public Material Material;
public List<MeshRenderer> Renderers = new List<MeshRenderer>();
public bool IsStatic = true;
}
private Dictionary<Material, BatchableMaterial> m_materialGroups = new Dictionary<Material, BatchableMaterial>();
private List<GameObject> m_dynamicBatchedObjects = new List<GameObject>();
void Start()
{
// 自动检测并合并静态物体
StartCoroutine(AutoBatchStaticObjects());
// 监控DrawCall
StartCoroutine(MonitorDrawCalls());
}
IEnumerator AutoBatchStaticObjects()
{
// 分批处理避免卡顿
MeshRenderer[] allRenderers = FindObjectsOfType<MeshRenderer>();
int batchSize = 50;
for (int i = 0; i < allRenderers.Length; i += batchSize)
{
int end = Mathf.Min(i + batchSize, allRenderers.Length);
for (int j = i; j < end; j++)
{
MeshRenderer renderer = allRenderers[j];
// 只处理静态物体
if (renderer.gameObject.isStatic)
{
AddToBatchGroup(renderer);
}
}
yield return null; // 下一帧继续
}
// 执行合并
ExecuteBatching();
}
void AddToBatchGroup(MeshRenderer renderer)
{
Material mat = renderer.sharedMaterial;
if (mat == null) return;
if (!m_materialGroups.TryGetValue(mat, out var group))
{
group = new BatchableMaterial { Material = mat };
m_materialGroups[mat] = group;
}
group.Renderers.Add(renderer);
}
void ExecuteBatching()
{
foreach (var kvp in m_materialGroups)
{
var group = kvp.Value;
if (group.Renderers.Count < 2) continue; // 需要至少2个渲染器
// 合并网格
CombineMeshes(group);
}
}
void CombineMeshes(BatchableMaterial group)
{
List<MeshFilter> meshFilters = new List<MeshFilter>();
List<CombineInstance> combineInstances = new List<CombineInstance>();
foreach (var renderer in group.Renderers)
{
MeshFilter filter = renderer.GetComponent<MeshFilter>();
if (filter != null && filter.sharedMesh != null)
{
meshFilters.Add(filter);
CombineInstance ci = new CombineInstance
{
mesh = filter.sharedMesh,
transform = filter.transform.localToWorldMatrix
};
combineInstances.Add(ci);
}
}
if (combineInstances.Count == 0) return;
// 创建合并后的游戏对象
GameObject combinedObject = new GameObject("CombinedMesh_" + group.Material.name);
MeshFilter combinedFilter = combinedObject.AddComponent<MeshFilter>();
MeshRenderer combinedRenderer = combinedObject.AddComponent<MeshRenderer>();
// 合并网格
Mesh combinedMesh = new Mesh();
combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
combinedFilter.mesh = combinedMesh;
combinedRenderer.material = group.Material;
// 禁用原始渲染器
foreach (var renderer in group.Renderers)
{
renderer.enabled = false;
}
m_dynamicBatchedObjects.Add(combinedObject);
Debug.Log($"合并了 {combineInstances.Count} 个网格,材质: {group.Material.name}");
}
IEnumerator MonitorDrawCalls()
{
while (true)
{
yield return new WaitForSeconds(1f);
#if UNITY_EDITOR
int drawCalls = UnityEditor.UnityStats.drawCalls;
int setPassCalls = UnityEditor.UnityStats.setPassCalls;
Debug.Log($"渲染统计 - DrawCalls: {drawCalls}, SetPassCalls: {setPassCalls}");
// 预警机制
if (drawCalls > GetDrawCallThreshold())
{
Debug.LogWarning($"DrawCall过高: {drawCalls}");
OnDrawCallExceeded(drawCalls);
}
#endif
}
}
int GetDrawCallThreshold()
{
// 根据平台设定阈值
#if UNITY_IOS
return 40; // iOS建议低于40
#elif UNITY_ANDROID
return 50; // Android可稍高
#else
return 100; // 其他平台
#endif
}
void OnDrawCallExceeded(int currentDrawCalls)
{
// 自动降低渲染质量
if (!QualitySettings.lodBias.Equals(0.5f))
{
QualitySettings.lodBias = 0.5f; // 降低LOD距离
Debug.Log("自动降低LOD偏置以减少DrawCall");
}
}
}
// GPU Instancing优化(大量相同物体)
public class GPUInstancingOptimizer : MonoBehaviour
{
[System.Serializable]
public class InstancingData
{
public Mesh Mesh;
public Material Material;
public int InstanceCount = 1000;
public Vector3 AreaSize = new Vector3(50, 0, 50);
[NonSerialized]
public Matrix4x4[] Matrices;
[NonSerialized]
public MaterialPropertyBlock PropertyBlock;
}
public List<InstancingData> InstancingGroups = new List<InstancingData>();
void Start()
{
InitializeInstancing();
}
void InitializeInstancing()
{
foreach (var group in InstancingGroups)
{
group.Matrices = new Matrix4x4[group.InstanceCount];
group.PropertyBlock = new MaterialPropertyBlock();
// 设置随机颜色
Vector4[] colors = new Vector4[group.InstanceCount];
for (int i = 0; i < group.InstanceCount; i++)
{
colors[i] = new Color(Random.value, Random.value, Random.value, 1f);
}
group.PropertyBlock.SetVectorArray("_Color", colors);
// 初始化位置
UpdateInstancingPositions(group);
}
}
void UpdateInstancingPositions(InstancingData group)
{
for (int i = 0; i < group.InstanceCount; i++)
{
Vector3 position = new Vector3(
Random.Range(-group.AreaSize.x, group.AreaSize.x),
Random.Range(-group.AreaSize.y, group.AreaSize.y),
Random.Range(-group.AreaSize.z, group.AreaSize.z)
);
Quaternion rotation = Quaternion.Euler(
Random.Range(0, 360),
Random.Range(0, 360),
Random.Range(0, 360)
);
Vector3 scale = Vector3.one * Random.Range(0.5f, 2f);
group.Matrices[i] = Matrix4x4.TRS(position, rotation, scale);
}
}
void Update()
{
RenderInstances();
}
void RenderInstances()
{
foreach (var group in InstancingGroups)
{
if (group.Mesh != null && group.Material != null)
{
// 使用GPU Instancing渲染大量实例
// 注意:移动端一次最多渲染1023个实例
int batchSize = 1023;
int instanceCount = group.InstanceCount;
for (int i = 0; i < instanceCount; i += batchSize)
{
int count = Mathf.Min(batchSize, instanceCount - i);
Graphics.DrawMeshInstanced(
group.Mesh,
0,
group.Material,
new ArraySegment<Matrix4x4>(group.Matrices, i, count).ToArray(),
count,
group.PropertyBlock
);
}
}
}
}
// 动态更新实例位置(优化版本)
public void UpdateInstancePosition(int groupIndex, int instanceIndex, Vector3 newPosition)
{
if (groupIndex >= 0 && groupIndex < InstancingGroups.Count)
{
var group = InstancingGroups[groupIndex];
if (instanceIndex >= 0 && instanceIndex < group.InstanceCount)
{
// 只更新变换矩阵的位置部分(避免重新分配)
var matrix = group.Matrices[instanceIndex];
matrix.m03 = newPosition.x;
matrix.m13 = newPosition.y;
matrix.m23 = newPosition.z;
group.Matrices[instanceIndex] = matrix;
}
}
}
}
2.2 移动端Shader优化
// 移动端优化Shader示例(使用Shader Graph或代码)
public class MobileShaderOptimizer
{
// 通用移动端Shader优化规则
public class OptimizationRules
{
public static string ApplyMobileOptimizations(string shaderCode)
{
// 1. 移除不必要的光照计算
shaderCode = shaderCode.Replace("#pragma multi_compile_fwdbase", "");
shaderCode = shaderCode.Replace("#pragma multi_compile_fwdadd", "");
// 2. 简化变体
shaderCode = shaderCode.Replace(
"multi_compile_fog",
"shader_feature _FOG_ON"
);
// 3. 减少纹理采样
// 检测并合并多个纹理采样
// 4. 使用半精度浮点数
shaderCode = shaderCode.Replace(
"float ",
"half "
).Replace(
"float2 ",
"half2 "
).Replace(
"float3 ",
"half3 "
).Replace(
"float4 ",
"half4 "
);
return shaderCode;
}
}
// 运行时Shader LOD系统
public class ShaderLODSystem : MonoBehaviour
{
[System.Serializable]
public class ShaderLODLevel
{
public string ShaderName;
public int LOD = 100;
public Shader ReplacementShader;
}
public List<ShaderLODLevel> LODLevels = new List<ShaderLODLevel>();
public float UpdateInterval = 2f;
private Dictionary<string, Shader> m_originalShaders = new Dictionary<string, Shader>();
private Dictionary<Renderer, Material[]> m_originalMaterials = new Dictionary<Renderer, Material[]>();
void Start()
{
StartCoroutine(MonitorAndAdjustShaderLOD());
}
IEnumerator MonitorAndAdjustShaderLOD()
{
while (true)
{
yield return new WaitForSeconds(UpdateInterval);
float fps = 1f / Time.unscaledDeltaTime;
float thermalState = GetThermalState();
// 根据性能指标决定Shader LOD
int targetLOD = CalculateTargetLOD(fps, thermalState);
ApplyShaderLOD(targetLOD);
}
}
int CalculateTargetLOD(float fps, float thermalState)
{
if (fps < 25 || thermalState > 0.7f)
return 100; // 最低质量
else if (fps < 45 || thermalState > 0.4f)
return 200; // 中等质量
else
return 300; // 高质量
}
void ApplyShaderLOD(int targetLOD)
{
foreach (var lodLevel in LODLevels)
{
if (targetLOD <= lodLevel.LOD && lodLevel.ReplacementShader != null)
{
ReplaceShader(lodLevel.ShaderName, lodLevel.ReplacementShader);
}
else
{
RestoreShader(lodLevel.ShaderName);
}
}
}
void ReplaceShader(string shaderName, Shader replacementShader)
{
Renderer[] allRenderers = FindObjectsOfType<Renderer>();
foreach (var renderer in allRenderers)
{
if (!m_originalMaterials.ContainsKey(renderer))
{
m_originalMaterials[renderer] = renderer.sharedMaterials;
}
Material[] newMaterials = new Material[renderer.sharedMaterials.Length];
for (int i = 0; i < renderer.sharedMaterials.Length; i++)
{
Material mat = renderer.sharedMaterials[i];
if (mat != null && mat.shader != null && mat.shader.name.Contains(shaderName))
{
if (!m_originalShaders.ContainsKey(mat.name))
{
m_originalShaders[mat.name] = mat.shader;
}
Material newMat = new Material(mat);
newMat.shader = replacementShader;
newMaterials[i] = newMat;
}
else
{
newMaterials[i] = mat;
}
}
renderer.sharedMaterials = newMaterials;
}
}
}
// 移动端友好的简化Shader(代码方式)
public class SimpleMobileShader
{
/*
Shader "Mobile/OptimizedDiffuse" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 150
CGPROGRAM
#pragma surface surf Lambert noforwardadd noshadow
#pragma skip_variants FOG_EXP FOG_EXP2
sampler2D _MainTex;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Mobile/VertexLit"
}
*/
}
}
第三部分:内存优化策略
3.1 纹理内存优化
// 纹理动态加载与卸载系统
public class TextureMemoryManager : MonoBehaviour
{
[System.Serializable]
public class TextureInfo
{
public string TextureName;
public Texture2D Texture;
public long MemorySize; // 字节
public float LastAccessTime;
public bool IsPermanent; // 是否常驻内存
public int ReferenceCount;
}
private Dictionary<string, TextureInfo> m_textureCache = new Dictionary<string, TextureInfo>();
private long m_maxMemoryBytes = 100 * 1024 * 1024; // 100MB
private long m_currentMemoryBytes = 0;
// 纹理加载策略
public enum LoadStrategy
{
Immediate, // 立即加载
Async, // 异步加载
Streaming, // 流式加载
OnDemand // 按需加载
}
public Texture2D LoadTexture(string path, LoadStrategy strategy = LoadStrategy.OnDemand)
{
// 检查缓存
if (m_textureCache.TryGetValue(path, out var info))
{
info.LastAccessTime = Time.time;
info.ReferenceCount++;
return info.Texture;
}
Texture2D texture = null;
switch (strategy)
{
case LoadStrategy.Immediate:
texture = Resources.Load<Texture2D>(path);
break;
case LoadStrategy.Async:
StartCoroutine(LoadTextureAsync(path, tex => texture = tex));
break;
case LoadStrategy.OnDemand:
// 检查内存是否足够
if (CanLoadTexture(path))
{
texture = Resources.Load<Texture2D>(path);
}
else
{
// 需要先卸载一些纹理
UnloadLeastUsedTextures(GetTextureSize(path));
texture = Resources.Load<Texture2D>(path);
}
break;
}
if (texture != null)
{
long size = CalculateTextureSize(texture);
AddToCache(path, texture, size);
}
return texture;
}
IEnumerator LoadTextureAsync(string path, Action<Texture2D> callback)
{
ResourceRequest request = Resources.LoadAsync<Texture2D>(path);
yield return request;
Texture2D texture = request.asset as Texture2D;
callback?.Invoke(texture);
}
long CalculateTextureSize(Texture2D texture)
{
// 计算纹理内存占用
// 公式: 宽度 × 高度 × 每个像素的字节数
int bytesPerPixel = GetBytesPerPixel(texture.format);
return texture.width * texture.height * bytesPerPixel;
}
int GetBytesPerPixel(TextureFormat format)
{
switch (format)
{
case TextureFormat.RGBA32: return 4;
case TextureFormat.RGB24: return 3;
case TextureFormat.RGBAFloat: return 16;
case TextureFormat.RGBAHalf: return 8;
case TextureFormat.ETC2_RGBA8: return 1; // 压缩格式
case TextureFormat.PVRTC_RGBA4: return 0.5f; // 4bpp
default: return 4;
}
}
void AddToCache(string path, Texture2D texture, long size)
{
var info = new TextureInfo
{
TextureName = path,
Texture = texture,
MemorySize = size,
LastAccessTime = Time.time,
ReferenceCount = 1
};
m_textureCache[path] = info;
m_currentMemoryBytes += size;
// 检查内存限制
if (m_currentMemoryBytes > m_maxMemoryBytes)
{
UnloadLeastUsedTextures(m_currentMemoryBytes - m_maxMemoryBytes);
}
}
void UnloadLeastUsedTextures(long bytesToFree)
{
// 按最后访问时间和引用计数排序
var texturesToUnload = m_textureCache.Values
.Where(t => !t.IsPermanent && t.ReferenceCount == 0)
.OrderBy(t => t.LastAccessTime)
.ToList();
long freedBytes = 0;
foreach (var textureInfo in texturesToUnload)
{
if (freedBytes >= bytesToFree) break;
Resources.UnloadAsset(textureInfo.Texture);
m_textureCache.Remove(textureInfo.TextureName);
m_currentMemoryBytes -= textureInfo.MemorySize;
freedBytes += textureInfo.MemorySize;
Debug.Log($"卸载纹理: {textureInfo.TextureName}, 释放: {textureInfo.MemorySize / 1024}KB");
}
}
// 纹理压缩优化
public void OptimizeTextureCompression()
{
#if UNITY_EDITOR
// 批量设置纹理压缩格式
string[] texturePaths = UnityEditor.AssetDatabase.FindAssets("t:Texture2D");
foreach (string guid in texturePaths)
{
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
UnityEditor.TextureImporter importer = UnityEditor.AssetImporter.GetAtPath(path) as UnityEditor.TextureImporter;
if (importer != null)
{
// 根据平台设置压缩格式
UnityEditor.TextureImporterPlatformSettings androidSettings = importer.GetPlatformTextureSettings("Android");
androidSettings.overridden = true;
androidSettings.format = UnityEditor.TextureImporterFormat.ETC2_RGBA8;
androidSettings.maxTextureSize = 2048; // 限制最大尺寸
importer.SetPlatformTextureSettings(androidSettings);
UnityEditor.TextureImporterPlatformSettings iosSettings = importer.GetPlatformTextureSettings("iPhone");
iosSettings.overridden = true;
iosSettings.format = UnityEditor.TextureImporterFormat.PVRTC_RGBA4;
iosSettings.maxTextureSize = 2048;
importer.SetPlatformTextureSettings(iosSettings);
// 启用Mipmap(根据需求)
importer.mipmapEnabled = !importer.textureType == UnityEditor.TextureImporterType.Sprite;
UnityEditor.AssetDatabase.ImportAsset(path);
}
}
#endif
}
// 纹理动态降级
public class TextureQualityScaler : MonoBehaviour
{
private Dictionary<Texture2D, Texture2D> m_lowResVersions = new Dictionary<Texture2D, Texture2D>();
public void ScaleTexturesBasedOnMemory(float scaleFactor)
{
// 根据可用内存调整纹理质量
long availableMemory = SystemInfo.systemMemorySize * 1024 * 1024;
long usedMemory = Profiler.GetTotalAllocatedMemoryLong();
float memoryUsageRatio = (float)usedMemory / availableMemory;
if (memoryUsageRatio > 0.7f) // 内存使用超过70%
{
// 降低所有纹理的分辨率
foreach (var kvp in m_textureCache)
{
ScaleTexture(kvp.Value.Texture, scaleFactor);
}
}
}
void ScaleTexture(Texture2D original, float scale)
{
if (m_lowResVersions.ContainsKey(original)) return;
// 创建低分辨率版本
int newWidth = Mathf.Max(32, (int)(original.width * scale));
int newHeight = Mathf.Max(32, (int)(original.height * scale));
Texture2D lowResTexture = new Texture2D(newWidth, newHeight, original.format, false);
// 使用双线性滤波缩放
Color[] pixels = original.GetPixels();
Color[] scaledPixels = ScalePixels(pixels, original.width, original.height, newWidth, newHeight);
lowResTexture.SetPixels(scaledPixels);
lowResTexture.Apply();
m_lowResVersions[original] = lowResTexture;
// 替换材质中的纹理
ReplaceTextureInMaterials(original, lowResTexture);
}
}
}
3.2 对象池与内存复用
// 智能对象池系统
public class SmartObjectPool : MonoBehaviour
{
[System.Serializable]
public class PoolConfig
{
public GameObject Prefab;
public int InitialSize = 10;
public int MaxSize = 100;
public bool CanGrow = true;
public float AutoCleanupTime = 60f; // 60秒后清理未使用的对象
}
private class PooledObject
{
public GameObject GameObject;
public float LastUseTime;
public bool IsInUse;
}
private Dictionary<string, Queue<PooledObject>> m_pools = new Dictionary<string, Queue<PooledObject>>();
private Dictionary<string, PoolConfig> m_poolConfigs = new Dictionary<string, PoolConfig>();
private Dictionary<GameObject, PooledObject> m_activeObjects = new Dictionary<GameObject, PooledObject>();
void Start()
{
// 启动自动清理
StartCoroutine(AutoCleanupRoutine());
}
public void RegisterPool(string poolName, PoolConfig config)
{
m_poolConfigs[poolName] = config;
m_pools[poolName] = new Queue<PooledObject>();
// 预热对象池
PrewarmPool(poolName, config.InitialSize);
}
void PrewarmPool(string poolName, int count)
{
var config = m_poolConfigs[poolName];
var pool = m_pools[poolName];
for (int i = 0; i < count; i++)
{
GameObject obj = Instantiate(config.Prefab);
obj.SetActive(false);
obj.transform.SetParent(transform);
pool.Enqueue(new PooledObject
{
GameObject = obj,
LastUseTime = Time.time,
IsInUse = false
});
}
}
public GameObject GetFromPool(string poolName, Vector3 position, Quaternion rotation)
{
if (!m_pools.ContainsKey(poolName))
{
Debug.LogError($"对象池未注册: {poolName}");
return null;
}
var pool = m_pools[poolName];
var config = m_poolConfigs[poolName];
PooledObject pooledObj = null;
// 尝试从池中获取
if (pool.Count > 0)
{
pooledObj = pool.Dequeue();
}
// 如果池为空且允许增长
else if (config.CanGrow && GetTotalPoolSize(poolName) < config.MaxSize)
{
GameObject newObj = Instantiate(config.Prefab);
newObj.transform.SetParent(transform);
pooledObj = new PooledObject
{
GameObject = newObj,
LastUseTime = Time.time,
IsInUse = false
};
}
if (pooledObj != null)
{
pooledObj.GameObject.SetActive(true);
pooledObj.GameObject.transform.position = position;
pooledObj.GameObject.transform.rotation = rotation;
pooledObj.LastUseTime = Time.time;
pooledObj.IsInUse = true;
m_activeObjects[pooledObj.GameObject] = pooledObj;
return pooledObj.GameObject;
}
return null;
}
public void ReturnToPool(GameObject obj)
{
if (!m_activeObjects.TryGetValue(obj, out var pooledObj))
{
Debug.LogWarning($"对象不属于任何对象池: {obj.name}");
Destroy(obj);
return;
}
// 重置对象状态
ResetPooledObject(pooledObj);
// 检查应该回到哪个池
string poolName = FindPoolNameForPrefab(obj);
if (!string.IsNullOrEmpty(poolName))
{
var pool = m_pools[poolName];
var config = m_poolConfigs[poolName];
// 检查池是否已满
if (pool.Count < config.MaxSize)
{
pool.Enqueue(pooledObj);
}
else
{
// 池已满,销毁对象
Destroy(obj);
}
}
m_activeObjects.Remove(obj);
}
void ResetPooledObject(PooledObject pooledObj)
{
GameObject obj = pooledObj.GameObject;
obj.SetActive(false);
obj.transform.SetParent(transform);
pooledObj.IsInUse = false;
pooledObj.LastUseTime = Time.time;
// 重置所有组件状态
var rigidbody = obj.GetComponent<Rigidbody>();
if (rigidbody != null)
{
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
}
// 重置粒子系统
var particleSystem = obj.GetComponent<ParticleSystem>();
if (particleSystem != null)
{
particleSystem.Clear();
}
// 重置动画状态
var animator = obj.GetComponent<Animator>();
if (animator != null)
{
animator.Rebind();
}
}
IEnumerator AutoCleanupRoutine()
{
while (true)
{
yield return new WaitForSeconds(30f); // 每30秒检查一次
CleanupUnusedObjects();
}
}
void CleanupUnusedObjects()
{
float currentTime = Time.time;
foreach (var kvp in m_pools)
{
string poolName = kvp.Key;
var pool = kvp.Value;
var config = m_poolConfigs[poolName];
if (config.AutoCleanupTime <= 0) continue;
// 检查并清理长时间未使用的对象
List<PooledObject> objectsToRemove = new List<PooledObject>();
foreach (var pooledObj in pool)
{
if (!pooledObj.IsInUse &&
(currentTime - pooledObj.LastUseTime) > config.AutoCleanupTime)
{
objectsToRemove.Add(pooledObj);
}
}
foreach (var obj in objectsToRemove)
{
pool = new Queue<PooledObject>(pool.Where(o => o != obj));
Destroy(obj.GameObject);
Debug.Log($"清理长时间未使用的对象: {poolName}");
}
m_pools[poolName] = pool;
}
}
// 内存压力响应
public void OnMemoryWarning()
{
Debug.Log("收到内存警告,清理对象池");
// 强制清理所有未使用的对象
foreach (var kvp in m_pools)
{
var pool = kvp.Value;
var config = m_poolConfigs[kvp.Key];
// 保留最小数量的对象
int objectsToKeep = Mathf.Min(5, config.InitialSize / 2);
while (pool.Count > objectsToKeep)
{
var obj = pool.Dequeue();
if (!obj.IsInUse)
{
Destroy(obj.GameObject);
}
else
{
// 如果正在使用,放回队列
pool.Enqueue(obj);
}
}
}
// 触发GC
System.GC.Collect();
Resources.UnloadUnusedAssets();
}
}
第四部分:CPU性能优化
4.1 脚本执行优化
// 分层更新系统
public class HierarchicalUpdateSystem : MonoBehaviour
{
[System.Serializable]
public class UpdateLayer
{
public string Name;
public float UpdateInterval = 0.1f; // 更新间隔(秒)
public List<MonoBehaviour> Components = new List<MonoBehaviour>();
[NonSerialized]
public float Timer;
[NonSerialized]
public int CurrentIndex;
}
public List<UpdateLayer> Layers = new List<UpdateLayer>();
private Dictionary<MonoBehaviour, UpdateLayer> m_componentLayers = new Dictionary<MonoBehaviour, UpdateLayer>();
void Update()
{
float deltaTime = Time.deltaTime;
foreach (var layer in Layers)
{
layer.Timer += deltaTime;
if (layer.Timer >= layer.UpdateInterval)
{
layer.Timer = 0f;
// 分帧更新:每帧只更新一部分组件
UpdateLayerComponents(layer);
}
}
}
void UpdateLayerComponents(UpdateLayer layer)
{
if (layer.Components.Count == 0) return;
// 每帧更新固定数量的组件
int componentsPerFrame = Mathf.CeilToInt(layer.Components.Count / (layer.UpdateInterval / Time.deltaTime));
componentsPerFrame = Mathf.Max(1, componentsPerFrame);
for (int i = 0; i < componentsPerFrame; i++)
{
int index = (layer.CurrentIndex + i) % layer.Components.Count;
var component = layer.Components[index];
if (component != null && component.isActiveAndEnabled)
{
UpdateComponent(component);
}
}
layer.CurrentIndex = (layer.CurrentIndex + componentsPerFrame) % layer.Components.Count;
}
void UpdateComponent(MonoBehaviour component)
{
// 使用接口或基类来调用更新方法
if (component is IUpdateable updateable)
{
updateable.OnUpdate();
}
// 或者使用反射(性能较差,仅作示例)
// var method = component.GetType().GetMethod("CustomUpdate");
// method?.Invoke(component, null);
}
public void RegisterComponent(MonoBehaviour component, float updateInterval)
{
// 查找或创建合适的层
UpdateLayer layer = Layers.Find(l => Mathf.Approximately(l.UpdateInterval, updateInterval));
if (layer == null)
{
layer = new UpdateLayer
{
Name = $"Layer_{updateInterval}s",
UpdateInterval = updateInterval
};
Layers.Add(layer);
}
layer.Components.Add(component);
m_componentLayers[component] = layer;
}
public void UnregisterComponent(MonoBehaviour component)
{
if (m_componentLayers.TryGetValue(component, out var layer))
{
layer.Components.Remove(component);
m_componentLayers.Remove(component);
}
}
// 动态调整更新频率
public void AdjustUpdateRateBasedOnPerformance()
{
float fps = 1f / Time.unscaledDeltaTime;
if (fps < 30)
{
// 性能不佳,降低更新频率
foreach (var layer in Layers)
{
layer.UpdateInterval *= 1.5f; // 增加50%间隔
}
}
else if (fps > 50)
{
// 性能良好,可提高更新频率
foreach (var layer in Layers)
{
layer.UpdateInterval = Mathf.Max(0.016f, layer.UpdateInterval * 0.8f); // 减少20%间隔
}
}
}
}
// 接口定义
public interface IUpdateable
{
void OnUpdate();
}
// 使用示例
public class OptimizedEnemyAI : MonoBehaviour, IUpdateable
{
private HierarchicalUpdateSystem m_updateSystem;
void Start()
{
m_updateSystem = FindObjectOfType<HierarchicalUpdateSystem>();
if (m_updateSystem != null)
{
// 注册为每0.5秒更新一次(而不是每帧)
m_updateSystem.RegisterComponent(this, 0.5f);
}
}
public void OnUpdate()
{
// AI逻辑,每0.5秒执行一次
UpdateAI();
}
void UpdateAI()
{
// 简化的AI逻辑
// ...
}
void OnDestroy()
{
if (m_updateSystem != null)
{
m_updateSystem.UnregisterComponent(this);
}
}
}
// 批量处理系统(减少每帧调用)
public class BatchProcessor : MonoBehaviour
{
private class BatchOperation
{
public Action Action;
public float Priority;
}
private List<BatchOperation> m_operations = new List<BatchOperation>();
private List<BatchOperation> m_pendingOperations = new List<BatchOperation>();
private object m_lock = new object();
public int MaxOperationsPerFrame = 10;
void Update()
{
// 处理批次操作
ProcessBatch();
}
public void AddOperation(Action action, float priority = 0)
{
lock (m_lock)
{
m_pendingOperations.Add(new BatchOperation
{
Action = action,
Priority = priority
});
}
}
void ProcessBatch()
{
// 将待处理操作添加到主列表
lock (m_lock)
{
if (m_pendingOperations.Count > 0)
{
m_operations.AddRange(m_pendingOperations);
m_pendingOperations.Clear();
}
}
// 按优先级排序
m_operations.Sort((a, b) => b.Priority.CompareTo(a.Priority));
// 每帧处理有限数量的操作
int processedCount = 0;
for (int i = m_operations.Count - 1; i >= 0 && processedCount < MaxOperationsPerFrame; i--)
{
try
{
m_operations[i].Action?.Invoke();
}
catch (Exception e)
{
Debug.LogError($"批处理操作失败: {e.Message}");
}
m_operations.RemoveAt(i);
processedCount++;
}
}
// 使用示例:批量更新UI
public class BatchUIUpdater : MonoBehaviour
{
private BatchProcessor m_batchProcessor;
private Dictionary<string, string> m_uiUpdates = new Dictionary<string, string>();
void Start()
{
m_batchProcessor = FindObjectOfType<BatchProcessor>();
}
public void QueueUIUpdate(string elementId, string newValue)
{
m_uiUpdates[elementId] = newValue;
// 添加到批处理器(低优先级)
m_batchProcessor.AddOperation(() =>
{
UpdateAllUIElements();
}, 0.5f);
}
void UpdateAllUIElements()
{
// 一次性更新所有UI元素
foreach (var kvp in m_uiUpdates)
{
// 更新UI逻辑
// ...
}
m_uiUpdates.Clear();
}
}
}
4.2 物理优化
// 移动端物理优化系统
public class MobilePhysicsOptimizer : MonoBehaviour
{
[System.Serializable]
public class PhysicsLayerConfig
{
public string LayerName;
public int LayerIndex;
public float UpdateFrequency = 0.1f; // 更新频率(秒)
public bool UseSimplifiedCollision = true;
public bool EnableOnlyWhenVisible = true;
}
public List<PhysicsLayerConfig> LayerConfigs = new List<PhysicsLayerConfig>();
private Dictionary<int, PhysicsLayer> m_physicsLayers = new Dictionary<int, PhysicsLayer>();
private class PhysicsLayer
{
public PhysicsLayerConfig Config;
public List<Rigidbody> Rigidbodies = new List<Rigidbody>();
public float Timer;
public bool IsVisible;
}
void Start()
{
InitializePhysicsLayers();
ApplyMobilePhysicsSettings();
}
void InitializePhysicsLayers()
{
// 初始化所有物理层
foreach (var config in LayerConfigs)
{
var layer = new PhysicsLayer
{
Config = config,
Timer = 0f
};
m_physicsLayers[config.LayerIndex] = layer;
}
// 收集所有刚体并分配到对应层
Rigidbody[] allRigidbodies = FindObjectsOfType<Rigidbody>();
foreach (var rb in allRigidbodies)
{
int layer = rb.gameObject.layer;
if (m_physicsLayers.TryGetValue(layer, out var physicsLayer))
{
physicsLayer.Rigidbodies.Add(rb);
// 应用层特定设置
ApplyLayerSettings(rb, physicsLayer.Config);
}
}
}
void ApplyLayerSettings(Rigidbody rb, PhysicsLayerConfig config)
{
if (config.UseSimplifiedCollision)
{
// 使用简化碰撞体
ReplaceWithSimplifiedCollider(rb.gameObject);
}
// 禁用不需要的物理特性
rb.sleepThreshold = 0.5f; // 提高睡眠阈值
rb.solverIterations = 4; // 减少求解器迭代次数
rb.solverVelocityIterations = 1;
}
void ReplaceWithSimplifiedCollider(GameObject obj)
{
Collider[] colliders = obj.GetComponents<Collider>();
foreach (var collider in colliders)
{
// 根据需求替换为简化碰撞体
if (collider is MeshCollider meshCollider)
{
// MeshCollider性能较差,替换为BoxCollider或CapsuleCollider
ReplaceMeshCollider(meshCollider);
}
}
}
void ReplaceMeshCollider(MeshCollider meshCollider)
{
GameObject obj = meshCollider.gameObject;
Bounds bounds = meshCollider.bounds;
// 根据形状选择最合适的简化碰撞体
Vector3 size = bounds.size;
Destroy(meshCollider);
// 根据长宽高比例选择碰撞体类型
float maxDimension = Mathf.Max(size.x, size.y, size.z);
float minDimension = Mathf.Min(size.x, size.y, size.z);
float ratio = maxDimension / (minDimension + 0.001f);
if (ratio > 3f)
{
// 长条形,使用胶囊碰撞体
CapsuleCollider capsule = obj.AddComponent<CapsuleCollider>();
capsule.center = bounds.center - obj.transform.position;
capsule.height = size.y;
capsule.radius = Mathf.Min(size.x, size.z) * 0.5f;
}
else
{
// 使用盒形碰撞体
BoxCollider box = obj.AddComponent<BoxCollider>();
box.center = bounds.center - obj.transform.position;
box.size = size;
}
}
void Update()
{
float deltaTime = Time.deltaTime;
// 更新各物理层
foreach (var kvp in m_physicsLayers)
{
var layer = kvp.Value;
layer.Timer += deltaTime;
// 检查可见性(如果启用)
if (layer.Config.EnableOnlyWhenVisible)
{
layer.IsVisible = CheckLayerVisibility(layer);
}
else
{
layer.IsVisible = true;
}
// 按频率更新物理
if (layer.IsVisible && layer.Timer >= layer.Config.UpdateFrequency)
{
layer.Timer = 0f;
UpdatePhysicsLayer(layer);
}
}
}
bool CheckLayerVisibility(PhysicsLayer layer)
{
// 检查该层是否有任何刚体在相机视野内
Camera mainCamera = Camera.main;
if (mainCamera == null) return true;
foreach (var rb in layer.Rigidbodies)
{
if (rb == null) continue;
Vector3 viewportPos = mainCamera.WorldToViewportPoint(rb.position);
if (viewportPos.x >= 0 && viewportPos.x <= 1 &&
viewportPos.y >= 0 && viewportPos.y <= 1 &&
viewportPos.z > 0)
{
return true;
}
}
return false;
}
void UpdatePhysicsLayer(PhysicsLayer layer)
{
// 更新该层的所有刚体
foreach (var rb in layer.Rigidbodies)
{
if (rb == null || !rb.gameObject.activeInHierarchy) continue;
// 手动更新物理(简化的模拟)
if (!rb.isKinematic)
{
// 应用重力
rb.velocity += Physics.gravity * layer.Config.UpdateFrequency;
// 应用阻力
rb.velocity *= Mathf.Clamp01(1f - layer.Config.UpdateFrequency * rb.drag);
// 更新位置
rb.position += rb.velocity * layer.Config.UpdateFrequency;
}
}
}
void ApplyMobilePhysicsSettings()
{
// 应用移动端优化的物理设置
Physics.defaultSolverIterations = 4;
Physics.defaultSolverVelocityIterations = 1;
Physics.sleepThreshold = 0.5f;
Physics.defaultContactOffset = 0.01f;
Physics.queriesHitBackfaces = false;
Physics.queriesHitTriggers = false; // 如果不需要触发检测
// 根据设备性能调整
if (SystemInfo.processorFrequency < 1500) // 低频CPU
{
Time.fixedDeltaTime = 0.05f; // 20 FPS物理更新
Physics.defaultSolverIterations = 2;
}
else
{
Time.fixedDeltaTime = 0.033f; // 30 FPS物理更新
}
}
// 动态调整物理质量
public class DynamicPhysicsQuality : MonoBehaviour
{
private float m_lastCheckTime;
private float m_checkInterval = 2f;
void Update()
{
if (Time.time - m_lastCheckTime > m_checkInterval)
{
m_lastCheckTime = Time.time;
AdjustPhysicsQuality();
}
}
void AdjustPhysicsQuality()
{
float frameTime = Time.unscaledDeltaTime * 1000f; // 毫秒
if (frameTime > 33.3f) // 低于30FPS
{
// 降低物理质量
Physics.defaultSolverIterations = Mathf.Max(2, Physics.defaultSolverIterations - 1);
Time.fixedDeltaTime = Mathf.Min(0.1f, Time.fixedDeltaTime * 1.5f);
Debug.Log($"降低物理质量: SolverIterations={Physics.defaultSolverIterations}, FixedDelta={Time.fixedDeltaTime}");
}
else if (frameTime < 16.7f) // 高于60FPS
{
// 可提高物理质量(如果有余量)
Physics.defaultSolverIterations = Mathf.Min(8, Physics.defaultSolverIterations + 1);
Time.fixedDeltaTime = Mathf.Max(0.016f, Time.fixedDeltaTime * 0.8f);
}
}
}
}
第五部分:功耗与发热控制
// 移动端功耗管理系统
public class PowerManagementSystem : MonoBehaviour
{
[System.Serializable]
public class PowerProfile
{
public string ProfileName;
public int TargetFrameRate = 30;
public float ScreenBrightness = 0.7f;
public bool EnableVSync = false;
public int ShadowQuality = 0;
public int TextureQuality = 1;
public bool ReduceParticles = true;
public bool SimplifyShaders = false;
}
public PowerProfile[] PowerProfiles = new PowerProfile[]
{
new PowerProfile { ProfileName = "HighPerformance", TargetFrameRate = 60 },
new PowerProfile { ProfileName = "Balanced", TargetFrameRate = 45 },
new PowerProfile { ProfileName = "PowerSaving", TargetFrameRate = 30 },
new PowerProfile { ProfileName = "UltraSaving", TargetFrameRate = 20 }
};
private PowerProfile m_currentProfile;
private float m_lastProfileChangeTime;
private const float PROFILE_CHANGE_COOLDOWN = 10f; // 10秒冷却
// 监控指标
private float m_batteryDrainRate; // 电池消耗率(%/分钟)
private float m_lastBatteryLevel;
private float m_lastCheckTime;
private float m_deviceTemperature; // 设备温度(模拟值)
void Start()
{
// 初始化为平衡模式
ApplyPowerProfile("Balanced");
// 初始化监控
m_lastBatteryLevel = GetBatteryLevel();
m_lastCheckTime = Time.time;
// 启动监控协程
StartCoroutine(PowerMonitoringRoutine());
}
IEnumerator PowerMonitoringRoutine()
{
while (true)
{
yield return new WaitForSeconds(60f); // 每分钟检查一次
UpdatePowerMetrics();
AdjustProfileBasedOnConditions();
}
}
void UpdatePowerMetrics()
{
float currentBatteryLevel = GetBatteryLevel();
float currentTime = Time.time;
float timeDelta = currentTime - m_lastCheckTime;
if (timeDelta > 0)
{
m_batteryDrainRate = (m_lastBatteryLevel - currentBatteryLevel) / (timeDelta / 60f);
}
m_lastBatteryLevel = currentBatteryLevel;
m_lastCheckTime = currentTime;
// 模拟设备温度(实际项目需要平台特定API)
m_deviceTemperature = EstimateDeviceTemperature();
Debug.Log($"功耗监控 - 电池消耗: {m_batteryDrainRate:F2}%/分钟, 温度: {m_deviceTemperature:F1}°C");
}
void AdjustProfileBasedOnConditions()
{
// 检查电池电量
float batteryLevel = GetBatteryLevel();
// 检查是否在充电
bool isCharging = IsCharging();
// 检查设备温度
bool isOverheating = m_deviceTemperature > 40f;
// 决定使用哪个功耗配置文件
string targetProfile = "Balanced";
if (isOverheating)
{
targetProfile = "UltraSaving";
Debug.LogWarning("设备过热,切换至超省电模式");
}
else if (!isCharging && batteryLevel < 20)
{
targetProfile = "PowerSaving";
Debug.Log("电量低于20%,切换至省电模式");
}
else if (!isCharging && batteryLevel < 50)
{
targetProfile = "Balanced";
}
else if (isCharging || batteryLevel > 80)
{
targetProfile = "HighPerformance";
}
// 应用配置文件(如果有冷却时间)
if (Time.time - m_lastProfileChangeTime > PROFILE_CHANGE_COOLDOWN)
{
ApplyPowerProfile(targetProfile);
m_lastProfileChangeTime = Time.time;
}
}
void ApplyPowerProfile(string profileName)
{
var profile = PowerProfiles.FirstOrDefault(p => p.ProfileName == profileName);
if (profile == null) return;
m_currentProfile = profile;
// 应用设置
Application.targetFrameRate = profile.TargetFrameRate;
QualitySettings.vSyncCount = profile.EnableVSync ? 1 : 0;
QualitySettings.shadowDistance = profile.ShadowQuality * 20f;
QualitySettings.masterTextureLimit = profile.TextureQuality;
if (profile.ReduceParticles)
{
ReduceParticleEffects();
}
if (profile.SimplifyShaders)
{
SimplifyShaderQuality();
}
Debug.Log($"应用功耗配置文件: {profileName}, 目标帧率: {profile.TargetFrameRate}");
}
void ReduceParticleEffects()
{
ParticleSystem[] allParticles = FindObjectsOfType<ParticleSystem>();
foreach (var ps in allParticles)
{
var main = ps.main;
main.maxParticles = Mathf.Min(main.maxParticles, 100);
var emission = ps.emission;
emission.rateOverTimeMultiplier *= 0.5f;
}
}
void SimplifyShaderQuality()
{
// 切换到简化Shader
// 实现参考前面的ShaderLODSystem
}
// 平台特定API封装
float GetBatteryLevel()
{
#if UNITY_ANDROID && !UNITY_EDITOR
try
{
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject intentFilter = new AndroidJavaObject("android.content.IntentFilter", "android.intent.action.BATTERY_CHANGED"))
using (AndroidJavaObject batteryIntent = currentActivity.Call<AndroidJavaObject>("registerReceiver", null, intentFilter))
{
int level = batteryIntent.Call<int>("getIntExtra", "level", -1);
int scale = batteryIntent.Call<int>("getIntExtra", "scale", -1);
if (level >= 0 && scale > 0)
{
return (float)level / (float)scale * 100f;
}
}
}
catch (Exception e)
{
Debug.LogWarning($"获取电池电量失败: {e.Message}");
}
#elif UNITY_IOS && !UNITY_EDITOR
// iOS使用UIDevice API
#endif
return 100f; // 默认值
}
bool IsCharging()
{
#if UNITY_ANDROID && !UNITY_EDITOR
try
{
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject intentFilter = new AndroidJavaObject("android.content.IntentFilter", "android.intent.action.BATTERY_CHANGED"))
using (AndroidJavaObject batteryIntent = currentActivity.Call<AndroidJavaObject>("registerReceiver", null, intentFilter))
{
int status = batteryIntent.Call<int>("getIntExtra", "status", -1);
return status == 2 || status == 5; // 2=充电中, 5=满电
}
}
catch (Exception e)
{
Debug.LogWarning($"检查充电状态失败: {e.Message}");
}
#endif
return false;
}
float EstimateDeviceTemperature()
{
// 实际项目需要平台特定API
// 这里使用性能指标估算
float frameTime = Time.unscaledDeltaTime * 1000f;
float cpuUsage = (frameTime > 16.7f) ? (frameTime / 33.3f) : 0.5f;
// 模拟温度:基础温度 + CPU使用率影响
return 30f + cpuUsage * 15f;
}
// 后台运行优化
void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
// 应用进入后台
OnEnterBackground();
}
else
{
// 应用回到前台
OnEnterForeground();
}
}
void OnEnterBackground()
{
Debug.Log("应用进入后台,降低资源消耗");
// 大幅降低帧率
Application.targetFrameRate = 10;
// 暂停非必要的协程和更新
PauseNonCriticalSystems();
// 减少内存占用
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
void OnEnterForeground()
{
Debug.Log("应用回到前台,恢复性能");
// 恢复帧率
ApplyPowerProfile(m_currentProfile.ProfileName);
// 恢复系统
ResumeCriticalSystems();
}
// 场景加载优化
public class OptimizedSceneLoader : MonoBehaviour
{
public float MaxLoadingFrameTime = 16.7f; // 确保加载时不低于60FPS
public IEnumerator LoadSceneAsyncWithLimit(string sceneName)
{
AsyncOperation asyncLoad = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName);
asyncLoad.allowSceneActivation = false;
while (asyncLoad.progress < 0.9f)
{
// 每帧限制加载时间
float startTime = Time.realtimeSinceStartup;
// 执行一部分加载
// ...
float elapsed = (Time.realtimeSinceStartup - startTime) * 1000f;
if (elapsed < MaxLoadingFrameTime)
{
// 如果还有时间,让出控制权
yield return null;
}
else
{
// 已经用完了帧时间,等待下一帧
yield return new WaitForEndOfFrame();
}
}
// 加载完成,激活场景
asyncLoad.allowSceneActivation = true;
}
}
}
第六部分:网络与IO优化
// 移动端网络优化
public class MobileNetworkOptimizer : MonoBehaviour
{
public enum NetworkType
{
Unknown,
WiFi,
Cellular4G,
Cellular3G,
Cellular2G
}
private NetworkType m_currentNetworkType = NetworkType.Unknown;
private float m_networkSpeed; // KB/s
private float m_lastNetworkCheckTime;
void Start()
{
StartCoroutine(NetworkMonitoringRoutine());
}
IEnumerator NetworkMonitoringRoutine()
{
while (true)
{
yield return new WaitForSeconds(5f); // 每5秒检查一次
DetectNetworkType();
AdjustNetworkSettings();
}
}
void DetectNetworkType()
{
#if UNITY_ANDROID && !UNITY_EDITOR
try
{
using (AndroidJavaClass connectivityManagerClass = new AndroidJavaClass("android.net.ConnectivityManager"))
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject connectivityManager = currentActivity.Call<AndroidJavaObject>("getSystemService", "connectivity"))
{
AndroidJavaObject networkInfo = connectivityManager.Call<AndroidJavaObject>("getActiveNetworkInfo");
if (networkInfo != null && networkInfo.Call<bool>("isConnected"))
{
int type = networkInfo.Call<int>("getType");
if (type == 1) // TYPE_WIFI
{
m_currentNetworkType = NetworkType.WiFi;
}
else if (type == 0) // TYPE_MOBILE
{
int subtype = networkInfo.Call<int>("getSubtype");
switch (subtype)
{
case 13: // LTE
case 19: // LTE_CA
m_currentNetworkType = NetworkType.Cellular4G;
break;
case 3: // UMTS
case 8: // HSDPA
case 9: // HSUPA
case 10: // HSPA
case 15: // HSPAP
m_currentNetworkType = NetworkType.Cellular3G;
break;
default:
m_currentNetworkType = NetworkType.Cellular2G;
break;
}
}
}
}
}
catch (Exception e)
{
Debug.LogWarning($"检测网络类型失败: {e.Message}");
}
#endif
}
void AdjustNetworkSettings()
{
switch (m_currentNetworkType)
{
case NetworkType.WiFi:
// 高质量设置
SetNetworkQuality(1.0f);
break;
case NetworkType.Cellular4G:
// 中等质量
SetNetworkQuality(0.7f);
break;
case NetworkType.Cellular3G:
// 低质量
SetNetworkQuality(0.4f);
break;
case NetworkType.Cellular2G:
// 最低质量
SetNetworkQuality(0.2f);
break;
default:
SetNetworkQuality(0.5f);
break;
}
Debug.Log($"网络类型: {m_currentNetworkType}, 应用相应优化");
}
void SetNetworkQuality(float quality)
{
// 调整网络相关设置
// 例如:纹理下载质量、同步频率等
// 调整AssetBundle下载质量
AdjustAssetBundleQuality(quality);
// 调整网络同步频率
AdjustSyncFrequency(quality);
}
void AdjustAssetBundleQuality(float quality)
{
// 根据网络质量决定下载的AssetBundle质量
if (quality < 0.3f)
{
// 使用低质量资源
PlayerPrefs.SetString("AssetBundleQuality", "Low");
}
else if (quality < 0.6f)
{
// 使用中等质量资源
PlayerPrefs.SetString("AssetBundleQuality", "Medium");
}
else
{
// 使用高质量资源
PlayerPrefs.SetString("AssetBundleQuality", "High");
}
}
// 智能资源下载
public class SmartAssetDownloader : MonoBehaviour
{
private class DownloadTask
{
public string AssetUrl;
public Action<byte[]> OnComplete;
public int Priority; // 0=最高,越大优先级越低
public float Timeout = 30f;
}
private Queue<DownloadTask> m_downloadQueue = new Queue<DownloadTask>();
private bool m_isDownloading = false;
private float m_maxDownloadSizePerFrame = 50 * 1024; // 50KB每帧
public void DownloadAsset(string url, Action<byte[]> callback, int priority = 10)
{
var task = new DownloadTask
{
AssetUrl = url,
OnComplete = callback,
Priority = priority
};
m_downloadQueue.Enqueue(task);
// 按优先级排序
m_downloadQueue = new Queue<DownloadTask>(
m_downloadQueue.OrderBy(t => t.Priority)
);
if (!m_isDownloading)
{
StartCoroutine(DownloadQueueProcessor());
}
}
IEnumerator DownloadQueueProcessor()
{
m_isDownloading = true;
while (m_downloadQueue.Count > 0)
{
var task = m_downloadQueue.Dequeue();
yield return StartCoroutine(DownloadWithLimit(task));
// 每下载一个任务后等待一帧,避免卡顿
yield return null;
}
m_isDownloading = false;
}
IEnumerator DownloadWithLimit(DownloadTask task)
{
using (UnityEngine.Networking.UnityWebRequest request =
UnityEngine.Networking.UnityWebRequest.Get(task.AssetUrl))
{
request.timeout = (int)task.Timeout;
request.SendWebRequest();
float startTime = Time.time;
long downloadedSize = 0;
while (!request.isDone)
{
// 限制每帧下载量
if (downloadedSize < request.downloadedBytes)
{
long newData = request.downloadedBytes - downloadedSize;
if (newData > m_maxDownloadSizePerFrame)
{
// 暂停下载直到下一帧
yield return null;
}
downloadedSize = request.downloadedBytes;
}
// 检查超时
if (Time.time - startTime > task.Timeout)
{
request.Abort();
Debug.LogWarning($"下载超时: {task.AssetUrl}");
task.OnComplete?.Invoke(null);
yield break;
}
yield return null;
}
if (request.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
{
task.OnComplete?.Invoke(request.downloadHandler.data);
}
else
{
Debug.LogError($"下载失败: {request.error}");
task.OnComplete?.Invoke(null);
}
}
}
}
// 本地缓存优化
public class LocalCacheOptimizer
{
private string m_cachePath;
private Dictionary<string, DateTime> m_cacheIndex = new Dictionary<string, DateTime>();
private long m_maxCacheSize = 100 * 1024 * 1024; // 100MB
public LocalCacheOptimizer()
{
m_cachePath = Application.persistentDataPath + "/Cache/";
if (!Directory.Exists(m_cachePath))
{
Directory.CreateDirectory(m_cachePath);
}
LoadCacheIndex();
}
public void SaveToCache(string key, byte[] data)
{
string filePath = m_cachePath + key;
File.WriteAllBytes(filePath, data);
m_cacheIndex[key] = DateTime.Now;
SaveCacheIndex();
// 检查缓存大小
CheckAndCleanCache();
}
public byte[] LoadFromCache(string key, float maxAgeHours = 24)
{
if (!m_cacheIndex.TryGetValue(key, out var lastAccess))
return null;
// 检查缓存是否过期
if ((DateTime.Now - lastAccess).TotalHours > maxAgeHours)
{
RemoveFromCache(key);
return null;
}
string filePath = m_cachePath + key;
if (File.Exists(filePath))
{
// 更新访问时间
m_cacheIndex[key] = DateTime.Now;
SaveCacheIndex();
return File.ReadAllBytes(filePath);
}
return null;
}
void CheckAndCleanCache()
{
long currentSize = CalculateCacheSize();
if (currentSize > m_maxCacheSize)
{
// 清理最旧的文件
var filesToDelete = m_cacheIndex
.OrderBy(kvp => kvp.Value)
.Take(10) // 每次清理10个最旧的文件
.ToList();
foreach (var kvp in filesToDelete)
{
RemoveFromCache(kvp.Key);
}
Debug.Log($"缓存清理完成,删除了{filesToDelete.Count}个文件");
}
}
long CalculateCacheSize()
{
long totalSize = 0;
foreach (var kvp in m_cacheIndex)
{
string filePath = m_cachePath + kvp.Key;
if (File.Exists(filePath))
{
totalSize += new FileInfo(filePath).Length;
}
}
return totalSize;
}
void LoadCacheIndex()
{
string indexFile = m_cachePath + "index.dat";
if (File.Exists(indexFile))
{
string[] lines = File.ReadAllLines(indexFile);
foreach (string line in lines)
{
string[] parts = line.Split('|');
if (parts.Length == 2)
{
if (DateTime.TryParse(parts[1], out DateTime date))
{
m_cacheIndex[parts[0]] = date;
}
}
}
}
}
void SaveCacheIndex()
{
string indexFile = m_cachePath + "index.dat";
using (StreamWriter writer = new StreamWriter(indexFile))
{
foreach (var kvp in m_cacheIndex)
{
writer.WriteLine($"{kvp.Key}|{kvp.Value:yyyy-MM-dd HH:mm:ss}");
}
}
}
}
}
总结:移动端性能优化最佳实践
7.1 性能优化检查清单
// 移动端性能检查系统
public class MobilePerformanceChecklist : MonoBehaviour
{
[System.Serializable]
public class ChecklistItem
{
public string Category;
public string Description;
public bool IsChecked;
public string HowToFix;
public Severity Severity;
}
public enum Severity
{
Critical, // 必须修复
High, // 应该修复
Medium, // 建议修复
Low // 可选修复
}
public List<ChecklistItem> Items = new List<ChecklistItem>();
void Start()
{
LoadDefaultChecklist();
StartCoroutine(RunPerformanceAudit());
}
void LoadDefaultChecklist()
{
Items = new List<ChecklistItem>
{
// 渲染优化
new ChecklistItem
{
Category = "渲染",
Description = "DrawCall是否超过平台限制?",
Severity = Severity.Critical,
HowToFix = "使用合批、GPU Instancing、减少材质数量"
},
new ChecklistItem
{
Category = "渲染",
Description = "是否有过多的透明物体?",
Severity = Severity.High,
HowToFix = "合并透明物体,使用Alpha Test代替Alpha Blend"
},
// 内存优化
new ChecklistItem
{
Category = "内存",
Description = "纹理是否使用压缩格式?",
Severity = Severity.Critical,
HowToFix = "Android使用ETC2,iOS使用PVRTC,启用纹理压缩"
},
new ChecklistItem
{
Category = "内存",
Description = "是否有内存泄漏?",
Severity = Severity.Critical,
HowToFix = "使用Profiler检查内存分配,实现对象池"
},
// CPU优化
new ChecklistItem
{
Category = "CPU",
Description = "是否每帧都有不必要的GC分配?",
Severity = Severity.High,
HowToFix = "避免在Update中分配内存,使用对象池和缓存"
},
new ChecklistItem
{
Category = "CPU",
Description = "物理计算是否过多?",
Severity = Severity.Medium,
HowToFix = "减少刚体数量,使用简化的碰撞体,分层更新物理"
},
// 功耗优化
new ChecklistItem
{
Category = "功耗",
Description = "帧率是否稳定?",
Severity = Severity.High,
HowToFix = "确保帧率稳定在30或60FPS,避免波动"
},
new ChecklistItem
{
Category = "功耗",
Description = "是否有过热问题?",
Severity = Severity.Medium,
HowToFix = "降低渲染质量,减少粒子效果,实现热限制自适应"
}
};
}
IEnumerator RunPerformanceAudit()
{
yield return new WaitForSeconds(5f); // 等待游戏稳定
Debug.Log("=== 移动端性能审计开始 ===");
foreach (var item in Items)
{
item.IsChecked = CheckItem(item);
if (!item.IsChecked && item.Severity <= Severity.High)
{
Debug.LogWarning($"[{item.Category}] {item.Description}");
Debug.Log($"建议修复: {item.HowToFix}");
}
yield return null; // 分帧检查避免卡顿
}
Debug.Log("=== 移动端性能审计完成 ===");
GeneratePerformanceReport();
}
bool CheckItem(ChecklistItem item)
{
switch (item.Description)
{
case "DrawCall是否超过平台限制?":
#if UNITY_EDITOR
int drawCalls = UnityEditor.UnityStats.drawCalls;
return drawCalls <= GetDrawCallLimit();
#else
return true; // 发布版本中需要其他检测方式
#endif
case "是否有内存泄漏?":
return CheckMemoryLeak();
case "是否每帧都有不必要的GC分配?":
return CheckGCAllocation();
default:
return true;
}
}
bool CheckMemoryLeak()
{
// 监控内存增长趋势
// 实际实现需要更复杂的监控
return true;
}
bool CheckGCAllocation()
{
// 检查GC分配频率
// 实际实现需要Profiler数据
return true;
}
void GeneratePerformanceReport()
{
StringBuilder report = new StringBuilder();
report.AppendLine("=== 移动端性能报告 ===");
report.AppendLine($"生成时间: {DateTime.Now}");
report.AppendLine($"设备: {SystemInfo.deviceModel}");
report.AppendLine($"平台: {Application.platform}");
report.AppendLine();
int criticalIssues = Items.Count(i => !i.IsChecked && i.Severity == Severity.Critical);
int highIssues = Items.Count(i => !i.IsChecked && i.Severity == Severity.High);
report.AppendLine($"关键问题: {criticalIssues}");
report.AppendLine($"重要问题: {highIssues}");
report.AppendLine();
if (criticalIssues > 0)
{
report.AppendLine("必须修复的问题:");
foreach (var item in Items.Where(i => !i.IsChecked && i.Severity == Severity.Critical))
{
report.AppendLine($" • {item.Description}");
}
}
Debug.Log(report.ToString());
}
}
7.2 平台特定优化指南
iOS优化要点:
- 使用Metal图形API(不要使用OpenGL ES)
- 纹理格式使用PVRTC(压缩比高)
- 注意iOS的内存警告(收到警告必须立即清理)
- 使用Asset Catalog管理资源
- 注意64位架构支持
Android优化要点:
- 支持多种分辨率和DPI
- 使用ETC2纹理压缩(支持Alpha通道)
- 注意不同厂商的GPU差异(Mali、Adreno、PowerVR)
- 处理Android内存管理(LMK机制)
- 支持多种ABI架构(armeabi-v7a, arm64-v8a)
7.3 持续优化策略
- 性能基准测试:建立性能基准,每次提交代码前进行测试
- 自动化测试:编写性能回归测试,集成到CI/CD流程
- 用户数据分析:收集真实用户的性能数据,发现实际瓶颈
- 渐进式优化:先解决最影响性能的问题,逐步优化次要问题
- 团队培训:确保所有开发人员了解移动端性能最佳实践
7.4 关键性能指标(KPI)
- 帧率稳定性:99%的帧在目标帧时间±30%内
- 内存峰值:低于平台限制的80%
- 启动时间:冷启动<3秒,热启动<1秒
- 电池消耗:每小时<15%(正常游戏)
- 安装包大小:iOS<200MB,Android<150MB(APK)
结论
移动端性能优化是一个系统工程,需要从架构设计、资源管理、代码实现到测试验证的全流程关注。成功的优化不是一次性工作,而是持续的过程。通过建立完善的性能监控体系、实施针对性的优化策略、并培养团队的优化意识,才能在移动平台上提供流畅、稳定的游戏体验。
记住:最好的优化是用户感受不到的优化——游戏既流畅又省电,安装包小却画质精美,这才是移动端性能优化的终极目标。
更多推荐
所有评论(0)