feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
CI / Go Backend (push) Canceled after 0s

初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
xyou
2026-08-08 14:07:56 +08:00
parent 9500c4c80a
commit f70b061d1a
1972 changed files with 159760 additions and 6 deletions
@@ -0,0 +1,140 @@
using System.Collections.Generic;
using UnityEngine;
namespace RedCircuit.AIAssistant
{
/// <summary>
/// 操作执行器,将 AI 操作指令转换为游戏内动作。
/// 所有操作必须经用户确认后才执行,支持撤销。
/// </summary>
public class AIActionExecutor : MonoBehaviour
{
/// <summary>待确认的操作队列</summary>
private readonly Queue<AIAction> _pendingQueue = new();
/// <summary>已执行的操作批次 (用于撤销)</summary>
private readonly Stack<ActionBatch> _executedBatches = new();
/// <summary>单次操作元件上限</summary>
private const int MaxActionsPerBatch = 20;
/// <summary>将 AI 操作加入待确认队列</summary>
public void QueueActions(List<AIAction> actions)
{
if (actions.Count > MaxActionsPerBatch)
{
Debug.LogWarning($"[AIAction] 操作数 {actions.Count} 超过单批上限 {MaxActionsPerBatch},将截断");
actions = actions.GetRange(0, MaxActionsPerBatch);
}
foreach (var action in actions)
{
_pendingQueue.Enqueue(action);
}
// TODO: 在画布上预览待执行操作 (半透明高亮)
PreviewActions(actions);
}
/// <summary>执行队列中所有待确认的操作</summary>
public void ExecuteQueuedActions()
{
if (_pendingQueue.Count == 0) return;
// 记录操作前快照 (用于撤销)
var batch = new ActionBatch();
batch.SnapshotBefore = CaptureCanvasSnapshot();
while (_pendingQueue.TryDequeue(out var action))
{
ExecuteAction(action);
batch.Actions.Add(action);
}
batch.SnapshotAfter = CaptureCanvasSnapshot();
_executedBatches.Push(batch);
Debug.Log($"[AIAction] 已执行 {batch.Actions.Count} 个操作");
ClearPreviews();
}
/// <summary>取消所有待确认的操作</summary>
public void CancelQueuedActions()
{
var count = _pendingQueue.Count;
_pendingQueue.Clear();
ClearPreviews();
Debug.Log($"[AIAction] 已取消 {count} 个待确认操作");
}
/// <summary>撤销上一次 AI 操作批次</summary>
public void UndoLastBatch()
{
if (!_executedBatches.TryPop(out var batch))
{
Debug.LogWarning("[AIAction] 没有可撤销的 AI 操作");
return;
}
// 恢复操作前快照
RestoreCanvasSnapshot(batch.SnapshotBefore);
Debug.Log($"[AIAction] 已撤销 {batch.Actions.Count} 个操作");
}
private void ExecuteAction(AIAction action)
{
switch (action.Type)
{
case ActionType.Place:
Debug.Log($"[AIAction] 放置 {action.Component} 于 ({action.X},{action.Y}) 旋转={action.Rotation}");
// TODO: 调用 PlacementController.PlaceComponent()
break;
case ActionType.Delete:
Debug.Log($"[AIAction] 删除 ({action.X},{action.Y})");
// TODO: 调用 PlacementController.DeleteComponent()
break;
case ActionType.Move:
Debug.Log($"[AIAction] 移动 ({action.FromX},{action.FromY}) -> ({action.X},{action.Y})");
// TODO: 调用 PlacementController.MoveComponent()
break;
case ActionType.Rotate:
Debug.Log($"[AIAction] 旋转 ({action.X},{action.Y}) -> {action.Rotation}");
// TODO: 调用 PlacementController.RotateComponent()
break;
case ActionType.Wire:
Debug.Log($"[AIAction] 连线 ({action.FromX},{action.FromY}) -> ({action.X},{action.Y})");
// TODO: 调用 PlacementController.PlaceWire()
break;
}
}
private void PreviewActions(List<AIAction> actions)
{
// TODO: 在画布上以半透明预览所有待执行操作
foreach (var action in actions)
{
// 创建预览 Ghost 对象
}
}
private void ClearPreviews()
{
// TODO: 清除所有预览 Ghost 对象
}
private string CaptureCanvasSnapshot()
{
// TODO: 序列化当前画布状态为 JSON
return "{}";
}
private void RestoreCanvasSnapshot(string snapshot)
{
// TODO: 从 JSON 快照恢复画布状态
}
}
}
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
namespace RedCircuit.AIAssistant
{
/// <summary>
/// AI 助搭核心协调器,管理 AI 会话生命周期,协调各子系统。
/// </summary>
public class AIAssistantManager : MonoBehaviour
{
public static AIAssistantManager Instance { get; private set; }
/// <summary>当前会话 ID</summary>
public string SessionId { get; private set; } = Guid.NewGuid().ToString("N")[..16];
/// <summary>AI 是否就绪</summary>
public bool IsReady { get; private set; }
/// <summary>聊天历史记录</summary>
public List<ChatMessage> History { get; } = new();
private AIChatController _chatController;
private AICircuitAdvisor _advisor;
private AIActionExecutor _executor;
private AIContextProvider _contextProvider;
private void Awake()
{
if (Instance != null) { Destroy(gameObject); return; }
Instance = this;
}
private void Start()
{
_chatController = GetComponent<AIChatController>() ?? gameObject.AddComponent<AIChatController>();
_advisor = GetComponent<AICircuitAdvisor>() ?? gameObject.AddComponent<AICircuitAdvisor>();
_executor = GetComponent<AIActionExecutor>() ?? gameObject.AddComponent<AIActionExecutor>();
_contextProvider = GetComponent<AIContextProvider>() ?? gameObject.AddComponent<AIContextProvider>();
IsReady = true;
Debug.Log("[AIAssistant] Manager initialized");
}
/// <summary>
/// 处理用户输入的消息。
/// </summary>
public async void HandleUserMessage(string message)
{
if (!IsReady)
{
_chatController.AppendSystemMessage("AI 助手尚未就绪,请稍候...");
return;
}
// 记录用户消息
History.Add(new ChatMessage { Role = MessageRole.User, Content = message, Timestamp = DateTime.Now });
_chatController.AppendUserMessage(message);
// 采集上下文
var context = _contextProvider.CollectContext();
// 发送到服务端 (TODO: 实现网络请求)
var response = await RequestAIChat(message, context);
// 处理响应
ProcessAIResponse(response);
}
private async Task<AIResponse> RequestAIChat(string message, GameContext context)
{
// TODO: 调用 ai-service POST /api/ai/chat
// 使用 SSE 流式接收响应
await Task.Delay(100); // 模拟网络延迟
return new AIResponse
{
Type = ResponseType.Text,
Content = "AI 助搭功能正在开发中,敬请期待!",
Actions = new List<AIAction>(),
RequiresConfirmation = false
};
}
private void ProcessAIResponse(AIResponse response)
{
// 记录 AI 消息
History.Add(new ChatMessage { Role = MessageRole.Assistant, Content = response.Content, Timestamp = DateTime.Now });
switch (response.Type)
{
case ResponseType.Text:
_chatController.AppendAIMessage(response.Content);
break;
case ResponseType.Analysis:
_chatController.AppendAIMessage(response.Content);
_advisor.ShowReport(response.Analysis);
break;
case ResponseType.Action:
_chatController.AppendAIMessage(response.Content);
if (response.RequiresConfirmation && response.Actions.Count > 0)
{
_executor.QueueActions(response.Actions);
_chatController.ShowConfirmationPrompt(response.Actions.Count);
}
break;
case ResponseType.Error:
_chatController.AppendErrorMessage(response.Content);
break;
}
}
/// <summary>用户确认执行 AI 操作</summary>
public void ConfirmActions()
{
_executor.ExecuteQueuedActions();
}
/// <summary>用户拒绝执行 AI 操作</summary>
public void RejectActions()
{
_executor.CancelQueuedActions();
}
/// <summary>撤销上一次 AI 操作</summary>
public void UndoLastAIAction()
{
_executor.UndoLastBatch();
}
}
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace RedCircuit.AIAssistant
{
/// <summary>
/// 聊天 UI 控制器,处理消息收发与流式显示。
/// </summary>
public class AIChatController : MonoBehaviour
{
/// <summary>聊天面板是否可见</summary>
public bool IsVisible { get; private set; }
/// <summary>消息列表 (用于 UI 渲染)</summary>
public List<ChatMessage> Messages { get; } = new();
/// <summary>显示聊天面板</summary>
public void Show()
{
IsVisible = true;
// TODO: 播放面板展开动画
}
/// <summary>隐藏聊天面板</summary>
public void Hide()
{
IsVisible = false;
// TODO: 播放面板收起动画
}
/// <summary>追加用户消息到 UI</summary>
public void AppendUserMessage(string content)
{
var msg = new ChatMessage
{
Role = MessageRole.User,
Content = content,
Timestamp = DateTime.Now
};
Messages.Add(msg);
// TODO: 实例化用户消息气泡 UI
}
/// <summary>追加 AI 消息到 UI (支持流式追加)</summary>
public void AppendAIMessage(string content)
{
var msg = new ChatMessage
{
Role = MessageRole.Assistant,
Content = content,
Timestamp = DateTime.Now
};
Messages.Add(msg);
// TODO: 实例化 AI 消息气泡 UI,支持打字机效果
}
/// <summary>追加系统消息</summary>
public void AppendSystemMessage(string content)
{
var msg = new ChatMessage
{
Role = MessageRole.System,
Content = content,
Timestamp = DateTime.Now
};
Messages.Add(msg);
// TODO: 实例化系统消息 (居中灰色样式)
}
/// <summary>追加错误消息</summary>
public void AppendErrorMessage(string content)
{
AppendSystemMessage($"[错误] {content}");
}
/// <summary>显示操作确认提示</summary>
public void ShowConfirmationPrompt(int actionCount)
{
// TODO: 显示 "AI 建议执行 N 个操作,是否确认?" 确认栏
Debug.Log($"[AIChat] 等待用户确认 {actionCount} 个操作");
}
/// <summary>清空聊天记录</summary>
public void ClearHistory()
{
Messages.Clear();
// TODO: 清空 UI 消息列表
}
}
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using UnityEngine;
namespace RedCircuit.AIAssistant
{
/// <summary>
/// 电路分析与建议,解析 AI 返回的电路方案并展示诊断报告。
/// </summary>
public class AICircuitAdvisor : MonoBehaviour
{
/// <summary>显示电路分析报告</summary>
public void ShowReport(CircuitAnalysisReport report)
{
if (report == null) return;
Debug.Log($"[AIAdvisor] 电路分析: {report.Summary}");
Debug.Log($"[AIAdvisor] 元件数: {report.Metrics.ComponentCount}, 成本: {report.Metrics.TotalCost}, 最大延迟: {report.Metrics.MaxDelay} tick");
foreach (var issue in report.Issues)
{
var icon = issue.Severity == IssueSeverity.Error ? "[ERROR]" : "[WARN]";
Debug.Log($"[AIAdvisor] {icon} ({issue.Location.X},{issue.Location.Y}) {issue.Description}");
if (!string.IsNullOrEmpty(issue.Suggestion))
Debug.Log($"[AIAdvisor] -> 建议: {issue.Suggestion}");
}
foreach (var opt in report.Optimizations)
{
Debug.Log($"[AIAdvisor] [OPT] {opt.Description} ({opt.EstimatedImprovement})");
}
// TODO: 在 UI 上展示分析报告面板
}
/// <summary>显示电路搭建方案供用户选择</summary>
public void ShowCircuitPlan(List<CircuitPlan> plans)
{
foreach (var plan in plans)
{
Debug.Log($"[AIAdvisor] 方案: {plan.Name} | 成本: {plan.Cost} | 延迟: {plan.Delay} tick | 元件: {plan.ComponentCount}");
Debug.Log($"[AIAdvisor] {plan.Description}");
}
// TODO: 在 UI 上展示方案对比卡片
}
/// <summary>高亮显示问题位置</summary>
public void HighlightIssue(GridPosition location, IssueSeverity severity)
{
// TODO: 在画布上高亮显示问题格子
// Error -> 红色边框, Warning -> 黄色边框
}
/// <summary>清除所有高亮</summary>
public void ClearHighlights()
{
// TODO: 移除画布上的所有问题高亮
}
}
}
@@ -0,0 +1,83 @@
using UnityEngine;
namespace RedCircuit.AIAssistant
{
/// <summary>
/// 上下文采集器,收集当前游戏状态供 AI 参考。
/// </summary>
public class AIContextProvider : MonoBehaviour
{
/// <summary>采集当前游戏上下文</summary>
public GameContext CollectContext()
{
var context = new GameContext
{
Mode = GetCurrentMode(),
CanvasWidth = GetCanvasWidth(),
CanvasHeight = GetCanvasHeight(),
ComponentCount = GetComponentCount(),
SelectedComponentId = GetSelectedComponentId(),
SelectedComponentType = GetSelectedComponentType(),
LevelId = GetCurrentLevelId(),
LevelTitle = GetCurrentLevelTitle(),
RedstoneCoins = GetRedstoneCoins()
};
return context;
}
private string GetCurrentMode()
{
// TODO: 从 GameManager 获取当前游戏模式
return "creative"; // creative / puzzle / automation / multiplayer
}
private int GetCanvasWidth()
{
// TODO: 从 GridCanvas 获取画布宽度
return 128;
}
private int GetCanvasHeight()
{
// TODO: 从 GridCanvas 获取画布高度
return 128;
}
private int GetComponentCount()
{
// TODO: 从 CircuitGraph 获取当前元件数
return 0;
}
private string GetSelectedComponentId()
{
// TODO: 从 PlacementController 获取选中元件 ID
return null;
}
private string GetSelectedComponentType()
{
// TODO: 从 PlacementController 获取选中元件类型
return null;
}
private int? GetCurrentLevelId()
{
// TODO: 解谜模式下获取当前关卡 ID
return null;
}
private string GetCurrentLevelTitle()
{
// TODO: 解谜模式下获取当前关卡标题
return null;
}
private long GetRedstoneCoins()
{
// TODO: 从 EconomyManager 获取红石币余额
return 0;
}
}
}
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
namespace RedCircuit.AIAssistant
{
// ==================== 消息模型 ====================
public enum MessageRole { User, Assistant, System }
[Serializable]
public class ChatMessage
{
public MessageRole Role;
public string Content;
public DateTime Timestamp;
}
// ==================== 响应模型 ====================
public enum ResponseType { Text, Analysis, Action, Error }
[Serializable]
public class AIResponse
{
public ResponseType Type;
public string Content;
public List<AIAction> Actions = new();
public bool RequiresConfirmation;
public CircuitAnalysisReport Analysis;
}
// ==================== 操作指令模型 ====================
public enum ActionType { Place, Delete, Move, Rotate, Wire }
[Serializable]
public class AIAction
{
public ActionType Type;
public string Component; // 元件类型 (Place 时使用)
public int X; // 目标 X 坐标
public int Y; // 目标 Y 坐标
public int FromX; // 源 X 坐标 (Move/Wire 时使用)
public int FromY; // 源 Y 坐标 (Move/Wire 时使用)
public int Rotation; // 旋转角度 (0/90/180/270)
public string Description; // 操作描述 (供用户确认时显示)
}
// ==================== 上下文模型 ====================
[Serializable]
public class GameContext
{
public string Mode; // creative / puzzle / automation / multiplayer
public int CanvasWidth;
public int CanvasHeight;
public int ComponentCount;
public string SelectedComponentId;
public string SelectedComponentType;
public int? LevelId;
public string LevelTitle;
public long RedstoneCoins;
}
// ==================== 电路分析模型 ====================
[Serializable]
public class CircuitAnalysisReport
{
public string Summary;
public CircuitMetrics Metrics;
public List<CircuitIssue> Issues = new();
public List<CircuitOptimization> Optimizations = new();
}
[Serializable]
public class CircuitMetrics
{
public int ComponentCount;
public int TotalCost;
public int MaxDelay;
public int SignalPaths;
}
public enum IssueSeverity { Error, Warning, Info }
[Serializable]
public class CircuitIssue
{
public IssueSeverity Severity;
public string Type; // signal_loss / short_circuit / redundant / etc.
public GridPosition Location;
public string Description;
public string Suggestion;
}
[Serializable]
public class CircuitOptimization
{
public string Type; // delay_reduction / cost_reduction / etc.
public string Description;
public string EstimatedImprovement;
}
// ==================== 电路方案模型 ====================
[Serializable]
public class CircuitPlan
{
public string Name;
public string Description;
public int Cost;
public int Delay;
public int ComponentCount;
public List<AIAction> Steps = new();
}
// ==================== 辅助类型 ====================
[Serializable]
public struct GridPosition
{
public int X;
public int Y;
public GridPosition(int x, int y) { X = x; Y = y; }
}
/// <summary>操作批次 (用于撤销)</summary>
public class ActionBatch
{
public List<AIAction> Actions = new();
public string SnapshotBefore;
public string SnapshotAfter;
}
}
@@ -0,0 +1,10 @@
{
"name": "RedCircuit.AIAssistant",
"rootNamespace": "RedCircuit.AIAssistant",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"autoReferenced": true,
"defineConstraints": []
}