//--------------------------------------------------------------------------------------------------
// 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 < std : : pair < float , float > > vecTrack ;
std : : list < float > 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 < std : : string , 4 > keyslist = { " UP " , " DOWN " , " LEFT " , " RIGHT " } ;
int configKeyIndex = - 1 ;
char pressedKey ;
public :
// Menu Setup
//----------------------------------------
bool activeOption = true ; // highlighting current option
int highlighted ;
struct MenuItem
{
std : : string label ; // label
olc : : vi2d pos ; // label's position
std : : string label2 ; //Another label. To the 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 < MenuItem > mainMenu ;
std : : vector < MenuItem > playGame ;
std : : vector < MenuItem > pickTrack ;
std : : vector < MenuItem > grandPrix ;
std : : vector < MenuItem > selectCar ;
std : : vector < MenuItem > controls ;
std : : vector < std : : string > gameOver ;
std : : vector < Object > 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 ,
track_5 ,
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 < std : : string , 98 > 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 " , " NUMPAD0 " , " NUMPAD1 " , " NUMPAD2 " , " NUMPAD3 " , " NUMPAD4 " , " NUMPAD5 " , " NUMPAD6 " , " NUMPAD7 " , " NUMPAD8 " , " NUMPAD9 " , " NUMPAD* " , " NUMPAD/ " , " NUMPAD+ " , " NUMPAD- " , " NUMPAD. " , " . " , " = " , " , " , " - " , " 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 ( ) / 4 * 3 } }
} ;
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 } }
} ;
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 } }
} ;
controls =
{
{ " FORWARD " , { ScreenWidth ( ) / 3 , 64 } , KeyToString ( Move_Up ) } ,
{ " BACK " , { ScreenWidth ( ) / 3 , 64 + 64 } , KeyToString ( Move_Down ) } ,
{ " LEFT " , { ScreenWidth ( ) / 3 , 64 + 128 } , KeyToString ( Move_Left ) } ,
{ " RIGHT " , { ScreenWidth ( ) / 3 , 64 + 192 } , KeyToString ( Move_Right ) }
} ;
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
// JX9 track layout
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // start/finish line
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
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 < MenuItem > list , bool horizontal = false )
{
// 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 = { 256 , - 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
if ( ! horizontal )
{
// vertical controls
if ( GetKey ( olc : : Key : : W ) . bPressed | | GetKey ( olc : : Key : : UP ) . bPressed )
{
highlighted - - ; // up
}
if ( GetKey ( olc : : Key : : S ) . bPressed | | GetKey ( olc : : Key : : DOWN ) . bPressed )
{
highlighted + + ; // down
}
}
else
{
if ( GetKey ( olc : : Key : : A ) . bPressed | | GetKey ( olc : : Key : : LEFT ) . bPressed )
{
highlighted - - ; // left
}
if ( GetKey ( olc : : Key : : D ) . bPressed | | GetKey ( olc : : Key : : RIGHT ) . bPressed )
{
highlighted + + ; // right
}
}
if ( highlighted > = ( int ) list . size ( ) )
{
highlighted = 0 ; // wrap around when above max
}
if ( highlighted < 0 )
{
highlighted = list . size ( ) - 1 ; // wrap around when below zero
}
}
void GetAnyKeyPress ( olc : : Key keypress ) override {
if ( configKeyIndex ! = - 1 ) {
switch ( configKeyIndex ) {
case 0 : {
Move_Up = keypress ;
} break ;
case 1 : {
Move_Down = keypress ;
} break ;
case 2 : {
Move_Left = keypress ;
} break ;
case 3 : {
Move_Right = 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 )
{
DrawStringDecal ( { 0 , 0 } , std : : string ( 1 , pressedKey ) , olc : : WHITE , { 3 , 3 } ) ;
// 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 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 1 )
{
gameState = state : : FIG_EIGHT ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 2 )
{
gameState = state : : ARROW_HEAD ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 3 )
{
gameState = state : : track_4 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 4 )
{
gameState = state : : OVERPASS ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 5 )
{
gameState = state : : track_5 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 6 )
{
gameState = state : : track_6 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 7 )
{
gameState = state : : track_7 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 8 )
{
gameState = state : : track_8 ;
gameState = state : : CAR_SELECT ;
}
//else
else
{
// return to previous screen, in this instance it should change according to whether the player
// came down via the Cup selection screen, or the Track selection screen
}
}
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 ;
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 ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 1 )
{
gameState = state : : STREET ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 2 )
{
gameState = state : : PRO ;
gameState = state : : CAR_SELECT ;
}
else if ( highlighted = = 3 )
{
gameState = state : : GRAND_PRIX ;
gameState = state : : CAR_SELECT ;
}
else
{
gameState = state : : PLAY_GAME ; // return to menu
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 : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
// needs to go to previous screen
// previous screen may be:
// -Cup Select
// -Track Select
}
}
//--------------------------------------------------------------------------------------------------
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
{
gameState = state : : MAIN_MENU ;
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
gameState = state : : MAIN_MENU ;
}
}
//--------------------------------------------------------------------------------------------------
else // 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 < olc : : vf2d > pos ;
std : : vector < olc : : vf2d > 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 , ( float ) fmod ( ( ( distance + horizon ) - ( track_dist - 20 ) ) / 20.0f , 0.5f ) + 0.5f } ) ;
uvs . push_back ( { 1 , ( float ) 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
//--------------------------------------------------------------------------------------------------