Files
OTG_6/Assets/Editor/Lab6SceneBuilder.cs
T

861 lines
39 KiB
C#
Raw Normal View History

2026-06-04 21:37:43 +03:00
using System.Collections.Generic;
using System.IO;
using OTG.Lab06.Abilities;
using OTG.Lab06.Characters;
using OTG.Lab06.Combat;
using OTG.Lab06.Effects;
using OTG.Lab06.UI;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public static class Lab6SceneBuilder
{
private const string Root = "Assets";
private const string ScenePath = "Assets/_Scenes/Lab06_AbilitySystem.unity";
private const string FontFilePath = "Assets/_Fonts/Ubuntu-R.ttf";
[MenuItem("Tools/Technical Game Design/Lab6/Create Ability System Scene")]
public static void CreateAbilitySystemScene()
{
CreateProjectStructure();
Font font = CopyUbuntuFont();
Dictionary<string, Material> materials = CreateMaterials();
Dictionary<string, Sprite> icons = CreateIcons();
GameObject deathEffect = CreateDeathEffectPrefab(materials["ArcaneViolet"]);
FloatingDamage floatingDamage = CreateFloatingDamagePrefab(font);
GameObject fireballEffect = CreateFireballPrefab(materials["Fire"], materials["FireTrail"]);
GameObject iceNovaEffect = CreateIceNovaPrefab(materials["Ice"]);
GameObject blinkEffect = CreateBlinkPrefab(materials["Blink"]);
AbilityData fireball = CreateAbility("Fireball", "fireball", "Launches a burning projectile that detonates on the first enemy hit.", 8f, 2f, 30f, 24f, icons["Fireball"], fireballEffect);
AbilityData iceNova = CreateAbility("Ice Nova", "ice_nova", "Releases a sharp frost ring that damages all nearby enemies.", 14f, 5f, 20f, 5.5f, icons["IceNova"], iceNovaEffect);
AbilityData blink = CreateAbility("Blink", "blink", "Teleports forward and stops safely before obstacles.", 10f, 6f, 0f, 8f, icons["Blink"], blinkEffect);
GameObject enemyPrefab = CreateEnemyPrefab(materials["Enemy"], deathEffect);
GameObject crystalPrefab = CreateManaCrystalPrefab(materials["ManaCrystal"]);
AbilitySlotUI slotPrefab = CreateAbilitySlotPrefab(font, materials);
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
scene.name = "Lab06_AbilitySystem";
BuildLighting();
BuildArena(materials);
GameObject player = BuildPlayer(materials, fireball, iceNova, blink);
BuildCamera(player.transform);
BuildEnemies(enemyPrefab);
BuildTrainingMannequins(materials);
BuildManaCrystals(crystalPrefab);
BuildFloatingDamageManager(floatingDamage);
BuildUI(player, slotPrefab, font, materials);
EditorSceneManager.SaveScene(scene, ScenePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("Lab06", "Ability System scene has been generated.", "OK");
}
private static void CreateProjectStructure()
{
string[] folders =
{
"_Scenes", "_Scripts", "_Scripts/Core", "_Scripts/Abilities", "_Scripts/Combat",
"_Scripts/Characters", "_Scripts/UI", "_Scripts/Effects", "_Data", "_Data/Abilities",
"_Prefabs", "_Prefabs/Abilities", "_Prefabs/UI", "_Prefabs/Enemies", "_Materials", "_Fonts", "Editor"
};
foreach (string folder in folders)
{
string path = $"{Root}/{folder}";
if (!AssetDatabase.IsValidFolder(path))
{
string parent = Path.GetDirectoryName(path).Replace('\\', '/');
string name = Path.GetFileName(path);
AssetDatabase.CreateFolder(parent, name);
}
}
}
private static Font CopyUbuntuFont()
{
string[] candidates =
{
"/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf",
"/usr/share/fonts/truetype/ubuntu/Ubuntu-M.ttf",
"/usr/share/fonts/TTF/Ubuntu-R.ttf"
};
string source = null;
foreach (string candidate in candidates)
{
if (File.Exists(candidate))
{
source = candidate;
break;
}
}
if (source == null)
{
throw new FileNotFoundException("Ubuntu font was not found in standard Linux font directories.");
}
File.Copy(source, FontFilePath, true);
AssetDatabase.ImportAsset(FontFilePath, ImportAssetOptions.ForceUpdate);
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
Font font = AssetDatabase.LoadAssetAtPath<Font>(FontFilePath);
if (font == null)
{
throw new FileNotFoundException("Ubuntu font was copied but Unity could not import it as a Font asset.");
}
return font;
}
private static Dictionary<string, Material> CreateMaterials()
{
var result = new Dictionary<string, Material>
{
["ArenaFloor"] = CreateMaterial("ArenaFloor", new Color(0.48f, 0.49f, 0.46f), 0.08f),
["ArenaTrim"] = CreateMaterial("ArenaTrim", new Color(0.34f, 0.36f, 0.36f), 0.06f),
["ArenaWall"] = CreateMaterial("ArenaWall", new Color(0.42f, 0.43f, 0.41f), 0.05f),
["ArenaLine"] = CreateMaterial("ArenaLine", new Color(0.58f, 0.60f, 0.57f), 0.03f),
["Player"] = CreateMaterial("Player", new Color(0.12f, 0.39f, 0.82f), 0.25f),
["Enemy"] = CreateMaterial("Enemy", new Color(0.58f, 0.08f, 0.12f), 0.16f),
["Mannequin"] = CreateMaterial("Mannequin", new Color(0.56f, 0.43f, 0.28f), 0.05f),
["Fire"] = CreateMaterial("Fire", new Color(1f, 0.32f, 0.08f), 0.4f, new Color(2.2f, 0.55f, 0.08f)),
["FireTrail"] = CreateMaterial("FireTrail", new Color(1f, 0.68f, 0.12f, 0.55f), 0.15f, new Color(1.3f, 0.55f, 0.12f)),
["Ice"] = CreateMaterial("Ice", new Color(0.42f, 0.85f, 1f, 0.62f), 0.25f, new Color(0.2f, 0.9f, 1.7f)),
["Blink"] = CreateMaterial("Blink", new Color(0.52f, 0.48f, 1f, 0.66f), 0.3f, new Color(0.75f, 0.55f, 2.1f)),
["ManaCrystal"] = CreateMaterial("ManaCrystal", new Color(0.18f, 0.9f, 0.95f), 0.45f, new Color(0.05f, 1.4f, 1.8f)),
["ArcaneViolet"] = CreateMaterial("ArcaneViolet", new Color(0.58f, 0.23f, 0.86f), 0.35f, new Color(0.8f, 0.2f, 1.4f)),
["UIBack"] = CreateMaterial("UIBack", new Color(0.07f, 0.08f, 0.09f, 0.88f), 0f),
};
return result;
}
private static Material CreateMaterial(string name, Color color, float smoothness, Color? emission = null)
{
string path = $"Assets/_Materials/{name}.mat";
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
if (material == null)
{
material = new Material(Shader.Find("Standard"));
AssetDatabase.CreateAsset(material, path);
}
material.color = color;
material.SetFloat("_Glossiness", smoothness);
if (emission.HasValue)
{
material.EnableKeyword("_EMISSION");
material.SetColor("_EmissionColor", emission.Value);
}
else
{
material.DisableKeyword("_EMISSION");
}
EditorUtility.SetDirty(material);
return material;
}
private static Dictionary<string, Sprite> CreateIcons()
{
return new Dictionary<string, Sprite>
{
["Fireball"] = CreateIcon("FireballIcon", new Color(0.95f, 0.22f, 0.07f), new Color(1f, 0.75f, 0.15f)),
["IceNova"] = CreateIcon("IceNovaIcon", new Color(0.1f, 0.55f, 0.95f), new Color(0.78f, 0.96f, 1f)),
["Blink"] = CreateIcon("BlinkIcon", new Color(0.35f, 0.2f, 0.85f), new Color(0.85f, 0.78f, 1f))
};
}
private static Sprite CreateIcon(string name, Color baseColor, Color markColor)
{
string pngPath = $"Assets/_Data/Abilities/{name}.png";
Texture2D texture = new Texture2D(64, 64, TextureFormat.RGBA32, false);
for (int y = 0; y < 64; y++)
{
for (int x = 0; x < 64; x++)
{
Vector2 p = new Vector2(x - 31.5f, y - 31.5f);
float ring = Mathf.InverseLerp(32f, 12f, p.magnitude);
Color color = Color.Lerp(new Color(0.04f, 0.05f, 0.06f, 1f), baseColor, Mathf.Clamp01(ring));
if (Mathf.Abs(p.x) < 5f || Mathf.Abs(p.y) < 5f || p.magnitude < 12f)
{
color = Color.Lerp(color, markColor, 0.75f);
}
texture.SetPixel(x, y, color);
}
}
texture.Apply();
File.WriteAllBytes(pngPath, texture.EncodeToPNG());
Object.DestroyImmediate(texture);
AssetDatabase.ImportAsset(pngPath, ImportAssetOptions.ForceUpdate);
TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(pngPath);
importer.textureType = TextureImporterType.Sprite;
importer.spritePixelsPerUnit = 64f;
importer.mipmapEnabled = false;
importer.SaveAndReimport();
return AssetDatabase.LoadAssetAtPath<Sprite>(pngPath);
}
private static AbilityData CreateAbility(string displayName, string id, string description, float mana, float cooldown, float damage, float range, Sprite icon, GameObject effectPrefab)
{
string path = $"Assets/_Data/Abilities/{displayName.Replace(" ", string.Empty)}.asset";
AbilityData data = AssetDatabase.LoadAssetAtPath<AbilityData>(path);
if (data == null)
{
data = ScriptableObject.CreateInstance<AbilityData>();
AssetDatabase.CreateAsset(data, path);
}
data.abilityId = id;
data.abilityName = displayName;
data.description = description;
data.icon = icon;
data.manaCost = mana;
data.cooldown = cooldown;
data.damage = damage;
data.castRange = range;
data.effectPrefab = effectPrefab;
EditorUtility.SetDirty(data);
return data;
}
private static GameObject CreateFireballPrefab(Material fire, Material trail)
{
GameObject root = new GameObject("PF_FireballEffect");
var collider = root.AddComponent<SphereCollider>();
collider.radius = 0.35f;
collider.isTrigger = true;
root.AddComponent<FireballEffect>();
GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Sphere);
visual.name = "Fireball_Visual";
visual.transform.SetParent(root.transform, false);
visual.transform.localScale = Vector3.one * 0.55f;
Object.DestroyImmediate(visual.GetComponent<Collider>());
visual.GetComponent<Renderer>().sharedMaterial = fire;
ParticleSystem particles = root.AddComponent<ParticleSystem>();
ParticleSystem.MainModule main = particles.main;
main.startLifetime = 0.35f;
main.startSpeed = 0.2f;
main.startSize = 0.32f;
main.startColor = new Color(1f, 0.42f, 0.05f, 0.7f);
ParticleSystem.EmissionModule emission = particles.emission;
emission.rateOverTime = 34f;
ParticleSystemRenderer renderer = particles.GetComponent<ParticleSystemRenderer>();
renderer.sharedMaterial = trail;
return SavePrefab(root, "Assets/_Prefabs/Abilities/PF_FireballEffect.prefab");
}
private static GameObject CreateIceNovaPrefab(Material material)
{
GameObject root = new GameObject("PF_IceNovaEffect");
root.AddComponent<IceNovaEffect>();
GameObject ring = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
ring.name = "IceNova_Ring";
ring.transform.SetParent(root.transform, false);
ring.transform.localScale = new Vector3(5.5f, 0.035f, 5.5f);
Object.DestroyImmediate(ring.GetComponent<Collider>());
ring.GetComponent<Renderer>().sharedMaterial = material;
ParticleSystem particles = root.AddComponent<ParticleSystem>();
ParticleSystem.MainModule main = particles.main;
main.startLifetime = 0.7f;
main.startSpeed = 2.1f;
main.startSize = 0.18f;
main.startColor = new Color(0.55f, 0.9f, 1f, 0.75f);
ParticleSystem.ShapeModule shape = particles.shape;
shape.shapeType = ParticleSystemShapeType.Circle;
shape.radius = 5.4f;
ParticleSystem.EmissionModule emission = particles.emission;
emission.rateOverTime = 90f;
return SavePrefab(root, "Assets/_Prefabs/Abilities/PF_IceNovaEffect.prefab");
}
private static GameObject CreateBlinkPrefab(Material material)
{
GameObject root = new GameObject("PF_BlinkEffect");
root.AddComponent<BlinkEffect>();
GameObject burst = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
burst.name = "Blink_Burst";
burst.transform.SetParent(root.transform, false);
burst.transform.localScale = new Vector3(1.25f, 0.05f, 1.25f);
Object.DestroyImmediate(burst.GetComponent<Collider>());
burst.GetComponent<Renderer>().sharedMaterial = material;
ParticleSystem particles = root.AddComponent<ParticleSystem>();
ParticleSystem.MainModule main = particles.main;
main.startLifetime = 0.45f;
main.startSpeed = 1.4f;
main.startSize = 0.22f;
main.startColor = new Color(0.65f, 0.55f, 1f, 0.8f);
ParticleSystem.EmissionModule emission = particles.emission;
emission.rateOverTime = 70f;
return SavePrefab(root, "Assets/_Prefabs/Abilities/PF_BlinkEffect.prefab");
}
private static GameObject CreateDeathEffectPrefab(Material material)
{
GameObject root = new GameObject("PF_EnemyDeathEffect");
ParticleSystem particles = root.AddComponent<ParticleSystem>();
ParticleSystem.MainModule main = particles.main;
main.startLifetime = 0.65f;
main.startSpeed = 1.8f;
main.startSize = 0.22f;
main.duration = 0.5f;
main.loop = false;
main.startColor = new Color(0.65f, 0.2f, 1f, 0.85f);
ParticleSystem.EmissionModule emission = particles.emission;
emission.rateOverTime = 0f;
emission.SetBursts(new[] { new ParticleSystem.Burst(0f, 34) });
particles.GetComponent<ParticleSystemRenderer>().sharedMaterial = material;
var destroy = root.AddComponent<SelfDestruct>();
SetFloat(destroy, "lifetime", 1.2f);
return SavePrefab(root, "Assets/_Prefabs/Abilities/PF_EnemyDeathEffect.prefab");
}
private static FloatingDamage CreateFloatingDamagePrefab(Font font)
{
GameObject root = new GameObject("PF_FloatingDamage");
Canvas canvas = root.AddComponent<Canvas>();
canvas.renderMode = RenderMode.WorldSpace;
RectTransform canvasRect = root.GetComponent<RectTransform>();
canvasRect.sizeDelta = new Vector2(120f, 48f);
canvasRect.localScale = Vector3.one * 0.01f;
GameObject labelObject = new GameObject("DamageLabel", typeof(RectTransform));
labelObject.transform.SetParent(root.transform, false);
Text label = labelObject.AddComponent<Text>();
label.font = font;
label.fontSize = 38;
label.alignment = TextAnchor.MiddleCenter;
label.color = new Color(1f, 0.86f, 0.28f, 1f);
Stretch(label.rectTransform);
FloatingDamage damage = root.AddComponent<FloatingDamage>();
SetObject(damage, "label", label);
GameObject prefab = SavePrefab(root, "Assets/_Prefabs/UI/PF_FloatingDamage.prefab");
return prefab.GetComponent<FloatingDamage>();
}
private static GameObject CreateEnemyPrefab(Material enemyMaterial, GameObject deathEffect)
{
GameObject root = GameObject.CreatePrimitive(PrimitiveType.Capsule);
root.name = "PF_Enemy";
root.transform.localScale = new Vector3(1f, 1.35f, 1f);
root.GetComponent<Renderer>().sharedMaterial = enemyMaterial;
CapsuleCollider hitbox = root.GetComponent<CapsuleCollider>();
hitbox.radius = 0.85f;
hitbox.height = 2.4f;
Enemy enemy = root.AddComponent<Enemy>();
SetObject(enemy, "deathEffectPrefab", deathEffect);
return SavePrefab(root, "Assets/_Prefabs/Enemies/PF_Enemy.prefab");
}
private static GameObject CreateManaCrystalPrefab(Material material)
{
GameObject root = new GameObject("PF_ManaCrystal");
SphereCollider trigger = root.AddComponent<SphereCollider>();
trigger.isTrigger = true;
trigger.radius = 1.2f;
GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Sphere);
visual.name = "CrystalVisual";
visual.transform.SetParent(root.transform, false);
visual.transform.localPosition = Vector3.up * 0.8f;
visual.transform.localScale = new Vector3(0.65f, 1.25f, 0.65f);
Object.DestroyImmediate(visual.GetComponent<Collider>());
visual.GetComponent<Renderer>().sharedMaterial = material;
ManaCrystal crystal = root.AddComponent<ManaCrystal>();
SetObject(crystal, "visualRoot", visual);
return SavePrefab(root, "Assets/_Prefabs/Abilities/PF_ManaCrystal.prefab");
}
private static AbilitySlotUI CreateAbilitySlotPrefab(Font font, Dictionary<string, Material> materials)
{
GameObject root = CreateUIObject("PF_AbilitySlot", null, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(76f, 76f), Vector2.zero);
Image back = root.AddComponent<Image>();
back.color = new Color(0.045f, 0.05f, 0.055f, 0.9f);
GameObject iconObject = CreateUIObject("Icon", root.transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), new Vector2(-14f, -22f), new Vector2(0f, 8f));
Image icon = iconObject.AddComponent<Image>();
icon.preserveAspect = true;
icon.raycastTarget = false;
GameObject overlayObject = CreateUIObject("CooldownOverlay", root.transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), new Vector2(-14f, -22f), new Vector2(0f, 8f));
Image overlay = overlayObject.AddComponent<Image>();
overlay.color = new Color(0f, 0f, 0f, 0.68f);
overlay.type = Image.Type.Filled;
overlay.fillMethod = Image.FillMethod.Radial360;
overlay.fillOrigin = 2;
overlay.raycastTarget = false;
Text key = CreateText("Hotkey", root.transform, font, "1", 18, TextAnchor.MiddleCenter, new Color(0.95f, 0.95f, 0.95f, 1f));
Anchor(key.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(22f, 22f), new Vector2(9f, -8f));
Text name = CreateText("Name", root.transform, font, "Fireball", 11, TextAnchor.MiddleCenter, new Color(0.88f, 0.91f, 0.94f, 1f));
Anchor(name.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(-8f, 18f), new Vector2(0f, 10f));
Text mana = CreateText("Mana", root.transform, font, "15", 13, TextAnchor.MiddleRight, new Color(0.43f, 0.9f, 1f, 1f));
Anchor(mana.rectTransform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(34f, 20f), new Vector2(-7f, -8f));
Text cooldown = CreateText("Cooldown", root.transform, font, "", 20, TextAnchor.MiddleCenter, Color.white);
Stretch(cooldown.rectTransform);
AbilitySlotUI slot = root.AddComponent<AbilitySlotUI>();
SetObject(slot, "icon", icon);
SetObject(slot, "cooldownOverlay", overlay);
SetObject(slot, "hotkeyLabel", key);
SetObject(slot, "nameLabel", name);
SetObject(slot, "manaLabel", mana);
SetObject(slot, "cooldownLabel", cooldown);
GameObject prefab = SavePrefab(root, "Assets/_Prefabs/UI/PF_AbilitySlot.prefab");
return prefab.GetComponent<AbilitySlotUI>();
}
private static void BuildLighting()
{
RenderSettings.ambientLight = new Color(0.38f, 0.39f, 0.40f);
RenderSettings.fog = true;
RenderSettings.fogColor = new Color(0.58f, 0.62f, 0.65f);
RenderSettings.fogMode = FogMode.Linear;
RenderSettings.fogStartDistance = 35f;
RenderSettings.fogEndDistance = 80f;
GameObject lightObject = new GameObject("Directional Light");
Light light = lightObject.AddComponent<Light>();
light.type = LightType.Directional;
light.intensity = 1.05f;
lightObject.transform.rotation = Quaternion.Euler(55f, -38f, 0f);
GameObject rimObject = new GameObject("Arena Arcane Fill Light");
Light rim = rimObject.AddComponent<Light>();
rim.type = LightType.Point;
rim.intensity = 3.2f;
rim.range = 28f;
rim.color = new Color(0.36f, 0.7f, 1f);
rimObject.transform.position = new Vector3(0f, 8f, -8f);
}
private static void BuildArena(Dictionary<string, Material> materials)
{
const float arenaSize = 52f;
const float half = arenaSize * 0.5f;
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
floor.name = "Flat Stone Arena Floor";
floor.transform.position = new Vector3(0f, -0.08f, 0f);
floor.transform.localScale = new Vector3(arenaSize, 0.16f, arenaSize);
floor.GetComponent<Renderer>().sharedMaterial = materials["ArenaFloor"];
CreateWall("North Wall", new Vector3(0f, 1.15f, half), new Vector3(arenaSize + 1.2f, 2.3f, 0.8f), materials["ArenaWall"]);
CreateWall("South Wall", new Vector3(0f, 1.15f, -half), new Vector3(arenaSize + 1.2f, 2.3f, 0.8f), materials["ArenaWall"]);
CreateWall("East Wall", new Vector3(half, 1.15f, 0f), new Vector3(0.8f, 2.3f, arenaSize + 1.2f), materials["ArenaWall"]);
CreateWall("West Wall", new Vector3(-half, 1.15f, 0f), new Vector3(0.8f, 2.3f, arenaSize + 1.2f), materials["ArenaWall"]);
for (int i = -2; i <= 2; i++)
{
if (i == 0)
{
continue;
}
CreateFloorLine($"Floor Guide X {i}", new Vector3(i * 8f, 0.012f, 0f), new Vector3(0.05f, 0.02f, arenaSize - 5f), materials["ArenaLine"]);
CreateFloorLine($"Floor Guide Z {i}", new Vector3(0f, 0.014f, i * 8f), new Vector3(arenaSize - 5f, 0.02f, 0.05f), materials["ArenaLine"]);
}
Vector3[] blockPositions =
{
new Vector3(-16f, 0.55f, -13f), new Vector3(16f, 0.55f, -13f),
new Vector3(-16f, 0.55f, 13f), new Vector3(16f, 0.55f, 13f),
new Vector3(-7f, 0.45f, 18f), new Vector3(7f, 0.45f, 18f)
};
Vector3[] blockScales =
{
new Vector3(4.5f, 1.1f, 2f), new Vector3(4.5f, 1.1f, 2f),
new Vector3(2f, 1.1f, 4.5f), new Vector3(2f, 1.1f, 4.5f),
new Vector3(3.2f, 0.9f, 2f), new Vector3(3.2f, 0.9f, 2f)
};
for (int i = 0; i < blockPositions.Length; i++)
{
CreateWall($"Arena Cover {i + 1:00}", blockPositions[i], blockScales[i], materials["ArenaTrim"]);
}
Vector3[] columnPositions =
{
new Vector3(-22f, 1.7f, -22f), new Vector3(22f, 1.7f, -22f),
new Vector3(-22f, 1.7f, 22f), new Vector3(22f, 1.7f, 22f),
new Vector3(-22f, 1.7f, 0f), new Vector3(22f, 1.7f, 0f)
};
for (int i = 0; i < columnPositions.Length; i++)
{
GameObject pillar = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
pillar.name = $"Stone Column {i + 1:00}";
pillar.transform.position = columnPositions[i];
pillar.transform.localScale = new Vector3(0.75f, 1.7f, 0.75f);
pillar.GetComponent<Renderer>().sharedMaterial = materials["ArenaWall"];
}
}
private static void CreateWall(string name, Vector3 position, Vector3 scale, Material material)
{
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.name = name;
wall.transform.position = position;
wall.transform.localScale = scale;
wall.GetComponent<Renderer>().sharedMaterial = material;
}
private static void CreateFloorLine(string name, Vector3 position, Vector3 scale, Material material)
{
GameObject line = GameObject.CreatePrimitive(PrimitiveType.Cube);
line.name = name;
line.transform.position = position;
line.transform.localScale = scale;
line.GetComponent<Renderer>().sharedMaterial = material;
Object.DestroyImmediate(line.GetComponent<Collider>());
}
private static GameObject BuildPlayer(Dictionary<string, Material> materials, params AbilityData[] abilities)
{
GameObject player = new GameObject("Player_Mage");
player.name = "Player_Mage";
player.transform.position = new Vector3(0f, 0f, -10f);
GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Capsule);
visual.name = "Player_Visual";
visual.transform.SetParent(player.transform, false);
visual.transform.localPosition = Vector3.up;
visual.GetComponent<Renderer>().sharedMaterial = materials["Player"];
Object.DestroyImmediate(visual.GetComponent<CapsuleCollider>());
CharacterController controller = player.AddComponent<CharacterController>();
controller.height = 2f;
controller.radius = 0.42f;
controller.center = Vector3.up;
PlayerStats stats = player.AddComponent<PlayerStats>();
player.AddComponent<PlayerController>();
Transform castOrigin = new GameObject("CastOrigin").transform;
castOrigin.SetParent(player.transform, false);
castOrigin.localPosition = new Vector3(0f, 1.25f, 0.55f);
AbilityManager manager = player.AddComponent<AbilityManager>();
SetObject(manager, "playerStats", stats);
SetObject(manager, "castOrigin", castOrigin);
SetObjectArray(manager, "learnedAbilities", abilities);
return player;
}
private static void BuildCamera(Transform player)
{
GameObject cameraObject = new GameObject("Main Camera");
Camera camera = cameraObject.AddComponent<Camera>();
cameraObject.tag = "MainCamera";
camera.nearClipPlane = 0.05f;
camera.fieldOfView = 48f;
cameraObject.transform.position = player.position + new Vector3(0f, 11f, -9f);
cameraObject.transform.LookAt(player.position + Vector3.up * 1.3f);
ThirdPersonCamera follow = cameraObject.AddComponent<ThirdPersonCamera>();
follow.SetTarget(player);
SetFloat(follow, "mouseSensitivity", 1.8f);
SetFloat(follow, "smoothTime", 0.12f);
SetFloat(follow, "minPitch", 45f);
SetFloat(follow, "maxPitch", 60f);
SetFloat(follow, "initialPitch", 52f);
SetFloat(follow, "distance", 14f);
SetFloat(follow, "lookAhead", 3.5f);
SetFloat(follow, "targetHeight", 1.25f);
PlayerController controller = player.GetComponent<PlayerController>();
SetObject(controller, "cameraTransform", cameraObject.transform);
}
private static void BuildEnemies(GameObject enemyPrefab)
{
Vector3[] positions =
{
new Vector3(-15f, 1.35f, 8f), new Vector3(-7f, 1.35f, 14f), new Vector3(7f, 1.35f, 14f), new Vector3(15f, 1.35f, 8f),
new Vector3(-18f, 1.35f, -3f), new Vector3(18f, 1.35f, -3f), new Vector3(-9f, 1.35f, -18f), new Vector3(9f, 1.35f, -18f)
};
for (int i = 0; i < positions.Length; i++)
{
GameObject enemy = (GameObject)PrefabUtility.InstantiatePrefab(enemyPrefab);
enemy.name = $"Enemy_{i + 1:00}";
enemy.transform.position = positions[i];
enemy.transform.LookAt(new Vector3(0f, enemy.transform.position.y, -10f));
}
}
private static void BuildTrainingMannequins(Dictionary<string, Material> materials)
{
for (int i = 0; i < 4; i++)
{
float x = -9f + i * 6f;
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
body.name = $"Training Mannequin_{i + 1:00}";
body.transform.position = new Vector3(x, 1.1f, 21f);
body.transform.localScale = new Vector3(0.75f, 1.1f, 0.75f);
body.GetComponent<Renderer>().sharedMaterial = materials["Mannequin"];
GameObject cross = GameObject.CreatePrimitive(PrimitiveType.Cube);
cross.name = "Practice Armature";
cross.transform.SetParent(body.transform, false);
cross.transform.localPosition = new Vector3(0f, 0.35f, 0f);
cross.transform.localScale = new Vector3(1.8f, 0.12f, 0.12f);
cross.GetComponent<Renderer>().sharedMaterial = materials["ArenaTrim"];
}
}
private static void BuildManaCrystals(GameObject crystalPrefab)
{
Vector3[] positions =
{
new Vector3(-21f, 0.1f, -12f),
new Vector3(21f, 0.1f, -12f),
new Vector3(-21f, 0.1f, 12f),
new Vector3(21f, 0.1f, 12f)
};
for (int i = 0; i < positions.Length; i++)
{
GameObject crystal = (GameObject)PrefabUtility.InstantiatePrefab(crystalPrefab);
crystal.name = $"Mana Crystal_{i + 1:00}";
crystal.transform.position = positions[i];
}
}
private static void BuildFloatingDamageManager(FloatingDamage prefab)
{
GameObject manager = new GameObject("FloatingDamageManager");
FloatingDamageManager damageManager = manager.AddComponent<FloatingDamageManager>();
SetObject(damageManager, "floatingDamagePrefab", prefab);
}
private static void BuildUI(GameObject player, AbilitySlotUI slotPrefab, Font font, Dictionary<string, Material> materials)
{
GameObject canvasObject = new GameObject("Gameplay UI", typeof(RectTransform));
Canvas canvas = canvasObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1280f, 720f);
scaler.matchWidthOrHeight = 0.5f;
canvasObject.AddComponent<GraphicRaycaster>();
GameObject eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
TooltipUI tooltip = BuildTooltip(canvasObject.transform, font);
BuildResourcePanel(canvasObject.transform, player.GetComponent<PlayerStats>(), font, false, new Vector2(24f, 72f), new Color(0.78f, 0.16f, 0.17f, 1f));
BuildResourcePanel(canvasObject.transform, player.GetComponent<PlayerStats>(), font, true, new Vector2(24f, 34f), new Color(0.12f, 0.55f, 0.95f, 1f));
BuildAbilityBar(canvasObject.transform, player.GetComponent<AbilityManager>(), slotPrefab, tooltip, font);
}
private static void BuildAbilityBar(Transform canvas, AbilityManager manager, AbilitySlotUI slotPrefab, TooltipUI tooltip, Font font)
{
GameObject panel = CreateUIObject("Ability Bar", canvas, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(318f, 92f), new Vector2(0f, 18f));
Image back = panel.AddComponent<Image>();
back.color = new Color(0.025f, 0.03f, 0.035f, 0.72f);
GameObject root = CreateUIObject("Slots", panel.transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), new Vector2(-20f, -16f), Vector2.zero);
HorizontalLayoutGroup layout = root.AddComponent<HorizontalLayoutGroup>();
layout.spacing = 10f;
layout.childAlignment = TextAnchor.MiddleCenter;
layout.childControlWidth = false;
layout.childControlHeight = false;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
AbilityBarUI bar = panel.AddComponent<AbilityBarUI>();
SetObject(bar, "abilityManager", manager);
SetObject(bar, "slotPrefab", slotPrefab);
SetObject(bar, "slotRoot", root.transform);
SetObject(bar, "tooltip", tooltip);
}
private static void BuildResourcePanel(Transform canvas, PlayerStats stats, Font font, bool mana, Vector2 anchoredPosition, Color fillColor)
{
GameObject panel = CreateUIObject(mana ? "Mana Bar" : "HP Bar", canvas, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(226f, 28f), anchoredPosition);
Image back = panel.AddComponent<Image>();
back.color = new Color(0.025f, 0.03f, 0.035f, 0.74f);
Text name = CreateText(mana ? "Mana Label" : "HP Label", panel.transform, font, mana ? "Mana" : "HP", 13, TextAnchor.MiddleLeft, new Color(0.9f, 0.92f, 0.94f, 1f));
Anchor(name.rectTransform, new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(50f, 0f), new Vector2(10f, 0f));
GameObject fillBackObject = CreateUIObject("Track", panel.transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), new Vector2(-62f, -10f), new Vector2(26f, 0f));
Image fillBack = fillBackObject.AddComponent<Image>();
fillBack.color = new Color(0f, 0f, 0f, 0.28f);
GameObject fillObject = CreateUIObject("Fill", panel.transform, Vector2.zero, Vector2.one, new Vector2(0f, 0.5f), new Vector2(-62f, -10f), new Vector2(26f, 0f));
Image fill = fillObject.AddComponent<Image>();
fill.color = fillColor;
fill.type = Image.Type.Filled;
fill.fillMethod = Image.FillMethod.Horizontal;
Text label = CreateText("Value", panel.transform, font, "100 / 100", 13, TextAnchor.MiddleRight, Color.white);
Anchor(label.rectTransform, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(78f, 0f), new Vector2(-9f, 0f));
ResourceBarUI resource = panel.AddComponent<ResourceBarUI>();
SetObject(resource, "playerStats", stats);
SetBool(resource, "useMana", mana);
SetObject(resource, "fill", fill);
SetObject(resource, "valueLabel", label);
}
private static TooltipUI BuildTooltip(Transform canvas, Font font)
{
GameObject panel = CreateUIObject("Ability Tooltip", canvas, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(280f, 172f), new Vector2(0f, 164f));
Image back = panel.AddComponent<Image>();
back.color = new Color(0.025f, 0.03f, 0.035f, 0.96f);
Text title = CreateText("Title", panel.transform, font, "Ability", 19, TextAnchor.MiddleLeft, Color.white);
title.horizontalOverflow = HorizontalWrapMode.Overflow;
title.verticalOverflow = VerticalWrapMode.Truncate;
Anchor(title.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(-28f, 30f), new Vector2(0f, -20f));
Text description = CreateText("Description", panel.transform, font, "", 13, TextAnchor.UpperLeft, new Color(0.78f, 0.82f, 0.86f, 1f));
description.horizontalOverflow = HorizontalWrapMode.Wrap;
description.verticalOverflow = VerticalWrapMode.Truncate;
Anchor(description.rectTransform, new Vector2(0f, 0.42f), new Vector2(1f, 0.79f), new Vector2(0.5f, 0.5f), new Vector2(-28f, 0f), new Vector2(0f, -4f));
Text stats = CreateText("Stats", panel.transform, font, "", 14, TextAnchor.UpperLeft, new Color(0.48f, 0.88f, 1f, 1f));
stats.horizontalOverflow = HorizontalWrapMode.Wrap;
stats.verticalOverflow = VerticalWrapMode.Truncate;
Anchor(stats.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0.34f), new Vector2(0.5f, 0f), new Vector2(-28f, -12f), new Vector2(0f, 10f));
TooltipUI tooltip = panel.AddComponent<TooltipUI>();
SetObject(tooltip, "titleLabel", title);
SetObject(tooltip, "descriptionLabel", description);
SetObject(tooltip, "statsLabel", stats);
panel.SetActive(false);
return tooltip;
}
private static GameObject CreateUIObject(string name, Transform parent, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 size, Vector2 anchoredPosition)
{
GameObject obj = new GameObject(name, typeof(RectTransform));
RectTransform rect = obj.GetComponent<RectTransform>();
if (parent != null)
{
rect.SetParent(parent, false);
}
Anchor(rect, anchorMin, anchorMax, pivot, size, anchoredPosition);
return obj;
}
private static Text CreateText(string name, Transform parent, Font font, string text, int size, TextAnchor alignment, Color color)
{
GameObject obj = CreateUIObject(name, parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(120f, 30f), Vector2.zero);
Text label = obj.AddComponent<Text>();
label.font = font;
label.text = text;
label.fontSize = size;
label.alignment = alignment;
label.color = color;
label.horizontalOverflow = HorizontalWrapMode.Wrap;
label.verticalOverflow = VerticalWrapMode.Truncate;
label.raycastTarget = false;
Shadow shadow = obj.AddComponent<Shadow>();
shadow.effectColor = new Color(0f, 0f, 0f, 0.65f);
shadow.effectDistance = new Vector2(1f, -1f);
return label;
}
private static void Anchor(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 size, Vector2 anchoredPosition)
{
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.pivot = pivot;
rect.sizeDelta = size;
rect.anchoredPosition = anchoredPosition;
}
private static void Stretch(RectTransform rect)
{
Anchor(rect, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
}
private static GameObject SavePrefab(GameObject root, string path)
{
if (AssetDatabase.LoadAssetAtPath<GameObject>(path) != null)
{
AssetDatabase.DeleteAsset(path);
}
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, path);
Object.DestroyImmediate(root);
return prefab;
}
private static void SetObject(Object target, string propertyName, Object value)
{
SerializedObject serializedObject = new SerializedObject(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.objectReferenceValue = value;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
private static void SetObjectArray(Object target, string propertyName, Object[] values)
{
SerializedObject serializedObject = new SerializedObject(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.arraySize = values.Length;
for (int i = 0; i < values.Length; i++)
{
property.GetArrayElementAtIndex(i).objectReferenceValue = values[i];
}
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
private static void SetFloat(Object target, string propertyName, float value)
{
SerializedObject serializedObject = new SerializedObject(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.floatValue = value;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
private static void SetVector3(Object target, string propertyName, Vector3 value)
{
SerializedObject serializedObject = new SerializedObject(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.vector3Value = value;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
private static void SetBool(Object target, string propertyName, bool value)
{
SerializedObject serializedObject = new SerializedObject(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.boolValue = value;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
}