From 1abceba4f099cd8f8135db3e91beac2a8fcc440a Mon Sep 17 00:00:00 2001 From: Minke Zhang Date: Sat, 31 Dec 2022 05:16:01 -0800 Subject: [PATCH 1/4] Rename Velocity -> TargetVelocity --- agent/agent.go | 16 +- collider/collider.go | 12 +- collider/collider_test.go | 214 ++++++++++++------------- internal/collider/collider_test.go | 64 ++++---- internal/kinematics/kinematics_test.go | 46 +++--- 5 files changed, 176 insertions(+), 176 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 5f434f9..151f7e1 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -17,10 +17,10 @@ import ( type O struct { Position vector.V - // Velocity is the target velocity of the agent. This is directly + // TargetVelocity is the target velocity of the agent. This is directly // mutable by the end-target, but may not reflect the actual velocity of // the agent. - Velocity vector.V + TargetVelocity vector.V // Radius is a non-negative number representing the size of the agent. Radius float64 @@ -37,7 +37,7 @@ type A struct { id id.ID position vector.M - velocity vector.M + target vector.M radius float64 // tv is the actual tick-to-tick velocity. This is used for smoothing @@ -61,7 +61,7 @@ func (a *A) ID() id.ID { return a.id } func (a *A) Mask() mask.M { return a.mask } func (a *A) Position() vector.V { return a.position.V() } -func (a *A) Velocity() vector.V { return a.velocity.V() } +func (a *A) TargetVelocity() vector.V { return a.target.V() } func (a *A) Radius() float64 { return a.radius } func (a *A) Heading() polar.V { return a.heading.V() } func (a *A) MaxVelocity() float64 { return a.maxVelocity } @@ -82,16 +82,16 @@ func New(o O) *A { p := vector.V([]float64{0, 0}).M() p.Copy(o.Position) - v := vector.V([]float64{0, 0}).M() - v.Copy(o.Velocity) + target := vector.V([]float64{0, 0}).M() + target.Copy(o.TargetVelocity) h := polar.V([]float64{0, 0}).M() h.Copy(polar.Normalize(o.Heading)) tv := vector.V([]float64{0, 0}).M() - tv.Copy(o.Velocity) + tv.Copy(o.TargetVelocity) a := &A{ position: p, - velocity: v, + target: target, tv: tv, radius: o.Radius, heading: h, diff --git a/collider/collider.go b/collider/collider.go index 807ec16..54a1b70 100644 --- a/collider/collider.go +++ b/collider/collider.go @@ -235,16 +235,16 @@ func (c *C) SetPosition(x id.ID, v vector.V) { } } -// SetVelocity directly sets the agent's new velocity vector. This function is -// called when an agent's goal vector is updated. This function may be called +// SetTargetVelocity sets the agent's new target velocity vector. This function +// is called when an agent's goal vector is updated. This function may be called // concurrently. -func (c *C) SetVelocity(x id.ID, v vector.V) { +func (c *C) SetTargetVelocity(x id.ID, v vector.V) { // SetVelocity does not mutate the BVH, but the central Tick function // does need to read the velocity. c.bvhL.RLock() defer c.bvhL.RUnlock() - c.getOrDie(x).Velocity().M().Copy(v) + c.getOrDie(x).TargetVelocity().M().Copy(v) } func (c *C) generate() []result { @@ -270,7 +270,7 @@ func (c *C) generate() []result { for _, a := range c.projectiles { out <- result{ agent: a, - v: a.Velocity(), + v: a.TargetVelocity(), } } }(out) @@ -283,7 +283,7 @@ func (c *C) generate() []result { // TODO(minkezhang): Investigate what // happens if we change this velocity to // the nearest 8-directional alignment. - v.Copy(a.Velocity()) + v.Copy(a.TargetVelocity()) aabb := agent.AABB(a.Position(), a.Radius()) ns := c.query(aabb, func(b *agent.A) bool { return collider.IsSquishableColliding(a, b) }) diff --git a/collider/collider_test.go b/collider/collider_test.go index b7cbc11..ca17faf 100644 --- a/collider/collider_test.go +++ b/collider/collider_test.go @@ -44,11 +44,11 @@ func TestQueryFeatures(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{5, 15}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 5, - Mask: mask.MSizeSmall, + Position: vector.V{5, 15}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 5, + Mask: mask.MSizeSmall, }) f := collider.InsertFeature(feature.O{ Min: vector.V{10, 10}, @@ -65,11 +65,11 @@ func TestQueryFeatures(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{9, 10}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{9, 10}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 1, + Mask: mask.MSizeSmall, }) f := collider.InsertFeature(feature.O{ Min: vector.V{10, 10}, @@ -86,11 +86,11 @@ func TestQueryFeatures(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{5, 5}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 5, - Mask: mask.MSizeSmall, + Position: vector.V{5, 5}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 5, + Mask: mask.MSizeSmall, }) f := collider.InsertFeature(feature.O{ Min: vector.V{10, 10}, @@ -131,11 +131,11 @@ func TestQuery(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 1, + Mask: mask.MSizeSmall, }) return config{ name: "Exclude/Self", @@ -148,26 +148,26 @@ func TestQuery(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 1, + Mask: mask.MSizeSmall, }) b := collider.Insert(agent.O{ - Position: a.Position(), - Velocity: a.Velocity(), - Heading: a.Heading(), - Radius: a.Radius(), - Mask: mask.MSizeSmall, + Position: a.Position(), + TargetVelocity: a.TargetVelocity(), + Heading: a.Heading(), + Radius: a.Radius(), + Mask: mask.MSizeSmall, }) collider.Insert(agent.O{ - Position: a.Position(), - Velocity: a.Velocity(), - Heading: a.Heading(), - Radius: a.Radius(), - Mask: mask.MSizeProjectile, + Position: a.Position(), + TargetVelocity: a.TargetVelocity(), + Heading: a.Heading(), + Radius: a.Radius(), + Mask: mask.MSizeProjectile, }) return config{ name: "Exclude/Projectiles", @@ -180,25 +180,25 @@ func TestQuery(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Radius: 1, - Mask: mask.MSizeMedium, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Radius: 1, + Mask: mask.MSizeMedium, }) b := collider.Insert(agent.O{ - Position: a.Position(), - Velocity: a.Velocity(), - Heading: a.Heading(), - Radius: a.Radius(), - Mask: mask.MSizeLarge, + Position: a.Position(), + TargetVelocity: a.TargetVelocity(), + Heading: a.Heading(), + Radius: a.Radius(), + Mask: mask.MSizeLarge, }) collider.Insert(agent.O{ - Position: a.Position(), - Velocity: a.Velocity(), - Heading: a.Heading(), - Radius: a.Radius(), - Mask: mask.MSizeSmall, + Position: a.Position(), + TargetVelocity: a.TargetVelocity(), + Heading: a.Heading(), + Radius: a.Radius(), + Mask: mask.MSizeSmall, }) return config{ name: "Exclude/Squishable", @@ -234,39 +234,39 @@ func TestTick(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{0, 0}, - Velocity: vector.V{1, 1}, - Heading: polar.V{1, math.Pi / 4}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{0, 0}, + TargetVelocity: vector.V{1, 1}, + Heading: polar.V{1, math.Pi / 4}, + Radius: 1, + Mask: mask.MSizeSmall, }) b := collider.Insert(agent.O{ - Position: vector.V{0, 1}, - Velocity: vector.V{0, -1}, - Heading: polar.V{1, 3 * math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{0, 1}, + TargetVelocity: vector.V{0, -1}, + Heading: polar.V{1, 3 * math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeSmall, }) c := collider.Insert(agent.O{ - Position: vector.V{0, -1}, - Velocity: vector.V{0, 1}, - Heading: polar.V{1, math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{0, -1}, + TargetVelocity: vector.V{0, 1}, + Heading: polar.V{1, math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeSmall, }) d := collider.Insert(agent.O{ - Position: vector.V{1, 0}, - Velocity: vector.V{-1, 0}, - Heading: polar.V{1, math.Pi}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{1, 0}, + TargetVelocity: vector.V{-1, 0}, + Heading: polar.V{1, math.Pi}, + Radius: 1, + Mask: mask.MSizeSmall, }) e := collider.Insert(agent.O{ - Position: vector.V{-1, 0}, - Velocity: vector.V{1, 0}, - Heading: polar.V{1, 2 * math.Pi}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{-1, 0}, + TargetVelocity: vector.V{1, 0}, + Heading: polar.V{1, 2 * math.Pi}, + Radius: 1, + Mask: mask.MSizeSmall, }) return config{ name: "Stuck", @@ -284,12 +284,12 @@ func TestTick(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{1, 1}, - MaxVelocity: math.Sqrt(2), - Heading: polar.V{1, math.Pi / 4}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{1, 1}, + MaxVelocity: math.Sqrt(2), + Heading: polar.V{1, math.Pi / 4}, + Radius: 1, + Mask: mask.MSizeSmall, }) return config{ name: "Trivial", @@ -303,20 +303,20 @@ func TestTick(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{0, 1}, - MaxVelocity: 1, - Heading: polar.V{1, math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{0, 1}, + MaxVelocity: 1, + Heading: polar.V{1, math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeSmall, }) b := collider.Insert(agent.O{ - Position: vector.V{10, 12}, - Velocity: vector.V{0, -1}, - MaxVelocity: 1, - Heading: polar.V{1, 3 * math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 12}, + TargetVelocity: vector.V{0, -1}, + MaxVelocity: 1, + Heading: polar.V{1, 3 * math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeSmall, }) return config{ name: "Collision", @@ -331,20 +331,20 @@ func TestTick(t *testing.T) { func() config { collider := New(DefaultO) a := collider.Insert(agent.O{ - Position: vector.V{10, 10}, - Velocity: vector.V{0, 1}, - MaxVelocity: 1, - Heading: polar.V{1, math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeSmall, + Position: vector.V{10, 10}, + TargetVelocity: vector.V{0, 1}, + MaxVelocity: 1, + Heading: polar.V{1, math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeSmall, }) b := collider.Insert(agent.O{ - Position: vector.V{10, 12}, - Velocity: vector.V{0, -1}, - MaxVelocity: 1, - Heading: polar.V{1, 3 * math.Pi / 2}, - Radius: 1, - Mask: mask.MSizeProjectile, + Position: vector.V{10, 12}, + TargetVelocity: vector.V{0, -1}, + MaxVelocity: 1, + Heading: polar.V{1, 3 * math.Pi / 2}, + Radius: 1, + Mask: mask.MSizeProjectile, }) return config{ name: "Collision/IgnoreProjectile", @@ -360,7 +360,7 @@ func TestTick(t *testing.T) { collider := New(DefaultO) a := collider.Insert(agent.O{ Position: vector.V{60.0040783686527, 80.40391843262739}, - Velocity: vector.V{10, 10}, + TargetVelocity: vector.V{10, 10}, Heading: polar.V{1, 1.550798992821703}, Radius: 10, MaxVelocity: 100, @@ -433,7 +433,7 @@ func BenchmarkTick(b *testing.B) { collider.Insert(agent.O{ Radius: R, Position: rv(min, max), - Velocity: rv(-1, 1), + TargetVelocity: rv(-1, 1), MaxVelocity: 60, MaxAcceleration: 10, MaxAngularVelocity: math.Pi / 4, diff --git a/internal/collider/collider_test.go b/internal/collider/collider_test.go index b986385..caa995a 100644 --- a/internal/collider/collider_test.go +++ b/internal/collider/collider_test.go @@ -20,9 +20,9 @@ func TestIsColliding(t *testing.T) { configs := []config{ func() config { a := agent.New(agent.O{ - Heading: polar.V{1, 0}, - Velocity: vector.V{0, 0}, - Position: vector.V{1, 1}, + Heading: polar.V{1, 0}, + TargetVelocity: vector.V{0, 0}, + Position: vector.V{1, 1}, }) agent.SetID(a, 1) return config{ @@ -34,17 +34,17 @@ func TestIsColliding(t *testing.T) { }(), func() config { a := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, }) b := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Mask: mask.MSizeProjectile, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Mask: mask.MSizeProjectile, }) agent.SetID(a, 1) agent.SetID(b, 2) @@ -57,18 +57,18 @@ func TestIsColliding(t *testing.T) { }(), func() config { a := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, }) b := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Mask: mask.MSizeSmall | mask.MTerrainLand | mask.MTerrainAccessibleLand, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Mask: mask.MSizeSmall | mask.MTerrainLand | mask.MTerrainAccessibleLand, }) agent.SetID(a, 1) agent.SetID(b, 2) @@ -81,18 +81,18 @@ func TestIsColliding(t *testing.T) { }(), func() config { a := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, }) b := agent.New(agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, + Position: vector.V{1, 1}, + Radius: 1, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Mask: mask.MSizeSmall | mask.MTerrainAir | mask.MTerrainAccessibleAir, }) agent.SetID(a, 1) agent.SetID(b, 2) diff --git a/internal/kinematics/kinematics_test.go b/internal/kinematics/kinematics_test.go index 658b544..142526c 100644 --- a/internal/kinematics/kinematics_test.go +++ b/internal/kinematics/kinematics_test.go @@ -63,9 +63,9 @@ func TestClampFeatureCollisionVelocity(t *testing.T) { v.Copy(c.v) a := agent.New(agent.O{ - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Position: c.p, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Position: c.p, }) f := feature.New(feature.O{ Min: vector.V(c.aabb.Min()), @@ -112,15 +112,15 @@ func TestClampCollisionVelocity(t *testing.T) { v.Copy(c.v) a := agent.New(agent.O{ - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Position: c.p, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Position: c.p, }) agent.SetID(a, 1) b := agent.New(agent.O{ - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Position: c.q, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Position: c.q, }) agent.SetID(b, 2) ClampCollisionVelocity(a, b, v) @@ -164,7 +164,7 @@ func TestClampAcceleration(t *testing.T) { Heading: polar.V{1, 0}, Position: vector.V{0, 0}, - Velocity: c.tv, + TargetVelocity: c.tv, MaxAcceleration: c.maxAcceleration, }) @@ -203,9 +203,9 @@ func TestClampVelocity(t *testing.T) { for _, c := range configs { t.Run(c.name, func(t *testing.T) { a := agent.New(agent.O{ - Position: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Velocity: vector.V{0, 0}, + Position: vector.V{0, 0}, + Heading: polar.V{1, 0}, + TargetVelocity: vector.V{0, 0}, MaxVelocity: c.maxVelocity, }) @@ -337,9 +337,9 @@ func TestSetFeatureCollisionVelocity(t *testing.T) { v.Copy(c.v) a := agent.New(agent.O{ - Heading: polar.V{1, 0}, - Velocity: vector.V{0, 0}, - Position: c.p, + Heading: polar.V{1, 0}, + TargetVelocity: vector.V{0, 0}, + Position: c.p, }) for _, aabb := range c.aabbs { f := feature.New(feature.O{ @@ -443,16 +443,16 @@ func TestSetCollisionVelocity(t *testing.T) { v.Copy(c.v) a := agent.New(agent.O{ - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Position: c.p, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Position: c.p, }) agent.SetID(a, 1) for _, q := range c.qs { b := agent.New(agent.O{ - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Position: q, + TargetVelocity: vector.V{0, 0}, + Heading: polar.V{1, 0}, + Position: q, }) agent.SetID(b, 2) SetCollisionVelocity(a, b, v) @@ -505,7 +505,7 @@ func TestClampHeading(t *testing.T) { for _, c := range configs { t.Run(c.name, func(t *testing.T) { a := agent.New(agent.O{ - Velocity: vector.V{0, 0}, + TargetVelocity: vector.V{0, 0}, Position: vector.V{0, 0}, Heading: c.h, MaxAngularVelocity: c.omega, From 5d634c97801f2dd323d3ab1ccb76075a9368ec98 Mon Sep 17 00:00:00 2001 From: Minke Zhang Date: Sat, 31 Dec 2022 05:29:13 -0800 Subject: [PATCH 2/4] Rename TickVelocity to Velocity --- agent/agent.go | 14 +++---- collider/collider.go | 53 ++++++++++++++------------ internal/kinematics/kinematics.go | 3 +- internal/kinematics/kinematics_test.go | 16 +++++--- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 151f7e1..fbe9856 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -40,9 +40,9 @@ type A struct { target vector.M radius float64 - // tv is the actual tick-to-tick velocity. This is used for smoothing + // velocity is the actual tick-to-tick velocity. This is used for smoothing // over acceleration values. - tv vector.M + velocity vector.M // heading is a unit polar vector whose angular component is oriented to // the positive X-axis. The angle is calculated according to normal 2D @@ -62,6 +62,7 @@ func (a *A) ID() id.ID { return a.id } func (a *A) Mask() mask.M { return a.mask } func (a *A) Position() vector.V { return a.position.V() } func (a *A) TargetVelocity() vector.V { return a.target.V() } +func (a *A) Velocity() vector.V { return a.velocity.V() } func (a *A) Radius() float64 { return a.radius } func (a *A) Heading() polar.V { return a.heading.V() } func (a *A) MaxVelocity() float64 { return a.maxVelocity } @@ -86,13 +87,13 @@ func New(o O) *A { target.Copy(o.TargetVelocity) h := polar.V([]float64{0, 0}).M() h.Copy(polar.Normalize(o.Heading)) - tv := vector.V([]float64{0, 0}).M() - tv.Copy(o.TargetVelocity) + velocity := vector.V([]float64{0, 0}).M() + velocity.Copy(o.TargetVelocity) a := &A{ position: p, target: target, - tv: tv, + velocity: velocity, radius: o.Radius, heading: h, @@ -119,5 +120,4 @@ func AABB(p vector.V, r float64) hyperrectangle.R { ) } -func SetTickVelocity(a *A, v vector.M) { a.tv.Copy(v.V()) } -func TickVelocity(a *A) vector.V { return a.tv.V() } +func SetVelocity(a *A, v vector.M) { a.velocity.Copy(v.V()) } diff --git a/collider/collider.go b/collider/collider.go index 54a1b70..ab4c156 100644 --- a/collider/collider.go +++ b/collider/collider.go @@ -269,8 +269,8 @@ func (c *C) generate() []result { defer wg.Done() for _, a := range c.projectiles { out <- result{ - agent: a, - v: a.TargetVelocity(), + agent: a, + velocity: a.TargetVelocity(), } } }(out) @@ -280,43 +280,45 @@ func (c *C) generate() []result { defer wg.Done() for a := range in { v := vector.M{0, 0} - // TODO(minkezhang): Investigate what - // happens if we change this velocity to - // the nearest 8-directional alignment. v.Copy(a.TargetVelocity()) aabb := agent.AABB(a.Position(), a.Radius()) ns := c.query(aabb, func(b *agent.A) bool { return collider.IsSquishableColliding(a, b) }) fs := c.queryFeatures(aabb, func(f *feature.F) bool { return collider.IsCollidingFeature(a, f) }) + // Check for collisions which the agent + // cares about, e.g. care about + // squishability. These functions set + // the input vector v to ensure that the + // normal components of the velocity is + // filtered out for each individual + // entity. However, this method is not + // always reliable, and a multi-body + // collision may flip the velocity back + // into the body of an existing entity. for _, y := range fs { kinematics.SetFeatureCollisionVelocity(a, c.features[y], v) } - - // Check for collisions which the agent - // cares about, e.g. care about - // squishability. for _, y := range ns { kinematics.SetCollisionVelocity(a, c.agents[y], v) } // Second pass ensures agent is not // colliding with any static features. + // or neighbors and forces the velocity + // to zero if it has flip-flopped back + // into the forbidden zone of another + // entity. for _, y := range fs { kinematics.ClampFeatureCollisionVelocity(a, c.features[y], v) } - - // Second pass across neighbors forces - // the velocity to zero if a velocity - // has flip-flopped back into the - // forbidden zone of another agent. for _, y := range ns { kinematics.ClampCollisionVelocity(a, c.agents[y], v) } out <- result{ - agent: a, - v: v.V(), + agent: a, + velocity: v.V(), } } }(in, out) @@ -357,19 +359,20 @@ func (c *C) Tick(d time.Duration) { go func(ch <-chan result) { defer wg.Done() for r := range ch { - kinematics.ClampVelocity(r.agent, r.v.M()) - kinematics.ClampAcceleration(r.agent, r.v.M(), d) + kinematics.ClampVelocity(r.agent, r.velocity.M()) + kinematics.ClampAcceleration(r.agent, r.velocity.M(), d) // N.B.: The velocity can be further reduced to // zero here due to the physical limitations of // the agent. - h := polar.V{1, r.agent.Heading().Theta()} - kinematics.ClampHeading(r.agent, d, r.v.M(), h.M()) + h := polar.M{0, 0} + h.Copy(r.agent.Heading()) + kinematics.ClampHeading(r.agent, d, r.velocity.M(), h) - r.agent.Position().M().Add(vector.Scale(t, r.v)) - r.agent.Heading().M().Copy(h) + r.agent.Position().M().Add(vector.Scale(t, r.velocity)) + r.agent.Heading().M().Copy(h.V()) - agent.SetTickVelocity(r.agent, r.v.M()) + agent.SetVelocity(r.agent, r.velocity.M()) } }(in) } @@ -383,6 +386,6 @@ func (c *C) Tick(d time.Duration) { } type result struct { - agent *agent.A - v vector.V + agent *agent.A + velocity vector.V } diff --git a/internal/kinematics/kinematics.go b/internal/kinematics/kinematics.go index 5d457db..132cd66 100644 --- a/internal/kinematics/kinematics.go +++ b/internal/kinematics/kinematics.go @@ -119,7 +119,7 @@ func ClampVelocity(a *agent.A, v vector.M) { func ClampAcceleration(a *agent.A, v vector.M, d time.Duration) { t := float64(d) / float64(time.Second) - mtv := vector.Magnitude(agent.TickVelocity(a)) + mtv := vector.Magnitude(a.Velocity()) mv := vector.Magnitude(v.V()) // Only clamp the velocity if the agent is speeding up. We want to // prevent collisions at all costs, so the braking acceleration is @@ -144,7 +144,6 @@ func ClampHeading(a *agent.A, d time.Duration, v vector.M, h polar.M) { return } - h.Copy(a.Heading()) p := polar.Polar(v.V()) // We do not need to worry about scaling v by t, as we only care about diff --git a/internal/kinematics/kinematics_test.go b/internal/kinematics/kinematics_test.go index 142526c..a5bc8f3 100644 --- a/internal/kinematics/kinematics_test.go +++ b/internal/kinematics/kinematics_test.go @@ -504,6 +504,11 @@ func TestClampHeading(t *testing.T) { for _, c := range configs { t.Run(c.name, func(t *testing.T) { + h := polar.M{0, 0} + h.Copy(c.h) + v := vector.M{0, 0} + v.Copy(c.v) + a := agent.New(agent.O{ TargetVelocity: vector.V{0, 0}, Position: vector.V{0, 0}, @@ -511,13 +516,12 @@ func TestClampHeading(t *testing.T) { MaxAngularVelocity: c.omega, }) - gotH := polar.M{0, 0} - ClampHeading(a, time.Second, c.v.M(), gotH) - if !vector.Within(c.v, c.wantV) { - t.Errorf("v = %v, want = %v", c.v, c.wantV) + ClampHeading(a, time.Second, v, h) + if !vector.Within(v.V(), c.wantV) { + t.Errorf("v = %v, want = %v", v, c.wantV) } - if !polar.Within(gotH.V(), c.wantH) { - t.Errorf("h = %v, want = %v", gotH, c.wantH) + if !polar.Within(h.V(), c.wantH) { + t.Errorf("h = %v, want = %v", h, c.wantH) } }) } From 7ebafb221ec547e35c62840978e243350a3a1519 Mon Sep 17 00:00:00 2001 From: Minke Zhang Date: Sat, 31 Dec 2022 05:32:24 -0800 Subject: [PATCH 3/4] Minor refactor --- agent/agent.go | 8 +++----- collider/collider.go | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index fbe9856..acadb02 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -87,13 +87,13 @@ func New(o O) *A { target.Copy(o.TargetVelocity) h := polar.V([]float64{0, 0}).M() h.Copy(polar.Normalize(o.Heading)) - velocity := vector.V([]float64{0, 0}).M() - velocity.Copy(o.TargetVelocity) + v := vector.V([]float64{0, 0}).M() + v.Copy(o.TargetVelocity) a := &A{ position: p, target: target, - velocity: velocity, + velocity: v, radius: o.Radius, heading: h, @@ -119,5 +119,3 @@ func AABB(p vector.V, r float64) hyperrectangle.R { }, ) } - -func SetVelocity(a *A, v vector.M) { a.velocity.Copy(v.V()) } diff --git a/collider/collider.go b/collider/collider.go index ab4c156..bf1d0b0 100644 --- a/collider/collider.go +++ b/collider/collider.go @@ -371,8 +371,7 @@ func (c *C) Tick(d time.Duration) { r.agent.Position().M().Add(vector.Scale(t, r.velocity)) r.agent.Heading().M().Copy(h.V()) - - agent.SetVelocity(r.agent, r.velocity.M()) + r.agent.Velocity().M().Copy(r.velocity) } }(in) } From 1244b40f7fa736397427a356de16502e8cfe52c6 Mon Sep 17 00:00:00 2001 From: Minke Zhang Date: Sat, 31 Dec 2022 05:37:55 -0800 Subject: [PATCH 4/4] Move header generation into generate --- collider/collider.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/collider/collider.go b/collider/collider.go index bf1d0b0..f223365 100644 --- a/collider/collider.go +++ b/collider/collider.go @@ -247,7 +247,7 @@ func (c *C) SetTargetVelocity(x id.ID, v vector.V) { c.getOrDie(x).TargetVelocity().M().Copy(v) } -func (c *C) generate() []result { +func (c *C) generate(d time.Duration) []result { results := make([]result, 0, 256) in := make(chan *agent.A, 256) @@ -271,6 +271,7 @@ func (c *C) generate() []result { out <- result{ agent: a, velocity: a.TargetVelocity(), + heading: a.Heading(), } } }(out) @@ -316,9 +317,20 @@ func (c *C) generate() []result { kinematics.ClampCollisionVelocity(a, c.agents[y], v) } + kinematics.ClampVelocity(a, v) + kinematics.ClampAcceleration(a, v, d) + + // N.B.: The velocity can be further reduced to + // zero here due to the physical limitations of + // the agent. + h := polar.M{0, 0} + h.Copy(a.Heading()) + kinematics.ClampHeading(a, d, v, h) + out <- result{ agent: a, velocity: v.V(), + heading: h.V(), } } }(in, out) @@ -344,7 +356,7 @@ func (c *C) Tick(d time.Duration) { in := make(chan result, 256) go func(ch chan<- result) { - for _, r := range c.generate() { + for _, r := range c.generate(d) { ch <- r } close(ch) @@ -359,18 +371,8 @@ func (c *C) Tick(d time.Duration) { go func(ch <-chan result) { defer wg.Done() for r := range ch { - kinematics.ClampVelocity(r.agent, r.velocity.M()) - kinematics.ClampAcceleration(r.agent, r.velocity.M(), d) - - // N.B.: The velocity can be further reduced to - // zero here due to the physical limitations of - // the agent. - h := polar.M{0, 0} - h.Copy(r.agent.Heading()) - kinematics.ClampHeading(r.agent, d, r.velocity.M(), h) - r.agent.Position().M().Add(vector.Scale(t, r.velocity)) - r.agent.Heading().M().Copy(h.V()) + r.agent.Heading().M().Copy(r.heading) r.agent.Velocity().M().Copy(r.velocity) } }(in) @@ -387,4 +389,5 @@ func (c *C) Tick(d time.Duration) { type result struct { agent *agent.A velocity vector.V + heading polar.V }