basic_application.js

import { Color } from "./color.js";
import { Maths } from "../maths/math.js";
import { MouseInput } from "../events/mouseinput.js";
import { KeyboardInput } from "../events/keyboardinput.js";
import { TouchInput } from "../events/touchinput.js";
import { SceneManager } from "./sceneManager.js";
import { ScriptManager } from "../components/scripts/scriptManager.js";
import { DestroyQueue } from "./destroyQueue.js";

/**
 * @class Application
 * @description The main class of the engine. Inherit from this class to create your own game.
 * 
 * @property {number} width - The width of the canvas.
 * @property {number} height - The height of the canvas.
 * @property {number} fps - The fps of the game.
 * @property {Color} clearColor - The color to clear the canvas with.
 * @property {number} deltaTime - The time passed since the last frame.
 * 
 * @note The Application class is a singleton class. Hence use `application` to access the instance of the class.
 */

export class Application {
    #playBtn;
    #canvas;
    #ctx;
    #currentTimeStamp;
    #width;
    #height;
    #clearColor;
    #fps;
    #initialCanvasSize = { width: 0, height: 0 };

    /**
     *  @private
     */
    constructor() {
        // Setup the GamePage
        this.#pageSetup();
        document.getElementById("neptune-loading")?.remove();

        // Initialize the application
        this.#width = window.innerWidth;
        this.#height = window.innerHeight;
        this.#initialCanvasSize = { width: window.outerWidth, height: window.outerHeight };
        this.#fps = 60;
        this.#clearColor = Color.FromHex(0x334455);

        // Setup the canvas
        this.#canvas = document.getElementById("neptune-canvas");
        this.#canvas.setAttribute("height", window.innerHeight);
        this.#canvas.setAttribute("width", window.innerWidth);
        this.#canvas.style.display = "none";
        this.#canvas.focus();
        this.#ctx = this.#canvas.getContext("2d");

        // Setup the play button
        this.#playBtn = document.getElementById("neptune-play");
        this.#playBtn.style.display = "block";
        this.#playBtn.onclick = () => {
            this.#playBtn.remove();
            document.getElementById("neptune-gamepage")?.remove();

            this.#canvas.setAttribute("tabindex", "1");
            this.#canvas.style.display = "block";
            this.#canvas.focus();

            this.#width = window.innerWidth;
            this.#height = window.innerHeight;
            this.#initialCanvasSize = { width: window.outerWidth, height: window.outerHeight };
            Maths.generateMeterToPixelConversionFactor(this.#initialCanvasSize.width, this.#initialCanvasSize.height, this.#width, this.#height);

            this.#init();
            this.#gameloop(0);
        }

        this.#currentTimeStamp = performance.now();
        this._deltaTime = 0;
    }

    get width() {
        return this.#width;
    }

    get height() {
        return this.#height;
    }

    get fps() {
        return this.#fps;
    }

    set fps(value) {
        this.#fps = value;
    }

    get clearColor() {
        return this.#clearColor;
    }


    set clearColor(value) {
        this.#clearColor = value;
    }

    get deltaTime() {
        return this._deltaTime;
    }



    #init() {
        // Resize the canvas
        this.#canvas.setAttribute("height", window.innerHeight);
        this.#canvas.setAttribute("width", window.innerWidth);


        // Keep the canvas size constant
        window.onresize = () => {
            this.#width = window.innerWidth;
            this.#height = window.innerHeight;
            this.#canvas.width = this.#width;
            this.#canvas.height = this.#height;

            Maths.generateMeterToPixelConversionFactor(this.#initialCanvasSize.width, this.#initialCanvasSize.height, this.#width, this.#height);

        }
        this.#canvas.focus();

        // Initialize the input
        MouseInput.init(this.#canvas);
        KeyboardInput.init(this.#canvas);
        TouchInput.init(this.#canvas);

        // Initialize the managers
        SceneManager.init();
        ScriptManager.behaviourInit();

    }


    #draw(ctx) {
        SceneManager.draw(ctx);
    }

    #update(timeStamp) {
        ScriptManager.behaviourUpdate(timeStamp);
    }

    #gameloop(timeStamp) {
        // Calculate the time passed since the last frame
        this._deltaTime = (timeStamp - this.#currentTimeStamp) * this.#fps / 1000;  //in seconds
        this._deltaTime = Maths.Clamp(this._deltaTime, 0, 1);
        this.#currentTimeStamp = timeStamp;

        // Clear the canvas
        this.#ctx.clearRect(0, 0, this.#width, this.#ctx.height);
        this.#ctx.fillStyle = this.#clearColor.toString() || Color.darkgray;
        this.#ctx.fillRect(0, 0, this.#width, this.#height);

        // Update and draw the entities
        this.#update(this._deltaTime);
        this.#draw(this.#ctx);

        // Clear the input
        MouseInput.clear();
        KeyboardInput.clear();
        TouchInput.clear();

        // Destroy the entities
        DestroyQueue.destroy();

        // Call the next frame
        requestAnimationFrame(this.#gameloop.bind(this));
    }

    #pageSetup() {
        const playBtn = document.createElement("button");
        playBtn.setAttribute("type", "button");
        playBtn.setAttribute("id", "neptune-play");
        playBtn.textContent = "Play Game";
        document.body.appendChild(playBtn);

        const canvas = document.createElement("canvas");
        canvas.setAttribute("id", "neptune-canvas");
        document.body.appendChild(canvas);


        let style = document.createElement("style");
        style.innerHTML = `
body {
    margin: 0;
    padding: 0;
    overflow: hidden;
}
#neptune-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: #334455;
    color: white;
    border: none;
    padding: 20px;
    font-size: 20px;
    cursor: pointer;
    display: none;
}

#neptune-canvas {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
}
    `;
        document.head.appendChild(style);
    }

}