Danro-Jump/Assets/Scripts/Runtime/Vfx/GameVfxService.cs
Danil Iarosh 9549d42ea3 Add unit tests for security contour, COM port shutdown and VFX service
- Äîáàâëåíû EditMode òåñòû äëÿ âåðèôèêàöèè ââîäà PIN-êîäà àâòîðèçàöèè ïåðåä îòêðûòèåì ìåíþ íàñòðîåê â MainMenuControllerServiceActionTests.cs.
- Ñîçäàíû íîâûå EditMode òåñòû VfxServiceTests.cs, ïîêðûâàþùèå null-safety è êîððåêòíîå ñîçäàíèå GameObject ñèñòåì ÷àñòèö äëÿ IVfxService è GameVfxService.
- Ñîçäàí íîâûé EditMode êëàññ òåñòîâ ComSystemShutdownTests.cs, ïðîâåðÿþùèé áûñòðîå è áåçîïàñíîå çàêðûòèå COM-ñèñòåìû.
- Èñïðàâëåí ïîðÿäîê âûçîâà RequestClose() â ServiceTextInputModalWindow.Confirm() äëÿ ïðåäîòâðàùåíèÿ çàêðûòèÿ ìîäàëüíûõ îêîí ïîâåðõ òåêóùåãî ïðè çàêðûòèè îêíà ââîäà.
- Èñïðàâëåíî óíè÷òîæåíèå îáúåêòîâ â GameVfxService.cs è JumpPlayerController.cs â EditMode ñ ïîìîùüþ ïðîâåðêè Application.isPlaying.
- Äîáàâëåíà ëåíèâàÿ èíèöèàëèçàöèÿ Rigidbody2D â ìåòîäàõ Bounce() è Die() â JumpPlayerController.cs äëÿ ïðåäîòâðàùåíèÿ NullReferenceException â èçîëèðîâàííûõ òåñòàõ.
- Âñå 174 EditMode è 7 PlayMode òåñòîâ óñïåøíî ïðîéäåíû â Unity Editor.
2026-06-23 15:38:21 +03:00

