//题目:假定一个2D场景中有数量众多且分布不均匀的(10w+)gameobject,摄像机在场景中自由移动,请按照您对场景管理的理解,自定设计目标,设计一个管理机制来管理这些gameobject,可用伪代码表达,并辅以必要的文字说明。
//设计思路
//1:区域划分:将整个 Unity 2D 场景均匀地分割成 N 块区域,每个区域可以用一个矩形来表示其范围。
//2:摄像机视口区域:确定摄像机的视口所在的区域,该区域作为中心区域。
//3:渲染区域选择:除了摄像机视口所在的区域外,还选择其周围的 8 块区域,总共渲染 9 块区域,以保证画面的连贯性。
//4:GameObject 管理:为每个 GameObject 记录其所属的区域,当需要渲染时,只处理这 9 块区域内的 GameObject,上次区域的 GameObject 进行隐藏或暂停相关处理。
using UnityEngine;
using System.Collections.Generic;
// 定义区域类
public class SceneRegion
{
public Rect regionBounds; // 区域的范围
public Dictionary<int, GameObject> objectsInRegion; // 区域内的 GameObject 列表
public SceneRegion(Rect bounds)
{
regionBounds = bounds;
objectsInRegion = new Dictionary<int, GameObject>();
}
// 将 GameObject 添加到区域中
public void AddObject(GameObject obj)
{
objectsInRegion.Add(obj.GetHashCode(), obj);
}
// 判断 GameObject 是否在区域内
public bool ContainsObject(GameObject obj)
{
return objectsInRegion.ContainsKey(obj.GetHashCode());
}
}
// 场景管理类
public class CameraAreaManager : MonoBehaviour
{
public Camera mainCamera;
public int numRegionsX; // X 轴方向划分的区域数量
public int numRegionsY; // Y 轴方向划分的区域数量
private SceneRegion[,] regions; // 存储所有区域的二维数组
private SceneRegion cameraRegion; // 摄像机视口所在的区域
private List<SceneRegion> regionsToRender; // 需要渲染的 9 块区域
private List<SceneRegion> unRegionsToRender = new List<SceneRegion>();// 需要移除渲染的区域
private int _oldXIndex = -999;
private int _oldYIndex = -999;
private int _intenalPace = 10000;
private Dictionary<long, SceneRegion> _oldIndexDic = new Dictionary<long, SceneRegion>();
private void Start()
{
// 初始化区域划分
InitializeRegions();
// 将所有 GameObject 分配到对应的区域中
AssignObjectsToRegions();
// 初始更新需要渲染的区域
UpdateRegionsToRender();
}
private void Update()
{
// 摄像机移动时更新需要渲染的区域
if (mainCamera.transform.hasChanged)
{
UpdateRegionsToRender();
mainCamera.transform.hasChanged = false;
}
}
private void InitializeRegions()
{
float sceneWidth = 1000; // 假设场景宽度
float sceneHeight = 1000; // 假设场景高度
float regionWidth = sceneWidth / numRegionsX;
float regionHeight = sceneHeight / numRegionsY;
regions = new SceneRegion[numRegionsX, numRegionsY];
for (int x = 0; x < numRegionsX; x++)
{
for (int y = 0; y < numRegionsY; y++)
{
Rect regionBounds = new Rect(x * regionWidth, y * regionHeight, regionWidth, regionHeight);
regions[x, y] = new SceneRegion(regionBounds);
}
}
}
private void AssignObjectsToRegions()
{
GameObject[] allObjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
foreach (GameObject obj in allObjects)
{
Vector2 position = obj.transform.position;
int xIndex = Mathf.FloorToInt(position.x / (1000 / numRegionsX));
int yIndex = Mathf.FloorToInt(position.y / (1000 / numRegionsY));
if (xIndex >= 0 && xIndex < numRegionsX && yIndex >= 0 && yIndex < numRegionsY)
{
regions[xIndex, yIndex].AddObject(obj);
}
}
}
private void UpdateRegionsToRender()
{
// 获取摄像机视口所在的区域
cameraRegion = GetCameraRegion();
//获取上次渲染的区域
if (_oldXIndex >= 0)
{
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
int oldX = _oldXIndex + i;
int oldY = _oldYIndex + j;
if (oldX >= 0 && oldX < numRegionsX && oldY >= 0 && oldY < numRegionsY)
{
if (i != 0 || j != 0)
{
_oldIndexDic.Add(oldX* _intenalPace + oldY, regions[oldX, oldY]);
}
}
}
}
}
// 确定需要渲染的 9 块区域
regionsToRender = new List<SceneRegion>();
regionsToRender.Add(cameraRegion);
int xIndex = GetRegionIndexX(cameraRegion.regionBounds.x);
int yIndex = GetRegionIndexY(cameraRegion.regionBounds.y);
_oldXIndex = xIndex;
_oldYIndex = yIndex;
unRegionsToRender.Clear();
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
int newX = xIndex + i;
int newY = yIndex + j;
if (newX >= 0 && newX < numRegionsX && newY >= 0 && newY < numRegionsY)
{
if (i != 0 || j != 0)
{
regionsToRender.Add(regions[newX, newY]);
}
}
if (_oldIndexDic.ContainsKey(newX * _intenalPace + newY) == true)
{
_oldIndexDic.Remove(newX * _intenalPace + newY);
}
}
}
foreach (var item in _oldIndexDic)
{
// 隐藏或者销毁区域内和外的 GameObject
}
// 处理渲染区域内和外的 GameObject
foreach (SceneRegion region in regionsToRender)
{
// 显示或者加载区域内 GameObject
}
}
private SceneRegion GetCameraRegion()
{
Rect cameraBounds = GetCameraBounds();
int xIndex = GetRegionIndexX(cameraBounds.x);
int yIndex = GetRegionIndexY(cameraBounds.y);
return regions[xIndex, yIndex];
}
private Rect GetCameraBounds()
{
float cameraHeight = 2f * mainCamera.orthographicSize;
float cameraWidth = cameraHeight * mainCamera.aspect;
Vector3 cameraPosition = mainCamera.transform.position;
return new Rect(cameraPosition.x - cameraWidth / 2, cameraPosition.y - cameraHeight / 2, cameraWidth, cameraHeight);
}
private int GetRegionIndexX(float x)
{
return Mathf.FloorToInt(x / (1000 / numRegionsX));
}
private int GetRegionIndexY(float y)
{
return Mathf.FloorToInt(y / (1000 / numRegionsY));
}
}
题4答案 -CameraAreaManager 场景物件管理
于 2025-03-06 11:42:52 首次发布