我來為您逐行詳細講解這個 `ViewMgr.cs` 文件。這是一個Unity游戲中的視野管理系統,用于優化游戲性能。
## 文件結構概覽
這個文件主要包含以下幾個部分:
1. 數據結構和接口定義
2. 視野管理器 `ViewMgr` 類
3. 工具類 `ViewTools`
讓我逐行為您講解:
## 1. 命名空間和引用
```1:7:Scripts/WeiLai/Mgr/ViewMgr.cs
using System.Collections.Generic;
using UnityEngine;
using WeiLai.Common;
using WeiLai.GameWorld.Camera;
using WeiLai.GameWorld;
using UnityEngine.Rendering;
```
這些是必要的引用:
- `System.Collections.Generic` - 用于List和Dictionary
- `UnityEngine` - Unity核心功能
- `WeiLai.Common` - 項目通用功能
- `WeiLai.GameWorld.Camera` - 相機管理
- `WeiLai.GameWorld` - 游戲世界相關
- `UnityEngine.Rendering` - 渲染相關
## 2. 數據結構定義
### GridRangeInt 類
```9:25:Scripts/WeiLai/Mgr/ViewMgr.cs
public class GridRangeInt : IRecycle
{
public int minPosX;
public int minPosY;
public int maxPosX;
public int maxPosY;
? ? public void Recycle()
{
}
? ? public void Fetch()
{
minPosX = 0;
minPosY = 0;
maxPosX = 0;
maxPosY = 0;
}
}
```
這個類表示整數網格范圍:
- 繼承 `IRecycle` 接口,支持對象池回收
- 存儲網格的最小和最大X、Y坐標
- `Recycle()` - 回收對象(這里為空實現)
- `Fetch()` - 從對象池獲取時重置數據
### GridRangeFloat 類
```27:43:Scripts/WeiLai/Mgr/ViewMgr.cs
public class GridRangeFloat : IRecycle
{
public float minPosX;
public float minPosY;
public float maxPosX;
public float maxPosY;
? ? public void Recycle()
{
}
? ? public void Fetch()
{
minPosX = 0;
minPosY = 0;
maxPosX = 0;
maxPosY = 0;
}
}
```
與 `GridRangeInt` 類似,但使用浮點數存儲世界坐標范圍。
### SkinnerItem 類
```45:70:Scripts/WeiLai/Mgr/ViewMgr.cs
public class SkinnerItem : IRecycle
{
public IRecycleSkinner m_Skinner;
public GridRangeInt m_RangeInt;
public GridRangeFloat m_RangeFloat;
? ? public void Recycle()
{
m_Skinner = null;
? ? ? ? if (m_RangeInt != null)
{
m_RangeInt.Recycle();
m_RangeInt = null;
}
? ? ? ? if (m_RangeFloat != null)
{
m_RangeFloat.Recycle();
m_RangeFloat = null;
}
}
? ? public void Fetch()
{
m_Skinner = null;
m_RangeFloat = null;
m_RangeInt = null;
}
}
```
這個類包裝了皮膚對象和其范圍信息:
- `m_Skinner` - 實現了 `IRecycleSkinner` 接口的對象
- `m_RangeInt` - 整數網格范圍
- `m_RangeFloat` - 浮點數世界坐標范圍
- `Recycle()` - 回收時清理所有引用
- `Fetch()` - 獲取時重置所有引用
## 3. 接口定義
### IRecycleSkinnerMoveable 接口
```72:85:Scripts/WeiLai/Mgr/ViewMgr.cs
public interface IRecycleSkinnerMoveable
{
/// <summary>
/// 進入攝像機視野
/// </summary>
public void InCameraView();
? ? /// <summary>
/// 退出攝像機視野
/// </summary>
public void OutCameraView();
/// <summary>
/// 獲取位置
/// </summary>
public Vector3 ?GetPosition();
}
```
可移動對象的接口:
- `InCameraView()` - 進入視野時的回調
- `OutCameraView()` - 退出視野時的回調
- `GetPosition()` - 獲取當前位置
### IRecycleSkinner 接口
```87:97:Scripts/WeiLai/Mgr/ViewMgr.cs
public interface IRecycleSkinner
{
/// <summary>
/// 進入攝像機視野
/// </summary>
public void InCameraView();
? ? /// <summary>
/// 退出攝像機視野
/// </summary>
public void OutCameraView();
}
```
靜態對象的接口,比可移動對象少了位置獲取方法。
## 4. ViewItem 類
```99:105:Scripts/WeiLai/Mgr/ViewMgr.cs
/// <summary>
/// 存放格子數據的,這個就不緩存了,整個游戲初始化以后一直保留
/// </summary>
public class ViewItem
{
public bool m_Visible;
public List<SkinnerItem> m_SkinnerList;
}
```
表示一個網格單元:
- `m_Visible` - 是否在視野內
- `m_SkinnerList` - 該網格內的所有皮膚對象列表
## 5. ViewMgr 主類
### 成員變量
```107:130:Scripts/WeiLai/Mgr/ViewMgr.cs
public class ViewMgr : Singleton<ViewMgr>
{
/// <summary>
/// 視野相機
/// </summary>
private Camera m_ViewCamera;?
/// <summary>
/// 所有的視野對象
/// </summary>
private Dictionary<int, ViewItem> m_ItemMap = new Dictionary<int, ViewItem>();
/// <summary>
/// 上一次對象
/// </summary>
private List<ViewItem> m_LastViewItems = new List<ViewItem>();
? ? /// <summary>
/// 怪物listcache
/// </summary>
private List<Monster> m_MonsterList = new List<Monster>();
private List<SceneGridItem> m_SceneGridList = new List<SceneGridItem>();
? ? /// <summary>
/// 皮膚和對應皮膚管理對象的緩存容器
/// </summary>
private Dictionary<IRecycleSkinner, SkinnerItem>
m_CacheSkinner = new Dictionary<IRecycleSkinner, SkinnerItem>();
? ? private List<IRecycleSkinnerMoveable> m_Moveables = new List<IRecycleSkinnerMoveable>();
```
- `m_ViewCamera` - 視野相機引用
- `m_ItemMap` - 所有網格的字典,key是網格ID
- `m_LastViewItems` - 上一幀在視野內的網格列表
- `m_MonsterList` - 視野內怪物的緩存列表
- `m_SceneGridList` - 視野內場景網格的緩存列表
- `m_CacheSkinner` - 皮膚對象到SkinnerItem的映射
- `m_Moveables` - 可移動對象列表
### 初始化方法
```132:135:Scripts/WeiLai/Mgr/ViewMgr.cs
public void InitData()
{
m_ItemMap.Clear();
}
```
清空所有網格數據。
### 可移動對象管理
```137:147:Scripts/WeiLai/Mgr/ViewMgr.cs
public void AddMoveableItem(IRecycleSkinnerMoveable skinner)
{
return;
? ? m_Moveables.Add(skinner);
}
public void RemoveMoveableItem(IRecycleSkinnerMoveable skinner)
{
return;
? ? m_Moveables.Remove(skinner);
}
```
注意這里直接返回了,說明可移動對象功能被暫時禁用。
### 添加視野對象
```149:155:Scripts/WeiLai/Mgr/ViewMgr.cs
public void AddViewItem(IRecycleSkinner skinner, float posX, float posY)
{
return;
? ? AddViewItem(skinner, ViewTools.GetGridRange(posX, posY), ViewTools.GetGridRangeFloat(posX, posY));
}
```
這個重載方法也被禁用了,原本應該調用下面的完整版本。
```157:200:Scripts/WeiLai/Mgr/ViewMgr.cs
public void AddViewItem(IRecycleSkinner skinner, GridRangeInt gridRange, GridRangeFloat gridRangeFloat)
{
return;
if (m_CacheSkinner.ContainsKey(skinner))
{
MyLogger.Error("why add again?");
return;
}
SkinnerItem skinnerItem = CacheObjectMgr.Inst.Fetch<SkinnerItem>();
skinnerItem.m_Skinner = skinner;
? ? skinnerItem.m_RangeInt = gridRange;
skinnerItem.m_RangeFloat = gridRangeFloat;
m_CacheSkinner[skinner] = skinnerItem;
? ? var minX = gridRange.minPosX;
var minY = gridRange.minPosY;
var maxX = gridRange.maxPosX;
var maxY = gridRange.maxPosY;
? ? //如果對象占用的網格足夠大,那就放多個格子上,檢測的時候,只要有一個格子在視野內也就顯示出來
for (int i = minX; i <= maxX; i++)
{
for (int j = minY; j <= maxY; j++)
{
var key = ViewTools.BlockPosToId(i, j);
#if UNITY_EDITOR
if (key < 0)
{
UnityEngine.Debug.LogError("key < 0");
}
#endif
? ? ? ? ? ? if (!m_ItemMap.TryGetValue(key, out var item))
{
item = new ViewItem();
item.m_Visible = true;
item.m_SkinnerList = new List<SkinnerItem>();
m_ItemMap.Add(key, item);
}
? ? ? ? ? ? item.m_SkinnerList.Add(skinnerItem);
}
}
}
```
這個方法也被禁用了,原本的邏輯是:
1. 檢查是否重復添加
2. 從對象池獲取SkinnerItem并設置數據
3. 將對象添加到所有覆蓋的網格中
4. 如果網格不存在則創建新的
### 移除視野對象
```202:230:Scripts/WeiLai/Mgr/ViewMgr.cs
public void RemoveViewItem(IRecycleSkinner skinner)
{
return;
if (!m_CacheSkinner.TryGetValue(skinner, out var skinnerItem))
{
return;
}
m_CacheSkinner.Remove(skinner);
? ? if (skinnerItem != null)
{
var minX = skinnerItem.m_RangeInt.minPosX;
var minY = skinnerItem.m_RangeInt.minPosY;
var maxX = skinnerItem.m_RangeInt.maxPosX;
var maxY = skinnerItem.m_RangeInt.maxPosY;
? ? ? ? //如果對象占用的網格足夠大,那就放多個格子上,檢測的時候,只要有一個格子在視野內也就顯示出來
for (int i = minX; i <= maxX; i++)
{
for (int j = minY; j <= maxY; j++)
{
var key = ViewTools.BlockPosToId(i, j);
if (m_ItemMap.TryGetValue(key, out var item))
{
skinnerItem.Recycle();
item.m_SkinnerList.Remove(skinnerItem);
}
}
}
}
else
{
MyLogger.Error("skinner not found");
}
}
```
這個方法也被禁用了,原本的邏輯是:
1. 從緩存中獲取SkinnerItem
2. 從所有覆蓋的網格中移除該對象
3. 回收SkinnerItem
### 視野檢查
```232:242:Scripts/WeiLai/Mgr/ViewMgr.cs
public void CheckSkinnerInView()
{
if (m_ItemMap.Count > 0)
{
//假設所有格子不可見
foreach (var viewItem in m_LastViewItems)
{
viewItem.m_Visible = false;
}
? ? ? ? _CheckGridItemInView();
}
}
```
主要的視野檢查方法:
1. 先將所有上一幀可見的網格標記為不可見
2. 調用內部方法進行詳細檢查
### 添加視野內項目
```244:252:Scripts/WeiLai/Mgr/ViewMgr.cs
private void _AddItemInView(ViewItem item)
{
//設置格子成可見
item.m_Visible = true;
if (!m_LastViewItems.Contains(item))
{
//保證,只添加一次
m_LastViewItems.Add(item);
}
}
```
將網格標記為可見并添加到當前視野列表。
### 檢查可移動對象
```254:268:Scripts/WeiLai/Mgr/ViewMgr.cs
private void _CheckMoveableInView(float minX, float minY, float maxX, float maxY)
{
for (int i = 0; i < m_Moveables.Count; i++)
{
var skinner = m_Moveables[i];
var pos = skinner.GetPosition();
if (pos.x > minX && pos.x < maxX && pos.y > minY &&
pos.y < maxY)
{
skinner.InCameraView();
}
else
{
skinner.OutCameraView();
}
}
}
```
檢查可移動對象是否在視野范圍內:
1. 遍歷所有可移動對象
2. 獲取其位置
3. 判斷是否在視野范圍內
4. 調用相應的回調方法
### 核心視野檢查邏輯
```270:320:Scripts/WeiLai/Mgr/ViewMgr.cs
private void _CheckGridItemInView()
{
var ctrl = CameraMgr.Inst.m_CameraMoveCtrl;
//擴大1個格子單位,以免出現剛進格子的時候,因加載延遲導致的對象閃爍的效果
int minX = Mathf.FloorToInt(ctrl.m_MinPosX / CameraMgr.m_ViewWidth) - 1;
int minY = Mathf.FloorToInt(ctrl.m_MinPosY / CameraMgr.m_ViewHeight) - 1;
? ? int maxX = Mathf.CeilToInt(ctrl.m_MaxPosX / CameraMgr.m_ViewWidth) + 1;
int maxY = Mathf.CeilToInt(ctrl.m_MaxPosY / CameraMgr.m_ViewHeight) + 1;
? ? _CheckMoveableInView(ctrl.m_MinPosX - 2, ctrl.m_MinPosY, ctrl.m_MaxPosX + 2, ctrl.m_MaxPosY);
? ? //檢查在視野內的范圍內是否有格子數據,一般沒數據就沒有格子
for (int i = minX; i <= maxX; i++)
{
for (int j = minY; j <= maxY; j++)
{
var key = ViewTools.BlockPosToId(i, j);
if (m_ItemMap.TryGetValue(key, out var item))
{
_AddItemInView(item);
}
}
}
? ? //更新格子內的對象視野
var minPosx = ctrl.m_MinPosX - 2;
var minPosy = ctrl.m_MinPosY - 2;
var maxPosx = ctrl.m_MaxPosX + 2;
var maxPosy = ctrl.m_MaxPosY + 2;
? ? for (int i = m_LastViewItems.Count - 1; i >= 0; i--)
{
var item = m_LastViewItems[i];
if (item.m_Visible)
{
for (int j = item.m_SkinnerList.Count - 1; j >= 0; j--)
{
//格子部分在視野內,那么就會有的對象在視野范圍內,有的對象不在視野范圍內
var skinner = item.m_SkinnerList[j];
if (ViewTools.CheckCollision(skinner.m_RangeFloat.minPosX,
skinner.m_RangeFloat.minPosY,
skinner.m_RangeFloat.maxPosX,
skinner.m_RangeFloat.maxPosY
, minPosx, minPosy, maxPosx, maxPosy))
{
skinner.m_Skinner.InCameraView();
}
else
{
skinner.m_Skinner.OutCameraView();
}
}
}
else
{
//整個格子都不在視野內了,那么格子內的對象也都不在視野范圍內
for (int j = item.m_SkinnerList.Count - 1; j >= 0; j--)
{
var skinner = item.m_SkinnerList[j];
skinner.m_Skinner.OutCameraView();
}
? ? ? ? ? ? //不再視野內的移除掉,只保留在視野內的
m_LastViewItems.RemoveAt(i);
}
}
}
```
這是最核心的視野檢查邏輯:
1. **計算視野范圍**:
- 獲取相機控制器的視野范圍
- 擴大1個格子單位避免閃爍
- 轉換為網格坐標
2. **檢查可移動對象**:
- 調用可移動對象檢查方法
3. **檢查網格可見性**:
- 遍歷視野范圍內的所有網格
- 如果網格存在數據,標記為可見
4. **更新對象視野狀態**:
- 遍歷上一幀可見的網格
- 對每個網格內的對象進行碰撞檢測
- 根據是否在視野內調用相應的回調
- 移除不再可見的網格
### 獲取視野內對象
```322:340:Scripts/WeiLai/Mgr/ViewMgr.cs
public List<Monster> GetMonstersInView()
{
if (SceneMgr.Inst.m_CurScene.GetIsCheckView())
{
m_MonsterList.Clear();
for (int i = 0; i < m_LastViewItems.Count; i++)
{
var viewItem = m_LastViewItems[i];
for (int j = 0; j < viewItem.m_SkinnerList.Count; j++)
{
var skinnerItem = viewItem.m_SkinnerList[j];
if (skinnerItem.m_Skinner is Monster monster)
{
m_MonsterList.Add(monster);
}
}
}
}
//else
//{
//如果不做視野管理,就直接返回m_MonsterList,其怪物在AddViewItem的時候就添加進去且一直存在,直到RemoveViewItem才移除
//}
return m_MonsterList;
}
```
獲取視野內的怪物列表:
1. 檢查當前場景是否啟用視野檢查
2. 清空緩存列表
3. 遍歷所有可見網格
4. 提取其中的Monster對象
5. 返回列表
```342:356:Scripts/WeiLai/Mgr/ViewMgr.cs
/// <summary>
/// 獲取視野內的格子
/// </summary>
/// <returns></returns>
public List<SceneGridItem> GetGridItemInView()
{
m_SceneGridList.Clear();
foreach (var viewItem in m_LastViewItems)
{
foreach (var skinnerItem in viewItem.m_SkinnerList)
{
if (skinnerItem.m_Skinner is SceneGridItem sceneGridItemData)
{
m_SceneGridList.Add(sceneGridItemData);
}
}
}
return m_SceneGridList;
}
```
獲取視野內的場景網格列表,邏輯與獲取怪物類似。
### 清理方法
```358:378:Scripts/WeiLai/Mgr/ViewMgr.cs
protected override void OnUnInit()
{
if(m_CacheSkinner.Count > 0)
{
//MyLogger.Error("why has item?"); ??
}
foreach (var kvViewItem in m_CacheSkinner)
{
kvViewItem.Value.Recycle();
}
m_CacheSkinner.Clear();
? ? m_LastViewItems.Clear();
m_ItemMap.Clear();
m_MonsterList.Clear();
base.OnUnInit();
}
```
清理所有數據:
1. 回收所有SkinnerItem
2. 清空所有集合
3. 調用基類清理方法
## 6. ViewTools 工具類
### 靜態變量
```382:384:Scripts/WeiLai/Mgr/ViewMgr.cs
public static class ViewTools
{
public static Vector2 m_GridSize = new(3.8f, 3.0f);
private static Vector2 m_HalfGridSize = m_GridSize / 2.0f;
```
定義網格大小和半網格大小。
### 網格坐標轉換
```386:390:Scripts/WeiLai/Mgr/ViewMgr.cs
public static int BlockPosToId(int x, int y)
{
return (x + 1000) | ((y + 1000) << 16);
}
```
將網格坐標轉換為唯一ID:
- 加1000是為了處理負數坐標
- 使用位運算將X和Y坐標組合成一個整數
```392:396:Scripts/WeiLai/Mgr/ViewMgr.cs
public static void IdToBlockPos(int id, out int x, out int y)
{
x = id & 0xFFFF - 1000;
y = (id >> 16) - 1000;
}
```
將ID轉換回網格坐標:
- 使用位運算分離X和Y坐標
- 減去1000恢復原始坐標
### 獲取網格大小
```398:402:Scripts/WeiLai/Mgr/ViewMgr.cs
public static Vector3 GetGridSize()
{
//配置的是每個100個像素等于1個unity單位
return m_GridSize;
}
```
返回網格大小,注釋說明100像素=1Unity單位。
### 獲取世界坐標范圍
```408:418:Scripts/WeiLai/Mgr/ViewMgr.cs
/// <summary>
/// 獲取世界坐標的范圍
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static GridRangeFloat GetGridRangeFloat(float x, float y)
{
var ret = CacheObjectMgr.Inst.Fetch<GridRangeFloat>();
ret.minPosX = x - m_HalfGridSize.x;
ret.minPosY = y - m_HalfGridSize.y;
ret.maxPosX = x + m_HalfGridSize.x;
ret.maxPosY = y + m_HalfGridSize.y;
return ret;
}
```
根據中心點計算世界坐標范圍:
1. 從對象池獲取GridRangeFloat
2. 計算以中心點為中心的矩形范圍
3. 返回范圍對象
### 獲取網格范圍
```424:434:Scripts/WeiLai/Mgr/ViewMgr.cs
/// <summary>
/// 獲取占據格子的范圍
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static GridRangeInt GetGridRange(float x, float y)
{
var ret = CacheObjectMgr.Inst.Fetch<GridRangeInt>();
ret.minPosX = Mathf.FloorToInt((x - m_HalfGridSize.x) / CameraMgr.m_ViewWidth);
ret.maxPosX = Mathf.FloorToInt((x + m_HalfGridSize.x) / CameraMgr.m_ViewWidth);
ret.minPosY = Mathf.FloorToInt((y - m_HalfGridSize.y) / CameraMgr.m_ViewHeight);
ret.maxPosY = Mathf.FloorToInt((y + m_HalfGridSize.y) / CameraMgr.m_ViewHeight);
return ret;
}
```
將世界坐標轉換為網格坐標范圍:
1. 從對象池獲取GridRangeInt
2. 將世界坐標范圍除以網格大小
3. 使用FloorToInt確保覆蓋所有相關網格
### 碰撞檢測
```436:460:Scripts/WeiLai/Mgr/ViewMgr.cs
public static bool CheckCollision(
float rect1MinX, float rect1MinY, float rect1MaxX, float rect1MaxY,
float rect2MinX, float rect2MinY, float rect2MaxX, float rect2MaxY)
{
// 檢查x軸是否有重疊
if (rect1MaxX < rect2MinX)
{
return false;
}
? ? if (rect2MaxX < rect1MinX)
{
return false;
}
? ? if (rect1MaxY < rect2MinY)
{
return false;
}
? ? if (rect2MaxY < rect1MinY)
{
return false;
}
? ? return true;
}
```
檢查兩個矩形是否重疊:
1. 檢查X軸是否有重疊
2. 檢查Y軸是否有重疊
3. 如果任一軸沒有重疊,返回false
4. 否則返回true
## 總結
這個視野管理系統的主要特點:
1. **性能優化**:通過網格化視野管理,只渲染視野內的對象
2. **對象池**:使用對象池減少內存分配
3. **雙重范圍**:同時維護整數網格范圍和浮點數世界坐標范圍
4. **回調機制**:通過接口回調通知對象進入/退出視野
5. **緩存機制**:緩存視野內的怪物和場景網格列表
目前大部分功能被禁用了(直接return),可能是為了調試或性能測試。
在C#中,protected override void OnInit()
?是一個方法的聲明,其中的?protected
?和?override
?是兩個關鍵修飾符,分別表示?訪問權限?和?方法重寫。以下是詳細解釋:
1.?
protected
(訪問修飾符)
作用:表示該方法只能在?當前類?或?派生類(子類)?中訪問,外部類無法直接調用。
對比其他修飾符:
public
:完全公開。
private
:僅當前類可訪問。
internal
:同一程序集內可訪問。class BaseClass {protected void ProtectedMethod() { } // 僅基類和子類可調用 }
2.?override
(方法重寫)作用:表示該方法?重寫(覆蓋)了父類的虛方法(
virtual
)或抽象方法(abstract
)。前提條件:
父類中必須存在標記為?
virtual
?或?abstract
?的同名方法。方法簽名(名稱、參數、返回類型)必須完全一致。
class ParentClass {public virtual void OnInit() { } // 父類虛方法 }class ChildClass : ParentClass {protected override void OnInit() { base.OnInit(); // 可選:調用父類邏輯// 子類新增邏輯} }
3.?為什么需要?
protected override
?封裝性:
protected
?確保方法只在繼承體系內使用,避免外部誤調用。多態性:
override
?允許子類自定義父類行為,實現多態(同一方法在不同子類中有不同實現)。4.?常見應用場景
框架/游戲生命周期方法:
如 Unity 的?OnInit()
、ASP.NET Core 的?OnInitialize()
,通常由框架基類定義虛方法,子類重寫以注入自定義邏輯。抽象類/接口實現:
若父類是抽象類(abstract
),子類必須重寫抽象方法。5.?注意事項
new
?vs?override
:
若子類方法用?new
?修飾,會隱藏父類方法(而非重寫),多態調用時仍執行父類邏輯。密封方法:
若父類方法標記為?sealed override
,子類不能再重寫。
protected override void OnInit()
?的含義:
這是一個受保護的方法,重寫了父類的虛方法或抽象方法,允許子類在繼承關系中擴展或修改其行為。典型用途:自定義初始化邏輯、實現多態、響應生命周期事件。
List<T> 簡介
List<T> 是 .NET 框架中泛型集合類,位于 System.Collections.Generic 命名空間下。
它提供了動態數組的功能,可以存儲任意類型的對象,并且在運行時可以根據需要動態調整大小。
PageInfo 類型
PageInfo 是一個自定義類型,通常用于表示 UI 頁面的相關信息(例如頁面名稱、資源路徑、是否已加載等)。
該類型可能包含一些屬性或方法來管理頁面的狀態或數據。
UIMgr?
循環遍歷 m_Caches 列表
使用一個從后往前(倒序)的 for 循環來遍歷 m_Caches 列表。
m_Caches 是一個存儲 PageInfo 對象的列表,通常用于緩存已經加載過的 UI 頁面信息。
倒序遍歷的原因可能是為了在刪除元素時不影響前面的索引,避免跳過某些元素。
獲取當前迭代的頁面對象
var page = m_Caches[i];:從 m_Caches 列表中取出索引為 i 的頁面對象,并將其賦值給局部變量 page。
這里的 var 關鍵字表示隱式類型推斷,編譯器會根據右側的表達式自動推斷出 page 的類型為 PageInfo。
判斷頁面類型是否匹配
if (page.m_PageType == ePageType):檢查當前頁面對象的 m_PageType 屬性是否與傳入的 ePageType 匹配。
m_PageType 是 PageInfo 類中的一個字段或屬性,表示該頁面的類型。
ePageType 是一個外部傳入的變量,通常是一個枚舉值,代表需要查找的目標頁面類型。