using System.Collections.Generic; using UnityEngine; namespace Code.Track { public class TrackBuilder : MonoBehaviour { [SerializeField] private GameObject roadTilePrefab; [SerializeField] private float roadWidth = 8f; [SerializeField] private GameObject leftBarrierPrefab; [SerializeField] private GameObject rightBarrierPrefab; [SerializeField] private float barrierSpacing = 2f; [SerializeField] private GameObject mountainPrefab; [SerializeField] private float mountainSpacing = 10f; [SerializeField] private float mountainOffsetFromRoad = 10f; [SerializeField] [Range(1f, 100f)] private float mountainMinDistance = 30f; [SerializeField] [Range(1f, 3f)] private float mountainScaleMin = 1f; [SerializeField] [Range(1f, 3f)] private float mountainScaleMax = 2f; private readonly List _spawned = new(); public void Build(TrackData data) { Clear(); if (data?.Waypoints == null || data.Waypoints.Count < 2) return; BuildRoadAndBarriers(data); BuildMountains(data); } private void BuildRoadAndBarriers(TrackData data) { var points = data.Waypoints; for (var i = 0; i < points.Count - 1; i++) { var a = points[i]; var b = points[i + 1]; var delta = b - a; if (delta.sqrMagnitude < 0.01f) continue; var forward = delta.normalized; var right = Vector3.Cross(Vector3.up, forward); var length = delta.magnitude; var roadPos = (a + b) / 2f; var roadRot = Quaternion.LookRotation(forward, Vector3.up); var road = Instantiate(roadTilePrefab, roadPos, roadRot, transform); road.transform.localScale = new Vector3(roadWidth, 1f, length); _spawned.Add(road); var fenceSegments = Mathf.Max(1, Mathf.CeilToInt(length / barrierSpacing)); for (var j = 0; j <= fenceSegments; j++) { var t = j / (float)fenceSegments; var pos = Vector3.Lerp(a, b, t); var leftPos = pos - right * (roadWidth * 0.5f + 0.5f); var rightPos = pos + right * (roadWidth * 0.5f + 0.5f); Instantiate(leftBarrierPrefab, leftPos, roadRot, transform); Instantiate(rightBarrierPrefab, rightPos, roadRot, transform); } } } private void BuildMountains(TrackData data) { if (mountainPrefab == null || data.Waypoints.Count < 2) return; var spacing = Mathf.Max(1f, mountainMinDistance); for (var i = 0; i < data.Waypoints.Count - 1; i++) { var a = data.Waypoints[i]; var b = data.Waypoints[i + 1]; var delta = b - a; if (delta.sqrMagnitude < 0.01f) continue; var forward = delta.normalized; var right = Vector3.Cross(Vector3.up, forward); var segmentLength = delta.magnitude; var mountainCountOnSegment = Mathf.Max(1, Mathf.RoundToInt(segmentLength / spacing)); for (var j = 0; j < mountainCountOnSegment; j++) { var t = (j + 0.5f) / mountainCountOnSegment; if (t > 1f) t = 1f; var posOnSegment = Vector3.Lerp(a, b, t); var useLeft = j % 2 == 0; var outwardDir = useLeft ? -right : right; var mountainPos = posOnSegment + outwardDir * (roadWidth * 0.5f + mountainOffsetFromRoad); mountainPos.y = 0f; var mountainRot = Quaternion.LookRotation(forward, Vector3.up); var mountain = Instantiate(mountainPrefab, mountainPos, mountainRot, transform); var scale = Random.Range(mountainScaleMin, mountainScaleMax); mountain.transform.localScale = Vector3.one * scale; _spawned.Add(mountain); } } } private void Clear() { foreach (var obj in _spawned) Destroy(obj); _spawned.Clear(); } } }