Package: com.multiversesocial.hyperview
Class: com.multiversesocial.hyperview.CollisionManager
Source: src/com/multiversesocial/hyperview/CollisionManager.java
The collision manager behind SplineConstants.COLLIDE_AT_END and the Gob flag GOB_COLLISION_OUTLINE. HyperView.collisionPass() calls collide(view) once per display frame, right after the loop that steps and draws the Gobs, on the display thread. collide() looks at the view’s Gob list gob[0 .. totalGobs) only, which is the list of the current Dispatch frame (Dispatch.begin() swaps that list); Gobs of a backed-up frame never collide with the Gobs of a nested one.
The tests are made cheapest first: bounding box reject, then pointInPoly() of each vertex of A’s outline against B’s polygon and of B’s against A’s. pointInPoly() is a small static even-odd ray crossing test using only integer arithmetic, with no allocation per call. The outline is set with Gob.setOutline(), or traced from the GobThe outline is set with Gob.setOutline(); without one it is the Gob’s bounding box (see Gob).rsquo;s image when it has the flag GOB_COLLISION_OUTLINE; without either it is the GobThe outline is set with Gob.setOutline(); without one it is the Gob’s bounding box (see Gob).rsquo;s bounding box (see Gob).
GOB_ON_DISPLAY, not GOB_ONGOB and not GOB_DESTROY and which either follows a Spline with COLLIDE_AT_END (collidesAtEnd(); a one-point Spline gives a stationary target) or has the Gob flag GOB_COLLISION_OUTLINE (gobFlags2 0x800; no Spline needed). A Gob with neither is not tested at all. The test is made every frame (while the Gob travels and after it has arrived at the end of its path, where it stays).COLLIDE_AT_END says “stay on the end point, stay attached, be tested”; the Gob flag GOB_COLLISION_OUTLINE says “compute and use an outline from my image, and be tested”. They are independent and can be combined: COLLIDE_AT_END alone uses a hand set outline or the bounding box; the flag alone works on a stationary or moving Gob; both together give a moving Gob with an image outline.updateOutline()): 1. a polygon set with Gob.setOutline() (wins, also over the flag), 2. the polygon traced from the image by OutlineTracer (flag only; computed once, at the first pass which finds the image loaded — until then that Gob is skipped, so it can not collide with a wrong shape; nothing at all is computed for Gobs without the flag), 3. the bounding box x,y .. x+width-1,y+height-1 (default; also for a fully transparent image).GOB_COLLIDED (cleared again in the first frame without contact) and collisionX/Y. A new contact (not touching in the previous pass) also increments collisionCount of both, sets collidedWith of both, increments collisionNum and adds one CollisionAnimGob at the contact point (not if maxFx are showing already or the Gob list is nearly full). Touching for many frames is one contact. Nothing is moved, stopped or removed.CollisionAnimGob is skipped by class (and has no Spline), so impact animations never collide.collisionTick, contactGob[], contactTick[]) is kept in the Gobs, not here, so it follows them when a Dispatch frame is backed up and restored.run() only waits. They remain because HyperView registers the object’s RunInfo.contactNormal); exact for convex outlines, the hull overlap for traced concave ones. Closing speed vn = (vA - vB) . n; no impulse when vn <= 0.RESPONSE_STOP bodies halt and absorb momentum (it is not conserved there). Frictionless.separationSlop, shared by inverse mass; only bodies the pass may move are shifted.damageScale * Gob.damageFactor hit points if energy >= damageThreshold. Hit points <= 0: explode() (Gob.onExplode hook, else a CollisionAnimGob sized to the Gob and removal; explosionMillis).walls = WALLS_BOUNCE bounces physical Gobs the pass moves off the edges of the view, or of the rectangle wallLeft/wallTop/wallRight/wallBottom.public class CollisionManager implements Runnable, HyperConstants, ThreadReport
| Declaration | Value / initializer | Description | Source |
|---|---|---|---|
int candN | Number of entries in cand. | ||
Gob[] cand | = new Gob[64] | Taking part Gobs of the current pass, reused and cleared at the end of each pass. | |
double[] cen | = new double[4] | No comment in source. | |
volatile static Thread collisionManagerTask | The older thread design; nothing starts it. | ||
int collisionNum | = 0 | Contacts detected by this manager (all passes). | |
HyperView curView | The view of the last pass. | ||
public static double damageScale | = 1.0 | Damage = impact energy * damageScale * Gob.damageFactor (energy = 0.5 * reduced mass * closing speed^2). | |
public static double damageThreshold | = 0.0 | Impacts with less energy than this do no damage (resting contact, gentle touches). | |
public static boolean edgeTest | = true | Test edges for crossings when no vertex is inside the other polygon (plus-sign case). Applies to every view. | |
public static volatile int explosionCount | = 0 | Explosions made by the physics (all views, this JVM). | |
public static int explosionMillis | = 700 | Length of the default explosion (CollisionAnimGob) of a Gob whose hit points ran out, milliseconds. | |
public static int fxMillis | = CollisionAnimGob.DEFAULT_MILLIS | Length of the CollisionAnimGob in milliseconds (600). | |
int hitX | Contact point found by touch() (hitX, hitY). | ||
int hitY | y of the contact point found by touch() (see hitX). | ||
public static volatile int impactCount | = 0 | Physical impacts resolved (all views, this JVM). | |
int iteration | = 0 | Collision passes made (also reported through getRunInfo()). | |
public int lastBoxHits | Pairs which survived the bounding box reject in the last pass (point-in-poly was run on them). | ||
public int lastCandidates | Gobs which took part in the last pass. | ||
public int lastNewContacts | New contacts in the last pass. | ||
long lastRun | = 0 | Time of the previous pass (milliseconds); reported by getRunInfo(). | |
long lastStepMs | = 0L | length of the current pass's physics step in frames | |
public int lastTouching | Gobs touching something in the last pass. | ||
public static int maxFx | = 64 | Do not add another CollisionAnimGob while this many are showing in the view. | |
public static double maxStepFrames | = 4.0 | Longest step in frames when realTimeStep is on. | |
long now | = 0 | Time of the current pass in milliseconds (set by collide()). | |
double[] nrm | = new double[2] | No comment in source. | |
public static boolean realTimeStep | = false | false (default): one physics step per collision pass (velocity = pixels per display frame, deterministic). true: the step length is the real time since the last pass in units of 1/60 s (clamped to maxStepFrames). | |
RunInfo runInfo | = new RunInfo((Object)this) | Thread report; part of the older thread design. | |
int runLag | = 0 | RunInfo value of the older thread design; not changed by collide(). | |
public static double separationSlop | = 1.0 | Extra pixels two bodies are pushed apart beyond their overlap, so the integer outlines stop touching. | |
long startTime | = 0 | RunInfo value of the older thread design; not changed by collide(). | |
double stepFrames | = 1.0 | length of the current pass's physics step in frames | |
int totalGobs | No comment in source; not used by collide(). | ||
int waitTime | = 200 | Wait time of the older thread loop in milliseconds. | |
public static int wallBottom | = 0 | No comment in source. | |
public static int wallLeft | = 0 | The wall rectangle of WALLS_BOUNCE in view pixels: left, top, and right / bottom (exclusive). right or bottom <= 0: the view's width / height. | |
public static int wallRight | = 0 | No comment in source. | |
public static final int WALLS_BOUNCE | = 1 | Edges of the view: physical Gobs bounce off them (RESPONSE_STOP Gobs stop). | |
public static final int WALLS_NONE | = 0 | Edges of the view: Gobs leave it (the default, as before). | |
public static int walls | = WALLS_NONE | What happens at the edges of the view for physical Gobs that the pass moves. | |
public static int wallTop | = 0 | No comment in source. |
| Signature | Description | Source |
|---|---|---|
CollisionManager() | Package-private; empty. | |
CollisionManager(HyperView tView) | Package-private; sets curView. HyperView.collisionPass() creates one on first use. |
| Signature | Description | Source |
|---|---|---|
static void applyVelocity(Gob g, double nvx, double nvy, boolean stop) | set a physical Gob's velocity after an impact; a Spline driven one is now owned by the physics | |
static void centroid(Gob g, double[] out) | the area centroid of the world outline of g (vertex average for a degenerate polygon) | |
public int collide(HyperView v) | One collision pass over the Gob list of the current Dispatch frame. Ages the CollisionAnimGobs (and removes finished ones), collects the taking part Gobs, clears their GOB_COLLIDED, rebuilds their world outlines, then tests every pair (bounding box, then touch()), flags, counts and spawns the CollisionAnimGob for new contacts. Returns the number of new contacts. | |
double contactNormal(Gob a, Gob b, double[] out) | The contact normal of two overlapping outlines: the separating axis of least overlap (SAT) over the edge normals of both polygons, pointing from a to b, written to out[0..1]. Returns the overlap (penetration depth) along it, >= 0. For convex outlines this is the exact minimum translation; for concave ones (traced art) the overlap of the convex hulls on the same axes (an upper bound of the true penetration). Falls back to the centroid direction when an outline has no area. | |
boolean contact(Gob a, Gob b) | Is this a NEW contact (did they not touch in the previous pass)? Always records that they touch now, in both Gobs’ contact lists. | |
public static void explode(HyperView v, Gob g, Gob from) | Explode g now: Gob.exploded is set, Gob.onExplode(from) is asked; unless it handled the explosion, a CollisionAnimGob the size of the Gob plays at its centre and the Gob is removed (Gob.remGob). Selection is cleared if g was selected. | |
static int free(Gob g) | A free (or reusable: destroyed or stale partner) slot in g’s contact list; the list grows by doubling. | |
public RunInfo getRunInfo() | Fills the RunInfo from now, runLag, lastRun, startTime, waitTime, iteration. Implements ThreadReport.getRunInfo(). | |
public void initialize() | Older thread design: creates the thread without starting it; RunInfo state RT_PAUSED. | |
static boolean isAlive() | True if the manager thread object exists. | |
static void motion(Gob g, double frames) | Per pass, before the outline is rebuilt: move a physical Gob by its velocity, or measure the velocity of a Spline driven / kinematic one. frames = the length of this step in frames. | |
static boolean movable(Gob g) | may the pass move g (separation, walls)? | |
static long orient(int px, int py, int qx, int qy, int cx, int cy) | > 0 if c is left of the line p to q, < 0 right, 0 on it. | |
public static boolean pointInPoly(Polygon p, int x, int y) | Same for a java.awt.Polygon; uses its arrays directly (no copy). | |
public static boolean pointInPoly(int[] px, int[] py, int n, int x, int y) | Is x,y inside (or exactly on the edge of) the polygon px[0..n-1],py[0..n-1]? Even-odd ray crossing test: a ray from x,y towards +x crosses the polygon edges an odd number of times when the point is inside. Works for concave polygons and self crossing outlines (even-odd rule). Integer arithmetic only (cross products in long, so no rounding and no overflow while the coordinates are within +-1,000,000,000), no allocation, no division: small and static so the JIT inlines it into the vertex loops. Points on an edge or vertex count as inside. n < 3 is never inside. | |
boolean respond(Gob a, Gob b) | a and b touch (their outlines overlap) and at least one is physical: bounce / stop, damage, separate. Returns true if a physical impact (closing speed > 0) happened. | |
public synchronized void run() | Waits in waitTime steps until the thread is replaced; does no detection. Implements Runnable.run(). | |
void separate(Gob a, Gob b, double nx, double ny, double depth) | push a and b apart along n (a to b) by their overlap, shared by inverse mass among the bodies which may move | |
static void shift(Gob g, double dx, double dy) | move g by dx,dy pixels (float position) and rebuild its world outline | |
static int slot(Gob g, Gob o) | Index of o in g’s contact list, or -1. | |
static boolean splineDriven(Gob g) | is the Gob moved by a Spline right now (an attached Spline that physics has not taken over)? | |
public synchronized void start() | Older thread design: creates and starts the manager thread (nothing calls it). | |
public synchronized void stop() | Older thread design: stops the thread. | |
static void syncExact(Gob g) | keep the exact position in step with x,y when somebody else moved the Gob | |
boolean touch(Gob a, Gob b) | Do the outlines of a and b overlap? Vertices of a in b, vertices of b in a (each pre-rejected by the other’s bounds), then edge crossings if edgeTest. Sets hitX, hitY. | |
public static void updateOutline(Gob g) | Rebuilds g.worldX/worldY/worldN and the bounding box from the Gob’s outline, position (x,y) and size (width,height; they differ from widthBase,heightBase for z-buffered Gobs, so the outline is scaled by width/widthBase and height/heightBase). Arrays are allocated once and reused. Default (no outline set): the box x,y .. x+width-1,y+height-1. | |
static void wallBounce(HyperView v, Gob g) | walls of the view |
The display loop calls the pass — HyperView.java, line 2304
// V3.21: collision pass for Gobs whose Spline has COLLIDE_AT_END (or which have GOB_COLLISION_OUTLINE). Only the Gob list // of the current Dispatch frame (gob[0..totalGobs)) is looked at. See CollisionManager. collisionPass();
A Gob with an outline and a COLLIDE_AT_END Spline — InfoSteps.java, lines 958–962
s.setOutline(poly);
Spline sp = new Spline(d.curView,v,new int[] {x0,x1},new int[] {y0,y1},(int[])null,
Spline.CURVE_LINEAR,SplineConstants.COLLIDE_AT_END);
s.addSpline(sp);
Reading the result
if(s.isCollided()) { /* touching now */ }
int n = s.getCollisionCount(); // separate contacts so far
Gob other = s.getCollidedWith(); // partner of the latest new contact, or null
boolean in = CollisionManager.pointInPoly(xs,ys,xs.length,px,py);