Slide 1

Slide 1 text

NEEDS - FEWER - ♥ NI-DEV-CONF '17 ♥ PIXELS Building A JavaScript Retro Game Engine

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

JAVASCRIPT-POWERED SUPER-RETRO GAME JAMMING GOODNESS

Slide 4

Slide 4 text

THE 'ABOUT ME' BIT NEIL McCALLION Senior UI Developer @ Moola Co-organizer @ BelfastJS njmcode.github.io

Slide 5

Slide 5 text

A NEAT IDEA

Slide 6

Slide 6 text

'FANTASY CONSOLES'

Slide 7

Slide 7 text

THINGS I WANT From A Retro Game Engine / Fantasy Console ♥ JavaScript ♥ Limited features / capability Creatively inspiring Easy to work with Retro aesthetics Good performance

Slide 8

Slide 8 text

NON-INTERACTIVE SLIDES - DEMO UNAVAILABLE N-GINE™ JS-powered 128 x 128 px 16 colors, 2 fonts 2-button D-pad controls (keys) ~40 FPS Minimal core API

Slide 9

Slide 9 text

CORE

Slide 10

Slide 10 text

MAKE GAMES, NOT ENGINES. - Someone who tried to write a game engine

Slide 11

Slide 11 text

WHAT MAKES A GAME? INPUT EVENTS - KEYBOARD - MOUSE - TOUCH GAMEPAD API GYRO / ACCEL + FEEDBACK HTML5 CANVAS WEB AUDIO API + TIME DATE.NOW()

Slide 12

Slide 12 text

API DESIGN addimg blit button cls color cursor font img line plot rect rectf circle circlef rnd text screen start ... const { color, cursor, font, rect, rnd, text } = ngine /* ... */ function render () { cursor(20, 20) color(3) font(1) text('Hello there') rect(20, 50, rnd(2, 8)) }

Slide 13

Slide 13 text

RESTRICTING ACCESS / ENFORCING LIMITS

Slide 14

Slide 14 text

GAME LOOP // myGame.js ngine.start({ init() { // runs after bootup }, update(t) { // input, movement, etc }, render(t) { // draw scene }, }) // ngine.core.js const frameTime = 1000 / 40 let lt = 0 function _frame() { requestAnimationFrame(_frame) const ct = Date.now() if (ct - lt > frameTime) { _update(ct) _render(ct) lt = ct } }

Slide 15

Slide 15 text

GRAPHICS

Slide 16

Slide 16 text

CANVAS ABSTRACTION // HTML5 Canvas ctx.beginPath() ctx.arc(x, y, rad, 0, 2 * Math.PI, false) ctx.strokeStyle = 'white' ctx.stroke() ctx.fillRect(x, y, w, h) // ngine color(15) circ(x, y, rad) rectf(x, y, w) // h = w PALETTE + FONTS PICO-8 MONO UPPER Press Start P2

Slide 17

Slide 17 text

CSS SCALING + CANVAS SUB-PIXEL RENDERING NON-INTERACTIVE SLIDES - DEMO UNAVAILABLE .ngine-screen { image-rendering: pixelated; } // game.js plot(20.3, 10.75) // ngine.core.js ngine.plot = (x, y) => { x = round(x) y = round(y) // ...draw pixel }

Slide 18

Slide 18 text

HANDLING IMAGE DATA // game.js function init() { // id, data, w, h addimg('ship', [/* data */], 48, 24) } function render(t) { // id, screen x/y, clip x/y img('ship', /* params */) }

Slide 19

Slide 19 text

LOGIC

Slide 20

Slide 20 text

CONTROLS // game.js function update(t) { if (button(0)) { player.y -= player.speed } if (button(4)) { player.shoot(t) } // etc. } 0 - up 1 - down 2 - left 3 - right 4 - but1 (z) 5 - but2 (x) 6 - start (Enter)

Slide 21

Slide 21 text

COLLISIONS // game.js // Ball = { x, y, width, height, ... } // bricks = [{ ... }, { ... }, { ... }] collide(Ball, bricks, function (ballObj, brickObj) { // destroy brickObj // bounce ball // increase score }) collide(Ball, Bat, function (ballObj, batObj) { // bounce ball })

Slide 22

Slide 22 text

COLLISIONS NON-INTERACTIVE SLIDES - DEMO UNAVAILABLE

Slide 23

Slide 23 text

PERFORMANCE

Slide 24

Slide 24 text

QUICK PERFORMANCE WINS // Bitwise floor, ceil & round e.g. ngine.floor = n => n << 0 // Object pools const Pool = { /* ... */ getNext() { return entities.find(p => !p.alive) }, createEntity(opts) { const newEntity = this.getNext() // init entity if available, // fail or re-use old one if not } }

Slide 25

Slide 25 text

EMITTER OBJECTS NON-INTERACTIVE SLIDES - DEMO UNAVAILABLE

Slide 26

Slide 26 text

FUTURE PLANS Go public sound() + music() Editing tools Perf boosts (WebGL?) Custom hardware? ... ...make games

Slide 27

Slide 27 text

Improve perf skills Understand browser painting Architectural challenges Creative coding and... WHY MAKE AN ENGINE?

Slide 28

Slide 28 text

FUN

Slide 29

Slide 29 text

NJMCODE.GITHUB.IO THANK YOU ♥