Files
rudder-unity-sdk/Assets/LiveOpsExamples/Scripts/GameWorld.cs
T

150 lines
4.3 KiB
C#
Raw Normal View History

2026-08-12 14:03:44 +03:00
using System;
using UnityEngine;
namespace LiveOpsExamples
{
/// <summary>
/// Gameplay world for the Cozy Collector demo — the Unity port of the Phaser
/// GameScene.ts. Renders only the game world: player movement (WASD/arrows),
/// round timer, spawn cadence and pickups. All chrome lives in the UI panels,
/// all SDK calls in <see cref="CozyCollectorController"/>.
///
/// The Phaser field is 1280×720 px at 100 PPU, i.e. 12.8×7.2 world units
/// centered on the origin; tuning speeds/intervals stay in Phaser units
/// (px/s, ms) and are converted here.
/// </summary>
public class GameWorld : MonoBehaviour
{
private const float PixelsPerUnit = 100f;
[SerializeField] private SpriteRenderer player;
[SerializeField] private SnackSpawner spawner;
[SerializeField] private CozyCollectorController controller;
[SerializeField] private ScorePopupView scorePopupPrefab;
[SerializeField] private ParticleSystem collectBurstPrefab;
[SerializeField] private Vector2 moveMin = new Vector2(-6.0f, -3.2f);
[SerializeField] private Vector2 moveMax = new Vector2(6.0f, 3.2f);
private bool _paused;
private float _spawnElapsedMs;
private int _lastSecond = -1;
private bool _finishRaised;
public int Score { get; private set; }
public int Collected { get; private set; }
public float TimeLeftSeconds { get; private set; }
public bool RoundActive { get; private set; }
public event Action StateChanged;
public event Action<int, int> RoundFinished;
private void Awake()
{
spawner.Player = player;
spawner.Collected += OnSnackCollected;
}
private void OnDestroy()
{
spawner.Collected -= OnSnackCollected;
}
private void Update()
{
MovePlayer(_paused || !RoundActive);
if (!RoundActive || _paused)
return;
TimeLeftSeconds = Mathf.Max(0f, TimeLeftSeconds - Time.deltaTime);
_spawnElapsedMs += Time.deltaTime * 1000f;
if (_spawnElapsedMs >= controller.Tuning.SpawnIntervalMs)
{
_spawnElapsedMs = 0f;
spawner.Spawn();
}
var second = Mathf.CeilToInt(TimeLeftSeconds);
if (second != _lastSecond)
{
_lastSecond = second;
StateChanged?.Invoke();
}
if (TimeLeftSeconds <= 0f)
FinishRound();
}
public void SetPaused(bool paused)
{
_paused = paused;
}
public void RestartRound()
{
Score = 0;
Collected = 0;
TimeLeftSeconds = controller.Tuning.RoundSeconds;
_spawnElapsedMs = 0f;
_lastSecond = -1;
_finishRaised = false;
spawner.Clear();
RoundActive = true;
spawner.Spawn();
StateChanged?.Invoke();
}
private void MovePlayer(bool frozen)
{
if (frozen)
return;
var vx = (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A) ? -1 : 0) +
(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D) ? 1 : 0);
var vy = (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S) ? -1 : 0) +
(Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W) ? 1 : 0);
if (vx == 0 && vy == 0)
return;
var direction = new Vector2(vx, vy).normalized;
var speed = controller.Tuning.PlayerSpeed / PixelsPerUnit;
var position = player.transform.position;
position += (Vector3)(direction * speed * Time.deltaTime);
position.x = Mathf.Clamp(position.x, moveMin.x, moveMax.x);
position.y = Mathf.Clamp(position.y, moveMin.y, moveMax.y);
player.transform.position = position;
if (vx != 0)
player.flipX = vx < 0;
}
private void OnSnackCollected(Snack snack)
{
var position = snack.transform.position;
var gain = Mathf.RoundToInt(controller.Tuning.CollectibleScore);
Score += gain;
Collected += 1;
var popup = Instantiate(scorePopupPrefab, position, Quaternion.identity);
popup.Set(gain);
Instantiate(collectBurstPrefab, position, Quaternion.identity);
StateChanged?.Invoke();
}
private void FinishRound()
{
if (_finishRaised)
return;
_finishRaised = true;
RoundActive = false;
spawner.Clear();
StateChanged?.Invoke();
RoundFinished?.Invoke(Score, Collected);
}
}
}