Добавить детерминированное размещение dungeon prefab в voxel-мире через stamp/carve в данных чанков. #6
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2657f564ee47c514db4e1cada2770fbc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0f36445faa7413742b67196646eb2631, type: 3}
|
||||
m_Name: WorldPrefabCollection
|
||||
m_EditorClassIdentifier: VoxelWorld.Runtime::InfiniteWorld.VoxelWorld.WorldPrefabCollection
|
||||
maxPlacementsPerChunk: 2
|
||||
attemptsPerPlacement: 8
|
||||
chunkEdgePadding: 1
|
||||
entries:
|
||||
- id: EntranceCrypt
|
||||
prefab: {fileID: 155468, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
weight: 10
|
||||
placementMode: 1
|
||||
footprint: {x: 0, y: 0}
|
||||
clearance: 4
|
||||
flattenPadding: 4
|
||||
flattenSearchRadius: 4
|
||||
allowRotations: 1
|
||||
- id: GlowingOrb
|
||||
prefab: {fileID: 190932, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
weight: 0
|
||||
placementMode: 0
|
||||
footprint: {x: 0, y: 0}
|
||||
clearance: 4
|
||||
flattenPadding: 4
|
||||
flattenSearchRadius: 4
|
||||
allowRotations: 1
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b91d23f483c774f4dbb1a77660881d87
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -37,6 +37,9 @@ namespace InfiniteWorld.VoxelWorld
|
||||
public float biomeNoiseScale = 0.02f;
|
||||
[Min(1f)] public float biomeSize = 48f;
|
||||
|
||||
[Header("Placements")]
|
||||
public List<WorldPrefabCollection> placementCollections = new List<WorldPrefabCollection>();
|
||||
|
||||
[Header("Runtime")]
|
||||
[Min(1)] public int maxAsyncChunkJobs = 2;
|
||||
[Min(1)] public int maxChunkBuildsPerFrame = 1;
|
||||
@@ -50,6 +53,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
internal readonly struct VoxelWorldResolvedSettings
|
||||
{
|
||||
private static readonly IReadOnlyList<VoxelBiomeProfile> EmptyBiomes = System.Array.Empty<VoxelBiomeProfile>();
|
||||
private static readonly IReadOnlyList<WorldPrefabCollection> EmptyPlacementCollections = System.Array.Empty<WorldPrefabCollection>();
|
||||
|
||||
public static readonly VoxelWorldResolvedSettings Default = Resolve(null);
|
||||
|
||||
@@ -75,6 +79,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
IReadOnlyList<VoxelBiomeProfile> biomeProfiles,
|
||||
float biomeNoiseScale,
|
||||
float biomeSize,
|
||||
IReadOnlyList<WorldPrefabCollection> placementCollections,
|
||||
int maxAsyncChunkJobs,
|
||||
int maxChunkBuildsPerFrame,
|
||||
int maxChunkMeshBuildsPerFrame,
|
||||
@@ -104,6 +109,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
BiomeProfiles = biomeProfiles;
|
||||
BiomeNoiseScale = biomeNoiseScale;
|
||||
BiomeSize = biomeSize;
|
||||
PlacementCollections = placementCollections;
|
||||
MaxAsyncChunkJobs = maxAsyncChunkJobs;
|
||||
MaxChunkBuildsPerFrame = maxChunkBuildsPerFrame;
|
||||
MaxChunkMeshBuildsPerFrame = maxChunkMeshBuildsPerFrame;
|
||||
@@ -134,6 +140,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
public IReadOnlyList<VoxelBiomeProfile> BiomeProfiles { get; }
|
||||
public float BiomeNoiseScale { get; }
|
||||
public float BiomeSize { get; }
|
||||
public IReadOnlyList<WorldPrefabCollection> PlacementCollections { get; }
|
||||
public int MaxAsyncChunkJobs { get; }
|
||||
public int MaxChunkBuildsPerFrame { get; }
|
||||
public int MaxChunkMeshBuildsPerFrame { get; }
|
||||
@@ -147,6 +154,9 @@ namespace InfiniteWorld.VoxelWorld
|
||||
IReadOnlyList<VoxelBiomeProfile> biomes = config != null && config.biomeProfiles != null
|
||||
? config.biomeProfiles
|
||||
: EmptyBiomes;
|
||||
IReadOnlyList<WorldPrefabCollection> placements = config != null && config.placementCollections != null
|
||||
? config.placementCollections
|
||||
: EmptyPlacementCollections;
|
||||
|
||||
return new VoxelWorldResolvedSettings(
|
||||
Mathf.Max(8, config != null ? config.chunkSize : 16),
|
||||
@@ -170,6 +180,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
biomes,
|
||||
config != null ? config.biomeNoiseScale : 0.02f,
|
||||
Mathf.Max(1f, config != null ? config.biomeSize : 48f),
|
||||
placements,
|
||||
Mathf.Max(1, config != null ? config.maxAsyncChunkJobs : 2),
|
||||
Mathf.Max(1, config != null ? config.maxChunkBuildsPerFrame : 1),
|
||||
Mathf.Max(1, config != null ? config.maxChunkMeshBuildsPerFrame : 1),
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace InfiniteWorld.VoxelWorld
|
||||
}
|
||||
}
|
||||
|
||||
ApplyChunkPlacementHeights(coord, heights);
|
||||
|
||||
return new ChunkBuildResult(coord, heights, biomeIndices, version, session, runtimeId);
|
||||
}
|
||||
|
||||
|
||||
@@ -435,14 +435,14 @@ namespace InfiniteWorld.VoxelWorld
|
||||
|
||||
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime) || !runtime.HasData)
|
||||
{
|
||||
return SampleRock(worldCell) ? SampleHeight(worldCell) : 0;
|
||||
return GetSampledFinalHeightAtWorldCell(worldCell);
|
||||
}
|
||||
|
||||
int localX = worldCell.x - coord.x * chunkSize;
|
||||
int localZ = worldCell.y - coord.y * chunkSize;
|
||||
if (localX < 0 || localZ < 0 || localX >= chunkSize || localZ >= chunkSize)
|
||||
{
|
||||
return SampleRock(worldCell) ? SampleHeight(worldCell) : 0;
|
||||
return GetSampledFinalHeightAtWorldCell(worldCell);
|
||||
}
|
||||
|
||||
return runtime.Heights[localZ * chunkSize + localX];
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace InfiniteWorld.VoxelWorld
|
||||
{
|
||||
public sealed partial class VoxelWorldGenerator
|
||||
{
|
||||
private readonly Dictionary<Vector2Int, WorldChunkPlacementPlan> chunkPlacementPlans = new Dictionary<Vector2Int, WorldChunkPlacementPlan>();
|
||||
private IReadOnlyList<WorldPrefabCollection> placementCollections => settings.PlacementCollections;
|
||||
|
||||
public IReadOnlyList<WorldPrefabCollection> PlacementCollections => placementCollections;
|
||||
|
||||
public WorldChunkPlacementPlan GetChunkPlacementPlan(Vector2Int chunkCoord)
|
||||
{
|
||||
return GetOrCreateChunkPlacementPlan(chunkCoord);
|
||||
}
|
||||
|
||||
public int GetBaseHeightAtWorldCell(Vector2Int worldCell)
|
||||
{
|
||||
return SampleRock(worldCell) ? SampleHeight(worldCell) : 0;
|
||||
}
|
||||
|
||||
public int GetFinalHeightAtWorldCell(Vector2Int worldCell)
|
||||
{
|
||||
return GetSampledFinalHeightAtWorldCell(worldCell);
|
||||
}
|
||||
|
||||
public byte GetBiomeIndexAtWorldCell(Vector2Int worldCell)
|
||||
{
|
||||
return SampleBiomeIndex(worldCell);
|
||||
}
|
||||
|
||||
private WorldChunkPlacementPlan GetOrCreateChunkPlacementPlan(Vector2Int chunkCoord)
|
||||
{
|
||||
if (chunkPlacementPlans.TryGetValue(chunkCoord, out WorldChunkPlacementPlan plan))
|
||||
{
|
||||
return plan;
|
||||
}
|
||||
|
||||
plan = WorldSpawnPlanner.PlanChunk(seed, chunkCoord, chunkSize, placementCollections, GetBaseHeightAtWorldCell);
|
||||
chunkPlacementPlans[chunkCoord] = plan;
|
||||
return plan;
|
||||
}
|
||||
|
||||
private int GetSampledFinalHeightAtWorldCell(Vector2Int worldCell)
|
||||
{
|
||||
int baseHeight = GetBaseHeightAtWorldCell(worldCell);
|
||||
Vector2Int chunkCoord = new Vector2Int(
|
||||
Mathf.FloorToInt(worldCell.x / (float)chunkSize),
|
||||
Mathf.FloorToInt(worldCell.y / (float)chunkSize));
|
||||
return GetOrCreateChunkPlacementPlan(chunkCoord).GetFinalHeight(worldCell, baseHeight);
|
||||
}
|
||||
|
||||
private void ApplyChunkPlacementHeights(Vector2Int chunkCoord, int[] heights)
|
||||
{
|
||||
WorldChunkPlacementPlan plan = GetOrCreateChunkPlacementPlan(chunkCoord);
|
||||
if (!plan.HasFlattening)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2Int chunkOrigin = new Vector2Int(chunkCoord.x * chunkSize, chunkCoord.y * chunkSize);
|
||||
for (int z = 0; z < chunkSize; z++)
|
||||
{
|
||||
for (int x = 0; x < chunkSize; x++)
|
||||
{
|
||||
Vector2Int worldCell = new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + z);
|
||||
heights[z * chunkSize + x] = plan.GetFinalHeight(worldCell, heights[z * chunkSize + x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanupPlacementPlans()
|
||||
{
|
||||
chunkPlacementPlans.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa5a2f44cc77fe844a2f0b14725a6dd8
|
||||
@@ -120,6 +120,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
|
||||
CleanupChunks();
|
||||
CleanupRegions();
|
||||
CleanupPlacementPlans();
|
||||
atlas?.Dispose();
|
||||
atlas = null;
|
||||
}
|
||||
@@ -285,6 +286,7 @@ namespace InfiniteWorld.VoxelWorld
|
||||
Vector2Int regionCoord = ChunkToRegion(coord);
|
||||
MarkRegionDirty(coord);
|
||||
chunks.Remove(coord);
|
||||
chunkPlacementPlans.Remove(coord);
|
||||
runtime.Dispose();
|
||||
TryDisposeRegionIfEmpty(regionCoord);
|
||||
QueueNeighborRefresh(coord);
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace InfiniteWorld.VoxelWorld
|
||||
{
|
||||
public enum WorldPlacementMode : byte
|
||||
{
|
||||
GroundOnly,
|
||||
FlattenTerrain
|
||||
}
|
||||
|
||||
[CreateAssetMenu(menuName = "Infinite World/World Prefab Collection", fileName = "WorldPrefabCollection")]
|
||||
public sealed class WorldPrefabCollection : ScriptableObject
|
||||
{
|
||||
[Tooltip("Maximum number of spawn points this collection may contribute to a single chunk.")]
|
||||
[Min(0)] public int maxPlacementsPerChunk = 2;
|
||||
[Tooltip("How many deterministic candidate positions are tested for each spawn slot before it is skipped.")]
|
||||
[Min(1)] public int attemptsPerPlacement = 8;
|
||||
[Tooltip("How many cells near the chunk border are reserved so large prefabs do not clip outside the chunk.")]
|
||||
[Min(0)] public int chunkEdgePadding = 1;
|
||||
public List<WorldPrefabEntry> entries = new List<WorldPrefabEntry>();
|
||||
|
||||
public IReadOnlyList<WorldPrefabEntry> Entries => entries;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class WorldPrefabEntry
|
||||
{
|
||||
[Tooltip("Stable logical identifier for this entry. Use it later for save data, analytics, or seeded config generation.")]
|
||||
public string id = "entry";
|
||||
|
||||
[Tooltip("Prefab that will be instantiated when this entry wins placement generation.")]
|
||||
public GameObject prefab;
|
||||
|
||||
[Tooltip("Relative weight inside the collection. Higher values make this entry more likely to be selected compared to others.")]
|
||||
[Min(0f)] public float weight = 1f;
|
||||
|
||||
[Tooltip("Percent chance from 0 to 100 that the selected entry will actually spawn in its slot after being chosen by weight.")]
|
||||
[Range(0f, 100f)] public float spawnChancePercent = 100f;
|
||||
|
||||
[Tooltip("GroundOnly requires an already flat area. FlattenTerrain carves the area to ground level and ensures an approach path.")]
|
||||
public WorldPlacementMode placementMode;
|
||||
|
||||
[Tooltip("Required placement size in world cells. Rotations may swap X and Y if enabled.")]
|
||||
public Vector2Int footprint = Vector2Int.one;
|
||||
|
||||
[Tooltip("Extra reserved cells around the footprint so other generated placements do not overlap too closely.")]
|
||||
[Min(0)] public int clearance;
|
||||
|
||||
[Tooltip("Extra cells around the footprint that will also be flattened when using FlattenTerrain.")]
|
||||
[Min(0)] public int flattenPadding = 1;
|
||||
|
||||
[Tooltip("Maximum search radius in cells for finding existing ground and cutting an access corridor to the flattened area.")]
|
||||
[Min(1)] public int flattenSearchRadius = 6;
|
||||
|
||||
[Tooltip("Allows deterministic 90-degree rotations so the same prefab can fit in more layout variations.")]
|
||||
public bool allowRotations = true;
|
||||
}
|
||||
|
||||
public sealed class WorldTerrainPatch
|
||||
{
|
||||
private readonly List<Vector2Int> flattenedCells;
|
||||
|
||||
public WorldTerrainPatch(IReadOnlyList<Vector2Int> cells)
|
||||
{
|
||||
flattenedCells = cells != null ? new List<Vector2Int>(cells) : new List<Vector2Int>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<Vector2Int> FlattenedCells => flattenedCells;
|
||||
}
|
||||
|
||||
public readonly struct WorldSpawnPoint
|
||||
{
|
||||
public WorldSpawnPoint(long spawnId, Vector2Int chunkCoord, int spawnOrdinalInChunk, int collectionIndex, int entryIndex, WorldPrefabEntry entry, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
SpawnId = spawnId;
|
||||
ChunkCoord = chunkCoord;
|
||||
SpawnOrdinalInChunk = spawnOrdinalInChunk;
|
||||
CollectionIndex = collectionIndex;
|
||||
EntryIndex = entryIndex;
|
||||
Entry = entry;
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
}
|
||||
|
||||
public long SpawnId { get; }
|
||||
public Vector2Int ChunkCoord { get; }
|
||||
public int SpawnOrdinalInChunk { get; }
|
||||
public int CollectionIndex { get; }
|
||||
public int EntryIndex { get; }
|
||||
public WorldPrefabEntry Entry { get; }
|
||||
public Vector3 Position { get; }
|
||||
public Quaternion Rotation { get; }
|
||||
}
|
||||
|
||||
public sealed class WorldChunkPlacementPlan
|
||||
{
|
||||
private static readonly IReadOnlyList<WorldSpawnPoint> EmptySpawnPoints = Array.Empty<WorldSpawnPoint>();
|
||||
private static readonly IReadOnlyList<WorldTerrainPatch> EmptyTerrainPatches = Array.Empty<WorldTerrainPatch>();
|
||||
|
||||
private readonly IReadOnlyList<WorldSpawnPoint> spawnPoints;
|
||||
private readonly IReadOnlyList<WorldTerrainPatch> terrainPatches;
|
||||
private readonly HashSet<Vector2Int> flattenedCells;
|
||||
|
||||
public static WorldChunkPlacementPlan Empty { get; } = new WorldChunkPlacementPlan(EmptySpawnPoints, EmptyTerrainPatches, null);
|
||||
|
||||
public WorldChunkPlacementPlan(IReadOnlyList<WorldSpawnPoint> spawnPoints, IReadOnlyList<WorldTerrainPatch> terrainPatches, HashSet<Vector2Int> flattenedCells)
|
||||
{
|
||||
this.spawnPoints = spawnPoints ?? EmptySpawnPoints;
|
||||
this.terrainPatches = terrainPatches ?? EmptyTerrainPatches;
|
||||
this.flattenedCells = flattenedCells;
|
||||
}
|
||||
|
||||
public IReadOnlyList<WorldSpawnPoint> SpawnPoints => spawnPoints;
|
||||
public IReadOnlyList<WorldTerrainPatch> TerrainPatches => terrainPatches;
|
||||
public bool HasFlattening => flattenedCells != null && flattenedCells.Count > 0;
|
||||
|
||||
public int GetFinalHeight(Vector2Int worldCell, int baseHeight)
|
||||
{
|
||||
return flattenedCells != null && flattenedCells.Contains(worldCell) ? 0 : baseHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f36445faa7413742b67196646eb2631
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace InfiniteWorld.VoxelWorld
|
||||
{
|
||||
internal static class WorldSeedUtility
|
||||
{
|
||||
public static uint Hash(params int[] values)
|
||||
{
|
||||
uint hash = 2166136261u;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
hash ^= unchecked((uint)values[i]);
|
||||
hash *= 16777619u;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 1274126177u;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static float Value01(uint hash)
|
||||
{
|
||||
return (hash & 0x00FFFFFFu) / 16777215f;
|
||||
}
|
||||
|
||||
public static int Range(uint hash, int minInclusive, int maxExclusive)
|
||||
{
|
||||
if (maxExclusive <= minInclusive)
|
||||
{
|
||||
return minInclusive;
|
||||
}
|
||||
|
||||
return minInclusive + (int)(hash % (uint)(maxExclusive - minInclusive));
|
||||
}
|
||||
|
||||
public static long ToStableId(uint hashA, uint hashB)
|
||||
{
|
||||
return ((long)hashA << 32) | hashB;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24b3661eb20e8b44a96a4f8cdf49e466
|
||||
@@ -0,0 +1,489 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace InfiniteWorld.VoxelWorld
|
||||
{
|
||||
internal static class WorldSpawnPlanner
|
||||
{
|
||||
public static WorldChunkPlacementPlan PlanChunk(
|
||||
int worldSeed,
|
||||
Vector2Int chunkCoord,
|
||||
int chunkSize,
|
||||
IReadOnlyList<WorldPrefabCollection> collections,
|
||||
System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
if (collections == null || collections.Count == 0)
|
||||
{
|
||||
return WorldChunkPlacementPlan.Empty;
|
||||
}
|
||||
|
||||
List<WorldSpawnPoint> spawnPoints = new List<WorldSpawnPoint>();
|
||||
List<WorldTerrainPatch> terrainPatches = new List<WorldTerrainPatch>();
|
||||
HashSet<Vector2Int> flattenedCells = new HashSet<Vector2Int>();
|
||||
HashSet<Vector2Int> occupiedCells = new HashSet<Vector2Int>();
|
||||
int spawnOrdinal = 0;
|
||||
|
||||
for (int collectionIndex = 0; collectionIndex < collections.Count; collectionIndex++)
|
||||
{
|
||||
WorldPrefabCollection collection = collections[collectionIndex];
|
||||
if (collection == null || collection.entries == null || collection.entries.Count == 0 || collection.maxPlacementsPerChunk <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int placementIndex = 0; placementIndex < collection.maxPlacementsPerChunk; placementIndex++)
|
||||
{
|
||||
if (!TryPickEntry(collection, worldSeed, chunkCoord, collectionIndex, placementIndex, out int entryIndex, out WorldPrefabEntry entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PassesSpawnChance(entry, worldSeed, chunkCoord, collectionIndex, placementIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryPlanPlacement(worldSeed, chunkCoord, chunkSize, collection, collectionIndex, placementIndex, entryIndex, entry, spawnOrdinal, occupiedCells, flattenedCells, getBaseHeight, out WorldSpawnPoint spawnPoint, out WorldTerrainPatch patch, out List<Vector2Int> reservedCells))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnPoints.Add(spawnPoint);
|
||||
if (patch != null)
|
||||
{
|
||||
terrainPatches.Add(patch);
|
||||
}
|
||||
|
||||
for (int i = 0; i < reservedCells.Count; i++)
|
||||
{
|
||||
occupiedCells.Add(reservedCells[i]);
|
||||
}
|
||||
|
||||
spawnOrdinal++;
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnPoints.Count == 0 && terrainPatches.Count == 0)
|
||||
{
|
||||
return WorldChunkPlacementPlan.Empty;
|
||||
}
|
||||
|
||||
return new WorldChunkPlacementPlan(spawnPoints, terrainPatches, flattenedCells.Count > 0 ? flattenedCells : null);
|
||||
}
|
||||
|
||||
private static bool TryPickEntry(WorldPrefabCollection collection, int worldSeed, Vector2Int chunkCoord, int collectionIndex, int placementIndex, out int entryIndex, out WorldPrefabEntry entry)
|
||||
{
|
||||
float totalWeight = 0f;
|
||||
for (int i = 0; i < collection.entries.Count; i++)
|
||||
{
|
||||
WorldPrefabEntry candidate = collection.entries[i];
|
||||
if (candidate == null || candidate.prefab == null || candidate.weight <= 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
totalWeight += candidate.weight;
|
||||
}
|
||||
|
||||
if (totalWeight <= 0f)
|
||||
{
|
||||
entryIndex = -1;
|
||||
entry = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
float pick = WorldSeedUtility.Value01(WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex, placementIndex, 1)) * totalWeight;
|
||||
float accumulated = 0f;
|
||||
for (int i = 0; i < collection.entries.Count; i++)
|
||||
{
|
||||
WorldPrefabEntry candidate = collection.entries[i];
|
||||
if (candidate == null || candidate.prefab == null || candidate.weight <= 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
accumulated += candidate.weight;
|
||||
if (pick <= accumulated)
|
||||
{
|
||||
entryIndex = i;
|
||||
entry = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = collection.entries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
WorldPrefabEntry candidate = collection.entries[i];
|
||||
if (candidate == null || candidate.prefab == null || candidate.weight <= 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entryIndex = i;
|
||||
entry = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
entryIndex = -1;
|
||||
entry = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool PassesSpawnChance(WorldPrefabEntry entry, int worldSeed, Vector2Int chunkCoord, int collectionIndex, int placementIndex)
|
||||
{
|
||||
if (entry == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.spawnChancePercent <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.spawnChancePercent >= 100f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float roll = WorldSeedUtility.Value01(WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex, placementIndex, 11)) * 100f;
|
||||
return roll <= entry.spawnChancePercent;
|
||||
}
|
||||
|
||||
private static bool TryPlanPlacement(
|
||||
int worldSeed,
|
||||
Vector2Int chunkCoord,
|
||||
int chunkSize,
|
||||
WorldPrefabCollection collection,
|
||||
int collectionIndex,
|
||||
int placementIndex,
|
||||
int entryIndex,
|
||||
WorldPrefabEntry entry,
|
||||
int spawnOrdinal,
|
||||
HashSet<Vector2Int> occupiedCells,
|
||||
HashSet<Vector2Int> flattenedCells,
|
||||
System.Func<Vector2Int, int> getBaseHeight,
|
||||
out WorldSpawnPoint spawnPoint,
|
||||
out WorldTerrainPatch patch,
|
||||
out List<Vector2Int> reservedCells)
|
||||
{
|
||||
reservedCells = null;
|
||||
spawnPoint = default;
|
||||
patch = null;
|
||||
|
||||
Vector2Int chunkOrigin = new Vector2Int(chunkCoord.x * chunkSize, chunkCoord.y * chunkSize);
|
||||
int attempts = Mathf.Max(1, collection.attemptsPerPlacement);
|
||||
for (int attempt = 0; attempt < attempts; attempt++)
|
||||
{
|
||||
int rotationSteps = entry.allowRotations ? WorldSeedUtility.Range(WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex, placementIndex, attempt, 2), 0, 4) : 0;
|
||||
Vector2Int footprint = GetFootprint(entry.footprint, rotationSteps);
|
||||
if (footprint.x <= 0 || footprint.y <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int localMinX = collection.chunkEdgePadding;
|
||||
int localMinZ = collection.chunkEdgePadding;
|
||||
int localMaxX = chunkSize - collection.chunkEdgePadding - footprint.x;
|
||||
int localMaxZ = chunkSize - collection.chunkEdgePadding - footprint.y;
|
||||
if (localMaxX < localMinX || localMaxZ < localMinZ)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int localX = WorldSeedUtility.Range(WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex, placementIndex, attempt, 3), localMinX, localMaxX + 1);
|
||||
int localZ = WorldSeedUtility.Range(WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex, placementIndex, attempt, 4), localMinZ, localMaxZ + 1);
|
||||
RectInt footprintRect = new RectInt(localX, localZ, footprint.x, footprint.y);
|
||||
RectInt reservedRect = ExpandRect(footprintRect, Mathf.Max(0, entry.clearance), chunkSize);
|
||||
|
||||
if (IntersectsOccupied(chunkOrigin, reservedRect, occupiedCells))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.placementMode == WorldPlacementMode.GroundOnly)
|
||||
{
|
||||
if (!IsGroundArea(chunkOrigin, reservedRect, flattenedCells, getBaseHeight))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
reservedCells = CollectCells(chunkOrigin, reservedRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TryCreateFlattenPatch(chunkOrigin, chunkSize, footprintRect, reservedRect, entry, occupiedCells, flattenedCells, getBaseHeight, out patch, out reservedCells))
|
||||
{
|
||||
patch = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float rotationY = rotationSteps * 90f;
|
||||
Vector3 position = new Vector3(
|
||||
chunkOrigin.x + footprintRect.x + footprintRect.width * 0.5f,
|
||||
0f,
|
||||
chunkOrigin.y + footprintRect.y + footprintRect.height * 0.5f);
|
||||
long spawnId = WorldSeedUtility.ToStableId(
|
||||
WorldSeedUtility.Hash(worldSeed, chunkCoord.x, chunkCoord.y, collectionIndex),
|
||||
WorldSeedUtility.Hash(entryIndex, placementIndex, spawnOrdinal, footprintRect.x, footprintRect.y));
|
||||
spawnPoint = new WorldSpawnPoint(spawnId, chunkCoord, spawnOrdinal, collectionIndex, entryIndex, entry, position, Quaternion.Euler(0f, rotationY, 0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateFlattenPatch(
|
||||
Vector2Int chunkOrigin,
|
||||
int chunkSize,
|
||||
RectInt footprintRect,
|
||||
RectInt reservedRect,
|
||||
WorldPrefabEntry entry,
|
||||
HashSet<Vector2Int> occupiedCells,
|
||||
HashSet<Vector2Int> flattenedCells,
|
||||
System.Func<Vector2Int, int> getBaseHeight,
|
||||
out WorldTerrainPatch patch,
|
||||
out List<Vector2Int> reservedCells)
|
||||
{
|
||||
int flattenPadding = Mathf.Max(entry.clearance, entry.flattenPadding);
|
||||
RectInt flattenRect = ExpandRect(footprintRect, flattenPadding, chunkSize);
|
||||
List<Vector2Int> flattened = CollectCells(chunkOrigin, flattenRect);
|
||||
if (IntersectsOccupied(flattened, occupiedCells))
|
||||
{
|
||||
patch = null;
|
||||
reservedCells = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Vector2Int> corridor = TryBuildAccessCorridor(chunkOrigin, chunkSize, flattenRect, Mathf.Max(1, entry.flattenSearchRadius), occupiedCells, flattenedCells, getBaseHeight);
|
||||
if (corridor == null)
|
||||
{
|
||||
patch = null;
|
||||
reservedCells = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < corridor.Count; i++)
|
||||
{
|
||||
flattened.Add(corridor[i]);
|
||||
}
|
||||
|
||||
HashSet<Vector2Int> uniqueFlattened = new HashSet<Vector2Int>(flattened);
|
||||
foreach (Vector2Int worldCell in uniqueFlattened)
|
||||
{
|
||||
flattenedCells.Add(worldCell);
|
||||
}
|
||||
|
||||
patch = new WorldTerrainPatch(flattened);
|
||||
reservedCells = CollectCells(chunkOrigin, reservedRect);
|
||||
reservedCells.AddRange(corridor);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<Vector2Int> TryBuildAccessCorridor(
|
||||
Vector2Int chunkOrigin,
|
||||
int chunkSize,
|
||||
RectInt flattenRect,
|
||||
int searchRadius,
|
||||
HashSet<Vector2Int> occupiedCells,
|
||||
HashSet<Vector2Int> flattenedCells,
|
||||
System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
if (HasAccessibleGroundNeighbor(chunkOrigin, flattenRect, flattenedCells, getBaseHeight))
|
||||
{
|
||||
return new List<Vector2Int>();
|
||||
}
|
||||
|
||||
Vector2Int center = new Vector2Int(flattenRect.x + flattenRect.width / 2, flattenRect.y + flattenRect.height / 2);
|
||||
Vector2Int? target = FindNearestGroundCell(chunkOrigin, chunkSize, center, flattenRect, searchRadius, occupiedCells, flattenedCells, getBaseHeight);
|
||||
if (!target.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector2Int entryCell = new Vector2Int(
|
||||
Mathf.Clamp(target.Value.x, chunkOrigin.x + flattenRect.xMin, chunkOrigin.x + flattenRect.xMax - 1),
|
||||
Mathf.Clamp(target.Value.y, chunkOrigin.y + flattenRect.yMin, chunkOrigin.y + flattenRect.yMax - 1));
|
||||
|
||||
List<Vector2Int> corridor = new List<Vector2Int>();
|
||||
Vector2Int cursor = target.Value;
|
||||
while (cursor.x != entryCell.x)
|
||||
{
|
||||
if (!occupiedCells.Contains(cursor))
|
||||
{
|
||||
corridor.Add(cursor);
|
||||
}
|
||||
|
||||
cursor.x += cursor.x < entryCell.x ? 1 : -1;
|
||||
}
|
||||
|
||||
while (cursor.y != entryCell.y)
|
||||
{
|
||||
if (!occupiedCells.Contains(cursor))
|
||||
{
|
||||
corridor.Add(cursor);
|
||||
}
|
||||
|
||||
cursor.y += cursor.y < entryCell.y ? 1 : -1;
|
||||
}
|
||||
|
||||
return corridor;
|
||||
}
|
||||
|
||||
private static bool HasAccessibleGroundNeighbor(Vector2Int chunkOrigin, RectInt flattenRect, HashSet<Vector2Int> flattenedCells, System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
for (int z = flattenRect.yMin - 1; z <= flattenRect.yMax; z++)
|
||||
{
|
||||
Vector2Int left = new Vector2Int(chunkOrigin.x + flattenRect.xMin - 1, chunkOrigin.y + z);
|
||||
Vector2Int right = new Vector2Int(chunkOrigin.x + flattenRect.xMax, chunkOrigin.y + z);
|
||||
if (GetPlannedHeight(left, flattenedCells, getBaseHeight) == 0 || GetPlannedHeight(right, flattenedCells, getBaseHeight) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = flattenRect.xMin; x < flattenRect.xMax; x++)
|
||||
{
|
||||
Vector2Int bottom = new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + flattenRect.yMin - 1);
|
||||
Vector2Int top = new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + flattenRect.yMax);
|
||||
if (GetPlannedHeight(bottom, flattenedCells, getBaseHeight) == 0 || GetPlannedHeight(top, flattenedCells, getBaseHeight) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Vector2Int? FindNearestGroundCell(
|
||||
Vector2Int chunkOrigin,
|
||||
int chunkSize,
|
||||
Vector2Int localCenter,
|
||||
RectInt excludedRect,
|
||||
int searchRadius,
|
||||
HashSet<Vector2Int> occupiedCells,
|
||||
HashSet<Vector2Int> flattenedCells,
|
||||
System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
for (int radius = 1; radius <= searchRadius; radius++)
|
||||
{
|
||||
for (int dz = -radius; dz <= radius; dz++)
|
||||
{
|
||||
for (int dx = -radius; dx <= radius; dx++)
|
||||
{
|
||||
if (Mathf.Max(Mathf.Abs(dx), Mathf.Abs(dz)) != radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int localX = localCenter.x + dx;
|
||||
int localZ = localCenter.y + dz;
|
||||
if (localX < 0 || localZ < 0 || localX >= chunkSize || localZ >= chunkSize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (excludedRect.Contains(new Vector2Int(localX, localZ)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2Int worldCell = new Vector2Int(chunkOrigin.x + localX, chunkOrigin.y + localZ);
|
||||
if (occupiedCells.Contains(worldCell))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetPlannedHeight(worldCell, flattenedCells, getBaseHeight) == 0)
|
||||
{
|
||||
return worldCell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int GetPlannedHeight(Vector2Int worldCell, HashSet<Vector2Int> flattenedCells, System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
return flattenedCells.Contains(worldCell) ? 0 : getBaseHeight(worldCell);
|
||||
}
|
||||
|
||||
private static bool IsGroundArea(Vector2Int chunkOrigin, RectInt rect, HashSet<Vector2Int> flattenedCells, System.Func<Vector2Int, int> getBaseHeight)
|
||||
{
|
||||
for (int z = rect.yMin; z < rect.yMax; z++)
|
||||
{
|
||||
for (int x = rect.xMin; x < rect.xMax; x++)
|
||||
{
|
||||
if (GetPlannedHeight(new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + z), flattenedCells, getBaseHeight) > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IntersectsOccupied(Vector2Int chunkOrigin, RectInt rect, HashSet<Vector2Int> occupiedCells)
|
||||
{
|
||||
for (int z = rect.yMin; z < rect.yMax; z++)
|
||||
{
|
||||
for (int x = rect.xMin; x < rect.xMax; x++)
|
||||
{
|
||||
if (occupiedCells.Contains(new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + z)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IntersectsOccupied(List<Vector2Int> cells, HashSet<Vector2Int> occupiedCells)
|
||||
{
|
||||
for (int i = 0; i < cells.Count; i++)
|
||||
{
|
||||
if (occupiedCells.Contains(cells[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<Vector2Int> CollectCells(Vector2Int chunkOrigin, RectInt rect)
|
||||
{
|
||||
List<Vector2Int> result = new List<Vector2Int>(rect.width * rect.height);
|
||||
for (int z = rect.yMin; z < rect.yMax; z++)
|
||||
{
|
||||
for (int x = rect.xMin; x < rect.xMax; x++)
|
||||
{
|
||||
result.Add(new Vector2Int(chunkOrigin.x + x, chunkOrigin.y + z));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static RectInt ExpandRect(RectInt rect, int amount, int chunkSize)
|
||||
{
|
||||
return new RectInt(
|
||||
Mathf.Max(0, rect.xMin - amount),
|
||||
Mathf.Max(0, rect.yMin - amount),
|
||||
Mathf.Min(chunkSize, rect.xMax + amount) - Mathf.Max(0, rect.xMin - amount),
|
||||
Mathf.Min(chunkSize, rect.yMax + amount) - Mathf.Max(0, rect.yMin - amount));
|
||||
}
|
||||
|
||||
private static Vector2Int GetFootprint(Vector2Int footprint, int rotationSteps)
|
||||
{
|
||||
footprint.x = Mathf.Max(1, footprint.x);
|
||||
footprint.y = Mathf.Max(1, footprint.y);
|
||||
return rotationSteps % 2 == 0 ? footprint : new Vector2Int(footprint.y, footprint.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0ff8828be489ea4b8cef4f1f62b3a14
|
||||
@@ -277,6 +277,63 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &1015513039
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 155468, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: SM_Env_Entrance_Crypt_01
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 3.1642048
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.00000032572393
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 2.031263
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 492300, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: ea0c7071e67bf1c43940a8ab3ea121f8, type: 3}
|
||||
--- !u!1001 &1165873058
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -338,6 +395,79 @@ PrefabInstance:
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 0b650fca685f2eb41a86538aa883e4c1, type: 3}
|
||||
--- !u!1001 &1382386155
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 190932, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: SM_Env_GlowingOrb_04
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 14.01
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 14.01
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalScale.z
|
||||
value: 14.01
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 11.45
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.27
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 4.4
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 495720, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
propertyPath: m_ConstrainProportionsScale
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 20e0bf298681fcd4693097c822277593, type: 3}
|
||||
--- !u!1001 &6493552143235564167
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -394,6 +524,10 @@ PrefabInstance:
|
||||
propertyPath: streamTarget
|
||||
value:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2927522923773808063, guid: 91b5caa5457131b4f8c542529f4ad7c3, type: 3}
|
||||
propertyPath: placementCollections.Array.size
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
@@ -407,3 +541,5 @@ SceneRoots:
|
||||
- {fileID: 6493552143235564167}
|
||||
- {fileID: 1165873058}
|
||||
- {fileID: 171707223}
|
||||
- {fileID: 1015513039}
|
||||
- {fileID: 1382386155}
|
||||
|
||||
Reference in New Issue
Block a user