Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <html>
- <!--/*
- HUE JUMPER - By Frank Force
- Low fi retro inspired endless runner in only 2 kilobytes!
- Features
- - Retro style 3D rendering engine in full HD
- - Realistic driving physics and collisions
- - Random level generation with increasing difficulty
- - Gradient sky with sun and moon
- - Procedurally generated mountain range
- - Random trees and rocks
- - Camera rumble and slow when off road
- - Checkpoint system, road markers, and hue shift
- - Time and distance display
- */-->
- <title>Hue Jumper</title>
- <meta charset="utf-8">
- <body bgcolor=#000>
- <canvas id=c style='touch-action:none;position:absolute;left:0px;top:0px;width:100%;height:100%'></canvas>
- <a hidden id=downloadLink></a>
- <script>
- 'use strict'; // strict mode
- // debug settings
- const debug = 0; // enable debug features
- const usePointerLock = 1; // remove pointer lock for 2k build
- // draw settings
- const context = c.getContext('2d'); // canvas 2d context
- const drawDistance = 800; // how many road segments to draw in front of player
- const cameraDepth = 1; // FOV of camera (1 / Math.tan((fieldOfView/2) * Math.PI/180))
- const roadSegmentLength = 100; // length of each road segment
- const roadWidth = 500; // how wide is road
- const warningTrackWidth = 150; // with of road plus warning track
- const dashLineWidth = 9; // width of the dashed line in the road
- const maxPlayerX = 2e3; // player can not move this far from center of road
- const mountainCount = 30; // how many mountains are there
- const timeDelta = 1/60; // inverse frame rate
- // player settings
- const playerHeight = 150; // how high is player above ground
- const playerMaxSpeed = 300; // limit max player speed
- const playerAccel = 1; // player acceleration
- const playerBrake = -3; // player acceleration when breaking
- const playerTurnControl = .2; // player turning rate
- const playerJumpSpeed = 25; // z speed added for jump
- const playerSpringConstant = .01; // spring players pitch
- const playerCollisionSlow = .1; // slow down from collisions
- const pitchLerp = .1; // speed that camera pitch changes
- const pitchSpringDamping = .9; // dampen the pitch spring
- const elasticity = 1.2; // bounce elasticity (2 is full bounce, 1 is none)
- const centrifugal = .002; // how much to pull player on turns
- const forwardDamping = .999; // dampen player z speed
- const lateralDamping = .7; // dampen player x speed
- const offRoadDamping = .98; // more damping when off road
- const gravity = -1; // gravity to apply in y axis
- const cameraHeadingScale = 2; // scale of player turning to rotate camera
- const worldRotateScale = .00005; // how much to rotate world around turns
- // level settings
- const maxTime = 20; // time to start with
- const checkPointTime = 10; // how much time for getting to checkpoint
- const checkPointDistance = 1e5; // how far between checkpoints
- const checkpointMaxDifficulty = 9; // how many checkpoints before max difficulty
- const roadEnd = 1e4; // how many sections until end of the road
- // global game variables
- let playerPos; // player position 3d vector
- let playerVelocity; // player velocity 3d vector
- let playerPitchSpring; // spring for player pitch bounce
- let playerPitchSpringVelocity; // velocity of pitch spring
- let playerPitchRoad; // pitch of road, or 0 if player is in air
- let playerAirFrame; // how many frames player has been in air
- let worldHeading; // heading to turn skybox
- let randomSeed; // random seed for level
- let startRandomSeed; // save the starting seed for active use
- let nextCheckPoint; // distance of next checkpoint
- let hueShift; // current hue shift for all hsl colors
- let road; // the list of road segments
- let time; // time left before game over
- let lastUpdate = 0; // time of last update
- let timeBuffer = 0; // frame rate adjustment
- function StartLevel()
- {
- /////////////////////////////////////////////////////////////////////////////////////
- // build the road with procedural generation
- /////////////////////////////////////////////////////////////////////////////////////
- let roadGenSectionDistanceMax = 0; // init end of section distance
- let roadGenWidth = roadWidth; // starting road width
- let roadGenSectionDistance = 0; // distance left for this section
- let roadGenTaper = 0; // length of taper
- let roadGenWaveFrequencyX = 0; // X wave frequency
- let roadGenWaveFrequencyY = 0; // Y wave frequency
- let roadGenWaveScaleX = 0; // X wave amplitude (turn size)
- let roadGenWaveScaleY = 0; // Y wave amplitude (hill size)
- startRandomSeed = randomSeed = Date.now(); // set random seed
- road = []; // clear list of road segments
- // generate the road
- for( let i = 0; i < roadEnd*2; ++i ) // build road past end
- {
- if (roadGenSectionDistance++ > roadGenSectionDistanceMax) // check for end of section
- {
- // calculate difficulty percent
- const difficulty = Math.min(1, i*roadSegmentLength/checkPointDistance/checkpointMaxDifficulty); // difficulty
- // randomize road settings
- roadGenWidth = roadWidth*Random(1-difficulty*.7, 3-2*difficulty); // road width
- roadGenWaveFrequencyX = Random(Lerp(difficulty, .01, .02)); // X frequency
- roadGenWaveFrequencyY = Random(Lerp(difficulty, .01, .03)); // Y frequency
- roadGenWaveScaleX = i > roadEnd ? 0 : Random(Lerp(difficulty, .2, .6)); // X scale
- roadGenWaveScaleY = Random(Lerp(difficulty, 1e3, 2e3)); // Y scale
- // apply taper and move back
- roadGenTaper = Random(99, 1e3)|0; // randomize taper
- roadGenSectionDistanceMax = roadGenTaper + Random(99, 1e3); // randomize segment distance
- roadGenSectionDistance = 0; // reset section distance
- i -= roadGenTaper; // subtract taper
- }
- // make a wavy road
- const x = Math.sin(i*roadGenWaveFrequencyX) * roadGenWaveScaleX; // road X
- const y = Math.sin(i*roadGenWaveFrequencyY) * roadGenWaveScaleY; // road Y
- road[i] = road[i]? road[i] : {x:x, y:y, w:roadGenWidth}; // get or make road segment
- // apply taper from last section
- const p = Clamp(roadGenSectionDistance / roadGenTaper, 0, 1); // get taper percent
- road[i].x = Lerp(p, road[i].x, x); // X pos and taper
- road[i].y = Lerp(p, road[i].y, y); // Y pos and taper
- road[i].w = i > roadEnd ? 0 : Lerp(p, road[i].w, roadGenWidth); // check for road end, width and taper
- road[i].a = road[i-1] ? Math.atan2(road[i-1].y-road[i].y, roadSegmentLength) : 0; // road pitch angle
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // init game
- /////////////////////////////////////////////////////////////////////////////////////
- // reset everything
- playerVelocity = new Vector3
- (
- playerPitchSpring =
- playerPitchSpringVelocity =
- playerPitchRoad =
- hueShift = 0
- );
- playerPos = new Vector3(0, playerHeight); // set player pos
- worldHeading = randomSeed; // randomize world heading
- nextCheckPoint = checkPointDistance; // init next checkpoint
- time = maxTime; // set the starting time
- }
- function Update()
- {
- // time regulation, in case running faster then 60 fps, though it causes judder REMOVE FROM MINFIED
- const now = performance.now();
- if (lastUpdate)
- {
- // limit to 60 fps
- const delta = now - lastUpdate;
- if (timeBuffer + delta < 0)
- {
- // running fast
- requestAnimationFrame(Update);
- return;
- }
- // update time buffer
- timeBuffer += delta;
- timeBuffer -= timeDelta * 1e3;
- if (timeBuffer > timeDelta * 1e3)
- timeBuffer = 0; // if running too slow
- }
- lastUpdate = now;
- // start frame
- if (snapshot) {c.width|0} else // DEBUG REMOVE FROM MINFIED
- c.width = window.innerWidth,c.height = window.innerHeight; // clear the screen and set size
- if (!c.width) // REMOVE FROM MINFIED
- {
- // fix bug on itch, wait for canvas before updating
- requestAnimationFrame(Update);
- return;
- }
- if (usePointerLock && document.pointerLockElement !== c && !touchMode) // set mouse down if pointer lock released
- mouseDown = 1;
- UpdateDebugPre(); // DEBUG REMOVE FROM MINFIED
- /////////////////////////////////////////////////////////////////////////////////////
- // update player - controls and physics
- /////////////////////////////////////////////////////////////////////////////////////
- // get player road segment
- const playerRoadSegment = playerPos.z/roadSegmentLength|0; // current player road segment
- const playerRoadSegmentPercent = playerPos.z/roadSegmentLength%1; // how far player is along current segment
- // get lerped values between last and current road segment
- const playerRoadX = Lerp(playerRoadSegmentPercent, road[playerRoadSegment].x, road[playerRoadSegment+1].x);
- const playerRoadY = Lerp(playerRoadSegmentPercent, road[playerRoadSegment].y, road[playerRoadSegment+1].y) + playerHeight;
- const roadPitch = Lerp(playerRoadSegmentPercent, road[playerRoadSegment].a, road[playerRoadSegment+1].a);
- const playerVelocityLast = playerVelocity.Add(0); // save last velocity
- playerVelocity.y += gravity; // gravity
- playerVelocity.x *= lateralDamping; // apply lateral damping
- playerVelocity.z = Math.max(0, time ? forwardDamping*playerVelocity.z : 0); // apply damping, prevent moving backwards
- playerPos = playerPos.Add(playerVelocity); // add player velocity
- const playerTurnAmount = Lerp(playerVelocity.z/playerMaxSpeed, mouseX * playerTurnControl, 0); // turning
- playerVelocity.x += // update x velocity
- playerVelocity.z * playerTurnAmount - // apply turn
- playerVelocity.z ** 2 * centrifugal * playerRoadX; // apply centrifugal force
- playerPos.x = Clamp(playerPos.x, -maxPlayerX, maxPlayerX); // limit player x position
- // check if on ground
- if (playerPos.y < playerRoadY)
- {
- // bounce velocity against ground normal
- playerPos.y = playerRoadY; // match y to ground plane
- playerAirFrame = 0; // reset air grace frames
- playerVelocity = new Vector3(0, Math.cos(roadPitch), Math.sin(roadPitch)) // get ground normal
- .Multiply(-elasticity * // apply bounce
- (Math.cos(roadPitch) * playerVelocity.y + Math.sin(roadPitch) * playerVelocity.z)) // dot of road and velocity
- .Add(playerVelocity); // add velocity
- playerVelocity.z +=
- mouseDown? playerBrake : // apply brake
- Lerp(playerVelocity.z/playerMaxSpeed, mouseWasPressed*playerAccel, 0); // apply accel
- if (Math.abs(playerPos.x) > road[playerRoadSegment].w) // check if off road
- {
- playerVelocity.z *= offRoadDamping; // slow down when off road
- playerPitchSpring += Math.sin(playerPos.z/99)**4/99; // bump when off road
- }
- }
- // update jump
- if (playerAirFrame++<6 && mouseDown && mouseUpFrames && mouseUpFrames<9 && time) // check for jump
- {
- playerVelocity.y += playerJumpSpeed; // apply jump velocity
- playerAirFrame = 9; // prevent jumping again
- }
- mouseUpFrames = mouseDown? 0 : mouseUpFrames+1; // update mouse up frames for double click
- const airPercent = (playerPos.y-playerRoadY)/99; // calculate above ground percent
- playerPitchSpringVelocity += Lerp(airPercent,0,playerVelocity.y/4e4); // pitch down with vertical velocity
- // update player pitch
- playerPitchSpringVelocity += (playerVelocity.z - playerVelocityLast.z)/2e3; // pitch down with forward accel
- playerPitchSpringVelocity -= playerPitchSpring * playerSpringConstant; // apply pitch spring constant
- playerPitchSpringVelocity *= pitchSpringDamping; // dampen pitch spring
- playerPitchSpring += playerPitchSpringVelocity; // update pitch spring
- playerPitchRoad = Lerp(pitchLerp, playerPitchRoad, Lerp(airPercent,-roadPitch,0));// match pitch to road
- const playerPitch = playerPitchSpring + playerPitchRoad; // update player pitch
- if (playerPos.z > nextCheckPoint) // crossed checkpoint
- {
- time += checkPointTime; // add more time
- nextCheckPoint += checkPointDistance; // set next checkpoint
- hueShift += 36; // shift hue
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // draw background - sky, sun/moon, mountains, and horizon
- /////////////////////////////////////////////////////////////////////////////////////
- // multi use local variables
- let x, y, w, i;
- randomSeed = startRandomSeed; // set start seed
- worldHeading = ClampAngle(worldHeading + playerVelocity.z * playerRoadX * worldRotateScale); // update world angle
- // pre calculate projection scale, flip y because y+ is down on canvas
- const projectScale = (new Vector3(1, -1, 1)).Multiply(c.width/2/cameraDepth); // get projection scale
- const cameraHeading = playerTurnAmount * cameraHeadingScale; // turn camera with player
- const cameraOffset = Math.sin(cameraHeading)/2; // apply heading with offset
- // draw sky
- const lighting = Math.cos(worldHeading); // brightness from sun
- const horizon = c.height/2 - Math.tan(playerPitch) * projectScale.y; // get horizon line
- const g = context.createLinearGradient(0,horizon-c.height/2,0,horizon); // linear gradient for sky
- g.addColorStop(0,LSHA(39+lighting*25,49+lighting*19,230-lighting*19)); // top sky color
- g.addColorStop(1,LSHA(5,79,250-lighting*9)); // bottom sky color
- DrawPoly(c.width/2, 0, c.width/2, c.width/2, c.height, c.width/2, g); // draw sky
- // draw sun and moon
- for( i = 2; i--; ) // 0 is sun, 1 is moon
- {
- const g = context.createRadialGradient( // radial gradient for sun
- x = c.width*(.5+Lerp( // angle 0 is center
- (worldHeading/Math.PI/2+.5+i/2)%1, // sun angle percent
- 4, -4)-cameraOffset), // sun x pos, move far away for wrap
- y = horizon - c.width/5, // sun y pos
- c.width/25, // sun size
- x, y, i?c.width/23:c.width); // sun end pos & size
- g.addColorStop(0, LSHA(i?70:99)); // sun start color
- g.addColorStop(1, LSHA(0,0,0,0)); // sun end color
- DrawPoly(c.width/2, 0, c.width/2, c.width/2, c.height, c.width/2, g); // draw sun
- }
- // draw mountains
- for( i = mountainCount; i--; ) // draw every mountain
- {
- const angle = ClampAngle(worldHeading+Random(19)); // mountain random angle
- const lighting = Math.cos(angle-worldHeading); // mountain lighting
- DrawPoly(
- x = c.width*(.5+Lerp(angle/Math.PI/2+.5, 4, -4)-cameraOffset), // mountain x pos, move far away for wrap
- y = horizon, // mountain base
- w = Random(.2,.8)**2*c.width/2, // mountain width
- x+w*Random(-.5,.5), // random tip skew
- y - Random(.5,.8)*w, 0, // mountain height
- LSHA(Random(15,25)+i/3-lighting*9,i/2+Random(19),Random(220,230))); // mountain color
- }
- // draw horizon
- DrawPoly(c.width/2, horizon, c.width/2, c.width/2, c.height, c.width/2, // horizon pos & size
- LSHA(25, 30, 95)); // horizon color
- /////////////////////////////////////////////////////////////////////////////////////
- // draw road and objects
- /////////////////////////////////////////////////////////////////////////////////////
- // calculate road x offsets and projections
- for( x = w = i = 0; i < drawDistance+1; )
- {
- // create road world position
- let p = new Vector3( // set road position
- x += w += road[playerRoadSegment+i].x, // sum local road offsets
- road[playerRoadSegment+i].y, (playerRoadSegment+i)*roadSegmentLength)// road y and z pos
- .Add(playerPos.Multiply(-1)); // subtract to get local space
- p.x = p.x*Math.cos(cameraHeading) - p.z*Math.sin(cameraHeading); // rotate camera heading
- // tilt camera pitch
- const z = 1 / (p.z*Math.cos(playerPitch) - p.y*Math.sin(playerPitch)); // invert z for projection
- p.y = p.y*Math.cos(playerPitch) - p.z*Math.sin(playerPitch);
- p.z = z;
- // project road segment to canvas space
- road[playerRoadSegment+i++].p = // set projected road point
- p.Multiply(new Vector3(z, z, 1)) // projection
- .Multiply(projectScale) // scale
- .Add(new Vector3(c.width/2,c.height/2)) // center on canvas
- }
- // draw the road segments
- let segment2 = road[playerRoadSegment+drawDistance]; // store the last segment
- for( i = drawDistance; i--; ) // iterate in reverse
- {
- const segment1 = road[playerRoadSegment+i];
- randomSeed = startRandomSeed + playerRoadSegment + i; // random seed for this segment
- const lighting = Math.sin(segment1.a) * Math.cos(worldHeading)*99; // calculate segment lighting
- const p1 = segment1.p; // projected point
- const p2 = segment2.p; // last projected point
- if (p1.z < 1e5 && p1.z > 0) // check near and far clip
- {
- // draw road segment
- if (i % (Lerp(i/drawDistance,1,9)|0) == 0) // fade in road resolution
- {
- // ground
- DrawPoly(c.width/2, p1.y, c.width/2, c.width/2, p2.y, c.width/2, // ground top & bottom
- LSHA(25+lighting, 30, 95)); // ground color
- // warning track
- if (segment1.w > 400) // no warning track if thin
- DrawPoly(p1.x, p1.y, p1.z*(segment1.w+warningTrackWidth), // warning track top
- p2.x, p2.y, p2.z*(segment2.w+warningTrackWidth), // warning track bottom
- LSHA(((playerRoadSegment+i)%19<9? 50: 20)+lighting)); // warning track stripe color
- // road
- const z = (playerRoadSegment+i)*roadSegmentLength; // segment distance
- DrawPoly(p1.x, p1.y, p1.z*segment1.w, // road top
- p2.x, p2.y, p2.z*segment2.w, // road bottom
- LSHA((z%checkPointDistance < 300 ? 70 : 7)+lighting)); // road color and checkpoint
- // dashed lines
- if (segment1.w > 300) // no dash lines if very thin
- (playerRoadSegment+i)%9==0 && i < drawDistance/3 && // make dashes and skip if far out
- DrawPoly(p1.x, p1.y, p1.z*dashLineWidth, // dash lines top
- p2.x, p2.y, p2.z*dashLineWidth, // dash lines bottom
- LSHA(70+lighting)); // dash lines color
- segment2 = segment1; // prep for next segment
- }
- // random object (tree or rock)
- if (Random()<.2 && playerRoadSegment+i>29) // check for road object
- {
- // player object collision check
- const z = (playerRoadSegment+i)*roadSegmentLength; // segment distance
- const height = (Random(2)|0) * 400; // object type & height
- x = 2*roadWidth * Random(10,-10) * Random(9); // choose object pos
- if (!segment1.h // prevent hitting the same object
- && Math.abs(playerPos.x - x) < 200 // x collision
- && Math.abs(playerPos.z - z) < 200 // z collision
- && playerPos.y-playerHeight < segment1.y+200+height) // y collision + object height
- {
- playerVelocity = playerVelocity.Multiply(segment1.h = playerCollisionSlow); // stop player and mark hit
- }
- // draw road object
- const alpha = Lerp(i/drawDistance, 4, 0); // fade in object alpha
- if (height) // tree
- {
- DrawPoly(x = p1.x+p1.z * x, p1.y, p1.z*29, // trunk bottom
- x, p1.y-99*p1.z, p1.z*29, // trunk top
- LSHA(5+Random(9), 50+Random(9), 29+Random(9), alpha)); // trunk color
- DrawPoly(x, p1.y-Random(50,99)*p1.z, p1.z*Random(199,250), // leaves bottom
- x, p1.y-Random(600,800)*p1.z, 0, // leaves top
- LSHA(25+Random(9), 80+Random(9), 9+Random(29), alpha)); // leaves color
- }
- else // rock
- {
- DrawPoly(x = p1.x+p1.z * x, p1.y, p1.z*Random(200,250), // rock bottom
- x+p1.z*(Random(99,-99)), p1.y-Random(200,250)*p1.z, p1.z*Random(99), // rock top
- LSHA(50+Random(19), 25+Random(19), 209+Random(9), alpha)); // rock color
- }
- }
- }
- }
- UpdateDebugPost(); // DEBUG REMOVE FROM MINFIED
- /////////////////////////////////////////////////////////////////////////////////////
- // draw and update time
- /////////////////////////////////////////////////////////////////////////////////////
- if (mouseWasPressed)
- {
- DrawText(Math.ceil(time = Clamp(time - timeDelta, 0, maxTime)), 9); // show and update time
- context.textAlign = 'right'; // set right alignment for distance
- DrawText(0|playerPos.z/1e3, c.width-9); // show distance
- }
- else
- {
- context.textAlign = 'center'; // set center alignment for title
- DrawText('HUE JUMPER', c.width/2); // draw title text
- }
- requestAnimationFrame(Update); // kick off next frame
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // math and helper functions
- /////////////////////////////////////////////////////////////////////////////////////
- const LSHA = (l, s=0, h=0, a=1) =>`hsl(${ h + hueShift },${ s }%,${ l }%,${ a })`;
- const Clamp = (v, min, max) => Math.min(Math.max(v, min), max);
- const ClampAngle = (a) => (a+Math.PI) % (2*Math.PI) + (a+Math.PI<0? Math.PI : -Math.PI);
- const Lerp = (p, a, b) => a + Clamp(p, 0, 1) * (b-a);
- const Random = (max=1, min=0) => Lerp((Math.sin(++randomSeed)+1)*1e5%1, min, max);
- // simple 3d vector class
- class Vector3
- {
- constructor(x=0, y=0, z=0) { this.x = x; this.y = y; this.z = z }
- Add(v) { v = isNaN(v) ? v : new Vector3(v,v,v); return new Vector3( this.x + v.x, this.y + v.y, this.z + v.z); }
- Multiply(v) { v = isNaN(v) ? v : new Vector3(v,v,v); return new Vector3( this.x * v.x, this.y * v.y, this.z * v.z); }
- }
- // draw a trapazoid shaped poly
- function DrawPoly(x1, y1, w1, x2, y2, w2, fillStyle)
- {
- context.beginPath(context.fillStyle = fillStyle);
- context.lineTo(x1-w1, y1|0);
- context.lineTo(x1+w1, y1|0);
- context.lineTo(x2+w2, y2|0);
- context.lineTo(x2-w2, y2|0);
- context.fill();
- }
- // draw outlined hud text
- function DrawText(text, posX)
- {
- context.font = '9em impact'; // set font size
- context.fillStyle = LSHA(99,0,0,.5); // set font
- context.fillText(text, posX, 129); // fill text
- context.lineWidth = 3; // line width
- context.strokeText(text, posX, 129); // outline text
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // mouse input
- /////////////////////////////////////////////////////////////////////////////////////
- let mouseDown = 0;
- let mouseWasPressed = 0;
- let mouseUpFrames = 0;
- let mouseX = 0;
- let mouseLockX = 0;
- let touchMode = 0;
- onmouseup = e => mouseDown = 0;
- onmousedown = e =>
- {
- if (mouseWasPressed)
- mouseDown = 1;
- mouseWasPressed = 1;
- if (usePointerLock && e.button == 0 && document.pointerLockElement !== c)
- {
- c.requestPointerLock = c.requestPointerLock || c.mozRequestPointerLock;
- c.requestPointerLock();
- mouseLockX = 0;
- }
- }
- onmousemove = e =>
- {
- if (!usePointerLock)
- {
- mouseX = e.x/window.innerWidth*2-1
- return;
- }
- if (document.pointerLockElement !== c)
- return;
- // adjust for pointer lock
- mouseLockX += e.movementX;
- mouseLockX = Clamp(mouseLockX, -window.innerWidth/2, window.innerWidth/2);
- // apply curve to input
- const inputCurve = 1.5;
- mouseX = mouseLockX;
- mouseX /= window.innerWidth/2;
- mouseX = Math.sign(mouseX) * (1-(1-Math.abs(mouseX))**inputCurve);
- mouseX *= window.innerWidth/2;
- mouseX += window.innerWidth/2;
- mouseX = mouseX/window.innerWidth*2-1
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // touch control
- /////////////////////////////////////////////////////////////////////////////////////
- if (typeof ontouchend != 'undefined')
- {
- let ProcessTouch = e =>
- {
- e.preventDefault();
- mouseDown = !(e.touches.length > 0);
- mouseWasPressed = 1;
- touchMode = 1;
- if (mouseDown)
- return;
- // average all touch positions
- let x = 0, y = 0;
- for (let touch of e.touches)
- {
- x += touch.clientX;
- y += touch.clientY;
- }
- mouseX = x/e.touches.length;
- mouseX = mouseX/window.innerWidth*2-1
- }
- c.addEventListener('touchstart', ProcessTouch, false);
- c.addEventListener('touchmove', ProcessTouch, false);
- c.addEventListener('touchcancel', ProcessTouch, false);
- c.addEventListener('touchend', ProcessTouch, false);
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // debug stuff
- /////////////////////////////////////////////////////////////////////////////////////
- let debugPrintLines;
- let snapshot;
- function UpdateDebugPre()
- {
- debugPrintLines = [];
- if (inputWasPushed[82]) // R = restart
- {
- mouseLockX = 0;
- StartLevel();
- }
- if (inputWasPushed[49]) // 1 = screenshot
- {
- snapshot = 1;
- // use 1080p resolution
- c.width = 1920;
- c.height = 1080;
- }
- }
- function UpdateDebugPost()
- {
- if (snapshot)
- {
- SaveSnapshot();
- snapshot = 0;
- }
- UpdateInput();
- if (!debug)
- return;
- UpdateFps();
- context.font='2em"';
- for (let i in debugPrintLines)
- {
- let line = debugPrintLines[i];
- context.fillStyle = line.color;
- context.fillText(line.text,c.width/2,35+35*i);
- }
- }
- function DebugPrint(text, color='#F00')
- {
- if (!debug)
- return;
- if (typeof text == 'object')
- text += JSON.stringify(text);
- let line = {text:text, color:color};
- debugPrintLines.push(line);
- }
- function SaveSnapshot()
- {
- downloadLink.download="snapshot.png";
- downloadLink.href=c.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream");
- downloadLink.click();
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // frame rate counter
- /////////////////////////////////////////////////////////////////////////////////////
- let lastFpsMS = 0;
- let averageFps = 0;
- function UpdateFps()
- {
- let ms = performance.now();
- let deltaMS = ms - lastFpsMS;
- lastFpsMS = ms;
- let fps = 1/(deltaMS/1e3);
- averageFps = averageFps*.9 + fps*.1;
- context.font='3em"';
- context.fillStyle='#0007';
- context.fillText(averageFps|0,c.width-90,c.height-40);
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // keyboard control
- /////////////////////////////////////////////////////////////////////////////////////
- let inputIsDown = [];
- let inputWasDown = [];
- let inputWasPushed = [];
- onkeydown = e => inputIsDown[e.keyCode] = 1;
- onkeyup = e => inputIsDown[e.keyCode] = 0;
- function UpdateInput()
- {
- inputWasPushed = inputIsDown.map((e,i) => e && !inputWasDown[i]);
- inputWasDown = inputIsDown.slice();
- }
- /////////////////////////////////////////////////////////////////////////////////////
- // init hue jumper
- /////////////////////////////////////////////////////////////////////////////////////
- // startup and kick off update loop
- StartLevel();
- Update();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement