├── .gitattributes ├── .gitignore ├── FPSSprites ├── fps_fireball1.spr ├── fps_lamp1.spr └── fps_wall1.spr ├── FroggerSprites ├── bus.spr ├── car1.spr ├── car2.spr ├── frog.spr ├── home.spr ├── log.spr ├── pavement.spr ├── wall.spr └── water.spr ├── JarioSprites ├── leveljario.spr └── minijario.spr ├── LICENSE ├── NeuralNetwork.cpp ├── OneLoneCoder_AR_OpticFlow.cpp ├── OneLoneCoder_Asteroids.cpp ├── OneLoneCoder_Balls1.cpp ├── OneLoneCoder_Balls2.cpp ├── OneLoneCoder_CaveDiver.cpp ├── OneLoneCoder_ComandLineFPS_2.cpp ├── OneLoneCoder_CommandLineFPS.cpp ├── OneLoneCoder_FlappyBird.cpp ├── OneLoneCoder_Frogger.cpp ├── OneLoneCoder_GameOfLife.cpp ├── OneLoneCoder_LogitechG13Twitch.cpp ├── OneLoneCoder_Matrix.cpp ├── OneLoneCoder_Mazes.cpp ├── OneLoneCoder_OverEngineered.cpp ├── OneLoneCoder_PanAndZoom.cpp ├── OneLoneCoder_PathFinding_AStar.cpp ├── OneLoneCoder_PerlinNoise.cpp ├── OneLoneCoder_PlatformGame1.cpp ├── OneLoneCoder_Pseudo3DPlanesMode7.cpp ├── OneLoneCoder_RacingLines.cpp ├── OneLoneCoder_RetroArcadeRacer.cpp ├── OneLoneCoder_Snake.cpp ├── OneLoneCoder_Splines1.cpp ├── OneLoneCoder_Splines2.cpp ├── OneLoneCoder_SpriteEditor.cpp ├── OneLoneCoder_Tetris.cpp ├── OneLoneCoder_Webcam.cpp ├── README.md ├── junk ├── mariokart.spr ├── olcConsoleGameEngine.h ├── sky1.spr └── worms ├── OneLoneCoder_Worms1.cpp ├── OneLoneCoder_Worms2.cpp ├── OneLoneCoder_Worms3.cpp ├── worms.spr └── worms1.spr /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /FPSSprites/fps_fireball1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FPSSprites/fps_fireball1.spr -------------------------------------------------------------------------------- /FPSSprites/fps_lamp1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FPSSprites/fps_lamp1.spr -------------------------------------------------------------------------------- /FPSSprites/fps_wall1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FPSSprites/fps_wall1.spr -------------------------------------------------------------------------------- /FroggerSprites/bus.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/bus.spr -------------------------------------------------------------------------------- /FroggerSprites/car1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/car1.spr -------------------------------------------------------------------------------- /FroggerSprites/car2.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/car2.spr -------------------------------------------------------------------------------- /FroggerSprites/frog.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/frog.spr -------------------------------------------------------------------------------- /FroggerSprites/home.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/home.spr -------------------------------------------------------------------------------- /FroggerSprites/log.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/log.spr -------------------------------------------------------------------------------- /FroggerSprites/pavement.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/pavement.spr -------------------------------------------------------------------------------- /FroggerSprites/wall.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/wall.spr -------------------------------------------------------------------------------- /FroggerSprites/water.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/FroggerSprites/water.spr -------------------------------------------------------------------------------- /JarioSprites/leveljario.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/JarioSprites/leveljario.spr -------------------------------------------------------------------------------- /JarioSprites/minijario.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/JarioSprites/minijario.spr -------------------------------------------------------------------------------- /NeuralNetwork.cpp: -------------------------------------------------------------------------------- 1 | // OneLoneCoder 2 | // Simple Neural Network Example with training 3 | // May have bugs - is not finished yet 4 | // WORK IN PROGRESS 5 | // User supplies an input and output vector to determine how many 6 | // neurons are in input and output layers 7 | // can also specify hidden layers (which are same size as input) 8 | 9 | // Example shows training XOR 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class NeuralNetwork 18 | { 19 | struct Synapse 20 | { 21 | int nAfferentNeuron; 22 | int nEfferentNeuron; 23 | float fWeight; 24 | }; 25 | 26 | struct Neuron 27 | { 28 | vector vecAfferentSynapse; 29 | vector vecEfferentSynapse; 30 | float fSummedInput; 31 | float fOutput; 32 | float fError; 33 | bool bIsInput; 34 | bool bIsOutput; 35 | }; 36 | 37 | vector &vecInput; 38 | vector &vecOutput; 39 | vector vecNodes; 40 | vector vecSynapses; 41 | 42 | public: 43 | NeuralNetwork(vector &in, vector &out, int nHiddenLayers) : vecInput(in), vecOutput(out) 44 | { 45 | // Construct Network (assumes non recurrent, and fully connected, hidden layers same size as input) 46 | int nNodeCount = 0; 47 | 48 | vector vecPreviousLayerIDs; 49 | vector vecNewPreviousLayerIDs; 50 | 51 | // Input Layer 52 | for (auto n : vecInput) 53 | { 54 | Neuron node; 55 | node.bIsInput = true; 56 | node.bIsOutput = false; 57 | vecNodes.push_back(node); 58 | vecNewPreviousLayerIDs.push_back(vecNodes.size() - 1); 59 | } 60 | 61 | // Hidden Layers (assumes same size as input) 62 | for (int h = 0; h < nHiddenLayers; h++) 63 | { 64 | vecPreviousLayerIDs = vecNewPreviousLayerIDs; 65 | vecNewPreviousLayerIDs.clear(); 66 | 67 | for (auto n : vecInput) // For layer size 68 | { 69 | // Create New Neuron 70 | Neuron node; 71 | node.bIsInput = false; 72 | node.bIsOutput = false; 73 | vecNodes.push_back(node); 74 | vecNewPreviousLayerIDs.push_back(vecNodes.size() - 1); 75 | 76 | // Fully connect it to previous layer 77 | for (auto p : vecPreviousLayerIDs) 78 | { 79 | // Create synapse 80 | Synapse syn; 81 | syn.nAfferentNeuron = p; 82 | syn.fWeight = (float)rand() / (float)RAND_MAX; //0.0f; 83 | syn.nEfferentNeuron = vecNodes.size() - 1; 84 | vecSynapses.push_back(syn); 85 | 86 | // Connect Afferent Node to synapse 87 | vecNodes[p].vecEfferentSynapse.push_back(vecSynapses.size() - 1); 88 | 89 | // Connect this node to synapse 90 | vecNodes.back().vecAfferentSynapse.push_back(vecSynapses.size() - 1); 91 | } 92 | } 93 | } 94 | 95 | // Output layer 96 | vecPreviousLayerIDs = vecNewPreviousLayerIDs; 97 | for (auto n : vecOutput) // For layer size 98 | { 99 | // Create New Neuron 100 | Neuron node; 101 | node.bIsInput = false; 102 | node.bIsOutput = true; 103 | vecNodes.push_back(node); 104 | vecNewPreviousLayerIDs.push_back(vecNodes.size() - 1); 105 | 106 | // Fully connect it to previous layer 107 | for (auto p : vecPreviousLayerIDs) 108 | { 109 | // Create synapse 110 | Synapse syn; 111 | syn.nAfferentNeuron = p; 112 | syn.fWeight = (float)rand() / (float)RAND_MAX; //0.0f; 113 | syn.nEfferentNeuron = vecNodes.size() - 1; 114 | vecSynapses.push_back(syn); 115 | 116 | // Connect Afferent Node to synapse 117 | vecNodes[p].vecEfferentSynapse.push_back(vecSynapses.size() - 1); 118 | 119 | // Connect this node to synapse 120 | vecNodes.back().vecAfferentSynapse.push_back(vecSynapses.size() - 1); 121 | } 122 | } 123 | } 124 | 125 | void PropagateForward() 126 | { 127 | int in_count = 0; 128 | int out_count = 0; 129 | for (auto &n : vecNodes) 130 | { 131 | n.fSummedInput = 0; 132 | if (n.bIsInput) 133 | { 134 | // Node is input, so just set output directly 135 | n.fOutput = vecInput[in_count]; 136 | in_count++; 137 | } 138 | else 139 | { 140 | // Accumulate input via weighted synapses 141 | for (auto s : n.vecAfferentSynapse) 142 | n.fSummedInput += vecNodes[vecSynapses[s].nAfferentNeuron].fOutput * vecSynapses[s].fWeight; 143 | 144 | // Activation Function 145 | n.fOutput = 1.0f / (1.0f + expf(-n.fSummedInput * 2.0f)); 146 | 147 | if (n.bIsOutput) 148 | { 149 | vecOutput[out_count] = n.fOutput; 150 | out_count++; 151 | } 152 | } 153 | } 154 | } 155 | 156 | void PropagateBackwards(vector &vecTrain, float fDelta) 157 | { 158 | // Go through neurons backwards, this way first vecTrain.size() are output layer 159 | // and layers are propagated backwards in correct order - smart eh? :P 160 | for (int n = vecNodes.size() - 1; n >= 0; n--) 161 | { 162 | if (vecNodes[n].bIsOutput) // Output Layer 163 | { 164 | vecNodes[n].fError = (vecTrain[vecTrain.size() - (vecNodes.size() - n)] - vecNodes[n].fOutput) * (vecNodes[n].fOutput * (1.0f - vecNodes[n].fOutput)); 165 | } 166 | else 167 | { 168 | vecNodes[n].fError = 0.0f; 169 | for (auto effsyn : vecNodes[n].vecEfferentSynapse) 170 | { 171 | float fEfferentNeuronError = vecNodes[vecSynapses[effsyn].nEfferentNeuron].fError; 172 | float fEfferentSynapseWeight = vecSynapses[effsyn].fWeight; 173 | vecNodes[n].fError += (fEfferentSynapseWeight * fEfferentNeuronError) * (vecNodes[n].fOutput * (1.0f - vecNodes[n].fOutput)); 174 | } 175 | } 176 | } 177 | 178 | // Update Synaptic Weights 179 | for (auto &s : vecSynapses) 180 | s.fWeight += fDelta * vecNodes[s.nEfferentNeuron].fError * vecNodes[s.nAfferentNeuron].fOutput; 181 | } 182 | 183 | }; 184 | 185 | int main() 186 | { 187 | vector input = { 0,0 }; 188 | vector output = { 0 }; 189 | vector train = { 0 }; 190 | 191 | NeuralNetwork nn(input, output, 1); 192 | 193 | // Example XOR Training 194 | for (int i = 0; i < 5000; i++) 195 | { 196 | int nA = rand() % 2; 197 | int nB = rand() % 2; 198 | int nY = nA ^ nB; 199 | 200 | input[0] = (float)nA; 201 | input[1] = (float)nB; 202 | train[0] = (float)nY; 203 | 204 | nn.PropagateForward(); 205 | 206 | int nO = (int)(output[0] + 0.5f); 207 | cout << "A: " << nA << " B: " << nB << " Y: " << nY << " NN: " << nO << " (" << to_string(output[0]) << ") " << (nO == nY ? "PASS" : "FAIL") << endl; 208 | nn.PropagateBackwards(train, 0.5f); 209 | } 210 | 211 | 212 | //system("pause"); 213 | return 0; 214 | }; -------------------------------------------------------------------------------- /OneLoneCoder_AR_OpticFlow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Augmenting Reality #1 - Optical Flow 3 | "My arms are tired now." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | GNU GPLv3 16 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 17 | 18 | From Javidx9 :) 19 | ~~~~~~~~~~~~~~~ 20 | Hello! Ultimately I don't care what you use this for. It's intended to be 21 | educational, and perhaps to the oddly minded - a little bit of fun. 22 | Please hack this, change it and use it in any way you see fit. You acknowledge 23 | that I am not responsible for anything bad that happens as a result of 24 | your actions. However this code is protected by GNU GPLv3, see the license in the 25 | github repo. This means you must attribute me if you use it. You can view this 26 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 27 | Cheers! 28 | 29 | Background 30 | ~~~~~~~~~~ 31 | Optical flow is the determination of motion in a video stream at the pixel level. 32 | Each pixel is associated with a motion vector that is used to create a map of 33 | velocity vectors which are then used to interact with a virtual object superimposed 34 | on the video stream. 35 | 36 | You will need to have watched my webcam video for this one to make sense! 37 | https://youtu.be/pk1Y_26j1Y4 38 | 39 | Author 40 | ~~~~~~ 41 | Twitter: @javidx9 42 | Blog: www.onelonecoder.com 43 | 44 | Video: 45 | ~~~~~~ 46 | https://youtu.be/aNtzgoEGC1Y 47 | 48 | Last Updated: 15/11/2017 49 | */ 50 | #include 51 | #include 52 | #include 53 | using namespace std; 54 | 55 | #include "olcConsoleGameEngine.h" 56 | #include "escapi.h" 57 | 58 | class OneLoneCoder_AROpticFlow : public olcConsoleGameEngine 59 | { 60 | public: 61 | OneLoneCoder_AROpticFlow() 62 | { 63 | m_sAppName = L"Augmented Reality Part #1 - Optic Flow"; 64 | } 65 | 66 | private: 67 | union RGBint 68 | { 69 | int rgb; 70 | unsigned char c[4]; 71 | }; 72 | 73 | int nCameras = 0; 74 | SimpleCapParams capture; 75 | 76 | // 2D Maps for image processing 77 | float *fOldCamera = nullptr; // Previous raw frame from camera 78 | float *fNewCamera = nullptr; // Recent raw frame from camera 79 | float *fFilteredCamera = nullptr; // low-pass filtered image 80 | float *fOldFilteredCamera = nullptr; // low-pass filtered image 81 | float *fOldMotionImage = nullptr; // previous motion image 82 | float *fMotionImage = nullptr; // recent motion image 83 | float *fFlowX = nullptr; // x-component of flow field vector 84 | float *fFlowY = nullptr; // y-component of flow field vector 85 | 86 | // Object Physics Variables 87 | float fBallX = 0.0f; // Ball position 2D 88 | float fBallY = 0.0f; 89 | float fBallVX = 0.0f; // Ball Velocity 2D 90 | float fBallVY = 0.0f; 91 | 92 | protected: 93 | virtual bool OnUserCreate() 94 | { 95 | // Initialise webcam to console dimensions 96 | nCameras = setupESCAPI(); 97 | if (nCameras == 0) return false; 98 | capture.mWidth = ScreenWidth(); 99 | capture.mHeight = ScreenHeight(); 100 | capture.mTargetBuf = new int[ScreenWidth() * ScreenHeight()]; 101 | if (initCapture(0, &capture) == 0) return false; 102 | 103 | // Allocate memory for images 104 | fOldCamera = new float[ScreenWidth() * ScreenHeight()]; 105 | fNewCamera = new float[ScreenWidth() * ScreenHeight()]; 106 | fFilteredCamera = new float[ScreenWidth() * ScreenHeight()]; 107 | fOldFilteredCamera = new float[ScreenWidth() * ScreenHeight()]; 108 | fFlowX = new float[ScreenWidth() * ScreenHeight()]; 109 | fFlowY = new float[ScreenWidth() * ScreenHeight()]; 110 | fOldMotionImage = new float[ScreenWidth() * ScreenHeight()]; 111 | fMotionImage = new float[ScreenWidth() * ScreenHeight()]; 112 | 113 | // Initialise images to 0 114 | memset(fOldCamera, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 115 | memset(fNewCamera, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 116 | memset(fFilteredCamera, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 117 | memset(fOldFilteredCamera, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 118 | memset(fFlowX, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 119 | memset(fFlowY, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 120 | memset(fOldMotionImage, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 121 | memset(fMotionImage, 0, sizeof(float) * ScreenWidth() * ScreenHeight()); 122 | 123 | // Set ball position to middle of frame 124 | fBallX = ScreenWidth() / 2.0f; 125 | fBallY = ScreenHeight() / 2.0f; 126 | return true; 127 | } 128 | 129 | virtual bool OnUserUpdate(float fElapsedTime) 130 | { 131 | // Lambda function to draw "image" in greyscale 132 | auto draw_image = [&](float *image) 133 | { 134 | for (int x = 0; x < capture.mWidth; x++) 135 | { 136 | for (int y = 0; y < capture.mHeight; y++) 137 | { 138 | wchar_t sym = 0; 139 | short bg_col = 0; 140 | short fg_col = 0; 141 | int pixel_bw = (int)(image[y*ScreenWidth() + x] * 13.0f); 142 | switch (pixel_bw) 143 | { 144 | case 0: bg_col = BG_BLACK; fg_col = FG_BLACK; sym = PIXEL_SOLID; break; 145 | case 1: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; 146 | case 2: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; 147 | case 3: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; 148 | case 4: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; 149 | case 5: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_QUARTER; break; 150 | case 6: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_HALF; break; 151 | case 7: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_THREEQUARTERS; break; 152 | case 8: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_SOLID; break; 153 | case 9: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; 154 | case 10: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; 155 | case 11: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; 156 | case 12: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; 157 | } 158 | Draw(x, y, sym, bg_col | fg_col); 159 | } 160 | } 161 | }; 162 | 163 | // Lambda function to read from a 2D array without error 164 | auto get_pixel = [&](float* image, int x, int y) 165 | { 166 | if (x >= 0 && x < ScreenWidth() && y >= 0 && y < ScreenHeight()) 167 | return image[y*ScreenWidth() + x]; 168 | else 169 | return 0.0f; 170 | }; 171 | 172 | // === Capture & Filter New Input Image ========================================== 173 | 174 | // Get Image from webcam 175 | doCapture(0); while (isCaptureDone(0) == 0) {} 176 | 177 | // Do Temporal Filtering per pixel 178 | for (int y = 0; y < capture.mHeight; y++) 179 | for (int x = 0; x < capture.mWidth; x++) 180 | { 181 | RGBint col; 182 | int id = y * capture.mWidth + x; 183 | col.rgb = capture.mTargetBuf[id]; 184 | int r = col.c[2], g = col.c[1], b = col.c[0]; 185 | float fR = (float)r / 255.0f; 186 | float fG = (float)g / 255.0f; 187 | float fB = (float)b / 255.0f; 188 | 189 | // Store previous camera frame for temporal processing 190 | fOldCamera[y*ScreenWidth() + x] = fNewCamera[y*ScreenWidth() + x]; 191 | 192 | // Store previous camera frame for temporal processing 193 | fOldFilteredCamera[y*ScreenWidth() + x] = fFilteredCamera[y*ScreenWidth() + x]; 194 | 195 | // Store previous motion only frame 196 | fOldMotionImage[y*ScreenWidth() + x] = fMotionImage[y*ScreenWidth() + x]; 197 | 198 | // Calculate luminance (greyscale equivalent) of pixel 199 | float fLuminance = 0.2987f * fR + 0.5870f * fG + 0.1140f * fB; 200 | fNewCamera[y*ScreenWidth() + x] = fLuminance; 201 | 202 | // Low-Pass filter camera image, to remove pixel jitter 203 | fFilteredCamera[y*ScreenWidth() + x] += (fNewCamera[y*ScreenWidth() + x] - fFilteredCamera[y*ScreenWidth() + x]) * 0.8f; 204 | 205 | // Create motion image as difference between two successive camera frames 206 | float fDiff = fabs(get_pixel(fFilteredCamera, x, y) - get_pixel(fOldFilteredCamera, x, y)); 207 | 208 | // Threshold motion image to remove filter out camera noise 209 | fMotionImage[y*ScreenWidth() + x] = (fDiff >= 0.05f) ? fDiff : 0.0f; 210 | } 211 | 212 | // === Calculate Optic Flow Vector Map ========================================== 213 | 214 | // Brute Force Local Spatial Pattern Matching 215 | int nPatchSize = 9; 216 | int nSearchSize = 7; 217 | 218 | for (int x = 0; x < ScreenWidth(); x++) 219 | { 220 | for (int y = 0; y < ScreenHeight(); y++) 221 | { 222 | // Initialise serach variables 223 | float fPatchDifferenceMax = INFINITY; 224 | float fPatchDifferenceX = 0.0f; 225 | float fPatchDifferenceY = 0.0f; 226 | fFlowX[y*ScreenWidth() + x] = 0.0f; 227 | fFlowY[y*ScreenWidth() + x] = 0.0f; 228 | 229 | // Search over a given rectangular area for a "patch" of old image 230 | // that "resembles" a patch of the new image. 231 | for (int sx = 0; sx < nSearchSize; sx++) 232 | { 233 | for (int sy = 0; sy < nSearchSize; sy++) 234 | { 235 | // Search vector is centre of patch test 236 | int nSearchVectorX = x + (sx - nSearchSize / 2); 237 | int nSearchVectorY = y + (sy - nSearchSize / 2); 238 | 239 | float fAccumulatedDifference = 0.0f; 240 | 241 | // For each pixel in search patch, accumulate difference with base patch 242 | for (int px = 0; px < nPatchSize; px++) 243 | for (int py = 0; py < nPatchSize; py++) 244 | { 245 | // Work out search patch offset indices 246 | int nPatchPixelX = nSearchVectorX + (px - nPatchSize / 2); 247 | int nPatchPixelY = nSearchVectorY + (py - nPatchSize / 2); 248 | 249 | // Work out base patch indices 250 | int nBasePixelX = x + (px - nPatchSize / 2); 251 | int nBasePixelY = y + (py - nPatchSize / 2); 252 | 253 | // Get adjacent values for each patch 254 | float fPatchPixel = get_pixel(fNewCamera, nPatchPixelX, nPatchPixelY); 255 | float fBasePixel = get_pixel(fOldCamera, nBasePixelX, nBasePixelY); 256 | 257 | // Accumulate difference 258 | fAccumulatedDifference += fabs(fPatchPixel - fBasePixel); 259 | } 260 | 261 | // Record the vector offset for the search patch that is the 262 | // least different to the base patch 263 | if (fAccumulatedDifference <= fPatchDifferenceMax) 264 | { 265 | fPatchDifferenceMax = fAccumulatedDifference; 266 | fFlowX[y*ScreenWidth() + x] = (float)(nSearchVectorX - x); 267 | fFlowY[y*ScreenWidth() + x] = (float)(nSearchVectorY - y); 268 | } 269 | } 270 | } 271 | } 272 | } 273 | 274 | // Modulate Optic Flow Vector Map with motion map, to remove vectors that 275 | // errornously indicate large local motion 276 | for (int i = 0; i < ScreenWidth()*ScreenHeight(); i++) 277 | { 278 | fFlowX[i] *= fMotionImage[i] > 0 ? 1.0f : 0.0f; 279 | fFlowY[i] *= fMotionImage[i] > 0 ? 1.0f : 0.0f; 280 | } 281 | 282 | // === Update Ball Physics ======================================================== 283 | 284 | // Ball velocity is updated by optic flow vector field 285 | fBallVX += 100.0f * fFlowX[(int)fBallY * ScreenWidth() + (int)fBallX] * fElapsedTime; 286 | fBallVY += 100.0f * fFlowY[(int)fBallY * ScreenWidth() + (int)fBallX] * fElapsedTime; 287 | 288 | // Ball position is updated by velocity 289 | fBallX += 1.0f * fBallVX * fElapsedTime; 290 | fBallY += 1.0f * fBallVY * fElapsedTime; 291 | 292 | // Add "drag" effect to ball velocity 293 | fBallVX *= 0.85f; 294 | fBallVY *= 0.85f; 295 | 296 | // Wrap ball around screen 297 | if (fBallX >= ScreenWidth()) fBallX -= (float)ScreenWidth(); 298 | if (fBallY >= ScreenHeight()) fBallY -= (float)ScreenHeight(); 299 | if (fBallX < 0) fBallX += (float)ScreenWidth(); 300 | if (fBallY < 0) fBallY += (float)ScreenHeight(); 301 | 302 | // === Update Screen ================================================================= 303 | 304 | // Draw Camera Image 305 | draw_image(fNewCamera); 306 | 307 | // Draw "Ball" 308 | Fill(fBallX - 4, fBallY - 4, fBallX + 4, fBallY + 4, PIXEL_SOLID, FG_RED); 309 | return true; 310 | } 311 | }; 312 | 313 | int main() 314 | { 315 | OneLoneCoder_AROpticFlow game; 316 | game.ConstructConsole(80, 60, 16, 16); 317 | game.Start(); 318 | return 0; 319 | } 320 | -------------------------------------------------------------------------------- /OneLoneCoder_Asteroids.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Code-It-Yourself! Asteroids at the command prompt (quick and simple c++) 3 | "This one just kept growing..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | GNU GPLv3 16 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 17 | 18 | From Javidx9 :) 19 | ~~~~~~~~~~~~~~~ 20 | Hello! Ultimately I don't care what you use this for. It's intended to be 21 | educational, and perhaps to the oddly minded - a little bit of fun. 22 | Please hack this, change it and use it in any way you see fit. You acknowledge 23 | that I am not responsible for anything bad that happens as a result of 24 | your actions. However this code is protected by GNU GPLv3, see the license in the 25 | github repo. This means you must attribute me if you use it. You can view this 26 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 27 | Cheers! 28 | 29 | Background 30 | ~~~~~~~~~~ 31 | I needed a quick video to keep up with my upload schedule, and thought "oh asteroids 32 | must be simple", and indeed it was, but it was a bit rubbish, and didn't really 33 | offer anything educational, until I made the decision to ditch sprites and use 34 | vector models like the original game. This made the game considerably more sophisticated, 35 | has opened up new possibilities for the game engine, and just goes to show, jus 'cos 36 | it's old, doesn't mean it's irrelevant. 37 | 38 | I ended up with a demonstration of physics, wireframe graphics, 2D matrix transformations, 39 | toroidal space mapping and vector manipulation. 40 | 41 | Controls 42 | ~~~~~~~~ 43 | Left and Right arrow keys steer the ship. Up arrow is thrust. There is no reverse thrust. 44 | Space bar fires bullets. Get the highest score by surviving waves of asteroids. 45 | 46 | Author 47 | ~~~~~~ 48 | Twitter: @javidx9 49 | Blog: www.onelonecoder.com 50 | YouTube: www.youtube.com/javidx9 51 | 52 | Video: 53 | ~~~~~~ 54 | https://youtu.be/QgDR8LrRZhk 55 | 56 | Last Updated: 30/08/2017 57 | */ 58 | 59 | #include 60 | #include 61 | #include 62 | using namespace std; 63 | 64 | #include "olcConsoleGameEngine.h" 65 | 66 | class OneLoneCoder_Asteroids : public olcConsoleGameEngine 67 | { 68 | public: 69 | OneLoneCoder_Asteroids() 70 | { 71 | m_sAppName = L"Asteroids"; 72 | } 73 | 74 | private: 75 | struct sSpaceObject 76 | { 77 | int nSize; 78 | float x; 79 | float y; 80 | float dx; 81 | float dy; 82 | float angle; 83 | }; 84 | 85 | vector vecAsteroids; 86 | vector vecBullets; 87 | sSpaceObject player; 88 | bool bDead = false; 89 | int nScore = 0; 90 | 91 | vector> vecModelShip; 92 | vector> vecModelAsteroid; 93 | 94 | protected: 95 | // Called by olcConsoleGameEngine 96 | virtual bool OnUserCreate() 97 | { 98 | vecModelShip = 99 | { 100 | { 0.0f, -5.0f}, 101 | {-2.5f, +2.5f}, 102 | {+2.5f, +2.5f} 103 | }; // A simple Isoceles Triangle 104 | 105 | // Create a "jagged" circle for the asteroid. It's important it remains 106 | // mostly circular, as we do a simple collision check against a perfect 107 | // circle. 108 | int verts = 20; 109 | for (int i = 0; i < verts; i++) 110 | { 111 | float noise = (float)rand() / (float)RAND_MAX * 0.4f + 0.8f; 112 | vecModelAsteroid.push_back(make_pair(noise * sinf(((float)i / (float)verts) * 6.28318f), 113 | noise * cosf(((float)i / (float)verts) * 6.28318f))); 114 | } 115 | 116 | ResetGame(); 117 | return true; 118 | } 119 | 120 | void ResetGame() 121 | { 122 | // Initialise Player Position 123 | player.x = ScreenWidth() / 2.0f; 124 | player.y = ScreenHeight() / 2.0f; 125 | player.dx = 0.0f; 126 | player.dy = 0.0f; 127 | player.angle = 0.0f; 128 | 129 | vecBullets.clear(); 130 | vecAsteroids.clear(); 131 | 132 | // Put in two asteroids 133 | vecAsteroids.push_back({ (int)16, 20.0f, 20.0f, 8.0f, -6.0f, 0.0f }); 134 | vecAsteroids.push_back({ (int)16, 100.0f, 20.0f, -5.0f, 3.0f, 0.0f }); 135 | 136 | // Reset game 137 | bDead = false; 138 | nScore = false; 139 | } 140 | 141 | // Implements "wrap around" for various in-game sytems 142 | void WrapCoordinates(float ix, float iy, float &ox, float &oy) 143 | { 144 | ox = ix; 145 | oy = iy; 146 | if (ix < 0.0f) ox = ix + (float)ScreenWidth(); 147 | if (ix >= (float)ScreenWidth()) ox = ix - (float)ScreenWidth(); 148 | if (iy < 0.0f) oy = iy + (float)ScreenHeight(); 149 | if (iy >= (float)ScreenHeight()) oy = iy - (float)ScreenHeight(); 150 | } 151 | 152 | // Overriden to handle toroidal drawing routines 153 | virtual void Draw(int x, int y, wchar_t c = 0x2588, short col = 0x000F) 154 | { 155 | float fx, fy; 156 | WrapCoordinates(x, y, fx, fy); 157 | olcConsoleGameEngine::Draw(fx, fy, c, col); 158 | } 159 | 160 | bool IsPointInsideCircle(float cx, float cy, float radius, float x, float y) 161 | { 162 | return sqrt((x-cx)*(x-cx) + (y-cy)*(y-cy)) < radius; 163 | } 164 | 165 | // Called by olcConsoleGameEngine 166 | virtual bool OnUserUpdate(float fElapsedTime) 167 | { 168 | if (bDead) 169 | ResetGame(); 170 | 171 | // Clear Screen 172 | Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, 0); 173 | 174 | // Steer Ship 175 | if (m_keys[VK_LEFT].bHeld) 176 | player.angle -= 5.0f * fElapsedTime; 177 | if (m_keys[VK_RIGHT].bHeld) 178 | player.angle += 5.0f * fElapsedTime; 179 | 180 | // Thrust? Apply ACCELERATION 181 | if (m_keys[VK_UP].bHeld) 182 | { 183 | // ACCELERATION changes VELOCITY (with respect to time) 184 | player.dx += sin(player.angle) * 20.0f * fElapsedTime; 185 | player.dy += -cos(player.angle) * 20.0f * fElapsedTime; 186 | } 187 | 188 | // VELOCITY changes POSITION (with respect to time) 189 | player.x += player.dx * fElapsedTime; 190 | player.y += player.dy * fElapsedTime; 191 | 192 | // Keep ship in gamespace 193 | WrapCoordinates(player.x, player.y, player.x, player.y); 194 | 195 | // Check ship collision with asteroids 196 | for (auto &a : vecAsteroids) 197 | if (IsPointInsideCircle(a.x, a.y, a.nSize, player.x, player.y)) 198 | bDead = true; // Uh oh... 199 | 200 | // Fire Bullet in direction of player 201 | if (m_keys[VK_SPACE].bReleased) 202 | vecBullets.push_back({ 0, player.x, player.y, 50.0f * sinf(player.angle), -50.0f * cosf(player.angle), 100.0f }); 203 | 204 | // Update and draw asteroids 205 | for (auto &a : vecAsteroids) 206 | { 207 | // VELOCITY changes POSITION (with respect to time) 208 | a.x += a.dx * fElapsedTime; 209 | a.y += a.dy * fElapsedTime; 210 | a.angle += 0.5f * fElapsedTime; // Add swanky rotation :) 211 | 212 | // Asteroid coordinates are kept in game space (toroidal mapping) 213 | WrapCoordinates(a.x, a.y, a.x, a.y); 214 | 215 | // Draw Asteroids 216 | DrawWireFrameModel(vecModelAsteroid, a.x, a.y, a.angle, (float)a.nSize, FG_YELLOW); 217 | } 218 | 219 | // Any new asteroids created after collision detection are stored 220 | // in a temporary vector, so we don't interfere with the asteroids 221 | // vector iterator in the for(auto) 222 | vector newAsteroids; 223 | 224 | // Update Bullets 225 | for (auto &b : vecBullets) 226 | { 227 | b.x += b.dx * fElapsedTime; 228 | b.y += b.dy * fElapsedTime; 229 | WrapCoordinates(b.x, b.y, b.x, b.y); 230 | b.angle -= 1.0f * fElapsedTime; 231 | 232 | // Check collision with asteroids 233 | for (auto &a : vecAsteroids) 234 | { 235 | //if (IsPointInsideRectangle(a.x, a.y, a.x + a.nSize, a.y + a.nSize, b.x, b.y)) 236 | if(IsPointInsideCircle(a.x, a.y, a.nSize, b.x, b.y)) 237 | { 238 | // Asteroid Hit - Remove bullet 239 | // We've already updated the bullets, so force bullet to be offscreen 240 | // so it is cleaned up by the removal algorithm. 241 | b.x = -100; 242 | 243 | // Create child asteroids 244 | if (a.nSize > 4) 245 | { 246 | float angle1 = ((float)rand() / (float)RAND_MAX) * 6.283185f; 247 | float angle2 = ((float)rand() / (float)RAND_MAX) * 6.283185f; 248 | newAsteroids.push_back({ (int)a.nSize >> 1 ,a.x, a.y, 10.0f * sinf(angle1), 10.0f * cosf(angle1), 0.0f }); 249 | newAsteroids.push_back({ (int)a.nSize >> 1 ,a.x, a.y, 10.0f * sinf(angle2), 10.0f * cosf(angle2), 0.0f }); 250 | } 251 | 252 | // Remove asteroid - Same approach as bullets 253 | a.x = -100; 254 | nScore += 100; // Small score increase for hitting asteroid 255 | } 256 | } 257 | } 258 | 259 | // Append new asteroids to existing vector 260 | for(auto a:newAsteroids) 261 | vecAsteroids.push_back(a); 262 | 263 | // Clear up dead objects - They are out of game space 264 | 265 | // Remove asteroids that have been blown up 266 | if (vecAsteroids.size() > 0) 267 | { 268 | auto i = remove_if(vecAsteroids.begin(), vecAsteroids.end(), [&](sSpaceObject o) { return (o.x < 0); }); 269 | if (i != vecAsteroids.end()) 270 | vecAsteroids.erase(i); 271 | } 272 | 273 | if (vecAsteroids.empty()) // If no asteroids, level complete! :) - you win MORE asteroids! 274 | { 275 | // Level Clear 276 | nScore += 1000; // Large score for level progression 277 | vecAsteroids.clear(); 278 | vecBullets.clear(); 279 | 280 | // Add two new asteroids, but in a place where the player is not, we'll simply 281 | // add them 90 degrees left and right to the player, their coordinates will 282 | // be wrapped by th enext asteroid update 283 | vecAsteroids.push_back({ (int)16, 30.0f * sinf(player.angle - 3.14159f/2.0f) + player.x, 284 | 30.0f * cosf(player.angle - 3.14159f/2.0f) + player.y, 285 | 10.0f * sinf(player.angle), 10.0f*cosf(player.angle), 0.0f }); 286 | 287 | vecAsteroids.push_back({ (int)16, 30.0f * sinf(player.angle + 3.14159f/2.0f) + player.x, 288 | 30.0f * cosf(player.angle + 3.14159f/2.0f) + player.y, 289 | 10.0f * sinf(-player.angle), 10.0f*cosf(-player.angle), 0.0f }); 290 | } 291 | 292 | // Remove bullets that have gone off screen 293 | if (vecBullets.size() > 0) 294 | { 295 | auto i = remove_if(vecBullets.begin(), vecBullets.end(), [&](sSpaceObject o) { return (o.x < 1 || o.y < 1 || o.x >= ScreenWidth() - 1 || o.y >= ScreenHeight() - 1); }); 296 | if (i != vecBullets.end()) 297 | vecBullets.erase(i); 298 | } 299 | 300 | // Draw Bullets 301 | for (auto b : vecBullets) 302 | Draw(b.x, b.y); 303 | 304 | // Draw Ship 305 | DrawWireFrameModel(vecModelShip, player.x, player.y, player.angle); 306 | 307 | // Draw Score 308 | DrawString(2, 2, L"SCORE: " + to_wstring(nScore)); 309 | return true; 310 | } 311 | 312 | void DrawWireFrameModel(const vector> &vecModelCoordinates, float x, float y, float r = 0.0f, float s = 1.0f, short col = FG_WHITE) 313 | { 314 | // pair.first = x coordinate 315 | // pair.second = y coordinate 316 | 317 | // Create translated model vector of coordinate pairs 318 | vector> vecTransformedCoordinates; 319 | int verts = vecModelCoordinates.size(); 320 | vecTransformedCoordinates.resize(verts); 321 | 322 | // Rotate 323 | for (int i = 0; i < verts; i++) 324 | { 325 | vecTransformedCoordinates[i].first = vecModelCoordinates[i].first * cosf(r) - vecModelCoordinates[i].second * sinf(r); 326 | vecTransformedCoordinates[i].second = vecModelCoordinates[i].first * sinf(r) + vecModelCoordinates[i].second * cosf(r); 327 | } 328 | 329 | // Scale 330 | for (int i = 0; i < verts; i++) 331 | { 332 | vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first * s; 333 | vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second * s; 334 | } 335 | 336 | // Translate 337 | for (int i = 0; i < verts; i++) 338 | { 339 | vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first + x; 340 | vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second + y; 341 | } 342 | 343 | // Draw Closed Polygon 344 | for (int i = 0; i < verts + 1; i++) 345 | { 346 | int j = (i + 1); 347 | DrawLine(vecTransformedCoordinates[i % verts].first, vecTransformedCoordinates[i % verts].second, 348 | vecTransformedCoordinates[j % verts].first, vecTransformedCoordinates[j % verts].second, PIXEL_SOLID, col); 349 | } 350 | } 351 | }; 352 | 353 | 354 | int main() 355 | { 356 | // Use olcConsoleGameEngine derived app 357 | OneLoneCoder_Asteroids game; 358 | game.ConstructConsole(160, 100, 8, 8); 359 | game.Start(); 360 | return 0; 361 | } 362 | -------------------------------------------------------------------------------- /OneLoneCoder_Balls1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Programming Balls! #1 Circle Vs Circle Collisions 3 | "..it's just balls bangin' together init..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | GNU GPLv3 16 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 17 | 18 | From Javidx9 :) 19 | ~~~~~~~~~~~~~~~ 20 | Hello! Ultimately I don't care what you use this for. It's intended to be 21 | educational, and perhaps to the oddly minded - a little bit of fun. 22 | Please hack this, change it and use it in any way you see fit. You acknowledge 23 | that I am not responsible for anything bad that happens as a result of 24 | your actions. However this code is protected by GNU GPLv3, see the license in the 25 | github repo. This means you must attribute me if you use it. You can view this 26 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 27 | Cheers! 28 | 29 | Background 30 | ~~~~~~~~~~ 31 | Collision detection engines can get quite complicated. This program shows the interactions 32 | between circular objects of different sizes and masses. Use Left mouse button to select 33 | and drag a ball to examin static collisions, and use Right mouse button to apply velocity 34 | to the balls as if using a pool/snooker/billiards cue. 35 | 36 | Author 37 | ~~~~~~ 38 | Twitter: @javidx9 39 | Blog: www.onelonecoder.com 40 | 41 | Video: 42 | ~~~~~~ 43 | Part #1 https://youtu.be/LPzyNOHY3A4 44 | 45 | Last Updated: 21/01/2017 46 | */ 47 | 48 | #include 49 | #include 50 | using namespace std; 51 | 52 | #include "olcConsoleGameEngine.h" 53 | 54 | 55 | struct sBall 56 | { 57 | float px, py; 58 | float vx, vy; 59 | float ax, ay; 60 | float radius; 61 | float mass; 62 | 63 | int id; 64 | }; 65 | 66 | 67 | class CirclePhysics : public olcConsoleGameEngine 68 | { 69 | public: 70 | CirclePhysics() 71 | { 72 | m_sAppName = L"Circle Physics"; 73 | } 74 | 75 | private: 76 | vector> modelCircle; 77 | vector vecBalls; 78 | sBall *pSelectedBall = nullptr; 79 | 80 | 81 | // Adds a ball to the vector 82 | void AddBall(float x, float y, float r = 5.0f) 83 | { 84 | sBall b; 85 | b.px = x; b.py = y; 86 | b.vx = 0; b.vy = 0; 87 | b.ax = 0; b.ay = 0; 88 | b.radius = r; 89 | b.mass = r * 10.0f; 90 | 91 | b.id = vecBalls.size(); 92 | vecBalls.emplace_back(b); 93 | } 94 | 95 | 96 | public: 97 | bool OnUserCreate() 98 | { 99 | // Define Circle Model 100 | modelCircle.push_back({ 0.0f, 0.0f }); 101 | int nPoints = 20; 102 | for (int i = 0; i < nPoints; i++) 103 | modelCircle.push_back({ cosf(i / (float)(nPoints - 1) * 2.0f * 3.14159f) , sinf(i / (float)(nPoints - 1) * 2.0f * 3.14159f) }); 104 | 105 | float fDefaultRad = 8.0f; 106 | //AddBall(ScreenWidth() * 0.25f, ScreenHeight() * 0.5f, fDefaultRad); 107 | //AddBall(ScreenWidth() * 0.75f, ScreenHeight() * 0.5f, fDefaultRad); 108 | 109 | // Add 10 Random Balls 110 | for (int i = 0; i <10; i++) 111 | AddBall(rand() % ScreenWidth(), rand() % ScreenHeight(), rand() % 16 + 2); 112 | 113 | 114 | return true; 115 | } 116 | 117 | bool OnUserUpdate(float fElapsedTime) 118 | { 119 | auto DoCirclesOverlap = [](float x1, float y1, float r1, float x2, float y2, float r2) 120 | { 121 | return fabs((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= (r1 + r2)*(r1 + r2); 122 | }; 123 | 124 | auto IsPointInCircle = [](float x1, float y1, float r1, float px, float py) 125 | { 126 | return fabs((x1 - px)*(x1 - px) + (y1 - py)*(y1 - py)) < (r1 * r1); 127 | }; 128 | 129 | if (m_mouse[0].bPressed || m_mouse[1].bPressed) 130 | { 131 | pSelectedBall = nullptr; 132 | for (auto &ball : vecBalls) 133 | { 134 | if (IsPointInCircle(ball.px, ball.py, ball.radius, m_mousePosX, m_mousePosY)) 135 | { 136 | pSelectedBall = &ball; 137 | break; 138 | } 139 | } 140 | } 141 | 142 | if (m_mouse[0].bHeld) 143 | { 144 | if (pSelectedBall != nullptr) 145 | { 146 | pSelectedBall->px = m_mousePosX; 147 | pSelectedBall->py = m_mousePosY; 148 | } 149 | } 150 | 151 | if (m_mouse[0].bReleased) 152 | { 153 | pSelectedBall = nullptr; 154 | } 155 | 156 | if (m_mouse[1].bReleased) 157 | { 158 | if (pSelectedBall != nullptr) 159 | { 160 | // Apply velocity 161 | pSelectedBall->vx = 5.0f * ((pSelectedBall->px) - (float)m_mousePosX); 162 | pSelectedBall->vy = 5.0f * ((pSelectedBall->py) - (float)m_mousePosY); 163 | } 164 | 165 | pSelectedBall = nullptr; 166 | } 167 | 168 | 169 | vector> vecCollidingPairs; 170 | 171 | // Update Ball Positions 172 | for (auto &ball : vecBalls) 173 | { 174 | // Add Drag to emulate rolling friction 175 | ball.ax = -ball.vx * 0.8f; 176 | ball.ay = -ball.vy * 0.8f; 177 | 178 | // Update ball physics 179 | ball.vx += ball.ax * fElapsedTime; 180 | ball.vy += ball.ay * fElapsedTime; 181 | ball.px += ball.vx * fElapsedTime; 182 | ball.py += ball.vy * fElapsedTime; 183 | 184 | // Wrap the balls around screen 185 | if (ball.px < 0) ball.px += (float)ScreenWidth(); 186 | if (ball.px >= ScreenWidth()) ball.px -= (float)ScreenWidth(); 187 | if (ball.py < 0) ball.py += (float)ScreenHeight(); 188 | if (ball.py >= ScreenHeight()) ball.py -= (float)ScreenHeight(); 189 | 190 | // Clamp velocity near zero 191 | if (fabs(ball.vx*ball.vx + ball.vy*ball.vy) < 0.01f) 192 | { 193 | ball.vx = 0; 194 | ball.vy = 0; 195 | } 196 | } 197 | 198 | // Static collisions, i.e. overlap 199 | for (auto &ball : vecBalls) 200 | { 201 | for (auto &target : vecBalls) 202 | { 203 | if (ball.id != target.id) 204 | { 205 | if (DoCirclesOverlap(ball.px, ball.py, ball.radius, target.px, target.py, target.radius)) 206 | { 207 | // Collision has occured 208 | vecCollidingPairs.push_back({ &ball, &target }); 209 | 210 | // Distance between ball centers 211 | float fDistance = sqrtf((ball.px - target.px)*(ball.px - target.px) + (ball.py - target.py)*(ball.py - target.py)); 212 | 213 | // Calculate displacement required 214 | float fOverlap = 0.5f * (fDistance - ball.radius - target.radius); 215 | 216 | // Displace Current Ball away from collision 217 | ball.px -= fOverlap * (ball.px - target.px) / fDistance; 218 | ball.py -= fOverlap * (ball.py - target.py) / fDistance; 219 | 220 | // Displace Target Ball away from collision 221 | target.px += fOverlap * (ball.px - target.px) / fDistance; 222 | target.py += fOverlap * (ball.py - target.py) / fDistance; 223 | } 224 | } 225 | } 226 | } 227 | 228 | // Now work out dynamic collisions 229 | for (auto c : vecCollidingPairs) 230 | { 231 | sBall *b1 = c.first; 232 | sBall *b2 = c.second; 233 | 234 | // Distance between balls 235 | float fDistance = sqrtf((b1->px - b2->px)*(b1->px - b2->px) + (b1->py - b2->py)*(b1->py - b2->py)); 236 | 237 | // Normal 238 | float nx = (b2->px - b1->px) / fDistance; 239 | float ny = (b2->py - b1->py) / fDistance; 240 | 241 | // Tangent 242 | float tx = -ny; 243 | float ty = nx; 244 | 245 | // Dot Product Tangent 246 | float dpTan1 = b1->vx * tx + b1->vy * ty; 247 | float dpTan2 = b2->vx * tx + b2->vy * ty; 248 | 249 | // Dot Product Normal 250 | float dpNorm1 = b1->vx * nx + b1->vy * ny; 251 | float dpNorm2 = b2->vx * nx + b2->vy * ny; 252 | 253 | // Conservation of momentum in 1D 254 | float m1 = (dpNorm1 * (b1->mass - b2->mass) + 2.0f * b2->mass * dpNorm2) / (b1->mass + b2->mass); 255 | float m2 = (dpNorm2 * (b2->mass - b1->mass) + 2.0f * b1->mass * dpNorm1) / (b1->mass + b2->mass); 256 | 257 | // Update ball velocities 258 | b1->vx = tx * dpTan1 + nx * m1; 259 | b1->vy = ty * dpTan1 + ny * m1; 260 | b2->vx = tx * dpTan2 + nx * m2; 261 | b2->vy = ty * dpTan2 + ny * m2; 262 | 263 | // Wikipedia Version - Maths is smarter but same 264 | //float kx = (b1->vx - b2->vx); 265 | //float ky = (b1->vy - b2->vy); 266 | //float p = 2.0 * (nx * kx + ny * ky) / (b1->mass + b2->mass); 267 | //b1->vx = b1->vx - p * b2->mass * nx; 268 | //b1->vy = b1->vy - p * b2->mass * ny; 269 | //b2->vx = b2->vx + p * b1->mass * nx; 270 | //b2->vy = b2->vy + p * b1->mass * ny; 271 | } 272 | 273 | // Clear Screen 274 | Fill(0, 0, ScreenWidth(), ScreenHeight(), ' '); 275 | 276 | // Draw Balls 277 | for (auto ball : vecBalls) 278 | DrawWireFrameModel(modelCircle, ball.px, ball.py, atan2f(ball.vy, ball.vx), ball.radius, FG_WHITE); 279 | 280 | // Draw static collisions 281 | for (auto c : vecCollidingPairs) 282 | DrawLine(c.first->px, c.first->py, c.second->px, c.second->py, PIXEL_SOLID, FG_RED); 283 | 284 | // Draw Cue 285 | if (pSelectedBall != nullptr) 286 | DrawLine(pSelectedBall->px, pSelectedBall->py, m_mousePosX, m_mousePosY, PIXEL_SOLID, FG_BLUE); 287 | 288 | return true; 289 | } 290 | 291 | }; 292 | 293 | 294 | int main() 295 | { 296 | CirclePhysics game; 297 | if (game.ConstructConsole(160, 120, 8, 8)) 298 | game.Start(); 299 | else 300 | wcout << L"Could not construct console" << endl; 301 | 302 | return 0; 303 | }; 304 | -------------------------------------------------------------------------------- /OneLoneCoder_CaveDiver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - MicroGame1 - Cave Diver 3 | "Err..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | Often as an exercise, I'll try to write code where every byte of source code 33 | should count. I feel that this is an excellent way to practice logic, code 34 | comprehension and smarmy arrogance. 35 | 36 | 37 | 38 | Author 39 | ~~~~~~ 40 | Twitter: @javidx9 41 | Blog: www.onelonecoder.com 42 | 43 | Video: 44 | ~~~~~~ 45 | https://youtu.be/oJGx8cqaJLc 46 | 47 | Last Updated: 16/10/2017 48 | */ 49 | 50 | #include 51 | #include 52 | using namespace std; 53 | using namespace chrono; 54 | 55 | // NOTE!! WILL ONLY WORK WITH UPDATED WINDOWS 10 56 | #include 57 | 58 | int main() 59 | { 60 | DWORD d = 0; 61 | GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &d); 62 | SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), d | 0x0004); // ??? Mystery Console Mode Feature! 63 | GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &d); 64 | SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), d | 0x0200); 65 | 66 | auto tp1 = system_clock::now(); auto tp2 = tp1; 67 | char s[80 * 30] = {'\0'}; 68 | char n[10]; 69 | 70 | auto pfxy = [](const char* b, int x, int y) {printf("\x1B[%d;%dH%s", y, x, b);}; 71 | auto sbxy = [&](int x, int y, char c){s[y * 80 + x] = c; }; 72 | 73 | float t = 0.0f, f = 0.0f, tt = 0.0f; // Timing Variables 74 | float w = 20.0f, m = 40.0f, tw = 20.0f, tm = 40.0f; // World Shape Variables 75 | float e = 1.0f, c = 40.0f; // Difficulty, Player Position 76 | int r = 0, h = 1000; // Player Score 77 | 78 | while (h>0) 79 | { 80 | // Timing 81 | this_thread::sleep_for(10ms); 82 | tp2 = system_clock::now(); 83 | duration elapsedTime = tp2 - tp1; tp1 = tp2; 84 | t+=(f=elapsedTime.count()); tt += f; 85 | 86 | // Get and Handle Input 87 | for (int i = 0; i < 2; i++) if (GetAsyncKeyState("\x25\x27"[i])!= 0) c += (i?1:-1) * 40.0f * f; 88 | 89 | // Update World 90 | if (t >= 1.5f){ t -= 1.5f; tw = rand() % 10 + 10; tm = rand() % (76 - ((int)tw >> 1)) + 4; } 91 | e += f*0.001f; w += (tw - w) * e * f; m += (tm - m) * e * f; 92 | float p = sinf(tt * e) * m / 2.0f + 40.0f; 93 | 94 | // Fill Row 95 | for (int x = 0; x < 79; x++) 96 | if (x+1 < p-w/2 || x> p+w/2) sbxy(x,r,'.'); 97 | else if (x == (int)(p - (w / 2)) || x== (int)(p + (w / 2))) sbxy(x, r, '#'); 98 | else sbxy(x,r,' '); 99 | 100 | // Scrolling Effect 101 | sbxy(79, r, '\0'); (r += 1) %= 28; 102 | 103 | // Draw To Screen 104 | for (int y = 1; y < 28; y++) pfxy(&s[((y+28+r)%28) * 80], 0, y); 105 | pfxy("O-V-O", c - 2, 2); pfxy(" O-O ", c - 2, 3); pfxy(" V ", c - 2, 4); 106 | 107 | // Collision Checking & Health Update 108 | h -= s[((4 + r) % 28) * 80 + ((int)c - 2)] == '.'; 109 | 110 | // Draw HUD 111 | pfxy("===============================================================================", 0, 28); 112 | pfxy("Cave Diver - www.onelonecoder.com - Left Arrow / Right Arrow - Survive!", 2, 29); 113 | _itoa_s((int)(tt * 100.0f), n, 10, 10); 114 | pfxy("Distance Fallen: ", 2, 30); pfxy(n, 19, 30); 115 | 116 | _itoa_s(h, n, 10, 10); 117 | pfxy("Health: ", 40, 30); pfxy(n, 48, 30); 118 | } 119 | 120 | pfxy("Dead...\n", 70, 30); 121 | system("pause"); // :-P 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /OneLoneCoder_CommandLineFPS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Command Line First Person Shooter (FPS) Engine 3 | "Why were games not done like this is 1990?" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | Whilst waiting for TheMexicanRunner to start the finale of his NesMania project, 33 | his Twitch stream had a counter counting down for a couple of hours until it started. 34 | With some time on my hands, I thought it might be fun to see what the graphical 35 | capabilities of the console are. Turns out, not very much, but hey, it's nice to think 36 | Wolfenstein could have existed a few years earlier, and in just ~200 lines of code. 37 | 38 | IMPORTANT!!!! 39 | ~~~~~~~~~~~~~ 40 | READ ME BEFORE RUNNING!!! This program expects the console dimensions to be set to 41 | 120 Columns by 40 Rows. I recommend a small font "Consolas" at size 16. You can do this 42 | by running the program, and right clicking on the console title bar, and specifying 43 | the properties. You can also choose to default to them in the future. 44 | 45 | Future Modifications 46 | ~~~~~~~~~~~~~~~~~~~~ 47 | 1) Shade block segments based on angle from player, i.e. less light reflected off 48 | walls at side of player. Walls straight on are brightest. 49 | 2) Find an interesting and optimised ray-tracing method. I'm sure one must exist 50 | to more optimally search the map space 51 | 3) Add bullets! 52 | 4) Add bad guys! 53 | 54 | Author 55 | ~~~~~~ 56 | Twitter: @javidx9 57 | Blog: www.onelonecoder.com 58 | 59 | Video: 60 | ~~~~~~ 61 | xxxxxxx 62 | 63 | Last Updated: 27/02/2017 64 | */ 65 | 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | using namespace std; 72 | 73 | #include 74 | #include 75 | 76 | int nScreenWidth = 120; // Console Screen Size X (columns) 77 | int nScreenHeight = 40; // Console Screen Size Y (rows) 78 | int nMapWidth = 16; // World Dimensions 79 | int nMapHeight = 16; 80 | 81 | float fPlayerX = 14.7f; // Player Start Position 82 | float fPlayerY = 5.09f; 83 | float fPlayerA = 0.0f; // Player Start Rotation 84 | float fFOV = 3.14159f / 4.0f; // Field of View 85 | float fDepth = 16.0f; // Maximum rendering distance 86 | float fSpeed = 5.0f; // Walking Speed 87 | 88 | int main() 89 | { 90 | // Create Screen Buffer 91 | wchar_t *screen = new wchar_t[nScreenWidth*nScreenHeight]; 92 | HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 93 | SetConsoleActiveScreenBuffer(hConsole); 94 | DWORD dwBytesWritten = 0; 95 | 96 | // Create Map of world space # = wall block, . = space 97 | wstring map; 98 | map += L"#########......."; 99 | map += L"#..............."; 100 | map += L"#.......########"; 101 | map += L"#..............#"; 102 | map += L"#......##......#"; 103 | map += L"#......##......#"; 104 | map += L"#..............#"; 105 | map += L"###............#"; 106 | map += L"##.............#"; 107 | map += L"#......####..###"; 108 | map += L"#......#.......#"; 109 | map += L"#......#.......#"; 110 | map += L"#..............#"; 111 | map += L"#......#########"; 112 | map += L"#..............#"; 113 | map += L"################"; 114 | 115 | auto tp1 = chrono::system_clock::now(); 116 | auto tp2 = chrono::system_clock::now(); 117 | 118 | while (1) 119 | { 120 | // We'll need time differential per frame to calculate modification 121 | // to movement speeds, to ensure consistant movement, as ray-tracing 122 | // is non-deterministic 123 | tp2 = chrono::system_clock::now(); 124 | chrono::duration elapsedTime = tp2 - tp1; 125 | tp1 = tp2; 126 | float fElapsedTime = elapsedTime.count(); 127 | 128 | 129 | // Handle CCW Rotation 130 | if (GetAsyncKeyState((unsigned short)'A') & 0x8000) 131 | fPlayerA -= (fSpeed * 0.75f) * fElapsedTime; 132 | 133 | // Handle CW Rotation 134 | if (GetAsyncKeyState((unsigned short)'D') & 0x8000) 135 | fPlayerA += (fSpeed * 0.75f) * fElapsedTime; 136 | 137 | // Handle Forwards movement & collision 138 | if (GetAsyncKeyState((unsigned short)'W') & 0x8000) 139 | { 140 | fPlayerX += sinf(fPlayerA) * fSpeed * fElapsedTime;; 141 | fPlayerY += cosf(fPlayerA) * fSpeed * fElapsedTime;; 142 | if (map.c_str()[(int)fPlayerX * nMapWidth + (int)fPlayerY] == '#') 143 | { 144 | fPlayerX -= sinf(fPlayerA) * fSpeed * fElapsedTime;; 145 | fPlayerY -= cosf(fPlayerA) * fSpeed * fElapsedTime;; 146 | } 147 | } 148 | 149 | // Handle backwards movement & collision 150 | if (GetAsyncKeyState((unsigned short)'S') & 0x8000) 151 | { 152 | fPlayerX -= sinf(fPlayerA) * fSpeed * fElapsedTime;; 153 | fPlayerY -= cosf(fPlayerA) * fSpeed * fElapsedTime;; 154 | if (map.c_str()[(int)fPlayerX * nMapWidth + (int)fPlayerY] == '#') 155 | { 156 | fPlayerX += sinf(fPlayerA) * fSpeed * fElapsedTime;; 157 | fPlayerY += cosf(fPlayerA) * fSpeed * fElapsedTime;; 158 | } 159 | } 160 | 161 | for (int x = 0; x < nScreenWidth; x++) 162 | { 163 | // For each column, calculate the projected ray angle into world space 164 | float fRayAngle = (fPlayerA - fFOV/2.0f) + ((float)x / (float)nScreenWidth) * fFOV; 165 | 166 | // Find distance to wall 167 | float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase 168 | float fDistanceToWall = 0.0f; // resolution 169 | 170 | bool bHitWall = false; // Set when ray hits wall block 171 | bool bBoundary = false; // Set when ray hits boundary between two wall blocks 172 | 173 | float fEyeX = sinf(fRayAngle); // Unit vector for ray in player space 174 | float fEyeY = cosf(fRayAngle); 175 | 176 | // Incrementally cast ray from player, along ray angle, testing for 177 | // intersection with a block 178 | while (!bHitWall && fDistanceToWall < fDepth) 179 | { 180 | fDistanceToWall += fStepSize; 181 | int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall); 182 | int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall); 183 | 184 | // Test if ray is out of bounds 185 | if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight) 186 | { 187 | bHitWall = true; // Just set distance to maximum depth 188 | fDistanceToWall = fDepth; 189 | } 190 | else 191 | { 192 | // Ray is inbounds so test to see if the ray cell is a wall block 193 | if (map.c_str()[nTestX * nMapWidth + nTestY] == '#') 194 | { 195 | // Ray has hit wall 196 | bHitWall = true; 197 | 198 | // To highlight tile boundaries, cast a ray from each corner 199 | // of the tile, to the player. The more coincident this ray 200 | // is to the rendering ray, the closer we are to a tile 201 | // boundary, which we'll shade to add detail to the walls 202 | vector> p; 203 | 204 | // Test each corner of hit tile, storing the distance from 205 | // the player, and the calculated dot product of the two rays 206 | for (int tx = 0; tx < 2; tx++) 207 | for (int ty = 0; ty < 2; ty++) 208 | { 209 | // Angle of corner to eye 210 | float vy = (float)nTestY + ty - fPlayerY; 211 | float vx = (float)nTestX + tx - fPlayerX; 212 | float d = sqrt(vx*vx + vy*vy); 213 | float dot = (fEyeX * vx / d) + (fEyeY * vy / d); 214 | p.push_back(make_pair(d, dot)); 215 | } 216 | 217 | // Sort Pairs from closest to farthest 218 | sort(p.begin(), p.end(), [](const pair &left, const pair &right) {return left.first < right.first; }); 219 | 220 | // First two/three are closest (we will never see all four) 221 | float fBound = 0.01; 222 | if (acos(p.at(0).second) < fBound) bBoundary = true; 223 | if (acos(p.at(1).second) < fBound) bBoundary = true; 224 | if (acos(p.at(2).second) < fBound) bBoundary = true; 225 | } 226 | } 227 | } 228 | 229 | // Calculate distance to ceiling and floor 230 | int nCeiling = (float)(nScreenHeight/2.0) - nScreenHeight / ((float)fDistanceToWall); 231 | int nFloor = nScreenHeight - nCeiling; 232 | 233 | // Shader walls based on distance 234 | short nShade = ' '; 235 | if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close 236 | else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; 237 | else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; 238 | else if (fDistanceToWall < fDepth) nShade = 0x2591; 239 | else nShade = ' '; // Too far away 240 | 241 | if (bBoundary) nShade = ' '; // Black it out 242 | 243 | for (int y = 0; y < nScreenHeight; y++) 244 | { 245 | // Each Row 246 | if(y <= nCeiling) 247 | screen[y*nScreenWidth + x] = ' '; 248 | else if(y > nCeiling && y <= nFloor) 249 | screen[y*nScreenWidth + x] = nShade; 250 | else // Floor 251 | { 252 | // Shade floor based on distance 253 | float b = 1.0f - (((float)y -nScreenHeight/2.0f) / ((float)nScreenHeight / 2.0f)); 254 | if (b < 0.25) nShade = '#'; 255 | else if (b < 0.5) nShade = 'x'; 256 | else if (b < 0.75) nShade = '.'; 257 | else if (b < 0.9) nShade = '-'; 258 | else nShade = ' '; 259 | screen[y*nScreenWidth + x] = nShade; 260 | } 261 | } 262 | } 263 | 264 | // Display Stats 265 | swprintf_s(screen, 40, L"X=%3.2f, Y=%3.2f, A=%3.2f FPS=%3.2f ", fPlayerX, fPlayerY, fPlayerA, 1.0f/fElapsedTime); 266 | 267 | // Display Map 268 | for (int nx = 0; nx < nMapWidth; nx++) 269 | for (int ny = 0; ny < nMapWidth; ny++) 270 | { 271 | screen[(ny+1)*nScreenWidth + nx] = map[ny * nMapWidth + nx]; 272 | } 273 | screen[((int)fPlayerX+1) * nScreenWidth + (int)fPlayerY] = 'P'; 274 | 275 | // Display Frame 276 | screen[nScreenWidth * nScreenHeight - 1] = '\0'; 277 | WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten); 278 | } 279 | 280 | return 0; 281 | } 282 | -------------------------------------------------------------------------------- /OneLoneCoder_FlappyBird.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Code-It-Yourself! Flappy Bird 3 | "You asked for it... wait, no one did, ever..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | Look it's Flappy Bird, OK? Press Space Bar to Flap. Get a high score. 33 | 34 | Author 35 | ~~~~~~ 36 | Twitter: @javidx9 37 | Blog: www.onelonecoder.com 38 | 39 | Video: 40 | ~~~~~~ 41 | https://youtu.be/b6A4XHkTjs8 42 | 43 | Last Updated: 05/11/2017 44 | */ 45 | 46 | #include 47 | #include 48 | using namespace std; 49 | 50 | #include "olcConsoleGameEngine.h" 51 | 52 | 53 | class OneLoneCoder_FlappyBird : public olcConsoleGameEngine 54 | { 55 | public: 56 | OneLoneCoder_FlappyBird() 57 | { 58 | m_sAppName = L"Flappy Bird"; 59 | } 60 | 61 | private: 62 | 63 | float fBirdPosition = 0.0f; 64 | float fBirdVelocity = 0.0f; 65 | float fBirdAcceleration = 0.0f; 66 | float fGravity = 100.0f; 67 | float fLevelPosition = 0.0f; 68 | 69 | float fSectionWidth; 70 | list listSection; 71 | 72 | bool bHasCollided = false; 73 | bool bResetGame = false; 74 | 75 | int nAttemptCount = 0; 76 | int nFlapCount = 0; 77 | int nMaxFlapCount = 0; 78 | 79 | 80 | protected: 81 | // Called by olcConsoleGameEngine 82 | virtual bool OnUserCreate() 83 | { 84 | listSection = { 0, 0, 0, 0 }; 85 | bResetGame = true; 86 | fSectionWidth = (float)ScreenWidth() / (float)(listSection.size() - 1); 87 | return true; 88 | } 89 | 90 | // Called by olcConsoleGameEngine 91 | virtual bool OnUserUpdate(float fElapsedTime) 92 | { 93 | if (bResetGame) 94 | { 95 | bHasCollided = false; 96 | bResetGame = false; 97 | listSection = { 0, 0, 0, 0 }; 98 | fBirdAcceleration = 0.0f; 99 | fBirdVelocity = 0.0f; 100 | fBirdPosition = ScreenHeight() / 2.0f; 101 | nFlapCount = 0; 102 | nAttemptCount++; 103 | } 104 | 105 | // Game 106 | if (bHasCollided) 107 | { 108 | // Do nothing until user releases space 109 | if (m_keys[VK_SPACE].bReleased) 110 | bResetGame = true; 111 | } 112 | else 113 | { 114 | 115 | if (m_keys[VK_SPACE].bPressed && fBirdVelocity >= fGravity / 10.0f) 116 | { 117 | fBirdAcceleration = 0.0f; 118 | fBirdVelocity = -fGravity / 4.0f; 119 | nFlapCount++; 120 | if (nFlapCount > nMaxFlapCount) 121 | nMaxFlapCount = nFlapCount; 122 | } 123 | else 124 | fBirdAcceleration += fGravity * fElapsedTime; 125 | 126 | if (fBirdAcceleration >= fGravity) 127 | fBirdAcceleration = fGravity; 128 | 129 | fBirdVelocity += fBirdAcceleration * fElapsedTime; 130 | fBirdPosition += fBirdVelocity * fElapsedTime; 131 | fLevelPosition += 14.0f * fElapsedTime; 132 | 133 | if (fLevelPosition > fSectionWidth) 134 | { 135 | fLevelPosition -= fSectionWidth; 136 | listSection.pop_front(); 137 | int i = rand() % (ScreenHeight() - 20); 138 | if (i <= 10) i = 0; 139 | listSection.push_back(i); 140 | } 141 | 142 | // Display 143 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 144 | 145 | // Draw Sections 146 | int nSection = 0; 147 | for (auto s : listSection) 148 | { 149 | if (s != 0) 150 | { 151 | Fill(nSection * fSectionWidth + 10 - fLevelPosition, ScreenHeight() - s, nSection * fSectionWidth + 15 - fLevelPosition, ScreenHeight(), PIXEL_SOLID, FG_GREEN); 152 | Fill(nSection * fSectionWidth + 10 - fLevelPosition, 0, nSection * fSectionWidth + 15 - fLevelPosition, ScreenHeight() - s - 15, PIXEL_SOLID, FG_GREEN); 153 | } 154 | nSection++; 155 | } 156 | 157 | int nBirdX = (int)(ScreenWidth() / 3.0f); 158 | 159 | // Collision Detection 160 | bHasCollided = fBirdPosition < 2 || fBirdPosition > ScreenHeight() - 2 || 161 | m_bufScreen[(int)(fBirdPosition + 0) * ScreenWidth() + nBirdX].Char.UnicodeChar != L' ' || 162 | m_bufScreen[(int)(fBirdPosition + 1) * ScreenWidth() + nBirdX].Char.UnicodeChar != L' ' || 163 | m_bufScreen[(int)(fBirdPosition + 0) * ScreenWidth() + nBirdX + 6].Char.UnicodeChar != L' ' || 164 | m_bufScreen[(int)(fBirdPosition + 1) * ScreenWidth() + nBirdX + 6].Char.UnicodeChar != L' '; 165 | 166 | // Draw Bird 167 | if (fBirdVelocity > 0) 168 | { 169 | DrawString(nBirdX, fBirdPosition + 0, L"\\\\\\"); 170 | DrawString(nBirdX, fBirdPosition + 1, L"<\\\\\\=Q"); 171 | } 172 | else 173 | { 174 | DrawString(nBirdX, fBirdPosition + 0, L" 55 | #include 56 | using namespace std; 57 | 58 | #include "olcConsoleGameEngine.h" 59 | 60 | 61 | class OneLoneCoder_Frogger : public olcConsoleGameEngine 62 | { 63 | public: 64 | OneLoneCoder_Frogger() 65 | { 66 | m_sAppName = L"Frogger"; 67 | } 68 | 69 | private: 70 | vector> vecLanes = 71 | { 72 | {0.0f, L"wwwhhwwwhhwwwhhwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"}, // 64 elements per lane 73 | {-3.0f, L",,,jllk,,jllllk,,,,,,,jllk,,,,,jk,,,jlllk,,,,jllllk,,,,jlllk,,,,"}, 74 | {3.0f, L",,,,jllk,,,,,jllk,,,,jllk,,,,,,,,,jllk,,,,,jk,,,,,,jllllk,,,,,,,"}, 75 | {2.0f, L",,jlk,,,,,jlk,,,,,jk,,,,,jlk,,,jlk,,,,jk,,,,jllk,,,,jk,,,,,,jk,,"}, 76 | {0.0f, L"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp"}, 77 | {-3.0f, L"....asdf.......asdf....asdf..........asdf........asdf....asdf..."}, 78 | { 3.0f, L".....ty..ty....ty....ty.....ty........ty..ty.ty......ty.......ty"}, 79 | {-4.0f, L"..zx.....zx.........zx..zx........zx...zx...zx....zx...zx...zx.."}, 80 | {2.0f, L"..ty.....ty.......ty.....ty......ty..ty.ty.......ty....ty......."}, 81 | {0.0f, L"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp"} 82 | }; 83 | 84 | float fTimeSinceStart = 0.0f; 85 | float fFrogX = 8.0f; 86 | float fFrogY = 9.0f; 87 | int nLaneWidth = 18; 88 | int nCellSize = 8; 89 | 90 | bool *bufDanger = nullptr; 91 | 92 | olcSprite *spriteBus = nullptr; 93 | olcSprite *spriteLog = nullptr; 94 | olcSprite *spriteWater = nullptr; 95 | olcSprite *spriteFrog = nullptr; 96 | olcSprite *spritePavement = nullptr; 97 | olcSprite *spriteCar1 = nullptr; 98 | olcSprite *spriteCar2 = nullptr; 99 | olcSprite *spriteWall = nullptr; 100 | olcSprite *spriteHome = nullptr; 101 | 102 | protected: 103 | // Called by olcConsoleGameEngine 104 | virtual bool OnUserCreate() 105 | { 106 | bufDanger = new bool[ScreenWidth() * ScreenHeight()]; 107 | memset(bufDanger, 0, ScreenWidth() * ScreenHeight() * sizeof(bool)); 108 | 109 | // You'll need these sprites! 110 | 111 | spriteBus = new olcSprite(L"FroggerSprites/bus.spr"); 112 | spriteLog = new olcSprite(L"FroggerSprites/log.spr"); 113 | spriteWater = new olcSprite(L"FroggerSprites/water.spr"); 114 | spriteFrog = new olcSprite(L"FroggerSprites/frog.spr"); 115 | spritePavement = new olcSprite(L"FroggerSprites/pavement.spr"); 116 | spriteWall = new olcSprite(L"FroggerSprites/wall.spr"); 117 | spriteHome = new olcSprite(L"FroggerSprites/home.spr"); 118 | spriteCar1 = new olcSprite(L"FroggerSprites/car1.spr"); 119 | spriteCar2 = new olcSprite(L"FroggerSprites/car2.spr"); 120 | return true; 121 | } 122 | 123 | // Called by olcConsoleGameEngine 124 | virtual bool OnUserUpdate(float fElapsedTime) 125 | { 126 | fTimeSinceStart += fElapsedTime; 127 | 128 | // Handle Input 129 | if (m_keys[VK_UP].bReleased) fFrogY -= 1.0f; 130 | if (m_keys[VK_DOWN].bReleased) fFrogY += 1.0f; 131 | if (m_keys[VK_LEFT].bReleased) fFrogX -= 1.0f; 132 | if (m_keys[VK_RIGHT].bReleased) fFrogX += 1.0f; 133 | 134 | // Frog is moved by platforms 135 | if (fFrogY <= 3) fFrogX -= fElapsedTime * vecLanes[(int)fFrogY].first; 136 | 137 | // Collision detection - check four corners of frog against danger buffer 138 | bool tl = bufDanger[(int)(fFrogY*nCellSize + 1) * ScreenWidth() + (int)(fFrogX*nCellSize + 1)]; 139 | bool tr = bufDanger[(int)(fFrogY*nCellSize + 1) * ScreenWidth() + (int)((fFrogX+1)*nCellSize - 1)]; 140 | bool bl = bufDanger[(int)((fFrogY+1)*nCellSize - 1) * ScreenWidth() + (int)(fFrogX*nCellSize + 1)]; 141 | bool br = bufDanger[(int)((fFrogY+1)*nCellSize - 1) * ScreenWidth() + (int)((fFrogX+1)*nCellSize - 1)]; 142 | 143 | if (tl || tr || bl || br) 144 | { 145 | // Frogs been hit :-( 146 | fFrogX = 8.0f; 147 | fFrogY = 9.0f; 148 | } 149 | 150 | // Draw Lanes 151 | int x=-1, y=0; 152 | for (auto lane : vecLanes) 153 | { 154 | // Find lane offset start 155 | int nStartPos = (int)(fTimeSinceStart * lane.first) % 64; 156 | int nCellOffset = (int)((float)nCellSize* fTimeSinceStart * lane.first) % nCellSize; 157 | if (nStartPos < 0) nStartPos = 64 - (abs(nStartPos) % 64); 158 | 159 | for (int i = 0; i < nLaneWidth; i++) 160 | { 161 | // Draw Graphics 162 | wchar_t graphic = lane.second[(nStartPos + i) % 64]; 163 | //Fill((x + i)*nCellSize - nCellOffset, y*nCellSize, (x + i + 1)*nCellSize - nCellOffset, (y + 1)*nCellSize, graphic); 164 | 165 | 166 | 167 | switch (graphic) // Graphics always make code verbose and less clear 168 | { 169 | case L'a': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteBus, 0, 0, 8, 8); break; // Bus 1 170 | case L's': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteBus, 8, 0, 8, 8); break; // Bus 2 171 | case L'd': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteBus, 16, 0, 8, 8); break; // Bus 3 172 | case L'f': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteBus, 24, 0, 8, 8); break; // Bus 4 173 | 174 | case L'j': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteLog, 0, 0, 8, 8); break; // Log Start 175 | case L'l': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteLog, 8, 0, 8, 8); break; // Log Middle 176 | case L'k': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteLog, 16, 0, 8, 8); break; // Log End 177 | 178 | case L'z': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteCar1, 0, 0, 8, 8); break; // Car1 Back 179 | case L'x': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteCar1, 8, 0, 8, 8); break; // Car1 Front 180 | 181 | case L't': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteCar2, 0, 0, 8, 8); break; // Car2 Back 182 | case L'y': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteCar2, 8, 0, 8, 8); break; // Car2 Front 183 | 184 | case L'w': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteWall, 0, 0, 8, 8); break; // Wall 185 | case L'h': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteHome, 0, 0, 8, 8); break; // Home 186 | case L',': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spriteWater, 0, 0, 8, 8); break; // Water 187 | case L'p': DrawPartialSprite((x + i)*nCellSize - nCellOffset, y*nCellSize, spritePavement, 0, 0, 8, 8); break; // Pavement 188 | case L'.': Fill((x + i)*nCellSize - nCellOffset, y*nCellSize, (x + i + 1)*nCellSize - nCellOffset, (y + 1)*nCellSize, L' '); break; // Road 189 | } 190 | 191 | // Fill Danger buffer 192 | for (int j = (x + i)*nCellSize - nCellOffset; j < (x + i + 1)*nCellSize - nCellOffset; j++) 193 | for (int k = y*nCellSize; k < (y + 1)*nCellSize; k++) 194 | if (j >= 0 && j < ScreenWidth() && k >= 0 && k < ScreenHeight()) 195 | bufDanger[k*ScreenWidth() + j] = 196 | !(graphic == L'.' || 197 | graphic == L'j' || graphic == L'k' || graphic == L'l' || 198 | graphic == L'p' || 199 | graphic == L'h'); 200 | } 201 | y++; 202 | } 203 | 204 | // Draw Frog 205 | DrawSprite(fFrogX*nCellSize, fFrogY*nCellSize, spriteFrog); 206 | return true; 207 | } 208 | }; 209 | 210 | 211 | int main() 212 | { 213 | // Use olcConsoleGameEngine derived app 214 | OneLoneCoder_Frogger game; 215 | game.ConstructConsole(128, 80, 12, 12); 216 | game.Start(); 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /OneLoneCoder_GameOfLife.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - What are Cellular Automata? John Conway's Game Of Life 3 | "Get lost..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Sometimes, its worth reflecting on the fact that from simple things can emerge 34 | significant beauty. This is an implementaion of Game Of Life that runs in the console 35 | 36 | Author 37 | ~~~~~~ 38 | Twitter: @javidx9 39 | Blog: www.onelonecoder.com 40 | 41 | Video: 42 | ~~~~~~ 43 | https://youtu.be/E7CxMHsYzSs 44 | 45 | Last Updated: 14/07/2017 46 | */ 47 | 48 | #include 49 | using namespace std; 50 | 51 | #include "olcConsoleGameEngine.h" 52 | 53 | class OneLoneCoder_GameOfLife : public olcConsoleGameEngine 54 | { 55 | public: 56 | OneLoneCoder_GameOfLife() 57 | { 58 | m_sAppName = L"Game Of Life"; 59 | } 60 | 61 | private: 62 | int *m_output; 63 | int *m_state; 64 | 65 | 66 | 67 | protected: 68 | // Called by olcConsoleGameEngine 69 | virtual bool OnUserCreate() 70 | { 71 | m_output = new int[ScreenWidth() * ScreenHeight()]; 72 | m_state = new int[ScreenWidth() * ScreenHeight()]; 73 | 74 | memset(m_output, 0, ScreenWidth() * ScreenHeight() * sizeof(int)); 75 | memset(m_state, 0, ScreenWidth() * ScreenHeight() * sizeof(int)); 76 | 77 | for (int i = 0; i < ScreenWidth()*ScreenHeight(); i++) 78 | m_state[i] = rand() % 2; 79 | 80 | auto set = [&](int x, int y, wstring s) 81 | { 82 | int p = 0; 83 | for (auto c : s) 84 | { 85 | m_state[y*ScreenWidth() + x + p] = c == L'#' ? 1 : 0; 86 | p++; 87 | } 88 | }; 89 | 90 | /* 91 | // R-Pentomino 92 | set(80, 50, L" ## "); 93 | set(80, 51, L" ## "); 94 | set(80, 52, L" # "); 95 | 96 | // Gosper Glider Gun 97 | set(60, 45, L"........................#............"); 98 | set(60, 46, L"......................#.#............"); 99 | set(60, 47, L"............##......##............##."); 100 | set(60, 48, L"...........#...#....##............##."); 101 | set(60, 49, L"##........#.....#...##..............."); 102 | set(60, 50, L"##........#...#.##....#.#............"); 103 | set(60, 51, L"..........#.....#.......#............"); 104 | set(60, 52, L"...........#...#....................."); 105 | set(60, 53, L"............##......................."); 106 | 107 | // Infinite Growth 108 | set(20, 50, L"########.#####...###......#######.#####"); 109 | */ 110 | 111 | return true; 112 | } 113 | 114 | 115 | 116 | // Called by olcConsoleGameEngine 117 | virtual bool OnUserUpdate(float fElapsedTime) 118 | { 119 | //this_thread::sleep_for(50ms); 120 | 121 | //if (!m_keys[VK_SPACE].bReleased) 122 | // return true; 123 | 124 | auto cell = [&](int x, int y) 125 | { 126 | return m_output[y * ScreenWidth() + x]; 127 | }; 128 | 129 | // Store output state 130 | for (int i = 0; i < ScreenWidth()*ScreenHeight(); i++) 131 | m_output[i] = m_state[i]; 132 | 133 | for (int x = 1; x < ScreenWidth() - 1; x++) 134 | for (int y = 1; y < ScreenHeight() - 1; y++) 135 | { 136 | // The secret of artificial life ================================================= 137 | int nNeighbours = cell(x - 1, y - 1) + cell(x - 0, y - 1) + cell(x + 1, y - 1) + 138 | cell(x - 1, y + 0) + 0 + cell(x + 1, y + 0) + 139 | cell(x - 1, y + 1) + cell(x + 0, y + 1) + cell(x + 1, y + 1); 140 | 141 | if (cell(x, y) == 1) 142 | m_state[y*ScreenWidth() + x] = nNeighbours == 2 || nNeighbours == 3; 143 | else 144 | m_state[y*ScreenWidth() + x] = nNeighbours == 3; 145 | // =============================================================================== 146 | 147 | 148 | if (cell(x, y) == 1) 149 | Draw(x, y, PIXEL_SOLID, FG_WHITE); 150 | else 151 | Draw(x, y, PIXEL_SOLID, FG_BLACK); 152 | } 153 | 154 | return true; 155 | } 156 | }; 157 | 158 | 159 | int main() 160 | { 161 | // Seed random number generator 162 | srand(clock()); 163 | 164 | // Use olcConsoleGameEngine derived app 165 | OneLoneCoder_GameOfLife game; 166 | game.ConstructConsole(160, 100, 8, 8); 167 | game.Start(); 168 | 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /OneLoneCoder_LogitechG13Twitch.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Program a Logitech Keyboard To Display Twitch Chat 3 | "Put Your Money Where Your Mouth Is" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | A very minimal example of using the Logitech SDK to interface with a keyboard display, 33 | then uses a really sloppy implementation of sockets, to connect to a twitch chat 34 | session, displaying the chat on the keybaord screen. 35 | 36 | Future Modifications 37 | ~~~~~~~~~~~~~~~~~~~~ 38 | 1) Wrap around text display on screen 39 | 40 | Author 41 | ~~~~~~ 42 | Twitter: @javidx9 43 | Blog: www.onelonecoder.com 44 | 45 | Video: 46 | ~~~~~~ 47 | https://youtu.be/8UXCo-GhiF0 48 | 49 | Last Updated: 23/05/2017 50 | */ 51 | 52 | #include 53 | #include 54 | using namespace std; 55 | 56 | // Include Winsock 57 | #include 58 | #include 59 | 60 | // Include Logitech library 61 | #include "LogitechLCDLib.h" 62 | 63 | int main() 64 | { 65 | // Load the driver 66 | LogiLcdInit(L"OneLoneCoder Test", LOGI_LCD_TYPE_MONO); 67 | 68 | // Check hardware is connected 69 | if (!LogiLcdIsConnected(LOGI_LCD_TYPE_MONO)) 70 | wcout << "Hardware not found" << endl; 71 | 72 | // Load WinSock 73 | WSADATA wsaData; 74 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 75 | wcout << "Could not start WinSock" << endl; 76 | 77 | // Get address to twitch server 78 | struct addrinfo *addr = nullptr; 79 | if (getaddrinfo("irc.chat.twitch.tv", "6667", nullptr, &addr) != 0) 80 | wcout << "Failed to get address info" << endl; 81 | 82 | // Create socket 83 | SOCKET sock = INVALID_SOCKET; 84 | sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); 85 | 86 | // Connect to server via socket 87 | int i = connect(sock, addr->ai_addr, (int)addr->ai_addrlen); 88 | if (i != SOCKET_ERROR) 89 | { 90 | // Handshake with twitch 91 | string s; 92 | 93 | // Authenticate 94 | s = "PASS oauth:###INSERT YOUR OAUTH HERE###\r\n"; 95 | send(sock, s.c_str(), s.length(), 0); 96 | 97 | // Register your twitch name 98 | s = "NICK javidx9\r\n"; 99 | send(sock, s.c_str(), s.length(), 0); 100 | 101 | // Join a twitch chat 102 | s = "JOIN #javidx9\r\n"; 103 | send(sock, s.c_str(), s.length(), 0); 104 | } 105 | 106 | char buffer[100]; 107 | string s; 108 | list sLines = { "","","","" }; 109 | 110 | while (1) 111 | { 112 | // Get Twitch Server Message 113 | int i = recv(sock, buffer, 100, 0); 114 | for (int j = 0; j < i; j++) 115 | { 116 | s.append(1, buffer[j]); 117 | if (buffer[j] == '\n') 118 | { 119 | // User name is between first ':' and '!'. 120 | // User chat is after second ':' 121 | size_t m = s.find('!'); 122 | string sUserName = s.substr(1, m - 1); 123 | size_t n = s.find(':', m); 124 | if (n != string::npos) 125 | { 126 | string chat = s.substr(n + 1); 127 | cout << sUserName.c_str() << ": " << chat.c_str() << endl; 128 | 129 | sLines.pop_front(); 130 | sLines.push_back(sUserName + ": " + chat); 131 | } 132 | s.clear(); 133 | } 134 | } 135 | 136 | // Display 4 lines of text from list 137 | int p = 0; 138 | for (auto k : sLines) 139 | { 140 | wstring ws; 141 | ws.assign(k.begin(), k.end()); 142 | LogiLcdMonoSetText(p, (wchar_t*)ws.c_str()); // yuck... 143 | p++; 144 | 145 | } 146 | 147 | // Update display 148 | LogiLcdUpdate(); 149 | } 150 | 151 | LogiLcdShutdown(); 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /OneLoneCoder_Matrix.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Programming The Matrix 3 | "Ribbet" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | I hit 0x1FF subscribers, so celebrated with a live stream! 34 | 35 | Controls 36 | ~~~~~~~~ 37 | None, just spot the blonde, brunette, etc, etc... 38 | 39 | Author 40 | ~~~~~~ 41 | Twitter: @javidx9 42 | Blog: www.onelonecoder.com 43 | 44 | Video: 45 | ~~~~~~ 46 | https://youtu.be/s7EbrvA188A 47 | 48 | 49 | Last Updated: 17/09/2017 50 | */ 51 | 52 | #include 53 | #include 54 | using namespace std; 55 | 56 | #include "olcConsoleGameEngine.h" 57 | 58 | 59 | class OneLoneCoder_Matrix : public olcConsoleGameEngine 60 | { 61 | public: 62 | OneLoneCoder_Matrix() 63 | { 64 | m_sAppName = L"Matrix"; 65 | } 66 | 67 | private: 68 | struct sStreamer 69 | { 70 | int nColumn = 0; 71 | float fPosition = 0; 72 | float fSpeed = 0; 73 | wstring sText; 74 | }; 75 | 76 | list listStreamers; 77 | 78 | int nMaxStreamers = 300; 79 | 80 | wchar_t RandomCharacter() 81 | { 82 | return (wchar_t)(rand() % 0x1EF + 0x00C0); 83 | return (wchar_t)(rand() % 93 + 33); // Random ASCII 84 | } 85 | 86 | void PrepareStreamer(sStreamer *s) 87 | { 88 | s->nColumn = rand() % ScreenWidth(); 89 | s->fPosition = 0; 90 | s->fSpeed = rand() % 40 + 5; 91 | s->sText.clear(); 92 | 93 | int nStreamerLength = rand() % 80 + 10; 94 | for (int i = 0; i < nStreamerLength; i++) 95 | s->sText.append(1, RandomCharacter()); 96 | 97 | //s->sText = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 98 | } 99 | 100 | 101 | protected: 102 | // Called by olcConsoleGameEngine 103 | virtual bool OnUserCreate() 104 | { 105 | for (int n = 0; n < nMaxStreamers; n++) 106 | { 107 | sStreamer s; 108 | PrepareStreamer(&s); 109 | listStreamers.push_back(s); 110 | } 111 | return true; 112 | } 113 | 114 | // Called by olcConsoleGameEngine 115 | virtual bool OnUserUpdate(float fElapsedTime) 116 | { 117 | // Clear Screen 118 | Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, 0); 119 | 120 | for (auto &s : listStreamers) 121 | { 122 | s.fPosition += fElapsedTime * s.fSpeed; 123 | for (int i = 0; i < s.sText.size(); i++) 124 | { 125 | // If you hate ternary operators do this 126 | // start -> 127 | short col = s.fSpeed < 15.0f ? FG_DARK_GREEN : FG_GREEN; // ;-) 128 | if (i == 0) 129 | col = FG_WHITE; 130 | else 131 | if (i <= 3) 132 | col = FG_GREY; 133 | 134 | int nCharIndex = (i - (int)s.fPosition) % s.sText.size(); 135 | 136 | Draw(s.nColumn, (int)s.fPosition - i, s.sText[nCharIndex], col); 137 | // <- end 138 | 139 | // If you like them, do this! 140 | //Draw(s.nColumn, (int)s.fPosition - i, s.sText[(abs(i - (int)s.fPosition) % s.sText.size())], i == 0 ? FG_WHITE : i<=3 ? FG_GREY: i>=s.sText.size()-2 || s.fSpeed < 15.0f? FG_DARK_GREEN: FG_GREEN); 141 | 142 | // Missed this out on live stream, occasionally glitch a character 143 | if (rand() % 1000 < 5) 144 | s.sText[i] = RandomCharacter(); 145 | } 146 | 147 | if ( s.fPosition - s.sText.size() >= ScreenHeight()) 148 | PrepareStreamer(&s); 149 | 150 | } 151 | return true; 152 | } 153 | }; 154 | 155 | 156 | int main() 157 | { 158 | // Use olcConsoleGameEngine derived app 159 | OneLoneCoder_Matrix game; 160 | game.ConstructConsole(128, 80, 12, 12); 161 | game.Start(); 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /OneLoneCoder_Mazes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Recursive Backtracker Maze Algorithm 3 | "Get lost..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | I really like perfect algorithms. This one shows how to generate a maze that guarantees 33 | all cells can reach all other cells, it just may take some time to get there. I introduce 34 | stacks, and show how the algorithm generates the maze visually. 35 | 36 | Author 37 | ~~~~~~ 38 | Twitter: @javidx9 39 | Blog: www.onelonecoder.com 40 | 41 | Video: 42 | ~~~~~~ 43 | https://youtu.be/Y37-gB83HKE 44 | 45 | Last Updated: 10/07/2017 46 | */ 47 | 48 | #include 49 | #include 50 | using namespace std; 51 | 52 | #include "olcConsoleGameEngine.h" 53 | 54 | class OneLoneCoder_Maze : public olcConsoleGameEngine 55 | { 56 | public: 57 | OneLoneCoder_Maze() 58 | { 59 | m_sAppName = L"MAZE"; 60 | } 61 | 62 | private: 63 | int m_nMazeWidth; 64 | int m_nMazeHeight; 65 | int *m_maze; 66 | 67 | 68 | // Some bit fields for convenience 69 | enum 70 | { 71 | CELL_PATH_N = 0x01, 72 | CELL_PATH_E = 0x02, 73 | CELL_PATH_S = 0x04, 74 | CELL_PATH_W = 0x08, 75 | CELL_VISITED = 0x10, 76 | }; 77 | 78 | 79 | // Algorithm variables 80 | int m_nVisitedCells; 81 | stack> m_stack; // (x, y) coordinate pairs 82 | int m_nPathWidth; 83 | 84 | 85 | protected: 86 | // Called by olcConsoleGameEngine 87 | virtual bool OnUserCreate() 88 | { 89 | // Maze parameters 90 | m_nMazeWidth = 40; 91 | m_nMazeHeight = 25; 92 | m_maze = new int[m_nMazeWidth * m_nMazeHeight]; 93 | memset(m_maze, 0x00, m_nMazeWidth * m_nMazeHeight * sizeof(int)); 94 | m_nPathWidth = 3; 95 | 96 | // Choose a starting cell 97 | int x = rand() % m_nMazeWidth; 98 | int y = rand() % m_nMazeHeight; 99 | m_stack.push(make_pair(x, y)); 100 | m_maze[y * m_nMazeWidth + x] = CELL_VISITED; 101 | m_nVisitedCells = 1; 102 | 103 | return true; 104 | } 105 | 106 | // Called by olcConsoleGameEngine 107 | virtual bool OnUserUpdate(float fElapsedTime) 108 | { 109 | // Slow down for animation 110 | this_thread::sleep_for(10ms); 111 | 112 | // Little lambda function to calculate index in a readable way 113 | auto offset = [&](int x, int y) 114 | { 115 | return (m_stack.top().second + y) * m_nMazeWidth + (m_stack.top().first + x); 116 | }; 117 | 118 | // Do Maze Algorithm 119 | if (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight) 120 | { 121 | // Create a set of unvisted neighbours 122 | vector neighbours; 123 | 124 | // North neighbour 125 | if (m_stack.top().second > 0 && (m_maze[offset(0, -1)] & CELL_VISITED) == 0) 126 | neighbours.push_back(0); 127 | // East neighbour 128 | if (m_stack.top().first < m_nMazeWidth - 1 && (m_maze[offset(1, 0)] & CELL_VISITED) == 0) 129 | neighbours.push_back(1); 130 | // South neighbour 131 | if (m_stack.top().second < m_nMazeHeight - 1 && (m_maze[offset(0, 1)] & CELL_VISITED) == 0) 132 | neighbours.push_back(2); 133 | // West neighbour 134 | if (m_stack.top().first > 0 && (m_maze[offset(-1, 0)] & CELL_VISITED) == 0) 135 | neighbours.push_back(3); 136 | 137 | // Are there any neighbours available? 138 | if (!neighbours.empty()) 139 | { 140 | // Choose one available neighbour at random 141 | int next_cell_dir = neighbours[rand() % neighbours.size()]; 142 | 143 | // Create a path between the neighbour and the current cell 144 | switch (next_cell_dir) 145 | { 146 | case 0: // North 147 | m_maze[offset(0, -1)] |= CELL_VISITED | CELL_PATH_S; 148 | m_maze[offset(0, 0)] |= CELL_PATH_N; 149 | m_stack.push(make_pair((m_stack.top().first + 0), (m_stack.top().second - 1))); 150 | break; 151 | 152 | case 1: // East 153 | m_maze[offset(+1, 0)] |= CELL_VISITED | CELL_PATH_W; 154 | m_maze[offset( 0, 0)] |= CELL_PATH_E; 155 | m_stack.push(make_pair((m_stack.top().first + 1), (m_stack.top().second + 0))); 156 | break; 157 | 158 | case 2: // South 159 | m_maze[offset(0, +1)] |= CELL_VISITED | CELL_PATH_N; 160 | m_maze[offset(0, 0)] |= CELL_PATH_S; 161 | m_stack.push(make_pair((m_stack.top().first + 0), (m_stack.top().second + 1))); 162 | break; 163 | 164 | case 3: // West 165 | m_maze[offset(-1, 0)] |= CELL_VISITED | CELL_PATH_E; 166 | m_maze[offset( 0, 0)] |= CELL_PATH_W; 167 | m_stack.push(make_pair((m_stack.top().first - 1), (m_stack.top().second + 0))); 168 | break; 169 | 170 | } 171 | 172 | m_nVisitedCells++; 173 | } 174 | else 175 | { 176 | // No available neighbours so backtrack! 177 | m_stack.pop(); 178 | } 179 | } 180 | 181 | 182 | // === DRAWING STUFF === 183 | 184 | // Clear Screen by drawing 'spaces' everywhere 185 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 186 | 187 | // Draw Maze 188 | for (int x = 0; x < m_nMazeWidth; x++) 189 | { 190 | for (int y = 0; y < m_nMazeHeight; y++) 191 | { 192 | // Each cell is inflated by m_nPathWidth, so fill it in 193 | for (int py = 0; py < m_nPathWidth; py++) 194 | for (int px = 0; px < m_nPathWidth; px++) 195 | { 196 | if (m_maze[y * m_nMazeWidth + x] & CELL_VISITED) 197 | Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_WHITE); // Draw Cell 198 | else 199 | Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_BLUE); // Draw Cell 200 | } 201 | 202 | // Draw passageways between cells 203 | for (int p = 0; p < m_nPathWidth; p++) 204 | { 205 | if (m_maze[y * m_nMazeWidth + x] & CELL_PATH_S) 206 | Draw(x * (m_nPathWidth + 1) + p, y * (m_nPathWidth + 1) + m_nPathWidth); // Draw South Passage 207 | 208 | if (m_maze[y * m_nMazeWidth + x] & CELL_PATH_E) 209 | Draw(x * (m_nPathWidth + 1) + m_nPathWidth, y * (m_nPathWidth + 1) + p); // Draw East Passage 210 | } 211 | } 212 | } 213 | 214 | // Draw Unit - the top of the stack 215 | for (int py = 0; py < m_nPathWidth; py++) 216 | for (int px = 0; px < m_nPathWidth; px++) 217 | Draw(m_stack.top().first * (m_nPathWidth + 1) + px, m_stack.top().second * (m_nPathWidth + 1) + py, 0x2588, FG_GREEN); // Draw Cell 218 | 219 | 220 | return true; 221 | } 222 | }; 223 | 224 | 225 | int main() 226 | { 227 | // Seed random number generator 228 | srand(clock()); 229 | 230 | // Use olcConsoleGameEngine derived app 231 | OneLoneCoder_Maze game; 232 | game.ConstructConsole(160, 100, 8, 8); 233 | game.Start(); 234 | 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /OneLoneCoder_OverEngineered.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/OneLoneCoder_OverEngineered.cpp -------------------------------------------------------------------------------- /OneLoneCoder_PanAndZoom.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Programming Panning & Zooming 3 | "Left a bit, bit more, there..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | GNU GPLv3 16 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 17 | 18 | From Javidx9 :) 19 | ~~~~~~~~~~~~~~~ 20 | Hello! Ultimately I don't care what you use this for. It's intended to be 21 | educational, and perhaps to the oddly minded - a little bit of fun. 22 | Please hack this, change it and use it in any way you see fit. You acknowledge 23 | that I am not responsible for anything bad that happens as a result of 24 | your actions. However this code is protected by GNU GPLv3, see the license in the 25 | github repo. This means you must attribute me if you use it. You can view this 26 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 27 | Cheers! 28 | 29 | Background 30 | ~~~~~~~~~~ 31 | Used in absolutely everything now, panning and zooming is an important 32 | tool to know how to implement. Even though it appears simple, getting 33 | your head around different spatial transforms can be challenging! 34 | 35 | Author 36 | ~~~~~~ 37 | Twitter: @javidx9 38 | Blog: http://www.onelonecoder.com 39 | Discord: https://discord.gg/WhwHUMV 40 | 41 | Video: 42 | ~~~~~~ 43 | https://youtu.be/ZQ8qtAizis4 44 | 45 | Last Updated: 10/06/2018 46 | */ 47 | 48 | #include "olcConsoleGameEngine.h" 49 | #include 50 | 51 | class OneLoneCoder_PanAndZoom : public olcConsoleGameEngine 52 | { 53 | public: 54 | OneLoneCoder_PanAndZoom() 55 | { 56 | m_sAppName = L"Pan And Zoom"; 57 | } 58 | 59 | private: 60 | 61 | float fOffsetX = 0.0f; 62 | float fOffsetY = 0.0f; 63 | float fScaleX = 1.0f; 64 | float fScaleY = 1.0f; 65 | 66 | float fStartPanX = 0.0f; 67 | float fStartPanY = 0.0f; 68 | 69 | float fSelectedCellX = 0.0f; 70 | float fSelectedCellY = 0.0f; 71 | 72 | 73 | protected: 74 | 75 | virtual bool OnUserCreate() 76 | { 77 | // Initialise offset so 0,0 in world space is middle of the screen 78 | fOffsetX = -ScreenWidth() / 2; 79 | fOffsetY = -ScreenHeight() / 2; 80 | return true; 81 | } 82 | 83 | // Convert coordinates from World Space --> Screen Space 84 | void WorldToScreen(float fWorldX, float fWorldY, int &nScreenX, int &nScreenY) 85 | { 86 | nScreenX = (int)((fWorldX - fOffsetX) * fScaleX); 87 | nScreenY = (int)((fWorldY - fOffsetY) * fScaleY); 88 | } 89 | 90 | // Convert coordinates from Screen Space --> World Space 91 | void ScreenToWorld(int nScreenX, int nScreenY, float &fWorldX, float &fWorldY) 92 | { 93 | fWorldX = ((float)nScreenX / fScaleX) + fOffsetX; 94 | fWorldY = ((float)nScreenY / fScaleY) + fOffsetY; 95 | } 96 | 97 | // Called by olcConsoleGameEngine 98 | virtual bool OnUserUpdate(float fElapsedTime) 99 | { 100 | // Just grab a copy of mouse coordinates for convenience 101 | float fMouseX = (float)GetMouseX(); 102 | float fMouseY = (float)GetMouseY(); 103 | 104 | // For panning, we need to capture the screen location when the user starts 105 | // to pan... 106 | if (GetMouse(2).bPressed) 107 | { 108 | fStartPanX = fMouseX; 109 | fStartPanY = fMouseY; 110 | } 111 | 112 | // ...as the mouse moves, the screen location changes. Convert this screen 113 | // coordinate change into world coordinates to implement the pan. Simples. 114 | if (GetMouse(2).bHeld) 115 | { 116 | fOffsetX -= (fMouseX - fStartPanX) / fScaleX; 117 | fOffsetY -= (fMouseY - fStartPanY) / fScaleY; 118 | 119 | // Start "new" pan for next epoch 120 | fStartPanX = fMouseX; 121 | fStartPanY = fMouseY; 122 | } 123 | 124 | // For zoom, we need to extract the location of the cursor before and after the 125 | // scale is changed. Here we get the cursor and translate into world space... 126 | float fMouseWorldX_BeforeZoom, fMouseWorldY_BeforeZoom; 127 | ScreenToWorld(fMouseX, fMouseY, fMouseWorldX_BeforeZoom, fMouseWorldY_BeforeZoom); 128 | 129 | 130 | // ...change the scale as required... 131 | if (GetKey(L'Q').bHeld) 132 | { 133 | fScaleX *= 1.001f; 134 | fScaleY *= 1.001f; 135 | } 136 | 137 | if (GetKey(L'A').bHeld) 138 | { 139 | fScaleX *= 0.999f; 140 | fScaleY *= 0.999f; 141 | } 142 | 143 | // ...now get the location of the cursor in world space again - It will have changed 144 | // because the scale has changed, but we can offset our world now to fix the zoom 145 | // location in screen space, because we know how much it changed laterally between 146 | // the two spatial scales. Neat huh? ;-) 147 | float fMouseWorldX_AfterZoom, fMouseWorldY_AfterZoom; 148 | ScreenToWorld(fMouseX, fMouseY, fMouseWorldX_AfterZoom, fMouseWorldY_AfterZoom); 149 | fOffsetX += (fMouseWorldX_BeforeZoom - fMouseWorldX_AfterZoom); 150 | fOffsetY += (fMouseWorldY_BeforeZoom - fMouseWorldY_AfterZoom); 151 | 152 | // Clear Screen 153 | Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_BLACK); 154 | 155 | 156 | // Clip 157 | float fWorldLeft, fWorldTop, fWorldRight, fWorldBottom; 158 | ScreenToWorld(0, 0, fWorldLeft, fWorldTop); 159 | ScreenToWorld(ScreenWidth(), ScreenHeight(), fWorldRight, fWorldBottom); 160 | 161 | auto function = [](float x) 162 | { 163 | return sinf(x); 164 | }; 165 | 166 | 167 | // Draw Main Axes a 10x10 Unit Grid 168 | // Draw 10 horizontal lines 169 | int nLinesDrawn = 0; 170 | for (float y = 0.0f; y <= 10.0f; y++) 171 | { 172 | if (y >= fWorldTop && y <= fWorldBottom) 173 | { 174 | float sx = 0.0f, sy = y; 175 | float ex = 10.0f, ey = y; 176 | 177 | int pixel_sx, pixel_sy, pixel_ex, pixel_ey; 178 | 179 | WorldToScreen(sx, sy, pixel_sx, pixel_sy); 180 | WorldToScreen(ex, ey, pixel_ex, pixel_ey); 181 | 182 | DrawLine(pixel_sx, pixel_sy, pixel_ex, pixel_ey, PIXEL_SOLID, FG_WHITE); 183 | nLinesDrawn++; 184 | } 185 | } 186 | 187 | // Draw 10 vertical lines 188 | for (float x = 0.0f; x <= 10.0f; x++) 189 | { 190 | if (x >= fWorldLeft && x <= fWorldRight) 191 | { 192 | float sx = x, sy = 0.0f; 193 | float ex = x, ey = 10.0f; 194 | 195 | int pixel_sx, pixel_sy, pixel_ex, pixel_ey; 196 | 197 | WorldToScreen(sx, sy, pixel_sx, pixel_sy); 198 | WorldToScreen(ex, ey, pixel_ex, pixel_ey); 199 | 200 | DrawLine(pixel_sx, pixel_sy, pixel_ex, pixel_ey, PIXEL_SOLID, FG_WHITE); 201 | nLinesDrawn++; 202 | } 203 | } 204 | 205 | // Draw selected cell 206 | 207 | // We can easily determine where the mouse is in world space. In fact we already 208 | // have this frame so just reuse the values 209 | if (GetMouse(1).bReleased) 210 | { 211 | fSelectedCellX = (int)fMouseWorldX_AfterZoom; 212 | fSelectedCellY = (int)fMouseWorldY_AfterZoom; 213 | } 214 | 215 | // Draw selected cell by filling with red circle. Convert cell coords 216 | // into screen space, also scale the radius 217 | int cx, cy, cr; 218 | WorldToScreen(fSelectedCellX + 0.5f, fSelectedCellY + 0.5f, cx, cy); 219 | cr = 0.3f * fScaleX; 220 | FillCircle(cx, cy, cr, PIXEL_SOLID, FG_RED); 221 | DrawString(2, 2, L"Lines Drawn: " + to_wstring(nLinesDrawn)); 222 | 223 | 224 | // Draw Chart 225 | float fWorldPerScreenWidthPixel = (fWorldRight - fWorldLeft) / ScreenWidth(); 226 | float fWorldPerScreenHeightPixel = (fWorldBottom - fWorldTop) / ScreenHeight(); 227 | int px, py, opx = 0, opy = 0; 228 | WorldToScreen(fWorldLeft-fWorldPerScreenWidthPixel, -function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f, opx, opy); 229 | for (float x = fWorldLeft; x < fWorldRight; x+=fWorldPerScreenWidthPixel) 230 | { 231 | float y = -function(x - 5.0f) + 5.0f; 232 | WorldToScreen(x, y, px, py); 233 | DrawLine(opx, opy, px, py, PIXEL_SOLID, FG_GREEN); 234 | opx = px; 235 | opy = py; 236 | } 237 | 238 | return true; 239 | } 240 | }; 241 | 242 | 243 | int main() 244 | { 245 | OneLoneCoder_PanAndZoom demo; 246 | demo.ConstructConsole(160, 100, 8, 8); 247 | demo.Start(); 248 | return 0; 249 | } -------------------------------------------------------------------------------- /OneLoneCoder_PathFinding_AStar.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - PathFinding A* 3 | "No more getting lost..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | The A* path finding algorithm is a widely used and powerful shortest path 34 | finding node traversal algorithm. A heuristic is used to bias the algorithm 35 | towards success. This code is probably more interesting than the video. :-/ 36 | 37 | 38 | Author 39 | ~~~~~~ 40 | Twitter: @javidx9 41 | Blog: www.onelonecoder.com 42 | 43 | Video: 44 | ~~~~~~ 45 | https://youtu.be/icZj67PTFhc 46 | 47 | Last Updated: 08/10/2017 48 | */ 49 | 50 | #include 51 | #include 52 | #include 53 | using namespace std; 54 | 55 | #include "olcConsoleGameEngine.h" 56 | 57 | class OneLoneCoder_PathFinding : public olcConsoleGameEngine 58 | { 59 | public: 60 | OneLoneCoder_PathFinding() 61 | { 62 | m_sAppName = L"Path Finding"; 63 | } 64 | 65 | private: 66 | 67 | struct sNode 68 | { 69 | bool bObstacle = false; // Is the node an obstruction? 70 | bool bVisited = false; // Have we searched this node before? 71 | float fGlobalGoal; // Distance to goal so far 72 | float fLocalGoal; // Distance to goal if we took the alternative route 73 | int x; // Nodes position in 2D space 74 | int y; 75 | vector vecNeighbours; // Connections to neighbours 76 | sNode* parent; // Node connecting to this node that offers shortest parent 77 | }; 78 | 79 | sNode *nodes = nullptr; 80 | int nMapWidth = 16; 81 | int nMapHeight = 16; 82 | 83 | sNode *nodeStart = nullptr; 84 | sNode *nodeEnd = nullptr; 85 | 86 | 87 | protected: 88 | virtual bool OnUserCreate() 89 | { 90 | // Create a 2D array of nodes - this is for convenience of rendering and construction 91 | // and is not required for the algorithm to work - the nodes could be placed anywhere 92 | // in any space, in multiple dimensions... 93 | nodes = new sNode[nMapWidth * nMapHeight]; 94 | for (int x = 0; x < nMapWidth; x++) 95 | for (int y = 0; y < nMapHeight; y++) 96 | { 97 | nodes[y * nMapWidth + x].x = x; // ...because we give each node its own coordinates 98 | nodes[y * nMapWidth + x].y = y; 99 | nodes[y * nMapWidth + x].bObstacle = false; 100 | nodes[y * nMapWidth + x].parent = nullptr; 101 | nodes[y * nMapWidth + x].bVisited = false; 102 | } 103 | 104 | // Create connections - in this case nodes are on a regular grid 105 | for (int x = 0; x < nMapWidth; x++) 106 | for (int y = 0; y < nMapHeight; y++) 107 | { 108 | if(y>0) 109 | nodes[y*nMapWidth + x].vecNeighbours.push_back(&nodes[(y - 1) * nMapWidth + (x + 0)]); 110 | if(y0) 113 | nodes[y*nMapWidth + x].vecNeighbours.push_back(&nodes[(y + 0) * nMapWidth + (x - 1)]); 114 | if(x0 && x>0) 119 | nodes[y*nMapWidth + x].vecNeighbours.push_back(&nodes[(y - 1) * nMapWidth + (x - 1)]); 120 | if (y0) 121 | nodes[y*nMapWidth + x].vecNeighbours.push_back(&nodes[(y + 1) * nMapWidth + (x - 1)]); 122 | if (y>0 && xx - b->x)*(a->x - b->x) + (a->y - b->y)*(a->y - b->y)); 150 | }; 151 | 152 | auto heuristic = [distance](sNode* a, sNode* b) // So we can experiment with heuristic 153 | { 154 | return distance(a, b); 155 | }; 156 | 157 | // Setup starting conditions 158 | sNode *nodeCurrent = nodeStart; 159 | nodeStart->fLocalGoal = 0.0f; 160 | nodeStart->fGlobalGoal = heuristic(nodeStart, nodeEnd); 161 | 162 | // Add start node to not tested list - this will ensure it gets tested. 163 | // As the algorithm progresses, newly discovered nodes get added to this 164 | // list, and will themselves be tested later 165 | list listNotTestedNodes; 166 | listNotTestedNodes.push_back(nodeStart); 167 | 168 | // if the not tested list contains nodes, there may be better paths 169 | // which have not yet been explored. However, we will also stop 170 | // searching when we reach the target - there may well be better 171 | // paths but this one will do - it wont be the longest. 172 | while (!listNotTestedNodes.empty() && nodeCurrent != nodeEnd)// Find absolutely shortest path // && nodeCurrent != nodeEnd) 173 | { 174 | // Sort Untested nodes by global goal, so lowest is first 175 | listNotTestedNodes.sort([](const sNode* lhs, const sNode* rhs){ return lhs->fGlobalGoal < rhs->fGlobalGoal; } ); 176 | 177 | // Front of listNotTestedNodes is potentially the lowest distance node. Our 178 | // list may also contain nodes that have been visited, so ditch these... 179 | while(!listNotTestedNodes.empty() && listNotTestedNodes.front()->bVisited) 180 | listNotTestedNodes.pop_front(); 181 | 182 | // ...or abort because there are no valid nodes left to test 183 | if (listNotTestedNodes.empty()) 184 | break; 185 | 186 | nodeCurrent = listNotTestedNodes.front(); 187 | nodeCurrent->bVisited = true; // We only explore a node once 188 | 189 | 190 | // Check each of this node's neighbours... 191 | for (auto nodeNeighbour : nodeCurrent->vecNeighbours) 192 | { 193 | // ... and only if the neighbour is not visited and is 194 | // not an obstacle, add it to NotTested List 195 | if (!nodeNeighbour->bVisited && nodeNeighbour->bObstacle == 0) 196 | listNotTestedNodes.push_back(nodeNeighbour); 197 | 198 | // Calculate the neighbours potential lowest parent distance 199 | float fPossiblyLowerGoal = nodeCurrent->fLocalGoal + distance(nodeCurrent, nodeNeighbour); 200 | 201 | // If choosing to path through this node is a lower distance than what 202 | // the neighbour currently has set, update the neighbour to use this node 203 | // as the path source, and set its distance scores as necessary 204 | if (fPossiblyLowerGoal < nodeNeighbour->fLocalGoal) 205 | { 206 | nodeNeighbour->parent = nodeCurrent; 207 | nodeNeighbour->fLocalGoal = fPossiblyLowerGoal; 208 | 209 | // The best path length to the neighbour being tested has changed, so 210 | // update the neighbour's score. The heuristic is used to globally bias 211 | // the path algorithm, so it knows if its getting better or worse. At some 212 | // point the algo will realise this path is worse and abandon it, and then go 213 | // and search along the next best path. 214 | nodeNeighbour->fGlobalGoal = nodeNeighbour->fLocalGoal + heuristic(nodeNeighbour, nodeEnd); 215 | } 216 | } 217 | } 218 | 219 | return true; 220 | } 221 | 222 | virtual bool OnUserUpdate(float fElapsedTime) 223 | { 224 | int nNodeSize = 9; 225 | int nNodeBorder = 2; 226 | 227 | // Use integer division to nicely get cursor position in node space 228 | int nSelectedNodeX = m_mousePosX / nNodeSize; 229 | int nSelectedNodeY = m_mousePosY / nNodeSize; 230 | 231 | if (m_mouse[0].bReleased) // Use mouse to draw maze, shift and ctrl to place start and end 232 | { 233 | if(nSelectedNodeX >=0 && nSelectedNodeX < nMapWidth) 234 | if (nSelectedNodeY >= 0 && nSelectedNodeY < nMapHeight) 235 | { 236 | if (m_keys[VK_SHIFT].bHeld) 237 | nodeStart = &nodes[nSelectedNodeY * nMapWidth + nSelectedNodeX]; 238 | else if (m_keys[VK_CONTROL].bHeld) 239 | nodeEnd = &nodes[nSelectedNodeY * nMapWidth + nSelectedNodeX]; 240 | else 241 | nodes[nSelectedNodeY * nMapWidth + nSelectedNodeX].bObstacle = !nodes[nSelectedNodeY * nMapWidth + nSelectedNodeX].bObstacle; 242 | 243 | Solve_AStar(); // Solve in "real-time" gives a nice effect 244 | } 245 | } 246 | 247 | // Draw Connections First - lines from this nodes position to its 248 | // connected neighbour node positions 249 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 250 | for (int x = 0; x < nMapWidth; x++) 251 | for (int y = 0; y < nMapHeight; y++) 252 | { 253 | for (auto n : nodes[y*nMapWidth + x].vecNeighbours) 254 | { 255 | DrawLine(x*nNodeSize + nNodeSize / 2, y*nNodeSize + nNodeSize / 2, 256 | n->x*nNodeSize + nNodeSize / 2, n->y*nNodeSize + nNodeSize / 2, PIXEL_SOLID, FG_DARK_BLUE); 257 | } 258 | } 259 | 260 | // Draw Nodes on top 261 | for (int x = 0; x < nMapWidth; x++) 262 | for (int y = 0; y < nMapHeight; y++) 263 | { 264 | 265 | Fill(x*nNodeSize + nNodeBorder, y*nNodeSize + nNodeBorder, 266 | (x + 1)*nNodeSize - nNodeBorder, (y + 1)*nNodeSize - nNodeBorder, 267 | PIXEL_HALF, nodes[y * nMapWidth + x].bObstacle ? FG_WHITE : FG_BLUE); 268 | 269 | if (nodes[y * nMapWidth + x].bVisited) 270 | Fill(x*nNodeSize + nNodeBorder, y*nNodeSize + nNodeBorder, (x + 1)*nNodeSize - nNodeBorder, (y + 1)*nNodeSize - nNodeBorder, PIXEL_SOLID, FG_BLUE); 271 | 272 | if(&nodes[y * nMapWidth + x] == nodeStart) 273 | Fill(x*nNodeSize + nNodeBorder, y*nNodeSize + nNodeBorder, (x + 1)*nNodeSize - nNodeBorder, (y + 1)*nNodeSize - nNodeBorder, PIXEL_SOLID, FG_GREEN); 274 | 275 | if(&nodes[y * nMapWidth + x] == nodeEnd) 276 | Fill(x*nNodeSize + nNodeBorder, y*nNodeSize + nNodeBorder, (x + 1)*nNodeSize - nNodeBorder, (y + 1)*nNodeSize - nNodeBorder, PIXEL_SOLID, FG_RED); 277 | 278 | } 279 | 280 | 281 | // Draw Path by starting ath the end, and following the parent node trail 282 | // back to the start - the start node will not have a parent path to follow 283 | if (nodeEnd != nullptr) 284 | { 285 | sNode *p = nodeEnd; 286 | while (p->parent != nullptr) 287 | { 288 | DrawLine(p->x*nNodeSize + nNodeSize / 2, p->y*nNodeSize + nNodeSize / 2, 289 | p->parent->x*nNodeSize + nNodeSize / 2, p->parent->y*nNodeSize + nNodeSize / 2, PIXEL_SOLID, FG_YELLOW); 290 | 291 | // Set next node to this node's parent 292 | p = p->parent; 293 | } 294 | } 295 | 296 | return true; 297 | } 298 | 299 | }; 300 | 301 | int main() 302 | { 303 | OneLoneCoder_PathFinding game; 304 | game.ConstructConsole(160, 160, 6, 6); 305 | game.Start(); 306 | return 0; 307 | } 308 | -------------------------------------------------------------------------------- /OneLoneCoder_PerlinNoise.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - What Is Perlin Noise? 3 | "Mountains, Clouds, Worms Landscapes?" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Noise is random - a bad thing when trying to procedurally generate content. Perlin 34 | noise adds coherence at varying spatial scales which create natural looking noise 35 | arrays. Perlin noise can be further processed into all sorts of assets, such as 36 | mountains, maps, rooms, textures, data sets. 37 | 38 | 39 | Author 40 | ~~~~~~ 41 | Twitter: @javidx9 42 | Blog: www.onelonecoder.com 43 | 44 | Video: 45 | ~~~~~~ 46 | https://youtu.be/6-0UaeJBumA 47 | 48 | Last Updated: 29/10/2017 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | using namespace std; 55 | 56 | #include "olcConsoleGameEngine.h" 57 | 58 | class OneLoneCoder_PerlinNoiseDemo : public olcConsoleGameEngine 59 | { 60 | public: 61 | OneLoneCoder_PerlinNoiseDemo() 62 | { 63 | m_sAppName = L"Perlin Noise"; 64 | } 65 | 66 | private: 67 | // 2D noise variables 68 | int nOutputWidth = 256; 69 | int nOutputHeight = 256; 70 | float *fNoiseSeed2D = nullptr; 71 | float *fPerlinNoise2D = nullptr; 72 | 73 | // 1D noise variables 74 | float *fNoiseSeed1D = nullptr; 75 | float *fPerlinNoise1D = nullptr; 76 | int nOutputSize = 256; 77 | 78 | 79 | int nOctaveCount = 1; 80 | float fScalingBias = 2.0f; 81 | int nMode = 1; 82 | 83 | 84 | virtual bool OnUserCreate() 85 | { 86 | nOutputWidth = ScreenWidth(); 87 | nOutputHeight = ScreenHeight(); 88 | 89 | fNoiseSeed2D = new float[nOutputWidth * nOutputHeight]; 90 | fPerlinNoise2D = new float[nOutputWidth * nOutputHeight]; 91 | for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; 92 | 93 | nOutputSize = ScreenWidth(); 94 | fNoiseSeed1D = new float[nOutputSize]; 95 | fPerlinNoise1D = new float[nOutputSize]; 96 | for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = (float)rand() / (float)RAND_MAX; 97 | 98 | return true; 99 | } 100 | 101 | virtual bool OnUserUpdate(float fElapsedTime) 102 | { 103 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 104 | 105 | if (m_keys[VK_SPACE].bReleased) 106 | nOctaveCount++; 107 | 108 | if (m_keys[L'1'].bReleased) 109 | nMode = 1; 110 | 111 | if (m_keys[L'2'].bReleased) 112 | nMode = 2; 113 | 114 | if (m_keys[L'3'].bReleased) 115 | nMode = 3; 116 | 117 | if (m_keys[L'Q'].bReleased) 118 | fScalingBias += 0.2f; 119 | 120 | if (m_keys[L'A'].bReleased) 121 | fScalingBias -= 0.2f; 122 | 123 | if (fScalingBias < 0.2f) 124 | fScalingBias = 0.2f; 125 | 126 | if (nOctaveCount == 9) 127 | nOctaveCount = 1; 128 | 129 | if (nMode == 1) // 1D Noise 130 | { 131 | if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1 132 | for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = (float)rand() / (float)RAND_MAX; 133 | 134 | if (m_keys[L'X'].bReleased) // Noise Between -1 and +1 135 | for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f; 136 | 137 | PerlinNoise1D(nOutputSize, fNoiseSeed1D, nOctaveCount, fScalingBias, fPerlinNoise1D); 138 | 139 | for (int x = 0; x < nOutputSize; x++) 140 | { 141 | int y = -(fPerlinNoise1D[x] * (float)ScreenHeight() / 2.0f) + (float)ScreenHeight() / 2.0f; 142 | if (y < ScreenHeight() / 2) 143 | for (int f = y; f < ScreenHeight() / 2; f++) 144 | Draw(x, f, PIXEL_SOLID, FG_GREEN); 145 | else 146 | for (int f = ScreenHeight() / 2; f <= y; f++) 147 | Draw(x, f, PIXEL_SOLID, FG_RED); 148 | } 149 | } 150 | 151 | if (nMode == 2) // 2D Noise 152 | { 153 | if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1 154 | for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; 155 | 156 | 157 | PerlinNoise2D(nOutputWidth, nOutputHeight, fNoiseSeed2D, nOctaveCount, fScalingBias, fPerlinNoise2D); 158 | 159 | for (int x = 0; x < nOutputWidth; x++) 160 | { 161 | for (int y = 0; y < nOutputHeight; y++) 162 | { 163 | short bg_col, fg_col; 164 | wchar_t sym; 165 | int pixel_bw = (int)(fPerlinNoise2D[y * nOutputWidth + x] * 12.0f); 166 | switch (pixel_bw) 167 | { 168 | case 0: bg_col = BG_BLACK; fg_col = FG_BLACK; sym = PIXEL_SOLID; break; 169 | 170 | case 1: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; 171 | case 2: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; 172 | case 3: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; 173 | case 4: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; 174 | 175 | case 5: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_QUARTER; break; 176 | case 6: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_HALF; break; 177 | case 7: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_THREEQUARTERS; break; 178 | case 8: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_SOLID; break; 179 | 180 | case 9: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; 181 | case 10: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; 182 | case 11: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; 183 | case 12: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; 184 | } 185 | 186 | Draw(x, y, sym, fg_col | bg_col); 187 | } 188 | } 189 | } 190 | 191 | if (nMode == 3) // 2D Noise - colourised 192 | { 193 | if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1 194 | for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; 195 | 196 | 197 | PerlinNoise2D(nOutputWidth, nOutputHeight, fNoiseSeed2D, nOctaveCount, fScalingBias, fPerlinNoise2D); 198 | 199 | for (int x = 0; x < nOutputWidth; x++) 200 | { 201 | for (int y = 0; y < nOutputHeight; y++) 202 | { 203 | short bg_col, fg_col; 204 | wchar_t sym; 205 | int pixel_bw = (int)(fPerlinNoise2D[y * nOutputWidth + x] * 16.0f); 206 | switch (pixel_bw) 207 | { 208 | case 0: bg_col = BG_DARK_BLUE; fg_col = FG_DARK_BLUE; sym = PIXEL_SOLID; break; 209 | 210 | case 1: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_QUARTER; break; 211 | case 2: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_HALF; break; 212 | case 3: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_THREEQUARTERS; break; 213 | case 4: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_SOLID; break; 214 | 215 | case 5: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_QUARTER; break; 216 | case 6: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_HALF; break; 217 | case 7: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_THREEQUARTERS; break; 218 | case 8: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_SOLID; break; 219 | 220 | case 9: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; 221 | case 10: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; 222 | case 11: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; 223 | case 12: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; 224 | 225 | case 13: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; 226 | case 14: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; 227 | case 15: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; 228 | case 16: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; 229 | } 230 | 231 | Draw(x, y, sym, fg_col | bg_col); 232 | } 233 | } 234 | } 235 | 236 | return true; 237 | } 238 | 239 | 240 | void PerlinNoise1D(int nCount, float *fSeed, int nOctaves, float fBias, float *fOutput) 241 | { 242 | // Used 1D Perlin Noise 243 | for (int x = 0; x < nCount; x++) 244 | { 245 | float fNoise = 0.0f; 246 | float fScaleAcc = 0.0f; 247 | float fScale = 1.0f; 248 | 249 | for (int o = 0; o < nOctaves; o++) 250 | { 251 | int nPitch = nCount >> o; 252 | int nSample1 = (x / nPitch) * nPitch; 253 | int nSample2 = (nSample1 + nPitch) % nCount; 254 | 255 | float fBlend = (float)(x - nSample1) / (float)nPitch; 256 | 257 | float fSample = (1.0f - fBlend) * fSeed[nSample1] + fBlend * fSeed[nSample2]; 258 | 259 | fScaleAcc += fScale; 260 | fNoise += fSample * fScale; 261 | fScale = fScale / fBias; 262 | } 263 | 264 | // Scale to seed range 265 | fOutput[x] = fNoise / fScaleAcc; 266 | } 267 | } 268 | 269 | void PerlinNoise2D(int nWidth, int nHeight, float *fSeed, int nOctaves, float fBias, float *fOutput) 270 | { 271 | // Used 1D Perlin Noise 272 | for (int x = 0; x < nWidth; x++) 273 | for (int y = 0; y < nHeight; y++) 274 | { 275 | float fNoise = 0.0f; 276 | float fScaleAcc = 0.0f; 277 | float fScale = 1.0f; 278 | 279 | for (int o = 0; o < nOctaves; o++) 280 | { 281 | int nPitch = nWidth >> o; 282 | int nSampleX1 = (x / nPitch) * nPitch; 283 | int nSampleY1 = (y / nPitch) * nPitch; 284 | 285 | int nSampleX2 = (nSampleX1 + nPitch) % nWidth; 286 | int nSampleY2 = (nSampleY1 + nPitch) % nWidth; 287 | 288 | float fBlendX = (float)(x - nSampleX1) / (float)nPitch; 289 | float fBlendY = (float)(y - nSampleY1) / (float)nPitch; 290 | 291 | float fSampleT = (1.0f - fBlendX) * fSeed[nSampleY1 * nWidth + nSampleX1] + fBlendX * fSeed[nSampleY1 * nWidth + nSampleX2]; 292 | float fSampleB = (1.0f - fBlendX) * fSeed[nSampleY2 * nWidth + nSampleX1] + fBlendX * fSeed[nSampleY2 * nWidth + nSampleX2]; 293 | 294 | fScaleAcc += fScale; 295 | fNoise += (fBlendY * (fSampleB - fSampleT) + fSampleT) * fScale; 296 | fScale = fScale / fBias; 297 | } 298 | 299 | // Scale to seed range 300 | fOutput[y * nWidth + x] = fNoise / fScaleAcc; 301 | } 302 | 303 | } 304 | 305 | 306 | }; 307 | 308 | 309 | int main() 310 | { 311 | OneLoneCoder_PerlinNoiseDemo game; 312 | game.ConstructConsole(256, 256, 3, 3); 313 | game.Start(); 314 | return 0; 315 | } 316 | -------------------------------------------------------------------------------- /OneLoneCoder_PlatformGame1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Code-It-Yourself! Simple Tile Based Platform Game 3 | "Its-a meee-a Jario!" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Tile maps are fundamental to most 2D games. This program explores emulating a classic 2D platformer 34 | using floating point truncation to implement robust collision between a moving tile and a tilemap 35 | representing the level. 36 | 37 | Controls 38 | ~~~~~~~~ 39 | Left and Right arrow keys move Jario, Space bar jumps. 40 | (Up and Down also move jario) 41 | 42 | Author 43 | ~~~~~~ 44 | Twitter: @javidx9 45 | Blog: www.onelonecoder.com 46 | YouTube: www.youtube.com/javidx9 47 | Discord: https://discord.gg/WhwHUMV 48 | 49 | Video: 50 | ~~~~~~ 51 | https://youtu.be/oJvJZNyW_rw 52 | 53 | Last Updated: 04/02/2018 54 | */ 55 | 56 | #include 57 | #include 58 | using namespace std; 59 | 60 | #include "olcConsoleGameEngine.h" 61 | 62 | class OneLoneCoder_Platformer : public olcConsoleGameEngine 63 | { 64 | public: 65 | OneLoneCoder_Platformer() 66 | { 67 | m_sAppName = L"Tile Based Platform Game"; 68 | } 69 | 70 | private: 71 | // Level storage 72 | wstring sLevel; 73 | int nLevelWidth; 74 | int nLevelHeight; 75 | 76 | // Player Properties 77 | float fPlayerPosX = 1.0f; 78 | float fPlayerPosY = 1.0f; 79 | float fPlayerVelX = 0.0f; 80 | float fPlayerVelY = 0.0f; 81 | bool bPlayerOnGround = false; 82 | 83 | // Camera properties 84 | float fCameraPosX = 0.0f; 85 | float fCameraPosY = 0.0f; 86 | 87 | // Sprite Resources 88 | olcSprite *spriteTiles = nullptr; 89 | olcSprite *spriteMan = nullptr; 90 | 91 | // Sprite selection flags 92 | int nDirModX = 0; 93 | int nDirModY = 0; 94 | 95 | protected: 96 | virtual bool OnUserCreate() 97 | { 98 | nLevelWidth = 64; 99 | nLevelHeight = 16; 100 | sLevel += L"................................................................"; 101 | sLevel += L"................................................................"; 102 | sLevel += L".......ooooo...................................................."; 103 | sLevel += L"........ooo....................................................."; 104 | sLevel += L".......................########................................."; 105 | sLevel += L".....BB?BBBB?BB.......###..............#.#......................"; 106 | sLevel += L"....................###................#.#......................"; 107 | sLevel += L"...................####........................................."; 108 | sLevel += L"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.##############.....########"; 109 | sLevel += L"...................................#.#...............###........"; 110 | sLevel += L"........................############.#............###..........."; 111 | sLevel += L"........................#............#.........###.............."; 112 | sLevel += L"........................#.############......###................."; 113 | sLevel += L"........................#................###...................."; 114 | sLevel += L"........................#################......................."; 115 | sLevel += L"................................................................"; 116 | 117 | spriteTiles = new olcSprite(L"JarioSprites/leveljario.spr"); 118 | spriteMan = new olcSprite(L"JarioSprites/minijario.spr"); 119 | 120 | return true; 121 | } 122 | 123 | virtual bool OnUserUpdate(float fElapsedTime) 124 | { 125 | // Utility Lambdas 126 | auto GetTile = [&](int x, int y) 127 | { 128 | if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight) 129 | return sLevel[y * nLevelWidth + x]; 130 | else 131 | return L' '; 132 | }; 133 | 134 | auto SetTile = [&](int x, int y, wchar_t c) 135 | { 136 | if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight) 137 | sLevel[y*nLevelWidth + x] = c; 138 | }; 139 | 140 | //fPlayerVelX = 0.0f; 141 | //fPlayerVelY = 0.0f; 142 | 143 | // Handle Input 144 | if (IsFocused()) 145 | { 146 | if (GetKey(VK_UP).bHeld) 147 | { 148 | fPlayerVelY = -6.0f; 149 | } 150 | 151 | if (GetKey(VK_DOWN).bHeld) 152 | { 153 | fPlayerVelY = 6.0f; 154 | } 155 | 156 | if (GetKey(VK_LEFT).bHeld) 157 | { 158 | fPlayerVelX += (bPlayerOnGround ? -25.0f : -15.0f) * fElapsedTime; 159 | nDirModY = 1; 160 | } 161 | 162 | if (GetKey(VK_RIGHT).bHeld) 163 | { 164 | fPlayerVelX += (bPlayerOnGround ? 25.0f : 15.0f) * fElapsedTime; 165 | nDirModY = 0; 166 | } 167 | 168 | if (GetKey(VK_SPACE).bPressed) 169 | { 170 | if (fPlayerVelY == 0) 171 | { 172 | fPlayerVelY = -12.0f; 173 | nDirModX = 1; 174 | } 175 | } 176 | } 177 | 178 | // Gravity 179 | fPlayerVelY += 20.0f * fElapsedTime; 180 | 181 | // Drag 182 | if (bPlayerOnGround) 183 | { 184 | fPlayerVelX += -3.0f * fPlayerVelX * fElapsedTime; 185 | if (fabs(fPlayerVelX) < 0.01f) 186 | fPlayerVelX = 0.0f; 187 | } 188 | 189 | // Clamp velocities 190 | if (fPlayerVelX > 10.0f) 191 | fPlayerVelX = 10.0f; 192 | 193 | if (fPlayerVelX < -10.0f) 194 | fPlayerVelX = -10.0f; 195 | 196 | if (fPlayerVelY > 100.0f) 197 | fPlayerVelY = 100.0f; 198 | 199 | if (fPlayerVelY < -100.0f) 200 | fPlayerVelY = -100.0f; 201 | 202 | // Calculate potential new position 203 | float fNewPlayerPosX = fPlayerPosX + fPlayerVelX * fElapsedTime; 204 | float fNewPlayerPosY = fPlayerPosY + fPlayerVelY * fElapsedTime; 205 | 206 | // Check for pickups! 207 | if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 0.0f) == L'o') 208 | SetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 0.0f, L'.'); 209 | 210 | if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f) == L'o') 211 | SetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f, L'.'); 212 | 213 | if (GetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 0.0f) == L'o') 214 | SetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 0.0f, L'.'); 215 | 216 | if (GetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 1.0f) == L'o') 217 | SetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 1.0f, L'.'); 218 | 219 | // Check for Collision 220 | if (fPlayerVelX <= 0) // Moving Left 221 | { 222 | if (GetTile(fNewPlayerPosX + 0.0f, fPlayerPosY + 0.0f) != L'.' || GetTile(fNewPlayerPosX + 0.0f, fPlayerPosY + 0.9f) != L'.') 223 | { 224 | fNewPlayerPosX = (int)fNewPlayerPosX + 1; 225 | fPlayerVelX = 0; 226 | } 227 | } 228 | else // Moving Right 229 | { 230 | if (GetTile(fNewPlayerPosX + 1.0f, fPlayerPosY + 0.0f) != L'.' || GetTile(fNewPlayerPosX + 1.0f, fPlayerPosY + 0.9f) != L'.') 231 | { 232 | fNewPlayerPosX = (int)fNewPlayerPosX; 233 | fPlayerVelX = 0; 234 | 235 | } 236 | } 237 | 238 | bPlayerOnGround = false; 239 | if (fPlayerVelY <= 0) // Moving Up 240 | { 241 | if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY) != L'.' || GetTile(fNewPlayerPosX + 0.9f, fNewPlayerPosY) != L'.') 242 | { 243 | fNewPlayerPosY = (int)fNewPlayerPosY + 1; 244 | fPlayerVelY = 0; 245 | } 246 | } 247 | else // Moving Down 248 | { 249 | if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f) != L'.' || GetTile(fNewPlayerPosX + 0.9f, fNewPlayerPosY + 1.0f) != L'.') 250 | { 251 | fNewPlayerPosY = (int)fNewPlayerPosY; 252 | fPlayerVelY = 0; 253 | bPlayerOnGround = true; // Player has a solid surface underfoot 254 | nDirModX = 0; 255 | } 256 | } 257 | 258 | // Apply new position 259 | fPlayerPosX = fNewPlayerPosX; 260 | fPlayerPosY = fNewPlayerPosY; 261 | 262 | // Link camera to player position 263 | fCameraPosX = fPlayerPosX; 264 | fCameraPosY = fPlayerPosY; 265 | 266 | // Draw Level 267 | int nTileWidth = 16; 268 | int nTileHeight = 16; 269 | int nVisibleTilesX = ScreenWidth() / nTileWidth; 270 | int nVisibleTilesY = ScreenHeight() / nTileHeight; 271 | 272 | // Calculate Top-Leftmost visible tile 273 | float fOffsetX = fCameraPosX - (float)nVisibleTilesX / 2.0f; 274 | float fOffsetY = fCameraPosY - (float)nVisibleTilesY / 2.0f; 275 | 276 | // Clamp camera to game boundaries 277 | if (fOffsetX < 0) fOffsetX = 0; 278 | if (fOffsetY < 0) fOffsetY = 0; 279 | if (fOffsetX > nLevelWidth - nVisibleTilesX) fOffsetX = nLevelWidth - nVisibleTilesX; 280 | if (fOffsetY > nLevelHeight - nVisibleTilesY) fOffsetY = nLevelHeight - nVisibleTilesY; 281 | 282 | // Get offsets for smooth movement 283 | float fTileOffsetX = (fOffsetX - (int)fOffsetX) * nTileWidth; 284 | float fTileOffsetY = (fOffsetY - (int)fOffsetY) * nTileHeight; 285 | 286 | // Draw visible tile map 287 | for (int x = -1; x < nVisibleTilesX + 1; x++) 288 | { 289 | for (int y = -1; y < nVisibleTilesY + 1; y++) 290 | { 291 | wchar_t sTileID = GetTile(x + fOffsetX, y + fOffsetY); 292 | switch (sTileID) 293 | { 294 | case L'.': // Sky 295 | Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_CYAN); 296 | break; 297 | case L'#': // Solid Block 298 | //Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_RED); 299 | DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 2 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); 300 | break; 301 | case L'G': // Ground Block 302 | DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 0 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); 303 | break; 304 | case L'B': // Brick Block 305 | DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 0 * nTileWidth, 1 * nTileHeight, nTileWidth, nTileHeight); 306 | break; 307 | case L'?': // Question Block 308 | DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 1 * nTileWidth, 1 * nTileHeight, nTileWidth, nTileHeight); 309 | break; 310 | case L'o': // Coin 311 | Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_CYAN); 312 | DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 3 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); 313 | break; 314 | default: 315 | Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_BLACK); 316 | break; 317 | } 318 | } 319 | } 320 | 321 | // Draw Player 322 | //Fill((fPlayerPosX - fOffsetX) * nTileWidth, (fPlayerPosY - fOffsetY) * nTileWidth, (fPlayerPosX - fOffsetX + 1.0f) * nTileWidth, (fPlayerPosY - fOffsetY + 1.0f) * nTileHeight, PIXEL_SOLID, FG_GREEN); 323 | DrawPartialSprite((fPlayerPosX - fOffsetX) * nTileWidth, (fPlayerPosY - fOffsetY) * nTileWidth, spriteMan, nDirModX * nTileWidth, nDirModY * nTileHeight, nTileWidth, nTileHeight); 324 | return true; 325 | } 326 | }; 327 | 328 | int main() 329 | { 330 | OneLoneCoder_Platformer game; 331 | if (game.ConstructConsole(256, 240, 4, 4)) 332 | game.Start(); 333 | return 0; 334 | } 335 | -------------------------------------------------------------------------------- /OneLoneCoder_Pseudo3DPlanesMode7.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Programming Pseudo 3D planes, aka MODE7 3 | "The SNES was too advanced for its time" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | 13 | https://www.github.com/onelonecoder 14 | https://www.onelonecoder.com 15 | https://www.youtube.com/javidx9 16 | 17 | GNU GPLv3 18 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 19 | 20 | From Javidx9 :) 21 | ~~~~~~~~~~~~~~~ 22 | Hello! Ultimately I don't care what you use this for. It's intended to be 23 | educational, and perhaps to the oddly minded - a little bit of fun. 24 | Please hack this, change it and use it in any way you see fit. You acknowledge 25 | that I am not responsible for anything bad that happens as a result of 26 | your actions. However this code is protected by GNU GPLv3, see the license in the 27 | github repo. This means you must attribute me if you use it. You can view this 28 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 29 | 30 | Cheers! 31 | 32 | Background 33 | ~~~~~~~~~~ 34 | Pseudo 3D was a technique made popular by the Super Nintendo 35 | in games such as Super Mario Kart. This code demonstrates the 36 | effect of that hardware mode. 37 | 38 | Author 39 | ~~~~~~ 40 | Twitter: @javidx9 41 | Blog: www.onelonecoder.com 42 | 43 | Video: 44 | ~~~~~~ 45 | https://youtu.be/ybLZyY655iY 46 | 47 | Last Updated: 15/04/2018 48 | */ 49 | 50 | #include "olcConsoleGameEngine.h" 51 | 52 | class OneLoneCoder_FakeMode7 : public olcConsoleGameEngine 53 | { 54 | public: 55 | OneLoneCoder_FakeMode7() 56 | { 57 | m_sAppName = L"Pseudo 3D Planes"; 58 | } 59 | 60 | 61 | private: 62 | float fWorldX = 1000.0f; 63 | float fWorldY = 1000.0f; 64 | float fWorldA = 0.1f; 65 | float fNear = 0.005f; 66 | float fFar = 0.03f; 67 | float fFoVHalf = 3.14159f / 4.0f; 68 | 69 | olcSprite *sprGround; 70 | olcSprite *sprSky; 71 | 72 | int nMapSize = 1024; 73 | 74 | protected: 75 | virtual bool OnUserCreate() 76 | { 77 | // Create a large sprite and fill it with horizontal and vertical lines 78 | //sprGround = new olcSprite(nMapSize, nMapSize); 79 | 80 | //for (int x = 0; x <= nMapSize; x += 32) 81 | //{ 82 | // for (int y = 0; y < nMapSize; y++) 83 | // { 84 | // sprGround->SetColour(x, y, FG_MAGENTA); 85 | // sprGround->SetGlyph(x, y, PIXEL_SOLID); 86 | 87 | // sprGround->SetColour(x+1, y, FG_MAGENTA); 88 | // sprGround->SetGlyph(x+1, y, PIXEL_SOLID); 89 | 90 | // sprGround->SetColour(x-1, y, FG_MAGENTA); 91 | // sprGround->SetGlyph(x-1, y, PIXEL_SOLID); 92 | 93 | // sprGround->SetColour(y, x, FG_BLUE); 94 | // sprGround->SetGlyph(y, x, PIXEL_SOLID); 95 | 96 | // sprGround->SetColour(y, x+1, FG_BLUE); 97 | // sprGround->SetGlyph(y, x+1, PIXEL_SOLID); 98 | 99 | // sprGround->SetColour(y, x-1, FG_BLUE); 100 | // sprGround->SetGlyph(y, x-1, PIXEL_SOLID); 101 | // } 102 | //} 103 | 104 | // Simply load very large sprites from file 105 | sprGround = new olcSprite(L"mariokart.spr"); 106 | sprSky = new olcSprite(L"sky1.spr"); 107 | 108 | return true; 109 | } 110 | 111 | virtual bool OnUserUpdate(float fElapsedTime) 112 | { 113 | 114 | // Control rendering parameters dynamically 115 | if (GetKey(L'Q').bHeld) fNear += 0.1f * fElapsedTime; 116 | if (GetKey(L'A').bHeld) fNear -= 0.1f * fElapsedTime; 117 | 118 | if (GetKey(L'W').bHeld) fFar += 0.1f * fElapsedTime; 119 | if (GetKey(L'S').bHeld) fFar -= 0.1f * fElapsedTime; 120 | 121 | if (GetKey(L'Z').bHeld) fFoVHalf += 0.1f * fElapsedTime; 122 | if (GetKey(L'X').bHeld) fFoVHalf -= 0.1f * fElapsedTime; 123 | 124 | 125 | // Create Frustum corner points 126 | float fFarX1 = fWorldX + cosf(fWorldA - fFoVHalf) * fFar; 127 | float fFarY1 = fWorldY + sinf(fWorldA - fFoVHalf) * fFar; 128 | 129 | float fNearX1 = fWorldX + cosf(fWorldA - fFoVHalf) * fNear; 130 | float fNearY1 = fWorldY + sinf(fWorldA - fFoVHalf) * fNear; 131 | 132 | float fFarX2 = fWorldX + cosf(fWorldA + fFoVHalf) * fFar; 133 | float fFarY2 = fWorldY + sinf(fWorldA + fFoVHalf) * fFar; 134 | 135 | float fNearX2 = fWorldX + cosf(fWorldA + fFoVHalf) * fNear; 136 | float fNearY2 = fWorldY + sinf(fWorldA + fFoVHalf) * fNear; 137 | 138 | // Starting with furthest away line and work towards the camera point 139 | for (int y = 0; y < ScreenHeight() / 2; y++) 140 | { 141 | // Take a sample point for depth linearly related to rows down screen 142 | float fSampleDepth = (float)y / ((float)ScreenHeight() / 2.0f); 143 | 144 | // Use sample point in non-linear (1/x) way to enable perspective 145 | // and grab start and end points for lines across the screen 146 | float fStartX = (fFarX1 - fNearX1) / (fSampleDepth) + fNearX1; 147 | float fStartY = (fFarY1 - fNearY1) / (fSampleDepth) + fNearY1; 148 | float fEndX = (fFarX2 - fNearX2) / (fSampleDepth) + fNearX2; 149 | float fEndY = (fFarY2 - fNearY2) / (fSampleDepth) + fNearY2; 150 | 151 | // Linearly interpolate lines across the screen 152 | for (int x = 0; x < ScreenWidth(); x++) 153 | { 154 | float fSampleWidth = (float)x / (float)ScreenWidth(); 155 | float fSampleX = (fEndX - fStartX) * fSampleWidth + fStartX; 156 | float fSampleY = (fEndY - fStartY) * fSampleWidth + fStartY; 157 | 158 | // Wrap sample coordinates to give "infinite" periodicity on maps 159 | fSampleX = fmod(fSampleX, 1.0f); 160 | fSampleY = fmod(fSampleY, 1.0f); 161 | 162 | // Sample symbol and colour from map sprite, and draw the 163 | // pixel to the screen 164 | wchar_t sym = sprGround->SampleGlyph(fSampleX, fSampleY); 165 | short col = sprGround->SampleColour(fSampleX, fSampleY); 166 | Draw(x, y + (ScreenHeight() / 2), sym, col); 167 | 168 | // Sample symbol and colour from sky sprite, we can use same 169 | // coordinates, but we need to draw the "inverted" y-location 170 | sym = sprSky->SampleGlyph(fSampleX, fSampleY); 171 | col = sprSky->SampleColour(fSampleX, fSampleY); 172 | Draw(x, (ScreenHeight() / 2) - y, sym, col); 173 | } 174 | } 175 | 176 | // Draw a blanking line to fill the gap between sky and ground 177 | DrawLine(0, ScreenHeight() / 2, ScreenWidth(), ScreenHeight() / 2, PIXEL_SOLID, FG_CYAN); 178 | 179 | // Handle user navigation with arrow keys 180 | if (GetKey(VK_LEFT).bHeld) 181 | fWorldA -= 1.0f * fElapsedTime; 182 | 183 | if (GetKey(VK_RIGHT).bHeld) 184 | fWorldA += 1.0f * fElapsedTime; 185 | 186 | if (GetKey(VK_UP).bHeld) 187 | { 188 | fWorldX += cosf(fWorldA) * 0.2f * fElapsedTime; 189 | fWorldY += sinf(fWorldA) * 0.2f * fElapsedTime; 190 | } 191 | 192 | if (GetKey(VK_DOWN).bHeld) 193 | { 194 | fWorldX -= cosf(fWorldA) * 0.2f * fElapsedTime; 195 | fWorldY -= sinf(fWorldA) * 0.2f * fElapsedTime; 196 | } 197 | 198 | return true; 199 | } 200 | 201 | 202 | 203 | }; 204 | 205 | int main() 206 | { 207 | OneLoneCoder_FakeMode7 game; 208 | game.ConstructConsole(320, 240, 4, 4); 209 | game.Start(); 210 | return 0; 211 | } -------------------------------------------------------------------------------- /OneLoneCoder_RacingLines.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Programming Racing Lines 3 | "Brake! Brake! Hard Left! " - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | GNU GPLv3 16 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 17 | 18 | From Javidx9 :) 19 | ~~~~~~~~~~~~~~~ 20 | Hello! Ultimately I don't care what you use this for. It's intended to be 21 | educational, and perhaps to the oddly minded - a little bit of fun. 22 | Please hack this, change it and use it in any way you see fit. You acknowledge 23 | that I am not responsible for anything bad that happens as a result of 24 | your actions. However this code is protected by GNU GPLv3, see the license in the 25 | github repo. This means you must attribute me if you use it. You can view this 26 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 27 | Cheers! 28 | 29 | Background 30 | ~~~~~~~~~~ 31 | Algorithmically generating a racing line is quite tricky. This simple framework 32 | allows me to explore different methods. Use mouse to drag points, and A & S keys 33 | to change the number of iterations. 34 | 35 | Author 36 | ~~~~~~ 37 | Twitter: @javidx9 38 | Blog: http://www.onelonecoder.com 39 | Discord: https://discord.gg/WhwHUMV 40 | 41 | Video: 42 | ~~~~~~ 43 | https://youtu.be/FlieT66N9OM 44 | 45 | Last Updated: 27/05/2018 46 | */ 47 | 48 | #include 49 | #include 50 | using namespace std; 51 | 52 | #include "olcConsoleGameEngine.h" 53 | 54 | // See Programming Splines! Videos 55 | struct sPoint2D 56 | { 57 | float x; 58 | float y; 59 | float length; 60 | }; 61 | 62 | struct sSpline 63 | { 64 | vector points; 65 | float fTotalSplineLength = 0.0f; 66 | bool bIsLooped = true; 67 | 68 | sPoint2D GetSplinePoint(float t) 69 | { 70 | int p0, p1, p2, p3; 71 | if (!bIsLooped) 72 | { 73 | p1 = (int)t + 1; 74 | p2 = p1 + 1; 75 | p3 = p2 + 1; 76 | p0 = p1 - 1; 77 | } 78 | else 79 | { 80 | p1 = ((int)t) % points.size(); 81 | p2 = (p1 + 1) % points.size(); 82 | p3 = (p2 + 1) % points.size(); 83 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 84 | } 85 | 86 | t = t - (int)t; 87 | 88 | float tt = t * t; 89 | float ttt = tt * t; 90 | 91 | float q1 = -ttt + 2.0f*tt - t; 92 | float q2 = 3.0f*ttt - 5.0f*tt + 2.0f; 93 | float q3 = -3.0f*ttt + 4.0f*tt + t; 94 | float q4 = ttt - tt; 95 | 96 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 97 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 98 | 99 | return{ tx, ty }; 100 | } 101 | 102 | sPoint2D GetSplineGradient(float t) 103 | { 104 | int p0, p1, p2, p3; 105 | if (!bIsLooped) 106 | { 107 | p1 = (int)t + 1; 108 | p2 = p1 + 1; 109 | p3 = p2 + 1; 110 | p0 = p1 - 1; 111 | } 112 | else 113 | { 114 | p1 = ((int)t) % points.size(); 115 | p2 = (p1 + 1) % points.size(); 116 | p3 = (p2 + 1) % points.size(); 117 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 118 | } 119 | 120 | t = t - (int)t; 121 | 122 | float tt = t * t; 123 | float ttt = tt * t; 124 | 125 | float q1 = -3.0f * tt + 4.0f*t - 1.0f; 126 | float q2 = 9.0f*tt - 10.0f*t; 127 | float q3 = -9.0f*tt + 8.0f*t + 1.0f; 128 | float q4 = 3.0f*tt - 2.0f*t; 129 | 130 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 131 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 132 | 133 | return{ tx, ty }; 134 | } 135 | 136 | float CalculateSegmentLength(int node) 137 | { 138 | float fLength = 0.0f; 139 | float fStepSize = 0.1; 140 | 141 | sPoint2D old_point, new_point; 142 | old_point = GetSplinePoint((float)node); 143 | 144 | for (float t = 0; t < 1.0f; t += fStepSize) 145 | { 146 | new_point = GetSplinePoint((float)node + t); 147 | fLength += sqrtf((new_point.x - old_point.x)*(new_point.x - old_point.x) 148 | + (new_point.y - old_point.y)*(new_point.y - old_point.y)); 149 | old_point = new_point; 150 | } 151 | 152 | return fLength; 153 | } 154 | 155 | 156 | float GetNormalisedOffset(float p) 157 | { 158 | // Which node is the base? 159 | int i = 0; 160 | while (p > points[i].length) 161 | { 162 | p -= points[i].length; 163 | i++; 164 | } 165 | 166 | // The fractional is the offset 167 | return (float)i + (p / points[i].length); 168 | } 169 | 170 | 171 | void UpdateSplineProperties() 172 | { 173 | // Use to cache local spline lengths and overall spline length 174 | fTotalSplineLength = 0.0f; 175 | 176 | if (bIsLooped) 177 | { 178 | // Each node has a succeeding length 179 | for (int i = 0; i < points.size(); i++) 180 | { 181 | points[i].length = CalculateSegmentLength(i); 182 | fTotalSplineLength += points[i].length; 183 | } 184 | } 185 | else 186 | { 187 | for (int i = 1; i < points.size() - 2; i++) 188 | { 189 | points[i].length = CalculateSegmentLength(i); 190 | fTotalSplineLength += points[i].length; 191 | } 192 | } 193 | } 194 | 195 | void DrawSelf(olcConsoleGameEngine* gfx, float ox, float oy, wchar_t c = 0x2588, short col = 0x000F) 196 | { 197 | if (bIsLooped) 198 | { 199 | for (float t = 0; t < (float)points.size() - 0; t += 0.005f) 200 | { 201 | sPoint2D pos = GetSplinePoint(t); 202 | gfx->Draw(pos.x, pos.y, c, col); 203 | } 204 | } 205 | else // Not Looped 206 | { 207 | for (float t = 0; t < (float)points.size() - 3; t += 0.005f) 208 | { 209 | sPoint2D pos = GetSplinePoint(t); 210 | gfx->Draw(pos.x, pos.y, c, col); 211 | } 212 | } 213 | } 214 | 215 | }; 216 | 217 | 218 | 219 | //////////////////////////////////////////////////////////////////////// 220 | //////////////////////////////////////////////////////////////////////// 221 | 222 | class OneLoneCoder_RacingLine : public olcConsoleGameEngine 223 | { 224 | public: 225 | OneLoneCoder_RacingLine() 226 | { 227 | m_sAppName = L"Racing Line"; 228 | } 229 | 230 | private: 231 | sSpline path, trackLeft, trackRight, racingLine; // Various splines 232 | 233 | int nNodes = 20; // Number of nodes in spline 234 | 235 | float fDisplacement[20]; // Displacement along spline node normal 236 | 237 | int nIterations = 1; 238 | float fMarker = 1.0f; 239 | int nSelectedNode = -1; 240 | 241 | vector> vecModelCar; 242 | 243 | protected: 244 | // Called by olcConsoleGameEngine 245 | virtual bool OnUserCreate() 246 | { 247 | for (int i = 0; i < nNodes; i++) 248 | { 249 | //path.points.push_back( 250 | // { 30.0f * sinf((float)i / (float)nNodes * 3.14159f * 2.0f) + ScreenWidth() / 2, 251 | // 30.0f * cosf((float)i / (float)nNodes * 3.14159f * 2.0f) + ScreenHeight() / 2 }); 252 | 253 | // Could use allocation functions for thes now, but just size via 254 | // append 255 | trackLeft.points.push_back({ 0.0f, 0.0f }); 256 | trackRight.points.push_back({ 0.0f, 0.0f }); 257 | racingLine.points.push_back({ 0.0f, 0.0f }); 258 | } 259 | 260 | // A hand crafted track 261 | path.points = { { 81.8f, 196.0f }, { 108.0f,210.0f }, { 152.0f,216.0f }, 262 | { 182.0f,185.6f }, { 190.0f,159.0f }, { 198.0f,122.0f }, { 226.0f,93.0f }, 263 | { 224.0f,41.0f }, { 204.0f,15.0f }, { 158.0f,24.0f }, { 146.0f,52.0f }, 264 | { 157.0f,93.0f }, { 124.0f,129.0f }, { 83.0f,104.0f }, { 77.0f,62.0f }, 265 | { 40.0f,57.0f }, { 21.0f,83.0f }, { 33.0f,145.0f }, { 30.0f,198.0f }, 266 | { 48.0f,210.0f } }; 267 | 268 | vecModelCar = { { 2,0 },{ 0,-1 },{ 0,1 } }; 269 | 270 | path.UpdateSplineProperties(); 271 | return true; 272 | } 273 | 274 | // Called by olcConsoleGameEngine 275 | virtual bool OnUserUpdate(float fElapsedTime) 276 | { 277 | // Clear Screen 278 | Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_DARK_GREEN); 279 | 280 | // Handle iteration count 281 | if (m_keys[L'A'].bHeld) nIterations++; 282 | if (m_keys[L'S'].bHeld) nIterations--; 283 | if (nIterations < 0) nIterations = 0; 284 | 285 | 286 | // Check if node is selected with mouse 287 | if (GetMouse(0).bPressed) 288 | { 289 | for (int i = 0; i < path.points.size(); i++) 290 | { 291 | float d = sqrtf(powf(path.points[i].x - GetMouseX(), 2) + powf(path.points[i].y - GetMouseY(), 2)); 292 | if (d < 5.0f) 293 | { 294 | nSelectedNode = i; 295 | break; 296 | } 297 | } 298 | } 299 | 300 | if (GetMouse(0).bReleased) 301 | nSelectedNode = -1; 302 | 303 | // Move selected node 304 | if (GetMouse(0).bHeld && nSelectedNode >= 0) 305 | { 306 | path.points[nSelectedNode].x = GetMouseX(); 307 | path.points[nSelectedNode].y = GetMouseY(); 308 | path.UpdateSplineProperties(); 309 | } 310 | 311 | // Move car around racing line 312 | fMarker += 2.0f * fElapsedTime; 313 | if (fMarker >= (float)racingLine.fTotalSplineLength) 314 | fMarker -= (float)racingLine.fTotalSplineLength; 315 | 316 | // Calculate track boundary points 317 | float fTrackWidth = 10.0f; 318 | for (int i = 0; i < path.points.size(); i++) 319 | { 320 | sPoint2D p1 = path.GetSplinePoint(i); 321 | sPoint2D g1 = path.GetSplineGradient(i); 322 | float glen = sqrtf(g1.x*g1.x + g1.y*g1.y); 323 | 324 | trackLeft.points[i].x = p1.x + fTrackWidth * (-g1.y / glen); 325 | trackLeft.points[i].y = p1.y + fTrackWidth * ( g1.x / glen); 326 | trackRight.points[i].x = p1.x - fTrackWidth * (-g1.y / glen); 327 | trackRight.points[i].y = p1.y - fTrackWidth * (g1.x / glen); 328 | } 329 | 330 | // Draw Track 331 | float fRes = 0.2f; 332 | for (float t = 0.0f; t < path.points.size(); t += fRes) 333 | { 334 | sPoint2D pl1 = trackLeft.GetSplinePoint(t); 335 | sPoint2D pr1 = trackRight.GetSplinePoint(t); 336 | sPoint2D pl2 = trackLeft.GetSplinePoint(t + fRes); 337 | sPoint2D pr2 = trackRight.GetSplinePoint(t + fRes); 338 | 339 | FillTriangle(pl1.x, pl1.y, pr1.x, pr1.y, pr2.x, pr2.y, PIXEL_SOLID, FG_GREY); 340 | FillTriangle(pl1.x, pl1.y, pl2.x, pl2.y, pr2.x, pr2.y, PIXEL_SOLID, FG_GREY); 341 | } 342 | 343 | // Reset racing line 344 | for (int i = 0; i < racingLine.points.size(); i++) 345 | { 346 | racingLine.points[i] = path.points[i]; 347 | fDisplacement[i] = 0; 348 | } 349 | racingLine.UpdateSplineProperties(); 350 | 351 | for (int n = 0; n < nIterations; n++) 352 | { 353 | for (int i = 0; i < racingLine.points.size(); i++) 354 | { 355 | // Get locations of neighbour nodes 356 | sPoint2D pointRight = racingLine.points[(i + 1) % racingLine.points.size()]; 357 | sPoint2D pointLeft = racingLine.points[(i + racingLine.points.size() - 1) % racingLine.points.size()]; 358 | sPoint2D pointMiddle = racingLine.points[i]; 359 | 360 | // Create vectors to neighbours 361 | sPoint2D vectorLeft = { pointLeft.x - pointMiddle.x, pointLeft.y - pointMiddle.y }; 362 | sPoint2D vectorRight = { pointRight.x - pointMiddle.x, pointRight.y - pointMiddle.y }; 363 | 364 | // Normalise neighbours 365 | float lengthLeft = sqrtf(vectorLeft.x*vectorLeft.x + vectorLeft.y*vectorLeft.y); 366 | sPoint2D leftn = { vectorLeft.x / lengthLeft, vectorLeft.y / lengthLeft }; 367 | float lengthRight = sqrtf(vectorRight.x*vectorRight.x + vectorRight.y*vectorRight.y); 368 | sPoint2D rightn = { vectorRight.x / lengthRight, vectorRight.y / lengthRight }; 369 | 370 | // Add together to create bisector vector 371 | sPoint2D vectorSum = { rightn.x + leftn.x, rightn.y + leftn.y }; 372 | float len = sqrtf(vectorSum.x*vectorSum.x + vectorSum.y*vectorSum.y); 373 | vectorSum.x /= len; vectorSum.y /= len; 374 | 375 | // Get point gradient and normalise 376 | sPoint2D g = path.GetSplineGradient(i); 377 | float glen = sqrtf(g.x*g.x + g.y*g.y); 378 | g.x /= glen; g.y /= glen; 379 | 380 | // Project required correction onto point tangent to give displacment 381 | float dp = -g.y*vectorSum.x + g.x * vectorSum.y; 382 | 383 | // Shortest path 384 | fDisplacement[i] += (dp * 0.3f); 385 | 386 | // Curvature 387 | //fDisplacement[(i + 1) % racingLine.points.size()] += dp * -0.2f; 388 | //fDisplacement[(i - 1 + racingLine.points.size()) % racingLine.points.size()] += dp * -0.2f; 389 | 390 | } 391 | 392 | // Clamp displaced points to track width 393 | for (int i = 0; i < racingLine.points.size(); i++) 394 | { 395 | if (fDisplacement[i] >= fTrackWidth) fDisplacement[i] = fTrackWidth; 396 | if (fDisplacement[i] <= -fTrackWidth) fDisplacement[i] = -fTrackWidth; 397 | 398 | sPoint2D g = path.GetSplineGradient(i); 399 | float glen = sqrtf(g.x*g.x + g.y*g.y); 400 | g.x /= glen; g.y /= glen; 401 | 402 | racingLine.points[i].x = path.points[i].x + -g.y * fDisplacement[i]; 403 | racingLine.points[i].y = path.points[i].y + g.x * fDisplacement[i]; 404 | } 405 | } 406 | 407 | path.DrawSelf(this, 0, 0); 408 | //trackLeft.DrawSelf(this, 0, 0); 409 | //trackRight.DrawSelf(this, 0, 0); 410 | 411 | racingLine.UpdateSplineProperties(); 412 | racingLine.DrawSelf(this, 0, 0, PIXEL_SOLID, FG_BLUE); 413 | 414 | for (auto i : path.points) 415 | Fill(i.x - 1, i.y - 1, i.x + 2, i.y + 2, PIXEL_SOLID, FG_RED); 416 | 417 | sPoint2D car_p = racingLine.GetSplinePoint(fMarker); 418 | sPoint2D car_g = racingLine.GetSplineGradient(fMarker); 419 | DrawWireFrameModel(vecModelCar, car_p.x, car_p.y, atan2f(car_g.y, car_g.x), 4.0f, FG_BLACK); 420 | 421 | return true; 422 | } 423 | }; 424 | 425 | 426 | int main() 427 | { 428 | OneLoneCoder_RacingLine demo; 429 | demo.ConstructConsole(256, 240, 4, 4); 430 | demo.Start(); 431 | return 0; 432 | } -------------------------------------------------------------------------------- /OneLoneCoder_RetroArcadeRacer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Code-It-Yourself! Racing Game at the command prompt (quick and simple c++) 3 | "Let's go, go, go!!!" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | I'm a sim-racer when I'm not coding. Racing games are far more sophisticated than 34 | they used to be. Frankly, retro racing games are a bit naff. But when done in the 35 | command prompt they have a new level of craziness. 36 | 37 | Controls 38 | ~~~~~~~~ 39 | Left Arrow/Right Arrow Steer, Up Arrow accelerates. There are no brakes! 40 | Set the fastest lap times you can! 41 | 42 | Author 43 | ~~~~~~ 44 | Twitter: @javidx9 45 | Blog: www.onelonecoder.com 46 | 47 | Video: 48 | ~~~~~~ 49 | https://youtu.be/KkMZI5Jbf18 50 | 51 | Last Updated: 10/07/2017 52 | */ 53 | 54 | #include 55 | #include 56 | using namespace std; 57 | 58 | #include "olcConsoleGameEngine.h" 59 | 60 | 61 | class OneLoneCoder_FormulaOLC : public olcConsoleGameEngine 62 | { 63 | public: 64 | OneLoneCoder_FormulaOLC() 65 | { 66 | m_sAppName = L"Formula OLC"; 67 | } 68 | 69 | private: 70 | 71 | float fDistance = 0.0f; // Distance car has travelled around track 72 | float fCurvature = 0.0f; // Current track curvature, lerped between track sections 73 | float fTrackCurvature = 0.0f; // Accumulation of track curvature 74 | float fTrackDistance = 0.0f; // Total distance of track 75 | 76 | float fCarPos = 0.0f; // Current car position 77 | float fPlayerCurvature = 0.0f; // Accumulation of player curvature 78 | float fSpeed = 0.0f; // Current player speed 79 | 80 | vector> vecTrack; // Track sections, sharpness of bend, length of section 81 | 82 | list listLapTimes; // List of previous lap times 83 | float fCurrentLapTime; // Current lap time 84 | 85 | protected: 86 | // Called by olcConsoleGameEngine 87 | virtual bool OnUserCreate() 88 | { 89 | // Define track 90 | vecTrack.push_back(make_pair(0.0f, 10.0f)); // Short section for start/finish line 91 | vecTrack.push_back(make_pair(0.0f, 200.0f)); 92 | vecTrack.push_back(make_pair(1.0f, 200.0f)); 93 | vecTrack.push_back(make_pair(0.0f, 400.0f)); 94 | vecTrack.push_back(make_pair(-1.0f, 100.0f)); 95 | vecTrack.push_back(make_pair(0.0f, 200.0f)); 96 | vecTrack.push_back(make_pair(-1.0f, 200.0f)); 97 | vecTrack.push_back(make_pair(1.0f, 200.0f)); 98 | vecTrack.push_back(make_pair(0.0f, 200.0f)); 99 | vecTrack.push_back(make_pair(0.2f, 500.0f)); 100 | vecTrack.push_back(make_pair(0.0f, 200.0f)); 101 | 102 | // Calculate total track distance, so we can set lap times 103 | for (auto t : vecTrack) 104 | fTrackDistance += t.second; 105 | 106 | listLapTimes = { 0,0,0,0,0 }; 107 | fCurrentLapTime = 0.0f; 108 | 109 | return true; 110 | } 111 | 112 | 113 | 114 | 115 | // Called by olcConsoleGameEngine 116 | virtual bool OnUserUpdate(float fElapsedTime) 117 | { 118 | // Handle control input 119 | int nCarDirection = 0; 120 | 121 | if (m_keys[VK_UP].bHeld) 122 | fSpeed += 2.0f * fElapsedTime; 123 | else 124 | fSpeed -= 1.0f * fElapsedTime; 125 | 126 | // Car Curvature is accumulated left/right input, but inversely proportional to speed 127 | // i.e. it is harder to turn at high speed 128 | if (m_keys[VK_LEFT].bHeld) 129 | { 130 | fPlayerCurvature -= 0.7f * fElapsedTime * (1.0f - fSpeed / 2.0f); 131 | nCarDirection = -1; 132 | } 133 | 134 | if (m_keys[VK_RIGHT].bHeld) 135 | { 136 | fPlayerCurvature += 0.7f * fElapsedTime * (1.0f - fSpeed / 2.0f); 137 | nCarDirection = +1; 138 | } 139 | 140 | // If car curvature is too different to track curvature, slow down 141 | // as car has gone off track 142 | if (fabs(fPlayerCurvature - fTrackCurvature) >= 0.8f) 143 | fSpeed -= 5.0f * fElapsedTime; 144 | 145 | // Clamp Speed 146 | if (fSpeed < 0.0f) fSpeed = 0.0f; 147 | if (fSpeed > 1.0f) fSpeed = 1.0f; 148 | 149 | // Move car along track according to car speed 150 | fDistance += (70.0f * fSpeed) * fElapsedTime; 151 | 152 | // Get Point on track 153 | float fOffset = 0; 154 | int nTrackSection = 0; 155 | 156 | // Lap Timing and counting 157 | fCurrentLapTime += fElapsedTime; 158 | if (fDistance >= fTrackDistance) 159 | { 160 | fDistance -= fTrackDistance; 161 | listLapTimes.push_front(fCurrentLapTime); 162 | listLapTimes.pop_back(); 163 | fCurrentLapTime = 0.0f; 164 | } 165 | 166 | // Find position on track (could optimise) 167 | while (nTrackSection < vecTrack.size() && fOffset <= fDistance) 168 | { 169 | fOffset += vecTrack[nTrackSection].second; 170 | nTrackSection++; 171 | } 172 | 173 | // Interpolate towards target track curvature 174 | float fTargetCurvature = vecTrack[nTrackSection - 1].first; 175 | float fTrackCurveDiff = (fTargetCurvature - fCurvature) * fElapsedTime * fSpeed; 176 | 177 | // Accumulate player curvature 178 | fCurvature += fTrackCurveDiff; 179 | 180 | // Accumulate track curvature 181 | fTrackCurvature += (fCurvature) * fElapsedTime * fSpeed; 182 | 183 | // Draw Sky - light blue and dark blue 184 | for (int y = 0; y < ScreenHeight() / 2; y++) 185 | for (int x = 0; x < ScreenWidth(); x++) 186 | Draw(x, y, y< ScreenHeight() / 4 ? PIXEL_HALF : PIXEL_SOLID, FG_DARK_BLUE); 187 | 188 | // Draw Scenery - our hills are a rectified sine wave, where the phase is adjusted by the 189 | // accumulated track curvature 190 | for (int x = 0; x < ScreenWidth(); x++) 191 | { 192 | int nHillHeight = (int)(fabs(sinf(x * 0.01f + fTrackCurvature) * 16.0f)); 193 | for (int y = (ScreenHeight() / 2) - nHillHeight; y < ScreenHeight() / 2; y++) 194 | Draw(x, y, PIXEL_SOLID, FG_DARK_YELLOW); 195 | } 196 | 197 | 198 | // Draw Track - Each row is split into grass, clip-board and track 199 | for (int y = 0; y < ScreenHeight() / 2; y++) 200 | for (int x = 0; x < ScreenWidth(); x++) 201 | { 202 | // Perspective is used to modify the width of the track row segments 203 | float fPerspective = (float)y / (ScreenHeight()/2.0f); 204 | float fRoadWidth = 0.1f + fPerspective * 0.8f; // Min 10% Max 90% 205 | float fClipWidth = fRoadWidth * 0.15f; 206 | fRoadWidth *= 0.5f; // Halve it as track is symmetrical around center of track, but offset... 207 | 208 | // ...depending on where the middle point is, which is defined by the current 209 | // track curvature. 210 | float fMiddlePoint = 0.5f + fCurvature * powf((1.0f - fPerspective), 3); 211 | 212 | // Work out segment boundaries 213 | int nLeftGrass = (fMiddlePoint - fRoadWidth - fClipWidth) * ScreenWidth(); 214 | int nLeftClip = (fMiddlePoint - fRoadWidth) * ScreenWidth(); 215 | int nRightClip = (fMiddlePoint + fRoadWidth) * ScreenWidth(); 216 | int nRightGrass = (fMiddlePoint + fRoadWidth + fClipWidth) * ScreenWidth(); 217 | 218 | int nRow = ScreenHeight() / 2 + y; 219 | 220 | // Using periodic oscillatory functions to give lines, where the phase is controlled 221 | // by the distance around the track. These take some fine tuning to give the right "feel" 222 | int nGrassColour = sinf(20.0f * powf(1.0f - fPerspective,3) + fDistance * 0.1f) > 0.0f ? FG_GREEN : FG_DARK_GREEN; 223 | int nClipColour = sinf(80.0f * powf(1.0f - fPerspective, 2) + fDistance) > 0.0f ? FG_RED : FG_WHITE; 224 | 225 | // Start finish straight changes the road colour to inform the player lap is reset 226 | int nRoadColour = (nTrackSection-1) == 0 ? FG_WHITE : FG_GREY; 227 | 228 | // Draw the row segments 229 | if (x >= 0 && x < nLeftGrass) 230 | Draw(x, nRow, PIXEL_SOLID, nGrassColour); 231 | if (x >= nLeftGrass && x < nLeftClip) 232 | Draw(x, nRow, PIXEL_SOLID, nClipColour); 233 | if (x >= nLeftClip && x < nRightClip) 234 | Draw(x, nRow, PIXEL_SOLID, nRoadColour); 235 | if (x >= nRightClip && x < nRightGrass) 236 | Draw(x, nRow, PIXEL_SOLID, nClipColour); 237 | if (x >= nRightGrass && x < ScreenWidth()) 238 | Draw(x, nRow, PIXEL_SOLID, nGrassColour); 239 | 240 | } 241 | 242 | // Draw Car - car position on road is proportional to difference between 243 | // current accumulated track curvature, and current accumulated player curvature 244 | // i.e. if they are similar, the car will be in the middle of the track 245 | fCarPos = fPlayerCurvature - fTrackCurvature; 246 | int nCarPos = ScreenWidth() / 2 + ((int)(ScreenWidth() * fCarPos) / 2.0) - 7; // Offset for sprite 247 | 248 | // Draw a car that represents what the player is doing. Apologies for the quality 249 | // of the sprite... :-( 250 | switch (nCarDirection) 251 | { 252 | case 0: 253 | DrawStringAlpha(nCarPos, 80, L" ||####|| "); 254 | DrawStringAlpha(nCarPos, 81, L" ## "); 255 | DrawStringAlpha(nCarPos, 82, L" #### "); 256 | DrawStringAlpha(nCarPos, 83, L" #### "); 257 | DrawStringAlpha(nCarPos, 84, L"||| #### |||"); 258 | DrawStringAlpha(nCarPos, 85, L"|||########|||"); 259 | DrawStringAlpha(nCarPos, 86, L"||| #### |||"); 260 | break; 261 | 262 | case +1: 263 | DrawStringAlpha(nCarPos, 80, L" //####//"); 264 | DrawStringAlpha(nCarPos, 81, L" ## "); 265 | DrawStringAlpha(nCarPos, 82, L" #### "); 266 | DrawStringAlpha(nCarPos, 83, L" #### "); 267 | DrawStringAlpha(nCarPos, 84, L"/// ####//// "); 268 | DrawStringAlpha(nCarPos, 85, L"//#######///O "); 269 | DrawStringAlpha(nCarPos, 86, L"/// #### //// "); 270 | break; 271 | 272 | case -1: 273 | DrawStringAlpha(nCarPos, 80, L"\\\\####\\\\ "); 274 | DrawStringAlpha(nCarPos, 81, L" ## "); 275 | DrawStringAlpha(nCarPos, 82, L" #### "); 276 | DrawStringAlpha(nCarPos, 83, L" #### "); 277 | DrawStringAlpha(nCarPos, 84, L" \\\\\\\\#### \\\\\\"); 278 | DrawStringAlpha(nCarPos, 85, L" O\\\\\\#######\\\\"); 279 | DrawStringAlpha(nCarPos, 86, L" \\\\\\\\ #### \\\\\\"); 280 | break; 281 | } 282 | 283 | // Draw Stats 284 | DrawString(0, 0, L"Distance: " + to_wstring(fDistance)); 285 | DrawString(0, 1, L"Target Curvature: " + to_wstring(fCurvature)); 286 | DrawString(0, 2, L"Player Curvature: " + to_wstring(fPlayerCurvature)); 287 | DrawString(0, 3, L"Player Speed : " + to_wstring(fSpeed)); 288 | DrawString(0, 4, L"Track Curvature : " + to_wstring(fTrackCurvature)); 289 | 290 | auto disp_time = [](float t) // Little lambda to turn floating point seconds into minutes:seconds:millis string 291 | { 292 | int nMinutes = t / 60.0f; 293 | int nSeconds = t - (nMinutes * 60.0f); 294 | int nMilliSeconds = (t - (float)nSeconds) * 1000.0f; 295 | return to_wstring(nMinutes) + L"." + to_wstring(nSeconds) + L":" + to_wstring(nMilliSeconds); 296 | }; 297 | 298 | // Display current laptime 299 | DrawString(10, 8, disp_time(fCurrentLapTime)); 300 | 301 | // Display last 5 lap times 302 | int j = 10; 303 | for (auto l : listLapTimes) 304 | { 305 | DrawString(10, j, disp_time(l)); 306 | j++; 307 | } 308 | 309 | return true; 310 | } 311 | }; 312 | 313 | 314 | int main() 315 | { 316 | // Use olcConsoleGameEngine derived app 317 | OneLoneCoder_FormulaOLC game; 318 | game.ConstructConsole(160, 100, 8, 8); 319 | game.Start(); 320 | 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /OneLoneCoder_Snake.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Command Line Snake 3 | "Give me a break, I'm on holiday..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Classic Snake! Controls are Arrow keys Left & Right, eat food, grow larger, avoid self! 34 | 35 | 36 | Author 37 | ~~~~~~ 38 | Twitter: @javidx9 39 | Blog: www.onelonecoder.com 40 | 41 | Video: 42 | ~~~~~~ 43 | https://youtu.be/e8lYLYlrGLg 44 | 45 | Last Updated: 24/06/2017 46 | */ 47 | 48 | #include 49 | #include 50 | #include 51 | using namespace std; 52 | 53 | #include 54 | 55 | int nScreenWidth = 120; 56 | int nScreenHeight = 30; 57 | 58 | struct sSnakeSegment 59 | { 60 | int x; 61 | int y; 62 | }; 63 | 64 | int main() 65 | { 66 | // Create Screen Buffer 67 | wchar_t *screen = new wchar_t[nScreenWidth*nScreenHeight]; 68 | for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' '; 69 | HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 70 | SetConsoleActiveScreenBuffer(hConsole); 71 | DWORD dwBytesWritten = 0; 72 | 73 | while(1) 74 | { 75 | // Reset to known state 76 | list snake = { {60,15},{61,15},{62,15},{63,15},{64,15},{65,15},{66,15},{67,15},{68,15},{69,15} }; 77 | int nFoodX = 30; 78 | int nFoodY = 15; 79 | int nScore = 0; 80 | int nSnakeDirection = 3; 81 | bool bDead = false; 82 | bool bKeyLeft = false, bKeyRight = false, bKeyLeftOld = false, bKeyRightOld = false; 83 | 84 | while (!bDead) 85 | { 86 | // Frame Timing, compensate for aspect ratio of command line 87 | auto t1 = chrono::system_clock::now(); 88 | while ((chrono::system_clock::now() - t1) < ((nSnakeDirection % 2 == 1) ? 120ms : 200ms)) 89 | { 90 | // Get Input, 91 | bKeyRight = (0x8000 & GetAsyncKeyState((unsigned char)('\x27'))) != 0; 92 | bKeyLeft = (0x8000 & GetAsyncKeyState((unsigned char)('\x25'))) != 0; 93 | 94 | if (bKeyRight && !bKeyRightOld) 95 | { 96 | nSnakeDirection++; 97 | if (nSnakeDirection == 4) nSnakeDirection = 0; 98 | } 99 | 100 | if (bKeyLeft && !bKeyLeftOld) 101 | { 102 | nSnakeDirection--; 103 | if (nSnakeDirection == -1) nSnakeDirection = 3; 104 | } 105 | 106 | bKeyRightOld = bKeyRight; 107 | bKeyLeftOld = bKeyLeft; 108 | } 109 | 110 | // ==== Logic 111 | 112 | // Update Snake Position, place a new head at the front of the list in 113 | // the right direction 114 | switch (nSnakeDirection) 115 | { 116 | case 0: // UP 117 | snake.push_front({ snake.front().x, snake.front().y - 1 }); 118 | break; 119 | case 1: // RIGHT 120 | snake.push_front({ snake.front().x + 1, snake.front().y }); 121 | break; 122 | case 2: // DOWN 123 | snake.push_front({ snake.front().x, snake.front().y + 1 }); 124 | break; 125 | case 3: // LEFT 126 | snake.push_front({ snake.front().x - 1, snake.front().y }); 127 | break; 128 | } 129 | 130 | // Collision Detect Snake V Food 131 | if (snake.front().x == nFoodX && snake.front().y == nFoodY) 132 | { 133 | nScore++; 134 | while (screen[nFoodY * nScreenWidth + nFoodX] != L' ') 135 | { 136 | nFoodX = rand() % nScreenWidth; 137 | nFoodY = (rand() % (nScreenHeight-3))+3; 138 | } 139 | 140 | for (int i = 0; i < 5; i++) 141 | snake.push_back({ snake.back().x, snake.back().y }); 142 | } 143 | 144 | // Collision Detect Snake V World 145 | if (snake.front().x < 0 || snake.front().x >= nScreenWidth) 146 | bDead = true; 147 | if (snake.front().y < 3 || snake.front().y >= nScreenHeight) 148 | bDead = true; 149 | 150 | // Collision Detect Snake V Snake 151 | for (list::iterator i = snake.begin(); i != snake.end(); i++) 152 | if (i != snake.begin() && i->x == snake.front().x && i->y == snake.front().y) 153 | bDead = true; 154 | 155 | // Chop off Snakes tail :-/ 156 | snake.pop_back(); 157 | 158 | // ==== Presentation 159 | 160 | // Clear Screen 161 | for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' '; 162 | 163 | // Draw Stats & Border 164 | for (int i = 0; i < nScreenWidth; i++) 165 | { 166 | screen[i] = L'='; 167 | screen[2 * nScreenWidth + i] = L'='; 168 | } 169 | wsprintf(&screen[nScreenWidth + 5], L"www.OneLoneCoder.com - S N A K E ! ! SCORE: %d", nScore); 170 | 171 | 172 | // Draw Snake Body 173 | for (auto s : snake) 174 | screen[s.y * nScreenWidth + s.x] = bDead ? L'+' : L'O'; 175 | 176 | // Draw Snake Head 177 | screen[snake.front().y * nScreenWidth + snake.front().x] = bDead ? L'X' : L'@'; 178 | 179 | // Draw Food 180 | screen[nFoodY * nScreenWidth + nFoodX] = L'%'; 181 | 182 | if (bDead) 183 | wsprintf(&screen[15 * nScreenWidth + 40], L" PRESS 'SPACE' TO PLAY AGAIN "); 184 | 185 | // Display Frame 186 | WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten); 187 | } 188 | 189 | // Wait for space 190 | while ((0x8000 & GetAsyncKeyState((unsigned char)('\x20'))) == 0); 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /OneLoneCoder_Splines1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Splines Part 1 3 | "Bendy Wavy Curly" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Curvy things are always better. Splines are a nice way to approximate 34 | curves and loops for games. This video is the first of two parts 35 | demonstrating how Catmull-Rom splines can be implemented. 36 | 37 | Use Z + X to select a point and move it with the arrow keys 38 | Use A + S to move the agent around the spline loop 39 | 40 | Author 41 | ~~~~~~ 42 | Twitter: @javidx9 43 | Blog: www.onelonecoder.com 44 | 45 | Video: 46 | ~~~~~~ 47 | https://youtu.be/9_aJGUTePYo 48 | 49 | Last Updated: 06/08/2017 50 | */ 51 | #include 52 | #include 53 | using namespace std; 54 | 55 | #include "olcConsoleGameEngine.h" 56 | 57 | struct sPoint2D 58 | { 59 | float x; 60 | float y; 61 | }; 62 | 63 | struct sSpline 64 | { 65 | vector points; 66 | 67 | sPoint2D GetSplinePoint(float t, bool bLooped = false) 68 | { 69 | int p0, p1, p2, p3; 70 | if (!bLooped) 71 | { 72 | p1 = (int)t + 1; 73 | p2 = p1 + 1; 74 | p3 = p2 + 1; 75 | p0 = p1 - 1; 76 | } 77 | else 78 | { 79 | p1 = (int)t; 80 | p2 = (p1 + 1) % points.size(); 81 | p3 = (p2 + 1) % points.size(); 82 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 83 | } 84 | 85 | t = t - (int)t; 86 | 87 | float tt = t * t; 88 | float ttt = tt * t; 89 | 90 | float q1 = -ttt + 2.0f*tt - t; 91 | float q2 = 3.0f*ttt - 5.0f*tt + 2.0f; 92 | float q3 = -3.0f*ttt + 4.0f*tt + t; 93 | float q4 = ttt - tt; 94 | 95 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 96 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 97 | 98 | return{ tx, ty }; 99 | } 100 | 101 | sPoint2D GetSplineGradient(float t, bool bLooped = false) 102 | { 103 | int p0, p1, p2, p3; 104 | if (!bLooped) 105 | { 106 | p1 = (int)t + 1; 107 | p2 = p1 + 1; 108 | p3 = p2 + 1; 109 | p0 = p1 - 1; 110 | } 111 | else 112 | { 113 | p1 = (int)t; 114 | p2 = (p1 + 1) % points.size(); 115 | p3 = (p2 + 1) % points.size(); 116 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 117 | } 118 | 119 | t = t - (int)t; 120 | 121 | float tt = t * t; 122 | float ttt = tt * t; 123 | 124 | float q1 = -3.0f * tt + 4.0f*t - 1; 125 | float q2 = 9.0f*tt - 10.0f*t; 126 | float q3 = -9.0f*tt + 8.0f*t + 1.0f; 127 | float q4 = 3.0f*tt - 2.0f*t; 128 | 129 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 130 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 131 | 132 | return{ tx, ty }; 133 | } 134 | }; 135 | 136 | class OneLoneCoder_Splines : public olcConsoleGameEngine 137 | { 138 | public: 139 | OneLoneCoder_Splines() 140 | { 141 | m_sAppName = L"Splines"; 142 | } 143 | 144 | private: 145 | sSpline path; 146 | int nSelectedPoint = 0; 147 | float fMarker = 0.0f; 148 | 149 | protected: 150 | // Called by olcConsoleGameEngine 151 | virtual bool OnUserCreate() 152 | { 153 | //path.points = { { 10, 41 },{ 40, 41 },{ 70, 41 },{ 100, 41 } }; 154 | path.points = { { 10, 41 },{ 20, 41 },{ 30, 41 },{ 40, 41 },{ 50, 41 },{ 60, 41 },{ 70, 41 },{ 80, 41 },{ 90, 41 },{ 100, 41 } }; 155 | return true; 156 | } 157 | 158 | // Called by olcConsoleGameEngine 159 | virtual bool OnUserUpdate(float fElapsedTime) 160 | { 161 | // Clear Screen 162 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 163 | 164 | // Handle input 165 | if (m_keys[L'X'].bReleased) 166 | { 167 | nSelectedPoint++; 168 | if (nSelectedPoint >= path.points.size()) 169 | nSelectedPoint = 0; 170 | } 171 | 172 | if (m_keys[L'Z'].bReleased) 173 | { 174 | nSelectedPoint--; 175 | if (nSelectedPoint < 0) 176 | nSelectedPoint = path.points.size() - 1; 177 | } 178 | 179 | if (m_keys[VK_LEFT].bHeld) 180 | path.points[nSelectedPoint].x -= 30.0f * fElapsedTime; 181 | 182 | if (m_keys[VK_RIGHT].bHeld) 183 | path.points[nSelectedPoint].x += 30.0f * fElapsedTime; 184 | 185 | if (m_keys[VK_UP].bHeld) 186 | path.points[nSelectedPoint].y -= 30.0f * fElapsedTime; 187 | 188 | if (m_keys[VK_DOWN].bHeld) 189 | path.points[nSelectedPoint].y += 30.0f * fElapsedTime; 190 | 191 | if (m_keys[L'A'].bHeld) 192 | fMarker -= 5.0f * fElapsedTime; 193 | 194 | if (m_keys[L'S'].bHeld) 195 | fMarker += 5.0f * fElapsedTime; 196 | 197 | if (fMarker >= (float)path.points.size()) 198 | fMarker -= (float)path.points.size(); 199 | 200 | if (fMarker < 0.0f) 201 | fMarker += (float)path.points.size(); 202 | 203 | // Draw Spline 204 | for (float t = 0; t < (float)path.points.size(); t += 0.005f) 205 | { 206 | sPoint2D pos = path.GetSplinePoint(t, true); 207 | Draw(pos.x, pos.y); 208 | } 209 | 210 | // Draw Control Points 211 | for (int i = 0; i < path.points.size(); i++) 212 | { 213 | Fill(path.points[i].x - 1, path.points[i].y - 1, path.points[i].x + 2, path.points[i].y + 2, PIXEL_SOLID, FG_RED); 214 | DrawString(path.points[i].x, path.points[i].y, to_wstring(i)); 215 | } 216 | 217 | // Highlight control point 218 | Fill(path.points[nSelectedPoint].x - 1, path.points[nSelectedPoint].y - 1, path.points[nSelectedPoint].x + 2, path.points[nSelectedPoint].y + 2, PIXEL_SOLID, FG_YELLOW); 219 | DrawString(path.points[nSelectedPoint].x, path.points[nSelectedPoint].y, to_wstring(nSelectedPoint)); 220 | 221 | // Draw agent to demonstrate gradient 222 | sPoint2D p1 = path.GetSplinePoint(fMarker, true); 223 | sPoint2D g1 = path.GetSplineGradient(fMarker, true); 224 | float r = atan2(-g1.y, g1.x); 225 | DrawLine(5.0f * sin(r) + p1.x, 5.0f * cos(r) + p1.y, -5.0f * sin(r) + p1.x, -5.0f * cos(r) + p1.y, PIXEL_SOLID, FG_BLUE); 226 | return true; 227 | } 228 | }; 229 | 230 | int main() 231 | { 232 | OneLoneCoder_Splines demo; 233 | demo.ConstructConsole(160, 80, 10, 10); 234 | demo.Start(); 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /OneLoneCoder_Splines2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Splines Part 2 3 | "Bendier Wavier Curlier" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Curvy things are always better. Splines are a nice way to approximate 34 | curves and loops for games. This video is the first of two parts 35 | demonstrating how Catmull-Rom splines can be implemented. 36 | 37 | Use Z + X to select a point and move it with the arrow keys 38 | Use A + S to move the agent around the spline loop 39 | 40 | Author 41 | ~~~~~~ 42 | Twitter: @javidx9 43 | Blog: www.onelonecoder.com 44 | 45 | Video: 46 | ~~~~~~ 47 | https://youtu.be/9_aJGUTePYo 48 | https://youtu.be/DzjtU4WLYNs 49 | 50 | Last Updated: 25/09/2017 51 | */ 52 | #include 53 | #include 54 | using namespace std; 55 | 56 | #include "olcConsoleGameEngine.h" 57 | 58 | struct sPoint2D 59 | { 60 | float x; 61 | float y; 62 | float length; 63 | }; 64 | 65 | struct sSpline 66 | { 67 | vector points; 68 | float fTotalSplineLength = 0.0f; 69 | 70 | sPoint2D GetSplinePoint(float t, bool bLooped = false) 71 | { 72 | int p0, p1, p2, p3; 73 | if (!bLooped) 74 | { 75 | p1 = (int)t + 1; 76 | p2 = p1 + 1; 77 | p3 = p2 + 1; 78 | p0 = p1 - 1; 79 | } 80 | else 81 | { 82 | p1 = (int)t; 83 | p2 = (p1 + 1) % points.size(); 84 | p3 = (p2 + 1) % points.size(); 85 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 86 | } 87 | 88 | t = t - (int)t; 89 | 90 | float tt = t * t; 91 | float ttt = tt * t; 92 | 93 | float q1 = -ttt + 2.0f*tt - t; 94 | float q2 = 3.0f*ttt - 5.0f*tt + 2.0f; 95 | float q3 = -3.0f*ttt + 4.0f*tt + t; 96 | float q4 = ttt - tt; 97 | 98 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 99 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 100 | 101 | return{ tx, ty }; 102 | } 103 | 104 | sPoint2D GetSplineGradient(float t, bool bLooped = false) 105 | { 106 | int p0, p1, p2, p3; 107 | if (!bLooped) 108 | { 109 | p1 = (int)t + 1; 110 | p2 = p1 + 1; 111 | p3 = p2 + 1; 112 | p0 = p1 - 1; 113 | } 114 | else 115 | { 116 | p1 = (int)t; 117 | p2 = (p1 + 1) % points.size(); 118 | p3 = (p2 + 1) % points.size(); 119 | p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; 120 | } 121 | 122 | t = t - (int)t; 123 | 124 | float tt = t * t; 125 | float ttt = tt * t; 126 | 127 | float q1 = -3.0f * tt + 4.0f*t - 1; 128 | float q2 = 9.0f*tt - 10.0f*t; 129 | float q3 = -9.0f*tt + 8.0f*t + 1.0f; 130 | float q4 = 3.0f*tt - 2.0f*t; 131 | 132 | float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); 133 | float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); 134 | 135 | return{ tx, ty }; 136 | } 137 | 138 | float CalculateSegmentLength(int node, bool bLooped = false) 139 | { 140 | float fLength = 0.0f; 141 | float fStepSize = 0.005; 142 | 143 | sPoint2D old_point, new_point; 144 | old_point = GetSplinePoint((float)node, bLooped); 145 | 146 | for (float t = 0; t < 1.0f; t += fStepSize) 147 | { 148 | new_point = GetSplinePoint((float)node + t, bLooped); 149 | fLength += sqrtf((new_point.x - old_point.x)*(new_point.x - old_point.x) 150 | + (new_point.y - old_point.y)*(new_point.y - old_point.y)); 151 | old_point = new_point; 152 | } 153 | 154 | return fLength; 155 | } 156 | 157 | 158 | float GetNormalisedOffset(float p) 159 | { 160 | // Which node is the base? 161 | int i = 0; 162 | while (p > points[i].length) 163 | { 164 | p -= points[i].length; 165 | i++; 166 | } 167 | 168 | // The fractional is the offset 169 | return (float)i + (p / points[i].length); 170 | } 171 | 172 | }; 173 | 174 | class OneLoneCoder_Splines : public olcConsoleGameEngine 175 | { 176 | public: 177 | OneLoneCoder_Splines() 178 | { 179 | m_sAppName = L"Splines 2"; 180 | } 181 | 182 | private: 183 | sSpline path; 184 | int nSelectedPoint = 0; 185 | float fMarker = 0.0f; 186 | 187 | vector> vecModelCar; 188 | 189 | protected: 190 | // Called by olcConsoleGameEngine 191 | virtual bool OnUserCreate() 192 | { 193 | //path.points = { { 10, 41 },{ 40, 41 },{ 70, 41 },{ 100, 41 } }; 194 | //path.points = { { 10, 41 },{ 20, 41 },{ 30, 41 },{ 40, 41 },{ 50, 41 },{ 60, 41 },{ 70, 41 },{ 80, 41 },{ 90, 41 },{ 100, 41 } }; 195 | 196 | for (int i = 0; i < 10; i++) 197 | path.points.push_back({ 30.0f * sinf((float)i / 10.0f * 3.14159f * 2.0f) + ScreenWidth() / 2, 198 | 30.0f * cosf((float)i / 10.0f * 3.14159f * 2.0f) + ScreenHeight() / 2 }); 199 | 200 | vecModelCar = { { 1,1 },{ 1,3 },{ 3,0 },{ 0,-3 },{ -3,0 },{ -1, 3 },{ -1,1 } }; 201 | 202 | 203 | return true; 204 | } 205 | 206 | // Called by olcConsoleGameEngine 207 | virtual bool OnUserUpdate(float fElapsedTime) 208 | { 209 | // Clear Screen 210 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); 211 | 212 | // Handle input 213 | if (m_keys[L'X'].bReleased) 214 | { 215 | nSelectedPoint++; 216 | if (nSelectedPoint >= path.points.size()) 217 | nSelectedPoint = 0; 218 | } 219 | 220 | if (m_keys[L'Z'].bReleased) 221 | { 222 | nSelectedPoint--; 223 | if (nSelectedPoint < 0) 224 | nSelectedPoint = path.points.size() - 1; 225 | } 226 | 227 | if (m_keys[VK_LEFT].bHeld) 228 | path.points[nSelectedPoint].x -= 30.0f * fElapsedTime; 229 | 230 | if (m_keys[VK_RIGHT].bHeld) 231 | path.points[nSelectedPoint].x += 30.0f * fElapsedTime; 232 | 233 | if (m_keys[VK_UP].bHeld) 234 | path.points[nSelectedPoint].y -= 30.0f * fElapsedTime; 235 | 236 | if (m_keys[VK_DOWN].bHeld) 237 | path.points[nSelectedPoint].y += 30.0f * fElapsedTime; 238 | 239 | if (m_keys[L'A'].bHeld) 240 | fMarker -= 20.0f * fElapsedTime; 241 | 242 | if (m_keys[L'S'].bHeld) 243 | fMarker += 20.0f * fElapsedTime; 244 | 245 | if (fMarker >= (float)path.fTotalSplineLength) 246 | fMarker -= (float)path.fTotalSplineLength; 247 | 248 | if (fMarker < 0.0f) 249 | fMarker += (float)path.fTotalSplineLength; 250 | 251 | // Draw Spline 252 | for (float t = 0; t < (float)path.points.size(); t += 0.005f) 253 | { 254 | sPoint2D pos = path.GetSplinePoint(t, true); 255 | Draw(pos.x, pos.y); 256 | } 257 | 258 | path.fTotalSplineLength = 0.0f; 259 | 260 | // Draw Control Points 261 | for (int i = 0; i < path.points.size(); i++) 262 | { 263 | path.fTotalSplineLength += (path.points[i].length = path.CalculateSegmentLength(i, true)); 264 | Fill(path.points[i].x - 1, path.points[i].y - 1, path.points[i].x + 2, path.points[i].y + 2, PIXEL_SOLID, FG_RED); 265 | DrawString(path.points[i].x, path.points[i].y, to_wstring(i)); 266 | DrawString(path.points[i].x + 3, path.points[i].y, to_wstring(path.points[i].length)); 267 | } 268 | 269 | // Highlight control point 270 | Fill(path.points[nSelectedPoint].x - 1, path.points[nSelectedPoint].y - 1, path.points[nSelectedPoint].x + 2, path.points[nSelectedPoint].y + 2, PIXEL_SOLID, FG_YELLOW); 271 | DrawString(path.points[nSelectedPoint].x, path.points[nSelectedPoint].y, to_wstring(nSelectedPoint)); 272 | 273 | // Draw agent to demonstrate gradient 274 | float fOffset = path.GetNormalisedOffset(fMarker); 275 | sPoint2D p1 = path.GetSplinePoint(fOffset, true); 276 | sPoint2D g1 = path.GetSplineGradient(fOffset, true); 277 | float r = atan2(-g1.y, g1.x); 278 | DrawLine(5.0f * sin(r) + p1.x, 5.0f * cos(r) + p1.y, -5.0f * sin(r) + p1.x, -5.0f * cos(r) + p1.y, PIXEL_SOLID, FG_BLUE); 279 | 280 | DrawWireFrameModel(vecModelCar, p1.x, p1.y, -r + (3.14159f / 2.0f), 5.0f, FG_CYAN); 281 | 282 | 283 | DrawString(2, 2, to_wstring(fOffset)); 284 | DrawString(2, 4, to_wstring(fMarker)); 285 | 286 | return true; 287 | } 288 | }; 289 | 290 | int main() 291 | { 292 | OneLoneCoder_Splines demo; 293 | demo.ConstructConsole(160, 80, 10, 10); 294 | demo.Start(); 295 | return 0; 296 | } 297 | -------------------------------------------------------------------------------- /OneLoneCoder_SpriteEditor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Sprite Editor 3 | "Stop Crying about Paint ya big baby" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | Editing ASCII is not a simple as it should be, especially if you want to 34 | include all the weird characters 35 | 36 | Controls 37 | ~~~~~~~~ 38 | 39 | 40 | Author 41 | ~~~~~~ 42 | Twitter: @javidx9 43 | Blog: www.onelonecoder.com 44 | 45 | Video: 46 | ~~~~~~ 47 | Several... 48 | 49 | Last Updated: 50 | */ 51 | 52 | #include 53 | #include 54 | using namespace std; 55 | 56 | #include "olcConsoleGameEngine.h" 57 | 58 | 59 | 60 | 61 | 62 | class OneLoneCoder_SpriteEditor : public olcConsoleGameEngine 63 | { 64 | public: 65 | OneLoneCoder_SpriteEditor() 66 | { 67 | m_sAppName = L"Sprite Editor"; 68 | } 69 | 70 | private: 71 | int nPosX = 0; 72 | int nPosY = 0; 73 | int nOffsetX = 0; 74 | int nOffsetY = 0; 75 | int nZoom = 4; 76 | int nCurrentGlyph = PIXEL_SOLID; 77 | int nCurrentColourFG = FG_RED; 78 | int nCurrentColourBG = FG_BLACK; 79 | 80 | olcSprite *sprite = nullptr; 81 | wstring sCurrentSpriteFile; 82 | 83 | protected: 84 | // Called by olcConsoleGameEngine 85 | virtual bool OnUserCreate() 86 | { 87 | sprite = new olcSprite(8, 32); 88 | sCurrentSpriteFile = L"fps_fireball1.spr"; 89 | 90 | return true; 91 | } 92 | 93 | // Called by olcConsoleGameEngine 94 | virtual bool OnUserUpdate(float fElapsedTime) 95 | { 96 | 97 | // Zooming 98 | if (m_keys[VK_PRIOR].bReleased) 99 | nZoom <<= 1; 100 | 101 | if (m_keys[VK_NEXT].bReleased) 102 | nZoom >>= 1; 103 | 104 | if (nZoom > 32) nZoom = 32; 105 | if (nZoom < 2) nZoom = 2; 106 | 107 | // Brushes 108 | if (m_keys[VK_F1].bReleased) nCurrentGlyph = PIXEL_SOLID; 109 | if (m_keys[VK_F2].bReleased) nCurrentGlyph = PIXEL_THREEQUARTERS; 110 | if (m_keys[VK_F3].bReleased) nCurrentGlyph = PIXEL_HALF; 111 | if (m_keys[VK_F4].bReleased) nCurrentGlyph = PIXEL_QUARTER; 112 | 113 | // Colours 114 | for (int i = 0; i < 8; i++) 115 | if (m_keys[L"01234567"[i]].bReleased) 116 | if (m_keys[VK_SHIFT].bHeld) 117 | nCurrentColourFG = i + 8; 118 | else 119 | nCurrentColourFG = i; 120 | 121 | 122 | if (m_keys[VK_F7].bReleased) 123 | nCurrentColourBG--; 124 | 125 | if (m_keys[VK_F8].bReleased) 126 | nCurrentColourBG++; 127 | 128 | if (nCurrentColourBG < 0) nCurrentColourBG = 15; 129 | if (nCurrentColourBG > 15) nCurrentColourBG = 0; 130 | 131 | // Cursing :-) 132 | if (m_keys[VK_SHIFT].bHeld) 133 | { 134 | if (m_keys[VK_UP].bReleased) nOffsetY++; 135 | if (m_keys[VK_DOWN].bReleased) nOffsetY--; 136 | if (m_keys[VK_LEFT].bReleased) nOffsetX++; 137 | if (m_keys[VK_RIGHT].bReleased) nOffsetX--; 138 | } 139 | else 140 | { 141 | if (m_keys[VK_UP].bReleased) nPosY--; 142 | if (m_keys[VK_DOWN].bReleased) nPosY++; 143 | if (m_keys[VK_LEFT].bReleased) nPosX--; 144 | if (m_keys[VK_RIGHT].bReleased) nPosX++; 145 | } 146 | 147 | if (sprite != nullptr) 148 | { 149 | if (nPosX < 0) nPosX = 0; 150 | if (nPosX >= sprite->nWidth) nPosX = sprite->nWidth - 1; 151 | if (nPosY < 0) nPosY = 0; 152 | if (nPosY >= sprite->nHeight) nPosY = sprite->nHeight - 1; 153 | 154 | if (m_keys[VK_SPACE].bReleased) 155 | { 156 | sprite->SetGlyph(nPosX - 0, nPosY - 0, nCurrentGlyph); 157 | sprite->SetColour(nPosX - 0, nPosY - 0, nCurrentColourFG | (nCurrentColourBG << 4)); 158 | } 159 | 160 | if (m_keys[VK_DELETE].bReleased) 161 | { 162 | sprite->SetGlyph(nPosX - 0, nPosY - 0, L' '); 163 | sprite->SetColour(nPosX - 0, nPosY - 0, 0); 164 | } 165 | 166 | if (m_keys[VK_F9].bReleased) 167 | { 168 | sprite->Load(sCurrentSpriteFile); 169 | } 170 | 171 | if (m_keys[VK_F10].bReleased) 172 | { 173 | sprite->Save(sCurrentSpriteFile); 174 | } 175 | } 176 | 177 | 178 | 179 | 180 | 181 | // Erase All 182 | Fill(0, 0, ScreenWidth(), ScreenHeight(), L' ', 0); 183 | 184 | // Draw Menu 185 | DrawString(1, 1, L"F1 = 100% F2 = 75% F3 = 50% F4 = 25% F9 = Load File F10 = Save File"); 186 | for (int i = 0; i < 8; i++) 187 | { 188 | DrawString(1 + 6 * i, 3, to_wstring(i) + L" = "); 189 | if (m_keys[VK_SHIFT].bHeld) 190 | Draw(1 + 6 * i + 4, 3, PIXEL_SOLID, (i + 8)); 191 | else 192 | Draw(1 + 6 * i + 4, 3, PIXEL_SOLID, (i)); 193 | } 194 | 195 | DrawString(1, 5, L"Current Brush = "); 196 | Draw(18, 5, nCurrentGlyph, nCurrentColourFG | (nCurrentColourBG << 4)); 197 | 198 | // Draw Canvas 199 | for (int x = 9; x < 138; x++) 200 | { 201 | Draw(x, 9); 202 | Draw(x, 74); 203 | } 204 | 205 | for (int y = 9; y < 75; y++) 206 | { 207 | Draw(9, y); 208 | Draw(138, y); 209 | } 210 | 211 | // Draw Visible Sprite 212 | if (sprite != nullptr) 213 | { 214 | int nVisiblePixelsX = 128 / nZoom; 215 | int nVisiblePixelsY = 64 / nZoom; 216 | 217 | for (int x = 0; x < nVisiblePixelsX; x++) 218 | for (int y = 0; y < nVisiblePixelsY; y++) 219 | { 220 | if (x - nOffsetX < sprite->nWidth && y - nOffsetY < sprite->nHeight && x - nOffsetX >= 0 && y - nOffsetY >= 0) 221 | { 222 | // Draw Sprite 223 | Fill(x * nZoom + 10, y*nZoom + 10, (x + 1)*nZoom + 10, (y + 1)*nZoom + 10, sprite->GetGlyph(x - nOffsetX, y - nOffsetY), sprite->GetColour(x - nOffsetX, y - nOffsetY)); 224 | 225 | 226 | // Draw Pixel Markers 227 | if (sprite->GetGlyph(x - nOffsetX, y - nOffsetY) == L' ') 228 | Draw((x)* nZoom + 10, (y)* nZoom + 10, L'.'); 229 | } 230 | 231 | if (x - nOffsetX == nPosX && y - nOffsetY == nPosY) 232 | Draw((x)* nZoom + 10, (y)* nZoom + 10, L'O'); 233 | } 234 | } 235 | 236 | 237 | // Draw Actual Sprite 238 | for (int x = 0; x < sprite->nWidth; x++) 239 | for (int y = 0; y < sprite->nHeight; y++) 240 | Draw(x + 10, y + 80, sprite->GetGlyph(x, y), sprite->GetColour(x, y)); 241 | 242 | 243 | 244 | return true; 245 | } 246 | }; 247 | 248 | 249 | int main() 250 | { 251 | // Use olcConsoleGameEngine derived app 252 | OneLoneCoder_SpriteEditor game; 253 | game.ConstructConsole(160, 100, 8, 8); 254 | game.Start(); 255 | 256 | return 0; 257 | } 258 | -------------------------------------------------------------------------------- /OneLoneCoder_Tetris.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Command Line Tetris 3 | "Put Your Money Where Your Mouth Is" - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | Background 31 | ~~~~~~~~~~ 32 | I made a video "8-Bits of advice for new programmers" (https://youtu.be/vVRCJ52g5m4) 33 | and suggested that building a tetris clone instead of Dark Sould IV might be a better 34 | approach to learning to code. Tetris is nice as it makes you think about algorithms. 35 | 36 | Controls are Arrow keys Left, Right & Down. Use Z to rotate the piece. 37 | You score 25pts per tetronimo, and 2^(number of lines)*100 when you get lines. 38 | 39 | Future Modifications 40 | ~~~~~~~~~~~~~~~~~~~~ 41 | 1) Show next block and line counter 42 | 43 | Author 44 | ~~~~~~ 45 | Twitter: @javidx9 46 | Blog: www.onelonecoder.com 47 | 48 | Video: 49 | ~~~~~~ 50 | https://youtu.be/8OK8_tHeCIA 51 | 52 | Last Updated: 30/03/2017 53 | */ 54 | 55 | #include 56 | #include 57 | #include 58 | using namespace std; 59 | 60 | #include 61 | #include 62 | 63 | int nScreenWidth = 80; // Console Screen Size X (columns) 64 | int nScreenHeight = 30; // Console Screen Size Y (rows) 65 | wstring tetromino[7]; 66 | int nFieldWidth = 12; 67 | int nFieldHeight = 18; 68 | unsigned char *pField = nullptr; 69 | 70 | int Rotate(int px, int py, int r) 71 | { 72 | int pi = 0; 73 | switch (r % 4) 74 | { 75 | case 0: // 0 degrees // 0 1 2 3 76 | pi = py * 4 + px; // 4 5 6 7 77 | break; // 8 9 10 11 78 | //12 13 14 15 79 | 80 | case 1: // 90 degrees //12 8 4 0 81 | pi = 12 + py - (px * 4); //13 9 5 1 82 | break; //14 10 6 2 83 | //15 11 7 3 84 | 85 | case 2: // 180 degrees //15 14 13 12 86 | pi = 15 - (py * 4) - px; //11 10 9 8 87 | break; // 7 6 5 4 88 | // 3 2 1 0 89 | 90 | case 3: // 270 degrees // 3 7 11 15 91 | pi = 3 - py + (px * 4); // 2 6 10 14 92 | break; // 1 5 9 13 93 | } // 0 4 8 12 94 | 95 | return pi; 96 | } 97 | 98 | bool DoesPieceFit(int nTetromino, int nRotation, int nPosX, int nPosY) 99 | { 100 | // All Field cells >0 are occupied 101 | for (int px = 0; px < 4; px++) 102 | for (int py = 0; py < 4; py++) 103 | { 104 | // Get index into piece 105 | int pi = Rotate(px, py, nRotation); 106 | 107 | // Get index into field 108 | int fi = (nPosY + py) * nFieldWidth + (nPosX + px); 109 | 110 | // Check that test is in bounds. Note out of bounds does 111 | // not necessarily mean a fail, as the long vertical piece 112 | // can have cells that lie outside the boundary, so we'll 113 | // just ignore them 114 | if (nPosX + px >= 0 && nPosX + px < nFieldWidth) 115 | { 116 | if (nPosY + py >= 0 && nPosY + py < nFieldHeight) 117 | { 118 | // In Bounds so do collision check 119 | if (tetromino[nTetromino][pi] != L'.' && pField[fi] != 0) 120 | return false; // fail on first hit 121 | } 122 | } 123 | } 124 | 125 | return true; 126 | } 127 | 128 | int main() 129 | { 130 | // Create Screen Buffer 131 | wchar_t *screen = new wchar_t[nScreenWidth*nScreenHeight]; 132 | for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' '; 133 | HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 134 | SetConsoleActiveScreenBuffer(hConsole); 135 | DWORD dwBytesWritten = 0; 136 | 137 | tetromino[0].append(L"..X...X...X...X."); // Tetronimos 4x4 138 | tetromino[1].append(L"..X..XX...X....."); 139 | tetromino[2].append(L".....XX..XX....."); 140 | tetromino[3].append(L"..X..XX..X......"); 141 | tetromino[4].append(L".X...XX...X....."); 142 | tetromino[5].append(L".X...X...XX....."); 143 | tetromino[6].append(L"..X...X..XX....."); 144 | 145 | pField = new unsigned char[nFieldWidth*nFieldHeight]; // Create play field buffer 146 | for (int x = 0; x < nFieldWidth; x++) // Board Boundary 147 | for (int y = 0; y < nFieldHeight; y++) 148 | pField[y*nFieldWidth + x] = (x == 0 || x == nFieldWidth - 1 || y == nFieldHeight - 1) ? 9 : 0; 149 | 150 | // Game Logic 151 | bool bKey[4]; 152 | int nCurrentPiece = 0; 153 | int nCurrentRotation = 0; 154 | int nCurrentX = nFieldWidth / 2; 155 | int nCurrentY = 0; 156 | int nSpeed = 20; 157 | int nSpeedCount = 0; 158 | bool bForceDown = false; 159 | bool bRotateHold = true; 160 | int nPieceCount = 0; 161 | int nScore = 0; 162 | vector vLines; 163 | bool bGameOver = false; 164 | 165 | while (!bGameOver) // Main Loop 166 | { 167 | // Timing ======================= 168 | this_thread::sleep_for(50ms); // Small Step = 1 Game Tick 169 | nSpeedCount++; 170 | bForceDown = (nSpeedCount == nSpeed); 171 | 172 | // Input ======================== 173 | for (int k = 0; k < 4; k++) // R L D Z 174 | bKey[k] = (0x8000 & GetAsyncKeyState((unsigned char)("\x27\x25\x28Z"[k]))) != 0; 175 | 176 | // Game Logic =================== 177 | 178 | // Handle player movement 179 | nCurrentX += (bKey[0] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX + 1, nCurrentY)) ? 1 : 0; 180 | nCurrentX -= (bKey[1] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX - 1, nCurrentY)) ? 1 : 0; 181 | nCurrentY += (bKey[2] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1)) ? 1 : 0; 182 | 183 | // Rotate, but latch to stop wild spinning 184 | if (bKey[3]) 185 | { 186 | nCurrentRotation += (bRotateHold && DoesPieceFit(nCurrentPiece, nCurrentRotation + 1, nCurrentX, nCurrentY)) ? 1 : 0; 187 | bRotateHold = false; 188 | } 189 | else 190 | bRotateHold = true; 191 | 192 | // Force the piece down the playfield if it's time 193 | if (bForceDown) 194 | { 195 | // Update difficulty every 50 pieces 196 | nSpeedCount = 0; 197 | nPieceCount++; 198 | if (nPieceCount % 50 == 0) 199 | if (nSpeed >= 10) nSpeed--; 200 | 201 | // Test if piece can be moved down 202 | if (DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1)) 203 | nCurrentY++; // It can, so do it! 204 | else 205 | { 206 | // It can't! Lock the piece in place 207 | for (int px = 0; px < 4; px++) 208 | for (int py = 0; py < 4; py++) 209 | if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] != L'.') 210 | pField[(nCurrentY + py) * nFieldWidth + (nCurrentX + px)] = nCurrentPiece + 1; 211 | 212 | // Check for lines 213 | for (int py = 0; py < 4; py++) 214 | if(nCurrentY + py < nFieldHeight - 1) 215 | { 216 | bool bLine = true; 217 | for (int px = 1; px < nFieldWidth - 1; px++) 218 | bLine &= (pField[(nCurrentY + py) * nFieldWidth + px]) != 0; 219 | 220 | if (bLine) 221 | { 222 | // Remove Line, set to = 223 | for (int px = 1; px < nFieldWidth - 1; px++) 224 | pField[(nCurrentY + py) * nFieldWidth + px] = 8; 225 | vLines.push_back(nCurrentY + py); 226 | } 227 | } 228 | 229 | nScore += 25; 230 | if(!vLines.empty()) nScore += (1 << vLines.size()) * 100; 231 | 232 | // Pick New Piece 233 | nCurrentX = nFieldWidth / 2; 234 | nCurrentY = 0; 235 | nCurrentRotation = 0; 236 | nCurrentPiece = rand() % 7; 237 | 238 | // If piece does not fit straight away, game over! 239 | bGameOver = !DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY); 240 | } 241 | } 242 | 243 | // Display ====================== 244 | 245 | // Draw Field 246 | for (int x = 0; x < nFieldWidth; x++) 247 | for (int y = 0; y < nFieldHeight; y++) 248 | screen[(y + 2)*nScreenWidth + (x + 2)] = L" ABCDEFG=#"[pField[y*nFieldWidth + x]]; 249 | 250 | // Draw Current Piece 251 | for (int px = 0; px < 4; px++) 252 | for (int py = 0; py < 4; py++) 253 | if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] != L'.') 254 | screen[(nCurrentY + py + 2)*nScreenWidth + (nCurrentX + px + 2)] = nCurrentPiece + 65; 255 | 256 | // Draw Score 257 | swprintf_s(&screen[2 * nScreenWidth + nFieldWidth + 6], 16, L"SCORE: %8d", nScore); 258 | 259 | // Animate Line Completion 260 | if (!vLines.empty()) 261 | { 262 | // Display Frame (cheekily to draw lines) 263 | WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten); 264 | this_thread::sleep_for(400ms); // Delay a bit 265 | 266 | for (auto &v : vLines) 267 | for (int px = 1; px < nFieldWidth - 1; px++) 268 | { 269 | for (int py = v; py > 0; py--) 270 | pField[py * nFieldWidth + px] = pField[(py - 1) * nFieldWidth + px]; 271 | pField[px] = 0; 272 | } 273 | 274 | vLines.clear(); 275 | } 276 | 277 | // Display Frame 278 | WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten); 279 | } 280 | 281 | // Oh Dear 282 | CloseHandle(hConsole); 283 | cout << "Game Over!! Score:" << nScore << endl; 284 | system("pause"); 285 | return 0; 286 | } 287 | 288 | -------------------------------------------------------------------------------- /OneLoneCoder_Webcam.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | OneLoneCoder.com - Command Line Video 3 | "Yuck..." - @Javidx9 4 | 5 | License 6 | ~~~~~~~ 7 | Copyright (C) 2018 Javidx9 8 | This program comes with ABSOLUTELY NO WARRANTY. 9 | This is free software, and you are welcome to redistribute it 10 | under certain conditions; See license for details. 11 | Original works located at: 12 | https://www.github.com/onelonecoder 13 | https://www.onelonecoder.com 14 | https://www.youtube.com/javidx9 15 | 16 | GNU GPLv3 17 | https://github.com/OneLoneCoder/videos/blob/master/LICENSE 18 | 19 | From Javidx9 :) 20 | ~~~~~~~~~~~~~~~ 21 | Hello! Ultimately I don't care what you use this for. It's intended to be 22 | educational, and perhaps to the oddly minded - a little bit of fun. 23 | Please hack this, change it and use it in any way you see fit. You acknowledge 24 | that I am not responsible for anything bad that happens as a result of 25 | your actions. However this code is protected by GNU GPLv3, see the license in the 26 | github repo. This means you must attribute me if you use it. You can view this 27 | license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE 28 | Cheers! 29 | 30 | 31 | Background 32 | ~~~~~~~~~~ 33 | I was interested in trying to recreate video that is displayed using font 34 | characters at the command line. Mixing the colours is quite challenging 35 | given the constraints of the palette and characters. 36 | 37 | You'll need the ESCAPI libary binaries! 38 | https://github.com/jarikomppa/escapi 39 | 40 | 41 | Author 42 | ~~~~~~ 43 | Twitter: @javidx9 44 | Blog: www.onelonecoder.com 45 | 46 | Video: 47 | ~~~~~~ 48 | https://youtu.be/pk1Y_26j1Y4 49 | 50 | Last Updated: 02/10/2017 51 | */ 52 | 53 | #include 54 | #include 55 | #include 56 | using namespace std; 57 | 58 | #include "olcConsoleGameEngine.h" 59 | #include "escapi.h" 60 | 61 | class OneLoneCoder_Video : public olcConsoleGameEngine 62 | { 63 | public: 64 | OneLoneCoder_Video() 65 | { 66 | m_sAppName = L"Video"; 67 | } 68 | 69 | private: 70 | int nCameras = 0; 71 | SimpleCapParams capture; 72 | 73 | protected: 74 | virtual bool OnUserCreate() 75 | { 76 | nCameras = setupESCAPI(); 77 | 78 | if (nCameras == 0) 79 | return false; 80 | 81 | capture.mWidth = ScreenWidth(); 82 | capture.mHeight = ScreenHeight(); 83 | capture.mTargetBuf = new int[ScreenWidth() * ScreenHeight()]; 84 | 85 | if (initCapture(0, &capture) == 0) 86 | return false; 87 | 88 | return true; 89 | } 90 | 91 | void ClassifyPixel_Grey(float r, float g, float b, wchar_t &sym, short &fg_col, short &bg_col) 92 | { 93 | float luminance = 0.2987f * r + 0.5870f * g + 0.1140f * b; 94 | int pixel_bw = (int)(luminance * 13.0f); 95 | switch (pixel_bw) 96 | { 97 | case 0: bg_col = BG_BLACK; fg_col = FG_BLACK; sym = PIXEL_SOLID; break; 98 | 99 | case 1: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; 100 | case 2: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; 101 | case 3: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; 102 | case 4: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; 103 | 104 | case 5: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_QUARTER; break; 105 | case 6: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_HALF; break; 106 | case 7: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_THREEQUARTERS; break; 107 | case 8: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_SOLID; break; 108 | 109 | case 9: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; 110 | case 10: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; 111 | case 11: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; 112 | case 12: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; 113 | } 114 | } 115 | 116 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 117 | // Taken from https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both 118 | typedef struct 119 | { 120 | float r; // a fraction between 0 and 1 121 | float g; // a fraction between 0 and 1 122 | float b; // a fraction between 0 and 1 123 | } rgb; 124 | 125 | typedef struct 126 | { 127 | float h; // angle in degrees 128 | float s; // a fraction between 0 and 1 129 | float v; // a fraction between 0 and 1 130 | } hsv; 131 | 132 | hsv rgb2hsv(rgb in) 133 | { 134 | hsv out; 135 | float min, max, delta; 136 | 137 | min = in.r < in.g ? in.r : in.g; 138 | min = min < in.b ? min : in.b; 139 | 140 | max = in.r > in.g ? in.r : in.g; 141 | max = max > in.b ? max : in.b; 142 | 143 | out.v = max; // v 144 | delta = max - min; 145 | if (delta < 0.00001f) 146 | { 147 | out.s = 0; 148 | out.h = 0; // undefined, maybe nan? 149 | return out; 150 | } 151 | if (max > 0.0) { // NOTE: if Max is == 0, this divide would cause a crash 152 | out.s = (delta / max); // s 153 | } 154 | else { 155 | // if max is 0, then r = g = b = 0 156 | // s = 0, h is undefined 157 | out.s = 0.0; 158 | out.h = NAN; // its now undefined 159 | return out; 160 | } 161 | if (in.r >= max) // > is bogus, just keeps compilor happy 162 | out.h = (in.g - in.b) / delta; // between yellow & magenta 163 | else 164 | if (in.g >= max) 165 | out.h = 2.0 + (in.b - in.r) / delta; // between cyan & yellow 166 | else 167 | out.h = 4.0 + (in.r - in.g) / delta; // between magenta & cyan 168 | 169 | out.h *= 60.0; // degrees 170 | 171 | if (out.h < 0.0) 172 | out.h += 360.0; 173 | 174 | return out; 175 | } 176 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 177 | 178 | void ClassifyPixel_HSL(float r, float g, float b, wchar_t &sym, short &fg_col, short &bg_col) 179 | { 180 | hsv col = rgb2hsv({ r, g, b }); 181 | 182 | const struct { wchar_t c; short fg; short bg; } hues[] = 183 | { 184 | { PIXEL_SOLID, FG_RED | BG_RED }, 185 | { PIXEL_QUARTER, FG_YELLOW | BG_RED }, 186 | { PIXEL_HALF, FG_YELLOW | BG_RED }, 187 | { PIXEL_THREEQUARTERS, FG_YELLOW | BG_RED }, 188 | 189 | { PIXEL_SOLID, FG_GREEN | BG_YELLOW }, 190 | { PIXEL_QUARTER, FG_GREEN | BG_YELLOW }, 191 | { PIXEL_HALF, FG_GREEN | BG_YELLOW }, 192 | { PIXEL_THREEQUARTERS, FG_GREEN | BG_YELLOW }, 193 | 194 | { PIXEL_SOLID, FG_CYAN | BG_GREEN }, 195 | { PIXEL_QUARTER, FG_CYAN | BG_GREEN }, 196 | { PIXEL_HALF, FG_CYAN | BG_GREEN }, 197 | { PIXEL_THREEQUARTERS, FG_CYAN | BG_GREEN }, 198 | 199 | { PIXEL_SOLID, FG_BLUE | BG_CYAN }, 200 | { PIXEL_QUARTER, FG_BLUE | BG_CYAN }, 201 | { PIXEL_HALF, FG_BLUE | BG_CYAN }, 202 | { PIXEL_THREEQUARTERS, FG_BLUE | BG_CYAN }, 203 | 204 | { PIXEL_SOLID, FG_MAGENTA | BG_BLUE }, 205 | { PIXEL_QUARTER, FG_MAGENTA | BG_BLUE }, 206 | { PIXEL_HALF, FG_MAGENTA | BG_BLUE }, 207 | { PIXEL_THREEQUARTERS, FG_MAGENTA | BG_BLUE }, 208 | 209 | { PIXEL_SOLID, FG_RED | BG_MAGENTA }, 210 | { PIXEL_QUARTER, FG_RED | BG_MAGENTA }, 211 | { PIXEL_HALF, FG_RED | BG_MAGENTA }, 212 | { PIXEL_THREEQUARTERS, FG_RED | BG_MAGENTA }, 213 | 214 | }; 215 | 216 | int index = (int)((col.h / 360.0f) * 24.0f); 217 | 218 | if (col.s > 0.2f) 219 | { 220 | sym = hues[index].c; 221 | fg_col = hues[index].fg; 222 | bg_col = hues[index].bg; 223 | } 224 | else 225 | ClassifyPixel_Grey(r, g, b, sym, fg_col, bg_col); 226 | } 227 | 228 | void ClassifyPixel_OLC(float r, float g, float b, wchar_t &sym, short &fg_col, short &bg_col) 229 | { 230 | // Is pixel coloured (i.e. RGB values exhibit significant variance) 231 | float fMean = (r + g + b) / 3.0f; 232 | float fRVar = (r - fMean)*(r - fMean); 233 | float fGVar = (g - fMean)*(g - fMean); 234 | float fBVar = (b - fMean)*(b - fMean); 235 | float fVariance = fRVar + fGVar + fBVar; 236 | 237 | if (fVariance < 0.0001f) 238 | { 239 | ClassifyPixel_Grey(r, g, b, sym, fg_col, bg_col); 240 | } 241 | else 242 | { 243 | // Pixel has colour so get dominant colour 244 | float y = min(r, g);// (r * g); 245 | float c = min(g, b);// (g * b); 246 | float m = min(b, r);// (b * r); 247 | 248 | float fMean2 = (y + c + m) / 3.0f; 249 | float fYVar = (y - fMean2)*(y - fMean2); 250 | float fCVar = (c - fMean2)*(c - fMean2); 251 | float fMVar = (m - fMean2)*(m - fMean2); 252 | 253 | float fMaxPrimaryVar = max(fRVar, fGVar); 254 | fMaxPrimaryVar = max(fMaxPrimaryVar, fBVar); 255 | 256 | float fMaxSecondaryVar = max(fCVar, fYVar); 257 | fMaxSecondaryVar = max(fMaxSecondaryVar, fMVar); 258 | 259 | float fShading = 0.5f; 260 | 261 | auto compare = [&](float fV1, float fV2, float fC1, float fC2, short FG_LIGHT, short FG_DARK, short BG_LIGHT, short BG_DARK) 262 | { 263 | if (fV1 >= fV2) 264 | { 265 | // Primary Is Dominant, Use in foreground 266 | fg_col = fC1 > 0.5f ? FG_LIGHT : FG_DARK; 267 | 268 | if (fV2 < 0.0001f) 269 | { 270 | // Secondary is not variant, Use greyscale in background 271 | if (fC2 >= 0.00f && fC2 < 0.25f) bg_col = BG_BLACK; 272 | if (fC2 >= 0.25f && fC2 < 0.50f) bg_col = BG_DARK_GREY; 273 | if (fC2 >= 0.50f && fC2 < 0.75f) bg_col = BG_GREY; 274 | if (fC2 >= 0.75f && fC2 <= 1.00f) bg_col = BG_WHITE; 275 | } 276 | else 277 | { 278 | // Secondary is varient, Use in background 279 | bg_col = fC2 > 0.5f ? BG_LIGHT : BG_DARK; 280 | } 281 | 282 | // Shade dominant over background (100% -> 0%) 283 | fShading = ((fC1 - fC2) / 2.0f) + 0.5f; 284 | } 285 | 286 | if (fShading >= 0.00f && fShading < 0.20f) sym = L' '; 287 | if (fShading >= 0.20f && fShading < 0.40f) sym = PIXEL_QUARTER; 288 | if (fShading >= 0.40f && fShading < 0.60f) sym = PIXEL_HALF; 289 | if (fShading >= 0.60f && fShading < 0.80f) sym = PIXEL_THREEQUARTERS; 290 | if (fShading >= 0.80f) sym = PIXEL_SOLID; 291 | }; 292 | 293 | if (fRVar == fMaxPrimaryVar && fYVar == fMaxSecondaryVar) 294 | compare(fRVar, fYVar, r, y, FG_RED, FG_DARK_RED, BG_YELLOW, BG_DARK_YELLOW); 295 | 296 | if (fRVar == fMaxPrimaryVar && fMVar == fMaxSecondaryVar) 297 | compare(fRVar, fMVar, r, m, FG_RED, FG_DARK_RED, BG_MAGENTA, BG_DARK_MAGENTA); 298 | 299 | if (fRVar == fMaxPrimaryVar && fCVar == fMaxSecondaryVar) 300 | compare(fRVar, fCVar, r, c, FG_RED, FG_DARK_RED, BG_CYAN, BG_DARK_CYAN); 301 | 302 | if (fGVar == fMaxPrimaryVar && fYVar == fMaxSecondaryVar) 303 | compare(fGVar, fYVar, g, y, FG_GREEN, FG_DARK_GREEN, BG_YELLOW, BG_DARK_YELLOW); 304 | 305 | if (fGVar == fMaxPrimaryVar && fCVar == fMaxSecondaryVar) 306 | compare(fGVar, fCVar, g, c, FG_GREEN, FG_DARK_GREEN, BG_CYAN, BG_DARK_CYAN); 307 | 308 | if (fGVar == fMaxPrimaryVar && fMVar == fMaxSecondaryVar) 309 | compare(fGVar, fMVar, g, m, FG_GREEN, FG_DARK_GREEN, BG_MAGENTA, BG_DARK_MAGENTA); 310 | 311 | if (fBVar == fMaxPrimaryVar && fMVar == fMaxSecondaryVar) 312 | compare(fBVar, fMVar, b, m, FG_BLUE, FG_DARK_BLUE, BG_MAGENTA, BG_DARK_MAGENTA); 313 | 314 | if (fBVar == fMaxPrimaryVar && fCVar == fMaxSecondaryVar) 315 | compare(fBVar, fCVar, b, c, FG_BLUE, FG_DARK_BLUE, BG_CYAN, BG_DARK_CYAN); 316 | 317 | if (fBVar == fMaxPrimaryVar && fYVar == fMaxSecondaryVar) 318 | compare(fBVar, fYVar, b, y, FG_BLUE, FG_DARK_BLUE, BG_YELLOW, BG_DARK_YELLOW); 319 | } 320 | 321 | } 322 | 323 | 324 | 325 | virtual bool OnUserUpdate(float fElapsedTime) 326 | { 327 | // Capture webcam image 328 | doCapture(0); while (isCaptureDone(0) == 0) {} 329 | 330 | for (int x = 0; x < capture.mWidth; x++) 331 | for (int y = 0; y < capture.mHeight; y++) 332 | { 333 | // Get pixel 334 | union RGBint 335 | { 336 | int rgb; 337 | unsigned char c[4]; 338 | }; 339 | 340 | RGBint col; 341 | int id = y * capture.mWidth + x; 342 | 343 | col.rgb = capture.mTargetBuf[id]; 344 | int r = col.c[2]; 345 | int g = col.c[1]; 346 | int b = col.c[0]; 347 | 348 | float fR = (float)r / 255.0f; 349 | float fG = (float)g / 255.0f; 350 | float fB = (float)b / 255.0f; 351 | 352 | // Convert into character/colour combo 353 | wchar_t sym; 354 | short bg_col = 0; 355 | short fg_col = 0; 356 | 357 | //ClassifyPixel_Grey(fR, fG, fB, sym, fg_col, bg_col); 358 | //ClassifyPixel_HSL(fR, fG, fB, sym, fg_col, bg_col); 359 | ClassifyPixel_OLC(fR, fG, fB, sym, fg_col, bg_col); 360 | 361 | // Draw pixel 362 | Draw(x, y, sym, bg_col | fg_col); 363 | 364 | 365 | } 366 | 367 | 368 | return true; 369 | } 370 | 371 | }; 372 | 373 | int main() 374 | { 375 | OneLoneCoder_Video game; 376 | game.ConstructConsole(320, 240, 4, 4); 377 | game.Start(); 378 | return 0; 379 | } 380 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # videos 2 | OneLoneCoder One Off Programs 3 | 4 | Hello! 5 | 6 | This repo contains all of the source files to the videos on my youtube channel! 7 | 8 | www.youtube.com/javidx9 9 | 10 | Enjoy them as you see fit, but if you find the content useful, spread the word, give me a shout out, help me out! 11 | 12 | Jx9 13 | -------------------------------------------------------------------------------- /junk: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mariokart.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/mariokart.spr -------------------------------------------------------------------------------- /sky1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/sky1.spr -------------------------------------------------------------------------------- /worms/worms.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/worms/worms.spr -------------------------------------------------------------------------------- /worms/worms1.spr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneLoneCoderCommunity/olcConsoleGameEngine/375e33b5e090ab9f9e5314f29b8f32f39321d329/worms/worms1.spr --------------------------------------------------------------------------------