Initial commit
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user