Limit movement based on fps
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
+321
-29
@@ -46,17 +46,46 @@ typedef struct
|
||||
} ButtonKeys;
|
||||
ButtonKeys Keys;
|
||||
|
||||
// * ---------------- HELPERS --------------------------------------------------
|
||||
|
||||
// Function to check if a number is a power of 2
|
||||
int isPowerOfTwo(int n)
|
||||
{
|
||||
return (n > 0) && ((n & (n - 1)) == 0);
|
||||
}
|
||||
|
||||
// * Player position (playerX, playerY), delta (playerDeltaX, playerDeltaY), and angle (pa)
|
||||
float degToRad(float a) { return a * M_PI / 180.0; }
|
||||
|
||||
float FixAng(float a)
|
||||
{
|
||||
if (a > 359)
|
||||
{
|
||||
a -= 360;
|
||||
}
|
||||
|
||||
if (a < 0)
|
||||
{
|
||||
a += 360;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// * Returns the distance between the player and the ray's endpoint
|
||||
// That's the hypotenuse, so we can use the Pythagorean theorem.
|
||||
float distance(float ax, float ay, float bx, float by, float ang)
|
||||
{
|
||||
// SOH - CAH - TOA
|
||||
return (sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay)));
|
||||
}
|
||||
|
||||
// * ---------------- PLAYER ---------------------------------------------------
|
||||
|
||||
// Player position (playerX, playerY), delta (playerDeltaX, playerDeltaY), and angle (pa)
|
||||
float playerX, playerY, playerDeltaX, playerDeltaY, playerAngle;
|
||||
|
||||
// * Draw the player on the screen
|
||||
void drawPlayer()
|
||||
// Draw the player on the map
|
||||
void drawPlayer2D()
|
||||
{
|
||||
// Draws the player square
|
||||
glColor3f(1, 1, 0);
|
||||
@@ -66,13 +95,271 @@ void drawPlayer()
|
||||
glEnd();
|
||||
|
||||
// Draws the player direction
|
||||
glLineWidth(3);
|
||||
glLineWidth(4);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2i(playerX, playerY);
|
||||
glVertex2i(playerX + playerDeltaX * 5, playerY + playerDeltaY * 5);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// * ---------------- Draw Rays and Walls --------------------------------------
|
||||
|
||||
void drawRays2D()
|
||||
{
|
||||
int r, mx, my, mapPoint, dof;
|
||||
|
||||
// Ensure mapSize is a power of 2, otherwise default or handle error
|
||||
if (!isPowerOfTwo(mapSize))
|
||||
{
|
||||
fprintf(stderr, "Error: mapSize must be a power of 2 (e.g., 16, 32, 64, 128).\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int shiftSize = (int)(log2f((float)mapSize));
|
||||
|
||||
glColor3f(0, 1, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(526, 0);
|
||||
glVertex2i(1006, 0);
|
||||
glVertex2i(1006, 160);
|
||||
glEnd();
|
||||
|
||||
glColor3f(0, 0, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(526, 160);
|
||||
glVertex2i(1006, 160);
|
||||
glVertex2i(1006, 320);
|
||||
glEnd();
|
||||
|
||||
// Ray x, y, angle
|
||||
float rayX, rayY, rayAngle;
|
||||
|
||||
float xo, yo;
|
||||
|
||||
// Final distance
|
||||
float disT;
|
||||
|
||||
// Let's initialize the ray angle n degrees from the player's facing position
|
||||
// ra = ray angle; pa = player angle; DR = degree radian; n = degrees to use
|
||||
rayAngle = playerAngle - DR * 30;
|
||||
// rayAngle = FixAng(playerAngle+30);
|
||||
|
||||
// Make sure to limit the next angle
|
||||
if (rayAngle < 0)
|
||||
{
|
||||
rayAngle += 2 * PI;
|
||||
}
|
||||
if (rayAngle > 2 * PI)
|
||||
{
|
||||
rayAngle -= 2 * PI;
|
||||
}
|
||||
|
||||
// * Cast n rays
|
||||
for (r = 0; r < 60; r++)
|
||||
{
|
||||
// * --- Check Horizontal Lines ---
|
||||
dof = 0;
|
||||
|
||||
// We need to get the shortest ray cast in either the vertical or horizontal
|
||||
// distance. So, we'll set a default that's way higher than we'll ever
|
||||
// calculate, and save the horizontal ray's X and Y position.
|
||||
float distanceHor = 1000000, hx = playerX, hy = playerY;
|
||||
|
||||
// Negative inverse of tangent
|
||||
float aTan = -1.0f / tanf(rayAngle);
|
||||
|
||||
// Check if the ray is looking up or down.
|
||||
// If it's less than π (e.g., 180°), we're looking down.
|
||||
|
||||
// ? Looking up (ray angle > π)
|
||||
if (rayAngle > PI)
|
||||
{
|
||||
// Round the ray's Y position to the nearest 64th value.
|
||||
// We can do this by bit-shifting down by `shiftSize`, then bit-shifting back up by
|
||||
// `shiftSize`. Then, we subtract a small amount for accuracy.
|
||||
rayY = (((int)playerY >> shiftSize) << shiftSize) - 0.0001f;
|
||||
|
||||
// The ray's X value is the distance between the player and the ray's Y
|
||||
// position, times the inverse of tangent, plus the player's X position.
|
||||
rayX = (playerY - rayY) * aTan + playerX;
|
||||
|
||||
// Now that we have the ray's first grid intersection position, we need
|
||||
// the next X and Y offset for the second grid intersection point.
|
||||
yo = -mapSize;
|
||||
xo = -yo * aTan;
|
||||
}
|
||||
|
||||
// ? Looking down
|
||||
if (rayAngle < PI)
|
||||
{
|
||||
// This is almost exactly the same as looking up, but we add one grid
|
||||
// unit, instead of subtracting a tiny amount.
|
||||
rayY = (((int)playerY >> shiftSize) << shiftSize) + mapSize;
|
||||
|
||||
rayX = (playerY - rayY) * aTan + playerX;
|
||||
|
||||
// This is almost exactly the same as looking up, but we add instead of
|
||||
// subtract.
|
||||
yo = mapSize;
|
||||
xo = -yo * aTan;
|
||||
}
|
||||
|
||||
// ? Looking straight left or right
|
||||
if (rayAngle == 0.0f || rayAngle == PI)
|
||||
{
|
||||
// It's impossible to ever hit a horizontal line
|
||||
rayX = playerX;
|
||||
rayY = playerY;
|
||||
}
|
||||
|
||||
int mv = 0, mh = 0;
|
||||
|
||||
// ? We don't want to get caught in an infinite loop, so use the
|
||||
// depth-of-field to limit our check to a max of 8.
|
||||
while (dof < 8)
|
||||
{
|
||||
// We know the coordinates where the ray will hit the wall, but we need
|
||||
// to know where that is in the map array.
|
||||
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
|
||||
// 6 places), and use that to find the position in the map's array.
|
||||
mx = (int)(rayX) >> shiftSize;
|
||||
my = (int)(rayY) >> shiftSize;
|
||||
mapPoint = my * mapX + mx;
|
||||
|
||||
// If the map position is less than the array's size, then we can check it
|
||||
// inside the map. If the value is 1, there's a wall. If we hit a wall,
|
||||
// we're done!
|
||||
if (mapPoint > 0 && mapPoint < mapX * mapY && map[mapPoint] > 0)
|
||||
{
|
||||
// We hit a wall!
|
||||
|
||||
mh = map[mapPoint];
|
||||
|
||||
// Save the values and calculate the distance from the player
|
||||
hx = rayX;
|
||||
hy = rayY;
|
||||
distanceHor = distance(playerX, playerY, hx, hy, rayAngle);
|
||||
|
||||
dof = 8; // Break the loop - no need to calculate more.
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we didn't hit a wall, we check the next horizontal line by adding
|
||||
// the x and y offset.
|
||||
rayX += xo;
|
||||
rayY += yo;
|
||||
dof += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// * --- Check Vertical Lines ---
|
||||
dof = 0;
|
||||
float distanceVert = 1000000, vx = playerX, vy = playerY;
|
||||
|
||||
// Negative of tangent
|
||||
float nTan = -tanf(rayAngle);
|
||||
|
||||
// ? Looking left
|
||||
if (rayAngle > PI2 && rayAngle < PI3)
|
||||
{
|
||||
rayX = (((int)playerX >> shiftSize) << shiftSize) - 0.0001f;
|
||||
rayY = (playerX - rayX) * nTan + playerY;
|
||||
xo = -mapSize;
|
||||
yo = -xo * nTan;
|
||||
}
|
||||
|
||||
// ? Looking right
|
||||
if (rayAngle < PI2 || rayAngle > PI3)
|
||||
{
|
||||
rayX = (((int)playerX >> shiftSize) << shiftSize) + mapSize;
|
||||
rayY = (playerX - rayX) * nTan + playerY;
|
||||
xo = mapSize;
|
||||
yo = -xo * nTan;
|
||||
}
|
||||
|
||||
// ? Looking straight up or down
|
||||
if (rayAngle == 0.0f || rayAngle == PI)
|
||||
{
|
||||
rayX = playerX;
|
||||
rayY = playerY;
|
||||
}
|
||||
|
||||
while (dof < 8)
|
||||
{
|
||||
// We know the coordinates where the ray will hit the wall, but we need
|
||||
// to know where that is in the map array.
|
||||
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
|
||||
// 6 places), and use that to find the position in the map's array.
|
||||
mx = (int)(rayX) >> shiftSize;
|
||||
my = (int)(rayY) >> shiftSize;
|
||||
mapPoint = my * mapX + mx;
|
||||
|
||||
// If the map position is less than the array's size, then we can check it
|
||||
// inside the map. If the value is 1, there's a wall. If we hit a wall,
|
||||
// we're done!
|
||||
if (mapPoint > 0 && mapPoint < mapX * mapY && map[mapPoint] > 0)
|
||||
{
|
||||
// We hit a wall!
|
||||
|
||||
mv = map[mapPoint];
|
||||
|
||||
// Save the values and calculate the distance from the player
|
||||
vx = rayX;
|
||||
vy = rayY;
|
||||
distanceVert = distance(playerX, playerY, vx, vy, rayAngle);
|
||||
|
||||
dof = 8; // Break the loop - no need to calculate more.
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we didn't hit a wall, we check the next horizontal line by adding
|
||||
// the x and y offset.
|
||||
rayX += xo;
|
||||
rayY += yo;
|
||||
dof += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// * Compare shortest horizontal and vertical line, then use the shortest
|
||||
// This is what actually determines our ray that hits a wall!
|
||||
|
||||
// Vertical wall hit
|
||||
if (distanceVert < distanceHor)
|
||||
{
|
||||
rayX = vx;
|
||||
rayY = vy;
|
||||
disT = distanceVert;
|
||||
}
|
||||
|
||||
// Horizontal wall hit
|
||||
if (distanceHor < distanceVert)
|
||||
{
|
||||
rayX = hx;
|
||||
rayY = hy;
|
||||
disT = distanceHor;
|
||||
}
|
||||
|
||||
// * Draw the ray
|
||||
glColor3f(0, 1, 1);
|
||||
glLineWidth(3);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2i(playerX, playerY);
|
||||
glVertex2i(rayX, rayY);
|
||||
glEnd();
|
||||
|
||||
// * Increase the ray angle to draw the next ray
|
||||
rayAngle += DR;
|
||||
if (rayAngle < 0)
|
||||
{
|
||||
rayAngle += 2 * PI;
|
||||
}
|
||||
if (rayAngle > 2 * PI)
|
||||
{
|
||||
rayAngle -= 2 * PI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * Draws the top-down map with white alls and black floors
|
||||
void drawMap2D()
|
||||
{
|
||||
@@ -108,14 +395,6 @@ void drawMap2D()
|
||||
}
|
||||
}
|
||||
|
||||
// * Returns the distance between the player and the ray's endpoint
|
||||
// That's the hypotenuse, so we can use the Pythagorean theorem.
|
||||
float dist(float ax, float ay, float bx, float by, float ang)
|
||||
{
|
||||
// SOH - CAH - TOA
|
||||
return (sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay)));
|
||||
}
|
||||
|
||||
// * The actual raycaster
|
||||
void drawRays3D()
|
||||
{
|
||||
@@ -237,7 +516,7 @@ void drawRays3D()
|
||||
// Save the values and calculate the distance from the player
|
||||
hx = rayX;
|
||||
hy = rayY;
|
||||
distanceHor = dist(playerX, playerY, hx, hy, rayAngle);
|
||||
distanceHor = distance(playerX, playerY, hx, hy, rayAngle);
|
||||
|
||||
dof = 8; // Break the loop - no need to calculate more.
|
||||
}
|
||||
@@ -305,7 +584,7 @@ void drawRays3D()
|
||||
// Save the values and calculate the distance from the player
|
||||
vx = rayX;
|
||||
vy = rayY;
|
||||
distanceVert = dist(playerX, playerY, vx, vy, rayAngle);
|
||||
distanceVert = distance(playerX, playerY, vx, vy, rayAngle);
|
||||
|
||||
dof = 8; // Break the loop - no need to calculate more.
|
||||
}
|
||||
@@ -352,13 +631,6 @@ void drawRays3D()
|
||||
}
|
||||
}
|
||||
|
||||
// * Draw the ray
|
||||
glLineWidth(3);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2i(playerX, playerY);
|
||||
glVertex2i(rayX, rayY);
|
||||
glEnd();
|
||||
|
||||
// * --- Draw 3D Walls ---
|
||||
|
||||
// Correct the fish-eye effect
|
||||
@@ -462,9 +734,26 @@ void drawFloor()
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// * ---------------- MAIN -----------------------------------------------------
|
||||
|
||||
float frame1, frame2, fps;
|
||||
|
||||
// * Main rendering
|
||||
void display()
|
||||
{
|
||||
// Frames per second
|
||||
frame2 = glutGet(GLUT_ELAPSED_TIME);
|
||||
|
||||
if (frame1 < 0)
|
||||
{
|
||||
frame1 = 10;
|
||||
}
|
||||
|
||||
fps = (frame2 - frame1);
|
||||
frame1 = glutGet(GLUT_ELAPSED_TIME);
|
||||
|
||||
float movementMultiplier = 0.05 * fps;
|
||||
|
||||
// * Handle keyboard inputs
|
||||
// ? Move left
|
||||
if (Keys.a == 1)
|
||||
@@ -475,7 +764,7 @@ void display()
|
||||
// 0° -> 360° ≈ 0 -> ~6.28
|
||||
|
||||
// Change player angle slightly (using radians)
|
||||
playerAngle -= 0.1;
|
||||
playerAngle -= 0.1 * movementMultiplier;
|
||||
|
||||
// If player angle drops below 0, let's reset back to 2π
|
||||
if (playerAngle < 0)
|
||||
@@ -492,7 +781,7 @@ void display()
|
||||
// ? Move right
|
||||
if (Keys.d == 1)
|
||||
{
|
||||
playerAngle += 0.1;
|
||||
playerAngle += 0.1 * movementMultiplier;
|
||||
|
||||
if (playerAngle > 2 * PI)
|
||||
{
|
||||
@@ -508,24 +797,27 @@ void display()
|
||||
{
|
||||
// We change the X and Y values based on our delta, which is calculated
|
||||
// during our rotation.
|
||||
playerX += playerDeltaX;
|
||||
playerY += playerDeltaY;
|
||||
playerX += playerDeltaX * movementMultiplier;
|
||||
playerY += playerDeltaY * movementMultiplier;
|
||||
}
|
||||
|
||||
// ? Move backwards
|
||||
if (Keys.s == 1)
|
||||
{
|
||||
playerX -= playerDeltaX;
|
||||
playerY -= playerDeltaY;
|
||||
playerX -= playerDeltaX * movementMultiplier;
|
||||
playerY -= playerDeltaY * movementMultiplier;
|
||||
}
|
||||
glutPostRedisplay();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
drawMap2D();
|
||||
drawRays2D();
|
||||
drawPlayer2D();
|
||||
|
||||
drawCeiling();
|
||||
drawFloor();
|
||||
drawMap2D();
|
||||
drawRays3D();
|
||||
drawPlayer();
|
||||
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user