using Unity.Mathematics; using UnityEngine; using UnityEngine.Pool; using Object = UnityEngine.Object; namespace DanieleMarotta.RiversongCodeShowcase { public class BuildToolPreview { private readonly GameConfig.UIConfig.BuildToolConfig _config; private readonly ITileSpace _tileSpace; private BuildingDefinition _building; private Vector3 _tilt; private Vector3 _tiltVelocity; private float _yaw; private float _yawVelocity; public BuildToolPreview(GameConfig.UIConfig.BuildToolConfig config, ITileSpace tileSpace) { _config = config; _tileSpace = tileSpace; } public GameObject PreviewObject { get; private set; } public bool IsVisible => PreviewObject && PreviewObject.activeSelf; public float3 Position => PreviewObject ? PreviewObject.transform.position : Vector3.zero; public void PrepareForBuilding(BuildingDefinition building) { _building = building; } public void Release() { ClearPreviewObject(); } public void Update(bool isValid, Vector3 pointer, Directions orientation) { if (!PreviewObject) { if (_building.Visualization.IsDone) CreatePreviewObject(); else return; } var snap = !PreviewObject.activeSelf && isValid; if (snap) { _tilt = Vector3.zero; _tiltVelocity = Vector3.zero; } PreviewObject.SetActive(isValid); if (!isValid) return; var rect = TileMath.GetBuildingRect(_tileSpace.WorldToTile(pointer), orientation, _building.Width, _building.Height); var position = _tileSpace.GetRectWorldCenter(rect); position.y += _config.Height; var dt = Time.unscaledDeltaTime; position = snap ? position : Vector3.Lerp(PreviewObject.transform.position, position, _config.LerpFactor * dt); var yaw = orientation.ToQuaternion().eulerAngles.y; _yaw = snap ? yaw : Mathf.SmoothDampAngle(_yaw, yaw, ref _yawVelocity, 0.1f, Mathf.Infinity, dt); var rotation = Quaternion.Euler(0, _yaw, 0); if (!snap) rotation = SimulateSpring(position, dt) * rotation; PreviewObject.transform.SetPositionAndRotation(position, rotation); } private Quaternion SimulateSpring(Vector3 targetPosition, float dt) { var positionDelta = targetPosition - PreviewObject.transform.position; _tilt -= positionDelta * _config.ImpulseScale; _tilt = _tilt.normalized * Mathf.Min(_tilt.magnitude, _config.MaxTilt); _tiltVelocity -= _tilt * (_config.Elasticity * dt); _tiltVelocity *= Mathf.Pow(1 - _config.Damping, dt); _tilt += _tiltVelocity * dt; if (_tilt.sqrMagnitude > 0.001f) { var axis = Vector3.Cross(Vector3.down, _tilt.normalized); var angle = _tilt.magnitude / _config.MaxTilt * _config.MaxTiltAngle; return Quaternion.AngleAxis(angle, axis); } return Quaternion.identity; } private void CreatePreviewObject() { PreviewObject = Object.Instantiate((GameObject)_building.Visualization.Asset); PreviewObject.SetLayerRecursively(GameObjectLayers.IgnoreAoE); using var _ = ListPool.Get(out var hide); PreviewObject.GetComponentsInChildren(hide); foreach (var obj in hide) obj.gameObject.SetActive(false); // Start the preview in the disabled state, causes the position to be snapped when enabled PreviewObject.SetActive(false); } public void ClearPreviewObject() { if (PreviewObject) { Object.Destroy(PreviewObject); PreviewObject = null; } } } }