239 lines
8.3 KiB
C#
239 lines
8.3 KiB
C#
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|