写点什么

3D 赛车【附源码】设计实现

作者:JavaPub
  • 2022 年 5 月 30 日
  • 本文字数:16996 字

    阅读完需:约 56 分钟

3D 赛车


3D 赛车【附源码】


我的网站已经上线了 http://javapub.net.cn/


文末源码、免费获取文末源码、免费获取文末源码、免费获取


点赞再看,养成习惯


适合人群:初级学习者和爱好者,下面有展示图。计算机毕业设计、java 精品项目


@[toc]

1 前言

🚀获取源码,文末公众号回复【赛车】,即可。⭐欢迎点赞留言

2 正文

公众号:JavaPub

2.1 展示预览

13MB GIF 可以欣赏:https://tvax4.sinaimg.cn/large/007F3CC8ly1h1onp5g8z9g31fz0pi7ws.gif


<img src="https://tva1.sinaimg.cn/large/007F3CC8ly1h1onp5g8z9g31fz0pi7ws.gif" alt="动画" width="1871" data-width="1871" data-height="918">




[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pbf7X6O3-1651070934070)(https://tvax3.sinaimg.cn/large/007F3CC8ly1h1onp5g8z9g31fz0pi7ws.gif)]


2.2 项目结构

2.2 主要代码展示

<html><!--/*
HUE JUMPER - By Frank ForceLow 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 settingsconst debug = 0; // enable debug featuresconst usePointerLock = 1; // remove pointer lock for 2k build
// draw settingsconst context = c.getContext('2d'); // canvas 2d contextconst drawDistance = 800; // how many road segments to draw in front of playerconst cameraDepth = 1; // FOV of camera (1 / Math.tan((fieldOfView/2) * Math.PI/180))const roadSegmentLength = 100; // length of each road segmentconst roadWidth = 500; // how wide is roadconst warningTrackWidth = 150; // with of road plus warning trackconst dashLineWidth = 9; // width of the dashed line in the roadconst maxPlayerX = 2e3; // player can not move this far from center of roadconst mountainCount = 30; // how many mountains are thereconst timeDelta = 1/60; // inverse frame rate
// player settingsconst playerHeight = 150; // how high is player above groundconst playerMaxSpeed = 300; // limit max player speedconst playerAccel = 1; // player accelerationconst playerBrake = -3; // player acceleration when breakingconst playerTurnControl = .2; // player turning rateconst playerJumpSpeed = 25; // z speed added for jumpconst playerSpringConstant = .01; // spring players pitchconst playerCollisionSlow = .1; // slow down from collisionsconst pitchLerp = .1; // speed that camera pitch changesconst pitchSpringDamping = .9; // dampen the pitch springconst elasticity = 1.2; // bounce elasticity (2 is full bounce, 1 is none)const centrifugal = .002; // how much to pull player on turnsconst forwardDamping = .999; // dampen player z speedconst lateralDamping = .7; // dampen player x speedconst offRoadDamping = .98; // more damping when off roadconst gravity = -1; // gravity to apply in y axisconst cameraHeadingScale = 2; // scale of player turning to rotate cameraconst worldRotateScale = .00005; // how much to rotate world around turns // level settingsconst maxTime = 20; // time to start withconst checkPointTime = 10; // how much time for getting to checkpointconst checkPointDistance = 1e5; // how far between checkpointsconst checkpointMaxDifficulty = 9; // how many checkpoints before max difficultyconst roadEnd = 1e4; // how many sections until end of the road // global game variables let playerPos; // player position 3d vectorlet playerVelocity; // player velocity 3d vectorlet playerPitchSpring; // spring for player pitch bouncelet playerPitchSpringVelocity; // velocity of pitch springlet playerPitchRoad; // pitch of road, or 0 if player is in airlet playerAirFrame; // how many frames player has been in airlet worldHeading; // heading to turn skyboxlet randomSeed; // random seed for levellet startRandomSeed; // save the starting seed for active uselet nextCheckPoint; // distance of next checkpointlet hueShift; // current hue shift for all hsl colorslet road; // the list of road segmentslet time; // time left before game overlet lastUpdate = 0; // time of last updatelet 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 classclass 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 polyfunction 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 textfunction 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 loopStartLevel();Update(); </script></body></html>
复制代码


JavaPub

源码下载

获取源码,公众号回复【赛车】,即可。更多最新 Java 面试题加群、见群公告。~


不会还有人没 点赞 + 关注 + 收藏 吧!


系列推荐:

Java 实现 捕鱼达人 小游戏【附源码】


Java 实现 贪吃蛇 小游戏【附源码】


Java 实现 1024 小游戏【附源码】


Java实现一个坦克大战的小游戏【附源码】


Java实现一个打飞机的小游戏【附源码】


Java 实现 植物大战僵尸 小游戏【附源码】

查看更多博主首页更多实战项目 >>>

项目源码获取方法


点赞本文,然后私信我,我免费分享给你哈~



发布于: 刚刚阅读数: 3
用户头像

JavaPub

关注

原创技术公众号:JavaPub 2018.12.02 加入

原创技术公众号:JavaPub | 限时免费领取原创PDF

评论

发布
暂无评论
3D赛车【附源码】设计实现_JavaPub_InfoQ写作社区