using UnityEngine;
namespace Vanguard.Board3D
{
///
/// Настройка 3D-поля: геометрия кругов, высоты, скорости анимаций,
/// камера и палитра. Один общий ассет на Classic; раскладка не зависит
/// от того, за какое место играет локальный игрок.
///
[CreateAssetMenu(
menuName = "Vanguard/Playmat Layout",
fileName = "PlaymatLayoutConfig")]
public sealed class PlaymatLayoutConfig : ScriptableObject
{
[Header("Карта")]
public float cardWidth = 0.63f;
public float cardHeight = 0.88f;
public float cardThickness = 0.006f;
[Tooltip("Общий масштаб всех карт на столе и в руке.")]
public float cardScale = 1.25f;
[Header("Поле")]
[Tooltip("Расстояние между центрами соседних кругов по X.")]
public float circleSpacingX = 1.42f;
[Tooltip("Z переднего ряда в локальных координатах игрока.")]
public float frontRowZ = 1.55f;
[Tooltip("Z заднего ряда в локальных координатах игрока.")]
public float backRowZ = 3.0f;
public float boardHalfWidth = 4.6f;
public float boardHalfLength = 4.0f;
[Header("Высоты")]
public float cardLift = 0.02f;
public float guardianLift = 0.008f;
public float damageLift = 0.012f;
public float pileLift = 0.018f;
public float dragLift = 0.14f;
[Header("Взаимодействие")]
public float snapRadius = 0.66f;
public float dragThresholdPixels = 7f;
public float smoothMoveSpeed = 16f;
public float smoothRotateSpeed = 14f;
[Tooltip("Подъём карты при наведении, доля её высоты.")]
[Range(0f, 0.5f)] public float handHoverLift = 0.1f;
[Header("Рука на столе (fallback без камеры)")]
public float handCenterZ = -5.3f;
public float handHeight = 1.12f;
[Range(0f, 85f)] public float handTilt = 58f;
public float handSpacing = 0.28f;
public float handMaxWidth = 7.4f;
public float handArc = 0.035f;
[Header("Рука в поле зрения")]
[Tooltip("Доля ширины экрана для центра карт: 0 — левый край, 0.5 — середина.")]
[Range(0.05f, 0.95f)] public float handViewportX = 0.5f;
[Tooltip("Доля высоты экрана для центра карт: 0 — низ, 0.5 — середина.")]
[Range(0.05f, 0.45f)] public float handViewportY = 0.10f;
[Tooltip("Расстояние от камеры до веера руки, метры.")]
public float handViewDistance = 4.0f;
public float handViewMaxWidth = 2.52f;
public float handViewScale = 0.5f;
public float handViewRoll = 3.2f;
public float handViewArc = 0.02f;
[Header("Рука противника")]
public bool opponentHandEnabled = true;
public float opponentHandZ = 4.3f;
public float opponentHandHeight = 0.75f;
public float opponentHandSpacing = 0.28f;
public float opponentHandMaxWidth = 3.78f;
public float opponentHandScale = 0.9f;
public float opponentHandRoll = 5f;
public float opponentHandArc = 0.08f;
[Header("Рука противника в поле зрения")]
[Range(0.5f, 0.95f)] public float opponentHandViewportY = 0.76f;
public float opponentHandViewDistance = 6f;
public float opponentHandViewScale = 0.5f;
public float opponentHandViewMaxWidth = 2.4f;
public float opponentHandViewRoll = 3.2f;
public float opponentHandViewArc = 0.08f;
[Header("Круг защиты")]
public float guardianRowGap = 0.55f;
public float guardianSpacing = 0.62f;
public float guardianMaxWidth = 3.6f;
[Header("Стопки")]
[Tooltip("Расстояние от центра стола до зоны урона.")]
public float damageX = 3.0f;
[Tooltip("Сдвиг следующей карты урона в долях ширины карты. 0.3 = новая карта перекрывает 30% предыдущей.")]
[Range(0.1f, 0.9f)] public float damageStackOffset = 0.3f;
[Tooltip("Расстояние от центра стола до колоды и дропа.")]
public float pileX = 2.95f;
[Tooltip("Показывать Drop рубашкой вверх. По Classic — false.")]
public bool dropZoneFaceDown = false;
[Header("Отладка")]
[Tooltip("Показывать подписи зон на 3D-столе.")]
public bool showZoneLabels = false;
[Header("Камера")]
[Tooltip("Фиксированная камера над полем. Не двигается игроком.")]
public Vector3 cameraPosition = new Vector3(0f, 4.2f, -8f);
public Vector3 cameraTarget = new Vector3(0f, 0f, 0f);
public float cameraFov = 46f;
[Header("Стартовая камера")]
[Tooltip("Начальная позиция: над стороной игрока, лицом к столу.")]
public Vector3 introCameraPosition = new Vector3(0f, 7.5f, -5.5f);
public Vector3 introCameraTarget = new Vector3(0f, 0.4f, 2f);
public float introCameraFov = 55f;
[Header("Палитра")]
public Color tableColor = new Color(0.045f, 0.04f, 0.055f);
public Color matSelfColor = new Color(0.055f, 0.095f, 0.16f);
public Color matOpponentColor = new Color(0.13f, 0.065f, 0.075f);
public Color lineColor = new Color(0.62f, 0.45f, 0.18f);
public Color ringColor = new Color(0.18f, 0.28f, 0.44f);
public Color vanguardRingColor = new Color(0.78f, 0.60f, 0.24f);
public Color guardianColor = new Color(0.18f, 0.58f, 0.70f);
public Color damageColor = new Color(0.58f, 0.16f, 0.18f);
public Color soulColor = new Color(0.42f, 0.25f, 0.68f);
public Color highlightColor = new Color(1f, 0.78f, 0.24f);
public Color selectedColor = new Color(0.35f, 0.95f, 0.56f);
[Header("Необязательный prefab карты")]
public Card3D cardPrefab;
private static PlaymatLayoutConfig _default;
/// Рантайм-конфиг, если в сцене нет ассета.
public static PlaymatLayoutConfig Default
{
get
{
if (_default == null)
{
_default = CreateInstance();
_default.name = "PlaymatLayoutConfig (runtime)";
_default.hideFlags = HideFlags.DontSave;
}
return _default;
}
}
public static PlaymatLayoutConfig CreateDefault()
{
var config = CreateInstance();
config.name = "PlaymatLayoutConfig";
return config;
}
public float CardCenterY => cardLift + (cardThickness * 0.5f);
public float GuardianCenterY => guardianLift + (cardThickness * 0.5f);
public float DamageCenterY => damageLift + (cardThickness * 0.5f);
private void OnValidate()
{
cardWidth = Mathf.Max(0.1f, cardWidth);
cardHeight = Mathf.Max(0.1f, cardHeight);
cardThickness = Mathf.Max(0.001f, cardThickness);
circleSpacingX = Mathf.Max(0.4f, circleSpacingX);
frontRowZ = Mathf.Max(0.2f, frontRowZ);
backRowZ = Mathf.Max(frontRowZ + 0.2f, backRowZ);
boardHalfWidth = Mathf.Max(2f, boardHalfWidth);
boardHalfLength = Mathf.Max(backRowZ + 0.5f, boardHalfLength);
dragLift = Mathf.Max(cardLift + 0.02f, dragLift);
snapRadius = Mathf.Clamp(snapRadius, 0.2f, 1.5f);
handMaxWidth = Mathf.Max(1f, handMaxWidth);
guardianMaxWidth = Mathf.Max(1f, guardianMaxWidth);
handViewportY = Mathf.Clamp(handViewportY, 0.05f, 0.45f);
handViewportX = Mathf.Clamp(handViewportX, 0.05f, 0.95f);
damageStackOffset = Mathf.Clamp(damageStackOffset, 0.1f, 0.9f);
introCameraFov = Mathf.Clamp(introCameraFov, 20f, 90f);
handViewDistance = Mathf.Max(1f, handViewDistance);
handViewMaxWidth = Mathf.Max(0.5f, handViewMaxWidth);
handViewScale = Mathf.Clamp(handViewScale, 0.3f, 1.5f);
handViewRoll = Mathf.Clamp(handViewRoll, 0f, 20f);
handViewArc = Mathf.Max(0f, handViewArc);
opponentHandZ = Mathf.Max(1f, opponentHandZ);
opponentHandHeight = Mathf.Max(0.2f, opponentHandHeight);
opponentHandSpacing = Mathf.Max(0.1f, opponentHandSpacing);
opponentHandMaxWidth = Mathf.Max(0.5f, opponentHandMaxWidth);
opponentHandScale = Mathf.Clamp(opponentHandScale, 0.3f, 1.5f);
opponentHandRoll = Mathf.Clamp(opponentHandRoll, 0f, 20f);
opponentHandArc = Mathf.Max(0f, opponentHandArc);
}
}
}