更新 temp.md

This commit is contained in:
2026-05-22 17:46:28 +08:00
parent fdbf7fed71
commit 413fa11a96

452
temp.md
View File

@@ -1,342 +1,110 @@
using System.Collections.Generic; # 背景故事:《玩偶末世:物件觉醒》
using Unity.AI.Navigation;
using UnityEngine; ---
using UnityEngine.AI;
## 序幕:寂静降临之日
public class ProceduralMapGenerator : MonoBehaviour
{ 没有人知道那一天究竟发生了什么。
[Header("生成配置")]
public int seed = 12345; 上一秒,城市还在呼吸——汽车的鸣笛、手机的铃声、楼道里邻居的争吵。下一秒,所有属于"人类"的声响同时消失,就像有人拔掉了世界的插头。
public int maxPieces = 20;
public float gridSize = 2.0f; 公寓楼的走廊灯还亮着,电视机还开着,锅里煮了一半的面条还在冒着热气。但人不见了。所有人,在同一瞬间,像是被橡皮擦从画布上抹去一样,消失得干干净净。
[Header("预制体库")] 而寂静只持续了三天。
public List<GameObject> mapPiecePrefabs = new List<GameObject>();
public GameObject wallPiecePrefab; // 封口模块 第四天凌晨,走廊尽头的旧衣柜发出了第一声"吱呀"。
[Header("后处理")] ---
public bool enableMeshCombining = true;
public bool enableNavMesh = true; ## 第一章:物件苏醒
[Header("调试")] 那不是木料的自然收缩。那是某种意志——某种被压抑了太久、如今终于挣脱了束缚的意志。
public bool showDebugGizmos = true;
没有人知道"觉醒"是从哪里开始的。也许是从那台永远在凌晨三点自动开机、播放雪花噪点的老电视。也许是从那把每次主人不在家就会自己从挂钩上掉下来的扫帚。又或许,是从那只看似不起眼、却永远在暗中注视着一切的玩具熊。
// 内部状态
private Dictionary<Vector3Int, GameObject> placedPieces = new Dictionary<Vector3Int, GameObject>(); 总之,在人类消失后的第三天到第七天之间,公寓楼里的每一件"东西",都活了过来。
private List<ConnectionPoint> openConnections = new List<ConnectionPoint>();
private System.Random random; 不是像机器那样接通了电源,而是真正地——拥有了生命。拥有了情绪。拥有了**敌意**。
private Transform mapRoot;
---
void Start()
{ ## 第二章:觉醒之阶
GenerateMap();
} 不同物件的觉醒程度并不相同,仿佛冥冥中有一条看不见的阶梯。
[ContextMenu("生成地图")] **最底层:茫然的动作者。**
public void GenerateMap() 拖鞋在地板上无意识地拖行,像是在模仿曾经穿着它的那个人。订书机在桌面上一点点爬动,偶尔"咔哒"一声咬下,仿佛还在执行早已无人在意的装订任务。
{
ClearMap(); **中间层:感知者。**
InitializeRandom(); 扫帚学会了扫地——不是清理灰尘,而是用木杆狠狠扫向挡在它面前的一切。玻璃瓶从窗台滚落、摔碎、又自行重组,它学会了用碎裂的边缘标记自己的领地。
// 创建地图根对象 **最顶层:支配者。**
mapRoot = new GameObject("GeneratedMap").transform; 旧衣柜不一样。从苏醒的那一刻起,它就没有困惑过。它知道这栋楼现在是它的了。它记得——记得人类将东西塞进它的肚子、关上它的门、忘记它的存在。它记得被遗忘的感觉。现在,它要那些还在楼里活动的"小东西"也感受一下被遗忘的滋味。
mapRoot.SetParent(transform);
于是,一场无声的战争在公寓楼的走廊和房间里蔓延开来。觉醒的物件们划分领地,吞噬彼此身上的"碎片"——一种闪着微光的、可能是生命本质的残留物——来变得更加强大。
// 选择起始模块
PlaceStartPiece(); ---
// 主生成循环 ## 第三章:纽扣里的光
GenerateLoop();
而在所有物件之中,有一个最特别的存在。
// 后处理
if (enableMeshCombining) 它曾经只是一个手工缝制的玩偶,属于这栋楼里的某个孩子。当人类消失时,它被遗落在沙发上,和一只靠垫、半块橡皮、以及一本折角的《安徒生童话》作伴。
CombineMeshes();
它不是普通物件。它身上的每一块布料、每一颗纽扣,都浸透了孩子的体温和情感。当觉醒的浪潮席卷整栋楼时,它没有像其他物件那样只剩下茫然的躁动或扭曲的敌意。在它的身体深处——也许是那颗被缝在心脏位置的老纽扣里——保留着一小簇不属于"物件"的光。
if (enableNavMesh)
BuildNavMesh(); 那是人类留下的光。
}
它睁开了纽扣做的眼睛。它从沙发上站了起来。它捡起了掉在茶几上的订书机——不是活的,不是敌人,而是一把武器。一把被它的小手紧紧握住、可以发射出金属钉的"手炮"。
[ContextMenu("清除地图")]
public void ClearMap() 它不知道自己要去哪里。但它隐隐觉得,楼上有什么东西在召唤它。而它必须在整栋楼——或者说,所有觉醒的物件——彻底失控之前,找到答案。
{
if (mapRoot != null) ---
DestroyImmediate(mapRoot.gameObject);
## 第四章:藏身处
placedPieces.Clear();
openConnections.Clear(); 玩偶找到了一个相对安全的角落:一间小小的储藏室,藏在走廊的尽头。门锁还勉强能用,窗户对着外墙,暂时没有哪个觉醒物件想到这里。它把这里叫做"藏身处"。
}
在这里,它可以修复自己战斗中破损的身体——从散落的布料和金属碎片中搜刮材料,缝补身上裂开的口子,加固即将散架的关节。它还发现,那些觉醒物件掉落的碎片可以被"编织"进自己的身体,赋予它新的力量。
private void InitializeRandom()
{ 更重要的是,它发现了一些"模块"——那些曾经只是普通工具的零件,经过觉醒能量的浸染,如今能够在战斗中自动响应它的意志。闪电从缝衣针尖迸发,扳手在身前盘旋成回旋的守护者,订书钉组成的无人机在头顶悬停警戒。
random = new System.Random(seed);
} 每拿到一个新的模块,它都感觉自己离那团"光"更近了一步。
private void PlaceStartPiece() ---
{
// 查找起始模块e ## 第五章:向上
GameObject startPrefab = null;
foreach (var prefab in mapPiecePrefabs) 玩偶知道,这栋楼有二十几层。也许更多。它不知道自己能撑到第几层。每一次推开通往下一层的门,都意味着面临更危险的敌人、更密集的追杀、以及更长的撤退路线。
{
var piece = prefab.GetComponent<MapPiece>(); 但它没有选择回头。因为每一次它从一场苦战中活下来、关好藏身处的门、把搜刮到的材料摊在桌子上时,它都能感觉到那团光在自己身体里亮了一点点。
if (piece != null && piece.isStartPiece)
{ 而它开始隐约听到一个声音——不一定是用耳朵听的。那是一种直接传到它纽扣心脏深处的低语,来自楼顶,来自衣柜封锁的某个地方。那声音说:
startPrefab = prefab;
break; *"继续走。继续爬。你在找的不是答案。是我。"*
}
} 它不知道那是什么。它只知道,在这栋充满敌意的楼里,所有还在"活着"的物件都在往上爬——或者往某个中心汇聚。就好像整栋公寓楼变成了一座倒悬的塔,而塔尖藏着吞噬一切的漩涡。
if (startPrefab == null && mapPiecePrefabs.Count > 0) 也许"觉醒"不是意外。也许"觉醒"只是某种仪式的前奏。
{
startPrefab = mapPiecePrefabs[0]; 而玩偶手中的订书机,扣响了第一声扳机。
}
---
if (startPrefab == null)
{ ## 第六章:每一局都是一次挑战
Debug.LogError("没有可用的起始模块预制体!");
return; 现在,这栋公寓楼在等待新的挑战者。
}
每当你操控玩偶走出藏身处,推开走廊尽头的防火门,一局新的"向上攀登"就开始了。你将在五个楼层中厮杀前行——在杂兵楼层的混乱中站稳脚跟在事件层做出生死抉择在精英层的逼迫下证明自己的走位最终直面每五层末端的衣柜Boss。
// 实例化起始模块
var startPiece = Instantiate(startPrefab, Vector3.zero, Quaternion.identity, mapRoot); 你可以选择在途中撤离,带着已经到手的所有材料安全返回藏身处。但你也可以赌一把——闯入下一层,面对更强的敌人,换取更多的资源。因为你知道,只有最勇敢的攀登者,才能接触到那扇被衣柜封锁的门后面隐藏的真相。
var mapPiece = startPiece.GetComponent<MapPiece>();
而无论你死了多少次,无论你撤离了多少回,藏身处的灯光永远亮着。材料还在手里。模块还在身上。你已经比昨天更强了一点。
// 记录位置
Vector3Int gridPos = WorldToGrid(Vector3.zero); 这就是玩偶的末世。所有日常之物都不再日常,所有熟悉之物都变得陌生,所有被遗忘之物都开始复仇。
placedPieces[gridPos] = startPiece;
而一个巴掌大的布娃娃,将用手里的订书机,一件一件地,把这个世界拼回来。
// 添加所有连接点到开放列表
openConnections.AddRange(mapPiece.GetAvailableConnectionPoints()); ---
Debug.Log($"放置起始模块: {mapPiece.pieceName} 在位置 {gridPos}"); *—— 故事,才刚刚开始。*
}
private void GenerateLoop()
{
int attempts = 0;
int maxAttempts = maxPieces * 3; // 最大尝试次数
while (openConnections.Count > 0 && placedPieces.Count < maxPieces && attempts < maxAttempts)
{
attempts++;
// 从开放列表中取出一个连接点
var connection = openConnections[0];
openConnections.RemoveAt(0);
// 尝试放置新模块
if (TryPlacePieceAtConnection(connection))
{
Debug.Log($"成功放置第 {placedPieces.Count} 个模块");
}
else
{
// 放置失败,放置封口模块
PlaceWallPiece(connection);
}
}
Debug.Log($"生成完成!共放置 {placedPieces.Count} 个模块,尝试次数: {attempts}");
}
private bool TryPlacePieceAtConnection(ConnectionPoint connection)
{
// 获取连接点的世界位置和方向
Vector3 worldPos = connection.localPosition; // 这里需要修正,应该从父对象获取
Vector3 worldDir = connection.localDirection;
// 计算新模块的位置
Vector3 newPiecePos = worldPos + worldDir * gridSize;
Vector3Int gridPos = WorldToGrid(newPiecePos);
// 检查位置是否已被占用
if (placedPieces.ContainsKey(gridPos))
return false;
// 查找合适的预制体
var candidates = FindCandidatePieces(connection);
if (candidates.Count == 0)
return false;
// 随机选择一个候选
GameObject selectedPrefab = candidates[random.Next(candidates.Count)];
// 计算旋转(使新模块的连接点与当前连接点对齐)
Quaternion rotation = Quaternion.LookRotation(-worldDir, Vector3.up);
// 实例化新模块
var newPiece = Instantiate(selectedPrefab, newPiecePos, rotation, mapRoot);
var newMapPiece = newPiece.GetComponent<MapPiece>();
// 检查碰撞
if (CheckCollision(newPiece))
{
DestroyImmediate(newPiece);
return false;
}
// 记录位置
placedPieces[gridPos] = newPiece;
// 添加新模块的连接点到开放列表(除了与当前连接点匹配的那个)
var newConnections = newMapPiece.GetAvailableConnectionPoints();
foreach (var newConn in newConnections)
{
// 简单的方向检查,避免添加反向连接
Vector3 newWorldDir = newPiece.transform.TransformDirection(newConn.localDirection);
if (Vector3.Dot(newWorldDir, worldDir) < -0.5f) // 大致相反方向
continue;
openConnections.Add(newConn);
}
return true;
}
private List<GameObject> FindCandidatePieces(ConnectionPoint connection)
{
var candidates = new List<GameObject>();
foreach (var prefab in mapPiecePrefabs)
{
var mapPiece = prefab.GetComponent<MapPiece>();
if (mapPiece == null) continue;
// 简单的风格匹配(可扩展)
if (mapPiece.style != connection.style && connection.style != "default")
continue;
candidates.Add(prefab);
}
return candidates;
}
private void PlaceWallPiece(ConnectionPoint connection)
{
if (wallPiecePrefab == null) return;
Vector3 worldPos = connection.localPosition;
Vector3 worldDir = connection.localDirection;
Vector3 wallPos = worldPos + worldDir * gridSize * 0.5f;
Vector3Int gridPos = WorldToGrid(wallPos);
if (!placedPieces.ContainsKey(gridPos))
{
var wallPiece = Instantiate(wallPiecePrefab, wallPos, Quaternion.LookRotation(-worldDir), mapRoot);
placedPieces[gridPos] = wallPiece;
}
}
private bool CheckCollision(GameObject piece)
{
var collider = piece.GetComponent<Collider>();
if (collider == null) return false;
// 简单的重叠检测
var bounds = collider.bounds;
foreach (var placedPiece in placedPieces.Values)
{
var placedCollider = placedPiece.GetComponent<Collider>();
if (placedCollider != null && placedCollider.bounds.Intersects(bounds))
return true;
}
return false;
}
private Vector3Int WorldToGrid(Vector3 worldPos)
{
return new Vector3Int(
Mathf.RoundToInt(worldPos.x / gridSize),
Mathf.RoundToInt(worldPos.y / gridSize),
Mathf.RoundToInt(worldPos.z / gridSize)
);
}
private void CombineMeshes()
{
// 简单的网格合并实现
var meshFilters = mapRoot.GetComponentsInChildren<MeshFilter>();
if (meshFilters.Length == 0) return;
// 按材质分组合并
var combineInstances = new List<CombineInstance>();
var materials = new List<Material>();
foreach (var meshFilter in meshFilters)
{
if (meshFilter.sharedMesh == null) continue;
var renderer = meshFilter.GetComponent<MeshRenderer>();
if (renderer == null || renderer.sharedMaterial == null) continue;
var combine = new CombineInstance
{
mesh = meshFilter.sharedMesh,
transform = meshFilter.transform.localToWorldMatrix
};
combineInstances.Add(combine);
// 禁用原始渲染器
renderer.enabled = false;
}
if (combineInstances.Count > 0)
{
// 创建合并的网格对象
var combinedObject = new GameObject("CombinedMesh");
combinedObject.transform.SetParent(mapRoot);
var combinedFilter = combinedObject.AddComponent<MeshFilter>();
var combinedRenderer = combinedObject.AddComponent<MeshRenderer>();
// 合并网格
var combinedMesh = new Mesh();
combinedMesh.CombineMeshes(combineInstances.ToArray());
combinedFilter.sharedMesh = combinedMesh;
// 设置材质(使用第一个材质)
if (materials.Count > 0)
combinedRenderer.sharedMaterial = materials[0];
Debug.Log($"网格合并完成,合并了 {combineInstances.Count} 个网格");
}
}
private void BuildNavMesh()
{
var navMeshSurface = mapRoot.gameObject.GetComponent<NavMeshSurface>();
if (navMeshSurface == null)
navMeshSurface = mapRoot.gameObject.AddComponent<NavMeshSurface>();
navMeshSurface.BuildNavMesh();
Debug.Log("NavMesh 生成完成");
}
private void OnDrawGizmos()
{
if (!showDebugGizmos) return;
// 绘制已放置模块的网格位置
Gizmos.color = Color.blue;
foreach (var kvp in placedPieces)
{
Vector3 worldPos = GridToWorld(kvp.Key);
Gizmos.DrawWireCube(worldPos, Vector3.one * gridSize * 0.8f);
}
// 绘制开放连接点
Gizmos.color = Color.red;
foreach (var connection in openConnections)
{
Vector3 worldPos = connection.localPosition;
Gizmos.DrawSphere(worldPos, 0.2f);
}
}
private Vector3 GridToWorld(Vector3Int gridPos)
{
return new Vector3(gridPos.x * gridSize, gridPos.y * gridSize, gridPos.z * gridSize);
}
}