This commit is contained in:
2026-03-29 02:26:31 +07:00
parent 1e458a4f09
commit 99c70886a5
22 changed files with 2430 additions and 44 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8028fe033c76d4b4e82877336273ad43
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+315
View File
@@ -0,0 +1,315 @@
using UnityEditor;
using UnityEngine;
namespace InfiniteWorld.Editor
{
public class WorldGeneratorEditorWindow : EditorWindow
{
private enum BrushMode
{
Erase,
Wall,
Environment
}
private WorldAutotileProfile profile;
private ChunkTemplate template;
private SerializedObject serializedProfile;
private Vector2 profileScroll;
private Vector2 chunkScroll;
private BrushMode brushMode = BrushMode.Wall;
private bool isPainting;
private int pendingWidth = 16;
private int pendingHeight = 16;
[MenuItem("Tools/Infinite World/World Builder")]
public static void Open()
{
GetWindow<WorldGeneratorEditorWindow>("World Builder");
}
private void OnGUI()
{
DrawToolbar();
EditorGUILayout.Space(6f);
EditorGUILayout.BeginHorizontal();
DrawChunkEditor();
GUILayout.Space(8f);
DrawProfileEditor();
EditorGUILayout.EndHorizontal();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
template = (ChunkTemplate)EditorGUILayout.ObjectField(template, typeof(ChunkTemplate), false, GUILayout.Width(position.width * 0.38f));
profile = (WorldAutotileProfile)EditorGUILayout.ObjectField(profile, typeof(WorldAutotileProfile), false, GUILayout.Width(position.width * 0.38f));
if (GUILayout.Button("New Chunk", EditorStyles.toolbarButton, GUILayout.Width(80f)))
{
CreateChunkAsset();
}
if (GUILayout.Button("New Profile", EditorStyles.toolbarButton, GUILayout.Width(80f)))
{
CreateProfileAsset();
}
EditorGUILayout.EndHorizontal();
}
private void DrawChunkEditor()
{
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.52f));
EditorGUILayout.LabelField("Chunk Painter", EditorStyles.boldLabel);
if (template == null)
{
EditorGUILayout.HelpBox("Create or assign a ChunkTemplate asset. Paint walls and environment directly on the chunk grid and mark exits on each side.", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
template.EnsureCellData();
pendingWidth = EditorGUILayout.IntField("Width", pendingWidth == 0 ? template.width : pendingWidth);
pendingHeight = EditorGUILayout.IntField("Height", pendingHeight == 0 ? template.height : pendingHeight);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Resize"))
{
Undo.RecordObject(template, "Resize Chunk Template");
template.Resize(pendingWidth, pendingHeight);
EditorUtility.SetDirty(template);
}
if (GUILayout.Button("Clear"))
{
Undo.RecordObject(template, "Clear Chunk Template");
template.Clear();
EditorUtility.SetDirty(template);
}
if (GUILayout.Button("Border From Exits"))
{
Undo.RecordObject(template, "Apply Border Walls");
template.ApplyBorderWallsFromExits(3);
EditorUtility.SetDirty(template);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Brush", EditorStyles.miniBoldLabel);
brushMode = (BrushMode)GUILayout.Toolbar((int)brushMode, new[] { "Erase", "Wall", "Environment" });
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Exits", EditorStyles.miniBoldLabel);
EditorGUILayout.BeginHorizontal();
DrawExitToggle("Top", ChunkExit.Top);
DrawExitToggle("Right", ChunkExit.Right);
DrawExitToggle("Bottom", ChunkExit.Bottom);
DrawExitToggle("Left", ChunkExit.Left);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(8f);
chunkScroll = EditorGUILayout.BeginScrollView(chunkScroll, GUILayout.ExpandHeight(true));
Rect gridRect = GUILayoutUtility.GetRect(template.width * 24f, template.height * 24f, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
DrawGrid(gridRect);
EditorGUILayout.EndScrollView();
EditorGUILayout.HelpBox("Gray = floor, brown = wall, green = environment. Drag with the selected brush to paint the chunk visually.", MessageType.None);
EditorGUILayout.EndVertical();
}
private void DrawProfileEditor()
{
EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("Tile Profile", EditorStyles.boldLabel);
if (profile == null)
{
EditorGUILayout.HelpBox("Assign a WorldAutotileProfile to map real tiles onto ground, walls, and environment.", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
if (serializedProfile == null || serializedProfile.targetObject != profile)
{
serializedProfile = new SerializedObject(profile);
}
serializedProfile.Update();
profileScroll = EditorGUILayout.BeginScrollView(profileScroll);
EditorGUILayout.PropertyField(serializedProfile.FindProperty("baseGroundTile"));
EditorGUILayout.Space(6f);
EditorGUILayout.LabelField("Wall Autotile", EditorStyles.miniBoldLabel);
SerializedProperty walls = serializedProfile.FindProperty("wallTiles");
DrawWallGrid(walls);
EditorGUILayout.Space(6f);
EditorGUILayout.PropertyField(serializedProfile.FindProperty("environmentTiles"), true);
EditorGUILayout.EndScrollView();
serializedProfile.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(profile);
}
EditorGUILayout.EndVertical();
}
private void DrawWallGrid(SerializedProperty walls)
{
DrawTriple(walls, "outerTopLeft", "top", "outerTopRight");
DrawTriple(walls, "left", "center", "right");
DrawTriple(walls, "outerBottomLeft", "bottom", "outerBottomRight");
DrawDouble(walls, "innerTopLeft", "innerTopRight");
DrawDouble(walls, "innerBottomLeft", "innerBottomRight");
}
private void DrawTriple(SerializedProperty root, string a, string b, string c)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(root.FindPropertyRelative(a), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(b), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(c), GUIContent.none);
EditorGUILayout.EndHorizontal();
}
private void DrawDouble(SerializedProperty root, string a, string b)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(root.FindPropertyRelative(a), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(b), GUIContent.none);
EditorGUILayout.EndHorizontal();
}
private void DrawExitToggle(string label, ChunkExit exit)
{
bool current = template.GetExit(exit);
bool next = GUILayout.Toggle(current, label, "Button");
if (next == current)
{
return;
}
Undo.RecordObject(template, "Toggle Chunk Exit");
switch (exit)
{
case ChunkExit.Top:
template.exitTop = next;
break;
case ChunkExit.Right:
template.exitRight = next;
break;
case ChunkExit.Bottom:
template.exitBottom = next;
break;
case ChunkExit.Left:
template.exitLeft = next;
break;
}
EditorUtility.SetDirty(template);
}
private void DrawGrid(Rect rect)
{
const float cellSize = 24f;
Event evt = Event.current;
for (int y = 0; y < template.height; y++)
{
for (int x = 0; x < template.width; x++)
{
Rect cellRect = new Rect(rect.x + x * cellSize, rect.y + (template.height - 1 - y) * cellSize, cellSize - 1f, cellSize - 1f);
EditorGUI.DrawRect(cellRect, GetCellColor(x, y));
if ((evt.type == EventType.MouseDown || evt.type == EventType.MouseDrag) && cellRect.Contains(evt.mousePosition) && evt.button == 0)
{
isPainting = true;
PaintCell(x, y);
evt.Use();
}
}
}
if (evt.type == EventType.MouseUp)
{
isPainting = false;
}
if (isPainting)
{
Repaint();
}
}
private Color GetCellColor(int x, int y)
{
if (template.GetWall(x, y))
{
return new Color(0.52f, 0.36f, 0.22f, 1f);
}
if (template.GetEnvironment(x, y))
{
return new Color(0.2f, 0.54f, 0.25f, 1f);
}
return new Color(0.4f, 0.4f, 0.4f, 1f);
}
private void PaintCell(int x, int y)
{
Undo.RecordObject(template, "Paint Chunk Template");
switch (brushMode)
{
case BrushMode.Erase:
template.SetWall(x, y, false);
template.SetEnvironment(x, y, false);
break;
case BrushMode.Wall:
template.SetWall(x, y, true);
break;
case BrushMode.Environment:
template.SetEnvironment(x, y, true);
break;
}
EditorUtility.SetDirty(template);
}
private void CreateChunkAsset()
{
string path = EditorUtility.SaveFilePanelInProject("Create Chunk Template", "ChunkTemplate", "asset", "Choose where to save the chunk template.");
if (string.IsNullOrEmpty(path))
{
return;
}
ChunkTemplate asset = CreateInstance<ChunkTemplate>();
asset.Resize(16, 16);
asset.ApplyBorderWallsFromExits(3);
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
template = asset;
pendingWidth = asset.width;
pendingHeight = asset.height;
Selection.activeObject = asset;
}
private void CreateProfileAsset()
{
string path = EditorUtility.SaveFilePanelInProject("Create World Profile", "WorldAutotileProfile", "asset", "Choose where to save the tile profile.");
if (string.IsNullOrEmpty(path))
{
return;
}
WorldAutotileProfile asset = CreateInstance<WorldAutotileProfile>();
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
profile = asset;
serializedProfile = new SerializedObject(profile);
Selection.activeObject = asset;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ff365d4e73b85cc4987c7984d5f8c7cb
+279 -44
View File
@@ -13,7 +13,7 @@ OcclusionCullingSettings:
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
serializedVersion: 10
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
@@ -38,13 +38,12 @@ RenderSettings:
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
serializedVersion: 13
m_BakeOnSceneLoad: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
@@ -67,9 +66,6 @@ LightmapSettings:
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
@@ -104,7 +100,7 @@ NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
@@ -117,7 +113,7 @@ NavMeshSettings:
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
@@ -135,6 +131,7 @@ GameObject:
- component: {fileID: 519420031}
- component: {fileID: 519420029}
- component: {fileID: 519420030}
- component: {fileID: 519420033}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
@@ -172,6 +169,7 @@ MonoBehaviour:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
@@ -179,8 +177,19 @@ MonoBehaviour:
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
m_Version: 2
--- !u!20 &519420031
Camera:
@@ -196,9 +205,17 @@ Camera:
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
@@ -232,14 +249,29 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &519420033
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 98ee4fb5b3ebf80478e6e25afa8fd337, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::InfiniteWorld.CameraFollow2D
target: {fileID: 0}
smoothTime: 0.18
offset: {x: 0, y: 0, z: -10}
--- !u!1 &619394800
GameObject:
m_ObjectHideFlags: 0
@@ -269,55 +301,29 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 073797afb82c5a1438f328866b10b3f0, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ComponentVersion: 1
m_ComponentVersion: 2
m_LightType: 4
m_BlendStyleIndex: 0
m_FalloffIntensity: 0.5
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 1
m_LightVolumeIntensity: 1
m_LightVolumeIntensityEnabled: 0
m_LightVolumeEnabled: 0
m_ApplyToSortingLayers: 00000000
m_LightCookieSprite: {fileID: 0}
m_DeprecatedPointLightCookieSprite: {fileID: 0}
m_LightOrder: 0
m_AlphaBlendOnOverlap: 0
m_OverlapOperation: 0
m_NormalMapDistance: 3
m_NormalMapQuality: 2
m_UseNormalMap: 0
m_ShadowIntensityEnabled: 0
m_ShadowsEnabled: 0
m_ShadowIntensity: 0.75
m_ShadowSoftness: 0
m_ShadowSoftnessFalloffIntensity: 0.5
m_ShadowVolumeIntensityEnabled: 0
m_ShadowVolumeIntensity: 0.75
m_Vertices:
- position: {x: 0.9985302, y: 0.9985302, z: 0}
color: {r: 0.70710677, g: 0.70710677, b: 0, a: 0}
uv: {x: 0, y: 0}
- position: {x: 0.9985302, y: 0.9985302, z: 0}
color: {r: 0, g: 0, b: 0, a: 1}
uv: {x: 0, y: 0}
- position: {x: -0.9985302, y: 0.9985302, z: 0}
color: {r: -0.70710677, g: 0.70710677, b: 0, a: 0}
uv: {x: 0, y: 0}
- position: {x: -0.9985302, y: 0.9985302, z: 0}
color: {r: 0, g: 0, b: 0, a: 1}
uv: {x: 0, y: 0}
- position: {x: -0.99853003, y: -0.9985304, z: 0}
color: {r: -0.70710665, g: -0.7071069, b: 0, a: 0}
uv: {x: 0, y: 0}
- position: {x: -0.99853003, y: -0.9985304, z: 0}
color: {r: 0, g: 0, b: 0, a: 1}
uv: {x: 0, y: 0}
- position: {x: 0.99853003, y: -0.9985304, z: 0}
color: {r: 0.70710665, g: -0.7071069, b: 0, a: 0}
uv: {x: 0, y: 0}
- position: {x: 0.99853003, y: -0.9985304, z: 0}
color: {r: 0, g: 0, b: 0, a: 1}
uv: {x: 0, y: 0}
- position: {x: 0, y: 0, z: 0}
color: {r: 0, g: 0, b: 0, a: 1}
uv: {x: 0, y: 0}
m_Triangles: 030001000800020000000100030002000100050003000800040002000300050004000300070005000800060004000500070006000500010007000800000006000700010000000700
m_LocalBounds:
m_Center: {x: 0, y: -0.00000011920929, z: 0}
m_Extent: {x: 0.9985302, y: 0.99853027, z: 0}
@@ -342,11 +348,240 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 619394800}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &669282973
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 669282975}
- component: {fileID: 669282974}
- component: {fileID: 669282978}
- component: {fileID: 669282977}
- component: {fileID: 669282976}
m_Layer: 0
m_Name: Player
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &669282974
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 669282973}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c9187e81c6ec8da4599e04a1694ec18b, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::InfiniteWorld.SimplePlayerInputMover
moveSpeed: 5
--- !u!4 &669282975
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 669282973}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!70 &669282976
CapsuleCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 669282973}
m_Enabled: 1
serializedVersion: 3
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_CompositeOperation: 0
m_CompositeOrder: 0
m_Offset: {x: 0, y: 0}
m_Size: {x: 0.5, y: 1}
m_Direction: 0
--- !u!212 &669282977
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 669282973}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_Sprite: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1, y: 1}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_SpriteSortPoint: 0
--- !u!50 &669282978
Rigidbody2D:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 669282973}
m_BodyType: 0
m_Simulated: 1
m_UseFullKinematicContacts: 0
m_UseAutoMass: 0
m_Mass: 1
m_LinearDamping: 0
m_AngularDamping: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 0
--- !u!1 &947604398
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 947604400}
- component: {fileID: 947604399}
m_Layer: 0
m_Name: World
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &947604399
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 947604398}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6bacd3a0ac13e6f4a94548426dd89ebb, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::InfiniteWorld.InfiniteWorldGenerator
player: {fileID: 0}
profile: {fileID: 0}
templates: []
chunkSize: 16
generationRadius: 2
seed: 12345
--- !u!4 &947604400
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 947604398}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 519420032}
- {fileID: 619394802}
- {fileID: 947604400}
- {fileID: 669282975}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0444b525419aa14429d7e31a7e4b7fef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ff30f393052d2674e94fd1d887712090
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+31
View File
@@ -0,0 +1,31 @@
using UnityEngine;
namespace InfiniteWorld
{
public class CameraFollow2D : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float smoothTime = 0.18f;
[SerializeField] private Vector3 offset = new Vector3(0f, 0f, -10f);
private Vector3 velocity;
private void LateUpdate()
{
if (target == null)
{
SimplePlayerInputMover player = FindFirstObjectByType<SimplePlayerInputMover>();
if (player == null)
{
return;
}
target = player.transform;
}
Vector3 desiredPosition = target.position + offset;
desiredPosition.z = offset.z;
transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref velocity, smoothTime);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 98ee4fb5b3ebf80478e6e25afa8fd337
@@ -0,0 +1,109 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace InfiniteWorld
{
public class SimplePlayerInputMover : MonoBehaviour
{
[SerializeField] private float moveSpeed = 5f;
private InputAction moveAction;
private Rigidbody2D rb;
private Vector2 moveInput;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
if (rb == null)
{
rb = gameObject.AddComponent<Rigidbody2D>();
}
ConfigurePhysics();
EnsureVisual();
moveAction = new InputAction("Move", InputActionType.Value);
moveAction.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
moveAction.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/upArrow")
.With("Down", "<Keyboard>/downArrow")
.With("Left", "<Keyboard>/leftArrow")
.With("Right", "<Keyboard>/rightArrow");
moveAction.AddBinding("<Gamepad>/leftStick");
}
private void OnEnable()
{
moveAction?.Enable();
}
private void OnDisable()
{
moveAction?.Disable();
}
private void OnDestroy()
{
moveAction?.Dispose();
}
private void Update()
{
if (moveAction == null)
{
return;
}
moveInput = moveAction.ReadValue<Vector2>().normalized;
if (moveInput.x != 0f)
{
Vector3 scale = transform.localScale;
scale.x = Mathf.Abs(scale.x) * Mathf.Sign(moveInput.x);
transform.localScale = scale;
}
}
private void FixedUpdate()
{
if (rb == null)
{
return;
}
Vector2 target = rb.position + moveInput * (moveSpeed * Time.fixedDeltaTime);
rb.MovePosition(target);
}
private void ConfigurePhysics()
{
rb.gravityScale = 0f;
rb.freezeRotation = true;
rb.interpolation = RigidbodyInterpolation2D.Interpolate;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
CapsuleCollider2D collider = GetComponent<CapsuleCollider2D>();
if (collider == null)
{
collider = gameObject.AddComponent<CapsuleCollider2D>();
}
collider.direction = CapsuleDirection2D.Vertical;
collider.size = new Vector2(0.55f, 0.8f);
collider.offset = new Vector2(0f, -0.05f);
}
private void EnsureVisual()
{
SpriteRenderer renderer = GetComponent<SpriteRenderer>();
if (renderer == null)
{
renderer = gameObject.AddComponent<SpriteRenderer>();
}
renderer.sprite = ProceduralWorldArt.CreatePlayerSprite();
renderer.sortingOrder = 10;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c9187e81c6ec8da4599e04a1694ec18b
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96adefd3df6641c40b9624eb12185ea4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+232
View File
@@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace InfiniteWorld
{
[CreateAssetMenu(menuName = "Infinite World/Chunk Template", fileName = "ChunkTemplate")]
public class ChunkTemplate : ScriptableObject
{
[Min(4)] public int width = 16;
[Min(4)] public int height = 16;
[Header("Exits")]
public bool exitTop = true;
public bool exitRight = true;
public bool exitBottom = true;
public bool exitLeft = true;
[SerializeField] private List<ChunkCellData> cells = new List<ChunkCellData>();
public int CellCount => width * height;
public void EnsureCellData()
{
int target = Mathf.Max(1, width * height);
while (cells.Count < target)
{
cells.Add(new ChunkCellData());
}
while (cells.Count > target)
{
cells.RemoveAt(cells.Count - 1);
}
}
public void Resize(int newWidth, int newHeight)
{
newWidth = Mathf.Max(4, newWidth);
newHeight = Mathf.Max(4, newHeight);
List<ChunkCellData> oldCells = new List<ChunkCellData>(cells);
int oldWidth = width;
int oldHeight = height;
width = newWidth;
height = newHeight;
cells = new List<ChunkCellData>(newWidth * newHeight);
for (int i = 0; i < newWidth * newHeight; i++)
{
cells.Add(new ChunkCellData());
}
for (int y = 0; y < Mathf.Min(oldHeight, newHeight); y++)
{
for (int x = 0; x < Mathf.Min(oldWidth, newWidth); x++)
{
int oldIndex = y * oldWidth + x;
if (oldIndex < oldCells.Count)
{
cells[Index(x, y)] = oldCells[oldIndex];
}
}
}
}
public void Clear()
{
EnsureCellData();
for (int i = 0; i < cells.Count; i++)
{
cells[i] = new ChunkCellData();
}
}
public bool GetWall(int x, int y)
{
return IsInBounds(x, y) && cells[Index(x, y)].wall;
}
public bool GetEnvironment(int x, int y)
{
return IsInBounds(x, y) && cells[Index(x, y)].environment;
}
public void SetWall(int x, int y, bool value)
{
if (!IsInBounds(x, y))
{
return;
}
EnsureCellData();
ChunkCellData data = cells[Index(x, y)];
data.wall = value;
if (value)
{
data.environment = false;
}
cells[Index(x, y)] = data;
}
public void SetEnvironment(int x, int y, bool value)
{
if (!IsInBounds(x, y))
{
return;
}
EnsureCellData();
ChunkCellData data = cells[Index(x, y)];
data.environment = value;
if (value)
{
data.wall = false;
}
cells[Index(x, y)] = data;
}
public bool GetExit(ChunkExit exit)
{
return exit switch
{
ChunkExit.Top => exitTop,
ChunkExit.Right => exitRight,
ChunkExit.Bottom => exitBottom,
ChunkExit.Left => exitLeft,
_ => false
};
}
public int ExitCount()
{
int count = 0;
count += exitTop ? 1 : 0;
count += exitRight ? 1 : 0;
count += exitBottom ? 1 : 0;
count += exitLeft ? 1 : 0;
return count;
}
public void ApplyBorderWallsFromExits(int openingWidth = 3)
{
EnsureCellData();
Clear();
for (int x = 0; x < width; x++)
{
SetWall(x, 0, true);
SetWall(x, height - 1, true);
}
for (int y = 0; y < height; y++)
{
SetWall(0, y, true);
SetWall(width - 1, y, true);
}
CarveExit(ChunkExit.Top, openingWidth);
CarveExit(ChunkExit.Right, openingWidth);
CarveExit(ChunkExit.Bottom, openingWidth);
CarveExit(ChunkExit.Left, openingWidth);
}
public void CarveExit(ChunkExit exit, int openingWidth = 3)
{
if (!GetExit(exit))
{
return;
}
int half = Mathf.Max(1, openingWidth) / 2;
switch (exit)
{
case ChunkExit.Top:
for (int x = width / 2 - half; x <= width / 2 + half; x++)
{
SetWall(x, height - 1, false);
SetWall(x, height - 2, false);
}
break;
case ChunkExit.Right:
for (int y = height / 2 - half; y <= height / 2 + half; y++)
{
SetWall(width - 1, y, false);
SetWall(width - 2, y, false);
}
break;
case ChunkExit.Bottom:
for (int x = width / 2 - half; x <= width / 2 + half; x++)
{
SetWall(x, 0, false);
SetWall(x, 1, false);
}
break;
case ChunkExit.Left:
for (int y = height / 2 - half; y <= height / 2 + half; y++)
{
SetWall(0, y, false);
SetWall(1, y, false);
}
break;
}
}
private int Index(int x, int y)
{
return y * width + x;
}
private bool IsInBounds(int x, int y)
{
return x >= 0 && y >= 0 && x < width && y < height;
}
}
public enum ChunkExit
{
Top,
Right,
Bottom,
Left
}
[Serializable]
public struct ChunkCellData
{
public bool wall;
public bool environment;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bae8eeae2da7d3f4396883671b297a47
@@ -0,0 +1,582 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace InfiniteWorld
{
public class InfiniteWorldGenerator : MonoBehaviour
{
[Header("References")]
[SerializeField] private Transform player;
[SerializeField] private WorldAutotileProfile profile;
[Header("Chunk Settings")]
[SerializeField] private int chunkSize = 16;
[SerializeField] private int generationRadius = 2;
[SerializeField] private int seed = 12345;
[Header("Rock Noise")]
[SerializeField] private float macroNoiseScale = 0.05f;
[SerializeField] private float detailNoiseScale = 0.12f;
[SerializeField] private float ridgeNoiseScale = 0.18f;
[SerializeField] private float wallThreshold = 0.6f;
[SerializeField] private float rockBias = 0.04f;
[SerializeField] private int smoothingPasses = 2;
[Header("Global Passes")]
[SerializeField] private float passNoiseScale = 0.018f;
[SerializeField] private float passDetailScale = 0.041f;
[SerializeField] private float passThreshold = 0.22f;
[SerializeField] private float passFeather = 0.12f;
[Header("Environment")]
[SerializeField] private float environmentNoiseScale = 0.19f;
[SerializeField] private float environmentThreshold = 0.7f;
private readonly Dictionary<Vector2Int, GeneratedChunk> chunks = new Dictionary<Vector2Int, GeneratedChunk>();
private Grid grid;
private Transform chunkRoot;
private Vector2Int lastGeneratedCenter = new Vector2Int(int.MinValue, int.MinValue);
private WorldAutotileProfile runtimeFallbackProfile;
private void Awake()
{
EnsureSceneInfrastructure();
EnsureRuntimeData();
TryFindPlayer();
}
private void Update()
{
if (player == null && !TryFindPlayer())
{
return;
}
Vector2Int playerChunk = WorldToChunk(player.position);
if (playerChunk == lastGeneratedCenter)
{
return;
}
lastGeneratedCenter = playerChunk;
GenerateAround(playerChunk);
}
private void EnsureSceneInfrastructure()
{
grid = GetComponentInChildren<Grid>();
if (grid == null)
{
GameObject gridObject = new GameObject("Grid", typeof(Grid));
gridObject.transform.SetParent(transform, false);
grid = gridObject.GetComponent<Grid>();
}
Transform existingChunkRoot = grid.transform.Find("Chunks");
if (existingChunkRoot == null)
{
GameObject root = new GameObject("Chunks");
root.transform.SetParent(grid.transform, false);
chunkRoot = root.transform;
}
else
{
chunkRoot = existingChunkRoot;
}
}
private void EnsureRuntimeData()
{
if (profile == null || !profile.HasAnyAssignedTiles())
{
runtimeFallbackProfile = RuntimeWorldProfileFactory.CreateFallbackProfile();
}
}
private bool TryFindPlayer()
{
if (player != null)
{
return true;
}
SimplePlayerInputMover mover = FindFirstObjectByType<SimplePlayerInputMover>();
if (mover == null)
{
return false;
}
player = mover.transform;
return true;
}
private void GenerateAround(Vector2Int centerChunk)
{
for (int y = -generationRadius; y <= generationRadius; y++)
{
for (int x = -generationRadius; x <= generationRadius; x++)
{
Vector2Int coord = new Vector2Int(centerChunk.x + x, centerChunk.y + y);
if (chunks.ContainsKey(coord))
{
continue;
}
GeneratedChunk chunk = CreateChunk(coord);
chunks.Add(coord, chunk);
BuildChunkData(coord, chunk);
RenderChunk(coord);
RefreshNeighborBorders(coord);
}
}
}
private GeneratedChunk CreateChunk(Vector2Int coord)
{
GameObject chunkObject = new GameObject($"Chunk_{coord.x}_{coord.y}");
chunkObject.transform.SetParent(chunkRoot, false);
chunkObject.transform.localPosition = new Vector3(coord.x * chunkSize, coord.y * chunkSize, 0f);
Tilemap ground = CreateTilemap("Ground", chunkObject.transform, 0, false);
Tilemap walls = CreateTilemap("Walls", chunkObject.transform, 1, true);
Tilemap environment = CreateTilemap("Environment", chunkObject.transform, 2, false);
return new GeneratedChunk(chunkObject.transform, ground, walls, environment, new bool[chunkSize, chunkSize], new bool[chunkSize, chunkSize]);
}
private Tilemap CreateTilemap(string name, Transform parent, int sortingOrder, bool addCollision)
{
GameObject tilemapObject = new GameObject(name, typeof(Tilemap), typeof(TilemapRenderer));
tilemapObject.transform.SetParent(parent, false);
TilemapRenderer renderer = tilemapObject.GetComponent<TilemapRenderer>();
renderer.sortingOrder = sortingOrder;
if (addCollision)
{
Rigidbody2D rb = tilemapObject.GetComponent<Rigidbody2D>();
if (rb == null)
{
rb = tilemapObject.AddComponent<Rigidbody2D>();
}
rb.bodyType = RigidbodyType2D.Static;
CompositeCollider2D composite = tilemapObject.GetComponent<CompositeCollider2D>();
if (composite == null)
{
composite = tilemapObject.AddComponent<CompositeCollider2D>();
}
composite.geometryType = CompositeCollider2D.GeometryType.Polygons;
TilemapCollider2D collider = tilemapObject.GetComponent<TilemapCollider2D>();
if (collider == null)
{
collider = tilemapObject.AddComponent<TilemapCollider2D>();
}
collider.compositeOperation = Collider2D.CompositeOperation.Merge;
}
return tilemapObject.GetComponent<Tilemap>();
}
private void BuildChunkData(Vector2Int coord, GeneratedChunk chunk)
{
int margin = Mathf.Max(2, smoothingPasses + 1);
int sampleSize = chunkSize + margin * 2;
bool[,] sampled = new bool[sampleSize, sampleSize];
for (int y = 0; y < sampleSize; y++)
{
for (int x = 0; x < sampleSize; x++)
{
int localX = x - margin;
int localY = y - margin;
Vector2Int worldCell = ChunkToWorldCell(coord, localX, localY);
sampled[x, y] = SampleRock(worldCell);
}
}
for (int pass = 0; pass < smoothingPasses; pass++)
{
sampled = SmoothSampledMask(sampled);
}
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
chunk.WallMask[x, y] = sampled[x + margin, y + margin];
}
}
BuildEnvironment(coord, chunk);
}
private bool[,] SmoothSampledMask(bool[,] source)
{
int width = source.GetLength(0);
int height = source.GetLength(1);
bool[,] result = new bool[width, height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int solidNeighbors = CountSampledWallNeighbors(source, x, y);
if (solidNeighbors >= 5)
{
result[x, y] = true;
}
else if (solidNeighbors <= 2)
{
result[x, y] = false;
}
else
{
result[x, y] = source[x, y];
}
}
}
return result;
}
private int CountSampledWallNeighbors(bool[,] sampled, int x, int y)
{
int width = sampled.GetLength(0);
int height = sampled.GetLength(1);
int count = 0;
for (int oy = -1; oy <= 1; oy++)
{
for (int ox = -1; ox <= 1; ox++)
{
if (ox == 0 && oy == 0)
{
continue;
}
int nx = x + ox;
int ny = y + oy;
if (nx < 0 || ny < 0 || nx >= width || ny >= height)
{
count++;
continue;
}
if (sampled[nx, ny])
{
count++;
}
}
}
return count;
}
private bool SampleRock(Vector2Int worldCell)
{
float macro = Mathf.PerlinNoise((worldCell.x + seed * 0.13f) * macroNoiseScale, (worldCell.y - seed * 0.17f) * macroNoiseScale);
float detail = Mathf.PerlinNoise((worldCell.x - seed * 0.23f) * detailNoiseScale, (worldCell.y + seed * 0.19f) * detailNoiseScale);
float ridge = 1f - Mathf.Abs(Mathf.PerlinNoise((worldCell.x + seed * 0.31f) * ridgeNoiseScale, (worldCell.y + seed * 0.29f) * ridgeNoiseScale) * 2f - 1f);
float rockValue = macro * 0.62f + detail * 0.18f + ridge * 0.20f + rockBias;
if (IsInsideGlobalPass(worldCell))
{
rockValue -= 0.45f;
}
return rockValue >= wallThreshold;
}
private bool IsInsideGlobalPass(Vector2Int worldCell)
{
float primary = Mathf.PerlinNoise((worldCell.x + seed * 0.41f) * passNoiseScale, (worldCell.y - seed * 0.43f) * passNoiseScale);
float detail = Mathf.PerlinNoise((worldCell.x - seed * 0.17f) * passDetailScale, (worldCell.y + seed * 0.23f) * passDetailScale);
float ridged = Mathf.Abs(primary * 2f - 1f);
float warped = Mathf.Lerp(ridged, Mathf.Abs(detail * 2f - 1f), 0.35f);
return warped <= passThreshold + passFeather * detail;
}
private void BuildEnvironment(Vector2Int coord, GeneratedChunk chunk)
{
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
if (chunk.WallMask[x, y])
{
chunk.EnvironmentMask[x, y] = false;
continue;
}
if (HasAdjacentOpenTiles(chunk.WallMask, x, y, 1))
{
chunk.EnvironmentMask[x, y] = false;
continue;
}
Vector2Int worldCell = ChunkToWorldCell(coord, x, y);
float noise = Mathf.PerlinNoise((worldCell.x + seed * 0.53f) * environmentNoiseScale, (worldCell.y - seed * 0.61f) * environmentNoiseScale);
chunk.EnvironmentMask[x, y] = noise >= environmentThreshold;
}
}
}
private bool HasAdjacentOpenTiles(bool[,] wallMask, int x, int y, int radius)
{
for (int oy = -radius; oy <= radius; oy++)
{
for (int ox = -radius; ox <= radius; ox++)
{
int nx = x + ox;
int ny = y + oy;
if (nx < 0 || ny < 0 || nx >= chunkSize || ny >= chunkSize)
{
continue;
}
if (!wallMask[nx, ny])
{
return true;
}
}
}
return false;
}
private void RenderChunk(Vector2Int coord)
{
if (!chunks.TryGetValue(coord, out GeneratedChunk chunk))
{
return;
}
WorldAutotileProfile activeProfile = profile != null && profile.HasAnyAssignedTiles() ? profile : runtimeFallbackProfile;
if (activeProfile == null)
{
return;
}
chunk.Ground.ClearAllTiles();
chunk.Walls.ClearAllTiles();
chunk.Environment.ClearAllTiles();
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
Vector3Int localCell = new Vector3Int(x, y, 0);
chunk.Ground.SetTile(localCell, activeProfile.baseGroundTile);
if (chunk.WallMask[x, y])
{
Vector2Int worldCell = ChunkToWorldCell(coord, x, y);
AutoTileShape shape = ResolveWallShape(worldCell);
chunk.Walls.SetTile(localCell, activeProfile.GetWallTile(shape));
}
else if (chunk.EnvironmentMask[x, y])
{
TileBase tile = PickEnvironmentTile(ChunkToWorldCell(coord, x, y), activeProfile);
if (tile != null)
{
chunk.Environment.SetTile(localCell, tile);
}
}
}
}
}
private void RefreshNeighborBorders(Vector2Int coord)
{
RenderChunk(coord + Vector2Int.up);
RenderChunk(coord + Vector2Int.right);
RenderChunk(coord + Vector2Int.down);
RenderChunk(coord + Vector2Int.left);
}
private AutoTileShape ResolveWallShape(Vector2Int worldCell)
{
bool top = HasWallAt(worldCell + Vector2Int.up);
bool right = HasWallAt(worldCell + Vector2Int.right);
bool bottom = HasWallAt(worldCell + Vector2Int.down);
bool left = HasWallAt(worldCell + Vector2Int.left);
bool topLeft = HasWallAt(worldCell + new Vector2Int(-1, 1));
bool topRight = HasWallAt(worldCell + new Vector2Int(1, 1));
bool bottomRight = HasWallAt(worldCell + new Vector2Int(1, -1));
bool bottomLeft = HasWallAt(worldCell + new Vector2Int(-1, -1));
if (!top && !left)
{
return AutoTileShape.OuterTopLeft;
}
if (!top && !right)
{
return AutoTileShape.OuterTopRight;
}
if (!bottom && !right)
{
return AutoTileShape.OuterBottomRight;
}
if (!bottom && !left)
{
return AutoTileShape.OuterBottomLeft;
}
if (top && left && !topLeft)
{
return AutoTileShape.InnerTopLeft;
}
if (top && right && !topRight)
{
return AutoTileShape.InnerTopRight;
}
if (bottom && right && !bottomRight)
{
return AutoTileShape.InnerBottomRight;
}
if (bottom && left && !bottomLeft)
{
return AutoTileShape.InnerBottomLeft;
}
if (!top)
{
return AutoTileShape.Top;
}
if (!right)
{
return AutoTileShape.Right;
}
if (!bottom)
{
return AutoTileShape.Bottom;
}
if (!left)
{
return AutoTileShape.Left;
}
return AutoTileShape.Center;
}
private bool HasWallAt(Vector2Int worldCell)
{
Vector2Int coord = new Vector2Int(Mathf.FloorToInt(worldCell.x / (float)chunkSize), Mathf.FloorToInt(worldCell.y / (float)chunkSize));
if (!chunks.TryGetValue(coord, out GeneratedChunk chunk))
{
return SampleRock(worldCell);
}
int localX = worldCell.x - coord.x * chunkSize;
int localY = worldCell.y - coord.y * chunkSize;
if (localX < 0 || localY < 0 || localX >= chunkSize || localY >= chunkSize)
{
return SampleRock(worldCell);
}
return chunk.WallMask[localX, localY];
}
private TileBase PickEnvironmentTile(Vector2Int worldCell, WorldAutotileProfile activeProfile)
{
if (activeProfile.environmentTiles == null || activeProfile.environmentTiles.Count == 0)
{
return null;
}
float total = 0f;
for (int i = 0; i < activeProfile.environmentTiles.Count; i++)
{
EnvironmentTileEntry entry = activeProfile.environmentTiles[i];
if (entry != null && entry.tile != null)
{
total += Mathf.Max(0.01f, entry.weight);
}
}
if (total <= 0f)
{
return null;
}
float selector = Hash01(worldCell.x, worldCell.y, seed + 701);
float threshold = selector * total;
float cumulative = 0f;
for (int i = 0; i < activeProfile.environmentTiles.Count; i++)
{
EnvironmentTileEntry entry = activeProfile.environmentTiles[i];
if (entry == null || entry.tile == null)
{
continue;
}
cumulative += Mathf.Max(0.01f, entry.weight);
if (threshold <= cumulative)
{
return entry.tile;
}
}
return activeProfile.environmentTiles[0].tile;
}
private Vector2Int WorldToChunk(Vector3 position)
{
return new Vector2Int(
Mathf.FloorToInt(position.x / chunkSize),
Mathf.FloorToInt(position.y / chunkSize));
}
private Vector2Int ChunkToWorldCell(Vector2Int coord, int localX, int localY)
{
return new Vector2Int(coord.x * chunkSize + localX, coord.y * chunkSize + localY);
}
private static int Hash(int x, int y, int seed)
{
int hash = x;
hash = hash * 397 ^ y;
hash = hash * 397 ^ seed;
hash = (hash << 13) ^ hash;
return hash * (hash * hash * 15731 + 789221) + 1376312589;
}
private static float Hash01(int x, int y, int seed)
{
return (Hash(x, y, seed) & int.MaxValue) / (float)int.MaxValue;
}
private readonly struct GeneratedChunk
{
public GeneratedChunk(Transform root, Tilemap ground, Tilemap walls, Tilemap environment, bool[,] wallMask, bool[,] environmentMask)
{
Root = root;
Ground = ground;
Walls = walls;
Environment = environment;
WallMask = wallMask;
EnvironmentMask = environmentMask;
}
public Transform Root { get; }
public Tilemap Ground { get; }
public Tilemap Walls { get; }
public Tilemap Environment { get; }
public bool[,] WallMask { get; }
public bool[,] EnvironmentMask { get; }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6bacd3a0ac13e6f4a94548426dd89ebb
@@ -0,0 +1,350 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace InfiniteWorld
{
public static class ProceduralWorldArt
{
private const int TextureSize = 32;
private static readonly Dictionary<string, Sprite> SpriteCache = new Dictionary<string, Sprite>();
private static readonly Dictionary<string, Tile> TileCache = new Dictionary<string, Tile>();
public static Sprite CreatePlayerSprite()
{
const string key = "player_sprite";
if (SpriteCache.TryGetValue(key, out Sprite cached))
{
return cached;
}
Color transparent = new Color(0f, 0f, 0f, 0f);
Texture2D texture = CreateTexture(key, transparent);
Color cloak = new Color(0.13f, 0.33f, 0.79f, 1f);
Color trim = new Color(0.85f, 0.92f, 1f, 1f);
Color face = new Color(0.99f, 0.89f, 0.72f, 1f);
FillRect(texture, 10, 4, 12, 16, cloak);
FillRect(texture, 9, 18, 14, 7, cloak);
FillRect(texture, 12, 24, 8, 5, face);
FillRect(texture, 11, 23, 10, 1, trim);
FillRect(texture, 8, 12, 3, 10, trim);
FillRect(texture, 21, 12, 3, 10, trim);
FillRect(texture, 12, 0, 3, 4, cloak);
FillRect(texture, 17, 0, 3, 4, cloak);
texture.Apply();
Sprite sprite = CreateSprite(texture);
SpriteCache[key] = sprite;
return sprite;
}
public static TileBase CreateSolidTile(string key, Color color, float noiseStrength = 0.04f)
{
if (TileCache.TryGetValue(key, out Tile cached))
{
return cached;
}
Texture2D texture = CreateTexture(key, color);
ApplyNoise(texture, noiseStrength, 31);
texture.Apply();
Tile tile = CreateTile(texture, key);
TileCache[key] = tile;
return tile;
}
public static TileBase CreateFeatureTile(string key, Color fill, Color border, bool top, bool right, bool bottom, bool left, InnerCornerMask innerCornerMask = InnerCornerMask.None, bool solidCollider = false)
{
if (TileCache.TryGetValue(key, out Tile cached))
{
return cached;
}
Texture2D texture = CreateTexture(key, fill);
ApplyNoise(texture, 0.05f, 71);
DrawBorder(texture, top, right, bottom, left, border, 4);
CarveInnerCorner(texture, innerCornerMask);
texture.Apply();
Tile tile = CreateTile(texture, key);
if (solidCollider)
{
tile.colliderType = Tile.ColliderType.Grid;
}
TileCache[key] = tile;
return tile;
}
public static TileBase CreateDecorationTile(string key, Color baseColor, Color accentColor, DecorationPattern pattern)
{
if (TileCache.TryGetValue(key, out Tile cached))
{
return cached;
}
Color transparent = new Color(0f, 0f, 0f, 0f);
Texture2D texture = CreateTexture(key, transparent);
switch (pattern)
{
case DecorationPattern.Bush:
DrawDisc(texture, 16, 15, 8, baseColor);
DrawDisc(texture, 10, 13, 6, baseColor);
DrawDisc(texture, 22, 13, 6, baseColor);
DrawDisc(texture, 14, 17, 3, accentColor);
DrawDisc(texture, 21, 17, 3, accentColor);
break;
case DecorationPattern.Flower:
DrawDisc(texture, 16, 12, 2, baseColor);
DrawDisc(texture, 12, 16, 2, baseColor);
DrawDisc(texture, 20, 16, 2, baseColor);
DrawDisc(texture, 16, 20, 2, baseColor);
DrawDisc(texture, 16, 16, 2, accentColor);
DrawLine(texture, 16, 0, 16, 12, new Color(0.2f, 0.5f, 0.2f, 1f));
break;
default:
DrawDisc(texture, 12, 12, 4, baseColor);
DrawDisc(texture, 20, 18, 3, accentColor);
DrawDisc(texture, 9, 20, 2, accentColor);
break;
}
texture.Apply();
Tile tile = CreateTile(texture, key);
TileCache[key] = tile;
return tile;
}
private static Texture2D CreateTexture(string key, Color fillColor)
{
Texture2D texture = new Texture2D(TextureSize, TextureSize, TextureFormat.RGBA32, false)
{
filterMode = FilterMode.Point,
wrapMode = TextureWrapMode.Clamp,
name = key
};
Color[] pixels = new Color[TextureSize * TextureSize];
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = fillColor;
}
texture.SetPixels(pixels);
return texture;
}
private static Tile CreateTile(Texture2D texture, string key)
{
Sprite sprite = CreateSprite(texture);
Tile tile = ScriptableObject.CreateInstance<Tile>();
tile.sprite = sprite;
tile.name = key;
tile.hideFlags = HideFlags.HideAndDontSave;
return tile;
}
private static Sprite CreateSprite(Texture2D texture)
{
Sprite sprite = Sprite.Create(texture, new Rect(0f, 0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), TextureSize);
sprite.name = texture.name;
return sprite;
}
private static void ApplyNoise(Texture2D texture, float strength, int offset)
{
if (strength <= 0f)
{
return;
}
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
Color pixel = texture.GetPixel(x, y);
if (pixel.a <= 0f)
{
continue;
}
float noise = Mathf.PerlinNoise((x + offset) * 0.21f, (y + offset) * 0.21f) - 0.5f;
texture.SetPixel(x, y, pixel * (1f + noise * strength * 2f));
}
}
}
private static void DrawBorder(Texture2D texture, bool top, bool right, bool bottom, bool left, Color color, int thickness)
{
if (top)
{
FillRect(texture, 0, TextureSize - thickness, TextureSize, thickness, color);
}
if (bottom)
{
FillRect(texture, 0, 0, TextureSize, thickness, color);
}
if (left)
{
FillRect(texture, 0, 0, thickness, TextureSize, color);
}
if (right)
{
FillRect(texture, TextureSize - thickness, 0, thickness, TextureSize, color);
}
}
private static void CarveInnerCorner(Texture2D texture, InnerCornerMask innerCornerMask)
{
if (innerCornerMask == InnerCornerMask.None)
{
return;
}
Color transparent = new Color(0f, 0f, 0f, 0f);
const int radius = 8;
if ((innerCornerMask & InnerCornerMask.TopLeft) != 0)
{
CutCorner(texture, 0, TextureSize - 1, radius, transparent, 1, -1);
}
if ((innerCornerMask & InnerCornerMask.TopRight) != 0)
{
CutCorner(texture, TextureSize - 1, TextureSize - 1, radius, transparent, -1, -1);
}
if ((innerCornerMask & InnerCornerMask.BottomRight) != 0)
{
CutCorner(texture, TextureSize - 1, 0, radius, transparent, -1, 1);
}
if ((innerCornerMask & InnerCornerMask.BottomLeft) != 0)
{
CutCorner(texture, 0, 0, radius, transparent, 1, 1);
}
}
private static void CutCorner(Texture2D texture, int originX, int originY, int radius, Color transparent, int dirX, int dirY)
{
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius; y++)
{
float dx = x / (float)radius;
float dy = y / (float)radius;
if (dx * dx + dy * dy > 1f)
{
continue;
}
int px = originX + x * dirX;
int py = originY + y * dirY;
if (px >= 0 && px < TextureSize && py >= 0 && py < TextureSize)
{
texture.SetPixel(px, py, transparent);
}
}
}
}
private static void DrawDisc(Texture2D texture, int centerX, int centerY, int radius, Color color)
{
int sqrRadius = radius * radius;
for (int y = -radius; y <= radius; y++)
{
for (int x = -radius; x <= radius; x++)
{
if (x * x + y * y > sqrRadius)
{
continue;
}
int px = centerX + x;
int py = centerY + y;
if (px >= 0 && px < TextureSize && py >= 0 && py < TextureSize)
{
texture.SetPixel(px, py, color);
}
}
}
}
private static void DrawLine(Texture2D texture, int x0, int y0, int x1, int y1, Color color)
{
int dx = Mathf.Abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1;
int dy = -Mathf.Abs(y1 - y0);
int sy = y0 < y1 ? 1 : -1;
int error = dx + dy;
while (true)
{
if (x0 >= 0 && x0 < TextureSize && y0 >= 0 && y0 < TextureSize)
{
texture.SetPixel(x0, y0, color);
}
if (x0 == x1 && y0 == y1)
{
break;
}
int twiceError = 2 * error;
if (twiceError >= dy)
{
error += dy;
x0 += sx;
}
if (twiceError <= dx)
{
error += dx;
y0 += sy;
}
}
}
private static void FillRect(Texture2D texture, int x, int y, int width, int height, Color color)
{
for (int py = y; py < y + height; py++)
{
if (py < 0 || py >= TextureSize)
{
continue;
}
for (int px = x; px < x + width; px++)
{
if (px < 0 || px >= TextureSize)
{
continue;
}
texture.SetPixel(px, py, color);
}
}
}
}
public enum DecorationPattern
{
Bush,
Flower,
Rocks
}
[System.Flags]
public enum InnerCornerMask
{
None = 0,
TopLeft = 1,
TopRight = 2,
BottomRight = 4,
BottomLeft = 8
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fc8c4a69ee2e7a046adf658709613591
@@ -0,0 +1,237 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace InfiniteWorld
{
public static class RuntimeWorldProfileFactory
{
public static WorldAutotileProfile CreateFallbackProfile()
{
WorldAutotileProfile profile = ScriptableObject.CreateInstance<WorldAutotileProfile>();
profile.name = "RuntimeFallbackWorldProfile";
Color grass = new Color(0.35f, 0.62f, 0.26f, 1f);
Color wallFill = new Color(0.48f, 0.37f, 0.22f, 1f);
Color wallBorder = new Color(0.29f, 0.21f, 0.12f, 1f);
profile.baseGroundTile = ProceduralWorldArt.CreateSolidTile("base_grass", grass, 0.06f);
profile.wallTiles.center = ProceduralWorldArt.CreateFeatureTile("wall_center", wallFill, wallBorder, false, false, false, false, InnerCornerMask.None, true);
profile.wallTiles.top = ProceduralWorldArt.CreateFeatureTile("wall_top", wallFill, wallBorder, true, false, false, false, InnerCornerMask.None, true);
profile.wallTiles.right = ProceduralWorldArt.CreateFeatureTile("wall_right", wallFill, wallBorder, false, true, false, false, InnerCornerMask.None, true);
profile.wallTiles.bottom = ProceduralWorldArt.CreateFeatureTile("wall_bottom", wallFill, wallBorder, false, false, true, false, InnerCornerMask.None, true);
profile.wallTiles.left = ProceduralWorldArt.CreateFeatureTile("wall_left", wallFill, wallBorder, false, false, false, true, InnerCornerMask.None, true);
profile.wallTiles.outerTopLeft = ProceduralWorldArt.CreateFeatureTile("wall_outer_tl", wallFill, wallBorder, true, false, false, true, InnerCornerMask.None, true);
profile.wallTiles.outerTopRight = ProceduralWorldArt.CreateFeatureTile("wall_outer_tr", wallFill, wallBorder, true, true, false, false, InnerCornerMask.None, true);
profile.wallTiles.outerBottomRight = ProceduralWorldArt.CreateFeatureTile("wall_outer_br", wallFill, wallBorder, false, true, true, false, InnerCornerMask.None, true);
profile.wallTiles.outerBottomLeft = ProceduralWorldArt.CreateFeatureTile("wall_outer_bl", wallFill, wallBorder, false, false, true, true, InnerCornerMask.None, true);
profile.wallTiles.innerTopLeft = ProceduralWorldArt.CreateFeatureTile("wall_inner_tl", wallFill, wallBorder, false, false, false, false, InnerCornerMask.TopLeft, true);
profile.wallTiles.innerTopRight = ProceduralWorldArt.CreateFeatureTile("wall_inner_tr", wallFill, wallBorder, false, false, false, false, InnerCornerMask.TopRight, true);
profile.wallTiles.innerBottomRight = ProceduralWorldArt.CreateFeatureTile("wall_inner_br", wallFill, wallBorder, false, false, false, false, InnerCornerMask.BottomRight, true);
profile.wallTiles.innerBottomLeft = ProceduralWorldArt.CreateFeatureTile("wall_inner_bl", wallFill, wallBorder, false, false, false, false, InnerCornerMask.BottomLeft, true);
profile.environmentTiles.Add(new EnvironmentTileEntry
{
id = "Bush",
tile = ProceduralWorldArt.CreateDecorationTile("env_bush", new Color(0.15f, 0.45f, 0.18f, 1f), new Color(0.23f, 0.58f, 0.25f, 1f), DecorationPattern.Bush),
weight = 1.4f
});
profile.environmentTiles.Add(new EnvironmentTileEntry
{
id = "FlowerBlue",
tile = ProceduralWorldArt.CreateDecorationTile("env_flower_blue", new Color(0.44f, 0.62f, 0.96f, 1f), new Color(0.98f, 0.91f, 0.52f, 1f), DecorationPattern.Flower),
weight = 0.75f
});
profile.environmentTiles.Add(new EnvironmentTileEntry
{
id = "Rock",
tile = ProceduralWorldArt.CreateDecorationTile("env_rock", new Color(0.48f, 0.48f, 0.52f, 1f), new Color(0.69f, 0.69f, 0.74f, 1f), DecorationPattern.Rocks),
weight = 0.9f
});
return profile;
}
public static List<ChunkTemplate> CreateFallbackTemplates(int size)
{
List<ChunkTemplate> templates = new List<ChunkTemplate>
{
CreateCross(size),
CreateStraightHorizontal(size),
CreateStraightVertical(size),
CreateCorner(size, true, true, false, false),
CreateCorner(size, false, true, true, false),
CreateCorner(size, false, false, true, true),
CreateCorner(size, true, false, false, true),
CreateTJunction(size, true, true, true, false),
CreateTJunction(size, false, true, true, true),
CreateTJunction(size, true, false, true, true),
CreateTJunction(size, true, true, false, true)
};
return templates;
}
private static ChunkTemplate CreateCross(int size)
{
ChunkTemplate template = CreateTemplate("Runtime_Cross", size, size, true, true, true, true);
DrawRoom(template, 3, 3, size - 6, size - 6);
DrawPillar(template, 4, 4);
DrawPillar(template, size - 5, 4);
DrawPillar(template, 4, size - 5);
DrawPillar(template, size - 5, size - 5);
ScatterEnvironment(template, 3);
return template;
}
private static ChunkTemplate CreateStraightHorizontal(int size)
{
ChunkTemplate template = CreateTemplate("Runtime_Straight_H", size, size, false, true, false, true);
DrawRoom(template, 2, 4, size - 4, size - 8);
DrawSideAlcoves(template, horizontal: true);
ScatterEnvironment(template, 2);
return template;
}
private static ChunkTemplate CreateStraightVertical(int size)
{
ChunkTemplate template = CreateTemplate("Runtime_Straight_V", size, size, true, false, true, false);
DrawRoom(template, 4, 2, size - 8, size - 4);
DrawSideAlcoves(template, horizontal: false);
ScatterEnvironment(template, 2);
return template;
}
private static ChunkTemplate CreateCorner(int size, bool top, bool right, bool bottom, bool left)
{
ChunkTemplate template = CreateTemplate($"Runtime_Corner_{top}_{right}_{bottom}_{left}", size, size, top, right, bottom, left);
DrawRoom(template, 3, 3, size - 6, size - 6);
if (!top)
{
DrawWallLine(template, 5, size - 5, size - 6, size - 5);
}
if (!right)
{
DrawWallLine(template, size - 5, 5, size - 5, size - 6);
}
if (!bottom)
{
DrawWallLine(template, 5, 4, size - 6, 4);
}
if (!left)
{
DrawWallLine(template, 4, 5, 4, size - 6);
}
ScatterEnvironment(template, 2);
return template;
}
private static ChunkTemplate CreateTJunction(int size, bool top, bool right, bool bottom, bool left)
{
ChunkTemplate template = CreateTemplate($"Runtime_T_{top}_{right}_{bottom}_{left}", size, size, top, right, bottom, left);
DrawRoom(template, 2, 2, size - 4, size - 4);
DrawPillar(template, size / 2, size / 2);
ScatterEnvironment(template, 3);
return template;
}
private static ChunkTemplate CreateTemplate(string name, int width, int height, bool top, bool right, bool bottom, bool left)
{
ChunkTemplate template = ScriptableObject.CreateInstance<ChunkTemplate>();
template.name = name;
template.Resize(width, height);
template.exitTop = top;
template.exitRight = right;
template.exitBottom = bottom;
template.exitLeft = left;
template.ApplyBorderWallsFromExits(3);
return template;
}
private static void DrawRoom(ChunkTemplate template, int xMin, int yMin, int width, int height)
{
for (int x = xMin; x < xMin + width; x++)
{
template.SetWall(x, yMin, true);
template.SetWall(x, yMin + height - 1, true);
}
for (int y = yMin; y < yMin + height; y++)
{
template.SetWall(xMin, y, true);
template.SetWall(xMin + width - 1, y, true);
}
template.CarveExit(ChunkExit.Top, 3);
template.CarveExit(ChunkExit.Right, 3);
template.CarveExit(ChunkExit.Bottom, 3);
template.CarveExit(ChunkExit.Left, 3);
}
private static void DrawPillar(ChunkTemplate template, int x, int y)
{
for (int py = -1; py <= 1; py++)
{
for (int px = -1; px <= 1; px++)
{
template.SetWall(x + px, y + py, true);
}
}
}
private static void DrawSideAlcoves(ChunkTemplate template, bool horizontal)
{
if (horizontal)
{
DrawWallLine(template, template.width / 2 - 1, 5, template.width / 2 - 1, 7);
DrawWallLine(template, template.width / 2 + 1, template.height - 8, template.width / 2 + 1, template.height - 6);
}
else
{
DrawWallLine(template, 5, template.height / 2 - 1, 7, template.height / 2 - 1);
DrawWallLine(template, template.width - 8, template.height / 2 + 1, template.width - 6, template.height / 2 + 1);
}
}
private static void DrawWallLine(ChunkTemplate template, int x0, int y0, int x1, int y1)
{
int dx = Math.Sign(x1 - x0);
int dy = Math.Sign(y1 - y0);
int x = x0;
int y = y0;
while (true)
{
template.SetWall(x, y, true);
if (x == x1 && y == y1)
{
break;
}
if (x != x1)
{
x += dx;
}
if (y != y1)
{
y += dy;
}
}
}
private static void ScatterEnvironment(ChunkTemplate template, int spacing)
{
for (int y = 2; y < template.height - 2; y += spacing + 2)
{
for (int x = 2; x < template.width - 2; x += spacing + 3)
{
if (!template.GetWall(x, y))
{
template.SetEnvironment(x, y, ((x + y) & 1) == 0);
}
}
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e5b485257bdc222448c314a7cd173845
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace InfiniteWorld
{
[CreateAssetMenu(menuName = "Infinite World/World Autotile Profile", fileName = "WorldAutotileProfile")]
public class WorldAutotileProfile : ScriptableObject
{
public TileBase baseGroundTile;
public AutoTileDefinition wallTiles = new AutoTileDefinition();
public List<EnvironmentTileEntry> environmentTiles = new List<EnvironmentTileEntry>();
public TileBase GetWallTile(AutoTileShape shape)
{
return wallTiles != null ? wallTiles.GetTile(shape) : null;
}
public bool HasAnyAssignedTiles()
{
if (baseGroundTile != null)
{
return true;
}
if (wallTiles != null && wallTiles.HasAnyAssignedTiles())
{
return true;
}
for (int i = 0; i < environmentTiles.Count; i++)
{
if (environmentTiles[i] != null && environmentTiles[i].tile != null)
{
return true;
}
}
return false;
}
}
public enum AutoTileShape
{
Center,
Top,
Right,
Bottom,
Left,
OuterTopLeft,
OuterTopRight,
OuterBottomRight,
OuterBottomLeft,
InnerTopLeft,
InnerTopRight,
InnerBottomRight,
InnerBottomLeft
}
[Serializable]
public class AutoTileDefinition
{
public TileBase center;
public TileBase top;
public TileBase right;
public TileBase bottom;
public TileBase left;
public TileBase outerTopLeft;
public TileBase outerTopRight;
public TileBase outerBottomRight;
public TileBase outerBottomLeft;
public TileBase innerTopLeft;
public TileBase innerTopRight;
public TileBase innerBottomRight;
public TileBase innerBottomLeft;
public TileBase GetTile(AutoTileShape shape)
{
TileBase tile = shape switch
{
AutoTileShape.Center => center,
AutoTileShape.Top => top,
AutoTileShape.Right => right,
AutoTileShape.Bottom => bottom,
AutoTileShape.Left => left,
AutoTileShape.OuterTopLeft => outerTopLeft,
AutoTileShape.OuterTopRight => outerTopRight,
AutoTileShape.OuterBottomRight => outerBottomRight,
AutoTileShape.OuterBottomLeft => outerBottomLeft,
AutoTileShape.InnerTopLeft => innerTopLeft,
AutoTileShape.InnerTopRight => innerTopRight,
AutoTileShape.InnerBottomRight => innerBottomRight,
AutoTileShape.InnerBottomLeft => innerBottomLeft,
_ => center
};
return tile ?? center;
}
public bool HasAnyAssignedTiles()
{
return center != null ||
top != null ||
right != null ||
bottom != null ||
left != null ||
outerTopLeft != null ||
outerTopRight != null ||
outerBottomRight != null ||
outerBottomLeft != null ||
innerTopLeft != null ||
innerTopRight != null ||
innerBottomRight != null ||
innerBottomLeft != null;
}
}
[Serializable]
public class EnvironmentTileEntry
{
public string id = "Environment";
public TileBase tile;
[Min(0f)] public float weight = 1f;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e36b055e81273a54c8e736e2ef74fa50
+121
View File
@@ -0,0 +1,121 @@
{
"templatePinStates": [],
"dependencyTypeInfos": [
{
"userAdded": false,
"type": "UnityEngine.AnimationClip",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.Animations.AnimatorController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.AnimatorOverrideController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.Audio.AudioMixerController",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.ComputeShader",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Cubemap",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.GameObject",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.LightingDataAsset",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.LightingSettings",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Material",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.MonoScript",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.PhysicsMaterial",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.PhysicsMaterial2D",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.PostProcessing.PostProcessResources",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Rendering.VolumeProfile",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEditor.SceneAsset",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Shader",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.ShaderVariantCollection",
"defaultInstantiationMode": 1
},
{
"userAdded": false,
"type": "UnityEngine.Texture",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Texture2D",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.Timeline.TimelineAsset",
"defaultInstantiationMode": 0
}
],
"defaultDependencyTypeInfo": {
"userAdded": false,
"type": "<default_scene_template_dependencies>",
"defaultInstantiationMode": 1
},
"newSceneOverride": 0
}