游戲開發日志

我來為您逐行詳細講解這個 `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

    • 前提條件

      1. 父類中必須存在標記為?virtual?或?abstract?的同名方法。

      2. 方法簽名(名稱、參數、返回類型)必須完全一致。

        class ParentClass
        {public virtual void OnInit() { } // 父類虛方法
        }class ChildClass : ParentClass
        {protected override void OnInit() { base.OnInit(); // 可選:調用父類邏輯// 子類新增邏輯}
        }

        3.?為什么需要?protected override

      3. 封裝性protected?確保方法只在繼承體系內使用,避免外部誤調用。

      4. 多態性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 是一個外部傳入的變量,通常是一個枚舉值,代表需要查找的目標頁面類型。



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91911.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91911.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91911.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 PlanetScope 衛星圖像繪制水質參數:以莫干湖為例

1.數據采集 我使用ArcGIS Pro 中的Planet Imagery插件下載了 2023 年 6 月 25 日的安卡拉莫干湖衛星圖像。 圖 1&#xff1a;使用 Planet 插件下載衛星圖像 圖 2&#xff1a;下載圖像的日期和傳感器選擇 我查閱的研究中指出&#xff0c;使用無降水時期的衛星圖像對于水質測定…

Docker部署前后端分離項目——多項目共享環境部署

目錄 一、簡介 二、文件目錄結構 三、前端部署流程&#xff08;多nginx&#xff09; 3.1 前端打包 3.2 編寫部署文件——項目1&#xff08;consult-system&#xff09; 3.3 編寫部署文件——項目2&#xff08;person-system&#xff09; 3.4 前端部署至linux服務器 3.5…

學習筆記(39):結合生活案例,介紹 10 種常見模型

學習筆記(39):結合生活案例&#xff0c;介紹 10 種常見模型線性回歸只是機器學習的 “冰山一角”&#xff01;根據不同的任務場景&#xff08;分類、回歸、聚類等&#xff09;&#xff0c;還有許多強大的模型可以選擇。下面我用最通俗易懂的語言&#xff0c;結合生活案例&#…

BabyAGI 是一個用于自構建自主代理的實驗框架

這個最新的 BabyAGI 是一個用于自構建自主代理的實驗框架 核心是一個新的函數框架 &#xff08;functionz&#xff09;&#xff0c;用于存儲、管理和執行數據庫中的函數。它提供了一個基于圖形的結構&#xff0c;用于跟蹤導入、依賴函數和身份驗證密鑰&#xff0c;并具有自動加…

商業秘密視域下計算機軟件的多重保護困境

作者&#xff1a;邱戈龍、柯堅豪重慶商業秘密律師廣東長昊律師事務所引言&#xff1a;計算機軟件保護的復雜性 在商業秘密保護的宏大版圖中&#xff0c;計算機軟件因其技術密集性和創新性占據著特殊地位。軟件的真正價值不僅在于其代碼本身&#xff0c;更在于其背后的流程、邏…

深入理解 Spring Boot 自動配置原理

Spring Boot 之所以能“開箱即用”&#xff0c;其核心就在于 自動配置機制&#xff08;Auto Configuration&#xff09;。本文將深入剖析 Spring Boot 自動配置的工作原理&#xff0c;從注解入手&#xff0c;再到底層的源碼機制&#xff0c;揭開 Spring Boot 背后的“魔法”。 …

Ubuntu18.04開機啟動執行腳本

#!/bin/bash # 運行 .NET Core 應用程序 dotnet /home/bruce/atg/SmartConsole.dll &# 打開瀏覽器 firefox 給文件權限sudo chmod 777 start.sh運行gnome-session-properties打開系統自帶的一個啟動程序

c語言進階 字符函數和字符串函數

字符函數和字符串函數字符函數和字符串函數1. strlenstrlen 函數詳解模擬實現1.計數器方式2.不能創建臨時變量計數器&#xff08;遞歸&#xff09;3.指針-指針的方式2. strcpystrcpy 函數詳解模擬實現3. strcatstrcat 函數詳解模擬實現4. strcmpstrcmp 函數詳解模擬實現5. strn…

(LeetCode 每日一題) 1233. 刪除子文件夾 (排序)

題目&#xff1a;1233. 刪除子文件夾 思路&#xff1a;排序&#xff0c;時間復雜度0(L*nlogn)。 文件夾a的子文件b&#xff0c;b字符串字典序列一定是大于a的&#xff0c;所以直接將字符串數組folder升序排序。每次只需判斷當前字符串&#xff0c;是否是父文件夾數組v最后一個…

集成算法學習bagging,boosting,stacking

baggibg(rf隨機森林) adaboostibg 用來展示 Project Jupyter | Home 展示源碼 Eclipse IDE | The Eclipse Foundation Eclipse 下載 |Eclipse 基金會 教程8-Adaboost決策邊界效果_嗶哩嗶哩_bilibili (23 封私信) 圖解機器學習神器&#xff1a;Scikit-Learn - 知乎 Baggi…

HOOPS SDK賦能PLM:打造全生命周期3D數據管理與協作能力

在制造業和工業領域&#xff0c;產品全生命周期管理&#xff08;PLM&#xff09; 已成為驅動企業數字化轉型、提升創新力與運營效率的核心引擎。一個高效的PLM平臺不僅需要管理海量的設計數據&#xff0c;還必須在設計、制造、供應鏈、銷售和服務等多個環節之間無縫流轉信息&am…

解決 Selenium 頁面跳轉過快導致的內容獲取問題:從原理到實踐

在使用 Selenium 進行網頁自動化操作時&#xff0c;很多開發者都會遇到一個頭疼的問題&#xff1a;頁面還沒加載完&#xff0c;代碼就已經執行到下一句了。結果要么是元素找不到&#xff0c;要么是獲取的內容不完整&#xff0c;甚至直接拋出異常。今天我們就來聊聊如何優雅地解…

【Python練習】051. 編寫一個函數,實現簡單的定時器功能

051. 編寫一個函數,實現簡單的定時器功能 051. 編寫一個函數,實現簡單的定時器功能 代碼說明: 示例運行: 擴展功能 代碼說明: 實現Python定時器的幾種方法 051. 編寫一個函數,實現簡單的定時器功能 以下是一個簡單的Python函數,用于實現定時器功能。這個定時器可以設置…

springboot基礎-demo

1.創建學生信息表 create table stu(id int unsigned primary key auto_increment comment ID,name varchar(100) comment 姓名,age tinyint unsigned comment 年齡,gender tinyint unsigned comment 性別, 1:男, 2:女,score double(5,2) comment 成績,phone varchar(11) comme…

關于transformer的一些疑點總結

殘差連接的作用 Transformer中的殘差連接&#xff08;Residual Connection&#xff09;是其深層架構能穩定訓練的核心設計之一&#xff0c;主要通過以下機制發揮作用&#xff1a; 1. 緩解梯度消失&#xff0c;支持深層訓練 梯度保護機制&#xff1a;在反向傳播時&#xff0c;…

【終極指南】解決 Windows 11 更新后 Docker 連接 localhost 奇慢(卡頓、超時十幾秒)的通用方案

聰明人能看得出這是 ai 寫的&#xff0c;但也是我親身實踐的&#xff0c;最后讓 ai 總結寫了一篇&#xff0c;放心食用 一、 結論先行&#xff08;直接用&#xff09;問題現象&#xff1a; 升級到某個 Windows 11 版本后&#xff0c;在本地訪問 Docker 容器中部署的任何服務&am…

Stream API

Java 8 引入的 Stream API 是處理集合數據的強大工具&#xff0c;它允許你以聲明式方式處理數據集合&#xff0c;支持各種聚合操作和并行處理。以下是 Stream API 的核心知識點及具體代碼示例&#xff1a; 1. Stream 概述 Stream 是數據渠道&#xff0c;用于操作數據源&#xf…

相機參數的格式與作用

在計算機視覺中&#xff0c;相機標定是非常重要的一步&#xff0c;主要目的是從圖像中恢復出物體的三維信息。為了做到這一點&#xff0c;我們需要了解和使用一系列的數學工具&#xff0c;這些工具描述了相機的成像過程&#xff0c;包括相機的內參、外參、畸變系數、投影矩陣和…

【jvm|基本原理】第四天

摘要&#xff1a;本文簡單分析了Java虛擬機的核心運行機制。首先介紹了基本數據類型在32位和64位虛擬機中的存儲差異&#xff0c;說明slot槽設計以空間換時間的優化思路。其次詳細解析了對象在堆內存中的存儲結構&#xff0c;包括對象頭、對象數據和對齊填充機制。然后探討了方…

Git高級操作與最佳實踐詳解

前言 熟練掌握Git的高級操作可以顯著提高開發效率&#xff0c;優化工作流程&#xff0c;解決復雜問題。本文將詳細介紹Git的高級操作技巧與最佳實踐&#xff0c;幫助開發者更加高效地管理代碼和協作開發。 1. 提交歷史管理 1.1 修改最近的提交 # 修改最近的提交信息 git co…