Say what was hit, which way is out, and how far
The narrowphase has been producing a contact since it went in, and the interpreter was throwing it away. `RCOLLISION(n, f)` reports it: what was hit (a sprite or a `SOLID` rectangle), which one, the contact normal, the penetration depth, the contact point, and which axis to reverse. **The normal points out of the other thing and toward this one**, so a program moves along it by the depth and is exactly clear. That sign is the one assertion in the new test that could not be caught any other way -- both parties of a sprite-against-sprite hit get their own record, each pointing the way *that* sprite has to move, and sharing one would tell both to go the same direction, which is how two things end up stuck inside each other. **Field 7 is the one that deletes the most BASIC.** It is the minimum translation axis, computed from the normal in C, and it is there because doing it in BASIC means comparing two floats -- which is exactly where this dialect's left-operand rule catches people. `BALLBRICKS`/`TESTCELL` in the artwork breakout spend six lines computing an overlap rectangle and comparing its width to its height to get this number. The record is **sticky and deepest-wins**: replaced whenever that sprite is in a contact and otherwise left alone, so `BUMP` stays the event and this stays the detail of it. Making it clear itself when nothing touches would break the pairing, because `BUMP` accumulates across steps and a once-a-frame poll would find the detail already gone. Reading `BUMP` clears both, so they cannot disagree. Deliberately narrower than `akgl_Contact`: no actor pointers, because BASIC has no actor; no tile fields, because there is no tilemap; no z, because every test is planar; and **no `dt` and no `sensor`**, which libakgl documents as filled in by the resolver. This interpreter never resolves anything, so those two come back zero and mean nothing, and an always-zero field in a reference table is a lie. Documented with the two caveats that matter: fields 2, 3 and 4 are floats and want a `%` variable, and the contact *point* is exact only for two boxes -- libakgl's solver returns a point on the portal it converged to, while the normal and depth are exact for every pair. Chapter 8's collision section stops claiming only type 1 exists, which has been false since the previous commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
This commit is contained in:
@@ -357,6 +357,17 @@ typedef struct
|
||||
uint16_t lastmask;
|
||||
uint16_t lastsolidmask;
|
||||
bool lastvalid;
|
||||
|
||||
/**
|
||||
* The deepest contact each sprite was in when the scan last ran.
|
||||
*
|
||||
* Kept as a side effect of the pass that computes the masks, because that is
|
||||
* the only place the information exists -- `akgl_collision_test()` produces
|
||||
* it and a caller that discards it cannot get it back. Deepest wins, because
|
||||
* the deepest overlap is the one a program has to undo.
|
||||
*/
|
||||
akbasic_Contact contacts[AKBASIC_MAX_SPRITES];
|
||||
bool hascontact[AKBASIC_MAX_SPRITES];
|
||||
} akbasic_AkglSprites;
|
||||
|
||||
/**
|
||||
|
||||
@@ -151,6 +151,34 @@ typedef struct
|
||||
double y2;
|
||||
} akbasic_Solid;
|
||||
|
||||
/**
|
||||
* @brief What sprite n last collided with, and how.
|
||||
*
|
||||
* Narrower than libakgl's `akgl_Contact` on purpose. No actor pointers, because
|
||||
* BASIC has no actor; no tile fields, because there is no tilemap; no z, because
|
||||
* every test is planar. And **no `dt` and no `sensor`**, which libakgl documents
|
||||
* as filled in by the resolver -- this interpreter never resolves anything, so
|
||||
* they would come back zero and mean nothing. An always-zero field in a reference
|
||||
* table is a lie.
|
||||
*
|
||||
* `axis` is the one field that is not the library's. It is the minimum
|
||||
* translation axis -- which way to reverse -- and it is here because working it
|
||||
* out from the normal is exactly where this dialect's left-operand arithmetic
|
||||
* rule catches people, and because it is the single field that deletes the most
|
||||
* BASIC.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int what; /* 0 nothing yet, 1 a sprite, 2 static geometry */
|
||||
int other; /* the other sprite's number, or the SOLID id */
|
||||
double nx; /* unit normal, out of the other and toward this */
|
||||
double ny;
|
||||
double depth; /* how far in, in pixels */
|
||||
double px; /* where they touched */
|
||||
double py;
|
||||
int axis; /* 1 reverse x, 2 reverse y */
|
||||
} akbasic_Contact;
|
||||
|
||||
/**
|
||||
* @brief The sprite verbs' own state, which lives on the runtime.
|
||||
*
|
||||
@@ -164,6 +192,7 @@ typedef struct
|
||||
{
|
||||
akbasic_Sprite sprites[AKBASIC_MAX_SPRITES];
|
||||
akbasic_Solid solids[AKBASIC_MAX_SOLIDS];
|
||||
akbasic_Contact contacts[AKBASIC_MAX_SPRITES];
|
||||
int sharedcolor1; /* SPRCOLOR's two multicolour registers, 1-16 */
|
||||
int sharedcolor2;
|
||||
uint16_t bumped; /* bit n-1 set when sprite n has collided */
|
||||
@@ -282,6 +311,22 @@ typedef struct akbasic_SpriteBackend
|
||||
* reports none -- a step is not a statement and has no line to blame.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*solids)(struct akbasic_SpriteBackend *self, uint16_t *mask);
|
||||
/**
|
||||
* Describe sprite @p n's deepest collision from the scan just run.
|
||||
*
|
||||
* @p found comes back false when that sprite hit nothing, and @p dest is
|
||||
* then untouched. Deepest rather than first, because the deepest is the one
|
||||
* a program has to undo.
|
||||
*
|
||||
* A pure accessor: it reads what the same pass that answered `collisions`
|
||||
* and `solids` already worked out. That is why the contract on `collisions`
|
||||
* says the two are about the same instant -- this depends on it.
|
||||
*
|
||||
* Optional. Withholding it makes `RCOLLISION` report nothing rather than
|
||||
* refuse, the way a runtime with no backend at all does.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*contact)(struct akbasic_SpriteBackend *self, int n,
|
||||
akbasic_Contact *dest, bool *found);
|
||||
} akbasic_SpriteBackend;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user