更新 temp.md
This commit is contained in:
452
temp.md
452
temp.md
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user