Skip to content

Commit

Permalink
Merge pull request #4 from downflux/feature_target_velocity
Browse files Browse the repository at this point in the history
Rename Velocity API
  • Loading branch information
minkezhang authored Dec 31, 2022
2 parents dc76e9b + 1244b40 commit 7f9c234
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 221 deletions.
24 changes: 11 additions & 13 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -37,12 +37,12 @@ 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
// 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
Expand All @@ -61,6 +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) 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() }
Expand All @@ -82,17 +83,17 @@ 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)
v := vector.V([]float64{0, 0}).M()
v.Copy(o.TargetVelocity)

a := &A{
position: p,
target: target,
velocity: v,
tv: tv,
radius: o.Radius,
heading: h,

Expand All @@ -118,6 +119,3 @@ 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() }
81 changes: 43 additions & 38 deletions collider/collider.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,19 @@ 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 {
func (c *C) generate(d time.Duration) []result {
results := make([]result, 0, 256)

in := make(chan *agent.A, 256)
Expand All @@ -269,8 +269,9 @@ func (c *C) generate() []result {
defer wg.Done()
for _, a := range c.projectiles {
out <- result{
agent: a,
v: a.Velocity(),
agent: a,
velocity: a.TargetVelocity(),
heading: a.Heading(),
}
}
}(out)
Expand All @@ -280,43 +281,56 @@ 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.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) })
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)
}

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,
v: v.V(),
agent: a,
velocity: v.V(),
heading: h.V(),
}
}
}(in, out)
Expand All @@ -342,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)
Expand All @@ -357,19 +371,9 @@ 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)

// 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())

r.agent.Position().M().Add(vector.Scale(t, r.v))
r.agent.Heading().M().Copy(h)

agent.SetTickVelocity(r.agent, r.v.M())
r.agent.Position().M().Add(vector.Scale(t, r.velocity))
r.agent.Heading().M().Copy(r.heading)
r.agent.Velocity().M().Copy(r.velocity)
}
}(in)
}
Expand All @@ -383,6 +387,7 @@ func (c *C) Tick(d time.Duration) {
}

type result struct {
agent *agent.A
v vector.V
agent *agent.A
velocity vector.V
heading polar.V
}
Loading

0 comments on commit 7f9c234

Please sign in to comment.