diff --git a/_posts/player_animation_collison/2025-01-07-PAC-Basics_IPYNB_2_.md b/_posts/player_animation_collison/2025-01-07-PAC-Basics_IPYNB_2_.md new file mode 100644 index 00000000..31170414 --- /dev/null +++ b/_posts/player_animation_collison/2025-01-07-PAC-Basics_IPYNB_2_.md @@ -0,0 +1,117 @@ +--- +layout: post +title: Player, Animation, and Collisons (PAC) +author: Aaryav Lal +description: How does PAC really work? +permalink: /player_animation_collison/PAC +toc: True +--- + +# Game Development Overview + +In game development, three core components often involved in creating interactive experiences are **Player**, **Animation**, and **Collision**. Understanding these components is crucial to building a functional game. Let's break them down: + +## 1. Player + +The **Player** is typically the entity that the user controls within the game. It can represent a character, vehicle, or object, and the player's actions often define the core gameplay. Key aspects include: + +- **Movement**: The player can be moved using input controls like keyboard, mouse, or game controllers. +- **Attributes**: Players often have specific attributes like health, speed, or strength. +- **Interaction**: The player interacts with the game world by collecting items, fighting enemies, or completing objectives. + +### Example: +```python +class Player: + def __init__(self, x: float, y: float, speed: float = 5.0): + """ + Initialize the player at position (x, y) with a default movement speed. + + Parameters: + x (float): The player's starting x-coordinate. + y (float): The player's starting y-coordinate. + speed (float): The player's movement speed. Defaults to 5. + """ + self.x = x + self.y = y + self.speed = speed + + def move(self, dx: float, dy: float): + """ + Move the player based on input direction (dx, dy) and speed. + + Parameters: + dx (float): The change in the x-direction. + dy (float): The change in the y-direction. + """ + self.x += dx * self.speed + self.y += dy * self.speed + + def get_position(self) -> tuple: + """ + Get the current position of the player as a tuple (x, y). + + Returns: + tuple: The player's current position. + """ + return self.x, self.y + + +## 2. Animation + +**Animation** is what brings the game world to life by providing movement or visual changes to the game’s elements. This involves changing the appearance or position of sprites over time to simulate movement or transitions. + +Key concepts in animation include: + +- **Frames**: Animation is typically composed of individual images called frames. When shown in sequence, they create the illusion of motion. +- **Frame Rate**: The speed at which frames are displayed, usually measured in frames per second (FPS). A higher FPS makes animations smoother. +- **Sprites**: These are 2D images or 3D models that are animated within the game. +- **Tweening**: Short for "in-betweening", this is the process of generating intermediate frames between two keyframes, providing a smooth transition. + +### Example: +```python +class Animation: + def __init__(self, frames, frame_rate): + self.frames = frames + self.frame_rate = frame_rate + self.current_frame = 0 + self.elapsed_time = 0 + + def update(self, delta_time): + self.elapsed_time += delta_time + if self.elapsed_time > 1 / self.frame_rate: + self.current_frame = (self.current_frame + 1) % len(self.frames) + self.elapsed_time = 0 + + +## 3. Collision + +**Collision** detection is crucial in games to determine when objects or characters interact with each other. It allows the game to respond to events like a player hitting an obstacle, picking up an item, or attacking an enemy. + +Key concepts in collision include: + +- **Bounding Box**: The simplest method of collision detection, where each object is enclosed in a rectangular or circular boundary. If these boundaries overlap, a collision is detected. +- **Pixel-Perfect Collision**: A more precise method of detection where the individual pixels of two objects are compared. This ensures more accurate detection but is computationally more expensive. +- **Collision Response**: Once a collision is detected, the game must decide how to respond, whether stopping movement, bouncing off surfaces, triggering damage, or generating game events. + +### Example: +```python +class Collision: + @staticmethod + def check_collision(rect1, rect2): + """ + Check for a collision between two rectangular objects. + + Parameters: + rect1: The first rectangle with attributes x, y, width, and height. + rect2: The second rectangle with attributes x, y, width, and height. + + Returns: + bool: True if the rectangles are colliding, False otherwise. + """ + return ( + rect1.x < rect2.x + rect2.width and + rect1.x + rect1.width > rect2.x and + rect1.y < rect2.y + rect2.height and + rect1.y + rect1.height > rect2.y + ) + diff --git a/_posts/player_animation_collison/2025-01-07-PAC1_IPYNB_2_.md b/_posts/player_animation_collison/2025-01-07-PAC1_IPYNB_2_.md new file mode 100644 index 00000000..33313431 --- /dev/null +++ b/_posts/player_animation_collison/2025-01-07-PAC1_IPYNB_2_.md @@ -0,0 +1,40 @@ +--- +layout: post +title: Player, Animation, and Collisons (PAC) +author: Aditya Srivastava +description: How does PAC really work? +permalink: /player_animation_collison/PAC +toc: True +--- + +# What is animation: +Animating is the process of creating the illusion of movement by displaying a sequendce of images or frames. This is important to our game and many other games because this is how our characters appear to be walking and jumping. Here is an example: + +![Image Description](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRWKlmwdjgWq7yCxuEGoBFMlyS1tYSbEzjw2Q&s) + + +# How Do We Animate in Our Game? + +In our game, all characters across levels use **sprite sheets** for animation. A **sprite sheet** is a large image containing a grid of smaller images (frames), each showing the character in a slightly different pose. These frames are displayed in rapid succession to create smooth animations for actions like walking, jumping, or attacking. + +Each frame represents a character from different angles or performing specific actions, with slight variations between frames to create the illusion of movement. + +Here's an example of what a sprite sheet might look like: + +- **Character Actions**: The sprite sheet contains frames for different actions (e.g., walking, jumping, attacking). +- **Animation Flow**: The frames are shown in sequence to make the animation appear seamless and natural. + +By cycling through the frames at the correct speed, we create fluid and continuous motion for the character in the game. + +### Example Sprite Sheet: +(You can add your image or a link to an example sprite sheet here) + +### How It Works: +1. **Loading the Sprite Sheet**: The game loads the sprite sheet, breaking it into individual frames. +2. **Animation Timing**: Each frame is displayed for a brief period before moving to the next one, based on the animation's frame rate. +3. **Smooth Transitions**: By repeating this process continuously, the character's animation looks smooth and lifelike. + +This technique helps bring our characters to life and gives the game its dynamic feel! + + +![Image Description](https://nighthawkcoders.github.io/portfolio_2025/images/platformer/sprites/lopezanimation.png) diff --git a/_posts/player_animation_collison/2025-01-07-PAC_IPYNB_2_.md b/_posts/player_animation_collison/2025-01-07-PAC_IPYNB_2_.md new file mode 100644 index 00000000..74d2714d --- /dev/null +++ b/_posts/player_animation_collison/2025-01-07-PAC_IPYNB_2_.md @@ -0,0 +1,9 @@ +--- +layout: post +title: Player, Animation, and Collisons (PAC) +author: Aneesh Devi +description: How does PAC really work? +permalink: /player_animation_collison/PAC +toc: True +--- + diff --git a/assets/js/platformer/GameEnv.js b/assets/js/platformer/GameEnv.js index 6b351262..4a24616e 100644 --- a/assets/js/platformer/GameEnv.js +++ b/assets/js/platformer/GameEnv.js @@ -235,12 +235,12 @@ export class GameEnv { } break; case "s": - if (keys.includes("a") && keys.includes("s")) { + if (key.includes("a") && key.includes("s")) { // If both "a" and "s" are clicked if (GameEnv.player?.x > 2) { GameEnv.backgroundDirection = -5; } - } else if (keys.includes("d") && keys.includes("s")) { + } else if (key.includes("d") && key.includes("s")) { // If both " d" and "s" are clicked if (GameEnv.player?.x < (GameEnv.innerWidth - 2)) { GameEnv.backgroundDirection = 5; diff --git a/assets/js/platformer/GameSetterSkibidi.js b/assets/js/platformer/GameSetterSkibidi.js index 15521de8..5e56edcf 100644 --- a/assets/js/platformer/GameSetterSkibidi.js +++ b/assets/js/platformer/GameSetterSkibidi.js @@ -44,7 +44,7 @@ const assets = { hitbox: { widthPercentage: 0.06, heightPercentage: 0.5}, width: 60, height: 500, - scaleSize: 100 + scaleSize: 90 }, cabin: { src: "/images/platformer/obstacles/cabin.png", @@ -491,20 +491,23 @@ const assets = { { name: 'laser', id: 'Laser', class: Laser, data: assets.obstacles.laser, xPercentage: 0.75, yPercentage: 0.5 }, { name: 'skibidiTitan', id: 'skibidiTitan', class: skibidiTitan, data: assets.enemies.skibidiTitan, xPercentage: 0.35, yPercentage: 0.5, minPosition: 0.5 }, { name: 'sand', id: 'platform', class: Platform, data: assets.platforms.sand }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.1, yPercentage: 0.9 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.3, yPercentage: 0.7 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.5, yPercentage: 0.8 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.7, yPercentage: 0.6 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.4, yPercentage: 0.4 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.2, yPercentage: 0.3 } , - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.6, yPercentage: 0.2 }, - { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.8, yPercentage: 0.5 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.3, yPercentage: 0.87 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.34, yPercentage: 0.8}, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.38, yPercentage: 0.8 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.42, yPercentage: 0.8 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.45, yPercentage: 0.75 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.6, yPercentage: 0.71 } , + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.14, yPercentage: 0.84 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.7, yPercentage: 0.84 }, { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.3, yPercentage: 0.4 }, - ///{ name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.475, yPercentage: 0.5 }, - { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.325, yPercentage: 0.7 }, - { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: -0.0125, yPercentage: 0.4 }, - { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.0125, yPercentage: 0.4 }, - { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.0325, yPercentage: 0.4 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.36, yPercentage: 0.8 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.4, yPercentage: 0.8 }, + { name: 'blocks', id: 'jumpPlatform', class: BlockPlatform, data: assets.platforms.sand, xPercentage: 0.32, yPercentage: 0.87 }, + //{ name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.475, yPercentage: 0.5 }, + { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.coin, xPercentage: 0.287, yPercentage: 0.3 }, + { name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.coin, xPercentage: 0.51, yPercentage: 0.9 }, + //{ name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.495, yPercentage: 0.87}, + //{ name: 'coin', id: 'coin', class: Coin, data: assets.obstacles.vbucks, xPercentage: 0.35, yPercentage: 0.88 }, { name: 'SkibidiToilet', id: 'SkibidiToilet', class: SkibidiToilet, data: assets.enemies.skibidiToilet, xPercentage: 0.3, minPosition: 0.07 }, { name: 'SkibidiToilet', id: 'SkibidiToilet', class: SkibidiToilet, data: assets.enemies.skibidiToilet, xPercentage: 0.5, minPosition: 0.3 }, { name: 'SkibidiToilet', id: 'SkibidiToilet', class: SkibidiToilet, data: assets.enemies.skibidiToilet, xPercentage: 0.75, minPosition: 0.5 }, diff --git a/assets/js/platformer/PlayerBase.js b/assets/js/platformer/PlayerBase.js index 9f20c7f4..2407cb95 100644 --- a/assets/js/platformer/PlayerBase.js +++ b/assets/js/platformer/PlayerBase.js @@ -1,6 +1,7 @@ import GameEnv from './GameEnv.js'; import Character from './Character.js'; import GameControl from './GameControl.js'; +import SettingsControl from './SettingsControl.js'; /** * @class PlayerBase class * @description PlayeiBase.js key objective is to handle the user-controlled player's actions and animations. @@ -52,7 +53,7 @@ export class PlayerBase extends Character { this.setY(this.y - (this.bottom * 0.35)); } updateMovement() { - const speedMultiplier = GameEnv.playerSpeedMultiplier || 1; // Default to 1 if not set + const speedMultiplier = SettingsControl.gameSpeed || 1; // Default to 1 if not set switch (this.state.animation) { case 'idle': break; diff --git a/assets/js/platformer/PlayerSkibidi.js b/assets/js/platformer/PlayerSkibidi.js index f10c2060..e6566cb6 100644 --- a/assets/js/platformer/PlayerSkibidi.js +++ b/assets/js/platformer/PlayerSkibidi.js @@ -138,26 +138,38 @@ export class PlayerSkibidi extends PlayerBaseOneD { /// Using PlayerBaseOneD add } break; - case "laser": // - if (this.collisionData.touchPoints.this.right || this.collisionData.touchPoints.this.left) { - if (GameEnv.difficulty === "normal" || GameEnv.difficulty === "hard") { - if (this.state.isDying == false) { - this.state.isDying = true; - this.canvas.style.transition = "transform 0.5s"; - this.canvas.style.transform = "rotate(-90deg) translate(-26px, 0%)"; - GameEnv.playSound("PlayerDeath"); - setTimeout(async() => { - await GameControl.transitionToLevel(GameEnv.levels[GameEnv.levels.indexOf(GameEnv.currentLevel)]); - }, 900); - } - } else if (GameEnv.difficulty === "easy" && this.collisionData.touchPoints.this.right) { - this.x -= 10; - } else if (GameEnv.difficulty === "easy" && this.collisionData.touchPoints.this.left) { - this.x += 10; - } - + case "laser": + // Adding a delay before the laser is spawned (3 seconds) + if (!this.spawnTime) { + this.spawnTime = Date.now(); // Record the spawn time when player spawns + } + + const timeSinceSpawn = Date.now() - this.spawnTime; // Calculate time since spawn + const delayBeforeLaser = 3000; // 3 seconds delay before spawning the laser + + // Only show and activate the laser after the delay + if (timeSinceSpawn >= delayBeforeLaser) { + if (this.collisionData.touchPoints.this.right || this.collisionData.touchPoints.this.left) { + if (GameEnv.difficulty === "normal" || GameEnv.difficulty === "hard") { + if (this.state.isDying == false) { + this.state.isDying = true; + this.canvas.style.transition = "transform 0.5s"; + this.canvas.style.transform = "rotate(-90deg) translate(-26px, 0%)"; + GameEnv.playSound("PlayerDeath"); + setTimeout(async() => { + await GameControl.transitionToLevel(GameEnv.levels[GameEnv.levels.indexOf(GameEnv.currentLevel)]); + }, 900); } - break; + } else if (GameEnv.difficulty === "easy" && this.collisionData.touchPoints.this.right) { + this.x -= 10; + } else if (GameEnv.difficulty === "easy" && this.collisionData.touchPoints.this.left) { + this.x += 10; + } + } + } + break; + + } } diff --git a/assets/js/platformer/SettingsControl.js b/assets/js/platformer/SettingsControl.js index 01208567..2fb3da02 100644 --- a/assets/js/platformer/SettingsControl.js +++ b/assets/js/platformer/SettingsControl.js @@ -65,623 +65,6 @@ const backgroundDim = { let isOpen = true -// define the SettingsControl class -// export class SettingsControl extends LocalStorage{ -// constructor(){ //default keys for localStorage -// var keys = { -// userID:"userID", -// currentLevel:"currentLevel", -// isInverted:"isInverted", -// gameSpeed:"gameSpeed", -// gravity:"gravity", -// difficulty: "difficulty", -// }; -// super(keys); //creates this.keys -// } - -// reloadGame() { -// // Add code to reload or restart your game here -// // You may want to perform actions like resetting the game state, restarting the level, etc. -// // Example: -// window.location.reload(); // Reload the entire page (this might not be suitable for all scenarios) -// // Alternatively, you may have a custom function to handle game restart logic. -// } - - -// /** -// * Note. Separated from constructor so that class can be created before levels are addeda -// * -// * Initializes the SettingsControl instance. -// * Loads all keys from local storage. -// * For each key, -// * * If it exists in local storage, loads and parses its value. -// * * Else when the key does not exist in local storage, sets key to the corresponding GameEnv.js variable. -// */ -// initialize(){ -// // Load all keys from local storage -// this.loadAll(); - -// window.addEventListener("difficulty", (e) => { -// // Update the difficulty value when a difficulty event is fired -// this[this.keys.difficulty] = e.detail.difficulty(); -// // Update the difficulty value in the game environment -// GameEnv.difficulty = parseFloat(this[this.keys.difficulty]); -// // Save the difficulty value to local storage -// this.save(this.keys.difficulty); - -// // Reload the game to apply the new difficulty settings -// this.reloadGame(); -// }); - -// /** -// * Handles a key by checking if it exists in local storage and parsing its value. -// * If the key does not exist in local storage, it sets the key to the current value of the game environment variable. -// * -// * @param {string} key - The localstorae key. -// * @param {*} gameEnvVariable - The corresponding game environment variable. -// * @param {function} [parser=(val) => val] - An optional function to parse the value from local storage. -// * If no parser parameter/function is provided, (val) => val is unchanged. -// * Else if parser is provided, the value is parsed ... e.g.: -// * * (val) => vall === "true" parses the value as a boolean -// * * (val) => parseFloat(val) parses the value as a floating point number -// */ -// const handleKey = (key, gameEnvVariable, parser = (val) => val) => { -// if (this[this.keys[key]]) { -// return parser(this[this.keys[key]]); -// } else { -// this[this.keys[key]] = gameEnvVariable; -// return gameEnvVariable; -// } -// }; - -// /* Call the handleKey function to set up each game environment variable -// * The handleKey function takes three parameters: -// * * key - the local storage key -// * * gameEnvVariable - the corresponding game environment variable -// * * parser - an optional function to parse the value extracted from local storage -// */ -// // 'userID', the value is parsed as a string -// GameEnv.userID = handleKey('userID', GameEnv.userID); -// // 'currentLevel', the value is parsed as a an index into the GameEnv.levels array -// GameEnv.currentLevel = handleKey('currentLevel', GameEnv.levels[Number(this[this.keys.currentLevel])]); -// // 'isInverted', the value is parsed to a boolean -// GameEnv.isInverted = handleKey('isInverted', GameEnv.isInverted, (val) => val === "true"); -// // 'gameSpeed', the value is parsed to a floating point number -// GameEnv.gameSpeed = handleKey('gameSpeed', GameEnv.gameSpeed, parseFloat); -// // 'gravity', the value is parsed to a floating point number -// GameEnv.gravity = handleKey('gravity', GameEnv.gravity, parseFloat); -// // 'difficulty', the value is parsed to a floating point number -// GameEnv.difficulty = handleKey('difficulty', GameEnv.difficulty); - - -// // List for th 'userID' update event -// window.addEventListener("userID", (e)=>{ -// // Update the userID value when a userID event is fired -// this[this.keys.userID] = e.detail.userID(); -// // Update the userID value in the game environment -// GameEnv.userID = this[this.keys.userID]; - -// Socket.sendData("name",GameEnv.userID); -// // Save the userID value to local storage -// this.save(this.keys.userID); -// }); - -// // Listen for the 'resize' update event -// window.addEventListener("resize",()=>{ -// // Update the current level index when the level changes -// this[this.keys.currentLevel] = GameEnv.levels.indexOf(GameEnv.currentLevel); -// // Save the current level index to local storage -// this.save(this.keys.currentLevel); -// }); - -// // Listen for the 'isInverted' update event -// window.addEventListener("isInverted", (e)=>{ -// // Update the isInverted value when an invert event is fired -// this[this.keys.isInverted] = e.detail.isInverted(); -// // Update the isInverted value in the game environment -// GameEnv.isInverted = this[this.keys.isInverted]; -// // Save the isInverted value to local storage -// this.save(this.keys.isInverted); -// }); - -// // Listen for the 'gameSpeed' update event -// window.addEventListener("gameSpeed",(e)=>{ -// // Update the gameSpeed value when a speed event is fired -// this[this.keys.gameSpeed] = e.detail.gameSpeed(); -// // Update the gameSpeed value in the game environment -// GameEnv.gameSpeed = parseFloat(this[this.keys.gameSpeed]); -// // Save the gameSpeed value to local storage -// this.save(this.keys.gameSpeed); -// }); - -// // Listen for the 'gravity' update event -// window.addEventListener("gravity",(e)=>{ -// // Update the gravity value when a gravity event is fired -// this[this.keys.gravity] = e.detail.gravity(); -// // Update the gravity value in the game environment -// GameEnv.gravity = parseFloat(this[this.keys.gravity]); -// // Save the gravity value to local storage -// this.save(this.keys.gravity); -// }); - -// // Listen for the 'gravity' update event -// window.addEventListener("difficulty",(e)=>{ -// // Update the gravity value when a gravity event is fired -// this[this.keys.difficulty] = e.detail.difficulty(); -// // Update the gravity value in the game environment -// GameEnv.difficulty = parseFloat(this[this.keys.difficulty]); -// // Save the gravity value to local storage -// this.save(this.keys.difficulty); -// }); - -// window.addEventListener("isTheme", (e)=>{ -// // Update the isInverted value when an invert event is fired -// this[this.keys.isTheme] = e.detail.isTheme(); -// // Update the isInverted value in the game environment -// GameEnv.isTheme = this[this.keys.isTheme]; -// // Save the isInverted value to local storage -// this.save(this.keys.isTheme); -// }); - -// } - -// /** -// * Getter for the userID property. -// * Creates a div with a text input for the user to enter a userID. -// * The input's value is bound to the GameEnv's userID string. -// * @returns {HTMLDivElement} The div containing the userID input. -// */ -// get userIDInput() { -// const div = document.createElement("div"); -// div.innerHTML = "User ID: "; // label - -// const userID = document.createElement("input"); // get user defined userID -// userID.type = "text"; -// userID.value = GameEnv.userID; // GameEnv contains latest userID -// userID.maxLength = 10; // set maximum length to 10 characters -// userID.className = "input userID"; // custom style in platformer-styles.scss - -// userID.addEventListener("change", () => { -// // dispatch event to update userID -// window.dispatchEvent(new CustomEvent("userID", { detail: {userID:()=>userID.value} })); -// }); - -// Socket.sendData("name",GameEnv.userID) - -// div.append(userID); // wrap input element in div -// return div; -// } - -// /** -// * Getter for the levelTable property. -// * Creates a table with a row for each game level. -// * Each row contains the level number and the level tag. -// * Passive levels are skipped and not added to the table. -// * @returns {HTMLTableElement} The table containing the game levels. -// */ -// get levelTable(){ -// // create table element -// var t = document.createElement("table"); -// t.className = "table levels"; -// //create table header -// var header = document.createElement("tr"); -// var th1 = document.createElement("th"); -// th1.innerText = "#"; -// header.append(th1); -// var th2 = document.createElement("th"); -// th2.innerText = "Level Tag"; -// header.append(th2); -// t.append(header); - -// // Create table rows/data -// for(let i = 0, count = 1; i < GameEnv.levels.length; i++){ -// if (GameEnv.levels[i].passive) //skip passive levels -// continue; -// // add level to table -// var row = document.createElement("tr"); -// var td1 = document.createElement("td"); -// td1.innerText = String(count++); //human counter -// row.append(td1); -// // place level name in button -// var td2 = document.createElement("td"); -// td2.innerText = GameEnv.levels[i].tag; -// row.append(td2); -// // listen for row click -// row.addEventListener("click",()=>{ // when player clicks on the row -// //transition to selected level -// GameControl.transitionToLevel(GameEnv.levels[i]); // resize event is triggered in transitionToLevel -// }) -// // add level row to table -// t.append(row); -// } - -// return t; //returns element -// } - -// /** -// * Getter for the isInvertedInput property. -// * Creates a div with a checkbox input for the user to invert the game controls. -// * The checkbox's checked state is bound to the GameEnv's isInverted state. -// * @returns {HTMLDivElement} The div containing the isInverted checkbox. -// */ -// get isInvertedInput() { -// const div = document.createElement("div"); -// div.innerHTML = "Invert: "; // label - -// const isInverted = document.createElement("input"); // get user defined invert boolean -// isInverted.type = "checkbox"; -// isInverted.checked = GameEnv.isInverted; // GameEnv contains latest isInverted state - -// isInverted.addEventListener("change", () => { -// //`dispatch event to update isInverted -// window.dispatchEvent(new CustomEvent("isInverted", { detail: {isInverted:()=>isInverted.checked} })); -// }); - -// div.append(isInverted); // wrap input element in div -// return div; -// } - -// get isThemeInput() { -// const localstorage = window.localStorage -// const lightmodekey = "islightMode" -// const div = document.createElement("div"); -// div.innerHTML = "Theme Change:"; // label -// const localStorageLightModeToggle = localstorage.getItem(lightmodekey) - -// if (localStorageLightModeToggle) { -// GameEnv.isLightMode = localStorageLightModeToggle.toLowerCase() === "true" -// } - - -// const islightMode = document.createElement("input"); // get user defined lightmode boolean -// islightMode.type = "checkbox"; -// if (GameEnv.isLightMode) { -// enableLightMode(); -// islightMode.checked = true; -// } else { -// enableDarkMode(); -// islightMode.checked = false; -// } -// islightMode.addEventListener('change', () => { -// if (islightMode.checked) { -// enableLightMode(); -// GameEnv.isLightMode = true; -// localstorage.setItem(lightmodekey, GameEnv.isLightMode) -// } else { -// enableDarkMode(); -// GameEnv.isLightMode = false; -// localstorage.setItem(lightmodekey, GameEnv.isLightMode) -// } -// console.log(GameEnv.isLightMode) -// }); - - -// // Append elements to the DOM or wherever appropriate -// div.appendChild(islightMode); -// return div -// // Append div to your settings container -// // For example: -// // document.getElementById('settingsContainer').appendChild(div); -// } - -// /** -// * Getter for the gameSpeedInput property. -// * Creates a div with a number input for the user to adjust the game speed. -// * The input's value is bound to the GameEnv's gameSpeed state. -// * @returns {HTMLDivElement} The div containing the gameSpeed input. -// */ - -// get gameSpeedInput() { -// const div = document.createElement("div"); -// div.innerHTML = "Game Speed: "; // label - -// const gameSpeed = document.createElement("input"); // get user defined game speed -// gameSpeed.type = "number"; -// gameSpeed.min = 1.0; -// gameSpeed.max = 8.0; -// gameSpeed.step = 0.1; -// gameSpeed.default = 2.0; // customed property for default value -// gameSpeed.value = GameEnv.gameSpeed; // GameEnv contains latest game speed -// gameSpeed.className = "input gameSpeed"; // custom style in platformer-styles.scss - -// gameSpeed.addEventListener("change", () => { -// // check values are within range -// const value = parseFloat(gameSpeed.value).toFixed(1); -// gameSpeed.value = (value < gameSpeed.min || value > gameSpeed.max || isNaN(value)) ? gameSpeed.default : value; -// // dispatch event to update game speed -// window.dispatchEvent(new CustomEvent("gameSpeed", { detail: {gameSpeed:()=>gameSpeed.value} })); -// }); - -// div.append(gameSpeed); // wrap input element in div -// return div; -// } - -// /** -// * Getter for the gravityInput property. -// * Creates a div with a number input for the user to adjust the game gravity. -// * The input's value is bound to the GameEnv's gravity state. -// * @returns {HTMLDivElement} The div containing the gravity input. -// */ -// get gravityInput() { -// const div = document.createElement("div"); -// div.innerHTML = "Gravity: "; // label - -// const gravity = document.createElement("input"); // get user defined gravity -// gravity.type = "number"; -// gravity.min = 1.0; -// gravity.max = 8.0; -// gravity.step = 0.1; -// gravity.default = 3.0; // customed property for default value -// gravity.value = GameEnv.gravity; // GameEnv contains latest gravity -// gravity.className = "input gravity"; // custom style in platformer-styles.scss - -// gravity.addEventListener("change", () => { -// // check values are within range -// const value = parseFloat(gravity.value).toFixed(1); -// gravity.value = (value < gravity.min || value > gravity.max || isNaN(value)) ? gravity.default : value; -// // dispatch event to update gravity -// window.dispatchEvent(new CustomEvent("gravity", { detail: {gravity:()=>gravity.value} })); -// }); - -// div.append(gravity); // wrap input element in div -// return div; -// } - - -// /** -// * Getter for the difficultyInput property. -// * Creates a div with a number input for the user to adjust the game difficulty. -// * @returns {HTMLDivElement} The div containing the difficultly input. -// */ -// get difficultyInput() { -// const div = document.createElement("div"); -// div.innerHTML = "Difficulty: "; // label - -// const difficulty = document.createElement("select"); // dropdown for difficulty -// const options = ["Easy", "Normal", "Hard", "Impossible"]; - -// options.forEach(option => { -// const opt = document.createElement("option"); -// opt.value = option.toLowerCase(); -// opt.text = option; -// difficulty.add(opt); -// }); - -// difficulty.value = GameEnv.difficulty; // GameEnv contains latest difficulty - -// difficulty.addEventListener("change", () => { -// // dispatch event to update difficulty -// window.dispatchEvent(new CustomEvent("difficulty", { detail: { difficulty: () => difficulty.value } })); -// }); - -// div.append(difficulty); // wrap select element in div -// return div; -// } - -// get multiplayerButton() { -// const div = document.createElement("div"); -// div.innerHTML = "Multiplayer: "; // label - -// const button = document.createElement("button"); // button for Multiplayer -// button.innerText = String(Socket.shouldBeSynced); - -// button.addEventListener("click", () => { -// // dispatch event to update difficulty -// button.innerText = String(Socket.changeStatus()); -// }); - -// div.append(button); // wrap button element in div -// return div; -// } - -// get chatButton() { -// const div = document.createElement("div"); -// div.innerHTML = "Chat: "; // label - -// const button = document.createElement("button"); // button for Multiplayer -// button.innerText = "open"; -// /** -// * Chat class to make the chat more refined and functional -// */ -// var ChatClass = new Chat([]); -// var chatBoxContainer = ChatClass.chatBoxContainer; -// var chatBox = chatBoxContainer.children.namedItem("chatBox"); -// var chatInput = chatBoxContainer.children.namedItem("chatInput"); -// var chatButton = chatBoxContainer.children.namedItem("chatButton"); -// chatBoxContainer.style.display = "none"; -// chatBoxContainer.style.zIndex = 2; -// chatBoxContainer.style.position = "absolute"; -// chatBoxContainer.style.top = "70%"; -// chatBoxContainer.style.left = "50%"; -// chatBoxContainer.style.width = "50%"; -// chatBoxContainer.style.height = "30%"; -// chatBoxContainer.style.backgroundColor = "grey"; -// chatBoxContainer.style.opacity = "65%"; -// chatBoxContainer.style.borderRadius = "1%"; -// chatBox.style.position = "relative"; -// chatBox.style.resize = "both"; -// chatBox.style.overflow = "auto"; -// chatBox.style.height = "90%"; -// chatBox.style.width = "100%"; -// chatBox.style.top = "0%"; -// chatInput.style.position = "relative"; -// chatInput.style.bottom = "0%"; -// chatInput.style.height = "10%" -// chatInput.style.width = "80%"; -// chatButton.style.position = "relative"; -// chatButton.style.height = "10%"; -// chatButton.style.width = "20%"; -// chatButton.style.bottom = "0%"; - - -// document.getElementById("sidebar").insertAdjacentElement("afterend",chatBoxContainer); - -// var isShown = false; -// button.addEventListener("click", () => { -// isShown=!isShown; -// if(isShown){ -// chatBoxContainer.style.display = "block"; -// button.innerText = "close"; -// }else{ -// chatBoxContainer.style.display = "none"; -// button.innerText = "open" -// } -// }); - -// div.append(button); // wrap button element in div -// return div; -// } - -// get playerCount(){ -// const div = document.createElement("div"); -// const text = document.createElement("p"); -// const button = document.createElement("button"); - -// text.innerText = "1/10 players"; -// button.innerText = "check player count"; - -// function update(d){ -// text.innerText = String(d)+"/10 players"; -// } -// Socket.createListener("playerCount",update); -// button.addEventListener("click",()=>{ -// Socket.removeAllListeners("playerCount") -// Socket.createListener("playerCount",update); -// Socket.socket.emit("checkPlayers",""); -// }); -// div.append(text); -// div.append(button); -// return div; -// } - -// /** -// * Static method to initialize the game settings controller and add the settings controls to the sidebar. -// * Constructs an HTML table/menu from GameEnv.levels[] and HTML inputs for invert, game speed, and gravity. -// * Each input has an event update associated with it. -// * All elements are appended to the sidebar. -// */ -// static sidebar(){ -// // Initiliaze Game settings controller -// var settingsControl = new SettingsControl(); -// settingsControl.initialize(); - -// // Get/Construct an HTML table/menu from GameEnv.levels[] -// var levels = settingsControl.levelTable; -// document.getElementById("sidebar").append(levels); - -// // Get/Construct HTML input and event update for invert -// var invertControl = settingsControl.isInvertedInput; -// document.getElementById("sidebar").append(invertControl); - -// var hintsSection = document.createElement("div") -// hintsSection.innerHTML = "Toggle fun facts: " -// // Store the updated toggle state in local storage -// // Create the hints button (checkbox) -// var hintsButton = document.createElement("input"); -// hintsButton.type = "checkbox"; - -// // Reference the hints section -// const hints = document.getElementsByClassName("fun_facts")[0]; - -// // Check localStorage for existing funFact state and set the initial state -// const localStorage = localStorage.getItem('funFact'); -// if (localStorageFunFact !== null) { -// GameEnv.funFact = localStorageFunFact.toLowerCase() === "true"; -// } else { -// // Default value if nothing is found in localStorage -// GameEnv.funFact = true; -// } - -// // Set the initial state of hints and the checkbox based on GameEnv.funFact -// if (GameEnv.funFact) { -// hints.style.display = "unset"; -// hintsButton.checked = true; -// } else { -// hints.style.display = "none"; -// hintsButton.checked = false; -// } - -// // Add the button to the DOM (assuming there is an element to append it to) -// document.body.appendChild(hintsButton); - -// // Add event listener to the button to update display and localStorage -// hintsButton.addEventListener("click", () => { -// if (!hintsButton.checked) { -// hints.style.display = "none"; -// GameEnv.funFact = false; -// } else { -// hints.style.display = "unset"; -// GameEnv.funFact = true; -// } -// localStorage.setItem('funFact', GameEnv.funFact); -// console.log(GameEnv.funFact); -// }); - - - -// hintsSection.append(hintsButton) -// document.getElementById("sidebar").append(hintsSection) - -// // Get/Construct HTML input and event update fo game speed -// var userID = settingsControl.userIDInput; -// document.getElementById("sidebar").append(userID); - -// // Get/Construct HTML input and event update for game speed -// var gameSpeed = settingsControl.gameSpeedInput; -// document.getElementById("sidebar").append(gameSpeed); - -// // Get/Construct HTML input and event update for gravity -// var gravityInput = settingsControl.gravityInput; -// document.getElementById("sidebar").append(gravityInput); - -// // Get/Construct HTML input and event update for difficulty -// var difficultyInput = settingsControl.difficultyInput; -// document.getElementById("sidebar").append(difficultyInput); - -// // Get/Construct HTML button and event update for multiplayer -// var multiplayerButton = settingsControl.multiplayerButton; -// document.getElementById("sidebar").append(multiplayerButton); - -// // Get/Construct HTML button and event update for theme -// var themeButton = settingsControl.themeButton; -// document.getElementById("sidebar").append(themeButton); - -// // Get/Construct HTML button and event update for multiplayer -// var chatButton = settingsControl.chatButton; -// document.getElementById("sidebar").append(chatButton); - -// // Get/Construct HTML button and event update for multiplayer -// var playerCount = settingsControl.playerCount; -// document.getElementById("sidebar").append(playerCount); - -// // Get/Construct HTML input and event update for theme change -// var themeChangeControl = settingsControl.isThemeInput; -// document.getElementById("sidebar").append(themeChangeControl); - -// // Listener, isOpen, and function for sidebar open and close -// var submenuHeight = 0; // calculated height of submenu -// function sidebarPanel(){ -// // toggle isOpen -// isOpen = true; -// // open and close properties for sidebar based on isOpen -// backgroundDim.create() -// var sidebar = document.querySelector('.sidebar'); -// sidebar.style.width = isOpen?"200px":"0px"; -// sidebar.style.paddingLeft = isOpen?"10px":"0px"; -// sidebar.style.paddingRight = isOpen?"10px":"0px"; -// sidebar.style.top = `calc(${submenuHeight}px + ${GameEnv.top}px)`; -// } -// // settings-button and event listener opens sidebar -// document.getElementById("settings-button").addEventListener("click",sidebarPanel); - -// window.addEventListener('load', function() { -// var submenu = document.querySelector('.submenu'); -// submenuHeight = submenu.offsetHeight; -// }); -// } - -// } - const SettingsControl = { initialize () { const sidebarDiv = document.getElementById("sidebar") @@ -844,19 +227,22 @@ const SettingsControl = { gameSpeed.min = 1.0; gameSpeed.max = 8.0; gameSpeed.step = 0.1; - gameSpeed.default = 2.0; // customed property for default value + gameSpeed.defaultValue = 2.0; // use defaultValue instead of custom property gameSpeed.value = GameEnv.gameSpeed; // GameEnv contains latest game speed gameSpeed.className = "input gameSpeed"; // custom style in platformer-styles.scss gameSpeed.addEventListener("change", () => { // check values are within range - const value = parseFloat(gameSpeed.value).toFixed(1); - gameSpeed.value = (value < gameSpeed.min || value > gameSpeed.max || isNaN(value)) ? gameSpeed.default : value; + let value = parseFloat(gameSpeed.value).toFixed(1); + value = (value < gameSpeed.min || value > gameSpeed.max || isNaN(value)) ? gameSpeed.defaultValue : value; + gameSpeed.value = value; + GameEnv.gameSpeed = value; // update GameEnv with new game speed // dispatch event to update game speed - window.dispatchEvent(new CustomEvent("gameSpeed", { detail: {gameSpeed:()=>gameSpeed.value} })); + window.dispatchEvent(new CustomEvent("gameSpeed", { detail: { gameSpeed: value } })); + updateCharacterSpeed(value); // call function to update character speed }); - div.append(gameSpeed); // wrap input element in div + div.appendChild(gameSpeed); return div; },