Урна: дух летит к делу лицом, над местом жестикулирует руками, дело - в середине жеста, назад лицом к урне

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-16 20:03:53 +03:00
co-authored by Claude Opus 5.5
parent 23587d78e0
commit 7e93e19c5f
3 changed files with 150 additions and 32 deletions
+66 -18
View File
@@ -226,10 +226,20 @@ namespace NecromancerTome
{
float _yaw;
float _phase;
/// <summary>Полёт к месту работы и обратно: точка в координатах модели урны и сколько осталось.</summary>
/// <summary>Полёт к месту работы и обратно (16.10: «возвращается задом», «делал действие не сразу, а хоть руками
/// пошевелив»): туда - лицом к месту, над местом - жест руками (дело делается в его середине, см. ActDelay),
/// обратно - развернувшись лицом к урне. Точка - в координатах модели урны; _t - время с начала полёта, -1 - дома.</summary>
Vector3 _visit;
float _visitLeft;
public const float VisitSeconds = 2.2f;
float _t = -1f;
public static float OutSeconds = 1.3f;
public static float WorkSeconds = 1.6f;
public static float BackSeconds = 1.3f;
/// <summary>Через сколько после начала полёта дело выполняется - в середине жеста.</summary>
public static float ActDelay => OutSeconds + WorkSeconds * 0.5f;
/// <summary>Жест: плечи вперёд до GestureFwd градусов, локти ходят на ±GestureWave вразнобой.</summary>
public static float GestureFwd = 55f;
public static float GestureWave = 30f;
bool _gesturing;
/// <summary>Почернел и замер (готовое класть некуда, решение 06.10).</summary>
bool _stuck;
/// <summary>Кости рук (плечо, предплечье, кисть по сторонам) и их поворот в позе покоя.</summary>
@@ -240,6 +250,11 @@ namespace NecromancerTome
/// <summary>Опустить руки из позы покоя: направление плеча и предплечья задаётся в осях модели (вперёд - +Z),
/// поворот - от текущего направления кости к нужному, так что оси самих костей значения не имеют.</summary>
public void Pose()
{
Pose(CinerariumGenie.ArmFwd, CinerariumGenie.Elbow, CinerariumGenie.Elbow);
}
void Pose(float _fwd, float _elbowL, float _elbowR)
{
if (_arm == null)
{
@@ -283,9 +298,9 @@ namespace NecromancerTome
continue;
}
float sign = Mathf.Sign(transform.InverseTransformPoint(up.position).x);
float o = CinerariumGenie.ArmOut * Mathf.Deg2Rad, f = CinerariumGenie.ArmFwd * Mathf.Deg2Rad;
float o = CinerariumGenie.ArmOut * Mathf.Deg2Rad, f = _fwd * Mathf.Deg2Rad;
Vector3 upDir = new Vector3(sign * Mathf.Sin(o), -Mathf.Cos(o) * Mathf.Cos(f), Mathf.Sin(f)).normalized;
Vector3 foreDir = Vector3.RotateTowards(upDir, Vector3.forward, CinerariumGenie.Elbow * Mathf.Deg2Rad, 0f);
Vector3 foreDir = Vector3.RotateTowards(upDir, Vector3.forward, (side == 0 ? _elbowL : _elbowR) * Mathf.Deg2Rad, 0f);
up.rotation = Quaternion.FromToRotation(fore.position - up.position, transform.TransformDirection(upDir)) * up.rotation;
fore.rotation = Quaternion.FromToRotation(hand.position - fore.position, transform.TransformDirection(foreDir)) * fore.rotation;
}
@@ -302,7 +317,7 @@ namespace NecromancerTome
return;
}
_visit = transform.parent.InverseTransformPoint(_worldUnity);
_visitLeft = VisitSeconds;
_t = 0f;
}
public void SetStuck(bool _on)
@@ -359,21 +374,54 @@ namespace NecromancerTome
}
float yawTarget = _yaw + CinerariumGenie.TurnSpeed * Time.deltaTime;
Vector3 home = new Vector3(0f, CinerariumGenie.OffsetY, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
if (_visitLeft > 0f)
if (_t >= 0f)
{
// Туда и обратно по синусу: на полпути - над местом работы, лицом к нему.
_visitLeft -= Time.deltaTime;
float k = Mathf.Sin(Mathf.Clamp01(1f - _visitLeft / VisitSeconds) * Mathf.PI);
Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
transform.localPosition = Vector3.Lerp(home, over, k);
Vector3 d = _visit - home;
if (d.x * d.x + d.z * d.z > 0.01f)
_t += Time.deltaTime;
if (_t < OutSeconds + WorkSeconds + BackSeconds)
{
yawTarget = Mathf.LerpAngle(_yaw, Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg, Time.deltaTime * 4f);
Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
Vector3 d = _visit - new Vector3(0f, CinerariumGenie.OffsetY, 0f);
bool far = d.x * d.x + d.z * d.z > 0.01f;
float toPlace = far ? Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg : _yaw;
float k, face, w = 0f;
if (_t < OutSeconds)
{
k = Mathf.SmoothStep(0f, 1f, _t / OutSeconds);
face = toPlace;
}
else if (_t < OutSeconds + WorkSeconds)
{
k = 1f;
face = toPlace;
w = Mathf.Sin((_t - OutSeconds) / WorkSeconds * Mathf.PI);
}
else
{
k = 1f - Mathf.SmoothStep(0f, 1f, (_t - OutSeconds - WorkSeconds) / BackSeconds);
face = far ? toPlace + 180f : _yaw; // назад - лицом к урне
}
_yaw = Mathf.MoveTowardsAngle(_yaw, face, 300f * Time.deltaTime) % 360f;
transform.localPosition = Vector3.Lerp(home, over, k);
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
if (w > 0f)
{
float wave = Mathf.Sin(_t * Mathf.PI * 2f * 1.5f) * GestureWave * w;
Pose(Mathf.Lerp(CinerariumGenie.ArmFwd, GestureFwd, w), CinerariumGenie.Elbow + 15f * w + wave, CinerariumGenie.Elbow + 15f * w - wave);
_gesturing = true;
}
else if (_gesturing)
{
Pose();
_gesturing = false;
}
return;
}
_t = -1f;
if (_gesturing)
{
Pose();
_gesturing = false;
}
_yaw = yawTarget % 360f;
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
return;
}
_yaw = yawTarget % 360f;
float bob = CinerariumGenie.BobPeriod > 0.01f