Happy_Forest/Assets/Scripts/PilleDotsBootstrap.cs

239 lines
8.3 KiB
C#
Raw Normal View History

2026-08-03 00:56:52 +02:00
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;
namespace HappyForest.Prototypen
{
// Einziges MonoBehaviour im DOTS-Setup. Aufgabe: Input lesen (Space/M/C/K),
// die Entity-World initialisieren und Pillen-Entities spawnen/löschen.
// Bewegung + Kollision laufen komplett in ISystem/Burst (siehe PilleMovementSystem
// und PilleCollisionSystem) - hier passiert keine Simulationslogik mehr.
public class PilleDotsBootstrap : MonoBehaviour
{
[Header("Rendering (Pflichtfelder)")]
[SerializeField] private Mesh pilleMesh;
[SerializeField] private Material pilleMaterial;
[Header("Einstellungen")]
[SerializeField] private float speed = 5f;
[SerializeField] private float pilleRadius = 0.5f;
[SerializeField] private float spawnHeight = 1f;
[SerializeField] private string wallTag = "Wall";
[SerializeField] private float actionDelay = 0.05f;
private EntityManager entityManager;
private Entity pilleTemplateEntity;
private EntityQuery pilleQuery;
private EntityQuery configQuery;
private int pilleCount = 0;
private float lastActionTime = 0f;
private bool collisionEnabledCache = false;
private Texture2D backgroundTexture;
void Start()
{
if (pilleMesh == null || pilleMaterial == null)
{
Debug.LogError("❌ PilleDotsBootstrap: Mesh oder Material im Inspector zuweisen!");
enabled = false;
return;
}
entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
SetupSimulationConfig();
SetupPilleTemplate();
pilleQuery = entityManager.CreateEntityQuery(typeof(PilleComponent), typeof(LocalTransform));
configQuery = entityManager.CreateEntityQuery(typeof(PilleSimulationConfig));
backgroundTexture = MakeTex(new Color(0f, 0f, 0f, 0.65f));
Debug.Log("✅ PilleDotsBootstrap bereit! Space=Spawn M=Delete C=Spawn100 K=Kollision Toggle");
}
void SetupSimulationConfig()
{
var wallBounds = ComputeWallBounds();
float margin = pilleRadius + 0.1f;
var configEntity = entityManager.CreateEntity(typeof(PilleSimulationConfig));
entityManager.SetComponentData(configEntity, new PilleSimulationConfig
{
BoundsMin = new float3(wallBounds.min.x + margin, spawnHeight, wallBounds.min.z + margin),
BoundsMax = new float3(wallBounds.max.x - margin, spawnHeight, wallBounds.max.z - margin),
PlayHeight = spawnHeight,
PilleRadius = pilleRadius,
PilleCollisionEnabled = false
});
}
Bounds ComputeWallBounds()
{
var walls = GameObject.FindGameObjectsWithTag(wallTag);
if (walls.Length == 0)
{
Debug.LogWarning($"⚠️ Keine GameObjects mit Tag '{wallTag}' gefunden. Nutze Standard-Grenzen (50x100).");
return new Bounds(Vector3.zero, new Vector3(50, 10, 100));
}
Bounds combined = default;
bool first = true;
foreach (var wall in walls)
{
var rend = wall.GetComponent<Renderer>();
if (rend == null) continue;
if (first) { combined = rend.bounds; first = false; }
else combined.Encapsulate(rend.bounds);
}
Debug.Log($"✅ Feld-Grenzen aus '{wallTag}'-Objekten berechnet: Min={combined.min} Max={combined.max}");
return combined;
}
void SetupPilleTemplate()
{
pilleTemplateEntity = entityManager.CreateEntity(
typeof(PilleComponent),
typeof(LocalTransform),
typeof(LocalToWorld));
var desc = new RenderMeshDescription(ShadowCastingMode.On, receiveShadows: true);
var renderMeshArray = new RenderMeshArray(new[] { pilleMaterial }, new[] { pilleMesh });
RenderMeshUtility.AddComponents(
pilleTemplateEntity,
entityManager,
desc,
renderMeshArray,
MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0));
entityManager.SetComponentData(pilleTemplateEntity,
LocalTransform.FromPosition(new float3(0, spawnHeight, 0)));
entityManager.SetComponentData(pilleTemplateEntity, new PilleComponent
{
Velocity = new float3(1, 0, 0),
Speed = speed
});
// WICHTIG: Prefab-Tag, damit dieses Template NICHT von den Queries/Systemen
// (Bewegung, Kollision, Zählung) erfasst wird. Instantiate() entfernt den Tag
// automatisch bei jeder Kopie.
entityManager.AddComponent<Prefab>(pilleTemplateEntity);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.K))
{
ToggleCollision();
}
if (Time.time - lastActionTime < actionDelay)
return;
if (Input.GetKey(KeyCode.Space))
{
SpawnPille();
lastActionTime = Time.time;
}
else if (Input.GetKey(KeyCode.M))
{
DeletePille();
lastActionTime = Time.time;
}
else if (Input.GetKey(KeyCode.C))
{
for (int i = 0; i < 100; i++)
SpawnPille();
lastActionTime = Time.time;
}
}
void SpawnPille()
{
float3 spawnPos = new float3(0, spawnHeight, 0);
int existing = pilleQuery.CalculateEntityCount();
if (existing > 0)
{
var transforms = pilleQuery.ToComponentDataArray<LocalTransform>(Allocator.Temp);
int idx = UnityEngine.Random.Range(0, transforms.Length);
spawnPos = transforms[idx].Position;
transforms.Dispose();
}
Entity newPille = entityManager.Instantiate(pilleTemplateEntity);
float angle = UnityEngine.Random.Range(0f, math.PI * 2f);
var velocity = new float3(math.cos(angle), 0, math.sin(angle));
entityManager.SetComponentData(newPille, LocalTransform.FromPosition(spawnPos));
entityManager.SetComponentData(newPille, new PilleComponent
{
Velocity = velocity,
Speed = speed
});
pilleCount++;
}
void DeletePille()
{
if (pilleQuery.CalculateEntityCount() == 0)
return;
var entities = pilleQuery.ToEntityArray(Allocator.Temp);
entityManager.DestroyEntity(entities[0]);
entities.Dispose();
pilleCount--;
}
void ToggleCollision()
{
if (configQuery.CalculateEntityCount() == 0)
return;
var configEntity = configQuery.GetSingletonEntity();
var cfg = entityManager.GetComponentData<PilleSimulationConfig>(configEntity);
cfg.PilleCollisionEnabled = !cfg.PilleCollisionEnabled;
entityManager.SetComponentData(configEntity, cfg);
collisionEnabledCache = cfg.PilleCollisionEnabled;
}
Texture2D MakeTex(Color color)
{
var tex = new Texture2D(1, 1);
tex.SetPixel(0, 0, color);
tex.Apply();
return tex;
}
void OnGUI()
{
GUI.DrawTexture(new Rect(5, 5, 430, 90), backgroundTexture);
GUI.color = Color.white;
GUI.skin.label.fontSize = 18;
GUI.Label(new Rect(15, 10, 410, 25), $"Pillen: {pilleCount} | FPS: {(int)(1f / Time.deltaTime)}");
GUI.Label(new Rect(15, 35, 410, 25), $"Pille-Kollision (K): {(collisionEnabledCache ? "AN" : "AUS")}");
GUI.Label(new Rect(15, 60, 410, 25), "Space=Spawn M=Delete C=Spawn100 K=Kollision Toggle");
}
void OnDestroy()
{
if (backgroundTexture != null)
Destroy(backgroundTexture);
}
}
}