Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Добавлен `ScoreSystem` — singleton-компонент, отслеживающий монеты, убитых врагов и экологические очки; финальный счёт вычисляется по формуле `Score = E × (P + 45C + 90K + 10K²) − 0.5T` с раздельными множителями для победы и проигрыша. - Добавлен `EnvironmentalCollectibleBase` — абстрактный базовый класс для подбираемых экологических объектов: срабатывает по триггеру, поддерживает пулинг, при подборе регистрирует очки в `ScoreSystem`. - Добавлен `LeafCollectible` — листик с синусоидальным горизонтальным дрейфом, вращением и детекцией земли. - Добавлен `BatCollectible` — летучая мышь с синусоидальным вертикальным движением, ограниченным горизонтальным блужданием и случайными сменами направления; переворачивает спрайт по X при смене направления. - Добавлен `EnvironmentalPool` — простой объектный пул по prefab-ключу с прогревом (`Warmup`) и возвратом объектов. - Добавлен `EnvironmentalCollectibleSpawner` — взвешенный случайный спаунер, поддерживающий фиксированное количество активных объектов; поддерживает лимиты по типу, принудительный выбор одного prefab и область спауна через `BoxCollider2D` или ручной `Bounds`. - В `CoinCollectible.TryPickup` добавлен вызов `ScoreSystem.Instance?.RegisterCoinCollected()`. - В `EnemyController.Die` добавлен guard через флаг `_isDead` для предотвращения повторного срабатывания; добавлен вызов `ScoreSystem.Instance?.RegisterEnemyKilled()`. - В `GameManager` добавлен вызов `ScoreSystem.Instance?.ResetStats()` при старте новой игры и при потере жизни.
73 lines
2.5 KiB
C#
73 lines
2.5 KiB
C#
using UnityEngine;
|
|
|
|
namespace RR.Common.Entities.Props
|
|
{
|
|
public sealed class LeafCollectible : EnvironmentalCollectibleBase
|
|
{
|
|
[Header("Fall")]
|
|
[SerializeField] private float fallSpeed = 1.25f;
|
|
[SerializeField] private float horizontalAmplitude = 0.4f;
|
|
[SerializeField] private float horizontalFrequency = 1.1f;
|
|
[SerializeField] private float rotationAmplitude = 22f;
|
|
[SerializeField] private float rotationSpeed = 2.2f;
|
|
|
|
[Header("Grounding")]
|
|
[SerializeField] private bool stopAtGround = true;
|
|
[SerializeField] private LayerMask groundMask = ~0;
|
|
[SerializeField] private float groundCheckDistance = 0.2f;
|
|
[SerializeField] private float minY = -10f;
|
|
[SerializeField] private float groundedSwayAmplitude = 0.08f;
|
|
[SerializeField] private float groundedSwaySpeed = 1.5f;
|
|
|
|
private Vector3 _basePosition;
|
|
private float _time;
|
|
private bool _isGrounded;
|
|
|
|
protected override void OnSpawned()
|
|
{
|
|
_basePosition = transform.position;
|
|
_time = 0f;
|
|
_isGrounded = false;
|
|
}
|
|
|
|
protected override void TickBehaviour(float dt)
|
|
{
|
|
_time += dt;
|
|
|
|
Vector3 position = _basePosition;
|
|
float horizontalOffset = Mathf.Sin(_time * horizontalFrequency) * horizontalAmplitude;
|
|
|
|
if (_isGrounded)
|
|
{
|
|
position.x += Mathf.Sin(_time * groundedSwaySpeed) * groundedSwayAmplitude;
|
|
transform.position = position;
|
|
transform.rotation = Quaternion.Euler(0f, 0f, Mathf.Sin(_time * rotationSpeed) * (rotationAmplitude * 0.25f));
|
|
return;
|
|
}
|
|
|
|
position.y -= fallSpeed * dt;
|
|
position.x += horizontalOffset;
|
|
|
|
bool reachedGround = position.y <= minY;
|
|
if (!reachedGround && stopAtGround)
|
|
{
|
|
RaycastHit2D hit = Physics2D.Raycast(position, Vector2.down, groundCheckDistance, groundMask);
|
|
reachedGround = hit.collider != null;
|
|
}
|
|
|
|
if (reachedGround)
|
|
{
|
|
_isGrounded = true;
|
|
}
|
|
else
|
|
{
|
|
_basePosition.y = position.y;
|
|
_basePosition.x = SpawnPosition.x;
|
|
}
|
|
|
|
transform.position = position;
|
|
transform.rotation = Quaternion.Euler(0f, 0f, Mathf.Sin(_time * rotationSpeed) * rotationAmplitude);
|
|
}
|
|
}
|
|
}
|