From 757293a903855ea1123976f395d6c87f48a13a83 Mon Sep 17 00:00:00 2001 From: bye <bye@byecorps.com> Date: Thu, 29 Aug 2024 13:10:07 +0100 Subject: [PATCH] update (havent dont that in a whike --- src/index.html | 12 +++-- src/js/extras.js | 16 +++++++ src/js/main.js | 87 ++++++++++++++++++++++------------ src/js/objects.js | 38 +++++++++++++++ src/js/objects/player.js | 21 ++++---- src/js/rooms/debug_sprites.js | 12 +++++ src/js/rooms/debug_stars.js | 11 +++++ src/js/rooms/game.js | 85 ++++++++++++++++++++++++++++----- src/js/rooms/mainMenu.js | 28 +++++++++-- utils/size.js | 7 +++ utils/sprite_editor/index.html | 7 ++- 11 files changed, 259 insertions(+), 65 deletions(-) create mode 100644 src/js/rooms/debug_sprites.js create mode 100644 src/js/rooms/debug_stars.js diff --git a/src/index.html b/src/index.html index e70b4a8..672636a 100644 --- a/src/index.html +++ b/src/index.html @@ -3,9 +3,7 @@ margin:0; background: #1a1a1a; } - canvas { - width: 256px; - height: 240px; + #c { position:fixed;top:50%;left:50%;translate:-50% -50%; image-rendering: pixelated; } @@ -14,14 +12,18 @@ <script src="./main.js"></script> <script> fl = Math.floor; + a = c.getContext`2d`; u = _=>{ w = innerWidth; h = innerHeight; wS = fl(w/256); hS = fl(h/256); s=Math.min(wS,hS); - c.style.width = 256*s; - c.style.height= 240*s; + c.width = 256*s; + c.height= 240*s; + canvas.pixelRatio = s; + canvas.setScale(canvas.scale); + a.imageSmoothingEnabled = false; } u();addEventListener('resize', u, !0); </script> \ No newline at end of file diff --git a/src/js/extras.js b/src/js/extras.js index dc884e9..77f3731 100644 --- a/src/js/extras.js +++ b/src/js/extras.js @@ -6,6 +6,22 @@ export function drawLineThroughPoint(x, y) { export const clone = structuredClone; +export const round = Math.round; +export const roundToRatio = x => { + return round(x * canvas.pixelRatio) / canvas.pixelRatio +} + +export const abs = Math.abs; + +export function mulberry32(a) { + return function() { + let t = a += 0x6D2B79F5; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + } +} + export const clonePlayer = player => { return player; } diff --git a/src/js/main.js b/src/js/main.js index 65c282b..7b676ee 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,13 +1,14 @@ import Canvas from "../../hampsterengine/src/canvas.js"; import Engine from "../../hampsterengine/src/engine.js"; -import {FontRenderer} from "./objects"; +import Keyboard from "../../hampsterengine/src/keyboard"; +import {FontRenderer, Stars} from "./objects"; import SoundBox from "./sb-player-small"; // import {mus_DEMOSONG} from "./songs/DEMOSONGDONOTCOMMIT"; // dependencies used for debugging. comment out code that uses this and they won't be included -// import Stats from "stats.js"; +import Stats from "stats.js"; // Images import font from "../img/font.webp"; @@ -15,39 +16,59 @@ import font from "../img/font.webp"; // Rooms import {rm_mainMenu} from "./rooms/mainMenu"; import {rm_game} from "./rooms/game"; -import {rm_DEBUG_button} from "./rooms/debug_button"; -import {rm_DEBUG_mouse} from "./rooms/debug_mouse"; -import {rm_DEBUG_music} from "./rooms/debug_music"; +// import {rm_DEBUG_button} from "./rooms/debug_button"; +// import {rm_DEBUG_mouse} from "./rooms/debug_mouse"; +// import {rm_DEBUG_music} from "./rooms/debug_music"; import {rm_DEBUG_INCURSION} from "./rooms/debug_incursion"; import {rm_DEBUG_text} from "./rooms/debug_text"; -import Mouse from "../../hampsterengine/src/mouse"; +import {rm_DEBUG_stars} from "./rooms/debug_stars"; +import {rm_DEBUG_sprites} from "./rooms/debug_sprites"; // Music // There is none // Parse query parameters const query = new URLSearchParams(window.location.search); +window.query = query; // Init the engine and canvas -const canvas = new Canvas('c'); +const canvas = new Canvas(c); const engine = new Engine(canvas); const assets = engine.assetStore; +const keyboard = new Keyboard(); assets.addImage('font', font); -const fontRenderer = new FontRenderer(assets.get('font')); +const fontRenderer = new FontRenderer(assets.get`font`); window.fR = fontRenderer // const mouse = new Mouse(engine); // window.mouse = mouse; -const scale = (query.get('debug') ? 4 : 1); -canvas.width = 256 * scale; -canvas.height = 240 * scale; -canvas.pixelRatio = scale; -canvas.ctx.setTransform(canvas.pixelRatio, 0, 0, canvas.pixelRatio, 0, 0); +engine.debug = query.get`debug`; +window.debug = engine.debug; + +canvas.width = 256; +canvas.height = 240; + +canvas.setScale(); canvas.ctx.imageSmoothingEnabled = false; engine.running = false; // Game uses this as a pause state actually +// // Prerender the stars +// const tempCanvasStars = document.createElement("canvas"); +// tempCanvasStars.width = 2560; +// tempCanvasStars.height = 2400; +// const stars = new Stars(tempCanvasStars); +// window.starsTest = stars; +// stars.draw(0, 0, 2560, 2400); +// const createStarsObjectURL = blob => { +// assets.addImage('stars', URL.createObjectURL(blob)); +// } +// tempCanvasStars.toBlob(createStarsObjectURL); + +assets.addMiniSprite('grass', 'IIIIIIQIQQQJZQZZRZRRZRZRSSRSRZZR'); +assets.renderMiniSprite('grass', ['#2a6', '#964', '#853']); + engine.registerRoom(rm_mainMenu, 'mainMenu'); engine.registerRoom(rm_game, 'game'); @@ -56,11 +77,13 @@ engine.registerRoom(rm_game, 'game'); // engine.registerRoom(rm_DEBUG_music, 'debug_music'); engine.registerRoom(rm_DEBUG_INCURSION, 'debug_incursion'); engine.registerRoom(rm_DEBUG_text, 'debug_text'); +engine.registerRoom(rm_DEBUG_stars, 'debug_stars'); +engine.registerRoom(rm_DEBUG_sprites, 'debug_sprites'); // Init stats.js -// const stats = new Stats(); -// stats.showPanel(0); -// document.body.appendChild( stats.dom ); +const stats = new Stats(); +stats.showPanel(0); +document.body.appendChild( stats.dom ); let physicsFrame=0; window.physicsFrame = physicsFrame; @@ -71,7 +94,7 @@ function main() { // Draw things. no user interaction or physics here. try { - // stats.begin(); + stats.begin(); engine.frames++; canvas.fill(engine.room.bgColor ?? 'black'); @@ -80,12 +103,13 @@ function main() { // engine.drawCursor(); - // stats.end(); + stats.end(); - if (query.get('debug')) { - canvas.drawText(`physics ticks: ${physicsFrame} (~${(physicsFrame/60).toFixed(1)}sec)`, 0, 0,{textBaseline:'top'}) - canvas.drawText(`frames: ${engine.frames} (~${(engine.frames/(physicsFrame/60)).toFixed(1)}FPS <--flawed)`, 0, 8,{textBaseline:'top'}) + if (debug) { + canvas.drawText(`physics ticks: ${engine.physicsFrames} (~${(engine.physicsFrames/60).toFixed(1)}sec)`, 0, 0,{textBaseline:'top'}) + canvas.drawText(`frames: ${engine.frames}`, 0, 8,{textBaseline:'top'}) canvas.drawText(`run time: ${((performance.now()-readyTime)/1000).toFixed(1)}sec`, 0, 16, {textBaseline:'top'}) + canvas.drawText(`keys: ${JSON.stringify(keyboard.keys)}`, 0, 24, {textBaseline:'top'}) } // Ask to run at the next frame @@ -97,10 +121,10 @@ function main() { canvas.ctx.setTransform(canvas.pixelRatio, 0, 0, canvas.pixelRatio, 0, 0); canvas.fill('#a05555d0'); - const logo = assets.get('splash') + const logo = assets.get`splash` canvas.drawImage(logo, 10, 10, logo.width, logo.height) - canvas.setFillColor('black'); + canvas.setFillColor`black`; canvas.drawText(e, 5, canvas.height-5, { maxWidth: canvas.width-10, textBaseline: 'bottom' @@ -112,19 +136,23 @@ function main() { function physicsTick() { // Runs 60 times a second regardless of frame rate. - physicsFrame++; + engine.physicsFrames++; + engine.lastPhysicsFrame = performance.now(); engine.room.step(); + + keyboard.keysThisFrame = []; + keyboard.keysUpThisFrame = []; } console.debug(engine.rooms); -if (query.get('room')) { - console.log('Requesting room', query.get('room')); +if (query.get`room`) { + console.log('Requesting room', query.get`room`); engine.loadDelay = 0; - engine.room = engine.getRoomIndex(query.get('room')); + engine.room = engine.getRoomIndex(query.get`room`); } else { - engine.room = engine.getRoomIndex('mainMenu'); + engine.room = engine.getRoomIndex`mainMenu`; } // Ensure assets are loaded. @@ -134,8 +162,9 @@ function load() { setTimeout(load, 1000/60); } else { readyTime = performance.now(); + engine.lastPhysicsFrame = performance.now(); main(); - setInterval(physicsTick, 1000/60); // Update physics 60 times a second + setInterval(physicsTick, (query.get`slowdown` ? 500 : 1000/60)); // Update physics 60 times a second } } diff --git a/src/js/objects.js b/src/js/objects.js index 7876278..e4054d1 100644 --- a/src/js/objects.js +++ b/src/js/objects.js @@ -1,6 +1,8 @@ import {Entity} from "../../hampsterengine/src/things"; import ButtonBorder from "../img/button.webp"; import {rm_game} from "./rooms/game"; +import {mulberry32, round, roundToRatio} from "./extras"; +import Canvas from "../../hampsterengine/src/canvas"; // Private @@ -127,6 +129,42 @@ export class Logo extends Entity { } +export class Stars { + constructor(canvas='c', seed=1522363656) { + // Set seed to (Math.random()*2**32)>>>0 for a random seed every time + this.seed = seed; + this.canvas = new Canvas(canvas); + + this.stars = []; + } + + init(w, h) { + // Split the draw area into 2*2 squares + const gridW = Math.floor(w / 2); + const gridH = Math.floor(h / 2); + + for (let ww = 0; ww <= gridW; ww++) { + for (let hh = 0; hh <= gridH; hh++) { + const offsetX = (ww*2) + (Math.ceil(mulberry32(this.seed - ww * hh)()*10) - 10); + const offsetY = (hh*2) + (Math.ceil(mulberry32(this.seed + hh)()*10) - 10); + if (mulberry32(this.seed * ww + hh)() > 0.99) this.stars.push({x: offsetX, y: offsetY}); + } + } + } + + draw(x, y, w, h) { + if (this.stars.length === 0) this.init(w, h); + + x = roundToRatio(x); + y = roundToRatio(y); + + canvas.setFillColor('rgba(255,255,255,0.3)'); + for (const star of this.stars) { + if (star.x + x < canvas.width) this.canvas.fillRect(x + star.x, y + star.y, 2, 2); + } + } +} + export class FontRenderer { constructor(font) { this.font = font; diff --git a/src/js/objects/player.js b/src/js/objects/player.js index 15c4469..3aefe7e 100644 --- a/src/js/objects/player.js +++ b/src/js/objects/player.js @@ -1,23 +1,20 @@ import {Entity} from "../../../hampsterengine/src/things"; +import {round, roundToRatio} from "../extras"; export default class Player extends Entity { constructor(props) { super(props); + + this.jumping = false; } draw() { canvas.setFillColor('red'); - canvas.fillRect(this.x,this.y,10,16); - } - - checkCollision(other) { - // Checks if colliding with another entity. Returns true if colliding. - - return ( - this.left < other.right && - this.right > other.left && - this.top < other.bottom && - this.bottom > other.top - ); + const timeSinceLastFrame = (performance.now() - engine.lastPhysicsFrame) / 1000 / (query.get('slowdown') ? 60 : 1); + const interpolationX = this.vx * timeSinceLastFrame; + const interpolationY = this.vy * timeSinceLastFrame; + // const interpolationX = 0; + // const interpolationY = 0; + canvas.fillRect(roundToRatio(this.x+interpolationX),roundToRatio(this.y+interpolationY),this.width,this.height); } } diff --git a/src/js/rooms/debug_sprites.js b/src/js/rooms/debug_sprites.js new file mode 100644 index 0000000..a59d14e --- /dev/null +++ b/src/js/rooms/debug_sprites.js @@ -0,0 +1,12 @@ + +import {Room} from "../../../hampsterengine/src/things"; + +export const rm_DEBUG_sprites = new Room(); +rm_DEBUG_sprites.start = _=> { + canvas.setScale(3); + canvas.ctx.imageSmoothingEnabled = false; +} + +rm_DEBUG_sprites.draw = _=> { + canvas.tileImage(engine.assetStore.get('grass').sprite, 0, 0, canvas.width, 8, 8, 8); +} diff --git a/src/js/rooms/debug_stars.js b/src/js/rooms/debug_stars.js new file mode 100644 index 0000000..938a7b6 --- /dev/null +++ b/src/js/rooms/debug_stars.js @@ -0,0 +1,11 @@ +import {Room} from "../../../hampsterengine/src/things"; +import {Stars} from "../objects"; + +export const rm_DEBUG_stars = new Room(); +rm_DEBUG_stars.bgColor = 'black'; + +rm_DEBUG_stars.draw = _ => { + stars.draw(0, 0, canvas.width, canvas.height); +} + +const stars = new Stars(); diff --git a/src/js/rooms/game.js b/src/js/rooms/game.js index 85c4a12..a1be296 100644 --- a/src/js/rooms/game.js +++ b/src/js/rooms/game.js @@ -1,11 +1,11 @@ import {Entity, Room} from "../../../hampsterengine/src/things"; import Player from "../objects/player"; -import {clone, clonePlayer} from "../extras"; +import {clone, clonePlayer, abs} from "../extras"; export const rm_game = new Room(); const GRAVITY_X = 0; // I don't think we're going to use X gravity but i'm going to keep in the source in case i do -const GRAVITY_Y = 150; // Per second +const GRAVITY_Y = 300; // Per second const entities = rm_game.entities; @@ -20,36 +20,97 @@ rm_game.stop = _=>{ rm_game.step = _=>{ const elapsed = 1 / 60; - // Clone the player so we can put them back after the first check - const old_player = {...player}; - const entitiesWithoutThePlayer = [...entities].slice(entities.indexOf(player)); + let friction = 0.9; + const boost = keyboard.keys.includes("Shift") ? 40 : 0; - // Accelerate the player towards GRAVITY_Y - player.y += GRAVITY_Y * elapsed; + for (const key of keyboard.keys) { + switch (key) { + case "ArrowLeft": + player.vx = -80-boost; + break; + case "ArrowRight": + player.vx = 80+boost; + break; + case " ": + if (!player.jumping) { + player.jumping = true; + player.vy -= 150; + } + } + } + + const entitiesWithoutThePlayer = [...entities].toSpliced(entities.indexOf(player), 1); + // console.debug(entitiesWithoutThePlayer); + + player.vx = Math.min(500, player.vx + (player.ax * elapsed + GRAVITY_X*elapsed)); + player.vy = Math.min(500, player.vy + (player.ay * elapsed + GRAVITY_Y*elapsed)); + player.x += player.vx * elapsed; + player.y += player.vy * elapsed; + + // Make acceleration decay + player.ax *= 0.1; + player.ay *= 0.1; - if (player.checkCollision(floor)) { - player.y = floor.top - player.height; + if (abs(player.ax) < 0.01) player.ax = 0; + if (abs(player.ay) < 0.01) player.ay = 0; + + rm_game.x = player.x; + rm_game.y = player.y; + + for (const entity of entitiesWithoutThePlayer) { + if (player.checkCollision(entity)) { + friction = 0.8; + let side = player.resolveCollision(entity); + if (side === 2) player.jumping = 0; + if (side === 1 || side === 3) friction = 0.5; + } } + + // player.vy *= friction; + player.vx *= friction; + + if (abs(player.vy) < 1) player.vy = 0; + if (abs(player.vx) < 1) player.vx = 0; } rm_game.drawGui = _ => { // Draw the player's position canvas.setFillColor('black'); - canvas.drawText(`(${player.x},${player.y})`, 10, canvas.height-10, { + canvas.drawText(`Position: (${player.x},${player.y})`, 10, canvas.height-10, { maxWidth: canvas.width-20 }); + canvas.drawText(`Velocity: (${player.vx},${player.vy})`, 10, canvas.height-18, { + maxWidth: canvas.width-20 + }); + canvas.drawText(`Acceleration: (${player.ax},${player.ay})`, 10, canvas.height-26, { + maxWidth: canvas.width-20 + }); + + // Draw the player's position + canvas.strokeRect(rm_game.x, rm_game.y, 10, 16); } let player = new Player(); + player.x = 40; player.y = 40; +player.width = 10; +player.height = 16; window.player = player; entities.push(player); let floor = new Entity(); -floor.x = 0; +floor.x = 64; floor.y = 150; -floor.width = 256; +floor.width = 128; floor.height = 50; floor.draw = _=> { canvas.setStrokeColor('black'); canvas.strokeRect(floor.x, floor.y, floor.width, floor.height) } rm_game.entities.push(floor); + +let ceiling = new Entity(); +ceiling.x = 64; +ceiling.y = 75; +ceiling.width = 128; +ceiling.height = 50; +ceiling.draw = _=> { canvas.setStrokeColor('black'); canvas.strokeRect(ceiling.x, ceiling.y, ceiling.width, ceiling.height) } +rm_game.entities.push(ceiling); diff --git a/src/js/rooms/mainMenu.js b/src/js/rooms/mainMenu.js index 507c62d..23ade9f 100644 --- a/src/js/rooms/mainMenu.js +++ b/src/js/rooms/mainMenu.js @@ -1,5 +1,5 @@ import {Room} from "../../../hampsterengine/src/things"; -import {Logo, MainMenuButton} from "../objects"; +import {Logo, MainMenuButton, Stars} from "../objects"; export const rm_mainMenu = new Room(); rm_mainMenu.bgColor = '#050911'; @@ -10,10 +10,26 @@ rm_mainMenu.bgColor = '#050911'; // logo.align = 2 // rm_mainMenu.entities.push(logo); -rm_mainMenu.flasher = !0; +const stars = new Stars(); rm_mainMenu.step = _=>{ - if (physicsFrame % 30) rm_mainMenu.flasher = !rm_mainMenu.flasher; + for (const key of keyboard.keysThisFrame) { + switch (key.toLowerCase()) { + case "enter": + engine.room = engine.getRoomIndex("game"); + break; + case "o": + alert("there are none!"); + break; + } + } +} + +rm_mainMenu.draw = _=> { + // canvas.tempFilter(function () { + // canvas.drawImage(engine.assetStore.get('stars'), 0, 0, 2560, 2400); + // }, "invert(1) opacity(0.3)"); + stars.draw(-(engine.frames/500), 0, 2560, 240) } rm_mainMenu.drawGui = _ => { @@ -22,7 +38,9 @@ rm_mainMenu.drawGui = _ => { fR.invert = 1; fR.draw("Thirteen minutes to space", 30, 8*4); - if(rm_mainMenu.flasher) fR.draw("Press [ENTER] to start.", 30, canvas.height-(8*8)); + fR.draw("Press [ENTER] to start.", 30, canvas.height-(8*8)); + fR.draw("Press [O] for options.", 30, canvas.height-(8*6)); fR.draw("created by bye", 30, canvas.height-(8*4)); fR.invert = 0; -} \ No newline at end of file +} + diff --git a/utils/size.js b/utils/size.js index 115f797..b7fe8a6 100644 --- a/utils/size.js +++ b/utils/size.js @@ -4,4 +4,11 @@ var stats = fs.statSync("build/game.zip"); var fileSizeInBytes = stats.size; var fileSizeInKilobytes = fileSizeInBytes / 1024; +var statsUncompressed = fs.statSync("build/index.html"); +var fileSizeInBytesUncompressed = statsUncompressed.size; +var fileSizeInKilobytesUncompressed = fileSizeInBytesUncompressed / 1024; + +var compressionRatio = fileSizeInBytesUncompressed / fileSizeInBytes; + console.log(`Current game size: ${Math.round(fileSizeInKilobytes*10)/10}/13.0 KB (${Math.round((fileSizeInKilobytes/13)*100)}%)`); +console.log(`Current game size (uncompressed): ${fileSizeInKilobytesUncompressed.toFixed(1)}/${(13.0 * compressionRatio).toFixed(1)} KB (${Math.round((fileSizeInKilobytesUncompressed/(13 * compressionRatio))*100)}%)`) diff --git a/utils/sprite_editor/index.html b/utils/sprite_editor/index.html index 613f182..02bb5ee 100644 --- a/utils/sprite_editor/index.html +++ b/utils/sprite_editor/index.html @@ -2,15 +2,18 @@ <html lang="en"> <head> <meta charset="UTF-8"> - <title>Sprite editor</title> + <title>Sprite checker</title> </head> <body> + + <p>using code generated with https://xem.github.io/miniPixelArt/</p> + <canvas id=a> <script> ctx=a.getContext`2d` // ctx pallette="000f000f000fff00fff0f" // color palette (you can remove the colors you didn't use to save bytes) pixels=[]; - "@@@@HHA@@A@@HIIAACCHQCSHARBHHIIA".replace(/./g, + "IIIIIIQIQQQJZQZZRZRRZRZRSSRSRZZR".replace(/./g, character=>{ characterCode=character.charCodeAt(), pixels.push(characterCode&7), // Gets the last three bits -- GitLab