2D Game Engine

A feature-packed 2D game engine built on C++

                                        #include "Engine.h"


void initGameObjects() {
    //This function alone lets a user create a variety of platformer games by placing platforms, moving platforms, and interactable zones that the engine offers. 
    platforms.push_back(Platform(sf::Vector2f(0, 0), sf::Vector2f(50, 600), sf::Color(0, 255, 0), true));
    platforms.push_back(Platform(sf::Vector2f(50, 300), sf::Vector2f(650, 50), sf::Color(255, 107, 0), true));
    platforms.push_back(Platform(sf::Vector2f(650, 500), sf::Vector2f(350, 50), sf::Color(0, 255, 212), true));
    platforms.push_back(Platform(sf::Vector2f(1250, 150), sf::Vector2f(150, 50), sf::Color(36, 148, 90), true));
    platforms.push_back(Platform(sf::Vector2f(1900, 100), sf::Vector2f(50, 250), sf::Color(22, 0, 56), true));
    platforms.push_back(Platform(sf::Vector2f(1950, 300), sf::Vector2f(250, 50), sf::Color(78, 13, 180), true));
    platforms.push_back(Platform(sf::Vector2f(2200, 0), sf::Vector2f(50, 600), sf::Color(12, 76, 37), true));

    movingplatforms.push_back(MovingPlatform(sf::Vector2f(1050, 450), sf::Vector2f(150, 50), sf::Color(255, 0, 255), 1050, 150, 1050, 500, 1500, true));
    movingplatforms.push_back(MovingPlatform(sf::Vector2f(1450, 300), sf::Vector2f(150, 50), sf::Color(255, 0, 255), 1450, 300, 1700, 100, 1000, true));
    movingplatforms.push_back(MovingPlatform(sf::Vector2f(200, 400), sf::Vector2f(150, 50), sf::Color(255, 0, 255), 200, 400, 900, 100, 1000, true));

    //Make sure the first 3 zones are always in this order, all death zones at the end.
    zones.push_back(Zone(Type::SPAWN, sf::Vector2f(200, 100), sf::Vector2f(50, 50), sf::Color(255, 255, 0)));
    zones.push_back(Zone(Type::FINISH, sf::Vector2f(2050, 250), sf::Vector2f(50, 50), sf::Color(255, 165, 0)));
    zones.push_back(Zone(Type::SCROLLRIGHT, sf::Vector2f(400, 0), sf::Vector2f(50, 600), sf::Color(128, 0, 0)));
    zones.push_back(Zone(Type::DEATH, sf::Vector2f(50, 550), sf::Vector2f(2150, 500), sf::Color(255, 0, 0)));
}

void main() {

    initGameState();

    initGameObjects();

    std::thread connections(&NetworkServer::initGameObjects, &server, std::ref(clients), std::ref(client_count), Platform::serialized, MovingPlatform::serialized, Zone::serialized, std::ref(points));
    std::thread sendMPF(&NetworkServer::sendMPF, &server);
    std::thread sendClients(&NetworkServer::sendClients, &server, std::ref(clients));
    std::thread resetOffset(&NetworkServer::resetOffset, &server, std::ref(idToOffset));
    std::thread recvEventList(&NetworkServer::recvEventList, &server, std::ref(eventQueue));
    std::thread handleEvents(CustomEvent::handleEvents, std::ref(clients), std::ref(platforms), std::ref(movingplatforms), std::ref(eventQueue), std::ref(replays), std::ref(server.eventM), zones[0].box.getPosition().x, zones[0].box.getPosition().y, std::ref(points));

    while (true)
    {

        gt.updateTimes();
        server.m.lock();
        Character::updateALL(gt.gameDT, clients, platforms, movingplatforms, inFocus);
        server.m.unlock();
        Zone::updateALL(eventQueue, clients, zones, points, offset, idToOffset, server.eventM);
        MovingPlatform::updateALL(movingplatforms, gt.gameDT * 10);

        replayDT += gt.gameDT;
        if (replayDT > 0.033 / 4) {
            server.eventM.lock();
            ReplayHandler::writeALL(replays, clients, movingplatforms);
            server.eventM.unlock();
            replayDT -= 0.033 / 4;
        }

    }
}
                                    

This Game Engine that I worked on is the highlight of my academic career, bringing together majority of what I've learned through the last few years. My engine includes features that are often the core of any commercial engine, such as basic game objects, event management, and collision systems. This multithreaded game engine also features more such as scripting, recording and watching replays in-game, multiplayer support with a dedicated server and logically unrestrained player clients, and timeline manipulation. With the engine being a full-fledged project merging various sub-fields of computer science into one, the amount of experience I gained through this project is highly valuable.

I have linked 3 videos that showcase different games that I have built out of this engine, as well as a snippet of how efficient and detatched the server code is from the engine code. While the code isn't hosted publically anywhere yet, you can always contact me to hear more about this engine and its workings.

Snake 2.5D

