collision detection works and is fast. for now.

This commit is contained in:
John Bintz 2024-02-25 12:52:42 -05:00
parent 903835a10a
commit 83c35f68c4
6 changed files with 123 additions and 41 deletions

View File

@ -5,4 +5,4 @@ void clearArenaRedrawRequests();
void redrawArena(); void redrawArena();
void drawOnlyArena(struct SpriteBounds *bounds); void drawOnlyArena(struct SpriteBounds *bounds);
void buildArena(); void buildArena();
void setupWallSprites(struct BMPImage *); void setupWallSprites();

127
combat.c
View File

@ -1,4 +1,5 @@
#include <math.h> #include <math.h>
#include <stdio.h>
#include "game.h" #include "game.h"
@ -12,7 +13,8 @@ void attemptToFireRabbitBullet(
struct BulletPosition rabbitBulletPosition[] struct BulletPosition rabbitBulletPosition[]
) { ) {
int okToFire = 0, availableBullet, i; int okToFire = 0, availableBullet, i;
signed char doubleVelocityX, doubleVelocityY; signed int doubleVelocityX, doubleVelocityY;
char buffer[20];
if (rabbitWeaponry->cooldown > 0) return; if (rabbitWeaponry->cooldown > 0) return;
@ -34,15 +36,15 @@ void attemptToFireRabbitBullet(
rabbitBulletPosition[availableBullet].isActive = 1; rabbitBulletPosition[availableBullet].isActive = 1;
rabbitBulletPosition[availableBullet].willBeInactive = 0; rabbitBulletPosition[availableBullet].willBeInactive = 0;
rabbitBulletPosition[availableBullet].x = rabbitPosition->rabbitPosition[0]; rabbitBulletPosition[availableBullet].x = rabbitPosition->rabbitPosition[0];
rabbitBulletPosition[availableBullet].y = rabbitPosition->rabbitPosition[1] - RABBIT_BULLET_HEIGHT_START; rabbitBulletPosition[availableBullet].y = rabbitPosition->rabbitPosition[1];
rabbitBulletPosition[availableBullet].oldX = rabbitBulletPosition[availableBullet].x; rabbitBulletPosition[availableBullet].oldX = rabbitBulletPosition[availableBullet].x;
rabbitBulletPosition[availableBullet].oldY = rabbitBulletPosition[availableBullet].y; rabbitBulletPosition[availableBullet].oldY = rabbitBulletPosition[availableBullet].y;
rabbitBulletPosition[availableBullet].velocityXSteps[0] = doubleVelocityX / RABBIT_BULLET_VELOCITY; rabbitBulletPosition[availableBullet].velocityXSteps[0] = doubleVelocityX / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityXSteps[1] = doubleVelocityX % RABBIT_BULLET_VELOCITY; rabbitBulletPosition[availableBullet].velocityXSteps[1] = doubleVelocityX - rabbitBulletPosition[availableBullet].velocityXSteps[0];
rabbitBulletPosition[availableBullet].velocityYSteps[0] = doubleVelocityY / RABBIT_BULLET_VELOCITY; rabbitBulletPosition[availableBullet].velocityYSteps[0] = doubleVelocityY / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[1] = doubleVelocityY % RABBIT_BULLET_VELOCITY; rabbitBulletPosition[availableBullet].velocityYSteps[1] = doubleVelocityY - rabbitBulletPosition[availableBullet].velocityYSteps[0];
rabbitBulletPosition[availableBullet].velocityStep = 0; rabbitBulletPosition[availableBullet].velocityStep = 0;
} }
@ -81,22 +83,12 @@ struct CollisionDetection {
struct CollisionDetection collisionDetection; struct CollisionDetection collisionDetection;
int isCollision() { int isCollision() {
int result; if (collisionDetection.sourceEY < collisionDetection.targetSY) return 0;
if (collisionDetection.sourceEX < collisionDetection.targetSX) return 0;
if (collisionDetection.targetEY < collisionDetection.sourceSY) return 0;
if (collisionDetection.targetEX < collisionDetection.sourceSX) return 0;
// first pass: are the hitboxes even close return 1;
result = !(
collisionDetection.sourceEY < collisionDetection.targetSY ||
collisionDetection.sourceEX < collisionDetection.targetSX ||
collisionDetection.targetEY < collisionDetection.sourceSY ||
collisionDetection.targetEX < collisionDetection.sourceSX
);
if (!result) return result;
// second pass: check against specific character shaped hitboxes
// these will be generated from the spritesheet generator
return result;
} }
void populateSourceCollision(struct CompiledSpriteRender *sprite) { void populateSourceCollision(struct CompiledSpriteRender *sprite) {
@ -117,33 +109,110 @@ void populateTargetCollision(struct CompiledSpriteRender *sprite) {
collisionDetection.targetEY = bounds.bottom; collisionDetection.targetEY = bounds.bottom;
} }
void handleRabbitBulletToEnemyCollisions( void handleRabbitToEnemyCollisions(
struct BulletPosition rabbitBulletPosition[], struct RabbitPosition *rabbitPosition,
struct EnemyPosition enemyPosition[] struct EnemyPosition enemyPosition[]
) { ) {
int bulletIdx, enemyIdx; int enemyIdx;
// to start: brute force items // to start: brute force items
// possible performance improvements: // possible performance improvements:
// * horizontal sweep against enemies to rule out enemies to check // * grid partitioning
for (bulletIdx = 0; bulletIdx < RABBIT_BULLET_LIMIT; ++bulletIdx) { rabbit.x = rabbitPosition->rabbitPosition[0];
if (!rabbitBulletPosition[bulletIdx].isActive) continue; rabbit.y = rabbitPosition->rabbitPosition[1];
if (rabbitBulletPosition[bulletIdx].willBeInactive) continue; populateSourceCollision(&rabbit);
populateSourceCollision(&bullet);
for (enemyIdx = 0; enemyIdx < ENEMY_MAX_COUNT; ++enemyIdx) { for (enemyIdx = 0; enemyIdx < ENEMY_MAX_COUNT; ++enemyIdx) {
if (!enemyPosition[enemyIdx].isActive) continue; if (!enemyPosition[enemyIdx].isActive) continue;
if (enemyPosition[enemyIdx].willBeInactive) continue; if (enemyPosition[enemyIdx].willBeInactive) continue;
enemy.x = enemyPosition[enemyIdx].enemyPosition[0];
enemy.y = enemyPosition[enemyIdx].enemyPosition[1];
populateTargetCollision(&enemy); populateTargetCollision(&enemy);
if (isCollision()) { if (isCollision()) {
enemyPosition[enemyIdx].willBeInactive = 1; enemyPosition[enemyIdx].willBeInactive = 1;
rabbitBulletPosition[bulletIdx].willBeInactive = 1; }
}
}
int rabbitBulletGrid[4][RABBIT_BULLET_LIMIT];
int enemyGrid[4][ENEMY_MAX_COUNT];
void handleRabbitBulletToEnemyCollisions(
struct BulletPosition rabbitBulletPosition[],
struct EnemyPosition enemyPosition[]
) {
int bulletIdx, enemyIdx, grid;
int resolvedBulletIdx, resolvedEnemyIdx;
int rabbitGridIndex[4], enemyGridIndex[4];
char buffer[30];
for (grid = 0; grid < 4; ++grid) {
rabbitGridIndex[grid] = 0;
}
for (bulletIdx = 0; bulletIdx < RABBIT_BULLET_LIMIT; ++bulletIdx) {
if (!rabbitBulletPosition[bulletIdx].isActive) continue;
if (rabbitBulletPosition[bulletIdx].willBeInactive) continue;
grid = (
rabbitBulletPosition[bulletIdx].x / (TILE_SIZE * 5)
) + (
(rabbitBulletPosition[bulletIdx].y / (TILE_SIZE * 5)) * 2
);
rabbitBulletGrid[grid][rabbitGridIndex[grid]++] = bulletIdx;
}
for (grid = 0; grid < 4; ++grid) {
enemyGridIndex[grid] = 0;
}
for (enemyIdx = 0; enemyIdx < ENEMY_MAX_COUNT; ++enemyIdx) {
if (!enemyPosition[enemyIdx].isActive) continue;
if (enemyPosition[enemyIdx].willBeInactive) continue;
grid = (
enemyPosition[enemyIdx].enemyPosition[0] / (TILE_SIZE * 5)
) + (
(enemyPosition[enemyIdx].enemyPosition[1] / (TILE_SIZE * 5)) * 2
);
enemyGrid[grid][enemyGridIndex[grid]++] = enemyIdx;
}
for (grid = 0; grid < 4; ++grid) {
if (enemyGridIndex[grid] == 0) continue;
for (bulletIdx = 0; bulletIdx < rabbitGridIndex[grid]; ++bulletIdx) {
resolvedBulletIdx = rabbitBulletGrid[grid][bulletIdx];
if (!rabbitBulletPosition[resolvedBulletIdx].isActive) continue;
if (rabbitBulletPosition[resolvedBulletIdx].willBeInactive) continue;
bullet.x = rabbitBulletPosition[resolvedBulletIdx].x;
bullet.y = rabbitBulletPosition[resolvedBulletIdx].y;
populateSourceCollision(&bullet);
for (enemyIdx = 0; enemyIdx < enemyGridIndex[grid]; ++enemyIdx) {
resolvedEnemyIdx = enemyGrid[grid][enemyIdx];
if (!enemyPosition[resolvedEnemyIdx].isActive) continue;
if (enemyPosition[resolvedEnemyIdx].willBeInactive) continue;
enemy.x = enemyPosition[resolvedEnemyIdx].enemyPosition[0];
enemy.y = enemyPosition[resolvedEnemyIdx].enemyPosition[1];
populateTargetCollision(&enemy);
if (isCollision()) {
enemyPosition[resolvedEnemyIdx].willBeInactive = 1;
rabbitBulletPosition[resolvedBulletIdx].willBeInactive = 1;
break; break;
} }
} }
} }
}
} }

View File

@ -13,3 +13,8 @@ void handleRabbitBulletToEnemyCollisions(
struct BulletPosition[], struct BulletPosition[],
struct EnemyPosition[] struct EnemyPosition[]
); );
void handleRabbitToEnemyCollisions(
struct RabbitPosition*,
struct EnemyPosition[]
);

View File

@ -10,7 +10,7 @@
#define RABBIT_MOTION_VELOCITY_DECAY (5) #define RABBIT_MOTION_VELOCITY_DECAY (5)
#define RABBIT_BULLET_LIMIT (12) #define RABBIT_BULLET_LIMIT (12)
#define RABBIT_BULLET_VELOCITY (RABBIT_MOTION_ACCELERATION + 2) #define RABBIT_BULLET_VELOCITY (RABBIT_MOTION_ACCELERATION + 1)
#define RABBIT_BULLET_COOLDOWN (10) #define RABBIT_BULLET_COOLDOWN (10)
#define RABBIT_BULLET_HEIGHT_START (8) #define RABBIT_BULLET_HEIGHT_START (8)
@ -22,4 +22,4 @@
#define RAD2DEG (180/3.14159) #define RAD2DEG (180/3.14159)
#define DEG2RAD (3.14159/180) #define DEG2RAD (3.14159/180)
#define ENEMY_MAX_COUNT (20) #define ENEMY_MAX_COUNT (50)

16
game.c
View File

@ -1,3 +1,4 @@
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <conio.h> #include <conio.h>
@ -42,7 +43,7 @@ struct SpawnPointRange {
}; };
struct SpawnPointRange spawnPointRanges[1] = { struct SpawnPointRange spawnPointRanges[1] = {
{ .left = 50, .width = 30, .top = 50, .height = 30 } { .left = 130, .width = 50, .top = 50, .height = 100 }
}; };
@ -66,7 +67,6 @@ void setupEnemies() {
} }
} }
void maybeSpawnEnemy() { void maybeSpawnEnemy() {
char canSpawn; char canSpawn;
int i, availableEnemy; int i, availableEnemy;
@ -238,7 +238,7 @@ int setupGame() {
spritesheetImage.transparentColor = 0; spritesheetImage.transparentColor = 0;
setupWallSprites(&spritesheetImage); setupWallSprites();
setupRabbitSprites(); setupRabbitSprites();
setupRabbitBullets(); setupRabbitBullets();
setupEnemySprites(); setupEnemySprites();
@ -286,6 +286,11 @@ void handleCombat() {
&rabbitWeaponry &rabbitWeaponry
); );
handleRabbitToEnemyCollisions(
&rabbitPosition,
enemyPosition
);
handleRabbitBulletToEnemyCollisions( handleRabbitBulletToEnemyCollisions(
rabbitBulletPosition, rabbitBulletPosition,
enemyPosition enemyPosition
@ -316,23 +321,26 @@ clock_t startTime;
int main(void) { int main(void) {
byte *drawBuffer; byte *drawBuffer;
int keepRunning = 1; int keepRunning = 1;
int i;
if (setupGame()) return 1; if (setupGame()) return 1;
drawBuffer = getDrawBuffer(); drawBuffer = getDrawBuffer();
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
maybeSpawnEnemy(); maybeSpawnEnemy();
}
while (keepRunning) { while (keepRunning) {
readMouse(&mouseStatus); readMouse(&mouseStatus);
populateKeyboardKeydownState(); populateKeyboardKeydownState();
handleMovement(); handleMovement();
handleCombat();
handleRedraw(); handleRedraw();
waitStartVbl(); waitStartVbl();
copyDrawBufferToDisplay(); copyDrawBufferToDisplay();
handleCombat();
waitEndVbl(); waitEndVbl();
if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; } if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; }

View File

@ -31,7 +31,7 @@ void calculateTargetAngle(
// Position the cursor // Position the cursor
distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0]; distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0];
distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1] - RABBIT_BULLET_HEIGHT_START; distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1];
angle = atan2(distanceY, distanceX) * RAD2DEG + 90; angle = atan2(distanceY, distanceX) * RAD2DEG + 90;
if (angle < 0) angle += 360; if (angle < 0) angle += 360;