//-------------------------------------------------------------------------------------------------- // F4 Racing //-------------------------------------------------------------------------------------------------- // This is an advancement in my game making skills, building off the back of my work on the "Collect the Balls" game. //-------------------------------------------------------------------------------------------------- // created by Rune // black box code by Sigonasr2 from the OLC (one lone coder) Discord server //-------------------------------------------------------------------------------------------------- #define OLC_PGE_APPLICATION #include "pixelGameEngine.h" // used for drawing the game //-------------------------------------------------------------------------------------------------- // class //-------------------------------------------------------------------------------------------------- class Racer : public olc::PixelGameEngine { public: Racer() { sAppName = "F4 Racing"; } private: float car_pos = 0.0f; float distance = 0.0f; float speed = 0.0f; float curvature = 0.0f; float track_curve = 0.0f; float car_curve = 0.0f; float track_dist = 0.0f; float cur_lap_time = 0.0f; std::vector> vecTrack; std::list list_times; bool bothKeysPressed = false; // checking for dual key press; could probably work with more than 2 keys // custom control template olc::Key Move_Up = olc::W; olc::Key Move_Down = olc::S; olc::Key Move_Left = olc::A; olc::Key Move_Right = olc::D; std::array keyslist = { "UP", "DOWN", "LEFT", "RIGHT" }; int configKeyIndex = -1; char pressedKey; int cup = -1; // for cup selection screen int track = -1; // for track selection screen int player = 0; // for car selection screen bool start = false; public: // Menu Setup //---------------------------------------- bool activeOption = true; // highlighting current option int highlighted=0; struct MenuItem { std::string label; // label olc::vi2d pos; // label's position std::string label2; // another label to right of label }; struct Object { olc::vf2d pos{ 0, 0 }; // the x position represents how far to the left/right it is from the center. -1 ~ 1: road's range; the y position is how far along the track it is olc::vf2d size{ 1, 1 }; olc::Decal* decal = nullptr; olc::vf2d offset{ 0, 0 }; // how far off the image will be bool rendered = false; olc::vf2d drawPos; olc::vf2d drawSize; olc::vf2d drawOffsetPos; }; std::vector mainMenu; std::vector playGame; std::vector pickTrack; std::vector grandPrix; std::vector selectCar; std::vector controls; std::vector gameOver; std::vector gameObjects; enum class state { MAIN_MENU, PLAY_GAME, CAR_SELECT, CONTROLS, RUN_GAME, // game TRACK_SELECT, CUP_SELECT, // tracks CIRCUIT, FIG_EIGHT, ARROW_HEAD, track_4, OVERPASS, PEANUT, track_6, track_7, track_8, track_9, JX9, // special track ;) // cups INDIE, STREET, PRO, GRAND_PRIX }; state gameState = state::MAIN_MENU; // Variables //---------------------------------------- // lap times // player int score; // score at end of track; based on final place int lap; // current lap player is on int place; // current place player is in olc::Decal* car; // initialize car olc::Decal* hill; // initialize hill olc::Decal* grass; // initialize grass olc::Decal* road; // initialize road, curbs, and finish line olc::Decal* banner; // initialize banner olc::Decal* title; // initialize title banner std::string KeyToString(olc::Key key) { const std::array keyStrings = { "NONE","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9", "F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","UP","DOWN","LEFT","RIGHT","SPACE","TAB","SHIFT","CTRL","INS","DEL","HOME","END","PGUP","PGDN","BACK","ESCAPE","ENTER","ENTER", "PAUSE","SCROLL LK","Kp-0","Kp-1","Kp-2","Kp-3","Kp-4","Kp-5","Kp-6","Kp-7","Kp-8","Kp-9","Kp-*","Kp-/","Kp-+","Kp--","Kp-.",".","=",",","-","OEM_1", "OEM_2","OEM_3","OEM_4","OEM_5","OEM_6","OEM_7","OEM_8","CAPS LOCK","END" }; return keyStrings[key]; } //-------------------------------------------------------------------------------------------------- // Create Game Objects //-------------------------------------------------------------------------------------------------- bool OnUserCreate() override { mainMenu = // show menu options { { "START GAME", { ScreenWidth() / 2, ScreenHeight() / 5 * 3} }, { "CONTROLS", { ScreenWidth() / 2, ScreenHeight() / 5 * 3 + 64} }, { "QUIT", {ScreenWidth() / 2, ScreenHeight() / 5 * 3 + 128 } } }; playGame = { { "TRACK SELECTION", { ScreenWidth() / 2, ScreenHeight() / 4 } }, { "GRAND PRIX", { ScreenWidth() / 2, ScreenHeight() / 2 } }, { "BACK", { ScreenWidth() / 2, ScreenHeight() / 6 * 5} } }; pickTrack = // show track options { { "CIRCUIT", { ScreenWidth() / 4, ScreenHeight() / 4 } }, { "INFINITY", { ScreenWidth() / 2, ScreenHeight() / 4 } }, { "ARROWHEAD", { ScreenWidth() / 4 * 3, ScreenHeight() / 4 } }, {"Track 4", { ScreenWidth() / 4, ScreenHeight() / 2 }}, { "OVERPASS", { ScreenWidth() / 2, ScreenHeight() / 2 } }, {"Track 6", { ScreenWidth() / 4 * 3, ScreenHeight() / 2 } } , {"Track 7", { ScreenWidth() / 4, ScreenHeight() / 4 * 3 } }, {"Track 8", { ScreenWidth() / 2, ScreenHeight() / 4 * 3 } }, {"Track 9", { ScreenWidth() / 4 * 3, ScreenHeight() / 4 * 3 } }, { "BACK", { ScreenWidth() / 2, ScreenHeight() / 6 * 5} } }; grandPrix = // show race cups { { "INDIE CUP", { ScreenWidth() / 3, ScreenHeight() / 3 } }, { "STREET CUP", { ScreenWidth() / 3 * 2, ScreenHeight() / 3 } }, { "PRO CUP", { ScreenWidth() / 3, ScreenHeight() / 3 * 2 } }, { "GRAND PRIX", { ScreenWidth() / 3 * 2, ScreenHeight() / 3 * 2 } }, { "BACK", { ScreenWidth() / 2, ScreenHeight() / 6 * 5 } } }; selectCar = // pick a color { { "RED", { ScreenWidth() / 5, ScreenHeight() / 3 } }, { "BLUE", { ScreenWidth() / 5 * 2, ScreenHeight() / 3 } }, { "GREEN", { ScreenWidth() / 5 * 3, ScreenHeight() / 3 } }, { "GOLD", { ScreenWidth() / 5 * 4, ScreenHeight() / 3 } }, { "PURPLE", { ScreenWidth() / 5, ScreenHeight() / 3 * 2 } }, { "WHITE", { ScreenWidth() / 5 * 2, ScreenHeight() / 3 * 2 } }, { "ORANGE", { ScreenWidth() / 5 * 3, ScreenHeight() / 3 * 2 } }, { "BLACK", { ScreenWidth() / 5 * 4, ScreenHeight() / 3 * 2 } }, { "BACK", { ScreenWidth() / 2, ScreenHeight() / 6 * 5} } }; controls = // customize controls { { "FORWARD", { ScreenWidth() / 3, ScreenHeight() / 9 * 2 }, KeyToString(Move_Up) }, { "BACK", { ScreenWidth() / 3, ScreenHeight() / 9 * 3 }, KeyToString(Move_Down) }, { "LEFT", { ScreenWidth() / 3, ScreenHeight() / 9 * 4 }, KeyToString(Move_Left) }, { "RIGHT", { ScreenWidth() / 3, ScreenHeight() / 9 * 5 }, KeyToString(Move_Right) }, { "DEFAULT", { ScreenWidth() / 2, ScreenHeight() / 9 * 6} }, { "BACK", { ScreenWidth() / 2, ScreenHeight() / 6 * 5} } }; gameOver = { "Return to Main Menu" }; //---------------------------------------- car = new olc::Decal(new olc::Sprite("art/car.png")); // load car hill = new olc::Decal(new olc::Sprite("art/hills.png"), false, false); // load hill grass = new olc::Decal(new olc::Sprite("art/grass.png"), false, false); // load grass road = new olc::Decal(new olc::Sprite("art/road.png"), false, false); // load road, curbs, and finish line banner = new olc::Decal(new olc::Sprite("art/start.png"), false, false); // load banner title = new olc::Decal(new olc::Sprite("art/title.png")); // load title banner //---------------------------------------- //---------------------------------------- // may need to separate the track list into it's own function so as to declutter this one // especially, given that there's a bunch of stuff going on with the tracks with much redundancy // JX9 track layout // javidx9() { // these happen to be measured in meters, as per Javid's original construction // they'll likely be wonky for the game as it's not really modified to fit my units of measure vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 200.0f)); // sharp right vecTrack.push_back(std::make_pair(0.0f, 400.0f)); // long straight vecTrack.push_back(std::make_pair(-1.0f, 100.0f)); // soft left vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight vecTrack.push_back(std::make_pair(-1.0f, 200.0f)); // sharp left vecTrack.push_back(std::make_pair(1.0f, 200.0f)); // sharp right vecTrack.push_back(std::make_pair(0.0, 200.0f)); // straight vecTrack.push_back(std::make_pair(0.2f, 500.0f)); // gradual right vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight } // all 9 tracks below are running on yards for their distance units // this means the end results are in miles; rather than kilometers // enjoy the US measures of Burgers per Freedom Eagle /*{ // Classic Oval // Circuit() { // 1 mile loop; 1,760 yards vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) vecTrack.push_back(std::make_pair(0.0f, 270.0f)); // straight vecTrack.push_back(std::make_pair(-1.0f, 330.0f)); // left vecTrack.push_back(std::make_pair(0.0f, 550.0f)); // straight vecTrack.push_back(std::make_pair(-1.0f, 330.0f)); // left vecTrack.push_back(std::make_pair(0.0f, 270.0f)); // straight } // Infinity // FigureEight() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight); this is positioned somewhere after the intersection vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(-1.0f, 0.0f)); // left vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight; this crosses the first straight, to provide for chances of crashing vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight } // Arrow Head // ArrowHead() { // miles vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(-1.0f, 0.0f)); // left vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight } // track 4 // ???() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) } // Over/Under // OverPass() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight vecTrack.push_back(std::make_pair(1.0f, 0.0f)); // right vecTrack.push_back(std::make_pair(0.0f, 0.0f)); // straight } // track 6 // ???() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) } // track 7 // ???() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) } // track 8 // ???() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) } // track 9 // ???() { vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight) } }//*/ for (auto t : vecTrack) { track_dist += t.second; } distance = track_dist - 100; list_times = { 0,0,0,0,0 }; gameObjects.push_back({ /*{-1.5,track_dist - 50}, {10,6}, {0,240}*/ }); return true; } //-------------------------------------------------------------------------------------------------- // reset game //-------------------------------------------------------------------------------------------------- void Reset() { car_pos = 0.0f; distance = 0.0f; speed = 0.0f; curvature = 0.0f; track_curve = 0.0f; car_curve = 0.0f; //track_dist = 0.0f; will need to be moved elsewhere; the game will calculate *ALL* track sizes at the start, and then simply use the appropriate numbers when loading each track on request cur_lap_time = 0.0f; // Note: the following all need to be reset during the process, when they've been implemented // -player position (on screen) // -player position (on track) // -track section // -all lap times // -score // // if called from other parts of the program, other than the 'F1' key, it should be resetting to the menu // the 'F1' key is meant to reset the player on the current track they're on // } //-------------------------------------------------------------------------------------------------- // main menu //-------------------------------------------------------------------------------------------------- void DisplayMenu(std::vector list) { // drawing and coloring menu selections for (int i = 0; i < list.size(); i++) { olc::vi2d textSize = GetTextSize(list[i].label) / 2 * 4; olc::Pixel textCol = olc::GREY; // greys out unselected options if (highlighted == i) { textCol = olc::Pixel(255, 201, 14); // gold } DrawStringDecal(list[i].pos - textSize, list[i].label, textCol, { 4.0f, 4.0f }); if (list[i].label2.size() > 0) { olc::vi2d offset = { ScreenWidth() / 3, -textSize.y / 2 }; DrawStringDecal(list[i].pos + offset, list[i].label2, textCol, { 4.0f, 4.0f }); } } // selecting options highlighted = std::clamp(highlighted, 0, (int)list.size() - 1); // loop if out of range int shortestDist=9999999; int largestDist=-9999999; int targetItem=highlighted; if (GetKey(olc::Key::DOWN).bPressed) { for (int i=0;ilist[i].pos.y) { //It's above us. int dist = abs(list[highlighted].pos.y-list[i].pos.y); if (distlist[i].pos.y) { //It's above us. int dist = abs(list[highlighted].pos.y-list[i].pos.y); if (distlist[i].pos.x) { //It's to the left of us. int dist = abs(list[highlighted].pos.x-list[i].pos.x); if (dist>largestDist) { targetItem=i; largestDist=dist; } } } } } } highlighted=targetItem; } if (GetKey(olc::Key::LEFT).bPressed) { for (int i=0;ilist[i].pos.x) { //It's to the left of us. int dist = abs(list[highlighted].pos.x-list[i].pos.x); if (distlargestDist) { targetItem=i; largestDist=dist; } } } } } } highlighted=targetItem; } if (highlighted >= (int)list.size()) { highlighted = 0; // wrap around when above max } if (highlighted < 0) { highlighted = list.size() - 1; // wrap around when below zero } } //-------------------------------------------------------------------------------------------------- // controls //-------------------------------------------------------------------------------------------------- void GetAnyKeyPress(olc::Key keypress) override { if (configKeyIndex != -1) { switch (configKeyIndex) { case 0: { Move_Up = keypress; }break; case 1: { Move_Right = keypress; }break; case 2: { Move_Left = keypress; }break; case 3: { Move_Down = keypress; }break; } controls[configKeyIndex].label2 = KeyToString(keypress); configKeyIndex = -1; } } //-------------------------------------------------------------------------------------------------- // Main Game Function //-------------------------------------------------------------------------------------------------- bool OnUserUpdate(float fElapsedTime) override { if (GetKey(olc::Key::CTRL).bHeld && GetKey(olc::Key::F12).bHeld && !bothKeysPressed) { return false; } // dry skybox GradientFillRectDecal({ 0.0f, 0.0f }, { ScreenWidth() + 0.0f, ScreenHeight() / 2 + 0.0f }, olc::DARK_BLUE, olc::BLUE, olc::BLUE, olc::DARK_BLUE); // the hills have eyes; y'know, like in Super Mario Bros. What reference did you think was going to be here? DrawPartialDecal({ 0.0f, ScreenHeight() / 2 - 100.0f }, hill, { 0.0f + track_curve * 200, 0.0f }, { ScreenWidth() + 0.0f, (float)hill->sprite->height }, { 1.5f, 1.0f }); //DrawPartialDecal({ 180.0f, ScreenHeight() / 2 - 100.0f }, hill, { 0.0f + track_curve * 200, 0.0f }, { ScreenWidth() + 0.0f, (float)hill->sprite->height }); // draw grass DrawPartialWarpedDecal(grass, { { 0.0f, ScreenHeight() / 2 + 0.0f }, { -ScreenWidth() * 2 + 0.0f, ScreenHeight() + 0.0f}, { ScreenWidth() * 2 + ScreenWidth() + 0.0f, ScreenHeight() + 0.0f }, { ScreenWidth() + 0.0f, ScreenHeight() / 2 + 0.0f } }, { 0.0f, -distance }, { 1 * 32, 4 * 64 }); //-------------------------------------------------------------------------------------------------- if (gameState == state::MAIN_MENU) { // display title DrawDecal({ 95.5f, 95.5f }, title/*, {0.4f, 0.4f}*/); DisplayMenu(mainMenu); //DrawStringDecal({ ScreenWidth() / 2 + 0.0f, ScreenHeight() / 2 + 0.0f }, "START GAME", olc::WHITE, { 4.0f, 4.0f }); if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) { // vertical menu if (highlighted == 0) { gameState = state::PLAY_GAME; highlighted = 0; } else if (highlighted == 1) { gameState = state::CONTROLS; highlighted = 0; } else { return false; // quit game } } if (GetKey(olc::Key::ESCAPE).bPressed) { return false; // exit via Escape key } } //-------------------------------------------------------------------------------------------------- else if (gameState == state::PLAY_GAME) { DisplayMenu(playGame); if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) { // vertical menu if (highlighted == 0) { gameState = state::TRACK_SELECT; highlighted = 0; } else if (highlighted == 1) { gameState = state::CUP_SELECT; highlighted = 0; } else { gameState = state::MAIN_MENU; // return to menu highlighted = 0; } } if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) { gameState = state::MAIN_MENU; highlighted = 0; } } //-------------------------------------------------------------------------------------------------- else if (gameState == state::TRACK_SELECT) { DisplayMenu(pickTrack); if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) { if (highlighted == 0) { gameState = state::CIRCUIT; track = 1; gameState = state::CAR_SELECT; } else if (highlighted == 1) { gameState = state::FIG_EIGHT; track = 2; gameState = state::CAR_SELECT; } else if (highlighted == 2) { gameState = state::ARROW_HEAD; track = 3; gameState = state::CAR_SELECT; } else if (highlighted == 3) { gameState = state::track_4; track = 4; gameState = state::CAR_SELECT; } else if (highlighted == 4) { gameState = state::OVERPASS; track = 5; gameState = state::CAR_SELECT; } else if (highlighted == 5) { gameState = state::PEANUT; track = 6; gameState = state::CAR_SELECT; } else if (highlighted == 6) { gameState = state::track_6; track = 7; gameState = state::CAR_SELECT; } else if (highlighted == 7) { gameState = state::track_7; track = 8; gameState = state::CAR_SELECT; } else if (highlighted == 8) { gameState = state::track_8; track = 9; gameState = state::CAR_SELECT; } else { gameState = state::PLAY_GAME; highlighted = 0; } } if (GetKey(olc::Key::CTRL).bHeld && GetKey(olc::Key::F9).bHeld) { // okay, so, gotta work out how to do cheat codes it seems xD gameState = state::JX9; track = 0; gameState = state::CAR_SELECT; } if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) { gameState = state::PLAY_GAME; highlighted = 0; } } //-------------------------------------------------------------------------------------------------- else if (gameState == state::CUP_SELECT) { DisplayMenu(grandPrix); if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) { // 2 x 2 menu if (highlighted == 0) { gameState = state::INDIE; cup = 0; gameState = state::CAR_SELECT; } else if (highlighted == 1) { gameState = state::STREET; cup = 1; gameState = state::CAR_SELECT; } else if (highlighted == 2) { gameState = state::PRO; cup = 2; gameState = state::CAR_SELECT; } else if (highlighted == 3) { gameState = state::GRAND_PRIX; cup = 3; gameState = state::CAR_SELECT; } else { gameState = state::PLAY_GAME; // return to menu cup = -1; highlighted = 0; } } if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) { gameState = state::PLAY_GAME; highlighted = 0; } } //-------------------------------------------------------------------------------------------------- else if (gameState == state::CAR_SELECT) { DisplayMenu(selectCar); if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) { if (highlighted == 0) { // red car player = 0; gameState=state::RUN_GAME; } else if (highlighted == 1) { // blue car player = 1; } else if (highlighted == 2) { // green car player = 2; } else if (highlighted == 3) { // gold car player = 3; } else if (highlighted == 4) { // purple car player = 4; } else if (highlighted == 5) { // white car player = 5; } else if (highlighted == 6) { // orange car player = 6; } else if (highlighted == 7) { // black car player = 7; } else { if (cup != -1) { // return to cup select gameState = state::CUP_SELECT; } if (track != -1) { // return to track select gameState = state::TRACK_SELECT; } highlighted = 0; } } if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) { if (cup != -1) { // return to cup select gameState = state::CUP_SELECT; } if (track != -1) { // return to track select gameState = state::TRACK_SELECT; } highlighted = 0; } } //-------------------------------------------------------------------------------------------------- else if (gameState == state::CONTROLS) { DisplayMenu(controls); if (GetKey(olc::Key::ENTER).bPressed) { if (highlighted == 0) { configKeyIndex = highlighted; } else if (highlighted == 1) { configKeyIndex = highlighted; } else if (highlighted == 2) { configKeyIndex = highlighted; } else if (highlighted == 3) { configKeyIndex = highlighted; } else if (highlighted == 4) { // reset default controls DisplayMenu(controls); Move_Up = olc::W; Move_Down = olc::S; Move_Left = olc::A; Move_Right = olc::D; } else { gameState = state::MAIN_MENU; highlighted = 0; } } if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) { gameState = state::MAIN_MENU; } } //-------------------------------------------------------------------------------------------------- if (gameState == state::RUN_GAME) // run main game loop { Clear(olc::BLACK); // this still needed? we've since replaced it with the gradient sky box // get a point on the track float offset = 0; int TrackSection = 0; cur_lap_time += fElapsedTime; // record lap time if (distance >= track_dist) { distance -= track_dist; list_times.push_front(cur_lap_time); // push time to front list_times.pop_back(); // pop time to back cur_lap_time = 0.0f; } // find position on track (could optimize) << should probably optimize given the complexity of this game while (TrackSection < vecTrack.size() && offset <= distance) { offset += vecTrack[TrackSection].second; TrackSection++; } float TargetCurvature = vecTrack[TrackSection - 1].first; // drawing curves to screen float TrackCurveDiff = (TargetCurvature - curvature) * fElapsedTime * speed; // correcting the drawing of curves to the screen curvature += TrackCurveDiff; // update track curve difference track_curve += (curvature)*fElapsedTime * speed; // draw racetrack canvas std::vector pos; std::vector uvs; for (int y = 0; y < ScreenHeight() / 2; y++) { // racetrack canvas variables float perspective = (float)y / (ScreenHeight() / 2.0f); float mid_point = 0.5f + curvature * powf((1.0f - perspective), 3); float road_width = 0.1f + perspective * 0.8f; float curb_width = road_width * 0.15f; road_width *= 0.5f; int lf_curb = (mid_point - road_width) * ScreenWidth(); int rt_curb = (mid_point + road_width) * ScreenWidth(); int row = ScreenHeight() / 2 + y; float horizon = powf(1.0f - perspective, 2) * 80; // check for finish line if (distance + horizon > track_dist - 20 && distance + horizon < track_dist) { // draw checkerboard pattern uvs.push_back({ 0, fmod(((distance + horizon) - (track_dist - 20)) / 20.0f, 0.5f) + 0.5f }); uvs.push_back({ 1, fmod(((distance + horizon) - (track_dist - 20)) / 20.0f, 0.5f) + 0.5f }); } else { // draw standard track piece uvs.push_back({ 0, sinf(80.0f * powf(1.0f - perspective, 2) + distance) / 4 + 0.25f }); uvs.push_back({ 1, sinf(80.0f * powf(1.0f - perspective, 2) + distance) / 4 + 0.25f }); } // drawing grass and curb pos.push_back({ lf_curb + 0.0f, row + 0.0f }); pos.push_back({ rt_curb + 0.0f, row + 0.0f }); // black box rendering shenanigans; if I ever understand programming enough at a later date, I can figure out wtf is going on here int i = 0; for (Object& obj : gameObjects) { float horizonRange = powf(1.0f - perspective, 2) * ((distance + horizon) - obj.pos.y) / horizon; if (!obj.rendered && (distance + horizon) > obj.pos.y && (distance + horizon) < obj.pos.y + 80 && row >= ScreenHeight() / 2 + ScreenHeight() / 2 * horizonRange) { obj.drawPos = { (mid_point + road_width * obj.pos.x) * ScreenWidth(), horizonRange * (ScreenHeight() / 2) + ScreenHeight() / 2 }; obj.drawOffsetPos = { obj.offset.x, obj.size.y * road_width * -obj.offset.y }; obj.drawSize = { obj.size.x * road_width, obj.size.y * road_width }; obj.rendered = true; // yeah.... so, even after copying it manually, i still have no clue what this thing is doing, lol } } } // on the road again; drawing the track SetDecalStructure(olc::DecalStructure::STRIP); DrawPolygonDecal(road, pos, uvs); SetDecalStructure(olc::DecalStructure::FAN); // draw trackside props for (Object& obj : gameObjects) { if (obj.rendered) { if (obj.decal != nullptr) { DrawDecal(obj.drawPos + obj.drawOffsetPos, obj.decal, obj.drawSize); } else { FillRectDecal(obj.drawPos, obj.drawSize, olc::BLUE); } } } //---------------------------------------- // debug quick-reset if (GetKey(olc::Key::F4).bPressed) { Reset(); } // move forward if (GetKey(olc::Key::W).bHeld || GetKey(olc::Key::UP).bHeld) { speed += 2.0f * fElapsedTime; } else // might remove this in a later stage of development { speed -= 1.0f * fElapsedTime; } // move back ? if (GetKey(olc::Key::S).bHeld || GetKey(olc::Key::DOWN).bHeld) { distance -= 20.0f * fElapsedTime; } int car_dir = 0; // default car facing // move left if (GetKey(olc::Key::A).bHeld || GetKey(olc::Key::LEFT).bHeld) { car_curve -= 0.7f * fElapsedTime; car_dir = -1; } // move right if (GetKey(olc::Key::D).bHeld || GetKey(olc::Key::RIGHT).bHeld) { car_curve += 0.7f * fElapsedTime; car_dir = +1; } // slow car if on grass if (fabs(car_curve - track_curve) >= 0.8f) { speed -= 5.0f * fElapsedTime; } // clamp speed if (speed < 0.0f) speed = 0.0f; if (speed > 1.0f) speed = 1.0f; // move car along track according to car speed distance += (70.0f * speed) * fElapsedTime; // draw car car_pos = car_curve - track_curve; DrawDecal({ ScreenWidth() / 2 - 128 + car_pos * ScreenWidth() / 2, ScreenHeight() / 4 * 3.0f - 128 }, car); SetPixelMode(olc::Pixel::ALPHA); // spedometer std::string mph = std::to_string(speed); DrawString(4, 4, mph); // show time auto disp_time = [](float t) { int min = t / 60.0f; int sec = t - (min * 60.0f); int milli = (t - (float)sec) * 1000.0f; // need to change this out to a 'DrawString()' function instead return std::to_string(min) + "." + std::to_string(sec) + ":" + std::to_string(milli); }; // correct for the first '0' in the seconds timer //if () //{ // // draw min + '0' + sec + milli // DrawString(4, 16, disp_time(cur_lap_time)); //} //else //{ DrawString(4, 16, disp_time(cur_lap_time)); //} // display last 5 lap times int j = 30; for (auto l : list_times) { DrawString(10, j, disp_time(l)); j += 10; } // debug code //std::string numb1 = std::to_string(car_pos); //DrawString(4, 26, numb1); //std::string numb2 = std::to_string(track_dist); //DrawString(4, 38, numb2); } return true; } }; //-------------------------------------------------------------------------------------------------- int main() { Racer game; if (game.Construct(1280, 720, 1, 1)) game.Start(); return 0; } //-------------------------------------------------------------------------------------------------- // END OF FILE //--------------------------------------------------------------------------------------------------