109 lines
3.8 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 System;
using System.Collections.Generic;
using Fusion;
using UnityEngine;
namespace TPSBR
{
public sealed class LootBoxSpawner : NetworkBehaviour
{
[Header("Prefab")]
[SerializeField] private NetworkPrefabRef _itemBoxPrefab;
[Header("Placement")]
[Min(1)] [SerializeField] private int _count = 10;
[Min(0f)] [SerializeField] private float _radius = 40f;
[Min(0f)] [SerializeField] private float _minSpacing = 3f;
[SerializeField] private LayerMask _groundMask = ~0;
[SerializeField] private float _raycastUp = 60f;
[SerializeField] private float _raycastDown = 120f;
[SerializeField] private uint _seed = 12345;
[SerializeField] private bool _randomYaw = true;
// Keep references to despawn/respawn later if you extend it
private readonly List<NetworkObject> _spawned = new();
public override void Spawned()
{
if (!HasStateAuthority) return;
SpawnAll();
}
private void SpawnAll()
{
// If this is re-entered (e.g., soft reset), clean previous
foreach (var no in _spawned)
if (no && no.IsValid) Runner.Despawn(no);
_spawned.Clear();
var rnd = new System.Random(unchecked((int)_seed));
var positions = SamplePositions(transform.position, _radius, _count, _minSpacing, rnd);
foreach (var p in positions)
{
var rot = _randomYaw ? Quaternion.Euler(0f, (float)rnd.NextDouble() * 360f, 0f) : Quaternion.identity;
// Spawn server-side; replicated to all clients
var obj = Runner.Spawn(_itemBoxPrefab, p, rot);
_spawned.Add(obj);
}
}
// Deterministic sampling inside a circle with ground projection + spacing
private List<Vector3> SamplePositions(Vector3 center, float radius, int count, float minSpacing, System.Random rnd)
{
var results = new List<Vector3>(count);
var maxTrials = 2500; // generous to pack in cluttered areas
int trials = 0;
while (results.Count < count && trials++ < maxTrials)
{
// pick point in disc (sqrt for uniform)
float r = radius * Mathf.Sqrt((float)rnd.NextDouble());
float ang = Mathf.PI * 2f * (float)rnd.NextDouble();
Vector3 local = new Vector3(Mathf.Cos(ang) * r, 0f, Mathf.Sin(ang) * r);
Vector3 rayFrom = center + local + Vector3.up * _raycastUp;
if (Physics.Raycast(rayFrom, Vector3.down, out var hit, _raycastUp + _raycastDown, _groundMask, QueryTriggerInteraction.Ignore))
{
var pos = hit.point;
// spacing test
bool ok = true;
if (minSpacing > 0f)
{
for (int i = 0; i < results.Count; i++)
{
if (Vector3.SqrMagnitude(results[i] - pos) < minSpacing * minSpacing) { ok = false; break; }
}
}
if (ok) results.Add(pos);
}
}
if (results.Count < count)
Debug.LogWarning($"[LootBoxSpawner] Only placed {results.Count}/{count}. Increase radius or lower spacing.");
return results;
}
// Scene gizmo previews the exact deterministic layout youll get at runtime
private void OnDrawGizmosSelected()
{
Gizmos.color = new Color(0.1f, 0.8f, 1f, 0.25f);
Gizmos.DrawWireSphere(transform.position, _radius);
// preview sample using same seed
var rnd = new System.Random(unchecked((int)_seed));
var preview = SamplePositions(transform.position, _radius, _count, _minSpacing, rnd);
Gizmos.color = new Color(0.1f, 0.8f, 1f, 0.7f);
foreach (var p in preview)
{
Gizmos.DrawSphere(p + Vector3.up * 0.15f, 0.25f);
Gizmos.DrawLine(p + Vector3.up * 3f, p); // drop line
}
}
}
}