

Making Balls Bounce off of Walls in a Labyrinth - lettergram
http://ssodelta.wordpress.com/2014/06/17/balls-bounce-labyrinth/

======
sillysaurus3
Don't represent a ball's velocity with an angle and speed. Use a 2D vector
instead.

To bounce left/right:

    
    
      vel.x = -vel.x
    

To bounce up/down:

    
    
      vel.y = -vel.y
    

This representation also generalizes to 3D. You'll have a lot of trouble
trying to represent a 3D heading with angles, whereas using a 3D velocity
vector is straightforward. (In 3D space, using angles to represent orientation
can also suffer from gimbal lock whenever you try to rotate the orientation.)

~~~
javajosh
This is true, but only for this special case where walls are strictly
horizontal or verticle. In the more general case an angle, magnitude
representation would be fine.

~~~
jerguismi
Wouldn't it make more sense to model also the walls as vectors? Then you
mostly need vector-to-vector intersection etc, fairly standard mathematical
stuff.

If the maze is not standard but can be anything, the walls can be stored in a
quadtree or some similar efficient structure.

~~~
javajosh
_> Wouldn't it make more sense to model also the walls as vectors?_

Yes. That's why r,theta representation makes more sense computationally
because reflection across a normal is just adding and subtracting two angles.
Of course, you pay heavily for using _only_ this representation on every
tick() during merely linear motion, because in the end our displays are
cartesian, and you'll have to convert r,theta to x,y. The solution, of course,
is to maintain both representations and use whichever one is faster - a
classic space-for-time trade-off.

~~~
sillysaurus3
Not really. That kind of code complexity is simply not worth the tradeoff.
You'll waste a bunch of time fixing bugs in your dual representation codebase.
It's always better to do the simplest thing first, and then optimize if and
only if it turns out to be necessary. In this case, the simplest and most
effective way is to use strictly vector representation, because it will result
in the lowest complexity codebase.

Modern computers are monstrously fast. So fast that it's hard to imagine just
how fast they are. The more time you spend thinking about optimizations before
they're necessary, the more time you're not spending on implementing features.

If it's just for fun, though, go for it. A learning exercise is always nice.

------
chipsy
Some annotations of my own, all coming from memory and experience.

