Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
| Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung | ||
| talit:fluid_simulation [2025-11-16 15:20] – [Auftrag A - Teil II] sca | talit:fluid_simulation [2025-11-16 19:27] (aktuell) – sca | ||
|---|---|---|---|
| Zeile 435: | Zeile 435: | ||
| 1. Dann liest man die entsprechenden Particles aus dem Dictionary aus. | 1. Dann liest man die entsprechenden Particles aus dem Dictionary aus. | ||
| 1. Nachdem man alle Zellen updated hat, **aktualisiert** man das Dictionary. | 1. Nachdem man alle Zellen updated hat, **aktualisiert** man das Dictionary. | ||
| + | |||
| + | === Unity Tipps === | ||
| == Dictionaries == | == Dictionaries == | ||
| Zeile 449: | Zeile 451: | ||
| myDict.ContainsKey(someKey); | myDict.ContainsKey(someKey); | ||
| </ | </ | ||
| + | |||
| + | <nodisp 2> | ||
| + | |||
| + | ++++Lösungen| | ||
| + | |||
| + | <code csharp> | ||
| + | // using static Unity.Mathematics.math; | ||
| + | using UnityEngine; | ||
| + | using System.Collections.Generic; | ||
| + | |||
| + | public enum CollisionDetectionType{ | ||
| + | BruteForce, | ||
| + | SpatialHashingDict | ||
| + | } | ||
| + | |||
| + | public class MainScript : MonoBehaviour | ||
| + | { | ||
| + | [Header(" | ||
| + | public GameObject prefabParticle; | ||
| + | // and all other game settings | ||
| + | |||
| + | // PRIVARTE FIELDS | ||
| + | // screen boarders | ||
| + | private float xMin = -9; | ||
| + | private float xMax = 9; | ||
| + | private float yMin = -5; | ||
| + | private float yMax = 5; | ||
| + | |||
| + | // particles | ||
| + | private bool giveParticlesInitVel = true; | ||
| + | private float vCompMax = 2.5f; | ||
| + | private float particleDiameter = 0.05f; // 0.05f | ||
| + | private float particleInitSep = 0.01f; // 0.01f | ||
| + | private int particleCount = 5000; // 500, make public later s.t. can adjust in Inspector | ||
| + | private float widthToHeightRatioInitState = 2.0f; // radio of rectangle in which particles are placed initially | ||
| + | private float particleInitSepRandRange = 0.0f; // should be smaller than particleInitSep / 2 to avoid overlap | ||
| + | |||
| + | // spatial hashing stuff | ||
| + | private CollisionDetectionType collisionDetectionType = CollisionDetectionType.SpatialHashingDict; | ||
| + | Dictionary< | ||
| + | private float gridSize; // should equal diameter of largest particle | ||
| + | |||
| + | // fields used in code below | ||
| + | private ParticleModel[] particleModels; | ||
| + | private ParticleView[] particleViews; | ||
| + | private SpriteRenderer[] particleRenderers; | ||
| + | float minDistanceSquared; | ||
| + | private float particleRadius; | ||
| + | private Color colorNoCollision = Color.green; | ||
| + | private Color colorCollision = Color.red; | ||
| + | |||
| + | void Start() | ||
| + | { | ||
| + | UnityEngine.Random.InitState(42); | ||
| + | InitParticles(); | ||
| + | } | ||
| + | |||
| + | void InitParticles() | ||
| + | { | ||
| + | Debug.Log(" | ||
| + | Debug.Log(" | ||
| + | |||
| + | // some calcs | ||
| + | minDistanceSquared = particleDiameter * particleDiameter; | ||
| + | particleRadius = particleDiameter / 2; | ||
| + | gridSize = particleDiameter; | ||
| + | |||
| + | // Check if prefab is assigned | ||
| + | if (prefabParticle == null) | ||
| + | { | ||
| + | Debug.LogError(" | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Create new arrays for particle models and views | ||
| + | particleModels = new ParticleModel[particleCount]; | ||
| + | particleViews = new ParticleView[particleCount]; | ||
| + | particleRenderers = new SpriteRenderer[particleCount]; | ||
| + | |||
| + | // Setup stuff for inital positions | ||
| + | int nx = Mathf.FloorToInt(Mathf.Sqrt((float)particleCount) * Mathf.Sqrt((float)widthToHeightRatioInitState)); | ||
| + | int ny = Mathf.CeilToInt(Mathf.Sqrt((float)particleCount) / Mathf.Sqrt((float)widthToHeightRatioInitState)); | ||
| + | if (nx * ny < particleCount) | ||
| + | { | ||
| + | Debug.Log(" | ||
| + | // throw; // new Exception(" | ||
| + | } | ||
| + | | ||
| + | // CREATE A PARTICLE AND ITS VIEW, THEN ASSIGN TO ARRAY | ||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | // create model for particle | ||
| + | int ix = i % nx; | ||
| + | int iy = i / nx; | ||
| + | float x = -nx / 2 * (particleRadius + particleInitSep) + ix * (particleRadius + particleInitSep); | ||
| + | float y = ny / 2 * (particleRadius + particleInitSep) - iy * (particleRadius + particleInitSep); | ||
| + | Vector2 position = new Vector2(UnityEngine.Random.Range(x - particleInitSepRandRange, | ||
| + | |||
| + | Vector2 velocity; | ||
| + | // initial velocity | ||
| + | if (giveParticlesInitVel) | ||
| + | { | ||
| + | velocity = new Vector2(UnityEngine.Random.Range(-vCompMax, | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | velocity = new Vector2(0, 0); | ||
| + | } | ||
| + | |||
| + | ParticleModel particleModel = new ParticleModel(position, | ||
| + | |||
| + | // create game object for particle with prefab as image, will be added to Unity Hierarchy: | ||
| + | GameObject particleGameObject = Instantiate(prefabParticle, | ||
| + | particleGameObject.transform.localScale = Vector3.one * particleDiameter; | ||
| + | if (particleGameObject != null) | ||
| + | { | ||
| + | // Try to get ParticleView component, if not found, add it | ||
| + | ParticleView particleView = particleGameObject.GetComponent< | ||
| + | if (particleView == null) | ||
| + | { | ||
| + | particleView = particleGameObject.AddComponent< | ||
| + | Debug.LogWarning(" | ||
| + | } | ||
| + | |||
| + | particleView.Bind(particleModel); | ||
| + | |||
| + | // assign particle model, view and renderer to array | ||
| + | particleModels[i] = particleModel; | ||
| + | particleViews[i] = particleView; | ||
| + | particleRenderers[i] = particleView.GetComponent< | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | Debug.LogError(" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Set correct color | ||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | particleRenderers[i].color = colorNoCollision; | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | void Update() | ||
| + | { | ||
| + | Step(); // one simulation step | ||
| + | } | ||
| + | |||
| + | void CollisionDetectionBruteForce() | ||
| + | { | ||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | ParticleModel particleModel1 = particleModels[i]; | ||
| + | for (int j = i + 1; j < particleCount; | ||
| + | { | ||
| + | if (i == j) continue; | ||
| + | ParticleModel particleModel2 = particleModels[j]; | ||
| + | |||
| + | Vector2 delta = particleModel1.position - particleModel2.position; | ||
| + | float distSq = delta.sqrMagnitude; | ||
| + | if (distSq < minDistanceSquared) | ||
| + | { | ||
| + | particleModel1.isColliding = true; | ||
| + | particleModel2.isColliding = true; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // SPATIAL HASING METHODS | ||
| + | int GetGridCellCoord(float x) | ||
| + | { | ||
| + | return Mathf.FloorToInt(x / gridSize); | ||
| + | } | ||
| + | | ||
| + | int CalculateCellHash(int xCell, int yCell) | ||
| + | { | ||
| + | return xCell * 73856093 ^ yCell * 19349663; | ||
| + | } | ||
| + | |||
| + | void CollisionDetectionHashTableDict(){ | ||
| + | // BUILD PARTICLE DICT | ||
| + | // clear dict | ||
| + | particleModelDict.Clear(); | ||
| + | // iterate over all particles, determine hash and add to dict | ||
| + | foreach (var particleModel in particleModels) | ||
| + | { | ||
| + | int xCell = GetGridCellCoord(particleModel.position.x); | ||
| + | int yCell = GetGridCellCoord(particleModel.position.y); | ||
| + | int hash = CalculateCellHash(xCell, | ||
| + | if (!particleModelDict.ContainsKey(hash)) | ||
| + | { | ||
| + | particleModelDict[hash] = new List< | ||
| + | } | ||
| + | particleModelDict[hash].Add(particleModel); | ||
| + | } | ||
| + | |||
| + | // CHECK FOR COLLISIONS | ||
| + | foreach (var particleModel in particleModels) | ||
| + | { | ||
| + | foreach (var neighbour in GetNeighbors(particleModel.position)) | ||
| + | { | ||
| + | if (particleModel == neighbour) continue; | ||
| + | |||
| + | float distanceSquared = (particleModel.position - neighbour.position).sqrMagnitude; | ||
| + | if(distanceSquared < minDistanceSquared) | ||
| + | { | ||
| + | particleModel.isColliding = true; | ||
| + | neighbour.isColliding = true; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // get all particles in same and eight neighboring cells | ||
| + | // two options: same method once as IEnumerable and once returning a list. Same performance. | ||
| + | IEnumerable< | ||
| + | { | ||
| + | int xCell = GetGridCellCoord(position.x); | ||
| + | int yCell = GetGridCellCoord(position.y); | ||
| + | for (int dx = -1; dx <= 1; dx++) | ||
| + | { | ||
| + | int x = xCell + dx; | ||
| + | for (int dy = -1; dy <= 1; dy++) | ||
| + | { | ||
| + | int y = yCell + dy; | ||
| + | int hash = CalculateCellHash(x, | ||
| + | if (!particleModelDict.ContainsKey(hash)) continue; | ||
| + | foreach (var particle in particleModelDict[hash]) | ||
| + | { | ||
| + | yield return particle; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | } | ||
| + | } | ||
| + | |||
| + | List< | ||
| + | { | ||
| + | List< | ||
| + | int xCell = GetGridCellCoord(position.x); | ||
| + | int yCell = GetGridCellCoord(position.y); | ||
| + | for (int dx = -1; dx <= 1; dx++) | ||
| + | { | ||
| + | int x = xCell + dx; | ||
| + | for (int dy = -1; dy <= 1; dy++) | ||
| + | { | ||
| + | int y = yCell + dy; | ||
| + | int hash = CalculateCellHash(x, | ||
| + | if (!particleModelDict.ContainsKey(hash)) continue; | ||
| + | foreach (var particle in particleModelDict[hash]) | ||
| + | { | ||
| + | neigbours.Add(particle); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | } | ||
| + | return neigbours; | ||
| + | } | ||
| + | | ||
| + | void Step() | ||
| + | { | ||
| + | // UPDATE POSITION | ||
| + | float dt = Time.deltaTime; | ||
| + | foreach (var particleModel in particleModels) | ||
| + | { | ||
| + | particleModel.position += particleModel.velocity * dt; | ||
| + | } | ||
| + | |||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | particleModels[i].wasColliding = particleModels[i].isColliding; | ||
| + | particleModels[i].isColliding = false; | ||
| + | } | ||
| + | |||
| + | // COLLISION DETECTION | ||
| + | if (collisionDetectionType == CollisionDetectionType.BruteForce){ | ||
| + | CollisionDetectionBruteForce(); | ||
| + | } | ||
| + | else if (collisionDetectionType == CollisionDetectionType.SpatialHashingDict) | ||
| + | { | ||
| + | CollisionDetectionHashTableDict(); | ||
| + | } | ||
| + | else{ | ||
| + | Debug.Log(" | ||
| + | } | ||
| + | |||
| + | // CHANGE COLOR IF COLLIDE | ||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | if (particleModels[i].isColliding && !particleModels[i].wasColliding) | ||
| + | { | ||
| + | particleRenderers[i].color = colorCollision; | ||
| + | } | ||
| + | else if (!particleModels[i].isColliding && particleModels[i].wasColliding) | ||
| + | { | ||
| + | particleRenderers[i].color = colorNoCollision; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // COLLISION HANDLING: WALLS ONLY | ||
| + | for (int i = 0; i < particleCount; | ||
| + | { | ||
| + | ParticleModel particle = particleModels[i]; | ||
| + | if (particle.position.x < xMin + particleRadius) | ||
| + | { | ||
| + | particle.position.x = xMin + particleRadius; | ||
| + | particle.velocity.x = -particle.velocity.x; | ||
| + | } | ||
| + | else if (particle.position.x > xMax - particleRadius) | ||
| + | { | ||
| + | particle.position.x = xMax - particleRadius; | ||
| + | particle.velocity.x = -particle.velocity.x; | ||
| + | } | ||
| + | if (particle.position.y < yMin + particleRadius) | ||
| + | { | ||
| + | particle.position.y = yMin + particleRadius; | ||
| + | particle.velocity.y = -particle.velocity.y; | ||
| + | } | ||
| + | else if (particle.position.y > yMax - particleRadius) | ||
| + | { | ||
| + | particle.position.y = yMax - particleRadius; | ||
| + | particle.velocity.y = -particle.velocity.y; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ++++ | ||
| + | |||
| + | </ | ||
| + | |||
| + | ==== Auftrag A - Teil III ==== | ||
| + | |||
| + | Formel für elastischen Stoss in 2D: | ||
| + | [[https:// | ||
| + | |||