A WebGL recreation of the classic Snake

                                        function setupShaders() {
    // define fragment shader in essl using es6 template strings
    var fShaderCode = `
    precision mediump float;
    uniform vec3 lightPos;
    uniform vec3 lightCol;
    uniform vec3 ambColor;
    uniform vec3 difColor;
    uniform vec3 spcColor;
    uniform vec3 eyePos;
    uniform float n;
    uniform float lightModel;
    varying vec3 normal;
    varying vec3 vPos;
    // varying vec3 fragColor;
    void main(void) {
        
            vec3 blinnPhongColor = ambColor * lightCol;
            blinnPhongColor += difColor * lightCol * (max(dot(normalize(normal),normalize((lightPos - vPos))),0.0));
            blinnPhongColor += spcColor * lightCol * ( pow(max (dot (  normalize((lightPos + (eyePos - vPos)  )), normalize(normal)  ),0.0),n ));
            gl_FragColor = vec4(blinnPhongColor, 1.0); // all fragments are white
        
    }
    `;
    // define vertex shader in essl using es6 template strings
    var vShaderCode = `
    attribute vec3 vertexPosition;
    varying vec3 vPos;
    attribute vec3 normalPosition;
    varying vec3 normal;
    uniform mat4 uModelMatrix; // the model matrix
    uniform mat4 uViewMatrix;
    uniform mat4 uProjMatrix;
    void main(void) {
        normal = normalPosition;
        vPos = vertexPosition;
        vec3 P = vec3(uModelMatrix * vec4(vertexPosition, 1.0));
        gl_Position = uViewMatrix * vec4(P,1.0);
        gl_Position = uProjMatrix * gl_Position;
    }
    `;
    try {
        var fShader = gl.createShader(gl.FRAGMENT_SHADER); // create frag shader
        gl.shaderSource(fShader, fShaderCode); // attach code to shader
        gl.compileShader(fShader); // compile the code for gpu execution
        var vShader = gl.createShader(gl.VERTEX_SHADER); // create vertex shader
        gl.shaderSource(vShader, vShaderCode); // attach code to shader
        gl.compileShader(vShader); // compile the code for gpu execution
        if (!gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) { // bad frag shader compile
            throw "error during fragment shader compile: " + gl.getShaderInfoLog(fShader);
            gl.deleteShader(fShader);
        } else if (!gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) { // bad vertex shader compile
            throw "error during vertex shader compile: " + gl.getShaderInfoLog(vShader);
            gl.deleteShader(vShader);
        } else { // no compile errors
            var shaderProgram = gl.createProgram(); // create the single shader program
            gl.attachShader(shaderProgram, fShader); // put frag shader in program
            gl.attachShader(shaderProgram, vShader); // put vertex shader in program
            gl.linkProgram(shaderProgram); // link program into gl context
            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { // bad program link
                throw "error during shader program linking: " + gl.getProgramInfoLog(shaderProgram);
            } else { // no shader program link errors
                gl.useProgram(shaderProgram); // activate shader program (frag and vert)
                vertexPositionAttrib = // get pointer to vertex shader input
                    gl.getAttribLocation(shaderProgram, "vertexPosition");
                normAttrib = // get pointer to vertex shader input
                    gl.getAttribLocation(shaderProgram, "normalPosition");
                vertexColorAttrib = // get pointer to vertex shader input
                    gl.getAttribLocation(shaderProgram, "vertexColor");
                modelMatrixULoc = gl.getUniformLocation(shaderProgram, "uModelMatrix"); // ptr to mmat
                viewMatrixULoc = gl.getUniformLocation(shaderProgram, "uViewMatrix");
                projMatrixULoc = gl.getUniformLocation(shaderProgram, "uProjMatrix");
                difColorULoc = gl.getUniformLocation(shaderProgram, "difColor");
                ambColorULoc = gl.getUniformLocation(shaderProgram, "ambColor");
                spcColorULoc = gl.getUniformLocation(shaderProgram, "spcColor");
                lightPosULoc = gl.getUniformLocation(shaderProgram, "lightPos");
                lightColULoc = gl.getUniformLocation(shaderProgram, "lightCol");
                eyeULoc = gl.getUniformLocation(shaderProgram, "eyePos");
                nULoc = gl.getUniformLocation(shaderProgram, "n");
                gl.enableVertexAttribArray(vertexPositionAttrib); // input to shader from array
                gl.enableVertexAttribArray(normAttrib);
            } // end if no shader program link errors
        } // end if no compile errors
    } // end try 
    catch (e) {
        console.log(e);
    } // end catch
} // end setup shaders
                                    

This recreation of the classic Snake is written entirely in WebGL. While this was also the starting point for my game logic skills, working with the shaders directly also let me understand how the hardware brings out the final outcome. Understanding how the buffers and shaders work have helped me write code better optimized for games in all my successive projects. Understanding the physics that go into lighting, reflection, shadows and color shading, and coding it from scratch let me see how to recreate real-world phenomenon in a virtual environment effectively.