using UnityEngine;
using Vanguard.Battle;
namespace Vanguard.Board3D
{
///
/// Стрелка атаки на столе: линия от атакующего к цели и наконечник.
/// Точки обновляются каждый кадр, поэтому стрелка едет за картами.
///
public sealed class AttackArrowView
{
private const float ShaftWidth = 0.075f;
private const float HeadWidth = 0.17f;
private const float HeadLength = 0.38f;
private GameObject _root;
private LineRenderer _shaft;
private LineRenderer _head;
private Material _material;
private AnimationConfig _config;
private Vector3 _from;
private Vector3 _to;
private bool _visible;
private float _alpha;
private float _widthScale = 1f;
public bool IsVisible => _visible || _alpha > 0.001f;
public void Build(Transform parent, AnimationConfig config)
{
_config = config;
_root = new GameObject("Attack Arrow");
_root.transform.SetParent(parent, false);
var shader = Shader.Find("Sprites/Default");
if (shader == null)
shader = Shader.Find("Unlit/Color");
_material = shader != null ? new Material(shader) : null;
_shaft = CreateLine("Shaft", ShaftWidth);
_head = CreateLine("Head", ShaftWidth * 0.7f);
_head.positionCount = 3;
_root.SetActive(false);
}
public void SetConfig(AnimationConfig config)
{
_config = config;
}
public void Show(Vector3 from, Vector3 to)
{
SetPoints(from, to);
_visible = true;
if (_root != null)
_root.SetActive(true);
}
public void Hide()
{
_visible = false;
}
public void HideImmediate()
{
_visible = false;
_alpha = 0f;
if (_root != null)
_root.SetActive(false);
}
public void SetPoints(Vector3 from, Vector3 to)
{
_from = from;
_to = to;
UpdateGeometry();
}
public void Tick(float deltaTime)
{
if (_root == null)
return;
var target = _visible ? 1f : 0f;
var duration = _visible
? (_config != null ? _config.arrowShowDuration : 0.25f)
: (_config != null ? _config.arrowHideDuration : 0.2f);
_alpha = Mathf.MoveTowards(
_alpha,
target,
deltaTime / Mathf.Max(0.01f, duration));
if (!_visible && _alpha <= 0.001f)
{
_root.SetActive(false);
return;
}
_root.SetActive(true);
_widthScale = 0.55f + (_alpha * 0.45f);
UpdateGeometry();
ApplyColor();
}
public void Dispose()
{
if (_root != null)
{
if (Application.isPlaying)
Object.Destroy(_root);
else
Object.DestroyImmediate(_root);
_root = null;
}
if (_material != null)
{
if (Application.isPlaying)
Object.Destroy(_material);
else
Object.DestroyImmediate(_material);
_material = null;
}
}
private LineRenderer CreateLine(string name, float width)
{
var go = new GameObject(name);
go.transform.SetParent(_root.transform, false);
var line = go.AddComponent();
line.useWorldSpace = false;
line.positionCount = 2;
line.widthMultiplier = width;
line.numCapVertices = 4;
line.numCornerVertices = 4;
line.alignment = LineAlignment.View;
line.textureMode = LineTextureMode.Stretch;
line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
line.receiveShadows = false;
if (_material != null)
line.material = _material;
return line;
}
private void UpdateGeometry()
{
if (_shaft == null || _head == null)
return;
var direction = _to - _from;
if (direction.sqrMagnitude < 0.0001f)
direction = Vector3.forward;
direction.Normalize();
_shaft.SetPosition(0, _from);
_shaft.SetPosition(1, _to);
_shaft.widthMultiplier = ShaftWidth * _widthScale;
var tip = _to;
var back = _to - (direction * HeadLength);
var side = Vector3.Cross(Vector3.up, direction).normalized;
if (side.sqrMagnitude < 0.0001f)
side = Vector3.right;
var half = side * (HeadWidth * _widthScale);
_head.SetPosition(0, back + half);
_head.SetPosition(1, tip);
_head.SetPosition(2, back - half);
_head.widthMultiplier = ShaftWidth * 0.7f * _widthScale;
}
private void ApplyColor()
{
var color = new Color(1f, 0.72f, 0.26f, _alpha * 0.92f);
foreach (var line in new[] { _shaft, _head })
{
if (line == null)
continue;
line.startColor = color;
line.endColor = color;
}
}
}
}