Change map
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -628,7 +628,7 @@ Transform:
|
||||
m_GameObject: {fileID: 1437922948}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 3.5546, y: -5.79626, z: 4.39807}
|
||||
m_LocalPosition: {x: 3.5546, y: -66, z: 4.39807}
|
||||
m_LocalScale: {x: 50, y: 1, z: 50}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
||||
@@ -8,97 +8,297 @@ namespace Hallucinate.GameSetup.Maze
|
||||
/// Responsible for the visual representation of the maze.
|
||||
/// Handles spawning, pooling, and animations with safety checks.
|
||||
/// </summary>
|
||||
// public class MazeRenderer : MonoBehaviour
|
||||
// {
|
||||
// [SerializeField] private MazeVisualProfile visualProfile;
|
||||
//
|
||||
// private readonly Dictionary<Vector2Int, GameObject> _spawnedCells = new Dictionary<Vector2Int, GameObject>();
|
||||
// private Transform _container;
|
||||
//
|
||||
// public void Initialize(MazeGrid grid, Transform container)
|
||||
// {
|
||||
// _container = container;
|
||||
// grid.OnCellChanged += HandleCellChanged;
|
||||
//
|
||||
// // Initial render
|
||||
// for (int z = 0; z < grid.Depth; z++)
|
||||
// {
|
||||
// for (int x = 0; x < grid.Width; x++)
|
||||
// {
|
||||
// UpdateCellVisual(x, z, grid.GetCell(x, z), false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void Clear()
|
||||
// {
|
||||
// StopAllCoroutines();
|
||||
//
|
||||
// foreach (var cell in _spawnedCells.Values)
|
||||
// {
|
||||
// if (cell != null) Destroy(cell);
|
||||
// }
|
||||
// _spawnedCells.Clear();
|
||||
// }
|
||||
//
|
||||
// private void HandleCellChanged(int x, int z, MazeCellType type)
|
||||
// {
|
||||
// UpdateCellVisual(x, z, type, true);
|
||||
// }
|
||||
//
|
||||
// private void UpdateCellVisual(int x, int z, MazeCellType type, bool animate)
|
||||
// {
|
||||
// Vector2Int pos = new Vector2Int(x, z);
|
||||
//
|
||||
// if (_spawnedCells.TryGetValue(pos, out GameObject oldObj))
|
||||
// {
|
||||
// Destroy(oldObj);
|
||||
// _spawnedCells.Remove(pos);
|
||||
// }
|
||||
//
|
||||
// GameObject prefab = visualProfile.GetPrefab(type);
|
||||
// if (prefab == null) return;
|
||||
//
|
||||
// // Ensure scale is always positive to avoid BoxCollider issues
|
||||
// float safeScale = Mathf.Max(0.001f, visualProfile.scale);
|
||||
// Vector3 worldPos = new Vector3(x * safeScale, 0, z * safeScale);
|
||||
//
|
||||
// GameObject newObj = Instantiate(prefab, worldPos, Quaternion.identity, _container);
|
||||
// newObj.transform.localScale = Vector3.one * safeScale;
|
||||
// _spawnedCells[pos] = newObj;
|
||||
//
|
||||
// if (animate && visualProfile.animationDuration > 0)
|
||||
// {
|
||||
// StartCoroutine(AnimateCell(newObj.transform));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private IEnumerator AnimateCell(Transform target)
|
||||
// {
|
||||
// if (target == null) yield break;
|
||||
//
|
||||
// float duration = Mathf.Max(0.01f, visualProfile.animationDuration);
|
||||
// float elapsed = 0;
|
||||
// Vector3 finalScale = target.localScale;
|
||||
// target.localScale = Vector3.one * 0.001f; // Use tiny positive instead of zero
|
||||
//
|
||||
// while (elapsed < duration)
|
||||
// {
|
||||
// if (target == null) yield break;
|
||||
//
|
||||
// elapsed += Time.deltaTime;
|
||||
// float t = Mathf.Clamp01(elapsed / duration);
|
||||
// float s = Mathf.Sin(t * Mathf.PI * 0.5f);
|
||||
//
|
||||
// // Ensure s is never negative
|
||||
// target.localScale = finalScale * Mathf.Max(0.001f, s);
|
||||
// yield return null;
|
||||
// }
|
||||
//
|
||||
// if (target != null)
|
||||
// {
|
||||
// target.localScale = finalScale;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public class MazeRenderer : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private MazeVisualProfile visualProfile;
|
||||
|
||||
private readonly Dictionary<Vector2Int, GameObject> _spawnedCells = new Dictionary<Vector2Int, GameObject>();
|
||||
private Transform _container;
|
||||
private MazeGrid _currentGrid; // Thêm biến để lưu trữ grid phục vụ việc kiểm tra hàng xóm
|
||||
|
||||
public void Initialize(MazeGrid grid, Transform container)
|
||||
{
|
||||
[SerializeField] private MazeVisualProfile visualProfile;
|
||||
_currentGrid = grid;
|
||||
_container = container;
|
||||
grid.OnCellChanged += HandleCellChanged;
|
||||
|
||||
private readonly Dictionary<Vector2Int, GameObject> _spawnedCells = new Dictionary<Vector2Int, GameObject>();
|
||||
private Transform _container;
|
||||
|
||||
public void Initialize(MazeGrid grid, Transform container)
|
||||
// Initial render
|
||||
for (int z = 0; z < grid.Depth; z++)
|
||||
{
|
||||
_container = container;
|
||||
grid.OnCellChanged += HandleCellChanged;
|
||||
|
||||
// Initial render
|
||||
for (int z = 0; z < grid.Depth; z++)
|
||||
for (int x = 0; x < grid.Width; x++)
|
||||
{
|
||||
for (int x = 0; x < grid.Width; x++)
|
||||
{
|
||||
UpdateCellVisual(x, z, grid.GetCell(x, z), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
|
||||
foreach (var cell in _spawnedCells.Values)
|
||||
{
|
||||
if (cell != null) Destroy(cell);
|
||||
}
|
||||
_spawnedCells.Clear();
|
||||
}
|
||||
|
||||
private void HandleCellChanged(int x, int z, MazeCellType type)
|
||||
{
|
||||
UpdateCellVisual(x, z, type, true);
|
||||
}
|
||||
|
||||
private void UpdateCellVisual(int x, int z, MazeCellType type, bool animate)
|
||||
{
|
||||
Vector2Int pos = new Vector2Int(x, z);
|
||||
|
||||
if (_spawnedCells.TryGetValue(pos, out GameObject oldObj))
|
||||
{
|
||||
Destroy(oldObj);
|
||||
_spawnedCells.Remove(pos);
|
||||
}
|
||||
|
||||
GameObject prefab = visualProfile.GetPrefab(type);
|
||||
if (prefab == null) return;
|
||||
|
||||
// Ensure scale is always positive to avoid BoxCollider issues
|
||||
float safeScale = Mathf.Max(0.001f, visualProfile.scale);
|
||||
Vector3 worldPos = new Vector3(x * safeScale, 0, z * safeScale);
|
||||
|
||||
GameObject newObj = Instantiate(prefab, worldPos, Quaternion.identity, _container);
|
||||
newObj.transform.localScale = Vector3.one * safeScale;
|
||||
_spawnedCells[pos] = newObj;
|
||||
|
||||
if (animate && visualProfile.animationDuration > 0)
|
||||
{
|
||||
StartCoroutine(AnimateCell(newObj.transform));
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator AnimateCell(Transform target)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
float duration = Mathf.Max(0.01f, visualProfile.animationDuration);
|
||||
float elapsed = 0;
|
||||
Vector3 finalScale = target.localScale;
|
||||
target.localScale = Vector3.one * 0.001f; // Use tiny positive instead of zero
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
|
||||
|
||||
// Ensure s is never negative
|
||||
target.localScale = finalScale * Mathf.Max(0.001f, s);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
target.localScale = finalScale;
|
||||
UpdateCellVisual(x, z, grid.GetCell(x, z), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
|
||||
foreach (var cell in _spawnedCells.Values)
|
||||
{
|
||||
if (cell != null) Destroy(cell);
|
||||
}
|
||||
_spawnedCells.Clear();
|
||||
_currentGrid = null;
|
||||
}
|
||||
|
||||
private void HandleCellChanged(int x, int z, MazeCellType type)
|
||||
{
|
||||
// 1. Cập nhật ô vừa thay đổi (Có chạy Animation)
|
||||
UpdateCellVisual(x, z, type, true);
|
||||
|
||||
// 2. Cập nhật 4 ô xung quanh để chúng tự nối ống với ô mới này (KHÔNG chạy Animation để tránh giật hình)
|
||||
UpdateNeighborVisual(x + 1, z);
|
||||
UpdateNeighborVisual(x - 1, z);
|
||||
UpdateNeighborVisual(x, z + 1);
|
||||
UpdateNeighborVisual(x, z - 1);
|
||||
}
|
||||
|
||||
private void UpdateNeighborVisual(int x, int z)
|
||||
{
|
||||
if (_currentGrid != null && _currentGrid.IsInBounds(x, z))
|
||||
{
|
||||
// Chỉ cập nhật hình ảnh nếu hàng xóm là đường đi (tránh việc gọi vẽ lại tường gây tốn tài nguyên)
|
||||
if (IsPath(x, z))
|
||||
{
|
||||
MazeCellType type = _currentGrid.GetCell(x, z);
|
||||
UpdateCellVisual(x, z, type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCellVisual(int x, int z, MazeCellType type, bool animate)
|
||||
{
|
||||
Vector2Int pos = new Vector2Int(x, z);
|
||||
|
||||
if (_spawnedCells.TryGetValue(pos, out GameObject oldObj))
|
||||
{
|
||||
Destroy(oldObj);
|
||||
_spawnedCells.Remove(pos);
|
||||
}
|
||||
|
||||
GameObject prefab = null;
|
||||
Quaternion rotation = Quaternion.identity;
|
||||
|
||||
if (type == MazeCellType.Corridor || type == MazeCellType.Processing)
|
||||
{
|
||||
(prefab, rotation) = GetCorridorPrefabAndRotation(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
prefab = visualProfile.GetPrefab(type);
|
||||
}
|
||||
|
||||
if (prefab == null) return;
|
||||
|
||||
float safeScale = Mathf.Max(0.001f, visualProfile.scale);
|
||||
|
||||
// --- SỬA ĐỔI TẠI ĐÂY: Thu nhỏ prefab thành 0.5 ---
|
||||
float modelScaleMultiplier = 0.5f;
|
||||
|
||||
// Giữ nguyên safeScale cho worldPos để các ô vẫn cách đều nhau
|
||||
Vector3 worldPos = new Vector3(x * safeScale, 0, z * safeScale);
|
||||
|
||||
GameObject newObj = Instantiate(prefab, worldPos, rotation, _container);
|
||||
|
||||
// Nhân thêm 0.5 vào kích thước cuối cùng của Object
|
||||
newObj.transform.localScale = Vector3.one * safeScale * modelScaleMultiplier;
|
||||
_spawnedCells[pos] = newObj;
|
||||
|
||||
if (animate && visualProfile.animationDuration > 0)
|
||||
{
|
||||
StartCoroutine(AnimateCell(newObj.transform));
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================================
|
||||
// THUẬT TOÁN BITMASK AUTO-TILING
|
||||
// =================================================================================
|
||||
private (GameObject, Quaternion) GetCorridorPrefabAndRotation(int x, int z)
|
||||
{
|
||||
bool top = IsPath(x, z + 1);
|
||||
bool right = IsPath(x + 1, z);
|
||||
bool bottom = IsPath(x, z - 1);
|
||||
bool left = IsPath(x - 1, z);
|
||||
|
||||
int mask = 0;
|
||||
if (top) mask += 1;
|
||||
if (right) mask += 2;
|
||||
if (bottom) mask += 4;
|
||||
if (left) mask += 8;
|
||||
|
||||
GameObject prefabToSpawn = visualProfile.corridorDeadEnd;
|
||||
float yRotation = 0f;
|
||||
|
||||
switch (mask)
|
||||
{
|
||||
// === NGÕ CỤT (Giữ nguyên) ===
|
||||
case 1: prefabToSpawn = visualProfile.corridorDeadEnd; yRotation = 180f; break;
|
||||
case 2: prefabToSpawn = visualProfile.corridorDeadEnd; yRotation = 270f; break;
|
||||
case 4: prefabToSpawn = visualProfile.corridorDeadEnd; yRotation = 0f; break;
|
||||
case 8: prefabToSpawn = visualProfile.corridorDeadEnd; yRotation = 90f; break;
|
||||
|
||||
// === ĐƯỜNG THẲNG (Giữ nguyên) ===
|
||||
case 5: prefabToSpawn = visualProfile.corridorStraight; yRotation = 0f; break;
|
||||
case 10: prefabToSpawn = visualProfile.corridorStraight; yRotation = 90f; break;
|
||||
|
||||
// === GÓC CUA (Giữ nguyên) ===
|
||||
case 3: prefabToSpawn = visualProfile.corridorCorner; yRotation = 0f; break;
|
||||
case 6: prefabToSpawn = visualProfile.corridorCorner; yRotation = 90f; break;
|
||||
case 12: prefabToSpawn = visualProfile.corridorCorner; yRotation = 180f; break;
|
||||
case 9: prefabToSpawn = visualProfile.corridorCorner; yRotation = -90f; break;
|
||||
|
||||
// === NGÃ BA (Đã điều chỉnh lại góc xoay) ===
|
||||
case 11: prefabToSpawn = visualProfile.corridorTJunction; yRotation = 0f; break; // Mở: Trên, Phải, Trái. Bịt: Dưới.
|
||||
case 7: prefabToSpawn = visualProfile.corridorTJunction; yRotation = -90f; break; // Mở: Trên, Phải, Dưới. Bịt: Trái.
|
||||
case 14: prefabToSpawn = visualProfile.corridorTJunction; yRotation = 180f; break; // Mở: Phải, Dưới, Trái. Bịt: Trên.
|
||||
case 13: prefabToSpawn = visualProfile.corridorTJunction; yRotation = 90f; break; // Mở: Trên, Dưới, Trái. Bịt: Phải.
|
||||
|
||||
// === NGÃ TƯ (Giữ nguyên) ===
|
||||
case 15: prefabToSpawn = visualProfile.corridorCross; yRotation = 0f; break;
|
||||
|
||||
default: prefabToSpawn = visualProfile.corridorDeadEnd; yRotation = 0f; break;
|
||||
}
|
||||
|
||||
if (prefabToSpawn == null) prefabToSpawn = visualProfile.corridorPrefab;
|
||||
|
||||
return (prefabToSpawn, Quaternion.Euler(0, yRotation, 0));
|
||||
}
|
||||
|
||||
private bool IsPath(int x, int z)
|
||||
{
|
||||
if (_currentGrid == null || !_currentGrid.IsInBounds(x, z)) return false;
|
||||
MazeCellType type = _currentGrid.GetCell(x, z);
|
||||
return type == MazeCellType.Corridor
|
||||
|| type == MazeCellType.Processing
|
||||
|| type == MazeCellType.Start
|
||||
|| type == MazeCellType.End
|
||||
|| type == MazeCellType.Path;
|
||||
}
|
||||
|
||||
// =================================================================================
|
||||
// ANIMATION
|
||||
// =================================================================================
|
||||
private IEnumerator AnimateCell(Transform target)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
float duration = Mathf.Max(0.01f, visualProfile.animationDuration);
|
||||
float elapsed = 0;
|
||||
Vector3 finalScale = target.localScale;
|
||||
target.localScale = Vector3.one * 0.001f;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
|
||||
|
||||
target.localScale = finalScale * Mathf.Max(0.001f, s);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
target.localScale = finalScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,13 @@ namespace Hallucinate.GameSetup.Maze
|
||||
public GameObject pathPrefab;
|
||||
public GameObject startPrefab;
|
||||
public GameObject endPrefab;
|
||||
|
||||
[Header("Corridor Types")]
|
||||
public GameObject corridorStraight;
|
||||
public GameObject corridorCorner;
|
||||
public GameObject corridorTJunction;
|
||||
public GameObject corridorCross;
|
||||
public GameObject corridorDeadEnd;
|
||||
|
||||
[Header("Visualization Settings")]
|
||||
public float scale = 1f;
|
||||
|
||||
@@ -13,10 +13,15 @@ MonoBehaviour:
|
||||
m_Name: DefaultMazeProfile
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Hallucinate.GameSetup.Maze.MazeVisualProfile
|
||||
wallPrefab: {fileID: 865692088774546613, guid: c49f25c5b1c3e4b43a2bc56717387124, type: 3}
|
||||
corridorPrefab: {fileID: 7041334646084879511, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
|
||||
corridorPrefab: {fileID: 919132149155446097, guid: 4721de3f311aa364e9609e41c5a28665, type: 3}
|
||||
processingPrefab: {fileID: 1560533784803380970, guid: 1e8b6ed6b01405e4b9e358abc8f7a058, type: 3}
|
||||
pathPrefab: {fileID: 0}
|
||||
startPrefab: {fileID: 0}
|
||||
endPrefab: {fileID: 0}
|
||||
corridorStraight: {fileID: 919132149155446097, guid: 4721de3f311aa364e9609e41c5a28665, type: 3}
|
||||
corridorCorner: {fileID: 919132149155446097, guid: 075b1b03af4930445823f0aadfe82cef, type: 3}
|
||||
corridorTJunction: {fileID: 919132149155446097, guid: c57b571fe619e894c833cf1d9e5c057a, type: 3}
|
||||
corridorCross: {fileID: 919132149155446097, guid: 25a6a5f070d975c48a85d820c7f78438, type: 3}
|
||||
corridorDeadEnd: {fileID: 919132149155446097, guid: 710f9f3daf0b6fa48ab017ff20db25ac, type: 3}
|
||||
scale: 1
|
||||
animationDuration: 0.25
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user