Compare commits

...

3 Commits

Author SHA1 Message Date
d9a4924e2e Merge branch 'main' of http://10.27.17.121:3000/kawagiri/TH1 into main 2026-04-17 15:01:17 +08:00
c88a608e47 性能优化 2026-04-17 15:01:13 +08:00
674c881ac1 编辑器优化 2026-04-17 15:00:44 +08:00
87 changed files with 1734 additions and 433 deletions

859
MD/代码优化标准.md Normal file
View File

@ -0,0 +1,859 @@
# 🔧 TH1 代码优化标准
> **适用范围**: TH1 回合制4X策略游戏全模块
> **核心目标**: 减少GC分配 · 加快计算速度 · 保证输入输出不变
> **约束条件**: 不改变整体逻辑 · 兼容 MemoryPack 本地/网络序列化 · 保持多人同步正确性
---
## 目录
- [第一章 总则与核心原则](#第一章-总则与核心原则)
- [第二章 序列化安全规范](#第二章-序列化安全规范)
- [第三章 GC优化标准](#第三章-gc优化标准)
- [第四章 计算优化标准](#第四章-计算优化标准)
- [第五章 模块专项优化指南](#第五章-模块专项优化指南)
- [第六章 优化验证清单](#第六章-优化验证清单)
- [附录A 已知GC热点清单](#附录a-已知gc热点清单)
- [附录B 禁止与允许操作速查表](#附录b-禁止与允许操作速查表)
---
## 第一章 总则与核心原则
### 1.1 优化铁律
| 编号 | 规则 | 说明 |
|------|------|------|
| **R-01** | **输入输出不变** | 任何优化不得改变方法的参数签名、返回值语义和副作用。对外行为必须与优化前完全一致 |
| **R-02** | **逻辑等价** | 优化只能改变"怎么做",不能改变"做什么"。所有分支路径、计算结果、状态变更必须等价 |
| **R-03** | **序列化安全** | 任何新增缓存/查找表字段必须加 `[MemoryPackIgnore]`,不得影响存档文件和网络消息的二进制格式 |
| **R-04** | **确定性一致** | 多人联网场景下,优化后的计算必须保持确定性(相同输入产生相同输出),不得引入浮点精度差异 |
| **R-05** | **渐进式优化** | 每次只优化一个点,优化后必须验证,确认无误后再优化下一个 |
### 1.2 优化优先级
```
优先级从高到低:
P0 - 消除热路径中的 GC 分配(每帧/每回合执行的代码)
P1 - 缓存重复计算结果(空间换时间,需考虑序列化)
P2 - 算法复杂度优化O(n²) → O(n) 或 O(n log n)
P3 - 减少非热路径的 GC 分配(初始化/低频调用)
P4 - 微优化(方法内联、分支预测等)
```
### 1.3 不优化原则
以下情况**不应**进行优化:
- 仅在游戏初始化时执行一次的代码(如 `SkillFactory` 首次反射扫描)
- 仅在加载/存档时执行的代码(除非造成明显卡顿)
- UI 层的低频更新代码
- 已经足够高效且无 GC 问题的代码
---
## 第二章 序列化安全规范
### 2.1 TH1 序列化架构概览
TH1 存在三种序列化场景,任何"空间换时间"优化必须同时考虑:
| 场景 | 序列化方式 | 频率 | 敏感度 |
|------|-----------|------|--------|
| **本地存档** | MemoryPack → `.dat` 文件 | 每回合一次 | 向后兼容(旧存档能读取) |
| **网络同步** | MemoryPack → Steam P2P | 每个Action、每2秒心跳 | 双向兼容(多客户端版本) |
| **训练数据** | MemoryPack / JSON → `.jsonl` | `#if ENABLE_TRAIN` 时每步一次 | 格式稳定 |
### 2.2 核心 MemoryPackable 类清单
以下类直接参与序列化,新增字段时**必须**遵守规范:
```
MapData ─ 游戏世界根节点(包含以下所有子结构)
├── MapConfig ─ 对局配置
├── GridMapData ─ 所有格子
│ └── GridData ─ 单个格子(附带 Skills
├── PlayerMapData─ 所有玩家
│ └── PlayerData─ 单个玩家(附带 Skills, DiplomacyData, TechTreeData
├── CityMapData ─ 所有城市
│ └── CityData ─ 单个城市(附带 TerritoryData, Skills
├── UnitMapData ─ 所有部队
│ └── UnitData ─ 单个部队(附带 Skills, ActionPoint
└── NetData ─ 网络状态(含 Actions 历史, 随机种子)
CommonActionParams ─ Action参数网络传输
BaseMessage (16种) ─ 网络消息(含 GameStartMessage 携带完整 MapData
SkillBase (242种子类)─ 技能多态序列化
```
### 2.3 新增缓存字段的标准做法
#### ✅ 正确做法:`[MemoryPackIgnore]` + 重建逻辑
```csharp
[MemoryPackable]
public partial class UnitData : IdentifierBase
{
// 已有的序列化字段
public List<SkillBase> Skills;
// ===== 新增的缓存字段 =====
[MemoryPackIgnore]
private Dictionary<SkillType, SkillBase> _skillLookup;
/// <summary>
/// O(1) 技能查找,替代 Skills 列表的 O(n) 线性搜索。
/// 缓存在反序列化后自动重建。
/// </summary>
public SkillBase GetSkillFast(SkillType type)
{
if (_skillLookup == null) RebuildSkillLookup();
return _skillLookup.TryGetValue(type, out var skill) ? skill : null;
}
// 当 Skills 列表发生变更时调用
public void InvalidateSkillLookup()
{
_skillLookup = null; // 延迟重建,下次查找时自动构建
}
private void RebuildSkillLookup()
{
_skillLookup ??= new Dictionary<SkillType, SkillBase>(Skills.Count);
_skillLookup.Clear();
foreach (var skill in Skills)
_skillLookup[skill.GetSkillType()] = skill;
}
[MemoryPackOnDeserialized]
public void OnAfterDeserialize()
{
_skillLookup = null; // 反序列化后标记为需要重建
// 不在此处立即重建,延迟到首次使用
}
}
```
#### ❌ 错误做法
```csharp
// 错误1缓存字段没有加 [MemoryPackIgnore]
[MemoryPackable]
public partial class UnitData
{
public Dictionary<SkillType, SkillBase> _skillLookup; // ❌ 会被序列化,增大存档/网络包体积
}
// 错误2在 [MemoryPackOnDeserialized] 中执行重量级初始化
[MemoryPackOnDeserialized]
public void OnAfterDeserialize()
{
RebuildSkillLookup(); // ❌ 网络同步的每次反序列化都会触发
RecalcAllTerritoryBonuses(); // ❌ 高频反序列化时造成卡顿
}
// 错误3修改序列化字段的类型或移除字段
public uint LegionId; // ❌ 如果改名或移除,旧存档无法反序列化
```
### 2.4 序列化兼容性检查清单
每次涉及 `[MemoryPackable]` 类的修改,必须检查:
- [ ] 新增字段是否加了 `[MemoryPackIgnore]`
- [ ] 是否修改/删除/重排了已有的序列化字段?(**禁止**
- [ ] 缓存重建逻辑是否在 `[MemoryPackOnDeserialized]` 中以**延迟方式**处理?
- [ ] 旧存档能否正常加载MemoryPack 对新增 Ignore 字段透明)
- [ ] 多人游戏中不同客户端版本能否互通?
### 2.5 网络消息的特殊约束
对于 `CommonActionParams``BaseMessage` 子类:
- **绝不**新增序列化字段(增加每次 Action 的网络开销)
- 如需传递额外数据,使用已有字段编码(如 `Value1`-`Value4` 通用数值字段)
- 对于高频消息心跳、Action确认任何字节增长都会累积
### 2.6 反序列化重建的性能规范
```
[MemoryPackOnDeserialized] 回调中:
├── ✅ 可以做:设置标志位、置空缓存引用
├── ✅ 可以做O(1) 的简单赋值
├── ⚠️ 谨慎做O(n) 的字典/集合重建(仅在必要时)
└── ❌ 禁止做O(n²) 或更复杂的计算、涉及其他对象的查询
```
理由:网络同步时,`ForceUpdateMessage``GameStartMessage` 会反序列化完整 `MapData`,触发所有子对象的 `[MemoryPackOnDeserialized]` 回调。如果每个回调都做重量级初始化,会造成瞬间卡顿。
---
## 第三章 GC优化标准
### 3.1 集合分配优化
#### 3.1.1 复用集合替代重复创建
**问题模式**:在频繁调用的方法中 `new List<T>()` / `new HashSet<T>()` / `new Dictionary<K,V>()`
```csharp
// ❌ 每次调用都分配新集合
public List<GridData> GetAroundGridData(GridData center, int radius)
{
List<GridData> result = new List<GridData>(); // GC 分配
// ... 填充 result
return result;
}
// ✅ 方案A调用方传入缓冲区
public void GetAroundGridData(GridData center, int radius, List<GridData> buffer)
{
buffer.Clear();
// ... 填充 buffer
}
// ✅ 方案B类级别复用集合非线程安全单线程游戏逻辑可用
private static readonly List<GridData> _sharedGridBuffer = new List<GridData>(64);
public List<GridData> GetAroundGridData(GridData center, int radius)
{
_sharedGridBuffer.Clear();
// ... 填充 _sharedGridBuffer
return _sharedGridBuffer; // 注意:调用方不应持有引用或修改
}
```
**适用范围**
| 方法 | 当前问题 | 建议方案 |
|------|---------|---------|
| `GridMapData.GetAroundGridData()` (4个重载) | 每次 new List/HashSet | 传入缓冲区 |
| `GridMapData.GetAroundGridIdList()` | 每次 new List\<uint\> | 传入缓冲区 |
| `MapData.GetPlayerTerritoryGridIdSet()` | 每次 new HashSet + new List | 玩家级缓存 + 脏标记 |
| `MapData.GetUnitDataListByPlayerId()` | null 参数时 new List | 必须传入缓冲区 |
| `MapData.GetUnitDataListByCityId()` | null 参数时 new List | 必须传入缓冲区 |
| `MapData.GetCityDataListByPlayerId()` | null 参数时 new List | 必须传入缓冲区 |
#### 3.1.2 集合预分配容量
```csharp
// ❌ 默认容量,后续频繁扩容
var list = new List<UnitData>();
// ✅ 预分配合理容量
var list = new List<UnitData>(expectedCount);
// ✅ 字典也需要预分配
var dict = new Dictionary<uint, CityData>(CityList.Count);
```
**预分配容量参考**
| 集合用途 | 建议初始容量 |
|---------|-------------|
| 格子周围列表radius=1 | 9 |
| 格子周围列表radius=2 | 25 |
| 玩家城市列表 | 8 |
| 玩家部队列表 | 32 |
| 城市领土格子集合 | 64 |
| 技能列表 | 8 |
#### 3.1.3 消除默认参数创建集合
```csharp
// ❌ 可选参数导致隐式分配
public void GetUnitDataListByPlayerId(uint pid, List<UnitData> list = null)
{
if (list == null) list = new List<UnitData>(); // 隐式 GC 分配
// ...
}
// ✅ 强制调用方提供缓冲区(消除隐式分配)
public void GetUnitDataListByPlayerId(uint pid, List<UnitData> buffer)
{
buffer.Clear();
// ...
}
```
### 3.2 字符串优化
#### 3.2.1 避免热路径中的字符串操作
```csharp
// ❌ 循环中拼接字符串
foreach (var unit in units)
{
string key = "Unit_" + unit.Id.ToString(); // 每次循环产生 2 次分配
Debug.Log($"Processing {key}"); // 插值字符串再分配
}
// ✅ 调试日志使用条件编译
#if UNITY_EDITOR
foreach (var unit in units)
{
Debug.Log($"Processing Unit_{unit.Id}");
}
#endif
// ✅ 需要运行时字符串时使用 StringBuilder 复用
private static readonly StringBuilder _sb = new StringBuilder(256);
public string GetDebugInfo()
{
_sb.Clear();
_sb.Append("Unit_").Append(unit.Id);
return _sb.ToString(); // 仅产生一次分配
}
```
#### 3.2.2 枚举转字符串缓存
```csharp
// ❌ 每次调用都装箱 + 反射
string name = skillType.ToString(); // 装箱 + 内部反射查找
// ✅ 预建枚举字符串映射表
private static readonly Dictionary<SkillType, string> _skillTypeNames;
static SkillHelper()
{
var values = (SkillType[])Enum.GetValues(typeof(SkillType));
_skillTypeNames = new Dictionary<SkillType, string>(values.Length);
foreach (var v in values) _skillTypeNames[v] = v.ToString();
}
public static string GetSkillTypeName(SkillType type) => _skillTypeNames[type];
```
### 3.3 对象创建优化
#### 3.3.1 技能工厂优化SkillFactory
```csharp
// ❌ 当前:每次创建技能都用 Activator.CreateInstance反射开销大
public static SkillBase GetSkillBySkillType(SkillType type)
{
return Activator.CreateInstance(_skillDict[type]) as SkillBase; // 反射 + 装箱
}
// ✅ 使用编译时委托替代反射
private static Dictionary<SkillType, Func<SkillBase>> _skillCreators;
static void InitCreators()
{
_skillCreators = new Dictionary<SkillType, Func<SkillBase>>();
foreach (var kv in _skillDict)
{
var type = kv.Value;
// 编译一次,后续调用零反射
var constructor = type.GetConstructor(Type.EmptyTypes);
var newExp = Expression.New(constructor);
var lambda = Expression.Lambda<Func<SkillBase>>(newExp).Compile();
_skillCreators[kv.Key] = lambda;
}
}
public static SkillBase GetSkillBySkillType(SkillType type)
{
return _skillCreators[type](); // 无反射,接近 new 的性能
}
```
#### 3.3.2 避免闭包和委托分配
```csharp
// ❌ Lambda 捕获外部变量产生闭包对象
int threshold = 5;
var filtered = units.Where(u => u.Level > threshold).ToList(); // 闭包 + List 分配
// ✅ 手动循环,零分配
private static readonly List<UnitData> _tempFilteredUnits = new List<UnitData>(32);
public List<UnitData> GetUnitsAboveLevel(List<UnitData> units, int threshold)
{
_tempFilteredUnits.Clear();
for (int i = 0; i < units.Count; i++)
{
if (units[i].Level > threshold)
_tempFilteredUnits.Add(units[i]);
}
return _tempFilteredUnits;
}
```
### 3.4 LINQ 禁用规范
**在热路径中完全禁止使用 LINQ**,包括但不限于:
| 禁止操作 | 原因 | 替代方案 |
|---------|------|---------|
| `.Where().ToList()` | 分配迭代器 + 新 List | 手动 for 循环 + 预分配 List |
| `.Select().ToArray()` | 分配迭代器 + 新 Array | 手动 for 循环 + 预分配 Array |
| `.Any(predicate)` | 分配闭包对象 | 手动 for 循环 + bool 标志 |
| `.Count(predicate)` | 分配闭包对象 | 手动 for 循环 + int 计数 |
| `.First() / .FirstOrDefault()` | 分配迭代器 | 手动 for 循环 + break |
| `.OrderBy()` | 分配排序缓冲区 | 原地排序或插入排序 |
| `.ToDictionary()` | 分配新 Dictionary | 预分配 + 手动填充 |
| `.Sum() / .Max() / .Min()` | 分配迭代器 | 手动累加 |
**热路径定义**:每帧、每回合、每个 Action、AI 决策循环中执行的代码。
**非热路径**(允许使用 LINQ初始化、UI 事件响应(低频点击)、加载/存档。
### 3.5 装箱避免
```csharp
// ❌ 值类型传给 object 参数
void Log(object value) { } // 调用 Log(42) 会装箱 int
// ❌ 枚举作为字典键(如果没有实现 IEqualityComparer
Dictionary<SkillType, SkillBase> dict; // SkillType 是 enum默认比较器会装箱
// ✅ 为枚举字典提供专用比较器
public struct SkillTypeComparer : IEqualityComparer<SkillType>
{
public bool Equals(SkillType x, SkillType y) => x == y;
public int GetHashCode(SkillType obj) => (int)obj;
}
var dict = new Dictionary<SkillType, SkillBase>(new SkillTypeComparer());
```
### 3.6 struct 使用规范
```csharp
// ✅ 短期使用、小数据量、值语义的数据适合 struct
public struct AttackResult // 战斗结果传递,无需 GC
{
public int Damage;
public bool IsKill;
public bool IsCritical;
}
// ✅ 事件系统已经使用 structUIEvents保持不变
public struct ShowUINotifyMoment // 零 GC 事件分发
{
public MomentType MomentType;
public CityData CityData; // 引用类型成员不影响 struct 本身
}
```
**struct 使用条件**
- 大小不超过 64 字节
- 不需要继承
- 创建后不会被修改(或修改成本可接受)
- 不会被装箱(不存入 object 或接口引用)
---
## 第四章 计算优化标准
### 4.1 缓存 + 脏标记模式
适用于结果稳定、查询频繁但修改不频繁的计算。
```csharp
/// <summary>
/// 玩家领土格子集合缓存。
/// 仅在领土变更时置脏,查询时按需重建。
/// </summary>
[MemoryPackable]
public partial class PlayerData
{
// ===== 已有序列化字段(不动)=====
// ===== 缓存字段 =====
[MemoryPackIgnore] private HashSet<uint> _territoryCache;
[MemoryPackIgnore] private bool _territoryDirty = true;
public HashSet<uint> GetTerritoryGridIdSet(MapData map)
{
if (_territoryDirty || _territoryCache == null)
{
_territoryCache ??= new HashSet<uint>();
_territoryCache.Clear();
// 从城市列表中收集领土
foreach (var city in map.CityMap.CityList)
{
if (map.CityToPlayerDict.TryGetValue(city.Id, out var pid) && pid == Id)
city.Territory.GetAllTerritoryArea(_territoryCache);
}
_territoryDirty = false;
}
return _territoryCache;
}
/// <summary>领土变更时调用(城市升级、征服、丢失城市)</summary>
public void InvalidateTerritoryCache() => _territoryDirty = true;
}
```
**适用场景**
| 缓存对象 | 失效条件 | 查询频率 |
|---------|---------|---------|
| 玩家领土集合 | 城市升级 / 征服 / 丢失 | 每回合多次AI评分、视野、建筑升级 |
| 玩家部队列表 | 部队创建 / 阵亡 / 城市易主 | 每回合多次AI决策 |
| 技能查找字典 | 技能添加 / 移除 | 战斗计算、回合结算 |
| 城市驻军列表 | 部队移动 / 阵亡 | 每次攻击判定 |
### 4.2 查找表优化
#### 4.2.1 O(n) 线性搜索 → O(1) 字典查找
```csharp
// ❌ 当前:遍历所有城市查找领土归属
public bool GetCityDataByTerritoryGridId(uint gid, out CityData cityData)
{
foreach (var city in CityList) // O(城市数)
{
if (city.CheckIsInTerritory(gid)) // O(领土格子数)
{
cityData = city;
return true;
}
}
// ...
}
// ✅ 维护 格子ID → 城市 的反向映射
[MemoryPackIgnore]
private Dictionary<uint, CityData> _gridToCityLookup;
public bool GetCityDataByTerritoryGridId_Fast(uint gid, out CityData cityData)
{
if (_gridToCityLookup == null) RebuildGridToCityLookup();
return _gridToCityLookup.TryGetValue(gid, out cityData); // O(1)
}
// 城市领土变更时调用
public void InvalidateGridToCityLookup() => _gridToCityLookup = null;
```
#### 4.2.2 MapData 中已有的映射表利用
MapData 中已维护以下映射关系,优化时**优先使用已有映射**,避免重复建表:
```
CityToPlayerDict 城市ID → 玩家ID
UnitToCityDict 部队ID → 城市ID
UnitToGridDict 部队ID → 格子ID
CityToGridDict 城市ID → 格子ID
GridToCityDict 格子ID → 城市ID中心格子
```
### 4.3 算法复杂度优化
#### 4.3.1 BFS/邻居搜索优化
`GetAroundGridData` 是最高频的空间查询方法,当前实现为三重嵌套循环:
```csharp
// ❌ 当前triple nested loop + 距离判断冗余
for (int r = 0; r <= radius; r++)
for (int x = center.X - offset; x <= center.X + offset; x++)
for (int y = center.Y - offset; y <= center.Y + offset; y++)
if (Mathf.Max(Mathf.Abs(x-cx), Mathf.Abs(y-cy)) != r) continue;
// ✅ 优化:直接按距离环遍历,消除冗余条件判断
// 对于小 radius1-3使用预计算偏移表
private static readonly Vector2Int[][] _neighborOffsets = PrecomputeOffsets(maxRadius: 5);
public void GetAroundGridData(GridData center, int radius, List<GridData> buffer)
{
buffer.Clear();
buffer.Add(center); // 中心格子
for (int r = 1; r <= radius; r++)
{
var offsets = _neighborOffsets[r];
for (int i = 0; i < offsets.Length; i++)
{
int x = center.Pos.x + offsets[i].x;
int y = center.Pos.y + offsets[i].y;
if (GetGridDataByPos(x, y, out var grid))
buffer.Add(grid);
}
}
}
```
#### 4.3.2 O(n²) 外交计算优化
```csharp
// ❌ 当前O(玩家数²) 的外交关系计算
foreach (var p1 in players)
foreach (var p2 in players)
foreach (var strategy in p1.GetFeelingStrategies(p2))
Calculate(strategy);
// ✅ 优化:增量计算 + 仅在外交事件触发时更新
// 方案:事件驱动的增量更新
public void OnDiplomacyEvent(uint player1Id, uint player2Id)
{
// 只重算涉及的两个玩家的外交关系
RecalcFeelingBetween(player1Id, player2Id);
}
```
#### 4.3.3 视野计算优化
```csharp
// ❌ 当前:每次移动都完整重算所有部队+城市的视野集合
UpdateSight_LogicView(player); // 遍历所有部队、所有城市领土
// ✅ 增量视野更新
public void UpdateSightIncremental(MapData map, PlayerData player,
UnitData movedUnit, Vector2Int oldPos, Vector2Int newPos)
{
// 1. 移除旧位置视野贡献
RemoveSightContribution(movedUnit, oldPos);
// 2. 添加新位置视野贡献
AddSightContribution(movedUnit, newPos);
// 比完整重算快 O(部队数 × 视野范围²) → O(视野范围²)
}
```
### 4.4 循环优化
#### 4.4.1 for 替代 foreach仅对 List\<T\>
```csharp
// ✅ List<T> 使用 for 循环(避免 enumerator 分配,虽然 List 的 enumerator 是 struct
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
// ...
}
// ✅ Dictionary 的 foreach 是安全的struct enumerator
foreach (var kv in dict) { } // OK
// ✅ HashSet 的 foreach 也是安全的struct enumerator
foreach (var item in set) { } // OK
```
#### 4.4.2 提前退出优化
```csharp
// ❌ 遍历完整列表仅为找到一个元素
foreach (var unit in allUnits)
{
if (unit.IsAlive() && unit.OwnerId == playerId)
{
result = unit;
// 没有 break继续遍历...
}
}
// ✅ 找到即退出
foreach (var unit in allUnits)
{
if (unit.IsAlive() && unit.OwnerId == playerId)
{
result = unit;
break;
}
}
```
#### 4.4.3 条件外提
```csharp
// ❌ 循环内重复计算不变量
for (int i = 0; i < units.Count; i++)
{
var grid = map.GridMap.GetGridDataByGid(units[i].GridId);
var player = map.PlayerMap.GetPlayerById(playerId); // 每次循环都查找同一个 player
// ...
}
// ✅ 循环不变量外提
var player = map.PlayerMap.GetPlayerById(playerId);
for (int i = 0; i < units.Count; i++)
{
var grid = map.GridMap.GetGridDataByGid(units[i].GridId);
// ...
}
```
### 4.5 数学计算优化
```csharp
// ❌ 使用 Mathf浮点 + Unity 函数调用开销)
int dist = Mathf.Max(Mathf.Abs(x1 - x2), Mathf.Abs(y1 - y2));
// ✅ 使用纯整数数学
int dist = Math.Max(Math.Abs(x1 - x2), Math.Abs(y1 - y2));
// ❌ 重复计算距离
for (int i = 0; i < targets.Count; i++)
{
float dist = Vector2.Distance(pos, targets[i].Pos); // sqrt 开销
if (dist < minDist) { ... }
}
// ✅ 使用距离平方比较(避免 sqrt
for (int i = 0; i < targets.Count; i++)
{
float distSq = (pos - targets[i].Pos).sqrMagnitude;
if (distSq < minDistSq) { ... }
}
```
---
## 第五章 模块专项优化指南
### 5.1 数据层TH1_Data
| 优化项 | 当前问题 | 优化方案 | 序列化影响 | 优先级 |
|-------|---------|---------|-----------|--------|
| `GetPlayerTerritoryGridIdSet` | 每次调用 new HashSet + new List | 玩家级缓存 + 脏标记 | `[MemoryPackIgnore]` 无影响 | P0 |
| `GetCityDataByTerritoryGridId` | O(城市数×领土数) 线性搜索,代码中标注 TODO | 维护格子→城市反向映射 | `[MemoryPackIgnore]` 无影响 | P0 |
| `GetAroundGridData` (4个重载) | 每次调用 new List/HashSet | 传入缓冲区模式 | 无 | P0 |
| `GetAroundGridIdList` | 每次调用 new List | 传入缓冲区模式 | 无 | P0 |
| `GetUnitDataListByPlayerId` | null 参数时隐式分配 | 强制传入缓冲区 | 无 | P1 |
| `GetCityDataListByPlayerId` | null 参数时隐式分配 | 强制传入缓冲区 | 无 | P1 |
| OnAfterMemoryPackDeserialize | 每次反序列化重建全部字典 | 延迟初始化(首次访问时重建) | 无 | P1 |
### 5.2 技能系统TH1_Logic/Skill
| 优化项 | 当前问题 | 优化方案 | 序列化影响 | 优先级 |
|-------|---------|---------|-----------|--------|
| SkillFactory | 每次创建技能用 Activator.CreateInstance | 编译时委托缓存 | 无 | P1 |
| 技能查找 | Skills 列表 O(n) 线性搜索 | `[MemoryPackIgnore]` 字典缓存 + 脏标记 | 无影响 | P1 |
| GetAttackAdditionParam | 遍历所有技能聚合加成值 | 缓存加成总值 + 技能变更时置脏 | `[MemoryPackIgnore]` 无影响 | P2 |
| OnTurnStart/End 技能回调 | 逐个遍历调用虚方法 | 仅遍历注册了该回调的技能(分类桶) | `[MemoryPackIgnore]` 无影响 | P2 |
### 5.3 AI系统TH1_Logic/AI
| 优化项 | 当前问题 | 优化方案 | 序列化影响 | 优先级 |
|-------|---------|---------|-----------|--------|
| AIActionGenerator.Init | 复制领土到新 List冗余遍历 | 直接引用已有缓存,减少拷贝 | 无 | P0 |
| AILogic 主循环 | 循环内 new List\<uint\>() | 预分配 NodeRecords 池 | 无 | P1 |
| 评分计算器 | 7个集合每次 Init 重建 | 复用集合 + Clear | 无 | P1 |
| AI 合法 Action 生成 | 生成大量临时 CommonActionParams | 对象池复用 | 无 | P2 |
| `validActions.Select().ToArray()` | LINQ 链式分配 | 预分配数组 + 手动填充 | 无 | P1 |
### 5.4 逻辑模块City/Unit/Player Logic
| 优化项 | 当前问题 | 优化方案 | 序列化影响 | 优先级 |
|-------|---------|---------|-----------|--------|
| 视野完整重算 | 每次移动重算全部视野 | 增量视野更新 | 无 | P1 |
| 外交好感度 O(n²) | 每帧/每回合全量重算 | 事件驱动增量更新 | 无 | P2 |
| 领土建筑升级遍历 | 遍历领土两遍(普通+学院/市场) | 单次遍历 + 分类处理 | 无 | P2 |
| BFS 城市连通性 | 每回合完整 BFS | 仅在道路/领土变化时重算 | 无 | P2 |
| 每回合资源计算 | 遍历所有领土格子 | 缓存产出总值 + 建筑变更时增量更新 | `[MemoryPackIgnore]` 无影响 | P3 |
### 5.5 渲染与动画TH1_Renderer / TH1_Anim
| 优化项 | 当前问题 | 优化方案 | 优先级 |
|-------|---------|---------|--------|
| Fragment 创建 | FragmentFactory 每次创建新对象 | 对象池复用 Fragment 实例 | P2 |
| UnitAtomAnim | 每次创建新动画对象 | 对象池复用 | P2 |
| 投射物 | ProjectileRenderer 每次实例化 | 已有 PrefabPool确保使用 | P3 |
### 5.6 事件系统TH1_Core/Events
事件系统已使用 struct 事件,零 GC 分配,**保持现有设计,不需优化**。
---
## 第六章 优化验证清单
### 6.1 每次优化必须验证
- [ ] **功能等价**:优化前后方法的输入参数和返回结果完全一致
- [ ] **单人游戏**:完整对局流程无报错、无异常行为
- [ ] **多人游戏**两个客户端对局Action 同步正确、MapConfirm 校验通过
- [ ] **存档兼容**:用优化前版本创建的存档能被优化后版本正常加载
- [ ] **AI 行为**AI 对局结果分布无异常偏移(相同种子应产生相同决策路径)
### 6.2 性能验证方法
```
1. Unity Profiler → 对比优化前后:
- GC Alloc (bytes/frame)
- 目标方法的 CPU 时间 (ms)
2. Deep Profile 模式 → 确认热路径 GC 分配降为 0
3. 多人对局 → 确认 MapConfirm 哈希始终一致
4. 存档测试 → 加载旧存档 → 进行 10 回合 → 再次存档 → 加载验证
```
### 6.3 回归测试范围
| 优化区域 | 最小测试范围 |
|---------|------------|
| 数据层MapData/GridData | 单人完整对局 + 多人2人对局 |
| 技能系统 | 包含全部8文明的对局 + 英雄技能触发 |
| AI系统 | 8人AI全自动对局200回合 |
| 逻辑模块 | 征服他国 + 联盟 + 奇迹建造 |
| 视野/领土 | 大地图 + 地形多样 |
---
## 附录A 已知GC热点清单
### 按严重程度排序
| # | 热点 | 位置 | 调用频率 | 分配类型 | 严重度 |
|---|------|------|---------|---------|--------|
| 1 | `GetAroundGridData` (4个重载) | GridData.cs | 每回合 100+ 次 | new List / new HashSet | 🔴 严重 |
| 2 | `GetPlayerTerritoryGridIdSet` | MapData.cs | 每回合 3-10 次 | new HashSet + new List | 🔴 严重 |
| 3 | `OnAfterMemoryPackDeserialize` (4处) | CityData/GridData/UnitData | 每次网络同步 | Dictionary 重建 | 🔴 严重 |
| 4 | `AIActionGenerator.Init` | AIActionGenerator.cs | 每个AI回合 | 列表拷贝 + HashSet | 🟡 中等 |
| 5 | `SkillFactory.GetSkillBySkillType` | SkillFactory.cs | 技能创建时 | Activator.CreateInstance | 🟡 中等 |
| 6 | `GetUnitDataListByPlayerId/ByCityId` | MapData.cs | 每回合多次 | null 参数时 new List | 🟡 中等 |
| 7 | AI主循环 NodeRecords | AILogic.cs | 每AI步 | new List\<uint\>() | 🟡 中等 |
| 8 | `validActions.Select().ToArray()` | AILogic.cs | AI 推理时 | LINQ 链分配 | 🟡 中等 |
| 9 | `UpdateTerritoryAllBuildingLevel` | PlayerLogic.cs | 每回合 | 领土集合分配 x2 | 🟢 轻度 |
| 10 | `GetCityDataByTerritoryGridId` | MapData.cs | 按需调用 | 仅 CPU 开销O(n²) | 🟢 轻度 |
---
## 附录B 禁止与允许操作速查表
### 🟢 允许的优化操作
| 操作 | 条件 |
|------|------|
| 新增 `[MemoryPackIgnore]` 字段作为缓存 | 配合延迟初始化和脏标记 |
| 将 `new List<T>()` 改为传入缓冲区模式 | 调用方和被调用方同步修改 |
| 用 for 循环替代 LINQ | 保证结果等价 |
| 新增 static readonly 共享缓冲区 | 仅限单线程游戏逻辑 |
| 预计算偏移表/查找表 | 只读数据,初始化时构建 |
| 对象池复用 | 正确的获取/归还生命周期 |
| 增量计算替代全量计算 | 脏标记在所有修改路径上正确设置 |
### 🔴 禁止的操作
| 操作 | 原因 |
|------|------|
| 向 `[MemoryPackable]` 类新增序列化字段 | 破坏存档兼容性和网络协议 |
| 删除或重命名已有的序列化字段 | 旧存档无法加载 |
| 修改方法的返回值类型或语义 | 违反 R-01 规则 |
| 在缓存中引入浮点累积误差 | 多人同步会出现不一致 |
| 使用多线程/异步优化游戏逻辑计算 | 破坏确定性 |
| 跳过 null 检查以"加速" | 引入崩溃风险 |
| 改变遍历顺序导致不同的执行结果 | 影响确定性AI/战斗/随机数消费) |
### 🟡 需审慎评估的操作
| 操作 | 评估点 |
|------|--------|
| 修改 `[MemoryPackOnDeserialized]` 逻辑 | 确保所有缓存在反序列化后正确重建 |
| static 共享缓冲区 | 确保单线程下无递归/重入问题 |
| 缓存失效策略 | 确保所有修改路径都调用 Invalidate |
| 移除冗余集合 | 确认没有外部引用依赖该集合 |
| 改变内部数据结构(如 List → Array | 确认所有增删操作都适配 |
---
> **文档版本**: 1.0
> **最后更新**: 2026-04-16
> **适用项目**: TH1 回合制4X策略游戏

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -21,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
{
public bool GreaterThan = true;
public float Count = 0;
private static List<GridData> _aroundBuf;
protected override string desc
@ -50,8 +53,10 @@ namespace NodeCanvas.Tasks.Actions
{
var selfCount = 0;
var otherCount = 0;
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
if (unit == null || unit == selfUnit) continue;

View File

@ -23,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
public class AIParamAroundNoUnitCity : BaseActionTask
{
public int Offset;
private static List<GridData> _aroundBuf;
protected override string desc
{
@ -56,8 +57,10 @@ namespace NodeCanvas.Tasks.Actions
Vector2Int[] targetPath = null;
var selfCity = new HashSet<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, selfCity);
var aroundGrid = param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid);
foreach (var grid in aroundGrid)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid, _aroundBuf);
foreach (var grid in _aroundBuf)
{
if (!param.MapData.GetCityDataByGid(grid.Id, out var city)) continue;
if (grid.RealUnit(param.MapData, out var cityUnit)) continue;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -19,7 +21,8 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamAroundOtherCity : BaseActionTask
{
protected override string desc => $"(移动力+射程)周围有敌方城市";
private static List<GridData> _aroundBuf;
protected override string desc=> $"(移动力+射程)周围有敌方城市";
protected override void OnExecute()
@ -40,8 +43,10 @@ namespace NodeCanvas.Tasks.Actions
if (selfGrid != null && selfPlayer != null)
{
var range = selfUnit.GetMoveRange(map) + selfUnit.GetAttackRange(map);
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(range, range, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(range, range, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
if (around == selfGrid) continue;
var city = around.CityOnGrid(map);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -21,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
{
public bool GreaterThan = true;
public float Count = 0;
private static List<GridData> _aroundBuf;
protected override string desc
@ -49,8 +52,10 @@ namespace NodeCanvas.Tasks.Actions
if (selfGrid != null && selfPlayer != null)
{
var count = 0;
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
if (unit == null || unit == selfUnit) continue;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -21,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
{
public bool GreaterThan = true;
public float Count = 0;
private static List<GridData> _aroundBuf;
protected override string desc
@ -49,8 +52,10 @@ namespace NodeCanvas.Tasks.Actions
if (selfGrid != null && selfPlayer != null)
{
var count = 0;
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
if (unit == null || unit == selfUnit) continue;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -21,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
{
public bool GreaterThan = true;
public float Count = 0;
private static List<GridData> _aroundBuf;
protected override string desc
@ -49,8 +52,10 @@ namespace NodeCanvas.Tasks.Actions
if (selfGrid != null && selfPlayer != null)
{
var count = 0;
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
if (unit == null || unit == selfUnit) continue;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
@ -23,6 +24,7 @@ namespace NodeCanvas.Tasks.Actions
public bool GreaterThan = true;
public float Count = 0;
public GridSpType SpType;
private static List<GridData> _aroundBuf;
protected override string desc
@ -50,8 +52,10 @@ namespace NodeCanvas.Tasks.Actions
if (selfGrid != null)
{
var count = 0;
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds) if (around.HasSpType(SpType)) count++;
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf) if (around.HasSpType(SpType)) count++;
if (GreaterThan) EndAction(count >= Count);
else EndAction(count <= Count);
return;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -20,6 +22,7 @@ namespace NodeCanvas.Tasks.Actions
public class AIParamAroundTargetSkillUnit : BaseActionTask
{
public SkillType TargetSkill;
private static List<GridData> _aroundBuf;
protected override string desc => $"目标点附近存在不满血的拥有 {TargetSkill} 的友方单位";
@ -41,9 +44,11 @@ namespace NodeCanvas.Tasks.Actions
var map = data.value.AIActions[i].Param.MapData;
var selfUnit = data.value.AIActions[i].Param.UnitData;
var targetGrid = data.value.AIActions[i].Param.GridData;
var arounds = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _aroundBuf);
var hasTarget = false;
foreach (var around in arounds)
foreach (var around in _aroundBuf)
{
if (around == targetGrid) continue;
around.RealUnit(map, out var unit);

View File

@ -24,7 +24,8 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamDefendTrainUnit : BaseActionTask
{
protected override string desc => string.Format($"空城紧急造兵");
private static List<GridData> _aroundBuf;
protected override string desc=> string.Format($"空城紧急造兵");
protected override void OnExecute()
{
@ -49,8 +50,10 @@ namespace NodeCanvas.Tasks.Actions
bool needTrain = false;
var selfUnit = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(data.value.TargetParam.PlayerData.Id, selfUnit);
var around = map.GridMap.GetAroundGridData(1, 1, cityGrid);
foreach (var grid in around)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, cityGrid, _aroundBuf);
foreach (var grid in _aroundBuf)
{
//TODO check player is right
if (!grid.VisibleUnit(map,player, out var attacker)) continue;

View File

@ -23,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
public class AIParamExplore : BaseActionTask
{
public int Offset = 3;
private static List<GridData> _aroundBuf;
protected override string desc
@ -50,10 +51,12 @@ namespace NodeCanvas.Tasks.Actions
var selfCity = new HashSet<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, selfCity);
var aroundGrids = param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid, _aroundBuf);
var targetGrids = new List<List<GridData>>();
for (int i = 0; i < 4; i++) targetGrids.Add(new List<GridData>());
foreach (var grid in aroundGrids)
foreach (var grid in _aroundBuf)
{
if (grid == unitGrid) continue;
if (grid.Resource == ResourceType.CityCenter && param.MapData.GetCityDataByGid(grid.Id, out var city) &&
@ -96,10 +99,11 @@ namespace NodeCanvas.Tasks.Actions
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (target.Pos.X, target.Pos.Y), param.MapData, param.PlayerData,param.UnitData);
if (!path.found) continue;
aroundGrids = param.MapData.GridMap.GetAroundGridData(3, 3, target);
_aroundBuf.Clear();
param.MapData.GridMap.GetAroundGridData(3, 3, target, _aroundBuf);
var selfScore = 0f;
var enemyScore = 0f;
foreach (var grid in aroundGrids)
foreach (var grid in _aroundBuf)
{
if (unitGrid.Id == grid.Id) continue;
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var unit)) continue;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -19,7 +21,8 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamIsAroundUnitHealthInCondi : BaseActionTask
{
protected override string desc => $"周围存在己方单位兵血量 <= Condi?";
private static List<GridData> _aroundBuf;
protected override string desc=> $"周围存在己方单位兵血量 <= Condi?";
protected override void OnExecute()
@ -43,8 +46,10 @@ namespace NodeCanvas.Tasks.Actions
var selfPlayer = selfUnit.Player(data.value.TargetParam.MapData);
if (selfGrid != null && selfPlayer != null)
{
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
var player = unit.Player(data.value.TargetParam.MapData);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -19,7 +21,8 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamIsAroundUnitInOtherCity : BaseActionTask
{
protected override string desc => $"周围己方单位是否占据了敌方城市?";
private static List<GridData> _aroundBuf;
protected override string desc=> $"周围己方单位是否占据了敌方城市?";
protected override void OnExecute()
@ -38,8 +41,10 @@ namespace NodeCanvas.Tasks.Actions
var selfPlayer = selfUnit.Player(data.value.TargetParam.MapData);
if (selfGrid != null && selfPlayer != null)
{
var arounds = data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
data.value.TargetParam.MapData.GridMap.GetAroundGridData(1, 1, selfGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
around.VisibleUnit(data.value.TargetParam.MapData,selfPlayer,out var unit);
var player = unit.Player(data.value.TargetParam.MapData);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -20,8 +22,9 @@ namespace NodeCanvas.Tasks.Actions
public class AIParamIsTargetGridAroundSkillAttacker : BaseActionTask
{
public SkillType TargetSkill;
private static List<GridData> _aroundBuf;
protected override string desc => "目标格子周围有指定技能敌军";
protected override string desc=> "目标格子周围有指定技能敌军";
protected override void OnExecute()
@ -41,9 +44,11 @@ namespace NodeCanvas.Tasks.Actions
var selfUnit = data.value.AIActions[i].Param.UnitData;
if (!selfUnit.Player(map, out var selfPlayer)) continue;
var targetGrid = data.value.AIActions[i].Param.GridData;
var arounds = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _aroundBuf);
var hasAttacker = false;
foreach (var around in arounds)
foreach (var around in _aroundBuf)
{
if (around == targetGrid) continue;
around.VisibleUnit(map,selfPlayer,out var unit);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -19,6 +21,7 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamIsTargetGridHasAttacker : BaseActionTask
{
private static List<GridData> _aroundBuf;
protected override string desc => "目标格子周围有敌军";
@ -39,9 +42,11 @@ namespace NodeCanvas.Tasks.Actions
var selfUnit = data.value.AIActions[i].Param.UnitData;
if (!selfUnit.Player(map, out var selfPlayer)) continue;
var targetGrid = data.value.AIActions[i].Param.GridData;
var arounds = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _aroundBuf);
var hasAttacker = false;
foreach (var around in arounds)
foreach (var around in _aroundBuf)
{
if (around == targetGrid) continue;
around.VisibleUnit(map,selfPlayer,out var unit);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -19,6 +21,7 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamIsTargetGridHasHeroAndAttacker : BaseActionTask
{
private static List<GridData> _aroundBuf;
protected override string desc => "目标格子周围有英雄且有敌军";
@ -44,10 +47,12 @@ namespace NodeCanvas.Tasks.Actions
continue;
}
var map = data.value.AIActions[i].Param.MapData;
var arounds = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _aroundBuf);
var hasHero = false;
var hasAttacker = false;
foreach (var around in arounds)
foreach (var around in _aroundBuf)
{
around.VisibleUnit(map,selfPlayer,out var unit);
var unitPlayer = unit?.Player(map);

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -21,6 +23,7 @@ namespace NodeCanvas.Tasks.Actions
{
public bool GreaterThan = true;
public float Ratio = 0.5f;
private static List<GridData> _aroundBuf;
protected override string desc
{
@ -54,9 +57,11 @@ namespace NodeCanvas.Tasks.Actions
continue;
}
var map = data.value.AIActions[i].Param.MapData;
var arounds = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _aroundBuf);
var match = false;
foreach (var around in arounds)
foreach (var around in _aroundBuf)
{
around.VisibleUnit(map,selfPlayer,out var unit);
var unitPlayer = unit?.Player(map);

View File

@ -21,7 +21,8 @@ namespace NodeCanvas.Tasks.Actions
[Serializable]
public class AIParamMustGarrison : BaseActionTask
{
protected override string desc => "必须驻守城市";
private static List<GridData> _aroundBuf;
protected override string desc=> "必须驻守城市";
protected override void OnExecute()
{
@ -57,8 +58,10 @@ namespace NodeCanvas.Tasks.Actions
}
var around = map.GridMap.GetAroundGridData(1, 1, unitGrid);
foreach (var grid in around)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, unitGrid, _aroundBuf);
foreach (var grid in _aroundBuf)
{
if (!grid.VisibleUnit(map,player, out var attacker)) continue;
if (selfUnit.Contains(attacker)) continue;

View File

@ -24,6 +24,7 @@ namespace NodeCanvas.Tasks.Actions
public bool MoreThan;
public float Value;
public int Offset;
private static List<GridData> _aroundBuf;
protected override string desc
{
@ -56,8 +57,10 @@ namespace NodeCanvas.Tasks.Actions
}
var dangerValue = 0f;
var arounds = map.GridMap.GetAroundGridData(Offset, Offset, grid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(Offset, Offset, grid, _aroundBuf);
foreach (var around in _aroundBuf)
{
if (around == grid) continue;
if(!around.VisibleUnit(map,player,out var attacker)) continue;

View File

@ -7,9 +7,11 @@
using System;
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
@ -20,6 +22,7 @@ namespace NodeCanvas.Tasks.Actions
public class AIParamTargetCityInAttackRange : BaseActionTask
{
public bool CheckInAttackRange = true;
private static List<GridData> _aroundBuf;
protected override string desc
@ -56,9 +59,11 @@ namespace NodeCanvas.Tasks.Actions
return;
}
var set = param.MapData.GridMap.GetAroundGridData(range, range, unitGrid);
if (CheckInAttackRange) EndAction(set.Contains(param.TargetGridData));
EndAction(!set.Contains(param.TargetGridData));
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
param.MapData.GridMap.GetAroundGridData(range, range, unitGrid, _aroundBuf);
if (CheckInAttackRange) EndAction(_aroundBuf.Contains(param.TargetGridData));
EndAction(!_aroundBuf.Contains(param.TargetGridData));
}
}
}

View File

@ -183,13 +183,12 @@ namespace RuntimeData
}
// 获取目标周围的所有格子(含center)
public List<GridData> GetAroundGridData(int xOffset, int yOffset, GridData gridData)
// 获取目标周围的所有格子(含center) - Buffer版本减少GC
public void GetAroundGridData(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
{
List<GridData> gridDataList = new List<GridData>();
if (gridData == null) return gridDataList;
buffer.Clear();
if (gridData == null) return;
int radius = Mathf.Max(xOffset, yOffset);
//从内圈到外圈按顺序将格子加入list
for (int r = 0; r <= radius; r++)
{
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
@ -198,15 +197,36 @@ namespace RuntimeData
{
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
if (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) continue;
gridDataList.Add(aroundGridData);
buffer.Add(aroundGridData);
}
}
}
}
// 获取目标周围的所有格子(含center) - 兼容版本(会分配新List)
public List<GridData> GetAroundGridData(int xOffset, int yOffset, GridData gridData)
{
var gridDataList = new List<GridData>();
GetAroundGridData(xOffset, yOffset, gridData, gridDataList);
return gridDataList;
}
// 获取目标周围的所有格子(不含center)
// 获取目标周围的所有格子(不含center) - Buffer版本减少GC
public void GetAroundGridDataSet_NOCENTER(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
{
buffer.Clear();
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
{
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
{
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
buffer.Add(aroundGridData);
}
}
}
// 获取目标周围的所有格子(不含center) - 兼容版本(会分配新HashSet)
public HashSet<GridData> GetAroundGridDataSet_NOCENTER(int xOffset, int yOffset, GridData gridData)
{
HashSet<GridData> gridDataSet = new HashSet<GridData>();
@ -223,29 +243,34 @@ namespace RuntimeData
return gridDataSet;
}
// 获取目标周围的所有格子,但是按照优先2468方向返回有序的List
public List<GridData> GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData)
// 获取目标周围的所有格子,按照优先2468方向 - Buffer版本减少GC
public void GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
{
List<GridData> gridDataList = new List<GridData>();
buffer.Clear();
for (int i = 1; i <= 9; i++)
{
//下方的公式使得遍历顺序是 2468 13579 也就是优先bfs上下左右再考虑斜的方向
int dirForHuman = (i <= 4) ? (2 * i) : (2 * (i - 5) + 1);
int dirForComputer = dirForHuman - 1;
int x = gridData.Pos.X + dirForComputer % 3 - 1;
int y = gridData.Pos.Y + dirForComputer / 3 - 1;
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
gridDataList.Add(aroundGridData);
buffer.Add(aroundGridData);
}
}
// 获取目标周围的所有格子,按照优先2468方向 - 兼容版本(会分配新List)
public List<GridData> GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData)
{
var gridDataList = new List<GridData>();
GetAroundGridDataSetByOrder(xOffset, yOffset, gridData, gridDataList);
return gridDataList;
}
public List<uint> GetAroundGridIdList(int radius, GridData gridData, bool remainCenter = false)
// 获取周围格子ID列表 - Buffer版本减少GC
public void GetAroundGridIdList(int radius, GridData gridData, List<uint> buffer, bool remainCenter = false)
{
List<uint> gridIdList = new List<uint>();
//从内圈到外圈按顺序将格子加入list
buffer.Clear();
for (int r = 0; r <= radius; r++)
{
for (int x = gridData.Pos.X - radius; x <= gridData.Pos.X + radius; x++)
@ -255,11 +280,17 @@ namespace RuntimeData
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
if (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) continue;
if (!remainCenter && x == gridData.Pos.X && y == gridData.Pos.Y) continue;
gridIdList.Add(aroundGridData.Id);
buffer.Add(aroundGridData.Id);
}
}
}
}
// 获取周围格子ID列表 - 兼容版本(会分配新List)
public List<uint> GetAroundGridIdList(int radius, GridData gridData, bool remainCenter = false)
{
var gridIdList = new List<uint>();
GetAroundGridIdList(radius, gridData, gridIdList, remainCenter);
return gridIdList;
}
@ -342,6 +373,9 @@ namespace RuntimeData
public TerrainType Terrain;
// 地形层
public TerrainFeature Feature;
// UpdateGeoInfo 复用的HashSet缓冲区
private static HashSet<GeoSmallClass> _geoSmallSetBuffer;
// 植被层
public Vegetation Vegetation;
// 资源层+建筑层
@ -501,10 +535,14 @@ namespace RuntimeData
}
//返回该格子周围是否存在没有视野的格子
[MemoryPackIgnore]
private List<GridData> _aroundBuffer;
public bool UnsightNearby(MapData map, PlayerData player)
{
var gridList = map.GridMap.GetAroundGridData(1, 1, this);
foreach (var grid in gridList)
_aroundBuffer ??= new List<GridData>();
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
foreach (var grid in _aroundBuffer)
{
if (player.Sight.CheckIsInSight(grid.Id)) return true;
}
@ -523,10 +561,11 @@ namespace RuntimeData
CommonColdTime++;
if (CommonColdTime >= 2)
{
var gridList = map.GridMap.GetAroundGridData(1, 1, this);
_aroundBuffer ??= new List<GridData>();
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
if (map.GetPlayerDataByTerritoryGridId(Id, out var player))
foreach (var grid in gridList)
foreach (var grid in _aroundBuffer)
{
if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue;
if (gridPlayer.Id != player.Id) continue;
@ -553,10 +592,11 @@ namespace RuntimeData
CommonColdTime++;
if (CommonColdTime >= 2)
{
var gridList = map.GridMap.GetAroundGridData(1, 1, this);
_aroundBuffer ??= new List<GridData>();
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
if (map.GetPlayerDataByTerritoryGridId(Id, out var player))
foreach (var grid in gridList)
foreach (var grid in _aroundBuffer)
{
if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue;
if (gridPlayer.Id != player.Id) continue;
@ -819,7 +859,8 @@ namespace RuntimeData
}
var smallSet = new HashSet<GeoSmallClass>() { };
var smallSet = _geoSmallSetBuffer ??= new HashSet<GeoSmallClass>();
smallSet.Clear();
uint id;
if (Vegetation == Vegetation.Trees && !forest)

View File

@ -364,9 +364,8 @@ namespace RuntimeData
}
// 通过玩家 ID 找城市列表
public void GetCityDataListByPlayerId(uint pid, List<CityData> cityDataList=null)
public void GetCityDataListByPlayerId(uint pid, List<CityData> cityDataList)
{
if (cityDataList == null) cityDataList = new List<CityData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
@ -376,9 +375,8 @@ namespace RuntimeData
}
// 通过玩家 ID 找城市Set
public void GetCityDataListByPlayerId(uint pid, HashSet<CityData> cityDataList=null)
public void GetCityDataListByPlayerId(uint pid, HashSet<CityData> cityDataList)
{
if (cityDataList == null) cityDataList = new HashSet<CityData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
@ -638,9 +636,8 @@ namespace RuntimeData
}
// 通过城市 ID 找小兵列表
public void GetUnitDataListByCityId(uint cid, List<UnitData> unitDataList=null)
public void GetUnitDataListByCityId(uint cid, List<UnitData> unitDataList)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
foreach (var kv in UnitToCityDict)
{
if (kv.Value != cid) continue;
@ -650,9 +647,8 @@ namespace RuntimeData
}
// 通过玩家 ID 找小兵列表
public void GetUnitDataListByPlayerId(uint pid, List<UnitData> unitDataList=null)
public void GetUnitDataListByPlayerId(uint pid, List<UnitData> unitDataList)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
@ -661,9 +657,8 @@ namespace RuntimeData
}
// 通过城市 ID 找小兵 Set
public void GetUnitDataListByCityId(uint cid, HashSet<UnitData> unitDataList=null)
public void GetUnitDataListByCityId(uint cid, HashSet<UnitData> unitDataList)
{
if (unitDataList == null) unitDataList = new HashSet<UnitData>();
foreach (var kv in UnitToCityDict)
{
if (kv.Value != cid) continue;
@ -673,9 +668,8 @@ namespace RuntimeData
}
// 通过玩家 ID 找小兵 Set
public void GetUnitDataListByPlayerId(uint pid, HashSet<UnitData> unitDataList=null)
public void GetUnitDataListByPlayerId(uint pid, HashSet<UnitData> unitDataList)
{
if (unitDataList == null) unitDataList = new HashSet<UnitData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
@ -684,9 +678,8 @@ namespace RuntimeData
}
// 通过玩家 ID 找敌方小兵列表
public void GetOtherUnitDataListByPlayerId(uint pid, List<UnitData> unitDataList=null)
public void GetOtherUnitDataListByPlayerId(uint pid, List<UnitData> unitDataList)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value == pid) continue;
@ -1016,16 +1009,23 @@ namespace RuntimeData
return false;
}
// 获取玩家的所有领土格子 ID
public HashSet<uint> GetPlayerTerritoryGridIdSet(uint pid)
// 获取玩家的所有领土格子 ID - Buffer版本
private readonly List<CityData> _territoryTmpCityList = new List<CityData>();
public void GetPlayerTerritoryGridIdSet(uint pid, HashSet<uint> gridSet)
{
var gridSet = new HashSet<uint>();
var cityList = new List<CityData>();
GetCityDataListByPlayerId(pid, cityList);
foreach (var cityData in cityList)
_territoryTmpCityList.Clear();
GetCityDataListByPlayerId(pid, _territoryTmpCityList);
foreach (var cityData in _territoryTmpCityList)
{
cityData.Territory.GetAllTerritoryArea(gridSet);
}
}
// 获取玩家的所有领土格子 ID - 兼容版本(会分配新集合)
public HashSet<uint> GetPlayerTerritoryGridIdSet(uint pid)
{
var gridSet = new HashSet<uint>();
GetPlayerTerritoryGridIdSet(pid, gridSet);
return gridSet;
}

View File

@ -338,6 +338,13 @@ namespace RuntimeData
// 玩家是否存活要用这条来判断
public bool IsSurvival => !IsSurrender && Alive;
// 复用的临时集合避免每回合外交计算GC
[MemoryPackIgnore] private HashSet<UnitData> _tmpUnitSetBuf;
[MemoryPackIgnore] private HashSet<UnitData> _tmpUnitSetBuf2;
[MemoryPackIgnore] private HashSet<uint> _tmpOriginCityBuf;
[MemoryPackIgnore] private List<CityData> _tmpCityListBuf;
[MemoryPackIgnore] private HashSet<CityData> _tmpCitySetBuf;
// 无参数初始化
[MemoryPackConstructor]
public PlayerData()
@ -659,11 +666,15 @@ namespace RuntimeData
maxScorePlayer = player.Id;
}
var originCity = new HashSet<uint>();
_tmpOriginCityBuf ??= new HashSet<uint>();
_tmpOriginCityBuf.Clear();
foreach (var player in map.PlayerMap.PlayerDataList)
{
originCity.Add(player.CradleCityId);
_tmpOriginCityBuf.Add(player.CradleCityId);
}
_tmpUnitSetBuf ??= new HashSet<UnitData>();
_tmpUnitSetBuf2 ??= new HashSet<UnitData>();
foreach (var player in map.PlayerMap.PlayerDataList)
{
@ -736,19 +747,19 @@ namespace RuntimeData
// 强大的 弱小的
var selfScore = 0f;
var playerScore = 0f;
var selfUnit = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(Id, selfUnit);
var targetUnit = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, targetUnit);
foreach (var unit in selfUnit) selfScore += unit.GetMilitary();
foreach (var unit in targetUnit) playerScore += unit.GetMilitary();
if (selfScore < playerScore)
var playerScore2 = 0f;
_tmpUnitSetBuf.Clear();
map.GetUnitDataListByPlayerId(Id, _tmpUnitSetBuf);
_tmpUnitSetBuf2.Clear();
map.GetUnitDataListByPlayerId(player.Id, _tmpUnitSetBuf2);
foreach (var unit in _tmpUnitSetBuf) selfScore += unit.GetMilitary();
foreach (var unit in _tmpUnitSetBuf2) playerScore2 += unit.GetMilitary();
if (selfScore < playerScore2)
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Powerful);
}
if (selfScore > playerScore)
if (selfScore > playerScore2)
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Weak);
@ -766,13 +777,13 @@ namespace RuntimeData
}
// 威胁的
var playerUnit = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, playerUnit);
_tmpUnitSetBuf2.Clear();
map.GetUnitDataListByPlayerId(player.Id, _tmpUnitSetBuf2);
var selfTerritory = map.GetPlayerTerritoryGridIdSet(Id);
foreach (var gridId in selfTerritory)
{
if (!map.GridMap.GetGridDataByGid(gridId, out var gridData)) continue;
foreach (var unit in playerUnit)
foreach (var unit in _tmpUnitSetBuf2)
{
if (!map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var dis = map.GridMap.CalcDistance(unitGrid, gridData);
@ -805,14 +816,15 @@ namespace RuntimeData
var count = 0f;
if (maxScorePlayer == player.Id)
{
var otherCity = new List<CityData>();
map.GetCityDataListByPlayerId(player.Id, otherCity);
foreach (var city in otherCity)
_tmpCityListBuf ??= new List<CityData>();
_tmpCityListBuf.Clear();
map.GetCityDataListByPlayerId(player.Id, _tmpCityListBuf);
foreach (var city in _tmpCityListBuf)
{
if (originCity.Contains(city.Id)) count++;
if (_tmpOriginCityBuf.Contains(city.Id)) count++;
}
if (count >= originCity.Count * 0.5f)
if (count >= _tmpOriginCityBuf.Count * 0.5f)
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Dominative);
@ -846,9 +858,10 @@ namespace RuntimeData
// 如果上一回合回合内双方未发生战斗,则该回合开始时变为中立关系
//判断有没有对方单位站在我方city上
bool otherPlayerOnSelfCity = false;
var cityList = new HashSet<CityData>();
map.GetCityDataListByPlayerId(Id,cityList);
foreach (var city in cityList)
_tmpCitySetBuf ??= new HashSet<CityData>();
_tmpCitySetBuf.Clear();
map.GetCityDataListByPlayerId(Id,_tmpCitySetBuf);
foreach (var city in _tmpCitySetBuf)
{
if(!map.GetGridDataByCityId(city.Id,out var grid))continue;
if(!map.GetUnitDataByGid_Core(grid.Id,out var unit))continue;
@ -1429,6 +1442,7 @@ namespace RuntimeData
public List<UnitFullType> HeroList;
public GiantType LeaderGiantType;
public Dictionary<UnitFullType, HeroTaskContentBase> HeroTaskDict;
[MemoryPackIgnore] private List<GridData> _tmpGridListBuf;
[MemoryPackConstructor]
public PlayerHeroData()
@ -1487,9 +1501,11 @@ namespace RuntimeData
map.GetCapitalCityDataByPlayerId(player.Id, out var city);
var cityGrid = city?.Grid(map);
if (cityGrid == null) continue;
var gridList = map.GridMap.GetAroundGridData(1, 1, cityGrid);
_tmpGridListBuf ??= new List<GridData>();
_tmpGridListBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, cityGrid, _tmpGridListBuf);
var randomList = new List<GridData>();
foreach (var grid in gridList)
foreach (var grid in _tmpGridListBuf)
{
if (grid == cityGrid) continue;
if (grid.RealUnit(map,out _)) continue;

View File

@ -218,7 +218,7 @@ namespace RuntimeData
private void RefreshSkillDict()
{
if (_skillDict == null) _skillDict = new Dictionary<SkillType, SkillBase>();
_skillDict ??= new Dictionary<SkillType, SkillBase>();
if (_skillDict.Count != Skills.Count)
{
_skillDict.Clear();
@ -235,10 +235,16 @@ namespace RuntimeData
foreach (var skill in Skills) _skillDict[skill.GetSkillType()] = skill;
}
// 共享的技能迭代缓冲区避免每次OnSkillsTurnStart/End都new List
[MemoryPackIgnore]
private List<SkillBase> _skillIterBuffer;
protected virtual void OnSkillsTurnStart(MapData map)
{
var copy = new List<SkillBase>(Skills);
foreach (var skill in copy)
_skillIterBuffer ??= new List<SkillBase>();
_skillIterBuffer.Clear();
_skillIterBuffer.AddRange(Skills);
foreach (var skill in _skillIterBuffer)
{
skill.BeforeTurnStart();
if (skill.IsFinished())
@ -253,8 +259,10 @@ namespace RuntimeData
protected virtual void OnSkillsAfterTurnStart(MapData map)
{
var copy = new List<SkillBase>(Skills);
foreach (var skill in copy)
_skillIterBuffer ??= new List<SkillBase>();
_skillIterBuffer.Clear();
_skillIterBuffer.AddRange(Skills);
foreach (var skill in _skillIterBuffer)
{
skill.OnAfterTurnStart(this, map);
}
@ -262,8 +270,10 @@ namespace RuntimeData
protected virtual void OnSkillsTurnEnd(MapData map)
{
var copy = new List<SkillBase>(Skills);
foreach (var skill in copy) skill.OnTurnEnd(this, map);
_skillIterBuffer ??= new List<SkillBase>();
_skillIterBuffer.Clear();
_skillIterBuffer.AddRange(Skills);
foreach (var skill in _skillIterBuffer) skill.OnTurnEnd(this, map);
}
}
}

View File

@ -95,11 +95,15 @@ namespace RuntimeData
}
}
[MemoryPackIgnore]
private List<UnitData> _turnUnitBuffer;
public void OnTurnStart(MapData map, PlayerData player)
{
var units = new List<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, units);
foreach (var unit in units)
_turnUnitBuffer ??= new List<UnitData>();
_turnUnitBuffer.Clear();
map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer);
foreach (var unit in _turnUnitBuffer)
{
Main.UnitLogic.StartNextTurn(map, unit);
unit.OnTurnStart(map);
@ -108,9 +112,10 @@ namespace RuntimeData
public void OnAfterTurnStart(MapData map, PlayerData player)
{
var units = new List<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, units);
foreach (var unit in units)
_turnUnitBuffer ??= new List<UnitData>();
_turnUnitBuffer.Clear();
map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer);
foreach (var unit in _turnUnitBuffer)
{
unit.OnAfterTurnStart(map);
}
@ -118,10 +123,11 @@ namespace RuntimeData
public void OnTurnEnd(MapData map, PlayerData player)
{
var units = new List<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, units);
_turnUnitBuffer ??= new List<UnitData>();
_turnUnitBuffer.Clear();
map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer);
foreach (var unit in units)
foreach (var unit in _turnUnitBuffer)
{
Main.UnitLogic.UnitEndTurn(map, unit);
unit.OnTurnEnd(map);

View File

@ -238,6 +238,12 @@ namespace Logic.AI
public List<UnitData> ForeachUnit;
public List<uint> ForeachLegion;
public List<CityData> ForeachCity;
// 复用的临时集合供静态Calculate方法使用
public List<GridData> AroundGridBuffer;
public HashSet<UnitData> TmpUnitSetBuffer;
public List<CityData> TmpCityListBuffer;
public HashSet<CityData> TmpCitySetBuffer;
public bool IsFinish;
public AIDiffInfo AiDiffInfo;
@ -258,6 +264,10 @@ namespace Logic.AI
ForeachLegion = new List<uint>();
ForeachCity = new List<CityData>();
TargetList = new List<uint>();
AroundGridBuffer = new List<GridData>();
TmpUnitSetBuffer = new HashSet<UnitData>();
TmpCityListBuffer = new List<CityData>();
TmpCitySetBuffer = new HashSet<CityData>();
MilitaryScore = new Dictionary<uint, float>();
DevelopmentScore = new Dictionary<uint, float>();
@ -819,8 +829,9 @@ namespace Logic.AI
{
var citySet = new HashSet<CityData>();
var centerGrid = LegionGrid[kv.Key];
var aroundGrids = Map.GridMap.GetAroundGridData(4, 4, centerGrid);
foreach (var grid in aroundGrids)
AroundGridBuffer.Clear();
Map.GridMap.GetAroundGridData(4, 4, centerGrid, AroundGridBuffer);
foreach (var grid in AroundGridBuffer)
{
if (!Map.GetCityDataByGid(grid.Id, out var cityData)) continue;
citySet.Add(cityData);
@ -1068,9 +1079,10 @@ namespace Logic.AI
}
// 如果2直线距离内交战国的敌军总分-友军总分>=6且自由人距离最近的我方城市中心距离>2则执行撤退战略
var aroundGrids = Map.GridMap.GetAroundGridData(2, 2, unitGrid);
AroundGridBuffer.Clear();
Map.GridMap.GetAroundGridData(2, 2, unitGrid, AroundGridBuffer);
var score = 0f;
foreach (var aroundGrid in aroundGrids)
foreach (var aroundGrid in AroundGridBuffer)
{
if (!aroundGrid.VisibleUnit(Map,Player, out var attacker)) continue;
if (!selfUnit.Contains(attacker)) score += attacker.GetMilitary();
@ -1443,9 +1455,10 @@ namespace Logic.AI
if (self.UnitType == UnitType.Catapult && map.GetGridDataByUnitId(self.Id, out var grid))
{
var gridList = Map.GridMap.GetAroundGridData(1, 1, grid);
AroundGridBuffer.Clear();
Map.GridMap.GetAroundGridData(1, 1, grid, AroundGridBuffer);
bool isSea = true;
foreach (var aroundGrid in gridList)
foreach (var aroundGrid in AroundGridBuffer)
{
if (aroundGrid.Terrain == TerrainType.Land) isSea = false;
}
@ -1563,8 +1576,9 @@ namespace Logic.AI
foreach (var city in selfCity)
{
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var gridList = Map.GridMap.GetAroundGridData(5, 5, cityGrid);
foreach (var grid in gridList)
AroundGridBuffer.Clear();
Map.GridMap.GetAroundGridData(5, 5, cityGrid, AroundGridBuffer);
foreach (var grid in AroundGridBuffer)
{
if (grid.Resource != ResourceType.CityCenter) continue;
if (Map.GetCityDataByGid(grid.Id, out var _)) continue;
@ -1625,8 +1639,9 @@ namespace Logic.AI
{
if (hasCityCenter) break;
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var gridList = Map.GridMap.GetAroundGridData(8, 8, cityGrid);
foreach (var grid in gridList)
AroundGridBuffer.Clear();
Map.GridMap.GetAroundGridData(8, 8, cityGrid, AroundGridBuffer);
foreach (var grid in AroundGridBuffer)
{
if (grid.Resource != ResourceType.CityCenter) continue;
if (Map.GetCityDataByGid(grid.Id, out var _)) continue;
@ -2082,8 +2097,9 @@ namespace Logic.AI
// 检查半径 1, 2, 3 范围内的威胁
for (int r = 1; r <= 3; r++)
{
var aroundGrids = map.GridMap.GetAroundGridData(r, r, grid);
foreach (var aroundGrid in aroundGrids)
AroundGridBuffer.Clear();
map.GridMap.GetAroundGridData(r, r, grid, AroundGridBuffer);
foreach (var aroundGrid in AroundGridBuffer)
{
// 获取该格子上的单位
if (!aroundGrid.VisibleUnit(map,player, out var enemyUnit)) continue;
@ -2120,8 +2136,9 @@ namespace Logic.AI
// 计算单位的最终射程
var attackRange = unit.GetAttackRange(map);
// 检查射程范围内的敌方单位
var aroundGrids = map.GridMap.GetAroundGridData(attackRange, attackRange, grid);
foreach (var aroundGrid in aroundGrids)
AroundGridBuffer.Clear();
map.GridMap.GetAroundGridData(attackRange, attackRange, grid, AroundGridBuffer);
foreach (var aroundGrid in AroundGridBuffer)
{
// 获取该格子上的单位
if (!aroundGrid.VisibleUnit(map,player, out var enemyUnit)) continue;
@ -2157,8 +2174,9 @@ namespace Logic.AI
// 计算单位的最终射程
var attackRange = unit.GetAttackRange(map);
// 检查射程范围内的敌方单位
var aroundGrids = map.GridMap.GetAroundGridData(attackRange, attackRange, grid);
foreach (var aroundGrid in aroundGrids)
AroundGridBuffer.Clear();
map.GridMap.GetAroundGridData(attackRange, attackRange, grid, AroundGridBuffer);
foreach (var aroundGrid in AroundGridBuffer)
{
// 获取该格子上的单位
if (!aroundGrid.VisibleUnit(map,player, out var enemyUnit)) continue;

View File

@ -29,6 +29,10 @@ namespace Logic.AI
public AIActionType ActionType => (AIActionType)(_actionRecord % (int)AIActionType.Max);
private HashSet<uint> _territoryGridSet;
private List<UnitData> _tmpUnitList;
private List<CityData> _tmpCityList;
public void Init(MapData map, PlayerData player)
{
_mapData = map;
@ -40,15 +44,19 @@ namespace Logic.AI
_waitGrids.Clear();
_waitCity.Clear();
var gridSet = map.GetPlayerTerritoryGridIdSet(player.Id);
var unitList = new List<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, unitList);
var cityList = new List<CityData>();
map.GetCityDataListByPlayerId(player.Id, cityList);
foreach (var unit in unitList) _waitUnits.Add(unit);
foreach (var city in cityList) _waitCity.Add(city);
_territoryGridSet ??= new HashSet<uint>();
_territoryGridSet.Clear();
map.GetPlayerTerritoryGridIdSet(player.Id, _territoryGridSet);
_tmpUnitList ??= new List<UnitData>();
_tmpUnitList.Clear();
map.GetUnitDataListByPlayerId(player.Id, _tmpUnitList);
_tmpCityList ??= new List<CityData>();
_tmpCityList.Clear();
map.GetCityDataListByPlayerId(player.Id, _tmpCityList);
foreach (var unit in _tmpUnitList) _waitUnits.Add(unit);
foreach (var city in _tmpCityList) _waitCity.Add(city);
foreach (var grid in _mapData.GridMap.GridList)
if(gridSet.Contains(grid.Id)) _waitGrids.Add(grid);
if(_territoryGridSet.Contains(grid.Id)) _waitGrids.Add(grid);
_actionRecord = 0;
_recordCount = 0;

View File

@ -83,6 +83,13 @@ namespace Logic.AI
private Dictionary<UnitData, float> _unitAttack;
private Dictionary<UnitData, float> _unitDefend;
private bool _hasUnitMoney;
// 复用的临时集合,避免每次计算分配
private HashSet<UnitData> _tmpSelfUnitsSet;
private List<UnitData> _tmpSelfUnitsList;
private List<CityData> _tmpCityListBuf;
private List<GridData> _tmpGridListBuf;
private HashSet<CityData> _tmpCitySetBuf;
public static MapData CalMap;
@ -160,15 +167,16 @@ namespace Logic.AI
{
if (mapData.UnitMap.UnitList.Count == 0) return;
var selfUnits = new HashSet<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
_tmpSelfUnitsSet ??= new HashSet<UnitData>();
_tmpSelfUnitsSet.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsSet);
float score = 0f;
foreach (var unit in mapData.UnitMap.UnitList)
{
if (!unit.IsAlive()) continue;
var unitScore = CalculateOneUnitScore(mapData, playerData, unit) * _cfg.UnitScore;
if (selfUnits.Contains(unit)) score += unitScore;
if (_tmpSelfUnitsSet.Contains(unit)) score += unitScore;
else score -= unitScore;
_unitScore.TryAdd(unit, unitScore);
}
@ -184,15 +192,13 @@ namespace Logic.AI
unitData.GetAllAttackValue(mapData) + unitData.GetAllDefenseValue(mapData);
foreach (var skill in unitData.Skills) unitScore += skill.GetScore();
var selfUnits = new HashSet<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
// 复用 _tmpSelfUnitsSet (由调用方 CalculateUnitScore 已填充)
var restraintScore = 0f;
var restraintUnitCount = 0f;
foreach (var otherUnit in mapData.UnitMap.UnitList)
{
if (!otherUnit.IsAlive()) continue;
if (selfUnits.Contains(otherUnit)) continue;
if (_tmpSelfUnitsSet.Contains(otherUnit)) continue;
restraintScore += GetRestraintScore(mapData,unitData, otherUnit);
restraintUnitCount++;
}
@ -218,8 +224,9 @@ namespace Logic.AI
private void CalculateCityScore(MapData mapData, PlayerData playerData, CalculateResult result)
{
if (mapData.CityMap.CityList.Count == 0) return;
var selfUnits = new HashSet<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
_tmpSelfUnitsSet ??= new HashSet<UnitData>();
_tmpSelfUnitsSet.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsSet);
float score = 0f;
foreach (var city in mapData.CityMap.CityList)
@ -275,14 +282,15 @@ namespace Logic.AI
private void RefreshUnitPos(MapData mapData, PlayerData playerData, CalculateResult result)
{
var selfCities = mapData.GetCityDataSetByPlayerId(playerData.Id);
var selfUnits = new HashSet<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
_tmpSelfUnitsSet ??= new HashSet<UnitData>();
_tmpSelfUnitsSet.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsSet);
foreach (var city in selfCities)
{
foreach (var unit in mapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (_tmpSelfUnitsSet.Contains(unit)) continue;
if (!mapData.GetGridDataByUnitId(unit.Id, out var unitData)) continue;
if (!city.Territory.TerritoryArea.Contains(unitData.Id)) continue;
_unitInCityTerritory.Add(unit);
@ -352,11 +360,12 @@ namespace Logic.AI
private void RefreshUnitExploreScore(MapData mapData, PlayerData playerData, CalculateResult result)
{
if (playerData.Sight.SightGidSet.Count == mapData.GridMap.GridList.Count) return;
var selfUnits = new List<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
if (selfUnits.Count == 0) return;
_tmpSelfUnitsList ??= new List<UnitData>();
_tmpSelfUnitsList.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsList);
if (_tmpSelfUnitsList.Count == 0) return;
foreach (var unit in selfUnits)
foreach (var unit in _tmpSelfUnitsList)
{
if (!mapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
GridData targetGrid = null;
@ -390,24 +399,26 @@ namespace Logic.AI
// 小兵探索村庄评分
private void RefreshUnitExploreCityCenterScore(MapData mapData, PlayerData playerData, CalculateResult result)
{
var selfUnits = new List<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
if (selfUnits.Count == 0) return;
_tmpSelfUnitsList ??= new List<UnitData>();
_tmpSelfUnitsList.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsList);
if (_tmpSelfUnitsList.Count == 0) return;
var targetGrids = new List<GridData>();
_tmpGridListBuf ??= new List<GridData>();
_tmpGridListBuf.Clear();
foreach (var gridData in _canMoveGrid)
{
if (gridData.Resource == ResourceType.CityCenter)
{
if (mapData.GetCityDataByGid(gridData.Id, out var _)) continue;
targetGrids.Add(gridData);
_tmpGridListBuf.Add(gridData);
}
}
if (targetGrids.Count == 0) return;
if (_tmpGridListBuf.Count == 0) return;
foreach (var grid in targetGrids)
foreach (var grid in _tmpGridListBuf)
{
foreach (var unit in selfUnits)
foreach (var unit in _tmpSelfUnitsList)
{
if (!mapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var path = PathFinder.FindPath((int)mapData.MapConfig.Width, (int)mapData.MapConfig.Height, new (unitGrid.Pos.X, unitGrid.Pos.Y), new (grid.Pos.X, grid.Pos.Y), mapData, playerData);
@ -422,20 +433,22 @@ namespace Logic.AI
// 刷新小兵探索遗迹评分
private void RefreshUnitExploreTreasureScore(MapData mapData, PlayerData playerData, CalculateResult result)
{
var selfUnits = new List<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
if (selfUnits.Count == 0) return;
_tmpSelfUnitsList ??= new List<UnitData>();
_tmpSelfUnitsList.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsList);
if (_tmpSelfUnitsList.Count == 0) return;
var targetGrids = new List<GridData>();
_tmpGridListBuf ??= new List<GridData>();
_tmpGridListBuf.Clear();
foreach (var gridData in _canMoveGrid)
{
if (gridData.Resource == ResourceType.Treasure) targetGrids.Add(gridData);
if (gridData.Resource == ResourceType.Treasure) _tmpGridListBuf.Add(gridData);
}
if (targetGrids.Count == 0) return;
if (_tmpGridListBuf.Count == 0) return;
foreach (var grid in targetGrids)
foreach (var grid in _tmpGridListBuf)
{
foreach (var unit in selfUnits)
foreach (var unit in _tmpSelfUnitsList)
{
if (!mapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var path = PathFinder.FindPath((int)mapData.MapConfig.Width, (int)mapData.MapConfig.Height, new (unitGrid.Pos.X, unitGrid.Pos.Y), new (grid.Pos.X, grid.Pos.Y), mapData, playerData);
@ -452,20 +465,22 @@ namespace Logic.AI
{
if (!playerData.TechTree.CheckIfHasTech(TechType.Navigation)) return;
var selfUnits = new List<UnitData>();
mapData.GetUnitDataListByPlayerId(playerData.Id, selfUnits);
if (selfUnits.Count == 0) return;
_tmpSelfUnitsList ??= new List<UnitData>();
_tmpSelfUnitsList.Clear();
mapData.GetUnitDataListByPlayerId(playerData.Id, _tmpSelfUnitsList);
if (_tmpSelfUnitsList.Count == 0) return;
var targetGrids = new List<GridData>();
_tmpGridListBuf ??= new List<GridData>();
_tmpGridListBuf.Clear();
foreach (var gridData in _canMoveGrid)
{
if (gridData.Resource == ResourceType.Starfish) targetGrids.Add(gridData);
if (gridData.Resource == ResourceType.Starfish) _tmpGridListBuf.Add(gridData);
}
if (targetGrids.Count == 0) return;
if (_tmpGridListBuf.Count == 0) return;
foreach (var grid in targetGrids)
foreach (var grid in _tmpGridListBuf)
{
foreach (var unit in selfUnits)
foreach (var unit in _tmpSelfUnitsList)
{
if (!mapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var path = PathFinder.FindPath((int)mapData.MapConfig.Width, (int)mapData.MapConfig.Height, new (unitGrid.Pos.X, unitGrid.Pos.Y), new (grid.Pos.X, grid.Pos.Y), mapData, playerData);
@ -848,9 +863,10 @@ namespace Logic.AI
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var around = param.MapData.GridMap.GetAroundGridData(5, 5, unitGrid);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(5, 5, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var grid in around)
foreach (var grid in data.AroundGridBuffer)
{
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var attackUnit)) continue;
if (attackUnit == param.UnitData) continue;
@ -865,9 +881,10 @@ namespace Logic.AI
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var grid in around)
foreach (var grid in data.AroundGridBuffer)
{
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var attackUnit)) continue;
if (attackUnit == param.UnitData) continue;
@ -883,8 +900,9 @@ namespace Logic.AI
var unit = param.UnitData;
var grid = unit?.Grid(map);
if (unit == null || grid == null) return;
var arounds = map.GridMap.GetAroundGridData(3, 3, grid);
foreach (var around in arounds)
data.AroundGridBuffer.Clear();
map.GridMap.GetAroundGridData(3, 3, grid, data.AroundGridBuffer);
foreach (var around in data.AroundGridBuffer)
{
if (around == grid) continue;
if (!unit.Player(map,out var unitPlayer) || !around.VisibleUnit(map,unitPlayer, out var attacker)) continue;
@ -893,7 +911,7 @@ namespace Logic.AI
if (dis > attacker.GetAttackRange(map)) continue;
result.Score[CalculateType.AroundAttackerDangerMin] -= Table.Instance.CalcDamage(map, attacker, unit);
}
}
}
private static void CalculateGridMiscCreateMountain(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
@ -907,8 +925,9 @@ namespace Logic.AI
private static void CalculateGridMiscGrowTree(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
result.Score[CalculateType.GridMiscGrowTree] = 0;
var aroundGrids = param.MapData.GridMap.GetAroundGridData(1, 1, param.GridData);
foreach (var around in aroundGrids)
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, param.GridData, data.AroundGridBuffer);
foreach (var around in data.AroundGridBuffer)
{
if (around == param.GridData) continue;
if (around.Resource == ResourceType.Sawmill) result.Score[CalculateType.GridMiscGrowTree] += 5;
@ -920,9 +939,10 @@ namespace Logic.AI
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var grid in around)
foreach (var grid in data.AroundGridBuffer)
{
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var attackUnit)) continue;
if (attackUnit == param.UnitData) continue;
@ -937,9 +957,10 @@ namespace Logic.AI
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var grid in around)
foreach (var grid in data.AroundGridBuffer)
{
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var attackUnit)) continue;
if (attackUnit == param.UnitData) continue;
@ -954,9 +975,10 @@ namespace Logic.AI
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var grid in around)
foreach (var grid in data.AroundGridBuffer)
{
if (!grid.VisibleUnit(param.MapData,param.PlayerData, out var unit)) continue;
if (unit == param.UnitData) continue;
@ -995,11 +1017,11 @@ namespace Logic.AI
private static void CalculateLegionDevelopmentKill(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
var score = 0f;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
data.TmpUnitSetBuffer.Clear();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, data.TmpUnitSetBuffer);
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (data.TmpUnitSetBuffer.Contains(unit)) continue;
score += unit.GetMilitary();
}
result.Score[CalculateType.LegionDevelopmentKill] = 1f / (score + 1);
@ -1063,9 +1085,9 @@ namespace Logic.AI
newParam.MainObjectType = MainObjectType.City;
newParam.MapData = param.MapData;
newParam.PlayerData = param.PlayerData;
var cityList = new List<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, cityList);
foreach (var city in cityList)
data.TmpCityListBuffer.Clear();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, data.TmpCityListBuffer);
foreach (var city in data.TmpCityListBuffer)
{
newParam.CityData = city;
newParam.OnParamChanged();
@ -1099,14 +1121,15 @@ namespace Logic.AI
var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (targetGrid.Pos.X, targetGrid.Pos.Y), param.MapData, param.PlayerData,param.UnitData);
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
data.TmpUnitSetBuffer.Clear();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, data.TmpUnitSetBuffer);
data.AroundGridBuffer.Clear();
param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid, data.AroundGridBuffer);
var score = 0f;
foreach (var aroundGrid in around)
foreach (var aroundGrid in data.AroundGridBuffer)
{
if (!aroundGrid.VisibleUnit(param.MapData,param.PlayerData, out var aroundUnit)) continue;
if (!selfUnits.Contains(aroundUnit)) continue;
if (!data.TmpUnitSetBuffer.Contains(aroundUnit)) continue;
score += aroundUnit.GetMilitary();
}
result.Score[CalculateType.LegionDefendMove] = 1 / ((float)path.length + 1) * 1000 + score / 1000f;
@ -1156,13 +1179,13 @@ namespace Logic.AI
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!cityGrid.VisibleUnit(param.MapData,param.PlayerData, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
data.TmpUnitSetBuffer.Clear();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, data.TmpUnitSetBuffer);
var score = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (data.TmpUnitSetBuffer.Contains(unit)) continue;
if (!param.MapData.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
if (!param.PlayerData.LastAttackPlayers.Contains(ownerId) &&
!param.PlayerData.CurAttackPlayers.Contains(ownerId)) continue;
@ -1176,13 +1199,13 @@ namespace Logic.AI
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!cityGrid.VisibleUnit(param.MapData,param.PlayerData, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
data.TmpUnitSetBuffer.Clear();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, data.TmpUnitSetBuffer);
var score = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (data.TmpUnitSetBuffer.Contains(unit)) continue;
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var dis = param.MapData.GridMap.CalcDistance(cityGrid, unitGrid);
if (dis > 3) continue;
@ -1196,13 +1219,13 @@ namespace Logic.AI
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!cityGrid.VisibleUnit(param.MapData,param.PlayerData, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
data.TmpUnitSetBuffer.Clear();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, data.TmpUnitSetBuffer);
var score = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (data.TmpUnitSetBuffer.Contains(unit)) continue;
if (!param.MapData.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
if (!param.PlayerData.LastAttackPlayers.Contains(ownerId) &&
!param.PlayerData.CurAttackPlayers.Contains(ownerId)) continue;

View File

@ -7,7 +7,6 @@
using System.Collections.Generic;
using System.Linq;
using Logic.Action;
using Logic.CrashSight;
using NodeCanvas.BehaviourTrees;
@ -64,6 +63,7 @@ namespace Logic.AI
private int _actionCount;
private List<float> _actionBitCodec;
private int _sameCount;
private List<List<uint>> _nodeRecords;
public static uint CurrentAIPlayerId;
public static Dictionary<uint, List<AIRecord>> AIRecordsDict;
@ -155,17 +155,23 @@ namespace Logic.AI
#endif
var index = 0;
var nodeRecords = new List<List<uint>>();
_nodeRecords ??= new List<List<uint>>();
for (int i = 0; i < _nodeRecords.Count; i++) _nodeRecords[i].Clear();
var nodeRecordsUsed = 0;
while (true)
{
if (MainEditor.Instance.IsEditor && !MainEditor.Instance.IsGo) return;
index++;
if (index > nodeRecords.Count) nodeRecords.Add(new List<uint>());
if (index > nodeRecordsUsed)
{
nodeRecordsUsed = index;
if (index > _nodeRecords.Count) _nodeRecords.Add(new List<uint>());
}
_data.ClearCache();
nodeRecords[index - 1].Add(MainEditor.Instance.BTNodeId);
_nodeRecords[index - 1].Add(MainEditor.Instance.BTNodeId);
_btOwner.UpdateBehaviour();
MainEditor.Instance.IsGo = false;
nodeRecords[index - 1].Add(MainEditor.Instance.BTNodeId);
_nodeRecords[index - 1].Add(MainEditor.Instance.BTNodeId);
if (_data.MaxAiAction != null || _data.IsFinish) break;
if (index > 150)
@ -260,7 +266,9 @@ namespace Logic.AI
{
reward += 10;
}
TrainingDataRecorder.Instance.RecordStep(curPlayer.Id, state, validActions.Select(x => x.ToArray()).ToArray(), packed.ToArray(), reward);
var actionsArray = new float[validActions.Count][];
for (int ai = 0; ai < validActions.Count; ai++) actionsArray[ai] = validActions[ai].ToArray();
TrainingDataRecorder.Instance.RecordStep(curPlayer.Id, state, actionsArray, packed.ToArray(), reward);
}
#endif
@ -349,7 +357,9 @@ namespace Logic.AI
LogSystem.LogError($"占领!!!");
reward += 10;
}
TrainingDataRecorder.Instance.RecordStep(curPlayer.Id, state, validActions.Select(x => x.ToArray()).ToArray(), packed.ToArray(), reward);
var actionsArray2 = new float[validActions.Count][];
for (int ai = 0; ai < validActions.Count; ai++) actionsArray2[ai] = validActions[ai].ToArray();
TrainingDataRecorder.Instance.RecordStep(curPlayer.Id, state, actionsArray2, packed.ToArray(), reward);
}
#endif

View File

@ -74,6 +74,8 @@ namespace Logic.Achievement
[Serializable]
public abstract class AchievementConditionBase : ISerializationCallbackReceiver
{
protected static List<GridData> _aroundBuf;
public virtual void OnBeforeSerialize() { }
public virtual void OnAfterDeserialize() { }
@ -484,8 +486,10 @@ namespace Logic.Achievement
if (gridData == null) return false;
_gridId = gridData.Id;
var aroundGrids = map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData);
foreach (var aroundGrid in aroundGrids)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData, _aroundBuf);
foreach (var aroundGrid in _aroundBuf)
{
if (Terrain != TerrainType.None && aroundGrid.Terrain == Terrain) _recordCount++;
if (Feature != TerrainFeature.None && aroundGrid.Feature == Feature) _recordCount++;
@ -571,8 +575,10 @@ namespace Logic.Achievement
if (gridData == null) return false;
_gridId = gridData.Id;
var aroundGrids = map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData);
foreach (var aroundGrid in aroundGrids)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData, _aroundBuf);
foreach (var aroundGrid in _aroundBuf)
{
if (aroundGrid.Wonder == AroundWonder) _recordCount++;
}
@ -650,12 +656,14 @@ namespace Logic.Achievement
if (gridData == null) return false;
_gridId = gridData.Id;
var aroundGrids = map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData, _aroundBuf);
_recordCount = 0;
var selfUnits = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(map.PlayerMap.SelfPlayerId, selfUnits);
foreach (var aroundGrid in aroundGrids)
foreach (var aroundGrid in _aroundBuf)
{
if (!aroundGrid.RealUnit(map, out var unit)) continue;
if (selfUnits.Contains(unit)) continue;
@ -734,12 +742,14 @@ namespace Logic.Achievement
if (gridData == null) return false;
_gridId = gridData.Id;
var aroundGrids = map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData, _aroundBuf);
_recordCount = 0;
var selfUnits = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(map.PlayerMap.SelfPlayerId, selfUnits);
foreach (var aroundGrid in aroundGrids)
foreach (var aroundGrid in _aroundBuf)
{
if (!aroundGrid.RealUnit(map, out var unit)) continue;
if (!selfUnits.Contains(unit)) continue;
@ -818,10 +828,12 @@ namespace Logic.Achievement
if (gridData == null) return false;
_gridId = gridData.Id;
var aroundGrids = map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
map.GridMap.GetAroundGridData(OffsetX, OffsetY, gridData, _aroundBuf);
_recordCount = 0;
foreach (var aroundGrid in aroundGrids)
foreach (var aroundGrid in _aroundBuf)
{
if (!map.GetCityDataByTerritoryGid(aroundGrid.Id, out var city)) continue;
if (city.CityInfo != TargetCity) continue;

View File

@ -1038,6 +1038,9 @@ namespace Logic.Action
set => _duration = value;
}
// 所有ActionLogic共享的临时集合行为是顺序执行的不会并发
protected static List<GridData> _sharedAroundBuf;
public ActionLogicBase(CommonActionId id)
{
_actionId = id;

View File

@ -76,8 +76,10 @@ namespace Logic.Action
actionParams.GridData.Feature = TerrainFeature.Road;
//更新周围各自的道路图案
var tlist = actionParams.MapData.GridMap.GetAroundGridData(1, 1, actionParams.GridData);
foreach (var tgrid in tlist)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, actionParams.GridData, _sharedAroundBuf);
foreach (var tgrid in _sharedAroundBuf)
tgrid.Renderer(actionParams.MapData)?.InstantUpdateGrid(true);
}
//处理bridge或者road
@ -88,8 +90,9 @@ namespace Logic.Action
actionParams.GridData.Resource = ResourceType.Bridge;
//更新周围各自的道路图案
var tlist = actionParams.MapData.GridMap.GetAroundGridData(1, 1, actionParams.GridData);
foreach (var tgrid in tlist)
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, actionParams.GridData, _sharedAroundBuf);
foreach (var tgrid in _sharedAroundBuf)
tgrid.Renderer(actionParams.MapData)?.InstantUpdateGrid(true);
}
//处理temple
@ -582,8 +585,10 @@ namespace Logic.Action
{
var map = actionParam.MapData;
var grid = actionParam.GridData;
var list = map.GridMap.GetAroundGridData(1, 1, grid);
foreach (var g in list)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var g in _sharedAroundBuf)
if (g.Resource == _actionId.ResourceType)
return true;
return false;
@ -978,9 +983,11 @@ namespace Logic.Action
if (grid.Feature == TerrainFeature.Mountain) return false;
if (grid.Vegetation == Vegetation.Trees) return false;
if (!(grid.Resource is ResourceType.None or ResourceType.Fruit or ResourceType.Crop)) return false;
var list = actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
int waterSide = 0;
foreach (var g in list)
foreach (var g in _sharedAroundBuf)
{
if (Mathf.Abs(g.Pos.X - grid.Pos.X) + Mathf.Abs(g.Pos.Y - grid.Pos.Y) != 1) continue;
if (g.Terrain is TerrainType.ShallowSea) waterSide++;
@ -995,9 +1002,11 @@ namespace Logic.Action
return false;
if (grid.Terrain != TerrainType.ShallowSea) return false;
if (!(grid.Resource is ResourceType.None or ResourceType.Fish or ResourceType.Starfish)) return false;
var list = actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
int landSide = 0;
foreach (var g in list)
foreach (var g in _sharedAroundBuf)
{
if (Mathf.Abs(g.Pos.X - grid.Pos.X) + Mathf.Abs(g.Pos.Y - grid.Pos.Y) != 1) continue;
if (g.Terrain is TerrainType.Land) landSide++;

View File

@ -781,8 +781,10 @@ namespace Logic.Action
var unit = actionParams.UnitData;
if (map == null || unit == null) return false;
if (!map.GetGridDataByUnitId(actionParams.UnitData.Id, out var targetGrid)) return false;
var roundGrid = map.GridMap.GetAroundGridData(1, 1, targetGrid);
foreach (var grid in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (!grid.RealUnit(map, out var tunit)) continue;
if(tunit.Id == unit.Id)continue;
@ -831,8 +833,10 @@ namespace Logic.Action
if(selfGrid == null)
if(!actionParams.MapData.GetGridDataByUnitId(selfUnit.Id, out selfGrid)) return false;
if (!actionParams.MapData.GetPlayerDataByUnitId(selfUnit.Id, out var selfPlayer)) return false;
var gridList = actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid);
foreach (var grid in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (grid == selfGrid) continue;
if(!grid.RealUnit(actionParams.MapData,out var targetUnit))continue;
@ -888,8 +892,10 @@ namespace Logic.Action
if (!actionParams.MapData.GetGridDataByUnitId(selfUnit.Id, out selfGrid)) return false;
}
if (!actionParams.MapData.GetPlayerDataByUnitId(selfUnit.Id, out var selfPlayer)) return false;
var gridList = actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid);
foreach (var grid in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (grid == selfGrid) continue;
@ -958,8 +964,10 @@ namespace Logic.Action
if(selfGrid == null)
if(!actionParams.MapData.GetGridDataByUnitId(selfUnit.Id, out selfGrid)) return false;
if (!actionParams.MapData.GetPlayerDataByUnitId(selfUnit.Id, out var selfPlayer)) return false;
var gridList = actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid);
foreach (var grid in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (grid == selfGrid) continue;
if(!grid.RealUnit(actionParams.MapData,out var targetUnit))continue;
@ -1135,8 +1143,10 @@ namespace Logic.Action
if(selfGrid == null)
if(!actionParams.MapData.GetGridDataByUnitId(selfUnit.Id, out selfGrid)) return false;
if (!actionParams.MapData.GetPlayerDataByUnitId(selfUnit.Id, out var selfPlayer)) return false;
var gridList = actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid);
foreach (var grid in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1,1,selfGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (grid.Resource == ResourceType.CityCenter) continue;
if (grid.Terrain != TerrainType.Land) continue;
@ -1250,9 +1260,10 @@ namespace Logic.Action
//Step #2 清除红雾
int count = 0;
var gridSet = actionParams.MapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, grid);
gridSet.Add(grid);
foreach (var tgrid in gridSet)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var tgrid in _sharedAroundBuf)
{
if (!tgrid.HasSpType(GridSpType.RemiliaGrid)) continue;
count++;
@ -1291,10 +1302,11 @@ namespace Logic.Action
//#step #4 check 当前grid是否是remiliaGrid
if (!actionParams.UnitData.Grid(actionParams.MapData, out var grid))return false;
var gridSet = actionParams.MapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, grid);
gridSet.Add(grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
actionParams.MapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
bool hasRemiliaGrid = false;
foreach (var tgrid in gridSet)
foreach (var tgrid in _sharedAroundBuf)
if (tgrid.HasSpType(GridSpType.RemiliaGrid))
{
hasRemiliaGrid = true;
@ -1617,8 +1629,10 @@ namespace Logic.Action
if (unit == null || !unit.Grid(map, out var grid)) return false;
//Step #1 对周围1格范围的单位不包括自己造成2点溅射伤害并添加1层KomeijiFear
var arounds = map.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in arounds)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (!around.RealUnit(map, out var target)) continue;
if (target.Id == unit.Id) continue;

View File

@ -22,6 +22,8 @@ namespace Logic
int explorerStep = 0;
Vector2Int explorerNowPos = new Vector2Int();
CityData explorerCityData;
private List<GridData> _aroundBuf;
public void GenerateTribe(MapData mapData, Vector2Int tpos, int cid)
@ -349,12 +351,14 @@ namespace Logic
{
if (!mapData.GetGridDataByCityId(cityData.Id, out var gridData)) return;
if (!mapData.GetPlayerDataByCityId(cityData.Id, out var playerData)) return;
var aroundGridList = mapData.GridMap.GetAroundGridData(2, 2, gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(2, 2, gridData, _aroundBuf);
Main.PlayerLogic.UpdateSight_LogicView(mapData,playerData,mapData.GridMap.GetAroundGridIdList(2,gridData));
var newTerritoryArea = new List<uint>();
foreach (var aroundGrid in aroundGridList)
foreach (var aroundGrid in _aroundBuf)
{
if (mapData.GetPlayerDataByTerritoryGridId(aroundGrid.Id, out var playerData1)) continue;
@ -398,7 +402,9 @@ namespace Logic
//获得周围一圈的grid必须是我方
var aroundGridList = mapData.GridMap.GetAroundGridData(1,1,gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1,1,gridData, _aroundBuf);
level = 0;
bool ret = false;
@ -427,7 +433,7 @@ namespace Logic
}
//其他建筑处理
foreach (var grid in aroundGridList)
foreach (var grid in _aroundBuf)
{
//如果就是中间这个格子,跳过
//if (grid == gridData) continue;
@ -514,7 +520,7 @@ namespace Logic
}
else
{
foreach (var grid in aroundGridList)
foreach (var grid in _aroundBuf)
{
if (grid == gridData) continue;
if (grid.HasSpType(GridSpType.LeyLine))
@ -570,10 +576,12 @@ namespace Logic
public bool CheckAroundGridHasSomeResourceBelongPlayer(MapData mapData, GridData gridData,PlayerData playerData, ResourceType buildingType)
{
//step #1 获得周围一圈的grid不管是不是我方
var aroundGridList = mapData.GridMap.GetAroundGridData(1,1,gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1,1,gridData, _aroundBuf);
//step #2 逐一判断每个周围的grid
foreach (var grid in aroundGridList)
foreach (var grid in _aroundBuf)
{
//如果不是player的领土continue
if (!mapData.CheckIfGidBelongPid(grid.Id, mapData.PlayerMap.SelfPlayerId)) continue;

View File

@ -147,7 +147,7 @@ namespace Logic.Editor
private GUIStyle _redBoxStyle;
private GUIStyle _whiteBoxStyle;
private string Path = "F:/2026.3.19oss";
private string Path = "";
// 筛选类
private OssStatisticType _statisticType;
@ -194,6 +194,15 @@ namespace Logic.Editor
GUI.skin.button.wordWrap = true;
_barPosition = EditorGUILayout.BeginScrollView(_barPosition);
EditorGUILayout.BeginHorizontal();
Path = EditorGUILayout.TextField("分析文件夹", Path);
if (GUILayout.Button("浏览...", GUILayout.Width(60)))
{
var selected = EditorUtility.OpenFolderPanel("选择分析文件夹", "", "");
if (!string.IsNullOrEmpty(selected)) Path = selected;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
InspectorUtils.InspectorTextWidthRich($"<b> 筛选类型 : </b>");
_statisticType = (OssStatisticType) EditorGUILayout.EnumPopup(_statisticType, GUILayout.Width(200));
@ -381,11 +390,11 @@ namespace Logic.Editor
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
if (collectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _unitResult);
ProcessCollectData(collectData, _unitResult);
}
catch (System.Exception e)
{
@ -425,11 +434,11 @@ namespace Logic.Editor
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
if (collectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _techResult);
ProcessCollectData(collectData, _techResult);
}
catch (System.Exception e)
{
@ -463,11 +472,11 @@ namespace Logic.Editor
try
{
var bytes = File.ReadAllBytes(file);
var ossData = MemoryPackSerializer.Deserialize<OssData>(bytes);
if (ossData?.CollectData == null) continue;
var collectData = MemoryPackSerializer.Deserialize<CollectData>(bytes);
if (collectData == null) continue;
_validFileCount++;
ProcessCollectData(ossData.CollectData, _empireResult);
ProcessCollectData(collectData, _empireResult);
}
catch (System.Exception e)
{

View File

@ -47,6 +47,7 @@ namespace Logic
public class MapGenerator
{
private static List<GridData> _aroundBuf;
private Main _main;
private MapData _mapData;
@ -1016,9 +1017,11 @@ namespace Logic
//获得首都4格内的遗迹视野
if(city.Id !=player.CradleCityId)continue;
var gridList = mapData.GridMap.GetAroundGridData(4, 4, city.Grid(mapData));
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(4, 4, city.Grid(mapData), _aroundBuf);
var gidList = new List<uint>();
foreach(var grid in gridList)
foreach(var grid in _aroundBuf)
if(grid.Resource == ResourceType.Treasure)
gidList.Add(grid.Id);
Main.PlayerLogic.UpdateSight_LogicView(mapData, player, gidList);
@ -1158,19 +1161,21 @@ namespace Logic
if (!mapData.GridMap.GetGridDataByPos(PlayerCivOri[rk].X, PlayerCivOri[rk].Y, out var cradleGrid)) continue;
// 检查2格内是否已有地脉
var aroundGrids = mapData.GridMap.GetAroundGridData(2, 2, cradleGrid);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(2, 2, cradleGrid, _aroundBuf);
bool hasLeyLine = false;
foreach (var g in aroundGrids)
foreach (var g in _aroundBuf)
if (g.HasSpType(GridSpType.LeyLine)) { hasLeyLine = true; break; }
if (hasLeyLine) continue;
// 没有则强制在2格内随机选一个合法格子放置
for (int i = aroundGrids.Count - 1; i > 0; i--)
for (int i = _aroundBuf.Count - 1; i > 0; i--)
{
int j = Random.Range(0, i + 1);
(aroundGrids[i], aroundGrids[j]) = (aroundGrids[j], aroundGrids[i]);
(_aroundBuf[i], _aroundBuf[j]) = (_aroundBuf[j], _aroundBuf[i]);
}
foreach (var g in aroundGrids)
foreach (var g in _aroundBuf)
{
if (g.Terrain != TerrainType.Land) continue;
if (g.Resource == ResourceType.CityCenter) continue;

View File

@ -27,6 +27,10 @@ namespace Logic
private static readonly WonderTypeEnum[] _cachedWonderTypes =
(WonderTypeEnum[])System.Enum.GetValues(typeof(WonderTypeEnum));
// 复用的临时集合
private HashSet<uint> _tmpTerritoryBuf;
private List<GridData> _tmpAroundBuf;
public PlayerLogic(Main main)
{
_main = main;
@ -404,8 +408,10 @@ namespace Logic
//Step #7 更新该城镇3范围内的所有单元格(包括可交互物体的glow以及边界的更新)
if (!mapData.GetGridDataByCityId(cityData.Id, out var grid))
return;
var gridList2 = mapData.GridMap.GetAroundGridData(3, 3, grid);
foreach (var tg in gridList2)
_tmpAroundBuf ??= new List<GridData>();
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(3, 3, grid, _tmpAroundBuf);
foreach (var tg in _tmpAroundBuf)
tg.Renderer(mapData)?.InstantUpdateGrid(true);
List<uint> list = new List<uint>(cityData.Territory.TerritoryArea);
@ -505,10 +511,12 @@ namespace Logic
public List<GridData> GetPlayerResourceNearbyList(MapData mapData, PlayerData playerData, GridData gridData, ResourceType resourceType)
{
var aroundGridList = new List<GridData>();
var aroundGrid = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, gridData);
_tmpAroundBuf ??= new List<GridData>();
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, gridData, _tmpAroundBuf);
//var territoryGidSet = mapData.GetPlayerTerritoryGridIdSet(playerData.Id);
foreach (var grid in aroundGrid)
foreach (var grid in _tmpAroundBuf)
{
//如果不是玩家的领土
if(mapData.GetPlayerDataByTerritoryGridId(grid.Id, out var player)
@ -525,10 +533,12 @@ namespace Logic
public List<GridData> GetPlayerFeatureNearbyList(MapData mapData, PlayerData playerData, GridData gridData, TerrainFeature feature)
{
var aroundGridList = new List<GridData>();
var aroundGrid = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, gridData);
_tmpAroundBuf ??= new List<GridData>();
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, gridData, _tmpAroundBuf);
var territoryGidSet = mapData.GetPlayerTerritoryGridIdSet(playerData.Id);
foreach (var grid in aroundGrid)
foreach (var grid in _tmpAroundBuf)
{
if (!territoryGidSet.Contains(grid.Id)) continue;
if (grid.Feature != feature) continue;
@ -686,8 +696,10 @@ namespace Logic
continue;
// 获取相邻格子
var neighbors = mapData.GridMap.GetAroundGridDataSetByOrder(1, 1, curGrid);
foreach (var neighbor in neighbors)
_tmpAroundBuf ??= new List<GridData>();
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSetByOrder(1, 1, curGrid, _tmpAroundBuf);
foreach (var neighbor in _tmpAroundBuf)
{
uint nextId = neighbor.Id;
@ -731,8 +743,9 @@ namespace Logic
foreach (var tmpStartId in startSet)
{
mapData.GridMap.GetGridDataByGid(tmpStartId, out var tmpStartGrid);
var aroundGridSet = mapData.GridMap.GetAroundGridData(1, 1, tmpStartGrid);
foreach(var tmpAroundGrid in aroundGridSet)
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, tmpStartGrid, _tmpAroundBuf);
foreach(var tmpAroundGrid in _tmpAroundBuf)
if (playerData.PlayerForceId < tmpAroundGrid.WaterRoadForceId.Length
&& tmpAroundGrid.WaterRoadForceId[playerData.PlayerForceId]
&& !visitedSet.Contains(tmpAroundGrid.Id))
@ -793,8 +806,9 @@ namespace Logic
{
grid.Feature = isRoad ? TerrainFeature.Road : TerrainFeature.None;
grid.Renderer(mapData)?.InstantUpdateGrid(true);
var neighbors = mapData.GridMap.GetAroundGridDataSetByOrder(1, 1, grid);
foreach (var nearbyGrid in neighbors)
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSetByOrder(1, 1, grid, _tmpAroundBuf);
foreach (var nearbyGrid in _tmpAroundBuf)
nearbyGrid.Renderer(mapData)?.InstantUpdateGrid(true);
}
@ -815,7 +829,9 @@ namespace Logic
var head = li.Dequeue();
if (!mapData.GridMap.GetGridDataByGid(head, out var headGrid)) continue;
foreach (var aroundGrid in mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, headGrid))
_tmpAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, headGrid, _tmpAroundBuf);
foreach (var aroundGrid in _tmpAroundBuf)
{
//Step #1如果不是我方领土或者中立领土不行
if (mapData.GetPlayerDataByTerritoryGridId(aroundGrid.Id, out var _) &&

View File

@ -36,8 +36,10 @@ namespace Logic.Skill
var selfUnitList = new HashSet<UnitData>();
mapData.GetUnitDataListByPlayerId(player.Id, selfUnitList);
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
foreach (var grid in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (!grid.VisibleUnit(mapData, player,out var unit)) continue;
if (!selfUnitList.Contains(unit)) continue;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
using TH1_Logic.Core;
@ -35,8 +36,10 @@ namespace Logic.Skill
if (!self.IsAlive()) return;
if (!self.Grid(mapData, out var grid)) return;
if (!self.Player(mapData, out var player)) return;
var gridSet = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach(var targetGrid in gridSet)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach(var targetGrid in _sharedAroundBuf)
//周围1格内的盟友
if(targetGrid.VisibleUnit(mapData,player,out var targetUnit) && targetUnit.Player(mapData,out var targetPlayer) && mapData.SameUnion(targetPlayer.Id,player.Id))
{

View File

@ -35,9 +35,11 @@ namespace Logic.Skill
var targetGrid = info.DamageTargetGrid;
if (targetGrid == null) return;
var aroundGrids = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
if (!mapData.GetPlayerDataByUnitId(info.DamageOrigin.Id, out var originPlayer)) return;
foreach (var grid in aroundGrids)
foreach (var grid in _sharedAroundBuf)
{
if (!grid.VisibleUnit(mapData,originPlayer, out var unitData)) continue;
var tplayer = unitData.Player(mapData);

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -33,7 +34,10 @@ namespace Logic.Skill
var grid = self.Grid(mapData);
if (grid == null) return 0f;
var mountainCount = 0;
foreach (var around in mapData.GridMap.GetAroundGridData(1, 1, grid))
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (around.Feature != TerrainFeature.Mountain) continue;
mountainCount++;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -29,9 +30,11 @@ namespace Logic.Skill
public override float GetAttackAdditionParam(MapData mapData, UnitData self, UnitData target = null)
{
var round = mapData.GridMap.GetAroundGridData(1, 1, self.Grid(mapData));
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, self.Grid(mapData), _sharedAroundBuf);
float ret = 0f;
foreach(var grid in round)
foreach(var grid in _sharedAroundBuf)
if (grid.Resource == ResourceType.Academy)
ret += 0.5f;
return ret;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -29,8 +30,10 @@ namespace Logic.Skill
public override float GetAttackAdditionParam(MapData mapData, UnitData self, UnitData target = null)
{
var round = mapData.GridMap.GetAroundGridData(1, 1, self.Grid(mapData));
foreach(var grid in round)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, self.Grid(mapData), _sharedAroundBuf);
foreach(var grid in _sharedAroundBuf)
if (grid.Resource == ResourceType.Academy)
return 0.5f;
return 0;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -29,9 +30,11 @@ namespace Logic.Skill
public override int GetExtraMoveRange(MapData map,UnitData self)
{
var round = map.GridMap.GetAroundGridData(1, 1, self.Grid(map));
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, self.Grid(map), _sharedAroundBuf);
int ret = 0;
foreach(var grid in round)
foreach(var grid in _sharedAroundBuf)
if (grid.Resource == ResourceType.MoriyaMilitary)
ret++;
return ret;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -29,8 +30,10 @@ namespace Logic.Skill
public override int GetExtraMoveRange(MapData map,UnitData self)
{
var round = map.GridMap.GetAroundGridData(1, 1, self.Grid(map));
foreach(var grid in round)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, self.Grid(map), _sharedAroundBuf);
foreach(var grid in _sharedAroundBuf)
if (grid.Resource == ResourceType.MoriyaMilitary)
return 1;
return 0;

View File

@ -35,8 +35,10 @@ namespace Logic.Skill
{
if (self == null || grid == null || mapData == null) return;
var arounds = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in arounds)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (!around.RealUnit(mapData, out var target)) continue;
if (target.Id == self.Id) continue;

View File

@ -32,8 +32,10 @@ namespace Logic.Skill
{
var grid = self.Grid(mapData);
if (grid == null) return;
var aroundGrids = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in aroundGrids)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (around == grid) continue;
around.RealUnit(mapData,out var target);

View File

@ -6,6 +6,7 @@
*/
using System.Collections.Generic;
using RuntimeData;
using Logic.CrashSight;
using TH1_Logic.Core;
@ -110,8 +111,10 @@ namespace Logic.Skill
// 先对周围1格范围造成伤害必须在自伤之前否则自伤死亡会清除grid绑定
// 导致后续DamageSettlement因找不到origin的grid而失败
var arounds = map.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in arounds)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (!around.RealUnit(map, out var target)) continue;
@ -162,8 +165,10 @@ namespace Logic.Skill
// 1层爆炸只有非链内爆炸才传播恐惧
if (canSpread)
{
var arounds = map.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in arounds)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (!around.RealUnit(map, out var target)) continue;

View File

@ -29,9 +29,11 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(selfPlayer.Id, selfUnitList);
var unitsToRefresh = new List<uint>();
var aroundGrids = mapData.GridMap.GetAroundGridData(1, 1, attackInfo.DamageTargetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, attackInfo.DamageTargetGrid, _sharedAroundBuf);
foreach (var grid in aroundGrids)
foreach (var grid in _sharedAroundBuf)
{
if (!grid.RealUnit(mapData, out var unit)) continue;
// 跳过攻击目标本身FearMaker已经处理

View File

@ -44,9 +44,11 @@ namespace Logic.Skill
}
else
{
var gridList = mapData.GridMap.GetAroundGridData(1, 1, info.DamageOriginGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, info.DamageOriginGrid, _sharedAroundBuf);
var randomList = new List<GridData>();
foreach (var grid in gridList)
foreach (var grid in _sharedAroundBuf)
{
if (grid == info.DamageOriginGrid) continue;
if (grid.RealUnit(mapData,out var _)) continue;

View File

@ -34,10 +34,12 @@ namespace Logic.Skill
var self = identifier as UnitData;
if (self == null) return;
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
var hasBuffUnit = new HashSet<UnitData>();
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
hasBuffUnit.Add(unit);

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -37,8 +38,10 @@ namespace Logic.Skill
if (selfPlayer == null) return;
var aroundHero = false;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, grid1);
foreach (var gridData in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid1, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if (!gridData.RealUnit(mapData, out var unit)) continue;
if (!unit.TreatedAsHero(mapData,info.DamageOrigin)) continue;
@ -50,8 +53,9 @@ namespace Logic.Skill
}
if (!aroundHero) return;
aroundHero = false;
roundGrid = mapData.GridMap.GetAroundGridData(1, 1, grid2);
foreach (var gridData in roundGrid)
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid2, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if(!gridData.RealUnit(mapData,out var unit))continue;
if(!unit.TreatedAsHero(mapData,info.DamageOrigin)) continue;

View File

@ -6,6 +6,7 @@
*/
using System.Collections.Generic;
using RuntimeData;
using TH1_Logic.Core;
using System.Linq;
@ -96,8 +97,10 @@ namespace Logic.Skill
if (selfPlayer == null) return;
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
foreach (var gridData in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if (gridData == targetGrid) continue;
if(!gridData.RealUnit(mapData,out var unit))continue;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -34,7 +35,10 @@ namespace Logic.Skill
if (!self.Player(mapData, out var player)) return;
var grid = self.Grid(mapData);
foreach (var around in mapData.GridMap.GetAroundGridData(1, 1, grid))
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (around == grid) continue;
around.VisibleUnit(mapData,player,out var unit);

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -34,8 +35,10 @@ namespace Logic.Skill
public override float GetGridMoveFloor(MapData mapData, UnitData origin, GridData target)
{
if (target == null || origin == null) return 0;
var gridList = mapData.GridMap.GetAroundGridData(1, 1, target);
foreach(var grid in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, target, _sharedAroundBuf);
foreach(var grid in _sharedAroundBuf)
if (grid.RealUnit(mapData, out var unit) && unit.GetSkill(SkillType.MOMIJIPREY, out var _))
//少量在Momijiprey附近的目标可以赦免
return 0;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using MemoryPack;
using RuntimeData;
@ -41,7 +42,10 @@ namespace Logic.Skill
{
var grid = unitData.Grid(mapData);
if (grid == null) return;
foreach (var around in mapData.GridMap.GetAroundGridData(1, 1, grid))
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
around.AddSkill_Legacy(SkillType.GRIDMOMIJIPREY, mapData, false,1,false,-1,false,SpecialAddSkillType.Force,originId);
}

View File

@ -59,8 +59,10 @@ namespace Logic.Skill
{
var newSights = new List<uint>();
if (!self.Player(mapData, out var player)) return newSights;
var aroundGrids = mapData.GridMap.GetAroundGridData(_range, _range, grid);
foreach (var around in aroundGrids)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(_range, _range, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
around.VisibleUnit(mapData,player,out var unit);
if (unit == null || !unit.IsAlive()) continue;

View File

@ -32,9 +32,11 @@ namespace Logic.Skill
var self = identifier as UnitData;
if (self == null) return;
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
unit.AddSkill(SkillType.MOONPRINCESS, mapData);

View File

@ -37,9 +37,11 @@ namespace Logic.Skill
if (!info.IsKill) return;
if (!mapData.GetGridDataByUnitId(info.DamageTarget.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
if (unit == info.DamageTarget) continue;
@ -55,9 +57,11 @@ namespace Logic.Skill
if (self == null) return;
if (Turns < TurnsLimit) return;
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
if (unit == self) continue;

View File

@ -59,8 +59,10 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(player.Id, selfUnitList);
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
foreach (var gridData in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if (!gridData.VisibleUnit(mapData,player, out var unit)) continue;
if (gridData == targetGrid) continue;

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using Logic.CrashSight;
using MemoryPack;
using RuntimeData;
@ -44,8 +45,10 @@ namespace Logic.Skill
//self.GetSkill(SkillType.PATCHOULIEARTH, out var earth);
//earth?.AddLevel(mapData, self, self, 1);
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var gridData in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if (gridData.Terrain == TerrainType.Land) continue;
self.AddSkill_Legacy(SkillType.PATCHOULIWATER, mapData,true,-1,true,1,true,SpecialAddSkillType.AddLevel,self.Id);

View File

@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using Logic.CrashSight;
using MemoryPack;
using RuntimeData;
@ -45,8 +46,10 @@ namespace Logic.Skill
//self.GetSkill(SkillType.PATCHOULIEARTH, out var earth);
//earth?.AddLevel(mapData, self, self, 1);
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var gridData in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var gridData in _sharedAroundBuf)
{
if (gridData.Terrain == TerrainType.Land) continue;
self.AddSkill_Legacy(SkillType.PATCHOULIWATER, mapData,true,-1,true,1,true,SpecialAddSkillType.AddLevel,self.Id);

View File

@ -40,8 +40,10 @@ namespace Logic.Skill
foreach (var vec2 in path)
{
if (!mapData.GridMap.GetGridDataByPos(vec2.x, vec2.y, out var pathGrid)) continue;
var gridSet = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, pathGrid);
foreach(var roundGrid in gridSet)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1, 1, pathGrid, _sharedAroundBuf);
foreach(var roundGrid in _sharedAroundBuf)
{
roundGrid.RealUnit(mapData,out var unit);
if (unit == null || !unit.IsAlive()) continue;

View File

@ -6,6 +6,7 @@
*/
using System.Collections.Generic;
using RuntimeData;
using UnityEngine;
@ -29,9 +30,11 @@ namespace Logic.Skill
{
var grid = self.Grid(mapData);
if (grid == null) return 0;
var arounds = mapData.GridMap.GetAroundGridData(1, 1, grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
var count = 0;
foreach (var around in arounds)
foreach (var around in _sharedAroundBuf)
{
if (!around.HasSpType(GridSpType.RemiliaGrid)) continue;
count++;

View File

@ -58,9 +58,11 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(player.Id, selfUnitList);
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
bool isExcute = false;
foreach (var gridData in roundGrid)
foreach (var gridData in _sharedAroundBuf)
{
gridData.VisibleUnit(mapData,player,out var unit);
if (unit == null || unit == self) continue;

View File

@ -47,9 +47,11 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(player.Id, selfUnitList);
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
bool isExcute = false;
foreach (var gridData in roundGrid)
foreach (var gridData in _sharedAroundBuf)
{
if (targetGrid == gridData) continue;
gridData.VisibleUnit(mapData,player,out var unit);

View File

@ -35,9 +35,11 @@ namespace Logic.Skill
if (info.DamageOrigin == null || info.DamageTarget == null || info.DamageType == DamageType.KillSelf) return;
if (!mapData.GetGridDataByUnitId(info.DamageTarget.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var gridData in roundGrid)
foreach (var gridData in _sharedAroundBuf)
{
gridData.RealUnit(mapData,out var unit);
if (unit == null) continue;

View File

@ -219,8 +219,10 @@ namespace Logic.Skill
ProjectileTypeInfo projInfo = null;
if (targetGrid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(ProjectileType.SanaeOmikuji, out projInfo);
var gridSet = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var round in gridSet)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var round in _sharedAroundBuf)
{
if (!round.RealUnit(mapData, out var unit)) continue;
if (!unit.IsAlive()) continue;
@ -258,8 +260,10 @@ namespace Logic.Skill
ProjectileTypeInfo projInfo = null;
if (targetGrid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(ProjectileType.SanaeOmikuji, out projInfo);
var gridSet = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var round in gridSet)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var round in _sharedAroundBuf)
{
if (!round.RealUnit(mapData, out var unit)) continue;
bool sameUnion = mapData.SameUnionByUnitId(unit.Id, originUnit.Id);

View File

@ -46,7 +46,10 @@ namespace Logic.Skill
if (self == null || !self.IsAlive()) return;
var grid = self.Grid(mapData);
if (grid == null) return;
foreach (var around in mapData.GridMap.GetAroundGridData(3, 3, grid))
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(3, 3, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
around.AddSkill(SkillType.GRIDSANAENINECONTINUEDAMAGE, mapData, self.Id);
}

View File

@ -34,7 +34,10 @@ namespace Logic.Skill
if (path == null || path.Count < 2) return;
int range = self.GetSkill(SkillType.SANAEMOVE,out var _) ? 1: 0 ;
if (!self.Player(mapData, out var player)) return;
foreach (var around in mapData.GridMap.GetAroundGridData(range, range, grid))
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(range, range, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
//if (around == grid) continue;
around.VisibleUnit(mapData,player,out var unit);

View File

@ -6,6 +6,7 @@
*/
using System.Collections.Generic;
using RuntimeData;
using Logic.CrashSight;
using TH1Renderer;
@ -39,8 +40,10 @@ namespace Logic.Skill
var unitsToRefresh = new System.Collections.Generic.List<uint>();
var arounds = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var around in arounds)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var around in _sharedAroundBuf)
{
if (around == grid) continue;
around.RealUnit(mapData,out var target);

View File

@ -46,8 +46,10 @@ namespace Logic.Skill
LogSystem.LogError($"SplashSkill info.DamageTarget is null");
return;
}
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, info.DamageTargetGrid);
foreach (var grid in roundGrid)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, info.DamageTargetGrid, _sharedAroundBuf);
foreach (var grid in _sharedAroundBuf)
{
if (!grid.RealUnit(mapData, out var unit)) continue;

View File

@ -38,8 +38,10 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(selfPlayer.Id, selfUnitList);
//遍历会溅射的所有格子
var roundGrids = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var roundGrid in roundGrids)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var roundGrid in _sharedAroundBuf)
{
var ROgrid = MapRenderer.Instance.ROGridMap[roundGrid.Id];
//如果格子上没有单位,播放地震动画

View File

@ -34,8 +34,10 @@ namespace Logic.Skill
//移动到友方伟人身边的时候才会恢复AP点
if (moveType == MoveType.ActiveMove)
{
var gridList = mapData.GridMap.GetAroundGridData(1, 1, grid);
foreach (var gd in gridList)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, grid, _sharedAroundBuf);
foreach (var gd in _sharedAroundBuf)
{
//如果就是自己这个格子,跳过
if (gd.Id == grid.Id) continue;

View File

@ -34,10 +34,12 @@ namespace Logic.Skill
if (self != null && self is UnitData unitData)
{
if (!mapData.GetGridDataByUnitId(self.Id, out var grid)) return;
var arround = mapData.GridMap.GetAroundGridData(4, 4, grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(4, 4, grid, _sharedAroundBuf);
if (!mapData.GetPlayerDataByUnitId(self.Id, out var player)) return;
var up = new List<uint>();
foreach (var t in arround)
foreach (var t in _sharedAroundBuf)
{
if (t.Resource == ResourceType.Treasure) up.Add(t.Id);
}
@ -47,10 +49,12 @@ namespace Logic.Skill
public override void OnMove(UnitData self, GridData grid, MapData mapData, MoveType moveType, List<Vector2Int> path = null)
{
var arround = mapData.GridMap.GetAroundGridData(4, 4, grid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(4, 4, grid, _sharedAroundBuf);
if (!mapData.GetPlayerDataByUnitId(self.Id, out var player)) return;
var up = new List<uint>();
foreach(var t in arround)
foreach(var t in _sharedAroundBuf)
if (t.Resource == ResourceType.Treasure)
up.Add(t.Id);
Main.PlayerLogic.UpdateSight_LogicView(mapData,player,up);

View File

@ -38,10 +38,12 @@ namespace Logic.Skill
mapData.GetUnitDataListByPlayerId(player.Id, selfUnitList);
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
var hasBuffUnit = new HashSet<UnitData>();
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
if (selfUnitList.Contains(unit)) continue;

View File

@ -54,9 +54,11 @@ namespace Logic.Skill
}
else
{
var gridList = mapData.GridMap.GetAroundGridData(1, 1, info.DamageTargetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, info.DamageTargetGrid, _sharedAroundBuf);
var randomList = new List<GridData>();
foreach (var grid in gridList)
foreach (var grid in _sharedAroundBuf)
{
if (grid == info.DamageTargetGrid) continue;
if (grid.RealUnit(mapData,out var _)) continue;

View File

@ -121,8 +121,10 @@ namespace Logic.Skill
for (int i = 1; i < path.Count; i++)
{
if (!mapData.GridMap.GetGridDataByV2(path[i], out var pathGrid)) continue;
var aroundGrids = mapData.GridMap.GetAroundGridData(1, 1, pathGrid);
foreach (var aroundGrid in aroundGrids)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, pathGrid, _sharedAroundBuf);
foreach (var aroundGrid in _sharedAroundBuf)
{
if (!aroundGrid.RealUnit(mapData, out var splashTarget)) continue;
if (splashTarget.Id == selfUnit.Id) continue;

View File

@ -84,8 +84,10 @@ namespace Logic.Skill
for (int i = 1; i < path.Count; i++)
{
if (!mapData.GridMap.GetGridDataByV2(path[i], out var pathGrid)) continue;
var aroundGrids = mapData.GridMap.GetAroundGridData(1, 1, pathGrid);
foreach (var aroundGrid in aroundGrids)
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, pathGrid, _sharedAroundBuf);
foreach (var aroundGrid in _sharedAroundBuf)
{
if (!aroundGrid.RealUnit(mapData, out var splashTarget)) continue;
if (splashTarget.Id == selfUnit.Id) continue;

View File

@ -32,9 +32,11 @@ namespace Logic.Skill
var self = identifier as UnitData;
if (self == null) return;
if (!mapData.GetGridDataByUnitId(self.Id, out var targetGrid)) return;
var roundGrid = mapData.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
foreach (var grid in roundGrid)
foreach (var grid in _sharedAroundBuf)
{
if (!mapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
unit.AddSkill(SkillType.SPEEDUP, mapData,false,0,false,-1,false,SpecialAddSkillType.AddTurnLimit,0);

View File

@ -108,9 +108,11 @@ namespace Logic.Skill
// 击杀英雄不生成骨堆
if (target.UnitFullType.UnitType != UnitType.Giant)
{
var gridList = map.GridMap.GetAroundGridData(1, 1, targetGrid);
_sharedAroundBuf ??= new List<GridData>();
_sharedAroundBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, targetGrid, _sharedAroundBuf);
var randomList = new List<GridData>();
foreach (var grid in gridList)
foreach (var grid in _sharedAroundBuf)
{
if (grid == targetGrid) continue;
if (grid.RealUnit(map,out _)) continue;

View File

@ -576,6 +576,9 @@ namespace Logic.Skill
[MemoryPackable]
public abstract partial class SkillBase : ISkill
{
// 所有技能共享的临时集合(技能逻辑顺序执行,不并发)
protected static List<GridData> _sharedAroundBuf;
[MemoryPackInclude]
protected bool IsPermanent;
//是否叠层

View File

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
@ -15,30 +16,35 @@ namespace Logic.Skill
{
public class SkillFactory
{
private static Dictionary<SkillType, Type> _skillDict;
// 使用编译后的工厂委托替代 Activator.CreateInstance消除反射开销
private static Dictionary<SkillType, Func<SkillBase>> _skillFactoryDict;
public static SkillBase GetSkillBySkillType(SkillType skillType)
{
if (_skillDict == null)
if (_skillFactoryDict == null)
{
_skillDict = new Dictionary<SkillType, Type>();
Assembly assembly = typeof(SkillBase).Assembly; // 获取基类所在程序集
_skillFactoryDict = new Dictionary<SkillType, Func<SkillBase>>();
Assembly assembly = typeof(SkillBase).Assembly;
foreach (Type skillClassType in assembly.GetTypes())
{
if (skillClassType.IsSubclassOf(typeof(SkillBase)) && !skillClassType.IsAbstract)
{
var skillObj = Activator.CreateInstance(skillClassType);
var skillBase = skillObj as SkillBase;
if (skillBase == null) continue;
_skillDict[skillBase.GetSkillType()] = skillClassType;
var ctor = skillClassType.GetConstructor(Type.EmptyTypes);
if (ctor == null) continue;
// 编译 () => new ConcreteSkillType() 委托,后续调用零反射
var factory = Expression.Lambda<Func<SkillBase>>(
Expression.New(ctor)).Compile();
var tmpSkill = factory();
if (tmpSkill == null) continue;
_skillFactoryDict[tmpSkill.GetSkillType()] = factory;
}
}
}
if (_skillDict.TryGetValue(skillType, out var skillClass))
if (_skillFactoryDict.TryGetValue(skillType, out var skillFactory))
{
var skillObj = Activator.CreateInstance(skillClass) as SkillBase;
var skillObj = skillFactory();
if (skillObj != null && Table.Instance.SkillDataAssets.GetSkillInfo(skillType, out var skillInfo))
{
skillObj.SetSkillPriority(skillInfo.skillPriority);

View File

@ -117,6 +117,10 @@ namespace Logic
bool[,] TransMap;//在常规移动之外,额外赋予的特殊可传送格子
bool[,] SanaeMap;//在常规移动之外,额外赋予的特殊可抵达格子
// 复用的临时集合
private List<GridData> _aroundBuf;
private List<GridData> _aroundBuf2;
public UnitLogic()
@ -907,8 +911,10 @@ namespace Logic
mapData.GetGridDataByUnitId(B.Id, out var gridDataB);
//盟友则不会有控制区域
if (mapData.SameUnion(playerDataB.Id ,playerData.Id)) continue;
var targetGridDataList = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB);
foreach (var targetGridData in targetGridDataList)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB, _aroundBuf);
foreach (var targetGridData in _aroundBuf)
{
int x = targetGridData.Pos.X, y = targetGridData.Pos.Y;
MovementCostMap[x, y] = (MovementCostMap[x, y] < 0) ? -1 : 999;
@ -955,8 +961,10 @@ namespace Logic
breakTime--;
if (!mapData.GridMap.GetGridDataByGid(q.Dequeue(), out var gridDataX)) continue;
if (MovementRemainMap[gridDataX.Pos.X, gridDataX.Pos.Y] < 0.1f) continue;
var nearby = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataX);
foreach (var gridDataY in nearby)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataX, _aroundBuf);
foreach (var gridDataY in _aroundBuf)
{
float cost = MovementCostMap[gridDataY.Pos.X, gridDataY.Pos.Y];
//如果不可达区
@ -1059,8 +1067,10 @@ namespace Logic
{
mapData.GetGridDataByUnitId(unitA.Id, out var gridB);
var nearby = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridB);
foreach (var gridC in nearby){
_aroundBuf2 ??= new List<GridData>();
_aroundBuf2.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridB, _aroundBuf2);
foreach (var gridC in _aroundBuf2){
int xx = gridC.Pos.X, yy = gridC.Pos.Y;
if(CheckUnitAbleForGrid_OfflineStatus(mapData,playerData,unitData,gridC)
&& !gridC.VisibleUnit(mapData,playerData,out var tt))
@ -1268,10 +1278,12 @@ namespace Logic
else if(unitDataA.GetSkill(SkillType.KANAKOBATTLEFIELDPRO,out var _) && unitDataA.GetSkill(SkillType.KANAKOSITTING,out var _))
//不在攻击范围内,有一种可能 kanako
{
var tmpSet = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB);
_aroundBuf2 ??= new List<GridData>();
_aroundBuf2.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB, _aroundBuf2);
bool can = false;
//kanako 的3级技能可以攻击任意远的我方英雄/hebi周围1格敌人
foreach(var tmpGrid in tmpSet)
foreach(var tmpGrid in _aroundBuf2)
if(tmpGrid.VisibleUnit(mapData,playerDataA,out var tmpUnit) && tmpUnit.Player(mapData) == gridDataA.Player(mapData)
&& (tmpUnit.GiantType != GiantType.None ||
(tmpUnit.UnitType == UnitType.MoriyaHebi && tmpUnit.UnitLevel >= 3))
@ -1585,8 +1597,10 @@ namespace Logic
GridData bestGrid = null;
int score = -1;
var gridList = mapData.GridMap.GetAroundGridData(1,1,gridData);
foreach (var targetGrid in gridList)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1,1,gridData, _aroundBuf);
foreach (var targetGrid in _aroundBuf)
{
if (targetGrid == gridData)
continue;

View File

@ -23,6 +23,7 @@ namespace TH1Renderer
public class MapRenderer
{
private Main _main;
private static List<GridData> _aroundBuf;
private MapData _mapData;
private Transform _unitRenderMap;
private Transform _gridRenderMap;
@ -577,9 +578,11 @@ namespace TH1Renderer
//unitLogic.DebugOutputMoveInfo();
int r = Mathf.Max((Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info)?info.MoveRange:0) * 2,
Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info2)?info2.AttackRange:0);
var targetGridDataList = Main.MapData.GridMap.GetAroundGridDataSet_NOCENTER(r,r,gridData);
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
Main.MapData.GridMap.GetAroundGridDataSet_NOCENTER(r,r,gridData, _aroundBuf);
bool hasUtsuhoBase2 = unitData.GetSkill(SkillType.UtsuhoBase, out var _);
foreach(var targetGridData in targetGridDataList)
foreach(var targetGridData in _aroundBuf)
{
//如果不在视野 跳过UtsuhoBase无视视野限制
if (!hasUtsuhoBase2 && !playerData.Sight.CheckIsInSight(targetGridData.Id)) continue;
@ -1022,8 +1025,10 @@ namespace TH1Renderer
{
//如果不是真map不操作
if (mapData != Main.MapData) return false;
var set = mapData.GridMap.GetAroundGridDataSet_NOCENTER(3, 3, grid);
foreach (var aroundGrid in set)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridDataSet_NOCENTER(3, 3, grid, _aroundBuf);
foreach (var aroundGrid in _aroundBuf)
{
//如果是玩家的unit 那么更新高亮
if (aroundGrid.MainSelfPlayerVisibleUnit( out var unit)

View File

@ -17,6 +17,7 @@ namespace TH1_Renderer
{
private uint _unitId;
private UnitData _unitData;
private static List<GridData> _aroundBuf;
private PlayerData _playerData;
private GridData _gridData;
private GameObject _ROUnit;
@ -464,8 +465,10 @@ namespace TH1_Renderer
var curGrid = _unitData.Grid(Main.MapData);
if (_playerData != null && _playerData.IsSelfPlayer() && curGrid != null)
{
var arounds = Main.MapData.GridMap.GetAroundGridData(1, 1, curGrid);
foreach (var around in arounds)
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
Main.MapData.GridMap.GetAroundGridData(1, 1, curGrid, _aroundBuf);
foreach (var around in _aroundBuf)
{
if (around == curGrid) continue;
if (!around.RealUnit(Main.MapData, out var nearUnit)) continue;