The theoretical basis for most collision algorithms is the separating axis
theorem(SAT). It simply states that for any two convex shapes there exists an
axis partitioning them - and as a consequence, deriving this axis(the
algorithm of which will depend on what kinds of shapes you're working with)
gives you the collision boundary of the shapes.

What SAT does not do is specify how to resolve shapes that start in an
overlapping position, how to resolve turning more than one axis of movement
into a _single_ axis of separation, and how to resolve overlaps of more than
one shape. Those things are all where a lot of collision code goes astray.
Some solutions to these problems include:

Maintain either a "lookahead" of where shapes will be in the future based on
their physics parameters, or a "timeline" of previous shape positions; then
the collision resolution can incorporate this information to determine
direction of travel prior to the moment of collision(and thus accurately
approximate pushout, bounce, etc.)

Perform collision resolution in a buffered fashion: before changing position
state, create "plans" for movable objects. Test the plans in multiple passes
to ensure that they satisfy all the potentially colliding shapes.

Step along the axes of movement in independent collision steps; e.g. first you
test to go up, then you test to go up and right, finally you move. Stacking
each axis on top of the previous in a fixed order ensures that at no point is
the colliding object "squashing" one of the tests when another passes(which
causes typical stuck-in-walls glitches). It causes a distortion to appear when
travel is diagonal, but its impact depends on the speed of movement and
whether "cutting corners" matters.

Alternately, one may test each ordering and choose a "best" one heuristically
so that diagonal movement is consistent in all cases.

------
lacker
I think there is a simpler way to represent this. Represent each wall as a
pair of tiles. Whenever a ball would move from tile A to tile B in a single
time step, check if there is a wall whose pair of tiles is A and B. If there
is such a wall, the ball bounces. If not, the ball doesn't bounce. I don't
think you should need to write any code that handles north/south/east/west
with a switch statement, it should all be vector math.

I think the only edge case here would be a ball that hits a corner dead on.
You would need code that says "if a ball goes from A to B in a time step, and
those are diagonal, treat it as A -> C -> B where C is one of the tiles
adjacent to both A and B, for bounce checking."

~~~
w0utert
This is exactly how I implemented almost the exact same thing in the iOS clone
of a classic DOS game (Snipes!) I made (shameless plug:
[https://itunes.apple.com/nl/app/snipes!/id428845764?mt=8](https://itunes.apple.com/nl/app/snipes!/id428845764?mt=8))

The linked article is pretty thorough, but a little complex for the nature of
the problem I think. What I did was just calculate all tiles (I call them
'cells') intersected by the displacement vector of the projectiles (balls)
moving around the maze, which can be done in constant time if your maze
dimensions are fixed. Then I look up the walls of intersected cells, and
calculate the closest intersection of the displacement vector with any of
these walls. This intersection happens somewhere along the vector between t=0
and t=1, so I translate the projectile position back in time using this value,
change its direction (invert either x or y based on the orientation of the
wall), then move it along (1 - t) * v_inverted to solve the 'fast projectiles
skipping walls' problem (this is basically a very simple case of continuous
collision detection).

If you allow a tiny bit of non-determinism in very rare cases, the edge case
where a ball exactly hits an corner doesn't need a lot of additional work, you
just repeat the intersection test iteratively until there are no more
intersecting edges (99 out of 100 cases there will be only 1, and in the
remaining case there will be 2, provided that your balls don't move faster
than 1 unit cell distance per frame).

------
joshu
This seems like an intricate implementation for a very specific case. What if
you wanted to have moving walls or non-rectilinear walls, etc? There are more
appropriate data structures for storing a set of lines and efficiently seeing
if another line (eg the dot's trajectory) has intersected one of them.

~~~
sillysaurus3
If your walls and balls are both moving, you'll need to do collision detection
by making the ball into a capsule (integrate its bounding sphere's position
over current timestep) and make the wall into a quadrilateral (integrate
wall's position over current timestep). If the ball's path intersects the
quadrilateral, its velocity needs to be reflected at the intersection point.
When your collision is dynamic like that, your performance isn't going to be
O(1), which is what the poster was trying to achieve. You can make it
sublinear, but when there are fewer than ~100 objects the speed gain isn't
really worth the complexity. 100x100 is still only 10,000 ops, which can
probably be done in javascript without performance problems. It's definitely a
fun learning exercise though, and it will give you some valuable insight into
how physics libraries work.

~~~
joshu
i think the usual way is to advance the timestep, see if the item penetrates
the other item, and then go back and advance half a timestep.

but my point is, you don't need to check the entire world if you are using a
quadtree or rtree or whatever.

~~~
sillysaurus3
I don't think that's the usual way, because it will completely miss collisions
for fast-moving balls.

They don't need a quadtree or an rtree if the number of objects is fewer than
100.

Also, when using a quadtree/rtree etc with dynamic objects, you'll need to
rebalance the tree as objects move around. If the rebalancing is more
expensive than the speed gained by using it, then those datastructures can be
harmful, not helpful. This is case is relatively rare but something to keep in
mind.

------
userbinator
I would've realised that the 16 different wall combinations would've fit well
into a 4-bit encoding - e.g. 1 for left, 2 for right, 4 for up, 8 for down.
That gets rid of the sets and reduces the check for wall existence to a simple
AND.

If you have a "wall" only in one tile and not its adjacent one, that
essentially creates a one-way wall, which could be an interesting effect too.

------
frownie
Given enough time, can a ball bounce out of the labyrinth ? Obvously, not
always. So, in which condition can this happen ?

------
joshvm
There are still some bugs in the final output - I saw a ball bouncing between
two tiles a few times.

