From 79b8f956f78e156241ffa6cfb98582c6875ccd32 Mon Sep 17 00:00:00 2001 From: outfrost Date: Thu, 16 Jul 2020 03:15:20 +0200 Subject: [PATCH] Implement first stage of obstacle detection --- src/engine/geometry.c | 9 ++++ src/engine/geometry.h | 1 + src/engine/render.c | 4 +- src/game/level.c | 61 ++++++++++++++++++++++++--- src/game/level.h | 29 ++++++++++--- src/game/player.c | 97 ++++++++++++++++++++++++++++++++++++++----- src/game/player.h | 1 + 7 files changed, 181 insertions(+), 21 deletions(-) diff --git a/src/engine/geometry.c b/src/engine/geometry.c index ae224b1..c37eb4a 100644 --- a/src/engine/geometry.c +++ b/src/engine/geometry.c @@ -78,6 +78,15 @@ Vector scaleVector(Vector vec, float scale) { vec.z * scale }; } +Vector growVectorNoFlip(Vector vec, float amount) { + float mag = magnitude(vec); + float factor = (mag + amount) / mag; + if (factor < 0.0f) { + factor = 0.0f; + } + return scaleVector(vec, factor); +} + Vector clampMagnitude(Vector vec, float maxMagnitude) { float m = magnitude(vec); if (m > maxMagnitude) { diff --git a/src/engine/geometry.h b/src/engine/geometry.h index e23a75a..38dd5e6 100644 --- a/src/engine/geometry.h +++ b/src/engine/geometry.h @@ -30,6 +30,7 @@ Vector subtractVectors(Vector v1, Vector v2); Vector crossProduct(Vector v1, Vector v2); float dotProduct(Vector v1, Vector v2); Vector scaleVector(Vector vec, float scale); +Vector growVectorNoFlip(Vector vec, float amount); Vector clampMagnitude(Vector vec, float maxMagnitude); float magnitude(Vector vec); Vector applyTransform(Transform transform, Vector vec); diff --git a/src/engine/render.c b/src/engine/render.c index 4188755..616b971 100644 --- a/src/engine/render.c +++ b/src/engine/render.c @@ -3,6 +3,8 @@ #include "geometry.h" #include "performance.h" +#include "game/player.h" + float viewportAspectRatio = 1.0f; const Scene* cameraAnchor; bool debugScene = false; @@ -67,7 +69,7 @@ static void renderScene(const Scene* scene, const Transform baseTransform) { glDisable(GL_LIGHTING); - if (debugScene) { + if (debugScene || scene == playerProjectedMovement) { drawAxes(); } diff --git a/src/game/level.c b/src/game/level.c index c814e4e..b9a6b58 100644 --- a/src/game/level.c +++ b/src/game/level.c @@ -11,11 +11,9 @@ BlockGrid levelGrid; -static const float BLOCKGRID_CELL_SIZE = 2.5f; - static Block blockEmpty = { .type = BLOCKTYPE_SPACE, .solid = NULL }; -static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE, +static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z, .solid = NULL }; static Transform playerSpawnTransform; @@ -106,8 +104,61 @@ static inline size_t nonNegative(long n) { return n < 0 ? 0u : n; } -GridLocation gridLocationFromTransform(Transform transform) { - Vector scaledPos = scaleVector(translationOf(transform), 1.0f / BLOCKGRID_CELL_SIZE); +GridLocation gridLocationFromPosition(Vector pos) { + Vector scaledPos = scaleVector(pos, 1.0f / BLOCKGRID_CELL_SIZE); return (GridLocation) { .x = nonNegative(scaledPos.x), .z = nonNegative(scaledPos.z) }; } + +Obstacle getObstacles(GridLocation loc) { + Obstacle result = OBSTACLE_NONE; + if (loc.x == 0) { + result |= OBSTACLE_XN | OBSTACLE_XN_ZP | OBSTACLE_XN_ZN; + } + if (loc.x >= levelGrid.width - 1) { + result |= OBSTACLE_XP | OBSTACLE_XP_ZP | OBSTACLE_XP_ZN; + } + if (loc.z == 0) { + result |= OBSTACLE_ZN | OBSTACLE_XP_ZN | OBSTACLE_XN_ZN; + } + if (loc.z >= levelGrid.depth - 1) { + result |= OBSTACLE_ZP | OBSTACLE_XP_ZP | OBSTACLE_XN_ZP; + } + if (!(result & OBSTACLE_XP) + && getBlockFromGrid(levelGrid, loc.x + 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) { + result |= OBSTACLE_XP; + } + if (!(result & OBSTACLE_XN) + && getBlockFromGrid(levelGrid, loc.x - 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) { + result |= OBSTACLE_XN; + } + if (!(result & OBSTACLE_ZP) + && getBlockFromGrid(levelGrid, loc.x, loc.z + 1)->type & BLOCKTYPE_OBSTACLE_Z) { + result |= OBSTACLE_ZP; + } + if (!(result & OBSTACLE_ZN) + && getBlockFromGrid(levelGrid, loc.x, loc.z - 1)->type & BLOCKTYPE_OBSTACLE_Z) { + result |= OBSTACLE_ZN; + } + if (!(result & OBSTACLE_XP_ZP) + && getBlockFromGrid(levelGrid, loc.x + 1, loc.z + 1)->type + & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) { + result |= OBSTACLE_XP_ZP; + } + if (!(result & OBSTACLE_XP_ZN) + && getBlockFromGrid(levelGrid, loc.x + 1, loc.z - 1)->type + & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) { + result |= OBSTACLE_XP_ZN; + } + if (!(result & OBSTACLE_XN_ZP) + && getBlockFromGrid(levelGrid, loc.x - 1, loc.z + 1)->type + & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) { + result |= OBSTACLE_XN_ZP; + } + if (!(result & OBSTACLE_XN_ZN) + && getBlockFromGrid(levelGrid, loc.x - 1, loc.z - 1)->type + & (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) { + result |= OBSTACLE_XN_ZN; + } + return result; +} diff --git a/src/game/level.h b/src/game/level.h index 3ce8241..cca8a8e 100644 --- a/src/game/level.h +++ b/src/game/level.h @@ -6,13 +6,25 @@ #include "engine/asset.h" enum BlockType { - BLOCKTYPE_SPACE, - BLOCKTYPE_OBSTACLE_X, - BLOCKTYPE_OBSTACLE_Z, - BLOCKTYPE_OBSTACLE + BLOCKTYPE_SPACE = 0, + BLOCKTYPE_OBSTACLE_X = 1 << 0, + BLOCKTYPE_OBSTACLE_Z = 1 << 1 +}; + +enum Obstacle { + OBSTACLE_NONE = 0, + OBSTACLE_XP = 1 << 0, + OBSTACLE_XN = 1 << 1, + OBSTACLE_ZP = 1 << 2, + OBSTACLE_ZN = 1 << 3, + OBSTACLE_XP_ZP = 1 << 4, + OBSTACLE_XP_ZN = 1 << 5, + OBSTACLE_XN_ZP = 1 << 6, + OBSTACLE_XN_ZN = 1 << 7 }; typedef enum BlockType BlockType; +typedef enum Obstacle Obstacle; typedef struct Block Block; typedef struct BlockGrid BlockGrid; typedef struct GridLocation GridLocation; @@ -33,11 +45,14 @@ struct GridLocation { size_t z; }; +static const float BLOCKGRID_CELL_SIZE = 2.5f; + extern BlockGrid levelGrid; void initLevel(); void startLevel(); -GridLocation gridLocationFromTransform(Transform transform); +GridLocation gridLocationFromPosition(Vector pos); +Obstacle getObstacles(GridLocation loc); static inline Block* getBlockFromGrid(BlockGrid grid, size_t x, size_t z) { return grid.blocks[(z * grid.width) + x]; @@ -47,4 +62,8 @@ static inline void setBlockInGrid(BlockGrid grid, size_t x, size_t z, Block* blo grid.blocks[(z * grid.width) + x] = block; } +static inline float cellBoundaryCoord(size_t cellIndex) { + return cellIndex * BLOCKGRID_CELL_SIZE; +} + #endif // LEVEL_H_ diff --git a/src/game/player.c b/src/game/player.c index f5dcac7..636b130 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -5,9 +5,11 @@ #include "level.h" -static const float movementSpeed = 2.5f; +static const float movementSpeed = 0.5f; +static const float collisionRadius = 0.5f; Scene* playerCharacter; +Scene* playerProjectedMovement; static Transform screenToWorldMovementTransform; static Vector worldMovementUp; static Vector worldMovementDown; @@ -32,11 +34,14 @@ void initPlayer() { playerCharacter = newScene(); cameraAnchor = playerCharacter; playerCharacter->solid = importSolid("assets/playercharacter.3ds"); + + playerProjectedMovement = newScene(); } void spawnPlayer(Transform transform) { playerCharacter->transform = transform; reparentScene(playerCharacter, currentScene); + reparentScene(playerProjectedMovement, currentScene); } void updatePlayer(float delta) { @@ -68,21 +73,93 @@ static void movePlayer(Vector direction, float delta) { direction = clampMagnitude(direction, 1.0f); Vector displacement = scaleVector(direction, delta * movementSpeed); +{ +Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f); + + playerProjectedMovement->transform = playerCharacter->transform; + + Vector initialPosition = translationOf(playerCharacter->transform); + Vector position = initialPosition; + + GridLocation location = gridLocationFromPosition(position); + Obstacle obstacle = getObstacles(location); + + // Eliminate redundant corner checks + if (obstacle & OBSTACLE_XP) { + obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN); + } + if (obstacle & OBSTACLE_XN) { + obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN); + } + if (obstacle & OBSTACLE_ZP) { + obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP); + } + if (obstacle & OBSTACLE_ZN) { + obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN); + } - //GridLocation location = gridLocationFromTransform(playerCharacter->transform); + float edgeXp = cellBoundaryCoord(location.x + 1); + float edgeXn = cellBoundaryCoord(location.x); + float edgeZp = cellBoundaryCoord(location.z + 1); + float edgeZn = cellBoundaryCoord(location.z); + float distanceXp = edgeXp - position.x; + if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius; + float distanceXn = edgeXn - position.x; + if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius; + float distanceZp = edgeZp - position.z; + if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius; + float distanceZn = edgeZn - position.z; + if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius; + + // Check all edges for intersecting already + if (distanceXp < 0.0f) { + position.x += distanceXp; + displacement = growVectorNoFlip(displacement, distanceXp); + distanceXp = 0.0f; + } + if (distanceXn > 0.0f) { + position.x += distanceXn; + displacement = growVectorNoFlip(displacement, - distanceXn); + distanceXn = 0.0f; + } + if (distanceZp < 0.0f) { + position.z += distanceZp; + displacement = growVectorNoFlip(displacement, distanceZp); + distanceZp = 0.0f; + } + if (distanceZn > 0.0f) { + position.z += distanceZn; + displacement = growVectorNoFlip(displacement, - distanceZn); + distanceZn = 0.0f; + } - if (displacement.x >= 0) { - // need to test +X edge + // Calculate direct movement limits + float dx = displacement.x; + float dz = displacement.z; + if (dx > distanceXp) { + dz *= distanceXp / dx; + dx = distanceXp; + // Might need a flag that we've reached a limit in X+? } - if (displacement.x <= 0) { - // need to test -X edge + if (dx < distanceXn) { + dz *= distanceXn / dx; + dx = distanceXn; + // ? } - if (displacement.z >= 0) { - // need to test +Z edge + if (dz > distanceZp) { + dx *= distanceZp / dz; + dz = distanceZp; + // ? } - if (displacement.z <= 0) { - // need to test -Z edge + if (dz < distanceZn) { + dx *= distanceZn / dz; + dz = distanceZn; + // ? } + // This is how far we can move until we collide or leave the grid cell + position = addVectors(position, (Vector) { dx, 0.0f, dz }); + translate(&playerProjectedMovement->transform, subtractVectors(position, initialPosition)); +} translate(&playerCharacter->transform, displacement); diff --git a/src/game/player.h b/src/game/player.h index 216e676..2515d1d 100644 --- a/src/game/player.h +++ b/src/game/player.h @@ -15,6 +15,7 @@ enum Direction { typedef enum Direction Direction; extern Scene* playerCharacter; +extern Scene* playerProjectedMovement; void initPlayer(); void spawnPlayer(Transform transform); -- 2.44.0