From b02675f4fb20044cc46d09badfd7c1069f9659a6 Mon Sep 17 00:00:00 2001 From: asraelite Date: Sat, 3 Mar 2018 13:29:14 +0000 Subject: [PATCH] Add zooming --- js/assets.mjs | 5 ++ js/consts.mjs | 9 ++- js/game/events.mjs | 3 + js/game/index.mjs | 4 +- js/graphics/index.mjs | 98 +++++++++++++++++++++++------ js/graphics/{ship.mjs => world.mjs} | 11 +++- js/input.mjs | 7 ++- js/world/body.mjs | 2 +- js/world/celestial.mjs | 21 ++++++- js/world/index.mjs | 2 + js/world/module.mjs | 6 +- js/world/spawn.mjs | 17 +++-- 12 files changed, 156 insertions(+), 29 deletions(-) rename js/graphics/{ship.mjs => world.mjs} (69%) diff --git a/js/assets.mjs b/js/assets.mjs index 313c893..18120a9 100644 --- a/js/assets.mjs +++ b/js/assets.mjs @@ -13,6 +13,11 @@ export const images = { thruster: { light: 'modules/light_thruster.svg' } + }, + celestials: { + green: { + "0": 'celestials/green_0.svg' + } } }; diff --git a/js/consts.mjs b/js/consts.mjs index 081277e..00266c2 100644 --- a/js/consts.mjs +++ b/js/consts.mjs @@ -1,11 +1,18 @@ /* * Constants that do not change during gameplay. * This can kind of be treated like a configuration file, I guess. + * + * All le */ -// Pixel length of sector. +// Unit length of sector. Only for internal representation. export const SECTOR_SIZE = 512; // Star count per sector. export const STAR_DENSITY = (SECTOR_SIZE ** 2) / 10000; // G, G-boy, The big G, Mr. G, g's big brother, G-dog export const GRAVITATIONAL_CONSTANT = 0.01; +// Perspective constraints. Higher zoom value = closer. +export const MIN_ZOOM = 2; +export const MAX_ZOOM = 30; +export const DEFAULT_ZOOM = 10; +export const ZOOM_SPEED = 0.01; diff --git a/js/game/events.mjs b/js/game/events.mjs index bdccf45..11a84c0 100644 --- a/js/game/events.mjs +++ b/js/game/events.mjs @@ -1,5 +1,8 @@ import * as game from './index.mjs'; +import * as graphics from '../graphics/index.mjs'; +import * as world from '../world/index.mjs'; export function startGame() { game.changeView('game'); + graphics.perspective.focusPlayer(); } diff --git a/js/game/index.mjs b/js/game/index.mjs index f62296c..890658b 100644 --- a/js/game/index.mjs +++ b/js/game/index.mjs @@ -18,7 +18,9 @@ export async function init() { gui.init(); input.init(); - //events.startGame(); + events.startGame(); + + //tick(); return; // Recursive `requestAnimationFrame` can cause problems with Parcel. while(true) { diff --git a/js/graphics/index.mjs b/js/graphics/index.mjs index 633b84b..def5dda 100644 --- a/js/graphics/index.mjs +++ b/js/graphics/index.mjs @@ -1,12 +1,13 @@ -import {game} from '../game/index.mjs'; -import {getContainedSectors} from '../world/index.mjs'; -import * as background from './background.mjs'; import * as gui from './gui.mjs'; import * as draw from './draw.mjs'; -import * as ship from './ship.mjs'; +import * as input from '../input.mjs'; +import {render as renderWorld} from './world.mjs'; +import {render as renderBackground} from './background.mjs'; +import * as world from '../world/index.mjs'; +import * as consts from '../consts.mjs'; export let canvas, context, tempCanvas, tempContext; -export let view; +export let perspective; export function init() { canvas = document.querySelector('#main'); @@ -17,12 +18,7 @@ export function init() { canvas.width = 600; canvas.height = 600; - view = { - bounds: [0, 0, canvas.width, canvas.height], - x: 0, - y: 0, - zoom: 1 - } + perspective = new Perspective(); draw.text('Loading...', canvas.width / 2, canvas.height / 2, { align: 'center', valign: 'middle' }); @@ -32,18 +28,84 @@ export function render() { context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = '#000'; context.fillRect(0, 0, canvas.width, canvas.height); + + renderBackground(); + context.save(); - - // TODO: Translate canvas. - - background.render(); - ship.render(); - + perspective.tick(); + perspective.transformCanvas(); + renderWorld(); context.restore(); gui.render(); } export function getVisibleSectors() { - return getContainedSectors(...view.bounds); + return world.getContainedSectors(...perspective.bounds); +} + +class Perspective { + constructor() { + this.x = 0; + this.y = 0; + this.bounds = [0, 0, canvas.width, canvas.height]; + this.reset(); + } + + tick() { + if (input.mouse.scroll !== 0) { + this.zoomDelta(-input.mouse.scroll); + } + + if (this.focus !== null) { + this.x = this.focus.x; + this.y = this.focus.y; + } + + if (this.rotationFocus !== null) { + this.targetRotation = this.rotationFocus.r; + } else { + this.targetRotation = 0; + } + + if (this.smoothRotation) { + this.rotation = (this.rotation * 0.9 + this.targetRotation * 0.1); + } else { + this.rotation = this.targetRotation; + } + } + + reset() { + this.rotation = 0; + this.targetRotation = 0; + this.smoothRotation = false; + this.zoom = consts.DEFAULT_ZOOM; + this.focus = null; + this.rotationFocus = null; + } + + focusPlayer() { + this.focus = world.playerShip; + this.rotationFocus = world.playerShip; + this.smoothRotation = false; + } + + zoomDelta(delta) { + let factor = 1 + (consts.ZOOM_SPEED * Math.abs(delta)); + this.zoom *= delta > 0 ? factor : 1 / factor; + this.normalize(); + } + + normalize() { + this.zoom = Math.max(consts.MIN_ZOOM, + Math.min(consts.MAX_ZOOM, this.zoom)); + } + + transformCanvas() { + let [bx, by, bw, bh] = this.bounds; + let tx = -this.x + bw / 2; + let ty = -this.y + bh / 2; + context.translate(tx, ty); + context.scale(this.zoom, this.zoom); + } } diff --git a/js/graphics/ship.mjs b/js/graphics/world.mjs similarity index 69% rename from js/graphics/ship.mjs rename to js/graphics/world.mjs index d0df59d..63a4fc8 100644 --- a/js/graphics/ship.mjs +++ b/js/graphics/world.mjs @@ -4,12 +4,13 @@ import * as world from '../world/index.mjs'; export function render() { world.ships.forEach(renderShip); + world.celestials.forEach(renderCelestial); } function renderShip(ship) { context.fillStyle = 'red'; //context.fillRect(ship.x, ship.y, 10, 10); - let size = 100; + let size = 1; context.drawImage(assets.modules.capsule.small, ship.x, ship.y, size, size); context.drawImage(assets.modules.fuel.small, ship.x, ship.y + size, @@ -17,3 +18,11 @@ function renderShip(ship) { context.drawImage(assets.modules.thruster.light, ship.x, ship.y + size * 2, size, size); } + +const celestialImages = { + green: Object.values(assets.celestials.green) +} + +function renderCelestial(cel) { + context.drawImage(cel.image, cel.x, cel.y, cel.diameter, cel.diameter); +} diff --git a/js/input.mjs b/js/input.mjs index d76bfe8..521b46d 100644 --- a/js/input.mjs +++ b/js/input.mjs @@ -1,6 +1,6 @@ import {canvas} from './graphics/index.mjs'; -export const mouse = { pressed: {}, held: {}, x: 0, y: 0 }; +export const mouse = { pressed: {}, held: {}, x: 0, y: 0, scroll: 0 }; export const keyCode = { pressed: {}, held: {} }; export const key = { pressed: {}, held: {} }; export const action = {}; @@ -11,6 +11,7 @@ export function tick() { mouse.pressed = {}; keyCode.pressed = {}; key.pressed = {}; + mouse.scroll = 0; } export function init() { @@ -43,4 +44,8 @@ export function init() { mouse.x = event.clientX - rect.left; mouse.y = event.clientY - rect.top; }); + + window.addEventListener('wheel', event => { + mouse.scroll = event.deltaY; + }); } diff --git a/js/world/body.mjs b/js/world/body.mjs index 7679640..1caf077 100644 --- a/js/world/body.mjs +++ b/js/world/body.mjs @@ -10,7 +10,7 @@ export default class Body { this.rvel = 0; this.mass = mass; } - + tickGravity(bodies) { bodies.forEach(b => { let force = b.mass / this.mass / (distanceTo(b) ** 2) * G; diff --git a/js/world/celestial.mjs b/js/world/celestial.mjs index 57b95ea..d990198 100644 --- a/js/world/celestial.mjs +++ b/js/world/celestial.mjs @@ -1,10 +1,29 @@ +import {images as assets} from '../assets.mjs'; import Body from './body.mjs'; export default class Celestial extends Body { constructor(x, y, radius, { density = 1, - mass = (radius ** 2) * density + type = 'rock' }) { + let mass = (radius ** 2) * density super(x, y, mass); + this.radius = radius; + + this.type = type; + let imageArr = Object.values(assets.celestials[this.type]); + this.image = imageArr[Math.random() * imageArr.length | 0]; + } + + tick() { + + } + + get center() { + return [this.x + this.radius / 2, this.y + this.radius / 2]; + } + + get diameter() { + return this.radius * 2; } } diff --git a/js/world/index.mjs b/js/world/index.mjs index 15f8f1f..e9365cb 100644 --- a/js/world/index.mjs +++ b/js/world/index.mjs @@ -17,6 +17,8 @@ export function init() { entities.clear(); celestials.clear(); spawn.player(); + spawn.startPlanet(); + } export function tick() { diff --git a/js/world/module.mjs b/js/world/module.mjs index 17f1456..324ea29 100644 --- a/js/world/module.mjs +++ b/js/world/module.mjs @@ -3,6 +3,9 @@ export default class Module { name = 'Unnamed Module', type = 'block', mass = 1, + // Fuel + filled = false, + fuelCapacity = 0, ...properties }) { this.x = x; @@ -10,6 +13,7 @@ export default class Module { this.name = name; this.type = type; this.mass = mass; - + // Fuel + this.fuel = filled ? fuelCapacity : 0; } } diff --git a/js/world/spawn.mjs b/js/world/spawn.mjs index ee33e79..6d98dca 100644 --- a/js/world/spawn.mjs +++ b/js/world/spawn.mjs @@ -1,6 +1,6 @@ import Ship from './ship.mjs'; import Module from './module.mjs'; -import Celestial from './ship.mjs'; +import Celestial from './celestial.mjs'; import {modules} from '../data.mjs'; import * as world from './index.mjs'; @@ -11,9 +11,18 @@ export function player() { ship.addModule(0, 2, modules.thruster.light); world.ships.add(ship); world.setPlayerShip(ship); + return ship; } -// Make module length = 1, define all other length off that. -export function celestial() { - let celestial = new Celestial(0, 50, 45) +export function startPlanet() { + return celestial(-40, 10, 40, { + density: 1, + type: 'green' + }); +} + +export function celestial(x, y, radius, params) { + let celestial = new Celestial(x, y, radius, params); + world.celestials.add(celestial); + return celestial; }