Add real-time lighting and shadows, training courses and targets, and animated salvage crates. Include bilingual assets and curated verification results.
148 lines
7.1 KiB
C#
148 lines
7.1 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace Ironspore
|
|
{
|
|
public static class WorldFactory
|
|
{
|
|
static readonly Dictionary<string, Material> materials = new Dictionary<string, Material>();
|
|
public enum Surface { Painted, Concrete, BareMetal }
|
|
public static Material Material(Color color, bool glow = false, Surface surface = Surface.Painted)
|
|
{
|
|
string key = ColorUtility.ToHtmlStringRGB(color) + (glow ? "g" : "") + (int)surface;
|
|
if (materials.TryGetValue(key, out var cached) && cached)
|
|
return cached;
|
|
var mat = new Material(Shader.Find("Standard"));
|
|
mat.color = color;
|
|
mat.SetFloat("_Metallic", surface == Surface.BareMetal ? .78f : surface == Surface.Concrete ? 0 : .04f);
|
|
mat.SetFloat("_Glossiness", surface == Surface.Concrete ? .15f : surface == Surface.BareMetal ? .48f : .32f);
|
|
mat.enableInstancing = true;
|
|
if (glow)
|
|
{
|
|
mat.EnableKeyword("_EMISSION");
|
|
mat.SetColor("_EmissionColor", color * 1.25f);
|
|
}
|
|
materials[key] = mat;
|
|
return mat;
|
|
}
|
|
public static GameObject Box(string name, Vector3 position, Vector3 scale, Color color, Transform parent, bool collision = true, bool glow = false)
|
|
{
|
|
return Primitive(PrimitiveType.Cube, name, position, scale, color, parent, collision, glow);
|
|
}
|
|
public static GameObject Cylinder(string name, Vector3 position, Vector3 scale, Color color, Transform parent, bool collision = true)
|
|
{
|
|
return Primitive(PrimitiveType.Cylinder, name, position, scale, color, parent, collision, false);
|
|
}
|
|
public static GameObject Primitive(PrimitiveType type, string name, Vector3 position, Vector3 scale, Color color, Transform parent, bool collision, bool glow)
|
|
{
|
|
var obj = GameObject.CreatePrimitive(type);
|
|
obj.name = name;
|
|
obj.transform.SetParent(parent);
|
|
obj.transform.position = position;
|
|
obj.transform.localScale = scale;
|
|
var renderer = obj.GetComponent<Renderer>();
|
|
var surface = name == "Ground" || name == "AccessRamp" ? Surface.Concrete : name == "ContainerRib" || name == "CanopyRail" ? Surface.BareMetal : Surface.Painted;
|
|
renderer.sharedMaterial = Material(color, glow, surface);
|
|
bool detail = name == "FloorSeam" || name == "AmberStripe" || name == "CrateLatch" || name == "RelayScreen" || name == "LandingLight" || name == "CanopyIndicator" || name.EndsWith("WorkFixture");
|
|
renderer.shadowCastingMode = detail || name == "EnemyBolt" ? ShadowCastingMode.Off : ShadowCastingMode.On;
|
|
renderer.receiveShadows = !glow;
|
|
if (name == "EnemyBolt") obj.layer = 10;
|
|
if (!collision)
|
|
Object.Destroy(obj.GetComponent<Collider>());
|
|
return obj;
|
|
}
|
|
public static GameObject Cockpit(Transform camera)
|
|
{
|
|
var root = new GameObject("CockpitFrame");
|
|
root.transform.SetParent(camera, false);
|
|
foreach (int side in new[] { -1, 1 })
|
|
{
|
|
var frame = Box("CanopyRail", Vector3.zero, new Vector3(.045f, .8f, .06f), new Color(.08f, .16f, .19f), root.transform, false);
|
|
frame.transform.localPosition = new Vector3(side * .48f, -.05f, .7f);
|
|
frame.transform.localRotation = Quaternion.Euler(0, 0, side * -12);
|
|
var indicator = Box("CanopyIndicator", Vector3.zero, new Vector3(.018f, .27f, .065f), new Color(.05f, .9f, .75f), root.transform, false, true);
|
|
indicator.transform.localPosition = new Vector3(side * .48f, -.15f, .66f);
|
|
}
|
|
var dashboard = Box("Dashboard", Vector3.zero, new Vector3(1.3f, .14f, .2f), new Color(.07f, .13f, .16f), root.transform, false);
|
|
dashboard.transform.localPosition = new Vector3(0, -.42f, .75f);
|
|
foreach (var child in root.GetComponentsInChildren<Transform>())
|
|
child.gameObject.layer = 8;
|
|
foreach (var renderer in root.GetComponentsInChildren<Renderer>())
|
|
{
|
|
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
renderer.lightProbeUsage = LightProbeUsage.Off;
|
|
renderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
|
|
}
|
|
root.SetActive(false);
|
|
return root;
|
|
}
|
|
public static void Label(string key, Vector3 position, Color color, Transform parent)
|
|
{
|
|
var obj = new GameObject("Sign");
|
|
obj.transform.SetParent(parent);
|
|
obj.transform.position = position;
|
|
obj.layer = 11;
|
|
var label = obj.AddComponent<TextMesh>();
|
|
label.fontSize = 40;
|
|
label.characterSize = .09f;
|
|
label.anchor = TextAnchor.MiddleCenter;
|
|
label.color = color;
|
|
var renderer = obj.GetComponent<Renderer>();
|
|
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
renderer.receiveShadows = false;
|
|
renderer.lightProbeUsage = LightProbeUsage.Off;
|
|
renderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
|
|
obj.AddComponent<LocalizedWorldLabel>().Initialize(key);
|
|
obj.AddComponent<FaceCamera>();
|
|
}
|
|
public static void Tracer(Vector3 from, Vector3 to, Color color)
|
|
{
|
|
var obj = new GameObject("ShotTrace");
|
|
var line = obj.AddComponent<LineRenderer>();
|
|
obj.layer = 11;
|
|
line.shadowCastingMode = ShadowCastingMode.Off;
|
|
line.receiveShadows = false;
|
|
line.lightProbeUsage = LightProbeUsage.Off;
|
|
line.reflectionProbeUsage = ReflectionProbeUsage.Off;
|
|
line.positionCount = 2;
|
|
line.SetPosition(0, from);
|
|
line.SetPosition(1, to);
|
|
line.startWidth = .045f;
|
|
line.endWidth = .015f;
|
|
line.sharedMaterial = Material(color, true);
|
|
Object.Destroy(obj, .07f);
|
|
}
|
|
}
|
|
public sealed class FaceCamera : MonoBehaviour
|
|
{
|
|
void LateUpdate()
|
|
{
|
|
var c = Camera.main;
|
|
if (!c)
|
|
c = Object.FindAnyObjectByType<Camera>();
|
|
if (c)
|
|
transform.rotation = c.transform.rotation;
|
|
}
|
|
}
|
|
public static class GameAudio
|
|
{
|
|
public static void Cue(float frequency, float length, float volume = .12f)
|
|
{
|
|
int count = (int)(22050 * length);
|
|
var data = new float[count];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
float t = i / 22050f;
|
|
data[i] = Mathf.Sin(t * frequency * Mathf.PI * 2) * Mathf.Exp(-t / length * 6);
|
|
}
|
|
var clip = AudioClip.Create("SynthCue", count, 1, 22050, false);
|
|
clip.SetData(data, 0);
|
|
var camera = Object.FindAnyObjectByType<AudioListener>();
|
|
if (camera)
|
|
AudioSource.PlayClipAtPoint(clip, camera.transform.position, volume);
|
|
Object.Destroy(clip, length + 1);
|
|
}
|
|
}
|
|
}
|