213 lines
9.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
namespace DanroJump.Vfx
{
/// <summary>
/// Служба визуальных эффектов, программно создающая и настраивающая системы частиц Unity.
/// Позволяет избежать зависимости от префабов в редакторе и обеспечивает высокую переносимость.
/// </summary>
public sealed class GameVfxService : IVfxService
{
public void SpawnJumpEffect(Vector3 position)
{
var go = new GameObject("VFX_Jump");
go.transform.position = position;
var ps = go.AddComponent<ParticleSystem>();
var main = ps.main;
main.startColor = new Color(0.9f, 0.9f, 0.9f, 0.5f);
main.startSize = new ParticleSystem.MinMaxCurve(0.08f, 0.22f);
main.startSpeed = new ParticleSystem.MinMaxCurve(1.5f, 3.5f);
main.startLifetime = new ParticleSystem.MinMaxCurve(0.2f, 0.45f);
main.duration = 0.25f;
main.loop = false;
main.gravityModifier = 0.6f;
var shape = ps.shape;
shape.shapeType = ParticleSystemShapeType.Cone;
shape.angle = 35f;
shape.radius = 0.15f;
shape.rotation = new Vector3(90f, 0f, 0f); // Направление вниз под ноги
var emission = ps.emission;
emission.rateOverTime = 0;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 12) });
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
var gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(Color.white, 0f), new GradientColorKey(Color.white, 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(0.6f, 0f), new GradientAlphaKey(0f, 1f) }
);
colorOverLifetime.color = gradient;
ps.Play();
if (Application.isPlaying)
{
Object.Destroy(go, main.duration + main.startLifetime.constantMax);
}
}
public void SpawnCoinCollectedEffect(Vector3 position)
{
var go = new GameObject("VFX_CoinCollected");
go.transform.position = position;
var ps = go.AddComponent<ParticleSystem>();
var main = ps.main;
main.startColor = new Color(1.0f, 0.85f, 0.0f, 0.9f); // Золотой цвет
main.startSize = new ParticleSystem.MinMaxCurve(0.1f, 0.25f);
main.startSpeed = new ParticleSystem.MinMaxCurve(2f, 4f);
main.startLifetime = new ParticleSystem.MinMaxCurve(0.3f, 0.6f);
main.duration = 0.3f;
main.loop = false;
main.gravityModifier = -0.2f; // Легкий подъем вверх
var shape = ps.shape;
shape.shapeType = ParticleSystemShapeType.Sphere;
shape.radius = 0.1f;
var emission = ps.emission;
emission.rateOverTime = 0;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 18) });
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
var gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(new Color(1f, 0.9f, 0.2f), 0f), new GradientColorKey(new Color(1f, 0.5f, 0f), 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(0.9f, 0f), new GradientAlphaKey(0f, 1f) }
);
colorOverLifetime.color = gradient;
ps.Play();
if (Application.isPlaying)
{
Object.Destroy(go, main.duration + main.startLifetime.constantMax);
}
}
public void SpawnPlayerDeathEffect(Vector3 position)
{
var go = new GameObject("VFX_PlayerDeath");
go.transform.position = position;
var ps = go.AddComponent<ParticleSystem>();
var main = ps.main;
main.startColor = new Color(0.8f, 0.1f, 0.1f, 0.8f); // Красный / серый взрыв
main.startSize = new ParticleSystem.MinMaxCurve(0.15f, 0.35f);
main.startSpeed = new ParticleSystem.MinMaxCurve(3f, 7f);
main.startLifetime = new ParticleSystem.MinMaxCurve(0.4f, 0.8f);
main.duration = 0.5f;
main.loop = false;
main.gravityModifier = 0.8f;
var shape = ps.shape;
shape.shapeType = ParticleSystemShapeType.Sphere;
shape.radius = 0.25f;
var emission = ps.emission;
emission.rateOverTime = 0;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 35) });
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
var gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(new Color(0.9f, 0.2f, 0.2f), 0f), new GradientColorKey(new Color(0.3f, 0.3f, 0.3f), 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(0.9f, 0f), new GradientAlphaKey(0f, 1f) }
);
colorOverLifetime.color = gradient;
ps.Play();
if (Application.isPlaying)
{
Object.Destroy(go, main.duration + main.startLifetime.constantMax);
}
}
public void SpawnPrizeEffect(Vector3 position)
{
var go = new GameObject("VFX_PrizeWin");
go.transform.position = position;
var ps = go.AddComponent<ParticleSystem>();
var main = ps.main;
main.startColor = Color.white; // Конфетти будут разных цветов
main.startSize = new ParticleSystem.MinMaxCurve(0.12f, 0.28f);
main.startSpeed = new ParticleSystem.MinMaxCurve(4f, 8f);
main.startLifetime = new ParticleSystem.MinMaxCurve(0.6f, 1.2f);
main.duration = 0.8f;
main.loop = false;
main.gravityModifier = 0.4f;
var shape = ps.shape;
shape.shapeType = ParticleSystemShapeType.Hemisphere; // Разлет вверх полусферой
shape.radius = 0.3f;
shape.rotation = new Vector3(-90f, 0f, 0f); // Поворот вверх
var emission = ps.emission;
emission.rateOverTime = 0;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 50) });
// Добавим смену цветов
var colorBySpeed = ps.colorBySpeed; // Используем градиент для создания разноцветных конфетти
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
// Настроим случайный цвет при рождении через градиент с множеством ключей или Random Between Two Colors
main.startColor = new ParticleSystem.MinMaxGradient(
new Color(0.2f, 0.6f, 1.0f), // Голубой
new Color(1.0f, 0.2f, 0.6f) // Розовый
);
ps.Play();
if (Application.isPlaying)
{
Object.Destroy(go, main.duration + main.startLifetime.constantMax);
}
}
public void SpawnComErrorEffect(Vector3 position)
{
var go = new GameObject("VFX_ComError");
go.transform.position = position;
var ps = go.AddComponent<ParticleSystem>();
var main = ps.main;
main.startColor = new Color(1.0f, 0.3f, 0.0f, 0.9f); // Оранжевые тревожные искры
main.startSize = new ParticleSystem.MinMaxCurve(0.06f, 0.18f);
main.startSpeed = new ParticleSystem.MinMaxCurve(3f, 6f);
main.startLifetime = new ParticleSystem.MinMaxCurve(0.25f, 0.5f);
main.duration = 0.4f;
main.loop = false;
main.gravityModifier = 0.3f;
var shape = ps.shape;
shape.shapeType = ParticleSystemShapeType.Cone;
shape.angle = 60f;
shape.radius = 0.1f;
var emission = ps.emission;
emission.rateOverTime = 0;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 20) });
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
var gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(new Color(1f, 0.5f, 0f), 0f), new GradientColorKey(new Color(0.8f, 0.1f, 0f), 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(0.9f, 0f), new GradientAlphaKey(0f, 1f) }
);
colorOverLifetime.color = gradient;
ps.Play();
if (Application.isPlaying)
{
Object.Destroy(go, main.duration + main.startLifetime.constantMax);
}
}
}
}