#include #include #include #include #include #define WINDOW_WIDTH 1920 #define WINDOW_HEIGHT 1088 // TODO FIRSST get rendering and movement around map working. DONE DONE DONE // Improving movement with strafing. // TODO Enemies, Load maps. Weapons // Map loading - needs to save maps to files. Maybe implement a map editor // Enemies - Some kind of AI? gotta shoot at you and do damage. Maybe less chance to hit when moving // Weapons - need some sort of graphic for different weapon types. Have to shoot and be able to hit things. Hitboxes for enemies and such. struct Tile { int type; bool discovered; }; struct Player { float x; float y; float angle; int health; }; struct Ray { float angle; float xDir; float yDir; float distance; int type; }; void printText(SDL_Renderer *renderer, TTF_Font *font, char *str, int x, int y, int h, int r, int g, int b) { SDL_Color c = {r, g, b, 255}; SDL_Surface *s = TTF_RenderText_Solid(font, str, c); SDL_Texture *t = SDL_CreateTextureFromSurface(renderer, s); SDL_Rect rect = {x, y, strlen(str) * (h / 2), h}; SDL_RenderCopy(renderer, t, NULL, &rect); } void loadMap(char *file, int map_w, int map_h, struct Tile map[map_w][map_h]) { FILE *f; char mapString[64]; f = fopen(file, "r"); fgets(mapString, 64, f); int ch = getc(f), x = 0, y = 0; while(ch != EOF) { printf("%c", ch); if(ch == '\n') { y++; x = 0; } else { map[x][y].type = ch - '0'; x++; } ch = getc(f); } } float shootRay(float xDir, float yDir, float x, float y, float angle, float baseAngle, float map_depth, int map_w, int map_h, struct Tile map[map_w][map_h], int *type) { float stepX, stepY, vx, vy, hx, hy; bool hit; //Checking Vertical Intersections if(xDir < 0.0) { stepX = -1; vx = floor(x); } else { stepX = 1; vx = ceil(x); } if(yDir < 0.0) vy = y - (fabs(vx - x) * fabs(tan(angle))); else vy = y + (fabs(vx - x) * fabs(tan(angle))); //printf("xDir: %f yDir: %f\n", xDir, yDir); //printf("--- Vertical check ---\n"); //printf("x: %f, y: %f", vx, vy); if(sqrt(pow(fabs(vx - x), 2) + pow(fabs(vy - y), 2)) > map_depth) hit = true; else if(xDir < 0.0 && map[(int)vx - 1][(int)vy].type != 0) hit = true; else if(map[(int)vx][(int)vy].type != 0) hit = true; else hit = false; //printf(" hit: %b\n", hit); while(!hit) { vx += stepX; if(yDir < 0.0) vy -= fabs(tan(angle)); else vy += fabs(tan(angle)); //printf("x: %f, y: %f", vx, vy); if(sqrt(pow(fabs(vx - x), 2) + pow(fabs(vy - y), 2)) > map_depth) hit = true; else if(xDir < 0.0 && map[(int)vx - 1][(int)vy].type != 0) hit = true; else if(map[(int)vx][(int)vy].type != 0) hit = true; //printf(" hit: %b\n", hit); } //printf("distance: %f\n", sqrt(pow(fabs(vx - x), 2) + pow(fabs(vy - y), 2))); //printf("--- Done ---\n\n"); //Checking Horizontal Intersections if(yDir < 0.0) { stepY = -1; hy = floor(y); } else { stepY = 1; hy = ceil(y); } if(xDir < 0.0) hx = x - (fabs(hy - y) / fabs(tan(angle))); else hx = x + (fabs(hy - y) / fabs(tan(angle))); //printf("--- Horizontal check ---\n"); //printf("x: %f, y: %f", hx, hy); if(sqrt(pow(fabs(hx - x), 2) + pow(fabs(y - hy), 2)) > map_depth) hit = true; else if(yDir < 0.0 && map[(int)hx][(int)hy - 1].type != 0) hit = true; else if(map[(int)hx][(int)hy].type != 0) hit = true; else hit = false; //printf(" hit: %b\n", hit); while(!hit) { hy += stepY; if(xDir < 0.0) hx -= 1/fabs(tan(angle)); else hx += 1/fabs(tan(angle)); //printf("x: %f, y: %f", hx, hy); if(sqrt(pow(fabs(hx - x), 2) + pow(fabs(hy - y), 2)) > map_depth) hit = true; else if(yDir < 0.0 && map[(int)hx][(int)hy - 1].type != 0) hit = true; else if(map[(int)hx][(int)hy].type != 0) hit = true; //printf(" hit: %b\n", hit); } //printf("distance: %f\n", sqrt(pow(fabs(hx - x), 2) + pow(fabs(hy - y), 2))); //printf("--- Done ---\n\n"); float vdistance = sqrt(pow(fabs(vx - x), 2) + pow(fabs(vy - y), 2)), hdistance = sqrt(pow(fabs(hx - x), 2) + pow(fabs(hy - y), 2)); if(vdistance < hdistance) { //if((fabs(ceil(vx) - vx) < 0.05) && (fabs(ceil(vy) - vy) < 0.05)) // *type = 0; if(xDir < 0.0) *type = map[(int)vx - 1][(int)vy].type; else *type = map[(int)vx][(int)vy].type; return(vdistance * cos(angle - baseAngle)); } else { //if((fabs(ceil(hx) -hx) < 0.05) && (fabs(ceil(hy) - hy) < 0.05)) // *type = 0; if(yDir < 0.0) *type = map[(int)hx][(int)hy - 1].type; else *type = map[(int)hx][(int)hy].type; return(hdistance * cos(angle - baseAngle)); } } bool gameLoop(int map_w, int map_h, SDL_Renderer *renderer, SDL_Event event, SDL_Window* window, struct Tile map[map_w][map_h]) { int framerate = 30, ticksPerFrame = 1000/framerate, startFrame = 0, ray_n = WINDOW_WIDTH / 2; float fov = 90 * (M_PI/180), angle_step = fov/ray_n, map_depth = sqrt(pow(map_w, 2) + pow(map_h, 2)), acceleration = 0.0, sideAcceleration = 0.0, mouseMovement; bool end = false; SDL_Rect floor = {0, WINDOW_HEIGHT / 2, WINDOW_WIDTH, WINDOW_HEIGHT / 2}; SDL_Rect skybox = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT / 2}; // Make minimap squares 60x60 pixels. DONE SDL_Rect minimap[map_w][map_h]; for(int y = 0;y < map_h;y++) { for(int x = 0;x < map_w;x++) { minimap[x][y].x = x * 34 + WINDOW_WIDTH; minimap[x][y].y = y * 34; minimap[x][y].w = 34; minimap[x][y].h = 34; } } struct Player player; player.x = 3.95; player.y = 3.95; player.angle = 0.0; player.health = 10; //struct Ray ray[ray_n]; struct Ray ray[ray_n + 1]; SDL_Rect rayDisplay[ray_n + 1]; while(!end) { startFrame = SDL_GetTicks(); SDL_RenderClear(renderer); // Make player shoot out rays from position with an FoV of 60 degrees DONE for(int i = 0;i <= ray_n;i++) { ray[i].angle = player.angle - (fov / 2) + (angle_step * i); ray[i].xDir = cos(ray[i].angle); ray[i].yDir = sin(ray[i].angle); ray[i].distance = shootRay(ray[i].xDir, ray[i].yDir, player.x, player.y, ray[i].angle, player.angle, map_depth, map_w, map_h, map, &ray[i].type); } /* --- CONTROLS --- */ mouseMovement = 0; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_MOUSEMOTION: mouseMovement = (float)event.motion.xrel; break; case SDL_QUIT: return true; break; } } player.angle += mouseMovement / 25; const Uint8* keys = SDL_GetKeyboardState(NULL); if(keys[SDL_SCANCODE_W]) { if(acceleration < 0.5) acceleration += 0.1; } if(keys[SDL_SCANCODE_S]) { if(acceleration > -0.5) acceleration -= 0.1; } if(keys[SDL_SCANCODE_A]) { if(sideAcceleration < 0.5) sideAcceleration -= 0.1; } if(keys[SDL_SCANCODE_D]) { if(sideAcceleration > -0.5) sideAcceleration += 0.1; } if(keys[SDL_SCANCODE_Q]) { player.angle -= 5 * angle_step; if(player.angle < 0.0) player.angle += 2 * M_PI; } if(keys[SDL_SCANCODE_E]) { player.angle += 5 * angle_step; if(player.angle > 2 * M_PI) player.angle -= 2 * M_PI; } if(map[(int)(player.x + acceleration * cos(player.angle))][(int)(player.y + acceleration * sin(player.angle))].type == 0) { player.x += acceleration * cos(player.angle); player.y += acceleration * sin(player.angle); } if(map[(int)(player.x + sideAcceleration * cos(player.angle + M_PI_2))][(int)(player.y + sideAcceleration * sin(player.angle + M_PI_2))].type == 0) { player.x += sideAcceleration * cos(player.angle + M_PI_2); player.y += sideAcceleration * sin(player.angle + M_PI_2); } if(acceleration > 0.0) acceleration -= 0.1; else if(acceleration < 0.0) acceleration += 0.1; if(acceleration < 0.01 && acceleration > -0.01) acceleration = 0.0; if(sideAcceleration > 0.0) sideAcceleration -= 0.1; else if(sideAcceleration < 0.0) sideAcceleration += 0.1; if(sideAcceleration < 0.01 && sideAcceleration > -0.01) sideAcceleration = 0.0; /* --- RENDERING --- */ SDL_SetRenderDrawColor(renderer, 0, 213, 255, 255); SDL_RenderFillRect(renderer, &skybox); SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); SDL_RenderFillRect(renderer, &floor); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); for(int y = 0;y < map_h;y++) { for(int x = 0;x < map_w;x++) { if(map[x][y].type == 0) SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); if(map[x][y].type == 1) SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); if(map[x][y].type == 2) SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); if(map[x][y].type == 3) SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); SDL_RenderFillRect(renderer, &minimap[x][y]); SDL_SetRenderDrawColor(renderer, 100, 100, 100, 50); SDL_RenderDrawLine(renderer, (x * 34) + WINDOW_WIDTH, y * 34, (x * 34) + 1920 + 34, y * 34); SDL_RenderDrawLine(renderer, (x * 34) + WINDOW_WIDTH, y * 34, (x * 34) + WINDOW_WIDTH, (y * 34) + 34); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } } for(int i = 0;i <= ray_n;i++) { rayDisplay[i].w = 2; rayDisplay[i].h = 800 / ray[i].distance; rayDisplay[i].x = i * 2; rayDisplay[i].y = WINDOW_HEIGHT / 2 - (rayDisplay[i].h / 2); //if(ray[i].type == 0) // SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); if(ray[i].type == 1) SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); else if(ray[i].type == 2) SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); else if(ray[i].type == 3) SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); SDL_RenderFillRect(renderer, &rayDisplay[i]); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderDrawLine(renderer, (player.x * 34) + WINDOW_WIDTH, player.y * 34, ((player.x + cos(ray[i].angle) * (ray[i].distance / cos(ray[i].angle - player.angle))) * 34) + WINDOW_WIDTH, (player.y + sin(ray[i].angle) * (ray[i].distance / cos(ray[i].angle - player.angle))) * 34); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); //SDL_RenderDrawLine(renderer, (player.x * 60) + WINDOW_WIDTH, player.y * 60, ((player.x + cos(player.angle)) * 60) + WINDOW_WIDTH, (player.y + sin(player.angle)) * 60); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderPresent(renderer); int ticks = startFrame - SDL_GetTicks(); if(ticks < ticksPerFrame) SDL_Delay(ticksPerFrame - ticks); } return true; } int main() { int map_w = 32, map_h = 32; struct Tile map[map_w][map_h]; bool quit = false; //for(int y = 0;y < map_h;y++) // { // for(int x = 0;x < map_w;x++) // { // if(x == 0 || x == map_w - 1 || y == 0 || y == map_h - 1) // map[x][y].type = 1; // else if(x == 3 && y == 5) // map[x][y].type = 2; // else if(x == 2 && y >= 2 && y <= 6) // map[x][y].type = 3; // else // map[x][y].type = 0; // } // } loadMap("./maps/test-lineless.map", map_w, map_h, map); SDL_Window *window; SDL_Renderer *renderer; SDL_Event event; SDL_Init(SDL_INIT_VIDEO); TTF_Init(); window = SDL_CreateWindow("Doom", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH + 1088, WINDOW_HEIGHT, SDL_WINDOW_OPENGL); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_SetRelativeMouseMode(SDL_TRUE); while(!quit) { quit = gameLoop(map_w, map_h, renderer, event, window, map); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }