diff --git a/.idea/2023.iml b/.idea/2023.iml index 24643cc37449b4bde54411a80b8ed61258225e34..d3f1fdaa54b82960b84dd01f99717e44688747a0 100644 --- a/.idea/2023.iml +++ b/.idea/2023.iml @@ -5,6 +5,7 @@ <excludeFolder url="file://$MODULE_DIR$/.tmp" /> <excludeFolder url="file://$MODULE_DIR$/temp" /> <excludeFolder url="file://$MODULE_DIR$/tmp" /> + <excludeFolder url="file://$MODULE_DIR$/dist" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/assets/ball.ase b/assets/ball.ase new file mode 100644 index 0000000000000000000000000000000000000000..066d42155ef8a13fb79c430e3f8ec8e63b2bfa4b Binary files /dev/null and b/assets/ball.ase differ diff --git a/assets/catapult.ase b/assets/catapult.ase new file mode 100644 index 0000000000000000000000000000000000000000..a8c382e32a94596922eefc37340c7bbbc4dfcec2 Binary files /dev/null and b/assets/catapult.ase differ diff --git a/src/img/ball.webp b/src/img/ball.webp new file mode 100644 index 0000000000000000000000000000000000000000..49d38764d87b47fd9a1391fb90d4ba8b70622c54 Binary files /dev/null and b/src/img/ball.webp differ diff --git a/src/img/catapult.webp b/src/img/catapult.webp new file mode 100644 index 0000000000000000000000000000000000000000..12ba75241a220c23e3bc79221a45d51fd70bff06 Binary files /dev/null and b/src/img/catapult.webp differ diff --git a/src/index.html b/src/index.html index 6cb5043ddfa1715fcc6329c04e16bdc12059329d..12f261209c2f2ea40590f7d6f04396b682fd518b 100644 --- a/src/index.html +++ b/src/index.html @@ -1,4 +1,4 @@ -<!-- maybe slightly copied from herebefrog's --> +<!-- maybe slightly copied from herebefrog --> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> <style> diff --git a/src/js/canvas.js b/src/js/canvas.js index 2cc5c389fdbc8bf9058124ae432eecc599cbbb99..2b3eb0d1f651ed825bceef381dcf6e7f5ae768ec 100644 --- a/src/js/canvas.js +++ b/src/js/canvas.js @@ -18,6 +18,13 @@ class Canvas { this.cY = 0; } + setDimensions (w, h) { + this.canvas.width = w; + this.canvas.height = h; + this.width = this.canvas.width; + this.height = this.canvas.height; + } + fill(c="black") { this.ctx.fillStyle = c; this.ctx.fillRect(0, 0, this.width, this.height); diff --git a/src/js/game.js b/src/js/game.js index e93272ed718b266687bd5d987ff45dca3004033b..9031c014d9ea9a982b9c0f3c160fba140ac7ad96 100644 --- a/src/js/game.js +++ b/src/js/game.js @@ -3,14 +3,20 @@ import { WIDTH, HEIGHT, GAME_TITLE } from "./config.js"; import { Canvas } from "./canvas.js"; import { TextRenderer } from "./text.js"; import { Room, Object } from "./objects.js"; -import { isKeyUp, whichKeyDown } from "./keyboard.js"; import { getParameter } from "./utils.js"; +import { getMousePos } from "./inputs/mouse.js"; +import { isKeyUp, whichKeyDown } from "./inputs/keyboard.js"; + + +let imgPrefix = "../img/"; let assets = { images: { - splash: "../img/splash1.webp", - font: "../img/hampsterfont.webp", - selector: "../img/selector.webp", + splash: "splash1.webp", + font: "hampsterfont.webp", + selector: "selector.webp", + catapult: "catapult.webp", + debug_ball: "ball.webp", }, } @@ -33,11 +39,11 @@ let pressedLastFrame = []; canvas.fill("#222034"); let splash = new Image(); -splash.src = assets.images.splash; +splash.src = imgPrefix + assets.images.splash; splash.onload = () => { canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2); let font = new Image(); - font.src = assets.images.font; + font.src = imgPrefix + assets.images.font; font.onload = () => { console.log("font loaded") text = new TextRenderer(canvas, font); @@ -48,14 +54,13 @@ splash.onload = () => { } } -// Entity class is here becuase otherwise every entity would need the canvas passed into it +// Entity class is here because otherwise every entity would need the canvas passed into it class Entity extends Object { constructor(x=0, y=0, spritesheet=null, sprite=null) { super(); this.x = x; this.y = y; this.sprite = sprite; - this.spritesheet = spritesheet; } draw() { @@ -70,7 +75,7 @@ let currentRoom = rooms[roomIndex]; let searchForRoom = (name) => { // returns the room's index in the rooms array for (let i = 0; i < rooms.length; i++) { - if (rooms[i].name == name) return i; + if (rooms[i].name === name) return i; } throw new Error("Room not found:"+name+". Are you sure it's pushed?"); } @@ -95,15 +100,10 @@ debugRoom.roomList = []; debugRoom.options = { "main": [ {"label": "Change Room", "action": _ => {debugRoom.submenu = "changeRoom"; debugRoom.index = 0;}}, - {"label": "Change Target FPS", "action": _ => {debugRoom.submenu = "changeTargetFPS"; debugRoom.index = 0;}}, {"label": "Unlock refresh rate", "action": _ => {runAtMonitorRefreshRate = !runAtMonitorRefreshRate;}}, {"label": "Exit", "action": _ => {changeRoom(searchForRoom("menu"))}}, ], "changeRoom": [], - "changeTargetFPS": [ - {"label": "60", "action": _ => {targetFrames = 60;}}, - {"label": "30", "action": _ => {targetFrames = 30;}}, - ], }; debugRoom.init = () => { if (!debug) changeRoom(searchForRoom("menu")); @@ -130,7 +130,6 @@ debugRoom.keyDown = (key) => { } debugRoom.draw = () => { - canvas.fill("black"); canvas.drawRect(Math.sin(currentFrame /(canvas.width / 2)) * canvas.width - 32, canvas.height-64, 32, 32, "#222034"); } @@ -151,14 +150,10 @@ menuRoom.options = [ ]; if (debug) menuRoom.options.push({"label": "Debug Room", "action": _ => {changeRoom(searchForRoom("debug"))}}); menuRoom.index = 0; - -menuRoom.draw = () => { - canvas.fill("black"); -} menuRoom.drawGUI = () => { text.render(GAME_TITLE, 8, 7*4); for (let i = 0; i < menuRoom.options.length; i++) { - if (i == menuRoom.index) { + if (i === menuRoom.index) { text.render(">", 8, 7*(i+5)); } text.render(menuRoom.options[i].label, 16, 7*(i+5)); @@ -184,18 +179,22 @@ const testing_graphing = new Room("testing_graphing"); testing_graphing.yScale = 10; testing_graphing.a = 0; testing_graphing.b = 0; -testing_graphing.c = (canvas.height/4)*testing_graphing.yScale; +testing_graphing.c = 0; + +testing_graphing.init = () => { + // canvas.setDimensions(window.innerWidth, window.innerHeight); +} testing_graphing.keyDown = (key) => { if (pressedLastFrame.includes(key)) return; const keyActions = { - ArrowUp: () => testing_graphing.a += 0.25, - ArrowDown: () => testing_graphing.a -= 0.25, - ArrowLeft: () => testing_graphing.b -= 0.25, - ArrowRight: () => testing_graphing.b += 0.25, - KeyA: () => testing_graphing.c -= 0.25, - KeyD: () => testing_graphing.c += 0.25, + ArrowUp: () => testing_graphing.a++, + ArrowDown: () => testing_graphing.a--, + ArrowLeft: () => testing_graphing.b --, + ArrowRight: () => testing_graphing.b ++, + KeyA: () => testing_graphing.c++, + KeyD: () => testing_graphing.c--, }; const action = keyActions[key]; @@ -205,7 +204,6 @@ testing_graphing.keyDown = (key) => { testing_graphing.draw = () => { // draws a quadratic graph // y = ax^2 + bx + c - canvas.fill("black"); canvas.drawLine(0, canvas.height/2, canvas.width, canvas.height/2, "white"); canvas.drawLine(canvas.width/2, 0, canvas.width/2, canvas.height, "white"); for (let i = 0; i < canvas.width; i++) { @@ -219,15 +217,105 @@ testing_graphing.drawGUI = () => { text.render(`y = ${testing_graphing.a}x^2 + ${testing_graphing.b}x + ${testing_graphing.c}`, 0, 0); } +const testing_physics = new Room("testing_physics"); +testing_physics.init = () => { + canvas.setDimensions(1000, 500) +} +testing_physics.gravity = 0.00005; // m/s^2 +testing_physics.rho = 1; + +let testing_ball = new Entity(canvas.width/2, 0); +testing_physics.objects.push(testing_ball); + +testing_ball.velocity = { x: 10, y: 0 }; +testing_ball.mass = 70; // kg +testing_ball.radius = 4; // pixels +testing_ball.restitution = 0.2; // 0-1, 1 being perfectly elastic +testing_ball.Cd = 0.47; // dimensionless + +testing_ball.step = () => { + // Calculate forces on the x and y axis + var Fx = -0.5 * testing_ball.Cd * testing_ball.A * testing_physics.rho * testing_ball.velocity.x * testing_ball.velocity.x * testing_ball.velocity.x / Math.abs(testing_ball.velocity.x); + var Fy = -0.5 * testing_ball.Cd * testing_ball.A * testing_physics.rho * testing_ball.velocity.y * testing_ball.velocity.y * testing_ball.velocity.y / Math.abs(testing_ball.velocity.y); + + Fx = (isNaN(Fx) ? 0 : Fx); + Fy = (isNaN(Fy) ? 0 : Fy); + + // Acceleration + var ax = (Fx / testing_ball.mass); + var ay = testing_physics.gravity + (Fy / testing_ball.mass); + + testing_ball.velocity.x += ax * targetFrames; + testing_ball.velocity.y += ay * targetFrames; + + // Position + testing_ball.x += testing_ball.velocity.x * targetFrames; + testing_ball.y += testing_ball.velocity.y * targetFrames; + + if (testing_ball.y > canvas.height - testing_ball.radius) { + testing_ball.velocity.y *= -testing_ball.restitution; + testing_ball.y = canvas.height - testing_ball.radius; + } + + if (testing_ball.x > canvas.width - testing_ball.radius) { + testing_ball.velocity.x *= -testing_ball.restitution; + testing_ball.x = canvas.width - testing_ball.radius; + } + + if (testing_ball.x < testing_ball.radius) { + testing_ball.velocity.x *= -testing_ball.restitution; + testing_ball.x = testing_ball.radius; + } + + debugStatuses.push(`acceleration: ${ax}, ${ay}`); + debugStatuses.push(`force: ${Fx}, ${Fy}`); +} + +testing_ball.draw = () => { + canvas.drawImage(assets.images.debug_ball, testing_ball.x - 4, testing_ball.y - 4); +} + +testing_physics.keyDown = (key) => { + if (pressedLastFrame.includes(key)) return; + + const keyActions = { + KeyL: _=> { + testing_ball.velocity = {x: 20, y: 100} + }, + KeyS: _=> { + testing_ball.velocity = {x: 0, y: 0} + } + }; + + const action = keyActions[key]; + if (action) action(); +} + +testing_physics.drawGUI = () => { + debugStatuses.push(`position: ${testing_ball.x}, ${testing_ball.y}`); + debugStatuses.push(`velocity: ${testing_ball.velocity.x}, ${testing_ball.velocity.y}`); + +} + +testing_physics.onclick = (pos={x:0,y:0}) => { + testing_ball.x = pos.x; + testing_ball.y = pos.y; +} + rooms.push(loadingRoom); rooms.push(menuRoom); rooms.push(debugRoom); // REMOVE THESE rooms.push(testing_graphing); +rooms.push(testing_physics); currentRoom = rooms[roomIndex]; +canvas.canvas.addEventListener('mousedown', function(evt) { + const mousePos = getMousePos(canvas.canvas, evt); + currentRoom.onclick(mousePos); +}, false); let main = () => { // main game loop @@ -243,7 +331,8 @@ let main = () => { // main game loop currentRoom.step(); - currentRoom.draw(); + // canvas.fill(currentRoom.background); + currentRoom.drawGUI(); let currentKeys = whichKeyDown(); @@ -267,12 +356,23 @@ let main = () => { // main game loop } for (let i = 0; i < debugStatuses.length; i++) { - // console.debug(debugStatuses[i]); - if (typeof(debugStatuses[i]) == "string") text.render(debugStatuses[i], 0, canvas.height-text.charHeight*(debugStatuses.length-i)); - if (typeof(debugStatuses[i]) == "object") {text.render(debugStatuses[i].msg, 0, canvas.height-text.charHeight*(debugStatuses.length-i)); debugStatuses[i].ttl--;} + switch (typeof (debugStatuses[i])) { + case "string": + text.render(debugStatuses[i], 0, canvas.height - text.charHeight * (debugStatuses.length - i)); + break; + case "object": + console.debug("OBJECT!!") + text.render(debugStatuses[i].msg, 0, canvas.height-text.charHeight*(debugStatuses.length-i)); + debugStatuses[i].ttl--; + break; + } + if (typeof(debugStatuses[i]) == "object") {} } + currentRoom.draw(); + + lastFrameTime = now; } @@ -282,7 +382,7 @@ let init = () => { for (let image in assets.images) { currentRoom.updateStatus("Loading image " + image); let img = new Image(); - img.src = assets.images[image]; + img.src = imgPrefix + assets.images[image]; img.onload = () => { assets.images[image] = img; } diff --git a/src/js/keyboard.js b/src/js/inputs/keyboard.js similarity index 100% rename from src/js/keyboard.js rename to src/js/inputs/keyboard.js diff --git a/src/js/inputs/mouse.js b/src/js/inputs/mouse.js new file mode 100644 index 0000000000000000000000000000000000000000..6b7c433061e0c336ec18c3a100fe7ef8878aee75 --- /dev/null +++ b/src/js/inputs/mouse.js @@ -0,0 +1,11 @@ +export const getMousePos = (canvas, evt) => { + const rect = canvas.getBoundingClientRect(); + let x = evt.clientX - rect.left; + let y = evt.clientY - rect.top; + x /= rect.width; + y /= rect.height; + x *= canvas.width; + y *= canvas.height; + return {x,y}; + } + diff --git a/src/js/objects.js b/src/js/objects.js index f1941091b405183685b8c6ce3bb0c87165da43f6..0db9387ef2fd3d3339c7621b2b4aaf06ce471e7c 100644 --- a/src/js/objects.js +++ b/src/js/objects.js @@ -9,13 +9,14 @@ class Room extends Object { super(); this.objects = []; this.name = name; // needs to be unique, otherwise the searching code will just use the first one it finds. + this.background = "#000000"; } init(){} draw() { - for (let i = 0; i < this.objects.length; i++) { - this.objects[i].draw(); + for (const item of this.objects) { + item.draw(); } } @@ -24,6 +25,9 @@ class Room extends Object { } keyDown(key) { + for (const item of this.objects) { + item.keyDown(key); + } } keyUp(key) { diff --git a/src/js/text.js b/src/js/text.js index 9fd464c6f1e0d0b203ce3e4d3b6c2354e16b199d..c881cc8f504aeeadb170fe9a4fdb81a5d1569d1c 100644 --- a/src/js/text.js +++ b/src/js/text.js @@ -16,20 +16,18 @@ class TextRenderer { let { canvas, fontWidth, fontHeight } = this; let index = this.fontChars.indexOf(letter.toLowerCase()); - if (index == -1) { + if (index === -1) { if (!substituteOK) return; - canvas.drawText(letter, x, y, "#ffffff", 7, "monospace"); + canvas.drawText(letter, x, y, "#ffffff", 5, "monospace"); } let sx = index * fontWidth; let sy = 0; // draw image to context let yOffset = 0; // if the letter is ",", offset it by -1 - if (letter == ",") { - yOffset = -1; - } + if (letter === ",") yOffset = 1; canvas.sliceImage(this.fontimg, x+canvas.cX, y + yOffset + canvas.cY, fontWidth, fontHeight, sx, sy, fontWidth, fontHeight); - // canvas.cX and canvas.cY are the camera offsets. we dont want to have text flying off the screen. + // canvas.cX and canvas.cY are the camera offsets. we don't want to have text flying off the screen. // you can counteract this by specifying x-cX and x-cY when calling this. } @@ -37,7 +35,7 @@ class TextRenderer { let heightOffset = 0; let xOffset = 0; for (let i = 0; i < text.length; i++) { - if (text[i] == "\n") { + if (text[i] === "\n") { heightOffset++; xOffset = 0; continue;