first commit
This commit is contained in:
@@ -0,0 +1,446 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the complete lab scene from code:
|
||||
/// folders, materials, prefabs, player, camera, generator, UI and saved scene.
|
||||
/// </summary>
|
||||
public static class Lab1SceneBuilder
|
||||
{
|
||||
private const string ScriptsFolder = "Assets/_Scripts";
|
||||
private const string ScenesFolder = "Assets/_Scenes";
|
||||
private const string PrefabsFolder = "Assets/_Prefabs";
|
||||
private const string MaterialsFolder = "Assets/_Materials";
|
||||
private const string EditorFolder = "Assets/Editor";
|
||||
private const string ScenePath = "Assets/_Scenes/Level_Procedural.unity";
|
||||
|
||||
[MenuItem("Tools/Technical Game Design/Lab1/Create Procedural Level Scene")]
|
||||
public static void CreateProceduralLevelScene()
|
||||
{
|
||||
EnsureProjectFolders();
|
||||
|
||||
Material basicMaterial = CreateOrUpdateMaterial("M_Platform_Basic", new Color(0.35f, 0.48f, 0.68f));
|
||||
Material wideMaterial = CreateOrUpdateMaterial("M_Platform_Wide", new Color(0.28f, 0.62f, 0.43f));
|
||||
Material narrowMaterial = CreateOrUpdateMaterial("M_Platform_Narrow", new Color(0.82f, 0.65f, 0.28f));
|
||||
Material obstacleBaseMaterial = CreateOrUpdateMaterial("M_Platform_Obstacle_Base", new Color(0.42f, 0.42f, 0.48f));
|
||||
Material obstacleMaterial = CreateOrUpdateMaterial("M_Obstacle_Red", new Color(0.86f, 0.16f, 0.14f));
|
||||
Material directionMaterial = CreateOrUpdateMaterial("M_Direction_Marker", new Color(0.15f, 0.9f, 0.95f));
|
||||
Material playerMaterial = CreateOrUpdateMaterial("M_Player", new Color(0.2f, 0.2f, 0.24f));
|
||||
|
||||
GameObject basicPrefab = CreatePlatformPrefab(
|
||||
"Platform_Basic",
|
||||
PlatformInfo.PlatformType.Basic,
|
||||
new Vector3(3f, 0.5f, 3f),
|
||||
basicMaterial,
|
||||
directionMaterial,
|
||||
false,
|
||||
obstacleMaterial);
|
||||
|
||||
GameObject widePrefab = CreatePlatformPrefab(
|
||||
"Platform_Wide",
|
||||
PlatformInfo.PlatformType.Wide,
|
||||
new Vector3(5f, 0.5f, 3f),
|
||||
wideMaterial,
|
||||
directionMaterial,
|
||||
false,
|
||||
obstacleMaterial);
|
||||
|
||||
GameObject narrowPrefab = CreatePlatformPrefab(
|
||||
"Platform_Narrow",
|
||||
PlatformInfo.PlatformType.Narrow,
|
||||
new Vector3(2f, 0.5f, 3f),
|
||||
narrowMaterial,
|
||||
directionMaterial,
|
||||
false,
|
||||
obstacleMaterial);
|
||||
|
||||
GameObject obstaclePrefab = CreatePlatformPrefab(
|
||||
"Platform_Obstacle",
|
||||
PlatformInfo.PlatformType.Obstacle,
|
||||
new Vector3(3f, 0.5f, 3f),
|
||||
obstacleBaseMaterial,
|
||||
directionMaterial,
|
||||
true,
|
||||
obstacleMaterial);
|
||||
|
||||
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
scene.name = "Level_Procedural";
|
||||
|
||||
SetupLighting();
|
||||
GameObject player = CreatePlayer(playerMaterial);
|
||||
Camera camera = CreateCamera(player.transform);
|
||||
LevelGenerator generator = CreateGenerator(player, basicPrefab, widePrefab, narrowPrefab, obstaclePrefab);
|
||||
LevelGenUI ui = CreateUi(generator);
|
||||
|
||||
generator.ui = ui;
|
||||
ui.generator = generator;
|
||||
generator.GenerateInitialLevel();
|
||||
ui.UpdateInfo();
|
||||
|
||||
Selection.activeObject = generator.gameObject;
|
||||
EditorSceneManager.SaveScene(scene, ScenePath);
|
||||
AddSceneToBuildSettings(ScenePath);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("Lab 1 procedural level scene created: " + ScenePath);
|
||||
}
|
||||
|
||||
private static void EnsureProjectFolders()
|
||||
{
|
||||
EnsureFolder(ScriptsFolder);
|
||||
EnsureFolder(ScenesFolder);
|
||||
EnsureFolder(PrefabsFolder);
|
||||
EnsureFolder(MaterialsFolder);
|
||||
EnsureFolder(EditorFolder);
|
||||
}
|
||||
|
||||
private static void EnsureFolder(string path)
|
||||
{
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] parts = path.Split('/');
|
||||
string current = parts[0];
|
||||
for (int i = 1; i < parts.Length; i++)
|
||||
{
|
||||
string next = current + "/" + parts[i];
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
{
|
||||
AssetDatabase.CreateFolder(current, parts[i]);
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material CreateOrUpdateMaterial(string materialName, Color color)
|
||||
{
|
||||
string path = MaterialsFolder + "/" + materialName + ".mat";
|
||||
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
||||
if (material == null)
|
||||
{
|
||||
Shader shader = Shader.Find("Standard");
|
||||
if (shader == null)
|
||||
{
|
||||
shader = Shader.Find("Diffuse");
|
||||
}
|
||||
|
||||
material = new Material(shader);
|
||||
AssetDatabase.CreateAsset(material, path);
|
||||
}
|
||||
|
||||
material.color = color;
|
||||
EditorUtility.SetDirty(material);
|
||||
return material;
|
||||
}
|
||||
|
||||
private static GameObject CreatePlatformPrefab(
|
||||
string prefabName,
|
||||
PlatformInfo.PlatformType platformType,
|
||||
Vector3 platformScale,
|
||||
Material baseMaterial,
|
||||
Material directionMaterial,
|
||||
bool withObstacle,
|
||||
Material obstacleMaterial)
|
||||
{
|
||||
GameObject root = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
root.name = prefabName;
|
||||
root.transform.localScale = platformScale;
|
||||
root.GetComponent<Renderer>().sharedMaterial = baseMaterial;
|
||||
|
||||
PlatformInfo info = root.AddComponent<PlatformInfo>();
|
||||
info.platformType = platformType;
|
||||
info.generatedIndex = -1;
|
||||
info.isSafe = platformType != PlatformInfo.PlatformType.Obstacle;
|
||||
|
||||
CreateDirectionMarker(root.transform, platformScale, directionMaterial);
|
||||
|
||||
if (withObstacle)
|
||||
{
|
||||
GameObject obstacle = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
obstacle.name = "Obstacle";
|
||||
obstacle.transform.SetParent(root.transform, false);
|
||||
obstacle.transform.localPosition = new Vector3(
|
||||
0.9f / platformScale.x,
|
||||
0.78f / platformScale.y,
|
||||
0.35f / platformScale.z);
|
||||
obstacle.transform.localScale = new Vector3(
|
||||
0.75f / platformScale.x,
|
||||
1.05f / platformScale.y,
|
||||
0.95f / platformScale.z);
|
||||
obstacle.GetComponent<Renderer>().sharedMaterial = obstacleMaterial;
|
||||
}
|
||||
|
||||
string path = PrefabsFolder + "/" + prefabName + ".prefab";
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
Object.DestroyImmediate(root);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
private static void CreateDirectionMarker(Transform parent, Vector3 platformScale, Material directionMaterial)
|
||||
{
|
||||
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
marker.name = "Direction_Marker";
|
||||
marker.transform.SetParent(parent, false);
|
||||
marker.transform.localPosition = new Vector3(0f, 0.58f, 0.22f);
|
||||
marker.transform.localScale = new Vector3(0.55f, 0.08f, 0.1f);
|
||||
marker.GetComponent<Renderer>().sharedMaterial = directionMaterial;
|
||||
|
||||
Collider markerCollider = marker.GetComponent<Collider>();
|
||||
if (markerCollider != null)
|
||||
{
|
||||
Object.DestroyImmediate(markerCollider);
|
||||
}
|
||||
}
|
||||
|
||||
private static GameObject CreatePlayer(Material playerMaterial)
|
||||
{
|
||||
GameObject player = GameObject.CreatePrimitive(PrimitiveType.Capsule);
|
||||
player.name = "Player";
|
||||
player.transform.position = new Vector3(0f, 1.35f, 0f);
|
||||
player.GetComponent<Renderer>().sharedMaterial = playerMaterial;
|
||||
|
||||
Rigidbody rb = player.AddComponent<Rigidbody>();
|
||||
rb.mass = 1f;
|
||||
rb.drag = 0f;
|
||||
rb.angularDrag = 0.05f;
|
||||
rb.interpolation = RigidbodyInterpolation.Interpolate;
|
||||
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
|
||||
rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
|
||||
|
||||
SimplePlayerController controller = player.AddComponent<SimplePlayerController>();
|
||||
controller.moveSpeed = 6f;
|
||||
controller.jumpForce = 7f;
|
||||
controller.groundCheckRadius = 0.25f;
|
||||
controller.groundMask = LayerMask.GetMask("Default");
|
||||
controller.fallResetY = -10f;
|
||||
controller.lastSafePosition = player.transform.position;
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
private static Camera CreateCamera(Transform player)
|
||||
{
|
||||
GameObject cameraObject = new GameObject("Main Camera");
|
||||
Camera camera = cameraObject.AddComponent<Camera>();
|
||||
camera.tag = "MainCamera";
|
||||
camera.clearFlags = CameraClearFlags.Skybox;
|
||||
camera.fieldOfView = 60f;
|
||||
camera.nearClipPlane = 0.1f;
|
||||
camera.farClipPlane = 250f;
|
||||
|
||||
CameraFollow follow = cameraObject.AddComponent<CameraFollow>();
|
||||
follow.target = player;
|
||||
follow.offset = new Vector3(0f, 8f, -8f);
|
||||
follow.followSpeed = 6f;
|
||||
follow.lookHeight = 1f;
|
||||
|
||||
cameraObject.transform.position = player.position + follow.offset;
|
||||
cameraObject.transform.LookAt(player.position + Vector3.up);
|
||||
return camera;
|
||||
}
|
||||
|
||||
private static LevelGenerator CreateGenerator(
|
||||
GameObject player,
|
||||
GameObject basicPrefab,
|
||||
GameObject widePrefab,
|
||||
GameObject narrowPrefab,
|
||||
GameObject obstaclePrefab)
|
||||
{
|
||||
GameObject generatorObject = new GameObject("LevelGenerator");
|
||||
LevelGenerator generator = generatorObject.AddComponent<LevelGenerator>();
|
||||
generator.platformPrefabs = new[] { basicPrefab, widePrefab, narrowPrefab, obstaclePrefab };
|
||||
generator.player = player.transform;
|
||||
generator.playerController = player.GetComponent<SimplePlayerController>();
|
||||
generator.initialPlatformCount = 12;
|
||||
generator.stepDistance = 3.5f;
|
||||
generator.generationThreshold = 12f;
|
||||
generator.removeThreshold = 18f;
|
||||
generator.obstacleChance = 0.3f;
|
||||
generator.sideOffsetRange = 2f;
|
||||
generator.infiniteMode = true;
|
||||
generator.maxPlatformsAlive = 30;
|
||||
return generator;
|
||||
}
|
||||
|
||||
private static void SetupLighting()
|
||||
{
|
||||
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
|
||||
RenderSettings.ambientLight = new Color(0.45f, 0.48f, 0.54f);
|
||||
RenderSettings.skybox = null;
|
||||
|
||||
GameObject lightObject = new GameObject("Directional Light");
|
||||
Light light = lightObject.AddComponent<Light>();
|
||||
light.type = LightType.Directional;
|
||||
light.intensity = 1.15f;
|
||||
light.shadows = LightShadows.Soft;
|
||||
lightObject.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
|
||||
}
|
||||
|
||||
private static LevelGenUI CreateUi(LevelGenerator generator)
|
||||
{
|
||||
Font font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||
|
||||
GameObject canvasObject = new GameObject("Canvas");
|
||||
Canvas canvas = canvasObject.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvasObject.AddComponent<GraphicRaycaster>();
|
||||
|
||||
CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
|
||||
LevelGenUI ui = canvasObject.AddComponent<LevelGenUI>();
|
||||
ui.generator = generator;
|
||||
|
||||
ui.titleText = CreateText(
|
||||
"Title",
|
||||
canvasObject.transform,
|
||||
font,
|
||||
30,
|
||||
FontStyle.Bold,
|
||||
new Color(1f, 1f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(28f, -24f),
|
||||
new Vector2(900f, 42f),
|
||||
TextAnchor.UpperLeft);
|
||||
|
||||
ui.hintsText = CreateText(
|
||||
"Controls_Hints",
|
||||
canvasObject.transform,
|
||||
font,
|
||||
22,
|
||||
FontStyle.Normal,
|
||||
new Color(0.9f, 0.95f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(0f, 1f),
|
||||
new Vector2(30f, -82f),
|
||||
new Vector2(360f, 110f),
|
||||
TextAnchor.UpperLeft);
|
||||
|
||||
ui.parametersText = CreateText(
|
||||
"Generation_Parameters",
|
||||
canvasObject.transform,
|
||||
font,
|
||||
22,
|
||||
FontStyle.Normal,
|
||||
new Color(0.92f, 1f, 0.92f),
|
||||
new Vector2(1f, 1f),
|
||||
new Vector2(1f, 1f),
|
||||
new Vector2(1f, 1f),
|
||||
new Vector2(-370f, -28f),
|
||||
new Vector2(340f, 170f),
|
||||
TextAnchor.UpperLeft);
|
||||
|
||||
ui.statusText = CreateText(
|
||||
"Status",
|
||||
canvasObject.transform,
|
||||
font,
|
||||
20,
|
||||
FontStyle.Normal,
|
||||
new Color(1f, 0.94f, 0.78f),
|
||||
new Vector2(0f, 0f),
|
||||
new Vector2(0f, 0f),
|
||||
new Vector2(0f, 0f),
|
||||
new Vector2(30f, 28f),
|
||||
new Vector2(620f, 65f),
|
||||
TextAnchor.LowerLeft);
|
||||
|
||||
ui.messageText = CreateText(
|
||||
"Message",
|
||||
canvasObject.transform,
|
||||
font,
|
||||
26,
|
||||
FontStyle.Bold,
|
||||
new Color(0.55f, 1f, 0.55f),
|
||||
new Vector2(0.5f, 1f),
|
||||
new Vector2(0.5f, 1f),
|
||||
new Vector2(0.5f, 1f),
|
||||
new Vector2(-180f, -150f),
|
||||
new Vector2(360f, 42f),
|
||||
TextAnchor.MiddleCenter);
|
||||
ui.messageText.gameObject.SetActive(false);
|
||||
|
||||
CreateEventSystem();
|
||||
return ui;
|
||||
}
|
||||
|
||||
private static Text CreateText(
|
||||
string name,
|
||||
Transform parent,
|
||||
Font font,
|
||||
int fontSize,
|
||||
FontStyle fontStyle,
|
||||
Color color,
|
||||
Vector2 anchorMin,
|
||||
Vector2 anchorMax,
|
||||
Vector2 pivot,
|
||||
Vector2 anchoredPosition,
|
||||
Vector2 sizeDelta,
|
||||
TextAnchor alignment)
|
||||
{
|
||||
GameObject textObject = new GameObject(name);
|
||||
textObject.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rectTransform = textObject.AddComponent<RectTransform>();
|
||||
rectTransform.anchorMin = anchorMin;
|
||||
rectTransform.anchorMax = anchorMax;
|
||||
rectTransform.pivot = pivot;
|
||||
rectTransform.anchoredPosition = anchoredPosition;
|
||||
rectTransform.sizeDelta = sizeDelta;
|
||||
|
||||
Text text = textObject.AddComponent<Text>();
|
||||
text.font = font;
|
||||
text.fontSize = fontSize;
|
||||
text.fontStyle = fontStyle;
|
||||
text.color = color;
|
||||
text.alignment = alignment;
|
||||
text.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
text.verticalOverflow = VerticalWrapMode.Overflow;
|
||||
text.raycastTarget = false;
|
||||
|
||||
Outline outline = textObject.AddComponent<Outline>();
|
||||
outline.effectColor = new Color(0f, 0f, 0f, 0.8f);
|
||||
outline.effectDistance = new Vector2(1.4f, -1.4f);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static void CreateEventSystem()
|
||||
{
|
||||
GameObject eventSystemObject = new GameObject("EventSystem");
|
||||
eventSystemObject.AddComponent<EventSystem>();
|
||||
eventSystemObject.AddComponent<StandaloneInputModule>();
|
||||
}
|
||||
|
||||
private static void AddSceneToBuildSettings(string scenePath)
|
||||
{
|
||||
EditorBuildSettingsScene[] oldScenes = EditorBuildSettings.scenes;
|
||||
for (int i = 0; i < oldScenes.Length; i++)
|
||||
{
|
||||
if (oldScenes[i].path == scenePath)
|
||||
{
|
||||
oldScenes[i].enabled = true;
|
||||
EditorBuildSettings.scenes = oldScenes;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EditorBuildSettingsScene[] newScenes = new EditorBuildSettingsScene[oldScenes.Length + 1];
|
||||
oldScenes.CopyTo(newScenes, 0);
|
||||
newScenes[newScenes.Length - 1] = new EditorBuildSettingsScene(scenePath, true);
|
||||
EditorBuildSettings.scenes = newScenes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32c22bb9a7ab2490daf033faee36e98d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user