Огонёк оссуария: подъём снаружи котла и дуга точно через центр окошка (#69)
22.10: «Огонёк летит сквозь котёл. А надо чтобы поднимался почти на уровень окошка, и по дуге летел прямо через его центр». Путь - гладкая кривая через точки: нутро, центр окошка, дуга снаружи, вертикаль на 25 см дальше самого широкого места котла (по модели), земля. Скорость по пути ровная. necroossuary wisp log - вылет/влёт в лог (чтобы снять). Проверено в test8 снимками по сигналу из лога. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
643c9fa056
commit
ead6823c8a
+77
-22
@@ -17,6 +17,8 @@ namespace NecromancerTome
|
||||
public static bool Enabled = true;
|
||||
/// <summary>Дикий дух-огонёк (#69): летает, пока оссуарий работает. Выключатель - necroossuary wisp off.</summary>
|
||||
public static bool WispEnabled = true;
|
||||
/// <summary>necroossuary wisp log - писать в лог вылет и влёт (чтобы успеть снять).</summary>
|
||||
public static bool WispLog;
|
||||
public const string WispPath = "#@modfolder(NecromancerTome):Resources/necrowisp?necroWispPrefab.prefab";
|
||||
static GameObject _wispPrefab;
|
||||
static bool _wispTried;
|
||||
@@ -196,7 +198,10 @@ namespace NecromancerTome
|
||||
/// <summary>Окошки крышки (модель #67): шесть, между швами, через 60°; по высоте - на WindowUp от низа крышки,
|
||||
/// от оси - на WindowR (доли полуширины крышки; в модели 0.14 м и 0.37 м при полуширине 0.418).</summary>
|
||||
const float WindowUp = 0.14f / 0.836f, WindowR = 0.37f / 0.418f;
|
||||
const float Out = 0.9f; // насколько дальше от оси котла дух уходит в землю, м
|
||||
/// <summary>Подъём и спуск - вертикально снаружи, на Clear дальше самого широкого места котла (22.10: «Огонёк летит сквозь
|
||||
/// котёл. А надо чтобы поднимался почти на уровень окошка, и по дуге летел прямо через его центр»).</summary>
|
||||
const float Clear = 0.25f;
|
||||
const float BelowWindow = 0.1f; // вертикаль кончается на столько ниже окошка, м; дальше дуга
|
||||
const float Under = 0.35f; // насколько под землю, м
|
||||
const float FlyTime = 2.4f;
|
||||
static readonly int TintId = Shader.PropertyToID("_TintColor");
|
||||
@@ -208,7 +213,7 @@ namespace NecromancerTome
|
||||
TrailRenderer _trail;
|
||||
int _phase = -1; // -1 дома, 0 вылет и уход в землю, 1 под землёй, 2 подъём и влёт
|
||||
float _t0, _until;
|
||||
Vector3 _winOut, _winIn, _inOut, _inIn, _down, _up;
|
||||
Vector3[] _pathOut, _pathIn; // вылет: нутро -> окошко -> дуга -> вниз в землю; влёт - обратный порядок с другой стороны
|
||||
|
||||
static float Ease(float _k) => _k * _k * (3f - 2f * _k);
|
||||
|
||||
@@ -239,12 +244,16 @@ namespace NecromancerTome
|
||||
{
|
||||
return;
|
||||
}
|
||||
Show(_inOut);
|
||||
Show(_pathOut[0]);
|
||||
if (OssuaryGlow.WispLog)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] OssuaryWisp: вылет " + _pathOut[1]);
|
||||
}
|
||||
Next(0, FlyTime);
|
||||
return;
|
||||
case 0: // из окошка наружу и вниз, в землю; тает
|
||||
Fly(_inOut, _winOut, _down, k);
|
||||
Alpha(1f - Ease(Mathf.InverseLerp(0.25f, 1f, k)));
|
||||
Fly(_pathOut, k);
|
||||
Alpha(1f - Ease(Mathf.InverseLerp(0.45f, 1f, k)));
|
||||
if (k >= 1f)
|
||||
{
|
||||
Hide();
|
||||
@@ -255,13 +264,17 @@ namespace NecromancerTome
|
||||
if (now >= _until)
|
||||
{
|
||||
Alpha(0f);
|
||||
Show(_up);
|
||||
Show(_pathIn[0]);
|
||||
if (OssuaryGlow.WispLog)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] OssuaryWisp: влёт " + _pathIn[_pathIn.Length - 2]);
|
||||
}
|
||||
Next(2, FlyTime);
|
||||
}
|
||||
return;
|
||||
case 2: // с другой стороны снизу вверх, проявляется, влетает в противоположное окошко
|
||||
Fly(_up, _winIn, _inIn, k);
|
||||
Alpha(Ease(Mathf.InverseLerp(0f, 0.75f, k)));
|
||||
Fly(_pathIn, k);
|
||||
Alpha(Ease(Mathf.InverseLerp(0f, 0.55f, k)));
|
||||
if (k >= 1f)
|
||||
{
|
||||
Hide();
|
||||
@@ -279,41 +292,77 @@ namespace NecromancerTome
|
||||
_until = _t0 + _dur;
|
||||
}
|
||||
|
||||
/// <summary>Квадратичная кривая _a -> _b -> _c: проходит окошко _b по касательной.</summary>
|
||||
void Fly(Vector3 _a, Vector3 _b, Vector3 _c, float _k)
|
||||
/// <summary>Гладкая кривая (Catmull-Rom) точно через точки пути; скорость по пути ровная (время - по длинам отрезков).</summary>
|
||||
void Fly(Vector3[] _path, float _k)
|
||||
{
|
||||
float k = Ease(_k);
|
||||
Vector3 p = (1f - k) * (1f - k) * _a + 2f * k * (1f - k) * _b + k * k * _c;
|
||||
p.y += 0.02f * Mathf.Sin(Time.time * 7f); // лёгкое дрожание огонька
|
||||
float total = 0f;
|
||||
for (int i = 1; i < _path.Length; i++)
|
||||
{
|
||||
total += Vector3.Distance(_path[i - 1], _path[i]);
|
||||
}
|
||||
float s = Ease(_k) * total;
|
||||
int seg = 0;
|
||||
float len = 0f;
|
||||
for (; seg < _path.Length - 2; seg++)
|
||||
{
|
||||
len = Vector3.Distance(_path[seg], _path[seg + 1]);
|
||||
if (s <= len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
s -= len;
|
||||
}
|
||||
len = Vector3.Distance(_path[seg], _path[seg + 1]);
|
||||
float u = len > 0.0001f ? Mathf.Clamp01(s / len) : 1f;
|
||||
Vector3 p0 = _path[Mathf.Max(0, seg - 1)], p1 = _path[seg], p2 = _path[seg + 1], p3 = _path[Mathf.Min(_path.Length - 1, seg + 2)];
|
||||
Vector3 p = 0.5f * (2f * p1 + (p2 - p0) * u + (2f * p0 - 5f * p1 + 4f * p2 - p3) * u * u + (3f * p1 - p0 - 3f * p2 + p3) * u * u * u);
|
||||
p.y += 0.015f * Mathf.Sin(Time.time * 7f); // лёгкое дрожание огонька
|
||||
_go.transform.position = p;
|
||||
}
|
||||
|
||||
/// <summary>Окошко вылета - случайное из шести, влёта - напротив; точки в земле - снаружи от них, высота земли - лучом.</summary>
|
||||
/// <summary>Окошко вылета - случайное из шести, влёта - напротив. Путь: нутро, центр окошка, точка дуги снаружи на высоте
|
||||
/// окошка, вертикаль снаружи котла чуть ниже окошка, та же вертикаль под землёй (высота земли - лучом).</summary>
|
||||
bool Places(Transform _model)
|
||||
{
|
||||
Transform lid = _model.Find("lid");
|
||||
Transform body = _model.Find("body");
|
||||
Renderer lr = lid != null ? lid.GetComponent<Renderer>() : null;
|
||||
Renderer br = body != null ? body.GetComponent<Renderer>() : null;
|
||||
if (lr == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Bounds b = lr.bounds;
|
||||
Vector3 axis = new Vector3(b.center.x, b.min.y + WindowUp * (b.size.x + b.size.z) * 0.5f, b.center.z);
|
||||
float half = (b.size.x + b.size.z) * 0.25f;
|
||||
float bodyHalf = br != null ? (br.bounds.size.x + br.bounds.size.z) * 0.25f : half * 1.1f;
|
||||
float winY = b.min.y + WindowUp * half * 2f;
|
||||
Vector3 axis = new Vector3(b.center.x, winY, b.center.z);
|
||||
int w = Random.Range(0, 6);
|
||||
Vector3 dOut = lid.rotation * Quaternion.Euler(0f, w * 60f, 0f) * Vector3.right;
|
||||
dOut.y = 0f;
|
||||
dOut.Normalize();
|
||||
Vector3 dIn = -dOut;
|
||||
_winOut = axis + dOut * (half * WindowR);
|
||||
_winIn = axis + dIn * (half * WindowR);
|
||||
_inOut = axis + dOut * (half * WindowR * 0.4f);
|
||||
_inIn = axis + dIn * (half * WindowR * 0.4f);
|
||||
_down = Ground(axis + dOut * (half + Out)) + Vector3.down * Under;
|
||||
_up = Ground(axis + dIn * (half + Out)) + Vector3.down * Under;
|
||||
_pathOut = Path(axis, dOut, half, Mathf.Max(bodyHalf, half) + Clear);
|
||||
_pathIn = Path(axis, -dOut, half, Mathf.Max(bodyHalf, half) + Clear);
|
||||
System.Array.Reverse(_pathIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3[] Path(Vector3 _axis, Vector3 _dir, float _half, float _rOut)
|
||||
{
|
||||
float rWin = _half * WindowR;
|
||||
Vector3 low = _axis + _dir * _rOut + Vector3.down * BelowWindow;
|
||||
Vector3 under = Ground(_axis + _dir * _rOut) + Vector3.down * Under;
|
||||
return new[]
|
||||
{
|
||||
_axis + _dir * (rWin * 0.45f), // внутри котла
|
||||
_axis + _dir * rWin, // центр окошка
|
||||
_axis + _dir * (rWin + (_rOut - rWin) * 0.55f), // дуга: снаружи на высоте окошка
|
||||
low, // вертикаль снаружи, чуть ниже окошка
|
||||
new Vector3(low.x, Mathf.Lerp(low.y, under.y, 0.5f), low.z),
|
||||
under // в земле
|
||||
};
|
||||
}
|
||||
|
||||
Vector3 Ground(Vector3 _p)
|
||||
{
|
||||
if (Physics.Raycast(_p + Vector3.up * 1.5f, Vector3.down, out RaycastHit hit, 8f, SnakeControl.WallMask, QueryTriggerInteraction.Ignore))
|
||||
@@ -547,6 +596,12 @@ namespace NecromancerTome
|
||||
con.Output("necroossuary: пульсация " + OssuaryGlow.GlowPulse + ", скорость " + OssuaryGlow.GlowPulseSpeed);
|
||||
break;
|
||||
case "wisp":
|
||||
if (_params.Count > 1 && _params[1] == "log")
|
||||
{
|
||||
OssuaryGlow.WispLog = !OssuaryGlow.WispLog;
|
||||
con.Output("necroossuary: лог огонька " + (OssuaryGlow.WispLog ? "включён" : "выключен"));
|
||||
break;
|
||||
}
|
||||
if (_params.Count > 1)
|
||||
{
|
||||
OssuaryGlow.WispEnabled = _params[1] == "on";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user