原生js编写的一款三维赛车游戏
代码语言:html
所属分类:游戏
代码描述:原生js编写的一款三维赛车游戏,玩法:鼠标 = 转向,点击 = 刹车,双击 = 跳转,R = 重新启动,1 = 屏幕截图。道路是随机生成的,每次都不同。您从 20 秒开始,通过检查点再获得 10 秒。通过转向或跳过岩石和树木来避开它们。路在1000结束,祝你好运!
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<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, .........完整代码请登录后点击上方下载按钮下载查看
网友评论0