I have done something similar with ai but it was... janky to say the least
Code:
#include <graphx.h>
#include <math.h>
#include <stdint.h>
#include <keypadc.h>
// Define palette-safe colors
#define BLUE 16 // Blue (background)
#define GREEN 3 // Green (blocks)
#define GRAY 7 // Gray (bedrock)
#define WIDTH 5
#define HEIGHT 5
#define DEPTH 5
#define CUBE_SIZE 10
#define SCREEN_CENTER_X 160
#define SCREEN_CENTER_Y 120
uint8_t world[WIDTH][HEIGHT][DEPTH];
// Camera position and rotation
float cameraX = 2.5f, cameraY = 2.5f, cameraZ = -5.0f; // Start behind the world
float cameraYaw = 0.0f, cameraPitch = 0.0f; // Camera rotation angles
// Initialize the 3D world with some blocks
void initializeWorld() {
for (int z = 0; z < DEPTH; z++) {
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
if (y == HEIGHT - 1) {
world[x][y][z] = 1; // Bedrock
} else {
world[x][y][z] = 0; // Empty space
}
}
}
}
// Example: Adding some green blocks
world[2][3][2] = 2; // Green block
}
// Perspective projection of a 3D point
void projectPoint(float x, float y, float z, int *px, int *py) {
// Transform point based on camera position and rotation
float dx = x - cameraX;
float dy = y - cameraY;
float dz = z - cameraZ;
float cosYaw = cos(cameraYaw), sinYaw = sin(cameraYaw);
float cosPitch = cos(cameraPitch), sinPitch = sin(cameraPitch);
// Rotate around the Y-axis (yaw)
float tempX = cosYaw * dx - sinYaw * dz;
float tempZ = sinYaw * dx + cosYaw * dz;
// Rotate around the X-axis (pitch)
float tempY = cosPitch * dy - sinPitch * tempZ;
tempZ = sinPitch * dy + cosPitch * tempZ;
// Simple perspective projection
float fov = 90.0f; // Field of view
float viewerDistance = 5.0; // Distance from the camera to the screen
float factor = fov / (viewerDistance + tempZ);
*px = SCREEN_CENTER_X + (int)(tempX * factor);
*py = SCREEN_CENTER_Y - (int)(tempY * factor);
}
// Check for collisions
int isCollision(float x, float y, float z) {
int blockX = (int)x;
int blockY = (int)y;
int blockZ = (int)z;
if (blockX >= 0 && blockX < WIDTH &&
blockY >= 0 && blockY < HEIGHT &&
blockZ >= 0 && blockZ < DEPTH) {
return world[blockX][blockY][blockZ] != 0; // Return true if block is not empty
}
return 1; // Collide with the boundary
}
// Handle keyboard input for movement
void handleMovement() {
kb_Scan();
float moveSpeed = 0.1f;
float dx = 0, dy = 0, dz = 0;
if (kb_IsDown(kb_KeyMode)) dz -= moveSpeed; // Forward
if (kb_IsDown(kb_KeyApps)) dz += moveSpeed; // Backward
if (kb_IsDown(kb_KeyStat)) dx += moveSpeed; // Right
if (kb_IsDown(kb_KeyAlpha)) dx -= moveSpeed; // Left
if (kb_IsDown(kb_KeyAdd)) dy += moveSpeed; // Up
if (kb_IsDown(kb_KeySub)) dy -= moveSpeed; // Down
// Check for collisions before moving
if (!isCollision(cameraX + dx, cameraY, cameraZ)) cameraX += dx;
if (!isCollision(cameraX, cameraY + dy, cameraZ)) cameraY += dy;
if (!isCollision(cameraX, cameraY, cameraZ + dz)) cameraZ += dz;
}
// Handle keyboard input for looking
void handleLooking() {
kb_Scan();
float lookSpeed = 0.05f;
if (kb_IsDown(kb_KeyUp)) {
cameraPitch -= lookSpeed; // Look up
if (cameraPitch < -1.57f) cameraPitch = -1.57f; // Clamp to -90 degrees
}
if (kb_IsDown(kb_KeyDown)) {
cameraPitch += lookSpeed; // Look down
if (cameraPitch > 1.57f) cameraPitch = 1.57f; // Clamp to +90 degrees
}
if (kb_IsDown(kb_KeyLeft)) {
cameraYaw -= lookSpeed; // Look left
if (cameraYaw < -6.28f) cameraYaw += 6.28f; // Wrap yaw to avoid overflow
}
if (kb_IsDown(kb_KeyRight)) {
cameraYaw += lookSpeed; // Look right
if (cameraYaw > 6.28f) cameraYaw -= 6.28f; // Wrap yaw to avoid overflow
}
}
// Draw a 3D-like cube using projected 2D points
void drawCube(float x, float y, float z, uint8_t color) {
int p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
int p5x, p5y, p6x, p6y, p7x, p7y, p8x, p8y;
// Project the 8 vertices of the cube
projectPoint(x, y, z, &p1x, &p1y);
projectPoint(x + CUBE_SIZE, y, z, &p2x, &p2y);
projectPoint(x + CUBE_SIZE, y + CUBE_SIZE, z, &p3x, &p3y);
projectPoint(x, y + CUBE_SIZE, z, &p4x, &p4y);
projectPoint(x, y, z + CUBE_SIZE, &p5x, &p5y);
projectPoint(x + CUBE_SIZE, y, z + CUBE_SIZE, &p6x, &p6y);
projectPoint(x + CUBE_SIZE, y + CUBE_SIZE, z + CUBE_SIZE, &p7x, &p7y);
projectPoint(x, y + CUBE_SIZE, z + CUBE_SIZE, &p8x, &p8y);
// Draw faces of the cube (back to front for correct depth)
gfx_SetColor(color - 3); // Darker for the back face
gfx_FillTriangle(p5x, p5y, p6x, p6y, p7x, p7y); // Back face top triangle
gfx_FillTriangle(p5x, p5y, p7x, p7y, p8x, p8y); // Back face bottom triangle
gfx_SetColor(color - 2); // Side shading
gfx_FillTriangle(p1x, p1y, p2x, p2y, p6x, p6y); // Side face
gfx_FillTriangle(p1x, p1y, p6x, p6y, p5x, p5y);
gfx_SetColor(color); // Front face
gfx_FillTriangle(p1x, p1y, p2x, p2y, p3x, p3y);
gfx_FillTriangle(p1x, p1y, p3x, p3y, p4x, p4y);
}
// Render the entire 3D world
void renderWorld() {
gfx_FillScreen(BLUE); // Set the background to blue
for (int z = DEPTH - 1; z >= 0; z--) { // Render back to front
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
if (world[x][y][z] == 1) {
drawCube(x * CUBE_SIZE, y * CUBE_SIZE, z * CUBE_SIZE, GRAY); // Bedrock
} else if (world[x][y][z] == 2) {
drawCube(x * CUBE_SIZE, y * CUBE_SIZE, z * CUBE_SIZE, GREEN); // Green block
}
}
}
}
}
int main() {
gfx_Begin(); // Initialize graphics mode
gfx_SetDrawBuffer(); // Enable double buffering
initializeWorld(); // Set up the 3D world
while (1) {
kb_Scan(); // Scan for key presses
handleMovement(); // Handle movement
handleLooking(); // Handle looking (camera rotation)
// Exit when [CLEAR] is pressed
if (kb_IsDown(kb_KeyClear)) {
break;
}
renderWorld(); // Call the renderWorld function to draw the frame
gfx_SwapDraw(); // Swap buffers to display the frame
}
gfx_End(); // Close graphics mode
return 0; // Indicate successful execution
}