package main import ( "image/color" "sync" "time" rl "github.com/gen2brain/raylib-go/raylib" ) const MAXPOINTS = 100000 const MAXOBSTACLES = 20 const PI = 3.141592653589793 type quad struct { lu rl.Vector2 ru rl.Vector2 rd rl.Vector2 ld rl.Vector2 } func spawnParticlesAsGrid(particleWidth float32, particleHeight float32, particleColumnCount int, particleRowCount int, particleList *[MAXPOINTS]rl.Vector2) { var particleLeftCornerPos rl.Vector2 = rl.Vector2{200, 100} for y := 0; y < (particleColumnCount); y++ { for x := 0; x < particleRowCount; x++ { newParticle := rl.NewVector2(particleWidth*(float32(x)/float32(particleRowCount))+particleLeftCornerPos.X, particleHeight*(float32(y)/float32(particleColumnCount))+particleLeftCornerPos.Y) particleList[y*particleRowCount+x] = newParticle } } } func createRectangle(centerPoint rl.Vector2, width float32, height float32, rotation float32) quad { rectangle := quad{} rectangle.lu = rl.Vector2Add( centerPoint, rl.Vector2Rotate(rl.Vector2{-0.5 * width, -0.5 * height}, rotation)) rectangle.ru = rl.Vector2Add( centerPoint, rl.Vector2Rotate(rl.Vector2{0.5 * width, -0.5 * height}, rotation)) rectangle.rd = rl.Vector2Add( centerPoint, rl.Vector2Rotate(rl.Vector2{0.5 * width, 0.5 * height}, rotation)) rectangle.ld = rl.Vector2Add( centerPoint, rl.Vector2Rotate(rl.Vector2{-0.5 * width, 0.5 * height}, rotation)) return rectangle } func drawRec(rectangle *quad) { rl.DrawLineV((*rectangle).lu, (*rectangle).ru, rl.Black) rl.DrawLineV((*rectangle).ru, (*rectangle).rd, rl.Black) rl.DrawLineV((*rectangle).rd, (*rectangle).ld, rl.Black) rl.DrawLineV((*rectangle).ld, (*rectangle).lu, rl.Black) } func obstacleReflect(particlePos rl.Vector2, particleSpeed *rl.Vector2, obstacles *([MAXOBSTACLES](*quad)), particleRadius *float32, obstacleCount *int) { var friction float32 = 0.99 for k := 0; k < *obstacleCount; k++ { if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).lu, (*((*obstacles)[k])).ru) { var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).ru, (*((*obstacles)[k])).lu) var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2)) *particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction) } else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).ru, (*((*obstacles)[k])).rd) { var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).rd, (*((*obstacles)[k])).ru) var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2)) *particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction) } else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).rd, (*((*obstacles)[k])).ld) { var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).ld, (*((*obstacles)[k])).rd) var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2)) *particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction) } else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).ld, (*((*obstacles)[k])).lu) { var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).lu, (*((*obstacles)[k])).ld) var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2)) *particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction) } } } func physics(particleList *[MAXPOINTS]rl.Vector2, particleCount int, particleListKey *sync.Mutex, obstacles *([MAXOBSTACLES](*quad)), particleRadius *float32, obstacleCount *int, particleColor *[MAXPOINTS]rl.Color) { var particleSpeed = [MAXPOINTS]rl.Vector2{{0, -300}} //pixel pro Sekunde var timeIncrement float64 = 0.03 var gravity rl.Vector2 = rl.Vector2{0, 0 * 300} var newParticleList [MAXPOINTS]rl.Vector2 var newParticleSpeed [MAXPOINTS]rl.Vector2 var particleFriction float32 = 0.65 //0.65 var particleLadung int = -1 var masse float32 = 0.1 var electrostaticRange float32 = 50 //range wiie weit die kraftwirkung ist var electroStrength float32 = 4000 //4000 var hue = [MAXPOINTS]float32{} var speedcolor float32 = 30 for j := float64(0); j < 50; { var startTime time.Time = time.Now() // Snapshot der aktuellen Positionen für den parallelen Block (read-only) particleListKey.Lock() currentPositions := *particleList particleListKey.Unlock() var wg sync.WaitGroup for k := 0; k < particleCount; k++ { wg.Add(1) go func(i int) { defer wg.Done() var electroForce rl.Vector2 // Jede Goroutine arbeitet nur auf Index i → kein Datenkonflikt for l := 0; l < particleCount; l++ { if l == i { continue } diff := rl.Vector2Subtract(currentPositions[i], currentPositions[l]) dist := rl.Vector2Length(diff) minDist := *particleRadius * 2 if dist < minDist && dist > 0 { colNormal := rl.Vector2Scale(diff, 1/dist) //durch länge des Vektoren teilen // Exakt die Hälfte der Überlappung korrigieren overlap := minDist - dist currentPositions[i] = rl.Vector2Add(currentPositions[i], rl.Vector2Scale(colNormal, overlap*0.5)) particleSpeed[i] = rl.Vector2Add(rl.Vector2Scale(rl.Vector2Reflect(particleSpeed[i], colNormal), particleFriction), rl.Vector2Scale(colNormal, 12)) } if dist < electrostaticRange { var tempForce rl.Vector2 = rl.Vector2Scale(diff, -float32(particleLadung)*(electroStrength/(dist*dist*dist))) electroForce = rl.Vector2Add(electroForce, tempForce) } newParticleSpeed[i] = rl.Vector2Add( particleSpeed[i], rl.Vector2Scale(electroForce, float32(timeIncrement)/masse)) } //hue[i]++ //(*particleColor)[i] = rl.ColorFromHSV(hue[i], 1.0, 1.0) hue[i] = rl.Clamp(speedcolor*360/(rl.Vector2Length(newParticleSpeed[i])+1), 1, 360) (*particleColor)[i] = rl.ColorFromHSV(hue[i], 1.0, 1.0) obstacleReflect(currentPositions[i], &particleSpeed[i], obstacles, particleRadius, obstacleCount) newParticleList[i] = rl.Vector2Add( currentPositions[i], rl.Vector2Scale(particleSpeed[i], float32(timeIncrement))) newParticleSpeed[i] = rl.Vector2Add( rl.Vector2Add(particleSpeed[i], rl.Vector2Scale(gravity, float32(timeIncrement))), rl.Vector2Scale(electroForce, float32(timeIncrement)/masse)) }(k) } wg.Wait() // Ergebnisse zurückschreiben — nur dieser Block muss gelockt sein particleListKey.Lock() *particleList = newParticleList particleListKey.Unlock() //fmt.Println(particleList) particleSpeed = newParticleSpeed j += timeIncrement var endTime time.Time = time.Now() var elapsed time.Duration = endTime.Sub(startTime) var waitTime float64 = timeIncrement - elapsed.Seconds() rl.WaitTime(waitTime) println((waitTime / timeIncrement)) //rl.ClearBackground(rl.RayWhite) } } func main() { rl.InitWindow(1200, 800, "raylib [core] example - basic window") defer rl.CloseWindow() rl.SetTargetFPS(120) rl.BeginDrawing() rl.WaitTime(1) rl.ClearBackground(rl.RayWhite) rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) rl.EndDrawing() var particleWidth float32 = 700 var particleHeight float32 = 500 var particleColumnCount int = 40 var particleRowCount int = 40 var particleCount int = particleRowCount * particleColumnCount var particleRadius float32 = 5 var particleColorList [MAXPOINTS]rl.Color particleColor := &particleColorList var particleListKey sync.Mutex var obstacleCount int = 0 //var stepSize float32 = 10 var particleList = [MAXPOINTS]rl.Vector2{} spawnParticlesAsGrid(particleWidth, particleHeight, particleColumnCount, particleRowCount, &particleList) var floor quad = createRectangle(rl.Vector2{600, 700}, 1200, 100, 0*PI/8) var obstacles = [MAXOBSTACLES]*quad{} obstacles[0] = &floor obstacleCount++ var hillLeft quad = createRectangle(rl.Vector2{0, 500}, 1200, 100, PI/2) obstacles[1] = &hillLeft obstacleCount++ var hillRight quad = createRectangle(rl.Vector2{1200, 500}, 1200, 100, -PI/2) obstacles[2] = &hillRight obstacleCount++ var ceiling quad = createRectangle(rl.Vector2{600, 0}, 1200, 100, 0) obstacles[3] = &ceiling obstacleCount++ go physics(&particleList, particleCount, &particleListKey, &obstacles, &particleRadius, &obstacleCount, particleColor) for !rl.WindowShouldClose() { rl.ClearBackground(color.RGBA{15, 16, 23, 1}) particleListKey.Lock() rl.BeginDrawing() for i := 0; i < particleCount; i++ { rl.DrawCircleV(particleList[i], particleRadius, (*particleColor)[i]) } particleListKey.Unlock() drawRec(&floor) drawRec(&hillLeft) drawRec(&hillRight) drawRec(&ceiling) rl.EndDrawing() } }