#include #include #include #include // 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; }; 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 = 150; 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, 240, 640, 240}; // 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 * 60 + 640; minimap[x][y].y = y * 60; minimap[x][y].w = 60; minimap[x][y].h = 60; } } 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); SDL_WarpMouseInWindow(window, 320, 240); // 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 --- */ while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_MOUSEMOTION: mouseMovement = (float)event.motion.xrel; break; case SDL_QUIT: return true; break; } } if(mouseMovement > 10.0 || mouseMovement < -10.0) player.angle += mouseMovement / 500; 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, 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 * 60) + 640, y * 60, (x * 60) + 700, y * 60); SDL_RenderDrawLine(renderer, (x * 60) + 640, y * 60, (x * 60) + 640, (y * 60) + 60); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } } for(int i = 0;i <= ray_n;i++) { rayDisplay[i].w = 640 / ray_n; rayDisplay[i].h = 400 / ray[i].distance; rayDisplay[i].x = i * (640 / ray_n); rayDisplay[i].y = 240 - (rayDisplay[i].h / 2); if(ray[i].type == 0) SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); else 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 * 60) + 640, player.y * 60, ((player.x + cos(ray[i].angle) * (ray[i].distance / cos(ray[i].angle - player.angle))) * 60) + 640, (player.y + sin(ray[i].angle) * (ray[i].distance / cos(ray[i].angle - player.angle))) * 60); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); SDL_RenderDrawLine(renderer, (player.x * 60) + 640, player.y * 60, ((player.x + cos(player.angle)) * 60) + 640, (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 = 8, map_h = 8; 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; } } 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, 640 + 480, 480, 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; }