77 lines
2.6 KiB
C#
77 lines
2.6 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
namespace LiveOpsExamples
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Spawns collectible snacks at random positions inside the configured bounds,
|
|||
|
|
/// picking a random collectible slug (moonberry/acorn/star/snack) and applying
|
|||
|
|
/// its sprite from the library. Mirrors GameScene.spawnCollectible.
|
|||
|
|
///
|
|||
|
|
/// Phaser spawn margins x∈[115,1165], y∈[118,625] px on a 1280×720 field at
|
|||
|
|
/// 100 PPU map to x∈[-5.25,5.25], y∈[-2.65,2.42] world units (y is flipped).
|
|||
|
|
/// </summary>
|
|||
|
|
public class SnackSpawner : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
private static readonly string[] CollectibleSlugs = { "moonberry", "acorn", "star", "snack" };
|
|||
|
|
|
|||
|
|
[SerializeField] private Snack prefab;
|
|||
|
|
[SerializeField] private CozyCollectorSpriteLibrary library;
|
|||
|
|
[SerializeField] private Vector2 spawnMin = new Vector2(-5.25f, -2.65f);
|
|||
|
|
[SerializeField] private Vector2 spawnMax = new Vector2(5.25f, 2.42f);
|
|||
|
|
[SerializeField] private Transform parent;
|
|||
|
|
|
|||
|
|
private readonly List<Snack> _spawned = new List<Snack>();
|
|||
|
|
|
|||
|
|
public SpriteRenderer Player { get; set; }
|
|||
|
|
|
|||
|
|
public event Action<Snack> Collected;
|
|||
|
|
|
|||
|
|
public Snack Spawn()
|
|||
|
|
{
|
|||
|
|
var slug = CollectibleSlugs[UnityEngine.Random.Range(0, CollectibleSlugs.Length)];
|
|||
|
|
var position = new Vector3(
|
|||
|
|
UnityEngine.Random.Range(spawnMin.x, spawnMax.x),
|
|||
|
|
UnityEngine.Random.Range(spawnMin.y, spawnMax.y),
|
|||
|
|
0f);
|
|||
|
|
|
|||
|
|
var snack = Instantiate(prefab, position, Quaternion.identity, parent);
|
|||
|
|
var sprite = library.ForSlug(slug);
|
|||
|
|
snack.GetComponent<SpriteRenderer>().sprite = sprite;
|
|||
|
|
// Phaser setDisplaySize(52, 52) at 100 PPU -> 0.52 x 0.52 world units.
|
|||
|
|
var spriteBounds = sprite.bounds.size;
|
|||
|
|
var scale = new Vector3(0.52f / spriteBounds.x, 0.52f / spriteBounds.y, 1f);
|
|||
|
|
snack.transform.localScale = scale;
|
|||
|
|
// The prefab collider radius is tuned to snack.png; a circle collider's
|
|||
|
|
// world radius scales by the largest transform axis, so retune per sprite.
|
|||
|
|
snack.GetComponent<CircleCollider2D>().radius = 0.22f / Mathf.Max(scale.x, scale.y);
|
|||
|
|
snack.Player = Player;
|
|||
|
|
snack.Collected += OnSnackCollected;
|
|||
|
|
_spawned.Add(snack);
|
|||
|
|
return snack;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Clear()
|
|||
|
|
{
|
|||
|
|
foreach (var snack in _spawned)
|
|||
|
|
{
|
|||
|
|
if (snack == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
snack.Collected -= OnSnackCollected;
|
|||
|
|
Destroy(snack.gameObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_spawned.Clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnSnackCollected(Snack snack)
|
|||
|
|
{
|
|||
|
|
snack.Collected -= OnSnackCollected;
|
|||
|
|
_spawned.Remove(snack);
|
|||
|
|
Collected?.Invoke(snack);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|