diff --git a/game/game.go b/game/game.go index f289569..7a5cb0b 100644 --- a/game/game.go +++ b/game/game.go @@ -17,7 +17,7 @@ import ( const ( GameWidth = 640 GameHeight = 360 - GameParticleCount = 1000 + GameParticleCount = 2000 GameGravity = 2 GameParticleRadius = 5 GameDamping = .7 @@ -36,6 +36,8 @@ type Game struct { paused bool renderquads bool resolvecollisions bool + resolvers []func(particle *elements.Particle) + resolveridx int } func NewGame() *Game { @@ -44,6 +46,7 @@ func NewGame() *Game { paused: false, renderquads: false, resolvecollisions: false, + resolveridx: 0, } g.particlebox = &gamedata.Vector{ @@ -70,6 +73,9 @@ func NewGame() *Game { }, } + g.resolvers = append(g.resolvers, g.ResolveCollisionsA) + g.resolvers = append(g.resolvers, g.ResolveCollisionsB) + //g.InitializeColliders() g.InitializeParticles() @@ -113,8 +119,12 @@ func (g *Game) RenderParticles(img *ebiten.Image) { x0 := particle.Position.X - GameParticleRadius y0 := particle.Position.Y - GameParticleRadius + //redness := float32(particle.Position.Y / g.particlebox.Y) + //blueness := 1 - redness + op := &ebiten.DrawImageOptions{} op.GeoM.Translate(x0, y0) + //op.ColorScale.Scale(redness, 0, blueness, 1) img.DrawImage(g.particlebuff, op) //vector.FillCircle(img, float32(particle.Position.X), float32(particle.Position.Y), float32(particle.Radius), color.White, true) @@ -178,50 +188,47 @@ func (g *Game) UpdateParticles() { dt := GameDeltaTimeStep - /* - mx, my := ebiten.CursorPosition() - mpos := gamedata.Vector{X: float64(mx), Y: float64(my)} - maxdeflect := 40 * GameDeltaTimeStep * GameGravity - */ + mx, my := ebiten.CursorPosition() + mpos := gamedata.Vector{X: float64(mx), Y: float64(my)} + maxdeflect := 40 * GameDeltaTimeStep * GameGravity for _, particle := range g.particles { particle.Velocity.Y += GameGravity * dt //if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { - /* - if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { - delta := gamedata.Vector{ - X: mpos.X - particle.Position.X, - Y: mpos.Y - particle.Position.Y, - } - - dist := math.Sqrt(delta.X*delta.X + delta.Y*delta.Y) - theta := math.Atan2(delta.Y, delta.X) - - if dist < GameInfluenceRadius { - - dx := dist * math.Cos(theta) - dy := dist * math.Sin(theta) - - if dx != 0 { - gainx := (-1./GameInfluenceRadius)*math.Abs(dx) + 1. - particle.Velocity.X += 20 * gainx * -1 * math.Copysign(1, dx) - } - - if dy != 0 { - gainy := (-1./GameInfluenceRadius)*math.Abs(dy) + 1. - particle.Velocity.Y += maxdeflect * gainy * -1 * math.Copysign(1, dy) - } - } + if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { + delta := gamedata.Vector{ + X: mpos.X - particle.Position.X, + Y: mpos.Y - particle.Position.Y, } - */ + + dist := math.Sqrt(delta.X*delta.X + delta.Y*delta.Y) + theta := math.Atan2(delta.Y, delta.X) + + if dist < GameInfluenceRadius { + + dx := dist * math.Cos(theta) + dy := dist * math.Sin(theta) + + if dx != 0 { + gainx := (-1./GameInfluenceRadius)*math.Abs(dx) + 1. + particle.Velocity.X += 20 * gainx * -1 * math.Copysign(1, dx) + } + + if dy != 0 { + gainy := (-1./GameInfluenceRadius)*math.Abs(dy) + 1. + particle.Velocity.Y += maxdeflect * gainy * -1 * math.Copysign(1, dy) + } + } + + } particle.Position.X += particle.Velocity.X * dt particle.Position.Y += particle.Velocity.Y * dt if g.resolvecollisions { - g.ResolveCollisions(particle) + g.resolvers[g.resolveridx](particle) } g.BoundParticle(particle) @@ -288,6 +295,17 @@ func (g *Game) ParseInputs() { g.resolvecollisions = !g.resolvecollisions } + if inpututil.IsKeyJustPressed(ebiten.KeyLeft) { + g.resolveridx = g.resolveridx - 1 + if g.resolveridx < 0 { + g.resolveridx = len(g.resolvers) - 1 + } + } + + if inpututil.IsKeyJustPressed(ebiten.KeyRight) { + g.resolveridx = (g.resolveridx + 1) % len(g.resolvers) + } + } func (g *Game) RebuildQuadtree() { @@ -300,7 +318,7 @@ func (g *Game) RebuildQuadtree() { } } -func (g *Game) ResolveCollisions(particle *elements.Particle) { +func (g *Game) ResolveCollisionsA(particle *elements.Particle) { //construct search quadrant from current particle quadrant := quadtree.Quadrant{ Position: particle.Position, @@ -349,3 +367,44 @@ func (g *Game) ResolveCollisions(particle *elements.Particle) { } } } + +func (g *Game) ResolveCollisionsB(particle *elements.Particle) { + //construct search quadrant from current particle + quadrant := quadtree.Quadrant{ + Position: particle.Position, + Dimensions: particle.GetDimensions(), + } + + //find list of possible maybe collisions, we inspect those in more detail + maybes := g.quadtree.FindAll(quadrant) + + sqdist := float64(GameParticleRadius*GameParticleRadius) * 4 + + for _, p := range maybes { + if p == particle { + continue + } + + pos := p.GetPosition() + delta := gamedata.Vector{ + X: pos.X - particle.Position.X, + Y: pos.Y - particle.Position.Y, + } + + dist2 := delta.X*delta.X + delta.Y*delta.Y + + if dist2 < sqdist { + d := math.Sqrt(dist2) + overlap := GameParticleRadius*2 - d + theta := math.Atan2(delta.Y, delta.X) + pos.X += overlap * math.Cos(theta) + pos.Y += overlap * math.Sin(theta) + p.SetPosition(pos) + + m := p.(*elements.Particle) + m.Velocity.X *= -1 * GameDamping + m.Velocity.Y *= -1 * GameDamping + + } + } +} diff --git a/quadtree/quadtree.go b/quadtree/quadtree.go index c7e5d6f..4587326 100644 --- a/quadtree/quadtree.go +++ b/quadtree/quadtree.go @@ -6,7 +6,7 @@ import ( ) const ( - QuadtreeMaxColliders = 10 + QuadtreeMaxColliders = 40 ) type Quadrant struct { @@ -31,25 +31,27 @@ func (q *Quadtree) Insert(obj colliders.Collider) bool { var result bool = false //check that object meets containment criteria - if q.ContainsPoint(obj.GetPosition()) { - - //if we have children, we go deeper - if len(q.children) > 0 { - for _, node := range q.children { - if node.Insert(obj) { - result = true - break - } - } - } else if len(q.colliders) < QuadtreeMaxColliders { - //otherwise, if we have space, add - q.colliders = append(q.colliders, obj) - result = true - } else { - //otherwise we have to subdivide, reorganize, and add - result = q.SubdivideAndInsert(obj) - } + if !q.ContainsPoint(obj.GetPosition()) { + return result } + + //if we have children, we go deeper + if len(q.children) > 0 { + for _, node := range q.children { + if node.Insert(obj) { + result = true + break + } + } + } else if len(q.colliders) < QuadtreeMaxColliders { + //otherwise, if we have space, add + q.colliders = append(q.colliders, obj) + result = true + } else { + //otherwise we have to subdivide, reorganize, and add + result = q.SubdivideAndInsert(obj) + } + return result } @@ -67,26 +69,27 @@ func (q *Quadtree) ContainsPoint(p gamedata.Vector) bool { func (q *Quadtree) Remove(obj colliders.Collider) bool { var result bool = false - if q.ContainsPoint(obj.GetPosition()) { + if !q.ContainsPoint(obj.GetPosition()) { + return result + } - if len(q.colliders) > 0 { - //examine existing colliders - var collection []colliders.Collider - for _, node := range q.colliders { - if node == obj { - result = true - } else { - collection = append(collection, node) - } + if len(q.colliders) > 0 { + //examine existing colliders + var collection []colliders.Collider + for _, node := range q.colliders { + if node == obj { + result = true + } else { + collection = append(collection, node) } - q.colliders = collection - } else { - //need to check children - for _, child := range q.children { - if child.Remove(obj) { - result = true - break - } + } + q.colliders = collection + } else { + //need to check children + for _, child := range q.children { + if child.Remove(obj) { + result = true + break } } }