GetPixel引发清洁游戏卡顿,求优化方案及剩余污渍检测方法
清洁类游戏卡顿优化方案(针对GetPixel及污渍检测)
核心问题分析
卡顿根源在于两个高频性能开销操作:
- 双层循环内反复调用
GetPixel读取纹理像素,每次调用都会触发CPU-GPU数据同步,属于典型的CPU密集型冗余操作 - 每次
SetPixel后立即执行ApplyTexture,频繁向GPU提交纹理更新请求,造成大量性能浪费
具体优化方案
1. 预缓存纹理像素数组,彻底替代GetPixel
将Brush和污渍蒙版的像素数据一次性加载到CPU数组中,循环时直接访问数组元素,避免反复调用GetPixel:
// 类字段声明:添加缓存数组与相机引用 private Color[] _brushPixels; private Color[] _dirtMaskPixels; private Camera _mainCamera; private float _totalDirtAmount; private float _currentDirtAmount; // 在Awake/Start中初始化缓存 private void Awake() { _mainCamera = Camera.main; // 缓存相机,避免每次Raycast重复查找 _brushPixels = _brush.GetPixels(); // 一次性读取Brush所有像素 _dirtMaskPixels = _templateDirtMask.GetPixels(); // 一次性读取污渍蒙版所有像素 // 预计算初始总污渍量(用于后续剩余污渍检测) _totalDirtAmount = 0f; foreach (Color pixel in _dirtMaskPixels) { _totalDirtAmount += pixel.g; } _currentDirtAmount = _totalDirtAmount; }
修改SetPixel方法中的像素读取逻辑:
private void SetPixel(float textureCoordX, float textureCoordY) { int pixelX = (int)(textureCoordX * _templateDirtMask.width); int pixelY = (int)(textureCoordY * _templateDirtMask.height); int pixelXOffset = pixelX - (_brush.width / 2); int pixelYOffset = pixelY - (_brush.height / 2); for (int x = 0; x < _brush.width; x++) { for (int y = 0; y < _brush.height; y++) { // 计算数组索引,替代GetPixel的坐标读取 int brushIndex = x + y * _brush.width; int maskIndex = (pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width; int dataIndex = maskIndex; if (dataIndex > 0 && dataIndex < pixels.Length && brushIndex < _brushPixels.Length && maskIndex < _dirtMaskPixels.Length) { Color brushPixel = _brushPixels[brushIndex]; Color maskPixel = _dirtMaskPixels[maskIndex]; float removedAmount = maskPixel.g - (maskPixel.g * brushPixel.g); _currentDirtAmount -= removedAmount; // 更新目标像素与缓存的蒙版像素 pixels[dataIndex] = new Color(0, maskPixel.g * brushPixel.g, 0); _dirtMaskPixels[maskIndex] = pixels[dataIndex]; } } } }
2. 合并ApplyTexture调用,减少GPU同步
移除SetPixel方法末尾的ApplyTexture(),改为在触摸操作结束后统一提交纹理更新,避免频繁触发GPU同步:
private void Update() { if (Input.touchCount > 0) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { if (touch.position != prevPosition) { SetSinglePixel(touch.position); prevPosition = touch.position; } } else if (touch.phase == TouchPhase.Moved) { if (touch.position != prevPosition) { currentPosition = touch.position; Vector2 dif = currentPosition - prevPosition; float distance = dif.magnitude; if (distance > 36) { float count = distance / 36; float ddmo = 36; for (int i = 0; i <= count; i++) { Vector2 gapPoint = Vector2.MoveTowards(prevPosition, currentPosition, ddmo * i); if (Physics.Raycast(_mainCamera.ScreenPointToRay(gapPoint), out RaycastHit hit)) { gapPositions.Add(hit.textureCoord); } } SetMultiplePixel(gapPositions); gapPositions.Clear(); } else { SetSinglePixel(currentPosition); } prevPosition = touch.position; } } // 触摸结束时统一提交纹理更新 else if (touch.phase == TouchPhase.Ended) { ApplyTexture(); } } }
如果需要实时预览清洁效果,可以改为每2-3帧调用一次ApplyTexture,平衡性能与视觉体验。
3. 剩余污渍检测优化
直接通过内存中的_currentDirtAmount判断剩余污渍,完全脱离纹理读取操作:
// 检测是否清洁完成(可根据需求调整误差阈值) bool isCleanComplete = _currentDirtAmount <= 0.1f; // 获取剩余污渍比例 float remainingDirtRatio = _currentDirtAmount / _totalDirtAmount;
4. 细节优化
- 移除
SetPixel方法中无用的isCheckRemainingTexture参数 - 简化触摸位置判断逻辑,将
touch.position != currentPosition || touch.position != prevPosition改为touch.position != prevPosition,避免无效判断 - 缓存
_templateDirtMask.width、_templateDirtMask.height到类字段,减少循环内属性读取开销
内容的提问来源于stack exchange,提问作者Newboy11
相关产品推荐
相关产品推荐

