From bb3fab0d73773ecb1983a84f0c54088fd5a96e1a Mon Sep 17 00:00:00 2001 From: Cong Date: Sat, 25 Mar 2017 18:11:01 +1100 Subject: [PATCH] Use Minkowski Hex collision (#372) Move velocity to TileItem --- src/cdogs/actors.c | 41 +++++++++++---------- src/cdogs/actors.h | 1 - src/cdogs/ai.c | 3 +- src/cdogs/ai_utils.c | 5 ++- src/cdogs/bullet_class.c | 55 +++++++++++++++-------------- src/cdogs/collision/collision.c | 21 ++++++----- src/cdogs/collision/collision.h | 4 +-- src/cdogs/collision/minkowski_hex.c | 8 ++--- src/cdogs/handle_game_events.c | 7 ++-- src/cdogs/objs.h | 1 - src/cdogs/tile.c | 2 +- src/cdogs/tile.h | 4 ++- src/game.c | 10 +++--- 13 files changed, 85 insertions(+), 77 deletions(-) diff --git a/src/cdogs/actors.c b/src/cdogs/actors.c index 0be0e7be4..427b4b794 100644 --- a/src/cdogs/actors.c +++ b/src/cdogs/actors.c @@ -230,8 +230,7 @@ bool TryMoveActor(TActor *actor, Vec2i pos) IsPVP(gCampaign.Entry.Mode) }; TTileItem *target = OverlapGetFirstItem( - &actor->tileItem, realPos, Vec2iFull2Real(actor->Vel), - actor->tileItem.size, params); + &actor->tileItem, realPos, actor->tileItem.size, params); if (target) { Weapon *gun = ActorGetGun(actor); @@ -278,15 +277,13 @@ bool TryMoveActor(TActor *actor, Vec2i pos) Vec2i realYPos = Vec2iFull2Real(Vec2iNew(actor->Pos.x, pos.y)); if (OverlapGetFirstItem( - &actor->tileItem, realYPos, Vec2iFull2Real(actor->Vel), - actor->tileItem.size, params)) + &actor->tileItem, realYPos, actor->tileItem.size, params)) { pos.y = actor->Pos.y; } Vec2i realXPos = Vec2iFull2Real(Vec2iNew(pos.x, actor->Pos.y)); if (OverlapGetFirstItem( - &actor->tileItem, realXPos, Vec2iFull2Real(actor->Vel), - actor->tileItem.size, params)) + &actor->tileItem, realXPos, actor->tileItem.size, params)) { pos.x = actor->Pos.x; } @@ -468,9 +465,8 @@ static void CheckPickups(TActor *actor) 0, CalcCollisionTeam(true, actor), IsPVP(gCampaign.Entry.Mode) }; OverlapTileItems( - &actor->tileItem, Vec2iFull2Real(actor->Pos), - Vec2iFull2Real(actor->Vel), actor->tileItem.size, params, - CheckPickupFunc, actor); + &actor->tileItem, Vec2iFull2Real(actor->Pos), actor->tileItem.size, + params, CheckPickupFunc, actor); } static bool CheckPickupFunc(TTileItem *ti, void *data) { @@ -494,7 +490,7 @@ static void CheckRescue(const TActor *a) IsPVP(gCampaign.Entry.Mode) }; const TTileItem *target = OverlapGetFirstItem( - &a->tileItem, Vec2iFull2Real(a->Pos), Vec2iFull2Real(a->Vel), + &a->tileItem, Vec2iFull2Real(a->Pos), Vec2iAdd(a->tileItem.size, Vec2iNew(RESCUE_CHECK_PAD, RESCUE_CHECK_PAD)), params); if (target != NULL && target->kind == KIND_CHARACTER) @@ -885,8 +881,7 @@ void UpdateAllActors(int ticks) IsPVP(gCampaign.Entry.Mode) }; const TTileItem *collidingItem = OverlapGetFirstItem( - &actor->tileItem, realPos, Vec2iFull2Real(actor->Vel), - actor->tileItem.size, params); + &actor->tileItem, realPos, actor->tileItem.size, params); if (collidingItem && collidingItem->kind == KIND_CHARACTER) { TActor *collidingActor = CArrayGet( @@ -930,27 +925,31 @@ static void CheckManualPickups(TActor *a); static void ActorUpdatePosition(TActor *actor, int ticks) { Vec2i newPos = Vec2iAdd(actor->Pos, actor->MoveVel); - if (!Vec2iIsZero(actor->Vel)) + if (!Vec2iIsZero(actor->tileItem.VelFull)) { - newPos = Vec2iAdd(newPos, Vec2iScale(actor->Vel, ticks)); + newPos = Vec2iAdd(newPos, Vec2iScale(actor->tileItem.VelFull, ticks)); for (int i = 0; i < ticks; i++) { - if (actor->Vel.x > 0) + if (actor->tileItem.VelFull.x > 0) { - actor->Vel.x = MAX(0, actor->Vel.x - VEL_DECAY_X); + actor->tileItem.VelFull.x = + MAX(0, actor->tileItem.VelFull.x - VEL_DECAY_X); } else { - actor->Vel.x = MIN(0, actor->Vel.x + VEL_DECAY_X); + actor->tileItem.VelFull.x = + MIN(0, actor->tileItem.VelFull.x + VEL_DECAY_X); } - if (actor->Vel.y > 0) + if (actor->tileItem.VelFull.y > 0) { - actor->Vel.y = MAX(0, actor->Vel.y - VEL_DECAY_Y); + actor->tileItem.VelFull.y = + MAX(0, actor->tileItem.VelFull.y - VEL_DECAY_Y); } else { - actor->Vel.y = MIN(0, actor->Vel.y + VEL_DECAY_Y); + actor->tileItem.VelFull.y = + MIN(0, actor->tileItem.VelFull.y + VEL_DECAY_Y); } } } @@ -973,7 +972,7 @@ static void CheckManualPickups(TActor *a) 0, CalcCollisionTeam(true, a), IsPVP(gCampaign.Entry.Mode) }; OverlapTileItems( - &a->tileItem, Vec2iFull2Real(a->Pos), Vec2iFull2Real(a->Vel), + &a->tileItem, Vec2iFull2Real(a->Pos), a->tileItem.size, params, CheckManualPickupFunc, a); } static bool CheckManualPickupFunc(TTileItem *ti, void *data) diff --git a/src/cdogs/actors.h b/src/cdogs/actors.h index 431f1a87c..b4f36d34e 100644 --- a/src/cdogs/actors.h +++ b/src/cdogs/actors.h @@ -95,7 +95,6 @@ typedef struct Actor Vec2i Pos; // These are the full coordinates, including fractions // Vector that the player is attempting to move in, based on input Vec2i MoveVel; - Vec2i Vel; direction_e direction; // Rotation used to draw the actor, which will lag behind the actual // rotation in order to show smooth rotation diff --git a/src/cdogs/ai.c b/src/cdogs/ai.c index 421729041..123f920a8 100644 --- a/src/cdogs/ai.c +++ b/src/cdogs/ai.c @@ -141,8 +141,7 @@ static bool IsPosOK(TActor *actor, Vec2i pos) IsPVP(gCampaign.Entry.Mode) }; if (OverlapGetFirstItem( - &actor->tileItem, realPos, Vec2iFull2Real(actor->Vel), - actor->tileItem.size, params)) + &actor->tileItem, realPos, actor->tileItem.size, params)) { return false; } diff --git a/src/cdogs/ai_utils.c b/src/cdogs/ai_utils.c index 816b9229f..e55f2b765 100644 --- a/src/cdogs/ai_utils.c +++ b/src/cdogs/ai_utils.c @@ -385,9 +385,8 @@ TObject *AIGetObjectRunningInto(TActor *a, int cmd) TILEITEM_IMPASSABLE, CalcCollisionTeam(true, a), IsPVP(gCampaign.Entry.Mode) }; - item = OverlapGetFirstItem( - &a->tileItem, frontPos, Vec2iFull2Real(a->Vel), a->tileItem.size, - params); + item = + OverlapGetFirstItem(&a->tileItem, frontPos, a->tileItem.size, params); if (!item || item->kind != KIND_OBJECT) { return NULL; diff --git a/src/cdogs/bullet_class.c b/src/cdogs/bullet_class.c index 3bf1a6733..8db6b8ce2 100644 --- a/src/cdogs/bullet_class.c +++ b/src/cdogs/bullet_class.c @@ -95,7 +95,8 @@ static CPicDrawContext GetBulletDrawContext(const int id) const TMobileObject *obj = CArrayGet(&gMobObjs, id); CASSERT(obj->isInUse, "Cannot draw non-existent mobobj"); // Calculate direction based on velocity - const direction_e dir = RadiansToDirection(Vec2iToRadians(obj->vel)); + const direction_e dir = + RadiansToDirection(Vec2iToRadians(obj->tileItem.VelFull)); const Pic *pic = CPicGetPic(&obj->tileItem.CPic, dir); CPicDrawContext c; c.Dir = dir; @@ -171,15 +172,15 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) { for (int i = 0; i < ticks; i++) { - obj->vel = SeekTowards( - objPos, obj->vel, + obj->tileItem.VelFull = SeekTowards( + objPos, obj->tileItem.VelFull, obj->bulletClass->SpeedLow, target->Pos, obj->bulletClass->SeekFactor); } } } - Vec2i pos = Vec2iScale(Vec2iAdd(objPos, obj->vel), ticks); + Vec2i pos = Vec2iScale(Vec2iAdd(objPos, obj->tileItem.VelFull), ticks); HitType hitItem = HIT_NONE; if (!gCampaign.IsClient) { @@ -233,28 +234,29 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) } // Friction - const bool isDiagonal = obj->vel.x != 0 && obj->vel.y != 0; - int frictionComponent = isDiagonal ? + const bool isDiagonal = + obj->tileItem.VelFull.x != 0 && obj->tileItem.VelFull.y != 0; + const int frictionComponent = isDiagonal ? (int)round(obj->bulletClass->Friction / sqrt(2)) : obj->bulletClass->Friction; for (int i = 0; i < ticks; i++) { - if (obj->vel.x > 0) + if (obj->tileItem.VelFull.x > 0) { - obj->vel.x -= frictionComponent; + obj->tileItem.VelFull.x -= frictionComponent; } - else if (obj->vel.x < 0) + else if (obj->tileItem.VelFull.x < 0) { - obj->vel.x += frictionComponent; + obj->tileItem.VelFull.x += frictionComponent; } - if (obj->vel.y > 0) + if (obj->tileItem.VelFull.y > 0) { - obj->vel.y -= frictionComponent; + obj->tileItem.VelFull.y -= frictionComponent; } - else if (obj->vel.y < 0) + else if (obj->tileItem.VelFull.y < 0) { - obj->vel.y += frictionComponent; + obj->tileItem.VelFull.y += frictionComponent; } } @@ -268,7 +270,7 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) { GameEvent b = GameEventNew(GAME_EVENT_BULLET_BOUNCE); b.u.BulletBounce.UID = obj->UID; - if (hitWall && !Vec2iIsZero(obj->vel)) + if (hitWall && !Vec2iIsZero(obj->tileItem.VelFull)) { b.u.BulletBounce.HitType = (int)HIT_WALL; } @@ -289,15 +291,15 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) } } b.u.BulletBounce.BouncePos = Vec2i2Net(pos); - b.u.BulletBounce.BounceVel = Vec2i2Net(obj->vel); - if (hitWall && !Vec2iIsZero(obj->vel)) + b.u.BulletBounce.BounceVel = Vec2i2Net(obj->tileItem.VelFull); + if (hitWall && !Vec2iIsZero(obj->tileItem.VelFull)) { // Bouncing - Vec2i bounceVel = obj->vel; + Vec2i bounceVel = obj->tileItem.VelFull; pos = GetWallBounceFullPos(objPos, pos, &bounceVel); b.u.BulletBounce.BouncePos = Vec2i2Net(pos); b.u.BulletBounce.BounceVel = Vec2i2Net(bounceVel); - obj->vel = bounceVel; + obj->tileItem.VelFull = bounceVel; } GameEventsEnqueue(&gGameEvents, b); if (!alive) @@ -317,8 +319,8 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) { for (int i = 0; i < ticks; i++) { - obj->vel.x += ((rand() % 3) - 1) * 128; - obj->vel.y += ((rand() % 3) - 1) * 128; + obj->tileItem.VelFull.x += ((rand() % 3) - 1) * 128; + obj->tileItem.VelFull.y += ((rand() % 3) - 1) * 128; } } @@ -356,7 +358,7 @@ bool UpdateBullet(TMobileObject *obj, const int ticks) static void FireGuns(const TMobileObject *obj, const CArray *guns) { const Vec2i fullPos = Vec2iNew(obj->x, obj->y); - const double angle = Vec2iToRadians(obj->vel); + const double angle = Vec2iToRadians(obj->tileItem.VelFull); for (int i = 0; i < (int)guns->size; i++) { const GunDescription **g = CArrayGet(guns, i); @@ -393,7 +395,7 @@ static HitType HitItem( TILEITEM_CAN_BE_SHOT, COLLISIONTEAM_NONE, IsPVP(gCampaign.Entry.Mode) }; OverlapTileItems( - &obj->tileItem, Vec2iFull2Real(pos), Vec2iFull2Real(obj->vel), + &obj->tileItem, Vec2iFull2Real(pos), obj->tileItem.size, params, HitItemFunc, &data); return data.HitType; } @@ -409,7 +411,7 @@ static bool HitItemFunc(TTileItem *ti, void *data) int targetUID = -1; hData->HitType = GetHitType(ti, hData->Obj, &targetUID); Damage( - hData->Obj->vel, + hData->Obj->tileItem.VelFull, hData->Obj->bulletClass->Power, hData->Obj->bulletClass->Mass, hData->Obj->flags, hData->Obj->PlayerUID, hData->Obj->ActorUID, ti->kind, targetUID, @@ -774,12 +776,13 @@ void BulletAdd(const NAddBullet add) obj->z = add.MuzzleHeight; obj->dz = add.Elevation; - obj->vel = Vec2iFull2Real(Vec2iScale( + obj->tileItem.VelFull = Vec2iFull2Real(Vec2iScale( GetFullVectorsForRadians(add.Angle), RAND_INT(obj->bulletClass->SpeedLow, obj->bulletClass->SpeedHigh))); if (obj->bulletClass->SpeedScale) { - obj->vel.y = obj->vel.y * TILE_WIDTH / TILE_HEIGHT; + obj->tileItem.VelFull.y = + obj->tileItem.VelFull.y * TILE_WIDTH / TILE_HEIGHT; } obj->PlayerUID = add.PlayerUID; diff --git a/src/cdogs/collision/collision.c b/src/cdogs/collision/collision.c index 5864a5603..0fc545d94 100644 --- a/src/cdogs/collision/collision.c +++ b/src/cdogs/collision/collision.c @@ -51,6 +51,7 @@ #include "actors.h" #include "algorithms.h" #include "config.h" +#include "minkowski_hex.h" #define TILE_CACHE_TILE 1 #define TILE_CACHE_ADJACENT 2 @@ -287,11 +288,11 @@ static bool CheckParams( static void AddPosToTileCache(void *data, Vec2i pos); static bool CheckOverlaps( - const TTileItem *item, const Vec2i pos, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, const CollisionParams params, CollideItemFunc func, void *data, const CArray *tileThings); void OverlapTileItems( - const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i size, const CollisionParams params, CollideItemFunc func, void *data) { TileCacheReset(&gCollisionSystem.tileCache); @@ -299,13 +300,14 @@ void OverlapTileItems( AlgoLineDrawData drawData; drawData.Draw = AddPosToTileCache; drawData.data = &gCollisionSystem.tileCache; + const Vec2i vel = Vec2iFull2Real(item->VelFull); BresenhamLineDraw(pos, Vec2iAdd(pos, vel), &drawData); // Check collisions with all tiles in the cache - // TODO: use Minkowski-Hex for CCD CA_FOREACH(const Vec2i, dtv, gCollisionSystem.tileCache) const CArray *tileThings = &MapGetTile(&gMap, *dtv)->things; - if (!CheckOverlaps(item, pos, size, params, func, data, tileThings)) + if (!CheckOverlaps( + item, pos, vel, size, params, func, data, tileThings)) { return; } @@ -318,7 +320,7 @@ static void AddPosToTileCache(void *data, Vec2i pos) TileCacheAdd(tileCache, tv); } static bool CheckOverlaps( - const TTileItem *item, const Vec2i pos, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, const CollisionParams params, CollideItemFunc func, void *data, const CArray *tileThings) { @@ -328,7 +330,10 @@ static bool CheckOverlaps( { continue; } - if (!AABBOverlap(pos, Vec2iNew(ti->x, ti->y), size, ti->size)) + // TODO: use collision points + if (!MinkowskiHexCollide( + pos, vel, size, Vec2iNew(ti->x, ti->y), ti->VelFull, ti->size, + NULL, NULL)) { continue; } @@ -343,12 +348,12 @@ static bool CheckOverlaps( static bool OverlapGetFirstItemCallback(TTileItem *ti, void *data); TTileItem *OverlapGetFirstItem( - const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i size, const CollisionParams params) { TTileItem *firstItem = NULL; OverlapTileItems( - item, pos, vel, size, params, OverlapGetFirstItemCallback, &firstItem); + item, pos, size, params, OverlapGetFirstItemCallback, &firstItem); return firstItem; } static bool OverlapGetFirstItemCallback(TTileItem *ti, void *data) diff --git a/src/cdogs/collision/collision.h b/src/cdogs/collision/collision.h index b8e3069dc..ef73ae1f9 100644 --- a/src/cdogs/collision/collision.h +++ b/src/cdogs/collision/collision.h @@ -95,11 +95,11 @@ bool IsCollisionDiamond(const Map *map, const Vec2i pos, const Vec2i fullSize); // The callback returns bool continue, as multiple callbacks can result. typedef bool (*CollideItemFunc)(TTileItem *, void *); void OverlapTileItems( - const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i size, const CollisionParams params, CollideItemFunc func, void *data); // Get the first TTileItem that overlaps TTileItem *OverlapGetFirstItem( - const TTileItem *item, const Vec2i pos, const Vec2i vel, const Vec2i size, + const TTileItem *item, const Vec2i pos, const Vec2i size, const CollisionParams params); bool AABBOverlap( diff --git a/src/cdogs/collision/minkowski_hex.c b/src/cdogs/collision/minkowski_hex.c index c0016c60a..320eb83d6 100644 --- a/src/cdogs/collision/minkowski_hex.c +++ b/src/cdogs/collision/minkowski_hex.c @@ -53,14 +53,14 @@ bool MinkowskiHexCollide( // If intersection is at the start, it means we were overlapping already if (s == 0) { - *collideA = posA; - *collideB = posB; + if (collideA != NULL) *collideA = posA; + if (collideB != NULL) *collideB = posB; } else { // Find the actual intersection points based on the result - *collideA = Vec2iAdd(posA, Vec2iScaleD(velA, s)); - *collideB = Vec2iAdd(posB, Vec2iScaleD(velB, s)); + if (collideA != NULL) *collideA = Vec2iAdd(posA, Vec2iScaleD(velA, s)); + if (collideB != NULL) *collideB = Vec2iAdd(posB, Vec2iScaleD(velB, s)); } return true; diff --git a/src/cdogs/handle_game_events.c b/src/cdogs/handle_game_events.c index 09d4255ee..45acab06a 100644 --- a/src/cdogs/handle_game_events.c +++ b/src/cdogs/handle_game_events.c @@ -206,7 +206,7 @@ static void HandleGameEvent( { TActor *a = ActorGetByUID(e.u.ActorSlide.UID); if (!a->isInUse) break; - a->Vel = Net2Vec2i(e.u.ActorSlide.Vel); + a->tileItem.VelFull = Net2Vec2i(e.u.ActorSlide.Vel); // Slide sound if (ConfigGetBool(&gConfig, "Sound.Footsteps")) { @@ -220,7 +220,8 @@ static void HandleGameEvent( { TActor *a = ActorGetByUID(e.u.ActorImpulse.UID); if (!a->isInUse) break; - a->Vel = Vec2iAdd(a->Vel, Net2Vec2i(e.u.ActorImpulse.Vel)); + a->tileItem.VelFull = + Vec2iAdd(a->tileItem.VelFull, Net2Vec2i(e.u.ActorImpulse.Vel)); const Vec2i pos = Net2Vec2i(e.u.ActorImpulse.Pos); if (!Vec2iIsZero(pos)) { @@ -379,7 +380,7 @@ static void HandleGameEvent( } o->x = pos.x; o->y = pos.y; - o->vel = Net2Vec2i(e.u.BulletBounce.BounceVel); + o->tileItem.VelFull = Net2Vec2i(e.u.BulletBounce.BounceVel); } break; case GAME_EVENT_REMOVE_BULLET: diff --git a/src/cdogs/objs.h b/src/cdogs/objs.h index 7cd2e366c..d30de2cf9 100644 --- a/src/cdogs/objs.h +++ b/src/cdogs/objs.h @@ -81,7 +81,6 @@ typedef struct MobileObject // (prevent self collision) const BulletClass *bulletClass; int x, y, z; - Vec2i vel; int dz; int count; int range; diff --git a/src/cdogs/tile.c b/src/cdogs/tile.c index ad4b2ef95..746d34ad5 100644 --- a/src/cdogs/tile.c +++ b/src/cdogs/tile.c @@ -22,7 +22,7 @@ This file incorporates work covered by the following copyright and permission notice: - Copyright (c) 2013-2014, 2016 Cong Xu + Copyright (c) 2013-2014, 2016-2017 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/cdogs/tile.h b/src/cdogs/tile.h index 4dbdb5608..1db9dd99a 100644 --- a/src/cdogs/tile.h +++ b/src/cdogs/tile.h @@ -22,7 +22,7 @@ This file incorporates work covered by the following copyright and permission notice: - Copyright (c) 2013-2016, Cong Xu + Copyright (c) 2013-2017 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -116,6 +116,8 @@ typedef void (*TileItemDrawFunc)(const Vec2i, const TileItemDrawFuncData *); typedef struct TileItem { int x, y; + // Velocity in full coordinates + Vec2i VelFull; Vec2i size; TileItemKind kind; int id; // Id of item (actor, mobobj or obj) diff --git a/src/game.c b/src/game.c index 0ab2075bf..6a7e1833a 100644 --- a/src/game.c +++ b/src/game.c @@ -521,19 +521,21 @@ static GameLoopResult RunGameUpdate(void *data) const TActor *p = ActorGetByUID(pd->ActorUID); const int pad = CAMERA_SPLIT_PADDING; Vec2i vel = Vec2iZero(); - if (screen.x + pad > p->tileItem.x && p->Vel.x < 256) + if (screen.x + pad > p->tileItem.x && p->tileItem.VelFull.x < 256) { vel.x = screen.x + pad - p->tileItem.x; } - else if (screen.x + w - pad < p->tileItem.x && p->Vel.x > -256) + else if (screen.x + w - pad < p->tileItem.x && + p->tileItem.VelFull.x > -256) { vel.x = screen.x + w - pad - p->tileItem.x; } - if (screen.y + pad > p->tileItem.y && p->Vel.y < 256) + if (screen.y + pad > p->tileItem.y && p->tileItem.VelFull.y < 256) { vel.y = screen.y + pad - p->tileItem.y; } - else if (screen.y + h - pad < p->tileItem.y && p->Vel.y > -256) + else if (screen.y + h - pad < p->tileItem.y + && p->tileItem.VelFull.y > -256) { vel.y = screen.y + h - pad - p->tileItem.y; }