非常棒的问题!
是的,**你当前的 `地块生成系统` 还没有集成我们新增的 `边缘队列数据库`**,所以即使你创建了这个数据库,它也不会自动生效,**需要你对生成逻辑进行一些关键性的修改**,以实现:
- 启动时从 `边缘队列数据库` 恢复边缘队列状态
- 生成新地块时更新 `边缘队列数据库`
- 程序退出或每次生成后保存 `边缘队列数据库`
---
## ✅ 修改目标:
1. 引入 `边缘队列数据库` 字段
2. 在 `Start()` 中从数据库加载边缘队列
3. 在生成地块后更新并保存 `边缘队列数据库`
4. 确保队列中不会出现重复坐标
---
## ✅ 修改后的完整代码(已标注修改点)
```csharp
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class 地块生成系统 : MonoBehaviour
{
public 地块数据库 数据库;
public 边缘队列数据库 边缘数据库; // ✅ 新增:边缘队列数据库引用
public 聊天系统 聊天系统;
private Coroutine 生成协程 = null;
private HashSet<(int X, int Y)> 已生成坐标 = new HashSet<(int X, int Y)>();
private Queue<(int X, int Y)> 边缘队列 = new Queue<(int X, int Y)>();
[Header("外出模式生成配置")]
public float 地块生成间隔 = 5f;
[System.Serializable]
public class 生态类型Config
{
public string 名称;
public float 权重;
public string[] 变异型;
public bool Is极低频 => 名称 == "文明遗骸" || 名称 == "乐园" || 名称 == "死寂岭";
}
private 生态类型Config[] 所有生态类型 = new 生态类型Config[]
{
new 生态类型Config { 名称 = "森林", 权重 = 20 },
new 生态类型Config { 名称 = "草原", 权重 = 20 },
new 生态类型Config { 名称 = "荒漠", 权重 = 20 },
new 生态类型Config { 名称 = "咸水", 权重 = 20 },
new 生态类型Config { 名称 = "淡水", 权重 = 20 },
new 生态类型Config { 名称 = "湿地", 权重 = 10 },
new 生态类型Config { 名称 = "地下洞穴", 权重 = 10 },
new 生态类型Config { 名称 = "巨树", 权重 = 3 },
new 生态类型Config { 名称 = "文明遗骸", 权重 = 1 },
new 生态类型Config { 名称 = "乐园", 变异型 = new[] {"森林", "草原", "荒漠", "咸水", "淡水", "湿地", "地下洞穴", "巨树", "文明遗骸"} },
new 生态类型Config { 名称 = "死寂岭", 变异型 = new[] {"森林", "草原", "荒漠", "咸水", "淡水", "湿地", "地下洞穴", "巨树", "文明遗骸"} }
};
private void Start()
{
// ✅ 如果已有地块数据,从数据库恢复已生成坐标
foreach (var 地块 in 数据库.地块列表)
{
已生成坐标.Add((地块.X, 地块.Y));
}
// ✅ 从边缘队列数据库恢复边缘坐标
if (边缘数据库 != null)
{
var 所有坐标 = 边缘数据库.获取所有坐标();
foreach (var coord in 所有坐标)
{
if (!已生成坐标.Contains(coord))
{
边缘队列.Enqueue(coord);
已生成坐标.Add(coord);
}
}
}
}
public void 处于外出模式()
{
if (生成协程 != null)
{
StopCoroutine(生成协程);
生成协程 = null;
}
生成协程 = StartCoroutine(生成单次地块());
}
private IEnumerator 生成单次地块()
{
Debug.Log("🔍 开始一次性地块生成");
yield return StartCoroutine(生成单个地块());
}
private IEnumerator 生成单个地块()
{
Debug.Log("🔍 开始单次地块生成:检查初始地块状态");
if (数据库.地块列表.Count == 0)
{
var 初始地块 = new 地块Data
{
序号 = 1,
X = 0,
Y = 0,
生态类型 = 获取随机生态类型()
};
Debug.Log($"🔍 初始地块生态类型:{初始地块.生态类型},请求AI生成详情");
yield return StartCoroutine(请求AI生成内容(初始地块));
数据库.添加地块(初始地块);
已生成坐标.Add((初始地块.X, 初始地块.Y));
更新边缘队列(初始地块.X, 初始地块.Y);
Debug.Log($"✅ 初始地块生成完成:序号{初始地块.序号},坐标({初始地块.X},{初始地块.Y})");
// ✅ 初始生成后保存边缘队列
保存边缘队列();
}
else
{
if (边缘队列.Count == 0)
{
Debug.LogWarning("⚠️ 地图已填满,无法继续生成地块");
yield break;
}
List<(int X, int Y)> 候选 = new List<(int X, int Y)>();
while (边缘队列.Count > 0)
{
var coord = 边缘队列.Dequeue();
if (!已生成坐标.Contains(coord))
{
候选.Add(coord);
}
}
if (候选.Count == 0)
{
Debug.LogWarning("⚠️ 无法找到可生成的空位,跳过本次生成");
yield break;
}
var coordToUse = 候选[UnityEngine.Random.Range(0, 候选.Count)];
var 新地块 = new 地块Data
{
序号 = 数据库.地块列表.Count + 1,
X = coordToUse.X,
Y = coordToUse.Y,
生态类型 = 获取随机生态类型(coordToUse.X, coordToUse.Y)
};
Debug.Log($"🔍 新地块信息:序号{新地块.序号},坐标({新地块.X},{新地块.Y}),生态类型{新地块.生态类型},请求AI生成详情");
yield return StartCoroutine(请求AI生成内容(新地块));
数据库.添加地块(新地块);
已生成坐标.Add((新地块.X, 新地块.Y));
更新边缘队列(新地块.X, 新地块.Y);
Debug.Log($"✅ 新地块生成完成:序号{新地块.序号},坐标({新地块.X},{新地块.Y})");
// ✅ 生成新地块后保存边缘队列
保存边缘队列();
}
}
private void 保存边缘队列()
{
if (边缘数据库 != null)
{
边缘数据库.清空(); // 清空旧数据
foreach (var coord in 边缘队列)
{
边缘数据库.添加边缘坐标(coord.X, coord.Y);
}
边缘数据库.保存为JSON("地块数据"); // 保存到文件
}
}
private void 更新边缘队列(int x, int y)
{
var 候选 = new List<(int X, int Y)>
{
(x + 1, y),
(x - 1, y),
(x, y + 1),
(x, y - 1)
};
foreach (var coord in 候选)
{
if (!已生成坐标.Contains(coord))
{
边缘队列.Enqueue(coord);
已生成坐标.Add(coord);
}
}
}
private string 获取随机生态类型(int x = 0, int y = 0)
{
bool 周围有极低频 = false;
foreach (var 地块 in 数据库.地块列表)
{
if ((Mathf.Abs(地块.X - x) + Mathf.Abs(地块.Y - y)) == 1 &&
(地块.生态类型.Contains("文明遗骸") || 地块.生态类型.Contains("乐园") || 地块.生态类型.Contains("死寂岭")))
{
周围有极低频 = true;
break;
}
}
List<生态类型Config> 可选类型 = new List<生态类型Config>();
foreach (var 类型 in 所有生态类型)
{
if (类型.Is极低频 && 周围有极低频) continue;
可选类型.Add(类型);
}
float 总权重 = 0;
foreach (var 类型 in 可选类型)
{
总权重 += 类型.权重;
}
float 随机值 = UnityEngine.Random.Range(0f, 总权重);
foreach (var 类型 in 可选类型)
{
if (随机值 <= 类型.权重)
{
if (类型.变异型 != null && 类型.变异型.Length > 0)
{
string 原型 = 类型.变异型[UnityEngine.Random.Range(0, 类型.变异型.Length)];
return $"{类型.名称}({原型})";
}
return 类型.名称;
}
随机值 -= 类型.权重;
}
return "森林";
}
private IEnumerator 请求AI生成内容(地块Data 地块)
{
string 提示词 = $@"你是一个生态科考AI助手,现在要生成一个新的地块信息。
该地块序号为 {地块.序号},坐标为 ({地块.X}, {地块.Y}),生态类型为:{地块.生态类型}。
请根据以下要求生成内容:
- 描述:简洁的环境描述(1~2句话)
- 生物列表:至少3种生物(可以是动物、植物、菌类)
返回格式为 JSON,示例格式如下:
{{
""描述"": """",
""生物列表"": [""狼"", ""兔子"", ""蘑菇""]
}}";
Debug.Log($"📤 发送AI请求(地块{地块.序号}):{提示词}");
AIManager.Instance.发送消息给AI(提示词);
while (AIManager.Instance.当前回复 == null)
{
yield return null;
}
string AI回复 = AIManager.Instance.当前回复;
Debug.Log($"📥 AI返回地块{地块.序号}信息:\n{AI回复}");
try
{
var 数据 = JsonUtility.FromJson<地块Data>(AI回复);
地块.描述 = 数据.描述;
地块.生物列表 = 数据.生物列表;
}
catch (System.Exception ex)
{
Debug.LogError($"❌ 解析AI回复失败(地块{地块.序号}): " + ex.Message);
}
AIManager.Instance.当前回复 = null;
string 开场白提示词 = $@"你是一个生态科考AI助手,现在需要为以下地块生成一段开场白。
地块信息:
- 序号:{地块.序号}
- 坐标:({地块.X}, {地块.Y})
- 生态类型:{地块.生态类型}
- 描述:{地块.描述}
- 生物列表:{string.Join(", ", 地块.生物列表)}
请生成一段开场白,用于用户接受聊天邀请后显示在聊天框中。
";
Debug.Log($"📤 发送AI请求(地块{地块.序号}开场白):{开场白提示词}");
AIManager.Instance.发送消息给AI(开场白提示词);
while (AIManager.Instance.当前回复 == null)
{
yield return null;
}
string 开场白 = AIManager.Instance.当前回复;
Debug.Log($"📥 AI返回地块{地块.序号}开场白:\n{开场白}");
AIManager.Instance.当前回复 = null;
var 临时数据 = new 地块临时信息
{
地块 = 地块,
开场白 = 开场白
};
发出聊天邀请(临时数据);
}
private void 发出聊天邀请(地块临时信息 临时数据)
{
聊天邀请管理器.Instance.显示邀请(临时数据.开场白, () =>
{
聊天系统.打开聊天窗口(临时数据.地块);
});
}
}
```
---
## ✅ 修改说明:
| 修改点 | 说明 |
|--------|------|
| `public 边缘队列数据库 边缘数据库;` | 添加引用 |
| `Start()` 中加载边缘队列 | 从数据库恢复边缘坐标 |
| `保存边缘队列()` 方法 | 每次生成新地块后保存边缘队列 |
| `生成单个地块()` 中调用 `保存边缘队列()` | 实现持久化 |
| `更新边缘队列()` 不变 | 逻辑不变,只是队列来源变为数据库 |
---
## ✅ 使用说明:
1. 在 Unity 编辑器中创建一个 `边缘队列数据库.asset` 文件
2. 将它拖到 `地块生成系统` 的 `边缘数据库` 字段
3. 运行游戏,生成地块后关闭 Unity,再打开时边缘队列应已恢复
---
###