From 44355f99208c4cb0b108e3f36a52400cd5057e99 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 08 2020 17:03:12 +0000 Subject: improve collision --- diff --git a/demo/src/phisics.c b/demo/src/phisics.c index f677e3c..fb3f013 100644 --- a/demo/src/phisics.c +++ b/demo/src/phisics.c @@ -23,6 +23,7 @@ void phisicsInit() { spriteSetTintColor(brick1, colorByRGB(0, 0, 1)); spriteSetColliderRectangle(brick1, 0, 0, 0, -1, -1, 20); spriteSetBounciness(brick1, 0.5); + spriteSetWeightLevel(brick1, 1); brick2 = createSpriteEx(200+32, 200+64, 64, 64); spriteSetAnimation(brick2, createAnimation("data/sprite/bricks.png")); @@ -33,6 +34,7 @@ void phisicsInit() { spriteSetTintColor(brick3, colorByRGBA(1, 0, 1, 0.5)); spriteSetColliderRectangle(brick3, 0, 0, 0, -1, -1, 20); spriteSetBounciness(brick3, 2); + spriteSetWeightLevel(brick3, 1); movement = createGroup(); groupAdd(movement, ball); @@ -58,8 +60,8 @@ void phisicsDraw() { if (keyDown("down")) spriteSetVelocityY(ball, vy + accel*dt); int collision = FALSE; - if (groupCollideBetween(movement, 1)) collision = TRUE; - if (groupPushGroup(edges, movement, 0.9)) collision = TRUE; + if (groupCollideBetween(movement, 0)) collision = TRUE; + if (groupCollideGroup(edges, movement, 0)) collision = TRUE; if (collision) soundPlay(beep, FALSE); } diff --git a/src/animation.c b/src/animation.c index 5dd43b9..1fdc413 100644 --- a/src/animation.c +++ b/src/animation.c @@ -318,12 +318,18 @@ Animation createAnimation(const char *path) { return createAnimationEx(path, TRUE, FALSE, FALSE); } void animationDestroy(Animation animation) { - heliSpriteUnsetAnimation(animation); + for(int i = worldGetSpriteCount() - 1; i >= 0; --i) { + Sprite s = worldGetSprite(i); + if (spriteGetAnimation(s) == animation) + spriteSetNoAnimation(s); + } + HeliDrawingState *ss = heliDrawingGetState(); for(HeliDrawingState *s = heliDrawingGetStateStack(); s <= ss; ++s) { if (ss->fillTexture.animation == animation) ss->fillTexture.animation = NULL; if (ss->strokeTexture.animation == animation) ss->strokeTexture.animation = NULL; } + *(animation->prev ? &animation->prev->next : &first) = animation->next; *(animation->next ? &animation->next->prev : &last ) = animation->prev; heliArrayDestroy(&animation->frames); diff --git a/src/collider.c b/src/collider.c index 142639b..1ee2af2 100644 --- a/src/collider.c +++ b/src/collider.c @@ -131,7 +131,7 @@ int heliCheckCollision( HeliCollider *a, HeliCollider *b, double *dx, double *dy, double *vx, double *vy, - double bounciness ) + double extra ) { HeliCollisionData data = {}; @@ -150,7 +150,7 @@ int heliCheckCollision( // check collision if (a->type == COLLIDER_CIRCLE && b->type == COLLIDER_CIRCLE) { - data.radius = fabs(a->radius) + fabs(b->radius); + data.radius = fabs(a->radius) + fabs(b->radius) + fabs(extra); data.x1 = data.x2 = b->x - a->x; data.y1 = data.y2 = b->y - a->y; collision(&data); @@ -158,21 +158,21 @@ int heliCheckCollision( if (a->type == COLLIDER_CIRCLE && b->type == COLLIDER_RECTANGLE) { double corners[8]; calcCorners(b, corners); - data.radius = fabs(a->radius) + fabs(b->radius); - collisionCorners(&data, corners, a->x, a->y); + data.radius = fabs(a->radius) + fabs(b->radius) + fabs(extra); + if (data.radius > HELI_PRECISION) collisionCorners(&data, corners, a->x, a->y); } else if (a->type == COLLIDER_RECTANGLE && b->type == COLLIDER_CIRCLE) { double corners[8]; calcCorners(a, corners); data.flip = TRUE; - data.radius = fabs(a->radius) + fabs(b->radius); - collisionCorners(&data, corners, b->x, b->y); + data.radius = fabs(a->radius) + fabs(b->radius) + fabs(extra); + if (data.radius > HELI_PRECISION) collisionCorners(&data, corners, b->x, b->y); } else if (a->type == COLLIDER_RECTANGLE && b->type == COLLIDER_RECTANGLE) { double cornersA[8], cornersB[8]; calcCorners(a, cornersA); calcCorners(b, cornersB); - data.radius = fabs(a->radius) + fabs(b->radius); + data.radius = fabs(a->radius) + fabs(b->radius) + fabs(extra); for(int i = 0; i < 4; ++i) collisionCorners(&data, cornersB, cornersA[2*i+0], cornersA[2*i+1]); data.flip = TRUE; @@ -181,13 +181,12 @@ int heliCheckCollision( } // is collision found? - *dx = 0; *dy = 0; if ( !data.found || data.lmin >= -HELI_PRECISION || data.lmax <= HELI_PRECISION ) return FALSE; - // calc displacement and velocity + // calc normal double l, nx, ny; if (-data.lmin < data.lmax) { l = data.lmin; @@ -199,15 +198,45 @@ int heliCheckCollision( ny = data.nyMax; } + // check angle + //double a0x = cos(angle0); + //double a0y = sin(angle0); + //double a1x = cos(angle1); + //double a1y = sin(angle1); + //double dax = a1x - a0x; + //double day = a1y - a0y; + //double ka = day*nx - dax*ny; + //if (ka < -HELI_PRECISION) return FALSE; + + // calc displacement + *dx = 0; *dy = 0; double kd = (nx*data.dirx + ny*data.diry)*l; *dx = nx*kd; *dy = ny*kd; - double kn = *vx*nx + *vy*ny; - if (kn < 0) { - *vx -= kn*nx*(1 + fabs(bounciness)); - *vy -= kn*ny*(1 + fabs(bounciness)); - } + // calc velocity + double bounciness = fabs(a->bounciness * b->bounciness); + double bouncinessThreshold = fabs(a->bouncinessThreshold) + fabs(b->bouncinessThreshold); + if (bounciness < HELI_PRECISION) bounciness = 0; + + double sliding = a->sliding * b->sliding; + double slidingThreshold = fabs(a->slidingThreshold) + fabs(b->slidingThreshold); + if (!(slidingThreshold > HELI_PRECISION)) slidingThreshold = HELI_PRECISION; + if (sliding < HELI_PRECISION) sliding = 0; + + double vb = -(*vx*nx + *vy*ny); + double vs = *vy*nx - *vx*ny; + + vb -= bouncinessThreshold; + if (vb < HELI_PRECISION) vb = 0; + vb *= bounciness; + + if (vs > slidingThreshold) vs -= slidingThreshold; else + if (vs < slidingThreshold) vs += slidingThreshold; else vs = 0; + vs *= sliding; + + *vx = nx*vb - ny*vs; + *vy = ny*vb + nx*vs; return TRUE; } diff --git a/src/group.c b/src/group.c index c4d0d22..db0bc64 100644 --- a/src/group.c +++ b/src/group.c @@ -5,6 +5,7 @@ struct _Group { HeliArray sprites; + HeliArray spritesSorted; }; @@ -15,7 +16,7 @@ Group createGroup() { return g; } -Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double borderWidth) { +Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double borderWidth, int weightLevel) { if (!heliInitialized) return NULL; if (x2 < x1) { double x = x2; x2 = x1; x1 = x; } if (y2 < y1) { double y = y2; y2 = y1; y1 = y; } @@ -32,16 +33,18 @@ Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double bord groupAdd(g, createSpriteEx(x1 + w/2, y2 + b/2, w, b)); groupAdd(g, createSpriteEx(x1 + b/2, y1 + h/2, b, h)); groupAdd(g, createSpriteEx(x2 + b/2, y1 + h/2, b, h)); + groupSetWeightLevel(g, weightLevel); return g; } Group createEdgesGroup() - { return createEdgesGroupEx(0, 0, worldGetWidth(), worldGetHeight(), worldGetHeight()); } + { return createEdgesGroupEx(0, 0, worldGetWidth(), worldGetHeight(), worldGetHeight(), 100); } void groupDestroy(Group group) { groupClear(group); heliArrayDestroy(&group->sprites); + heliArrayDestroy(&group->spritesSorted); heliObjectUnregister(group); free(group); } @@ -55,6 +58,7 @@ void groupAdd(Group group, Sprite sprite) { if (!sprite) return; if (groupContains(group, sprite)) return; heliArrayInsert(&group->sprites, -1, sprite, NULL); + heliArrayInsert(&group->spritesSorted, -1, sprite, NULL); heliArrayInsert(heliSpriteGetGroups(sprite), -1, group, NULL); } @@ -62,6 +66,9 @@ void groupRemove(Group group, Sprite sprite) { for(int i = group->sprites.count-1; i >= 0; --i) if (group->sprites.items[i].value == sprite) heliArrayRemove(&group->sprites, i); + for(int i = group->spritesSorted.count-1; i >= 0; --i) + if (group->spritesSorted.items[i].value == sprite) + heliArrayRemove(&group->spritesSorted, i); HeliArray *groups = heliSpriteGetGroups(sprite); for(int i = groups->count-1; i >= 0; --i) if (groups->items[i].value == group) @@ -93,47 +100,59 @@ int groupGetCount(Group group) Sprite groupGet(Group group, int i) { return (Sprite)heliArrayGetValue(&group->sprites, i); } -int groupOverlap(Group group, Sprite sprite) - { return groupCollideEx(group, sprite, TRUE, TRUE, 0); } -int groupCollide(Group group, Sprite sprite, double bounciness) - { return groupCollideEx(group, sprite, FALSE, FALSE, bounciness); } -int groupBounceOff(Group group, Sprite sprite, double bounciness) - { return groupCollideEx(group, sprite, FALSE, TRUE, bounciness); } -int groupPush(Group group, Sprite sprite, double bounciness) - { return groupCollideEx(group, sprite, TRUE, FALSE, bounciness); } -int groupCollideEx(Group group, Sprite sprite, int keepVelocityGroup, int keepVelocitySprite, double bounciness) { + +Sprite groupSpriteByPoint(Group group, double x, double y, int onlyVisible) { + heliSpriteSort(&group->spritesSorted); + for(int i = group->spritesSorted.count - 1; i >= 0; --i) { + Sprite s = (Sprite)(group->spritesSorted.items[i].value); + if ((!onlyVisible || spriteGetVisible(s)) && spriteIsPointInside(s, x, y)) return s; + } + return NULL; +} + +int groupOverlap(Group group, Sprite sprite, double distance) { + for(int i = 0; i < groupGetCount(group); ++i) + if (spriteOverlap(groupGet(group, i), sprite, distance)) + return TRUE; + return FALSE; +} + +int groupCollide(Group group, Sprite sprite, double distance) { int result = FALSE; for(int i = 0; i < groupGetCount(group); ++i) - if (spriteCollideEx(groupGet(group, i), sprite, keepVelocityGroup, keepVelocitySprite, bounciness)) + if (spriteCollide(groupGet(group, i), sprite, distance)) result = TRUE; return result; } -int groupOverlapGroup(Group a, Group b) - { return groupCollideGroupEx(a, b, TRUE, TRUE, 0); } -int groupCollideGroup(Group a, Group b, double bounciness) - { return groupCollideGroupEx(a, b, FALSE, FALSE, bounciness); } -int groupBounceOffGroup(Group group, Group other, double bounciness) - { return groupCollideGroupEx(group, other, FALSE, TRUE, bounciness); } -int groupPushGroup(Group group, Group other, double bounciness) - { return groupCollideGroupEx(group, other, TRUE, FALSE, bounciness); } -int groupCollideGroupEx(Group a, Group b, int keepVelocityA, int keepVelocityB, double bounciness) { +int groupOverlapGroup(Group a, Group b, double distance) { + for(int i = 0; i < groupGetCount(b); ++i) + if (groupCollide(a, groupGet(b, i), distance)) + return TRUE; + return FALSE; +} + +int groupCollideGroup(Group a, Group b, double distance) { int result = FALSE; for(int i = 0; i < groupGetCount(b); ++i) - if (groupCollideEx(a, groupGet(b, i), keepVelocityA, keepVelocityB, bounciness)) + if (groupCollide(a, groupGet(b, i), distance)) result = TRUE; return result; } -int groupOverlapBetween(Group group) - { return groupCollideBetweenEx(group, TRUE, 0); } -int groupCollideBetween(Group group, double bounciness) - { return groupCollideBetweenEx(group, FALSE, bounciness); } -int groupCollideBetweenEx(Group group, int keepVelocity, double bounciness) { +int groupOverlapBetween(Group group, double distance) { + for(int i = 0; i < groupGetCount(group); ++i) + for(int j = i+1; j < groupGetCount(group); ++j) + if (spriteCollide(groupGet(group, i), groupGet(group, j), distance)) + return TRUE; + return FALSE; +} + +int groupCollideBetween(Group group, double distance) { int result = FALSE; for(int i = 0; i < groupGetCount(group); ++i) for(int j = i+1; j < groupGetCount(group); ++j) - if (spriteCollideEx(groupGet(group, i), groupGet(group, j), keepVelocity, keepVelocity, bounciness)) + if (spriteCollide(groupGet(group, i), groupGet(group, j), distance)) result = TRUE; return result; } @@ -195,12 +214,24 @@ void groupSetMirrorXEach(Group group, int mirrorX) { foreachInt(group, mirrorX, &spriteSetMirrorX); } void groupSetMirrorYEach(Group group, int mirrorY) { foreachInt(group, mirrorY, &spriteSetMirrorY); } -void groupSetTagEach(Group group, int tag) - { foreachInt(group, tag, &spriteSetTag); } void groupSetShapeColorEach(Group group, unsigned int colorCode) { foreachUInt(group, colorCode, &spriteSetShapeColor); } void groupSetTintColorEach(Group group, unsigned int colorCode) { foreachUInt(group, colorCode, &spriteSetTintColor); } +void groupSetUserTagEach(Group group, int tag) + { foreachInt(group, tag, &spriteSetUserTag); } + +void groupSetBounciness(Group group, double bounciness) + { foreachDouble(group, bounciness, &spriteSetBounciness); } +void groupSetBouncinessThreshold(Group group, double bouncinessThreshold) + { foreachDouble(group, bouncinessThreshold, &spriteSetBouncinessThreshold); } +void groupSetSliding(Group group, double sliding) + { foreachDouble(group, sliding, &spriteSetSliding); } +void groupSetSlidingThreshold(Group group, double slidingThreshold) + { foreachDouble(group, slidingThreshold, &spriteSetSlidingThreshold); } +void groupSetWeightLevel(Group group, int weightLevel) + { foreachInt(group, weightLevel, &spriteSetWeightLevel); } + void groupSetAnimationEach(Group group, Animation animation) { for(int i = groupGetCount(group) - 1; i >= 0 ; --i) @@ -227,6 +258,23 @@ void groupSetVelocityEach(Group group, double x, double y) { spriteSetVelocityXY(groupGet(group, i), x, y); } + +void groupSetUserTextEach(Group group, const char *text) { + for(int i = groupGetCount(group) - 1; i >= 0 ; --i) + spriteSetUserText(groupGet(group, i), text); +} + +void groupSetUserDataEach(Group group, void *data) { + for(int i = groupGetCount(group) - 1; i >= 0 ; --i) + spriteSetUserData(groupGet(group, i), data); +} + +void groupSetDestroyEach(Group group, SpriteCallback destroy) { + for(int i = groupGetCount(group) - 1; i >= 0 ; --i) + spriteSetDestroy(groupGet(group, i), destroy); +} + + void groupSetColliderEach(Group group, Collider type, double xOffset, double yOffset, double rotationOffset) { groupSetColliderEachEx(group, type, xOffset, yOffset, rotationOffset, -1, -1, -1); } void groupSetColliderCircleEach(Group group, double xOffset, double yOffset, double radius) diff --git a/src/group.h b/src/group.h index 1fb7540..7efb37b 100644 --- a/src/group.h +++ b/src/group.h @@ -10,7 +10,7 @@ typedef struct _Group *Group; Group createGroup(); Group createEdgesGroup(); -Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double borderWidth); +Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double borderWidth, int weightLevel); void groupDestroy(Group group); void groupDestroyEx(Group group, int destroySprites); @@ -23,21 +23,14 @@ int groupContains(Group group, Sprite sprite); int groupGetCount(Group group); Sprite groupGet(Group group, int i); -int groupOverlap(Group group, Sprite sprite); -int groupCollide(Group group, Sprite sprite, double bounciness); -int groupBounceOff(Group group, Sprite sprite, double bounciness); -int groupPush(Group group, Sprite sprite, double bounciness); -int groupCollideEx(Group group, Sprite sprite, int keepVelocityGroup, int keepVelocitySprite, double bounciness); +Sprite groupSpriteByPoint(Group group, double x, double y, int onlyVisible); -int groupOverlapGroup(Group a, Group b); -int groupCollideGroup(Group a, Group b, double bounciness); -int groupBounceOffGroup(Group group, Group other, double bounciness); -int groupPushGroup(Group group, Group other, double bounciness); -int groupCollideGroupEx(Group a, Group b, int keepVelocityA, int keepVelocityB, double bounciness); - -int groupOverlapBetween(Group group); -int groupCollideBetween(Group group, double bounciness); -int groupCollideBetweenEx(Group group, int keepVelocity, double bounciness); +int groupOverlap(Group group, Sprite sprite, double distance); +int groupCollide(Group group, Sprite sprite, double distance); +int groupOverlapGroup(Group a, Group b, double distance); +int groupCollideGroup(Group a, Group b, double distance); +int groupOverlapBetween(Group group, double distance); +int groupCollideBetween(Group group, double distance); double groupGetMinDepth(Group group); double groupGetMaxDepth(Group group); @@ -45,6 +38,12 @@ double groupGetMaxDepth(Group group); void groupDestroyEach(Group group); void groupDestroyTimerEach(Group group, double lifetime); +void groupSetBounciness(Group group, double bounciness); +void groupSetBouncinessThreshold(Group group, double bouncinessThreshold); +void groupSetSliding(Group group, double sliding); +void groupSetSlidingThreshold(Group group, double slidingThreshold); +void groupSetWeightLevel(Group group, int weightLevel); + void groupSetVisibleEach(Group group, int visible); void groupSetWidthEach(Group group, double width); void groupSetHeightEach(Group group, double height); @@ -67,6 +66,11 @@ void groupSetNoAnimationEach(Group group); void groupSetShapeColorEach(Group group, unsigned int colorCode); void groupSetTintColorEach(Group group, unsigned int colorCode); +void groupSetUserTagEach(Group group, int tag); +void groupSetUserTextEach(Group group, const char *text); +void groupSetUserDataEach(Group group, void *data); +void groupSetDestroyEach(Group group, SpriteCallback destroy); + void groupSetColliderEach(Group group, Collider type, double xOffset, double yOffset, double rotationOffset); void groupSetColliderCircleEach(Group group, double xOffset, double yOffset, double radius); void groupSetColliderRectangleEach( diff --git a/src/private.h b/src/private.h index aa2cd33..2fd1c90 100644 --- a/src/private.h +++ b/src/private.h @@ -189,13 +189,17 @@ typedef struct _HeliCollider { double width; double height; double rotation; + double bounciness; + double bouncinessThreshold; + double sliding; + double slidingThreshold; } HeliCollider; int heliCheckCollision( HeliCollider *a, HeliCollider *b, double *dx, double *dy, double *vx, double *vy, - double bounciness ); + double extra ); int heliPointCollision(HeliCollider *c, double x, double y); @@ -206,8 +210,8 @@ typedef void (*HeliSpriteEashInt)(Sprite, int); typedef void (*HeliSpriteEashUInt)(Sprite, unsigned int); typedef void (*HeliSpriteEashDouble)(Sprite, double); HeliArray* heliSpriteGetGroups(Sprite sprite); +void heliSpriteSort(HeliArray *sprites); void heliSpriteUpdate(double dt); -void heliSpriteUnsetAnimation(Animation animation); void heliSpriteFinish(); diff --git a/src/sprite.c b/src/sprite.c index 20ee9dd..177360d 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -21,7 +21,7 @@ struct _Sprite { double rotationSpeed; HeliCollider collider; - double bounciness; + int weightLevel; int rotateToDirection; int mirrorX; @@ -29,7 +29,6 @@ struct _Sprite { double depth; double lifeTime; int visible; - int tag; int debug; double shapeColor[4]; @@ -38,6 +37,11 @@ struct _Sprite { HeliArray groups; Animation animation; + + int userTag; + char *userText; + void *userData; + SpriteCallback destroyCallback; }; static HeliArray sprites; @@ -45,13 +49,10 @@ static HeliArray spritesSorted; static void prepareCollider(Sprite sprite, HeliCollider *collider) { - collider->type = sprite->collider.type; - collider->x = sprite->collider.x + sprite->x; - collider->y = sprite->collider.y + sprite->y; - collider->width = sprite->collider.width; - collider->height = sprite->collider.height; - collider->radius = sprite->collider.radius; - collider->rotation = sprite->collider.rotation + sprite->rotation; + memcpy(collider, &sprite->collider, sizeof(*collider)); + collider->x += sprite->x; + collider->y += sprite->y; + collider->rotation += sprite->rotation; // auto-size if (collider->type == COLLIDER_RECTANGLE) { @@ -99,7 +100,8 @@ Sprite createSpriteEx(double x, double y, double width, double height) { s->scale = 1; s->collider.type = COLLIDER_RECTANGLE; s->collider.width = s->collider.height = -1; - s->bounciness = 1; + s->collider.bounciness = 1; + s->collider.sliding = 1; s->mirrorX = 1; s->mirrorY = 1; @@ -110,6 +112,8 @@ Sprite createSpriteEx(double x, double y, double width, double height) { s->shapeColor[3] = 1; s->tintColor[0] = s->tintColor[1] = s->tintColor[2] = s->tintColor[3] = 1; + s->userText = heliStringCopy(""); + heliArrayInsert(&sprites, -1, s, NULL); heliArrayInsert(&spritesSorted, -1, s, NULL); @@ -121,6 +125,7 @@ Sprite createSprite(double x, double y) { return createSpriteEx(x, y, 100, 100); } void spriteDestroy(Sprite sprite) { + if (sprite->destroyCallback) sprite->destroyCallback(sprite); spriteSetNoAnimation(sprite); while(sprite->groups.count > 0) groupRemove((Group)sprite->groups.items[0].value, sprite); @@ -132,6 +137,7 @@ void spriteDestroy(Sprite sprite) { heliArrayRemove(&spritesSorted, i); heliArrayDestroy(&sprite->groups); heliObjectUnregister(sprite); + free(sprite->userText); free(sprite); } @@ -146,6 +152,7 @@ Sprite spriteClone(Sprite sprite) { memcpy(s, sprite, sizeof(*s)); s->lifeTime = -1; memset(&s->groups, 0, sizeof(s->groups)); + s->userText = heliStringCopy(sprite->userText); return s; } @@ -209,65 +216,77 @@ int spriteGetVisible(Sprite sprite) { return sprite->visible; } void spriteSetVisible(Sprite sprite, int visible) { sprite->visible = visible != FALSE; } -int spriteGetTag(Sprite sprite) { return sprite->tag; } -void spriteSetTag(Sprite sprite, int tag) - { sprite->tag = tag; } - int spriteGetDebug(Sprite sprite) { return sprite->debug; } void spriteSetDebug(Sprite sprite, int debug) { sprite->debug = debug != FALSE; } -int spriteOverlap(Sprite a, Sprite b) - { return spriteCollideEx(a, b, TRUE, TRUE, 0); } -int spriteCollide(Sprite a, Sprite b, double bounciness) - { return spriteCollideEx(a, b, FALSE, FALSE, bounciness); } -int spriteBounceOff(Sprite sprite, Sprite other, double bounciness) - { return spriteCollideEx(sprite, other, FALSE, TRUE, bounciness); } -int spritePush(Sprite sprite, Sprite other, double bounciness) - { return spriteCollideEx(sprite, other, TRUE, FALSE, bounciness); } - -int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, double bounciness) { +static int collide(Sprite a, Sprite b, double distance, int apply) { if (a == b) return FALSE; - HeliCollider ca, cb; + HeliCollider ca = {}, cb = {}; prepareCollider(a, &ca); prepareCollider(b, &cb); double dx = 0, dy = 0; double vx = a->vx - b->vx; double vy = a->vy - b->vy; - bounciness = fabs(bounciness * a->bounciness * b->bounciness); - if (!heliCheckCollision(&ca, &cb, &dx, &dy, &vx, &vy, bounciness)) + if (!heliCheckCollision(&ca, &cb, &dx, &dy, &vx, &vy, distance)) return FALSE; - if ( keepVelocityA && !keepVelocityB) { - b->x -= dx; - b->y -= dy; - spriteSetVelocityXY(b, a->vx - vx, a->vy - vy); - } else - if (!keepVelocityA && keepVelocityB) { - a->x += dx; - a->y += dy; - spriteSetVelocityXY(a, b->vx + vx, b->vy + vy); - } else - if (!keepVelocityA && !keepVelocityB) { - double vxAvg = 0.5*(a->vx + b->vx); - double vyAvg = 0.5*(a->vy + b->vy); - - a->x += 0.5*dx; - a->y += 0.5*dy; - spriteSetVelocityXY(a, vxAvg + 0.5*vx, vyAvg + 0.5*vy); - - b->x -= 0.5*dx; - b->y -= 0.5*dy; - spriteSetVelocityXY(b, vxAvg - 0.5*vx, vyAvg - 0.5*vy); + if (apply) { + if ( a->weightLevel > b->weightLevel) { + b->x -= dx; + b->y -= dy; + spriteSetVelocityXY(b, a->vx - vx, a->vy - vy); + } else + if ( b->weightLevel > a->weightLevel ) { + a->x += dx; + a->y += dy; + spriteSetVelocityXY(a, b->vx + vx, b->vy + vy); + } else { + double vxAvg = 0.5*(a->vx + b->vx); + double vyAvg = 0.5*(a->vy + b->vy); + + a->x += 0.5*dx; + a->y += 0.5*dy; + spriteSetVelocityXY(a, vxAvg + 0.5*vx, vyAvg + 0.5*vy); + + b->x -= 0.5*dx; + b->y -= 0.5*dy; + spriteSetVelocityXY(b, vxAvg - 0.5*vx, vyAvg - 0.5*vy); + } } return TRUE; } +int spriteOverlap(Sprite a, Sprite b, double distance) + { return collide(a, b, distance, FALSE); } +int spriteCollide(Sprite a, Sprite b, double distance) + { return collide(a, b, distance, TRUE); } + -double spriteGetBounciness(Sprite sprite) { return sprite->bounciness; } +double spriteGetBounciness(Sprite sprite) + { return sprite->collider.bounciness; } void spriteSetBounciness(Sprite sprite, double bounciness) - { sprite->bounciness = bounciness > 0 ? bounciness : 0; } + { sprite->collider.bounciness = bounciness > 0 ? bounciness : 0; } +double spriteGetBouncinessThreshold(Sprite sprite) + { return sprite->collider.bouncinessThreshold; } +void spriteSetBouncinessThreshold(Sprite sprite, double bouncinessThreshold) + { sprite->collider.bouncinessThreshold = bouncinessThreshold > 0 ? bouncinessThreshold : 0; } + +double spriteGetSliding(Sprite sprite) + { return sprite->collider.sliding; } +void spriteSetSliding(Sprite sprite, double sliding) + { sprite->collider.sliding = sliding > 0 ? sliding : 0; } +double spriteGetSlidingThreshold(Sprite sprite) + { return sprite->collider.slidingThreshold; } +void spriteSetSlidingThreshold(Sprite sprite, double slidingThreshold) + { sprite->collider.slidingThreshold = slidingThreshold > 0 ? slidingThreshold : 0; } + +int spriteGetWeightLevel(Sprite sprite) + { return sprite->weightLevel; } +void spriteSetWeightLevel(Sprite sprite, int weightLevel) + { sprite->weightLevel = weightLevel; } + void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset) { spriteSetColliderEx(sprite, type, xOffset, yOffset, rotationOffset, -1, -1, -1); } @@ -355,10 +374,28 @@ double spriteGetScaledHeight(Sprite sprite) { return sprite->height * sprite->scale; } +int spriteGetUserTag(Sprite sprite) + { return sprite->userTag; } +void spriteSetUserTag(Sprite sprite, int tag) + { sprite->userTag = tag; } +const char* spriteGetUserText(Sprite sprite) + { return sprite->userText; } +void spriteSetUserText(Sprite sprite, const char *text) { + if (sprite->userText == text) return; + free(sprite->userText); + sprite->userText = heliStringCopy(text); +} +void* spriteGetUserData(Sprite sprite) + { return sprite->userData; } +void spriteSetUserData(Sprite sprite, void *data) + { sprite->userData = data; } + +void spriteSetDestroy(Sprite sprite, SpriteCallback destroy) + { sprite->destroyCallback = destroy; } + int worldGetSpriteCount() { return sprites.count; } - Sprite worldGetSprite(int i) { return (Sprite)heliArrayGetValue(&sprites, i); } @@ -396,7 +433,7 @@ static void drawSpriteDebug(Sprite s) { restoreState(); } -static void drawSprite(Sprite s, double aaBorder) { +static void drawSprite(Sprite s) { double w = 0.5*s->scale*s->width; double h = 0.5*s->scale*s->height; if (w < HELI_PRECISION || h < HELI_PRECISION) return; @@ -420,42 +457,45 @@ static void drawSprite(Sprite s, double aaBorder) { restoreState(); } -void drawSprites() { - // sort +void heliSpriteSort(HeliArray *sprites) { int found = TRUE; while(found) { found = FALSE; // forward - for(int i = 1; i < spritesSorted.count; ++i) { - if ( ((Sprite)spritesSorted.items[i-1].value)->depth - < ((Sprite)spritesSorted.items[i ].value)->depth ) + for(int i = 1; i < sprites->count; ++i) { + if ( ((Sprite)sprites->items[i-1].value)->depth + < ((Sprite)sprites->items[i ].value)->depth ) { - void *x = spritesSorted.items[i].value; - spritesSorted.items[i].value = spritesSorted.items[i-1].value; - spritesSorted.items[i-1].value = x; + void *x = sprites->items[i].value; + sprites->items[i].value = sprites->items[i-1].value; + sprites->items[i-1].value = x; found = TRUE; } } if (!found) break; // backward found = FALSE; - for(int i = spritesSorted.count - 1; i > 0; --i) { - if ( ((Sprite)spritesSorted.items[i-1].value)->depth - < ((Sprite)spritesSorted.items[i ].value)->depth ) + for(int i = sprites->count - 1; i > 0; --i) { + if ( ((Sprite)sprites->items[i-1].value)->depth + < ((Sprite)sprites->items[i ].value)->depth ) { - void *x = spritesSorted.items[i].value; - spritesSorted.items[i].value = spritesSorted.items[i-1].value; - spritesSorted.items[i-1].value = x; + void *x = sprites->items[i].value; + sprites->items[i].value = sprites->items[i-1].value; + sprites->items[i-1].value = x; found = TRUE; } } } +} + +void drawSprites() { + // sort + heliSpriteSort(&spritesSorted); // draw - const double aaBorder = heliGLGetAAResolution(); for(int i = 0; i < spritesSorted.count; ++i) { Sprite s = (Sprite)(spritesSorted.items[i].value); - if (s->visible) drawSprite(s, aaBorder); + if (s->visible) drawSprite(s); } // draw debug marks @@ -489,14 +529,6 @@ void heliSpriteUpdate(double dt) { HeliArray* heliSpriteGetGroups(Sprite sprite) { return &sprite->groups; } -void heliSpriteUnsetAnimation(Animation animation) { - for(int i = 0; i < sprites.count; ++i) { - Sprite s = (Sprite)sprites.items[i].value; - if (spriteGetAnimation(s) == animation) - spriteSetNoAnimation(s); - } -} - void heliSpriteFinish() { while(worldGetSpriteCount()) { spriteDestroy(worldGetSprite(0)); } diff --git a/src/sprite.h b/src/sprite.h index 86defd1..325ec3f 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -7,6 +7,7 @@ typedef struct _Sprite *Sprite; +typedef void (*SpriteCallback)(Sprite sprite); typedef enum _Collider { @@ -65,22 +66,25 @@ void spriteSetDepth(Sprite sprite, double depth); int spriteGetVisible(Sprite sprite); void spriteSetVisible(Sprite sprite, int visible); -int spriteGetTag(Sprite sprite); -void spriteSetTag(Sprite sprite, int tag); - int spriteGetDebug(Sprite sprite); void spriteSetDebug(Sprite sprite, int debug); -int spriteOverlap(Sprite a, Sprite b); -int spriteCollide(Sprite a, Sprite b, double bounciness); -int spriteBounceOff(Sprite sprite, Sprite other, double bounciness); -int spritePush(Sprite sprite, Sprite other, double bounciness); -int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, double bounciness); - +int spriteOverlap(Sprite a, Sprite b, double distance); +int spriteCollide(Sprite a, Sprite b, double distance); int spriteIsPointInside(Sprite sprite, double x, double y); double spriteGetBounciness(Sprite sprite); void spriteSetBounciness(Sprite sprite, double bounciness); +double spriteGetBouncinessThreshold(Sprite sprite); +void spriteSetBouncinessThreshold(Sprite sprite, double bouncinessThreshold); + +double spriteGetSliding(Sprite sprite); +void spriteSetSliding(Sprite sprite, double sliding); +double spriteGetSlidingThreshold(Sprite sprite); +void spriteSetSlidingThreshold(Sprite sprite, double slidingThreshold); + +int spriteGetWeightLevel(Sprite sprite); +void spriteSetWeightLevel(Sprite sprite, int weightLevel); void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset); void spriteSetColliderCircle(Sprite sprite, double xOffset, double yOffset, double radius); @@ -108,5 +112,14 @@ void spritePointTo(Sprite sprite, double x, double y); double spriteGetScaledWidth(Sprite sprite); double spriteGetScaledHeight(Sprite sprite); +int spriteGetUserTag(Sprite sprite); +void spriteSetUserTag(Sprite sprite, int tag); +const char* spriteGetUserText(Sprite sprite); +void spriteSetUserText(Sprite sprite, const char *text); +void* spriteGetUserData(Sprite sprite); +void spriteSetUserData(Sprite sprite, void *data); + +void spriteSetDestroy(Sprite sprite, SpriteCallback destroy); + #endif