123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- /************************************************************************
- * GameHandler.js
- ************************************************************************
- * Copyright (c) 2021 Pedro Tonini Rosenberg Schneider.
- *
- * This file is part of Pandora.
- *
- * Pandora is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Pandora is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Pandora. If not, see <https://www.gnu.org/licenses/>.
- *************************************************************************/
- /**
- * This {@code GameHandler} singleton provides an interface for the user
- * to manipulate various parameters of the game, instance objects, and more.
- *
- * @author Pedro Schneider
- *
- * @namespace
- */
- const GameHandler = {
- nextId: 0, // ID to be given to the next object added to the tree.
- rootObjects: [], // List of objects on the root of the tree.
- renderMode: 1, // Can be RENDER_MODES.P2D or RENDER_MODES.WEBGL.
- bDrawDebugFPS: false, // Should fps be drawn (for debug only).
- debugFpsLabel: null, // Object that drwas fps.
- bDrawDebugBufferBounds: false, // Should the secondary buffer's bounds be drawn?
- prevMillis: 0, // Milliseconds ellapsed since the begining of the application.
- delta: 0, // Milliseconds ellapsed since the last frame.
- db: null, // Object to hold the secondary buffer.
- dbWidth: 1920, // Width of the secondary buffer.
- dbHeight: 1080, // Height of the secondary buffer.
- isMobile: null, // True if the device is a mobile device (tablet of phone).
- pixelDen: 1, // Pixel density for the canvas on destop devices.
- pixelDenMobile: 2, // Pixel denisty for the canvas on mobile devices.
- mouseX: 0, // X position of the mouse relative to the secondary buffer.
- mouseY: 0, // Y position of the mouse relative to the secondary buffer.
- pmouseX: 0, // X position of the mouse relative to the secondary buffer on the previous frame.
- pmouseY: 0, // Y position of the mouse relative to the secondary buffer on the previous frame.
- backgroundColor: null, // Default color to be drawn to the background.
- /**
- * Sets the initial game render mode.
- *
- * @param {RENDER_MODES} mode RENDER_MODES.P2D for default P5Js render or
- * RENDER_MODES.WEBGL for webgl (not recomended for mobile).
- */
- setRenderMode(mode)
- {
- this.renderMode = mode;
- },
- /**
- * Sets the width and height in pixels to initialize the secondary buffer.
- *
- * @param {number} w width in pixels to initialize the secondary buffer.
- * @param {number} h height in pixels to initialize the secondary buffer.
- */
- setDoubleBufferSize(w, h)
- {
- this.dbWidth = w;
- this.dbHeight = h;
- },
- /**
- * Sets the pixel density for the canvas to be initialized with on desktop
- * devices.
- *
- * @param {number} val pixel density for the canvas on desktop devices.
- */
- setPixelDensity(val)
- {
- this.pixelDen = val;
- },
- /**
- * Sets the pixel density for the canvas to be initialized with on desktop
- * devices.
- *
- * @param {number} val pixel density for the canvas on desktop devices.
- */
- setPixelDensityMobile(val)
- {
- this.pixelDenMobile = val;
- },
- /**
- * Sets the default color to be drawn to the main buffer's background.
- *
- * @param {Color} col new background color;
- */
- setBackgroundColor(col)
- {
- this.backgroundColor = col;
- },
- /**
- * Sets the flag to draw the debug fps.
- *
- * @param {boolean} val true if debug fps should be drawn, false if not.
- */
- drawDebugFPS(val)
- {
- this.bDrawDebugFPS = val;
- },
- /**
- * Sets the flag to draw secondary buffer bounds.
- *
- * @param {boolean} val true if debug secondary buffer bounds should be drawn, false if not.
- */
- drawDebugBufferBounds(val)
- {
- this.bDrawDebugBufferBounds = val;
- },
- /**
- * Initializes the game, creating the canvas, secondary buffer, and creates the
- * debug fps label if necessary.
- *
- * @param {number} fps target fps for the game (default if 60).
- */
- init(fps = 60)
- {
- // Sets the mobile flag.
- this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- // Creates the main canvas and the secondary buffer with the specified size and render mode.
- switch (this.renderMode)
- {
- case RENDER_MODES.P2D:
- createCanvas(windowWidth, windowHeight);
- this.db = createGraphics(this.dbWidth, this.dbHeight);
- break;
- case RENDER_MODES.WEBGL:
- createCanvas(windowWidth, windowHeight, WEBGL);
- this.db = createGraphics(this.dbWidth, this.dbHeight, WEBGL);
- this.db.smooth();
- break;
- }
- // Sets framerate and pixel density accordingly.
- frameRate(fps);
- if (this.isMobile)
- pixelDensity(this.pixelDenMobile);
- else
- pixelDensity(this.pixelDen);
- smooth();
- // Translates the canvas to the middle if render mode is webgl to maintain
- // consistency on the coordinate system.
- if (this.renderMode == RENDER_MODES.WEBGL)
- {
- translate(-windowWidth / 2, -windowHeight / 2);
- db.translate(-this.dbWidth / 2, -this.dbHeight / 2);
- }
- // Creates the debug fps label.
- if (this.bDrawDebugFPS)
- {
- this.debugFpsLabel = new Label("debugFps", `FPS: ${frameRate()}`);
- this.addRootObject(this.debugFpsLabel);
- }
- },
- /**
- * Instances a GameObject, meaning to give it an ID. This function is only called on the
- * constructor of GameObject, and probably shouldn't be used for anything else.
- *
- * @param {GameObject} obj GameObject to be instanced.
- */
- instanceGameObject(obj)
- {
- obj.id = this.nextId;
- this.nextId++;
- },
- /**
- * Adds a GameObject to the root of the tree. There should be as little root objects as possible.
- *
- * @param {GameObject} obj GameObject to be added as a root of the tree.
- */
- addRootObject(obj)
- {
- this.rootObjects.push(obj);
- obj.isRoot = true;
- obj.setup();
- },
- /**
- * Removes a GameObject from the root of the tree. This function is called automatically when a root object
- * is freed from memory, and probably shoudn't be used for anything else. DOES NOT DELETE THE OBJECT, ONLY
- * REMOVES IT FROM THE TREE.
- *
- * @param {number} id object id of the GameObject that should be removed from the tree.
- */
- removeRootObjectById(id)
- {
- for (let i = 0; i < this.rootObjects.length; i++)
- {
- if (this.rootObjects[i].id == id)
- this.rootObjects.splice(i, 1);
- }
- },
- upframecount: 0, // Frame count to be displayed.
- upframenum: 20, // Delay in frames to update the frame count.
- /**
- * Updates all of the GameObjects on the tree.
- */
- update()
- {
- // Updates the debug fps label if it existis.
- if (this.bDrawDebugFPS)
- {
- if (frameCount % this.upframenum == 0)
- {
- this.debugFpsLabel.setText(`FPS: ${
- Math.round(this.upframecount * 1000) / 1000
- }`);
- this.upframecount = 0;
- }
- else
- this.upframecount = max(this.upframecount, frameRate());
- }
- // Updates the delta.
- this.delta = (millis() - this.prevMillis) / 1000;
- // Update mouse position relative to the secondary buffer.
- this.pmouseX = this.mouseX;
- this.pmouseY = this.mouseY;
- let ar = this.db.screenWidth / this.db.width;
- let offsetx = (windowWidth - this.db.screenWidth) / 2;
- let offsety = (windowHeight - this.db.screenHeight) / 2;
- this.mouseX = (mouseX - offsetx) / ar;
- this.mouseY = (mouseY - offsety) / ar;
- // Updates all game objects on the tree.
- for (let i = 0; i < this.rootObjects.length; i++)
- this.rootObjects[i].update(this.delta);
- },
- /**
- * Draws all of the GameObjects on the tree.
- */
- draw()
- {
- // Clear the secondary buffer.
- this.db.clear();
- if (this.bDrawDebugBufferBounds)
- {
- // Draw a rectangle to visualize the secondary buffer.
- this.db.push();
- this.db.strokeWeight(5);
- this.db.noFill();
- this.db.rect(0, 0, this.dbWidth, this.dbHeight);
- this.db.pop();
- }
- // Centers the image and calculates the dimensions of the secondary
- // buffer to best fit the size of the window.
- imageMode(CENTER);
- if (windowWidth / windowHeight < this.dbWidth / this.dbHeight)
- {
- this.db.screenWidth = windowWidth;
- this.db.screenHeight = windowWidth * (this.dbHeight / this.dbWidth);
- }
- else
- {
- this.db.screenHeight = windowHeight;
- this.db.screenWidth = windowHeight * (this.dbWidth / this.dbHeight);
- }
- this.db.ellipse(this.pmouseX, this.pmouseY, 20);
- this.db.line(this.pmouseX, this.pmouseY, this.mouseX, this.mouseY);
- // Draw all game objects.
- for (let i = 0; i < this.rootObjects.length; i++)
- this.rootObjects[i].draw(this.delta, this.db);
- // Draws the secondary buffer to the main canvas.
- image(this.db, windowWidth / 2, windowHeight / 2, this.db.screenWidth, this.db.screenHeight);
- // Updates the delta
- this.prevMillis = millis();
- },
- /**
- * ! This function should be overriden, it provides no default functionality.
- * This function is called once when the page loads, and should be used by the user to load
- * assets and other forms of data that need to be already loaded when the prorgam starts.
- *
- * @callback
- */
- _preload()
- {
- },
- /**
- * ! This function should be overriden, it provides no default functionality.
- * This function is called once when the program starts, and should be used by the user to
- * initialize any necessary aspects of the game.
- *
- * @callback
- */
- _setup()
- {
- },
- }
- /**
- * This function is called once when the page loads. Serves to load assets and other
- * data that needs to be loaded when the program starts.
- *
- * @callback
- */
- function preload()
- {
- GameHandler._preload();
- }
- /**
- * This function is called once at the start of the program. Serves to initialize several
- * aspects of the game.
- *
- * @callback
- */
- function setup()
- {
- GameHandler._setup();
- GameHandler.init();
- }
- /**
- * This function is called once every frame. Serves to update and draw all GameObjects.
- *
- * @callback
- */
- function draw()
- {
- if (GameHandler.backgroundColor)
- background(GameHandler.backgroundColor.getP5Color());
- else
- background(200);
- GameHandler.update();
- GameHandler.draw();
- }
- /**
- * This function is called once every time the browser window is resized. Here, its used to make the game
- * always ocupy the entire browser window.
- *
- * @callback
- */
- function windowResized()
- {
- resizeCanvas(windowWidth, windowHeight);
- }
|