diff --git a/demo/src/main.c b/demo/src/main.c index 5965b68..95331e2 100644 --- a/demo/src/main.c +++ b/demo/src/main.c @@ -2,45 +2,55 @@ #include Sprite ball, brick1, brick2; +Group edges; +Group movement; Sound beep; void init() { ball = createSpriteEx(200, 200, 64, 64); spriteSetAnimation(ball, "data/sprite/breadball.png"); + spriteSetColliderCircle(ball, 0, 0, -1); + spriteSetRotateToDirection(ball, TRUE); brick1 = createSpriteEx(200-32, 200+64, 64, 64); spriteSetAnimation(brick1, "data/sprite/bricks.png"); spriteSetTintColor(brick1, rgb(0, 0, 1)); + spriteSetColliderRectangle(brick1, 0, 0, 0, -1, -1, 20); + spriteSetBounciness(brick1, 0.5); brick2 = createSpriteEx(200+32, 200+64, 64, 64); spriteSetAnimation(brick2, "data/sprite/bricks.png"); spriteSetTintColor(brick2, rgba(1, 0, 1, 0.5)); + spriteSetColliderRectangle(brick2, 0, 0, 0, -1, -1, 20); + + movement = createGroup(); + groupAdd(movement, ball); + groupAdd(movement, brick1); + groupAdd(movement, brick2); + edges = createEdgesGroup(); beep = createSound("data/sound/beep.ogg"); } void draw() { double dt = worldGetTimeStep(); - double speed = 100; - - spritePointTo(ball, mouseX(), mouseY()); + double accel = 100; - double x = spriteGetX(ball); - double y = spriteGetY(ball); - if (keyDown("left")) spriteSetX(ball, x - speed*dt); - if (keyDown("right")) spriteSetX(ball, x + speed*dt); - if (keyDown("up")) spriteSetY(ball, y - speed*dt); - if (keyDown("down")) spriteSetY(ball, y + speed*dt); - - spriteSetSpeedAndDirection(ball, speed/2, spriteGetRotation(ball)); + double vx = spriteGetVelocityX(ball); + double vy = spriteGetVelocityY(ball); + if (keyDown("left")) spriteSetVelocityX(ball, vx - accel*dt); + if (keyDown("right")) spriteSetVelocityX(ball, vx + accel*dt); + if (keyDown("up")) spriteSetVelocityY(ball, vy - accel*dt); + if (keyDown("down")) spriteSetVelocityY(ball, vy + accel*dt); if (mouseWentDown("left")) soundPlay(beep, FALSE); - spriteCollide(ball, brick1, 1); - spriteCollide(ball, brick2, 1); - spriteCollide(brick2, brick1, 1); + groupCollideBetween(movement, 1); + groupPushGroup(edges, movement, 1); drawSprites(); + + point(mouseX(), mouseY()); } int main() { diff --git a/src/SConstruct b/src/SConstruct index 7feccb8..1663720 100644 --- a/src/SConstruct +++ b/src/SConstruct @@ -25,7 +25,7 @@ libs = ['gtk+-3.0', 'glib-2.0', 'cairo', 'SDL2_mixer'] flags = ' -lm -Wall -fmessage-length=0 ' if int(DEBUG): - flags += ' -O0 -g ' + flags += ' -O0 -g -fdebug-prefix-map=src=../src' else: flags += ' -O3 ' @@ -93,7 +93,8 @@ pcdict = { } pcfile = env.Substfile(name + '.pc.in', SUBST_DICT = pcdict) -env.Install(idir_lib, [static_library, shared_library]) +env.Install(idir_lib, static_library) +env.InstallVersionedLib(idir_lib, shared_library) env.Install(idir_lib + '/pkgconfig', pcfile) env.Install(idir_inc + '/' + name + '/' + name, headers) env.Install(idir_inc + '/' + name, root_headers) diff --git a/src/collider.c b/src/collider.c index d75f7e3..4113881 100644 --- a/src/collider.c +++ b/src/collider.c @@ -71,14 +71,14 @@ static void collision(HeliCollisionData *data) { } else { // circle with line double div = B*data->dirx - A*data->diry; - if (fabs(div) > HELI_PRECISION) { + if (fabs(div) > HELI_PRECISION_SQR) { double mult = 1/div; double kn = 1/sqrt(AB2); double nx = -B*kn; double ny = A*kn; - double kx = nx*fabs(data->radius) - data->x1; - double ky = ny*fabs(data->radius) - data->y1; + double kx = -nx*fabs(data->radius) - data->x1; + double ky = -ny*fabs(data->radius) - data->y1; double ll = (ky*data->dirx - kx*data->diry)*mult; if (ll >= -HELI_PRECISION && ll <= 1 + HELI_PRECISION) { @@ -94,10 +94,10 @@ static void calcCorners(HeliCollider *c, double *corners) { double cn = cos(a); double sn = sin(a); - double wx = cn*fabs(c->width); - double wy = sn*fabs(c->width); - double hx = -sn*fabs(c->height); - double hy = cn*fabs(c->height); + double wx = 0.5*cn*fabs(c->width); + double wy = 0.5*sn*fabs(c->width); + double hx = -0.5*sn*fabs(c->height); + double hy = 0.5*cn*fabs(c->height); corners[0] = wx + hx + c->x; corners[1] = wy + hy + c->y; @@ -115,10 +115,10 @@ static void calcCorners(HeliCollider *c, double *corners) { static void collisionCorners(HeliCollisionData *data, double *corners, double x, double y) { for(int i = 0; i < 4; ++i) { int j = (i + 1)%4; - data->x1 = corners[i+0] - x; - data->y1 = corners[i+1] - y; - data->x2 = corners[j+0] - x; - data->y2 = corners[j+1] - y; + data->x1 = corners[2*i+0] - x; + data->y1 = corners[2*i+1] - y; + data->x2 = corners[2*j+0] - x; + data->y2 = corners[2*j+1] - y; collision(data); data->x2 = data->x1; data->y2 = data->y1; @@ -136,16 +136,10 @@ int heliCheckCollision( HeliCollisionData data = {}; // calc "moving" direction - data.dirx = *vx; - data.diry = *vy; + data.dirx = b->x - a->x; + data.diry = b->y - a->y; double k = data.dirx*data.dirx + data.diry*data.diry; if (k <= HELI_PRECISION_SQR) { - data.dirx = b->x - a->x; - data.diry = b->y - a->y; - k = data.dirx*data.dirx + data.diry*data.diry; - } - - if (k <= HELI_PRECISION_SQR) { data.dirx = 1; data.diry = 0; } else { @@ -180,10 +174,10 @@ int heliCheckCollision( calcCorners(b, cornersB); data.radius = fabs(a->radius) + fabs(b->radius); for(int i = 0; i < 4; ++i) - collisionCorners(&data, cornersB, cornersA[i+0], cornersA[i+1]); + collisionCorners(&data, cornersB, cornersA[2*i+0], cornersA[2*i+1]); data.flip = TRUE; for(int i = 0; i < 4; ++i) - collisionCorners(&data, cornersA, cornersB[i+0], cornersB[i+1]); + collisionCorners(&data, cornersA, cornersB[2*i+0], cornersB[2*i+1]); } // is collision found? @@ -195,8 +189,8 @@ int heliCheckCollision( // calc displacement and velocity double l, nx, ny; - if (-data.lmin > data.lmax) { - l = -data.lmin; + if (-data.lmin < data.lmax) { + l = data.lmin; nx = data.nxMin; ny = data.nyMin; } else { @@ -205,8 +199,10 @@ int heliCheckCollision( ny = data.nyMax; } - *dx = data.dirx*l; - *dy = data.diry*l; + 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 -= 2*kn*fabs(bounciness)*nx; diff --git a/src/group.c b/src/group.c index 8bef81b..6abcd42 100644 --- a/src/group.c +++ b/src/group.c @@ -25,15 +25,15 @@ Group createEdgesGroupEx(double x1, double y1, double x2, double y2, double bord y1 -= b; Group g = createGroup(); - groupAdd(g, createSpriteEx(x1, y1, w, b)); - groupAdd(g, createSpriteEx(x1, y2, w, b)); - groupAdd(g, createSpriteEx(x1, y1, b, h)); - groupAdd(g, createSpriteEx(x2, y1, b, h)); + groupAdd(g, createSpriteEx(x1 + w/2, y1 + b/2, w, b)); + 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)); return g; } Group createEdgesGroup() - { return createEdgesGroupEx(0, 0, worldGetWidth(), worldGetHeight(), 10); } + { return createEdgesGroupEx(0, 0, worldGetWidth(), worldGetHeight(), 100); } void groupDestroy(Group group) { @@ -120,6 +120,20 @@ int groupCollideGroupEx(Group a, Group b, int keepVelocityA, int keepVelocityB, 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 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)) + result = TRUE; + return result; +} + + double groupGetMinDepth(Group group) { if (groupGetCount(group) <= 0) return 0; double md = spriteGetDepth(groupGet(group, 0)); @@ -205,13 +219,27 @@ void groupSetVelocityEach(Group group, double x, double y) { spriteSetVelocityXY(groupGet(group, i), x, y); } -void groupSetColliderEachEx(Group group, Collider type, double xOffset, double yOffset, - double widthOrRadius, double height, double rotationOffset) +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) + { groupSetColliderEachEx(group, COLLIDER_CIRCLE, xOffset, yOffset, 0, 0, 0, radius); } +void groupSetColliderRectangleEach( + Group group, double xOffset, double yOffset, double rotationOffset, + double width, double height, double cornersRadius ) +{ + groupSetColliderEachEx( + group, COLLIDER_RECTANGLE, + xOffset, yOffset, rotationOffset, + width, height, cornersRadius); +} +void groupSetColliderEachEx( + Group group, Collider type, + double xOffset, double yOffset, double rotationOffset, + double width, double height, double radius) { for(int i = 0; i < groupGetCount(group); ++i) - spriteSetColliderEx(groupGet(group, i), type, xOffset, yOffset, - widthOrRadius, height, rotationOffset); + spriteSetColliderEx( + groupGet(group, i), type, + xOffset, yOffset, rotationOffset, + width, height, radius ); } - -void groupSetColliderEach(Group group, Collider type, double xOffset, double yOffset) - { groupSetColliderEachEx(group, type, xOffset, yOffset, -1, -1, 0); } diff --git a/src/group.h b/src/group.h index 124176f..4506168 100644 --- a/src/group.h +++ b/src/group.h @@ -35,6 +35,10 @@ 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); + double groupGetMinDepth(Group group); double groupGetMaxDepth(Group group); @@ -62,9 +66,16 @@ void groupSetAnimationEach(Group group, const char *path); void groupSetNoAnimationEach(Group group); void groupSetShapeColorEach(Group group, const char *color); void groupSetTintColorEach(Group group, const char *color); -void groupSetColliderEach(Group group, Collider type, double xOffset, double yOffset); -void groupSetColliderEachEx(Group group, Collider type, double xOffset, double yOffset, - double widthOrRadius, double height, double rotationOffset); + +void groupSetColliderEach(Group group, Collider type, double xOffset, double yOffset, double rotationOffset); +void groupSetColliderCircleEach(Group group, double xOffset, double yOffset, double radius); +void groupSetColliderRectangleEach( + Group group, double xOffset, double yOffset, double rotationOffset, + double width, double height, double cornersRadius ); +void groupSetColliderEachEx( + Group group, Collider type, + double xOffset, double yOffset, double rotationOffset, + double width, double height, double radius); #endif diff --git a/src/sprite.c b/src/sprite.c index c1fd971..c2ac2cb 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -55,17 +55,40 @@ static void prepareCollider(Sprite sprite, HeliCollider *collider) { collider->radius = sprite->collider.radius; collider->rotation = sprite->collider.rotation + sprite->rotation; - if (collider->width < 0) collider->width = sprite->width; - if (collider->height < 0) collider->height = sprite->height; - if (collider->radius < 0) collider->radius = sprite->width; + // auto-size + if (collider->type == COLLIDER_RECTANGLE) { + if (collider->width < -HELI_PRECISION) collider->width = sprite->width; + if (collider->height < -HELI_PRECISION) collider->height = sprite->height; + if (collider->radius < -HELI_PRECISION) collider->radius = 0; + } else + if (collider->type == COLLIDER_CIRCLE) { + collider->width = 0; + collider->height = 0; + if (collider->radius < -HELI_PRECISION) collider->radius = 0.5*sprite->width; + } + // scale collider->width *= sprite->scale; collider->height *= sprite->scale; collider->radius *= sprite->scale; + + // fix small values + if (collider->width < HELI_PRECISION) collider->width = HELI_PRECISION; + if (collider->height < HELI_PRECISION) collider->height = HELI_PRECISION; + if (collider->radius < HELI_PRECISION) collider->radius = HELI_PRECISION; + + // fix round corners + if (collider->type == COLLIDER_RECTANGLE) { + double rmax = 0.5*( collider->width < collider->height + ? collider->width : collider->height ); + if (collider->radius >= rmax - HELI_PRECISION) + collider->radius = rmax; + collider->width -= 2*collider->radius; + collider->height -= 2*collider->radius; + } } - Sprite createSpriteEx(double x, double y, double width, double height) { Sprite s = calloc(1, sizeof(*s)); s->x = x; @@ -75,7 +98,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 = s->collider.radius = -1; + s->collider.width = s->collider.height = -1; + s->bounciness = 1; s->mirrorX = 1; s->mirrorY = 1; @@ -190,6 +214,7 @@ 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) { + if (a == b) return FALSE; HeliCollider ca, cb; prepareCollider(a, &ca); prepareCollider(b, &cb); @@ -203,14 +228,12 @@ int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, do if ( keepVelocityA && !keepVelocityB) { b->x -= dx; b->y -= dy; - b->vx = a->vx - vx; - b->vy = a->vy - vy; + spriteSetVelocityXY(b, a->vx - vx, a->vy - vy); } else if (!keepVelocityA && keepVelocityB) { a->x += dx; a->y += dy; - a->vx = b->vx + vx; - a->vy = b->vy + vy; + spriteSetVelocityXY(a, b->vx + vx, b->vy + vy); } else if (!keepVelocityA && !keepVelocityB) { double vxAvg = 0.5*(a->vx + b->vx); @@ -218,13 +241,11 @@ int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, do a->x += 0.5*dx; a->y += 0.5*dy; - a->vx = vxAvg + 0.5*vx; - a->vy = vyAvg + 0.5*vy; + spriteSetVelocityXY(a, vxAvg + 0.5*vx, vyAvg + 0.5*vy); b->x -= 0.5*dx; b->y -= 0.5*dy; - b->vx = vxAvg - 0.5*vx; - b->vy = vyAvg - 0.5*vy; + spriteSetVelocityXY(b, vxAvg - 0.5*vx, vyAvg - 0.5*vy); } return TRUE; @@ -235,20 +256,45 @@ double spriteGetBounciness(Sprite sprite) { return sprite->bounciness; } void spriteSetBounciness(Sprite sprite, double bounciness) { sprite->bounciness = bounciness > 0 ? bounciness : 0; } -void spriteSetColliderEx(Sprite sprite, Collider type, double xOffset, double yOffset, - double widthOrRadius, double height, double rotationOffset) +void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset) + { spriteSetColliderEx(sprite, type, xOffset, yOffset, rotationOffset, -1, -1, -1); } +void spriteSetColliderCircle(Sprite sprite, double xOffset, double yOffset, double radius) + { spriteSetColliderEx(sprite, COLLIDER_CIRCLE, xOffset, yOffset, 0, 0, 0, radius); } +void spriteSetColliderRectangle( + Sprite sprite, double xOffset, double yOffset, double rotationOffset, + double width, double height, double cornersRadius ) +{ + spriteSetColliderEx( + sprite, COLLIDER_RECTANGLE, + xOffset, yOffset, rotationOffset, + width, height, cornersRadius); +} + +void spriteSetColliderEx( + Sprite sprite, Collider type, + double xOffset, double yOffset, double rotationOffset, + double width, double height, double radius ) { sprite->collider.type = type; sprite->collider.x = xOffset; sprite->collider.y = yOffset; - sprite->collider.width = sprite->collider.radius = widthOrRadius; - sprite->collider.height = height; sprite->collider.rotation = rotationOffset; + if (type == COLLIDER_CIRCLE) { + sprite->collider.width = 0; + sprite->collider.height = 0; + sprite->collider.radius = radius; + } else + if (type == COLLIDER_RECTANGLE) { + sprite->collider.width = width; + sprite->collider.height = height; + sprite->collider.radius = radius; + } else { + sprite->collider.width = 0; + sprite->collider.height = 0; + sprite->collider.radius = 0; + } } -void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset) - { spriteSetColliderEx(sprite, type, xOffset, yOffset, -1, -1, 0); } - void spriteSetAnimation(Sprite sprite, const char *path) { HeliAnimation *prev = sprite->animation; sprite->animation = heliAnimationLoad(path); diff --git a/src/sprite.h b/src/sprite.h index a3291a2..d948bcc 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -76,9 +76,15 @@ int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, do double spriteGetBounciness(Sprite sprite); void spriteSetBounciness(Sprite sprite, double bounciness); -void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset); -void spriteSetColliderEx(Sprite sprite, Collider type, double xOffset, double yOffset, - double widthOrRadius, double height, double rotationOffset); +void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset); +void spriteSetColliderCircle(Sprite sprite, double xOffset, double yOffset, double radius); +void spriteSetColliderRectangle( + Sprite sprite, double xOffset, double yOffset, double rotationOffset, + double width, double height, double cornersRadius ); +void spriteSetColliderEx( + Sprite sprite, Collider type, + double xOffset, double yOffset, double rotationOffset, + double width, double height, double radius); void spriteSetAnimation(Sprite sprite, const char *path); void spriteSetNoAnimation(Sprite sprite);