前言

一个js的忍者小游戏
话不多说
如图所示


简单好玩 打发时间

代码

HTML

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>忍者大闯关</title><link rel="stylesheet" href="./css.css">
</head>
<body><div class="container"><div id="score"></div><canvas id="game" width="375" height="375"></canvas><div id="introduction">按住鼠标伸出一根棍子</div><div id="perfect">双倍 得分</div><button id="restart">再玩一次</button></div><script src="./js.js"></script>
</body>
</html>

CSS

html,
body {height: 100%;margin: 0;
}body {font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;cursor: pointer;
}.container {display: flex;justify-content: center;align-items: center;height: 100%;
}#score {position: absolute;top: 30px;right: 30px;font-size: 2em;font-weight: 900;
}#introduction {width: 200px;height: 150px;position: absolute;font-weight: 600;font-size: 0.8em;font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;text-align: center;transition: opacity 2s;
}#restart {width: 120px;height: 120px;position: absolute;border-radius: 50%;color: white;background-color: red;border: none;font-weight: 700;font-size: 1.2em;font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;display: none;cursor: pointer;
}#perfect {position: absolute;opacity: 0;transition: opacity 2s;
}#youtube,
#youtube-card {display: none;
}@media (min-height: 425px) {/** Youtube logo by https://codepen.io/alvaromontoro */#youtube {z-index: 2;display: block;width: 100px;height: 70px;position: absolute;bottom: 20px;left: 20px;background: red;border-radius: 50% / 11%;transform: scale(0.8);transition: transform 0.5s;}#youtube:hover,#youtube:focus {transform: scale(0.9);}#youtube::before {content: "";display: block;position: absolute;top: 7.5%;left: -6%;width: 112%;height: 85%;background: red;border-radius: 9% / 50%;}#youtube::after {content: "";display: block;position: absolute;top: 20px;left: 40px;width: 45px;height: 30px;border: 15px solid transparent;box-sizing: border-box;border-left: 30px solid white;}#youtube span {font-size: 0;position: absolute;width: 0;height: 0;overflow: hidden;}#youtube:hover + #youtube-card {display: block;position: absolute;bottom: 12px;left: 10px;padding: 25px 25px 25px 130px;width: 300px;background-color: white;}
}
// Extend the base functionality of JavaScript
Array.prototype.last = function () {return this[this.length - 1];
};// A sinus function that acceps degrees instead of radians
Math.sinus = function (degree) {return Math.sin((degree / 180) * Math.PI);
};// Game data
let phase = "waiting"; // waiting | stretching | turning | walking | transitioning | falling
let lastTimestamp; // The timestamp of the previous requestAnimationFrame cyclelet heroX; // Changes when moving forward
let heroY; // Only changes when falling
let sceneOffset; // Moves the whole gamelet platforms = [];
let sticks = [];
let trees = [];// Todo: Save high score to localStorage (?)let score = 0;// Configuration
const canvasWidth = 375;
const canvasHeight = 375;
const platformHeight = 100;
const heroDistanceFromEdge = 10; // While waiting
const paddingX = 100; // The waiting position of the hero in from the original canvas size
const perfectAreaSize = 10;// The background moves slower than the hero
const backgroundSpeedMultiplier = 0.2;const hill1BaseHeight = 100;
const hill1Amplitude = 10;
const hill1Stretch = 1;
const hill2BaseHeight = 70;
const hill2Amplitude = 20;
const hill2Stretch = 0.5;const stretchingSpeed = 4; // Milliseconds it takes to draw a pixel
const turningSpeed = 4; // Milliseconds it takes to turn a degree
const walkingSpeed = 4;
const transitioningSpeed = 2;
const fallingSpeed = 2;const heroWidth = 17; // 24
const heroHeight = 30; // 40const canvas = document.getElementById("game");
canvas.width = window.innerWidth; // Make the Canvas full screen
canvas.height = window.innerHeight;const ctx = canvas.getContext("2d");const introductionElement = document.getElementById("introduction");
const perfectElement = document.getElementById("perfect");
const restartButton = document.getElementById("restart");
const scoreElement = document.getElementById("score");// Initialize layout
resetGame();// Resets game variables and layouts but does not start the game (game starts on keypress)
function resetGame() {// Reset game progressphase = "waiting";lastTimestamp = undefined;sceneOffset = 0;score = 0;introductionElement.style.opacity = 1;perfectElement.style.opacity = 0;restartButton.style.display = "none";scoreElement.innerText = score;// The first platform is always the same// x + w has to match paddingXplatforms = [{ x: 50, w: 50 }];generatePlatform();generatePlatform();generatePlatform();generatePlatform();sticks = [{ x: platforms[0].x + platforms[0].w, length: 0, rotation: 0 }];trees = [];generateTree();generateTree();generateTree();generateTree();generateTree();generateTree();generateTree();generateTree();generateTree();generateTree();heroX = platforms[0].x + platforms[0].w - heroDistanceFromEdge;heroY = 0;draw();
}function generateTree() {const minimumGap = 30;const maximumGap = 150;// X coordinate of the right edge of the furthest treeconst lastTree = trees[trees.length - 1];let furthestX = lastTree ? lastTree.x : 0;const x =furthestX +minimumGap +Math.floor(Math.random() * (maximumGap - minimumGap));const treeColors = ["#6D8821", "#8FAC34", "#98B333"];const color = treeColors[Math.floor(Math.random() * 3)];trees.push({ x, color });
}function generatePlatform() {const minimumGap = 40;const maximumGap = 200;const minimumWidth = 20;const maximumWidth = 100;// X coordinate of the right edge of the furthest platformconst lastPlatform = platforms[platforms.length - 1];let furthestX = lastPlatform.x + lastPlatform.w;const x =furthestX +minimumGap +Math.floor(Math.random() * (maximumGap - minimumGap));const w =minimumWidth + Math.floor(Math.random() * (maximumWidth - minimumWidth));platforms.push({ x, w });
}resetGame();// If space was pressed restart the game
window.addEventListener("keydown", function (event) {if (event.key == " ") {event.preventDefault();resetGame();return;}
});window.addEventListener("mousedown", function (event) {if (phase == "waiting") {lastTimestamp = undefined;introductionElement.style.opacity = 0;phase = "stretching";window.requestAnimationFrame(animate);}
});window.addEventListener("mouseup", function (event) {if (phase == "stretching") {phase = "turning";}
});window.addEventListener("resize", function (event) {canvas.width = window.innerWidth;canvas.height = window.innerHeight;draw();
});window.requestAnimationFrame(animate);// The main game loop
function animate(timestamp) {if (!lastTimestamp) {lastTimestamp = timestamp;window.requestAnimationFrame(animate);return;}switch (phase) {case "waiting":return; // Stop the loopcase "stretching": {sticks.last().length += (timestamp - lastTimestamp) / stretchingSpeed;break;}case "turning": {sticks.last().rotation += (timestamp - lastTimestamp) / turningSpeed;if (sticks.last().rotation > 90) {sticks.last().rotation = 90;const [nextPlatform, perfectHit] = thePlatformTheStickHits();if (nextPlatform) {// Increase scorescore += perfectHit ? 2 : 1;scoreElement.innerText = score;if (perfectHit) {perfectElement.style.opacity = 1;setTimeout(() => (perfectElement.style.opacity = 0), 1000);}generatePlatform();generateTree();generateTree();}phase = "walking";}break;}case "walking": {heroX += (timestamp - lastTimestamp) / walkingSpeed;const [nextPlatform] = thePlatformTheStickHits();if (nextPlatform) {// If hero will reach another platform then limit it's position at it's edgeconst maxHeroX = nextPlatform.x + nextPlatform.w - heroDistanceFromEdge;if (heroX > maxHeroX) {heroX = maxHeroX;phase = "transitioning";}} else {// If hero won't reach another platform then limit it's position at the end of the poleconst maxHeroX = sticks.last().x + sticks.last().length + heroWidth;if (heroX > maxHeroX) {heroX = maxHeroX;phase = "falling";}}break;}case "transitioning": {sceneOffset += (timestamp - lastTimestamp) / transitioningSpeed;const [nextPlatform] = thePlatformTheStickHits();if (sceneOffset > nextPlatform.x + nextPlatform.w - paddingX) {// Add the next stepsticks.push({x: nextPlatform.x + nextPlatform.w,length: 0,rotation: 0});phase = "waiting";}break;}case "falling": {if (sticks.last().rotation < 180)sticks.last().rotation += (timestamp - lastTimestamp) / turningSpeed;heroY += (timestamp - lastTimestamp) / fallingSpeed;const maxHeroY =platformHeight + 100 + (window.innerHeight - canvasHeight) / 2;if (heroY > maxHeroY) {restartButton.style.display = "block";return;}break;}default:throw Error("Wrong phase");}draw();window.requestAnimationFrame(animate);lastTimestamp = timestamp;
}// Returns the platform the stick hit (if it didn't hit any stick then return undefined)
function thePlatformTheStickHits() {if (sticks.last().rotation != 90)throw Error(`Stick is ${sticks.last().rotation}°`);const stickFarX = sticks.last().x + sticks.last().length;const platformTheStickHits = platforms.find((platform) => platform.x < stickFarX && stickFarX < platform.x + platform.w);// If the stick hits the perfect areaif (platformTheStickHits &&platformTheStickHits.x + platformTheStickHits.w / 2 - perfectAreaSize / 2 <stickFarX &&stickFarX <platformTheStickHits.x + platformTheStickHits.w / 2 + perfectAreaSize / 2)return [platformTheStickHits, true];return [platformTheStickHits, false];
}function draw() {ctx.save();ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);drawBackground();// Center main canvas area to the middle of the screenctx.translate((window.innerWidth - canvasWidth) / 2 - sceneOffset,(window.innerHeight - canvasHeight) / 2);// Draw scenedrawPlatforms();drawHero();drawSticks();// Restore transformationctx.restore();
}restartButton.addEventListener("click", function (event) {event.preventDefault();resetGame();restartButton.style.display = "none";
});function drawPlatforms() {platforms.forEach(({ x, w }) => {// Draw platformctx.fillStyle = "black";ctx.fillRect(x,canvasHeight - platformHeight,w,platformHeight + (window.innerHeight - canvasHeight) / 2);// Draw perfect area only if hero did not yet reach the platformif (sticks.last().x < x) {ctx.fillStyle = "red";ctx.fillRect(x + w / 2 - perfectAreaSize / 2,canvasHeight - platformHeight,perfectAreaSize,perfectAreaSize);}});
}function drawHero() {ctx.save();ctx.fillStyle = "black";ctx.translate(heroX - heroWidth / 2,heroY + canvasHeight - platformHeight - heroHeight / 2);// BodydrawRoundedRect(-heroWidth / 2,-heroHeight / 2,heroWidth,heroHeight - 4,5);// Legsconst legDistance = 5;ctx.beginPath();ctx.arc(legDistance, 11.5, 3, 0, Math.PI * 2, false);ctx.fill();ctx.beginPath();ctx.arc(-legDistance, 11.5, 3, 0, Math.PI * 2, false);ctx.fill();// Eyectx.beginPath();ctx.fillStyle = "white";ctx.arc(5, -7, 3, 0, Math.PI * 2, false);ctx.fill();// Bandctx.fillStyle = "red";ctx.fillRect(-heroWidth / 2 - 1, -12, heroWidth + 2, 4.5);ctx.beginPath();ctx.moveTo(-9, -14.5);ctx.lineTo(-17, -18.5);ctx.lineTo(-14, -8.5);ctx.fill();ctx.beginPath();ctx.moveTo(-10, -10.5);ctx.lineTo(-15, -3.5);ctx.lineTo(-5, -7);ctx.fill();ctx.restore();
}function drawRoundedRect(x, y, width, height, radius) {ctx.beginPath();ctx.moveTo(x, y + radius);ctx.lineTo(x, y + height - radius);ctx.arcTo(x, y + height, x + radius, y + height, radius);ctx.lineTo(x + width - radius, y + height);ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);ctx.lineTo(x + width, y + radius);ctx.arcTo(x + width, y, x + width - radius, y, radius);ctx.lineTo(x + radius, y);ctx.arcTo(x, y, x, y + radius, radius);ctx.fill();
}function drawSticks() {sticks.forEach((stick) => {ctx.save();// Move the anchor point to the start of the stick and rotatectx.translate(stick.x, canvasHeight - platformHeight);ctx.rotate((Math.PI / 180) * stick.rotation);// Draw stickctx.beginPath();ctx.lineWidth = 2;ctx.moveTo(0, 0);ctx.lineTo(0, -stick.length);ctx.stroke();// Restore transformationsctx.restore();});
}function drawBackground() {// Draw skyvar gradient = ctx.createLinearGradient(0, 0, 0, window.innerHeight);gradient.addColorStop(0, "#BBD691");gradient.addColorStop(1, "#FEF1E1");ctx.fillStyle = gradient;ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);// Draw hillsdrawHill(hill1BaseHeight, hill1Amplitude, hill1Stretch, "#95C629");drawHill(hill2BaseHeight, hill2Amplitude, hill2Stretch, "#659F1C");// Draw treestrees.forEach((tree) => drawTree(tree.x, tree.color));
}// A hill is a shape under a stretched out sinus wave
function drawHill(baseHeight, amplitude, stretch, color) {ctx.beginPath();ctx.moveTo(0, window.innerHeight);ctx.lineTo(0, getHillY(0, baseHeight, amplitude, stretch));for (let i = 0; i < window.innerWidth; i++) {ctx.lineTo(i, getHillY(i, baseHeight, amplitude, stretch));}ctx.lineTo(window.innerWidth, window.innerHeight);ctx.fillStyle = color;ctx.fill();
}function drawTree(x, color) {ctx.save();ctx.translate((-sceneOffset * backgroundSpeedMultiplier + x) * hill1Stretch,getTreeY(x, hill1BaseHeight, hill1Amplitude));const treeTrunkHeight = 5;const treeTrunkWidth = 2;const treeCrownHeight = 25;const treeCrownWidth = 10;// Draw trunkctx.fillStyle = "#7D833C";ctx.fillRect(-treeTrunkWidth / 2,-treeTrunkHeight,treeTrunkWidth,treeTrunkHeight);// Draw crownctx.beginPath();ctx.moveTo(-treeCrownWidth / 2, -treeTrunkHeight);ctx.lineTo(0, -(treeTrunkHeight + treeCrownHeight));ctx.lineTo(treeCrownWidth / 2, -treeTrunkHeight);ctx.fillStyle = color;ctx.fill();ctx.restore();
}function getHillY(windowX, baseHeight, amplitude, stretch) {const sineBaseY = window.innerHeight - baseHeight;return (Math.sinus((sceneOffset * backgroundSpeedMultiplier + windowX) * stretch) *amplitude +sineBaseY);
}function getTreeY(x, baseHeight, amplitude) {const sineBaseY = window.innerHeight - baseHeight;return Math.sinus(x) * amplitude + sineBaseY;
}

赶紧跑起来试试吧!!!!

分享一个好玩的JS小游戏相关推荐

  1. 分享一个蛋疼的俄罗斯方块小游戏

    分享一个蛋疼的俄罗斯方块小游戏 空间 转载请注明出处.http://www.cnblogs.com/dave_cn/ 我在Ubuntu 10.04下测试过,可以正常运行.不过界面让人蛋疼. 代码用到了 ...

  2. 安利一个好玩的JS编程游戏—warriorjs

    今天在Chrome的掘金插件上出现了一个好玩的项目-warriorjs.它的简介是这么写的: " warriorjs是一个采用JavaScript开发的游戏,用于学习JavaScript和人 ...

  3. 分享一个c++连线小游戏

    前言 想看正文的请跳过.热衷小游戏的我又回来了!上次准备给飞机大战小游戏升级,发现写的一坨,难维护拓展,吸取上次教训,这次的清爽多啦!(抽时间把飞机大战重写,再加上联机功能吧),分享给感兴趣的小伙伴, ...

  4. 一个好玩的编程小游戏——埋伏行动

    题目: 我叫王大锤,是一名特工.我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺.和我一起行动的还有另外两名特工. 我们在字节跳动大街的N个建筑中选定3个埋伏地点. 为了相互照应,我们决定相 ...

  5. 一个好玩的编程小游戏—— 母牛生小牛

    题目: 母牛从3~7岁初每年会生产1头小母牛,10岁后死亡(10岁任然存活),假设初始有一头刚出生的母牛,请问第n年有多少头母牛?(年从第一年开始计数) 注:第三年初会出生 第一头母牛,故第三年有两头 ...

  6. 【JavaScript】(一)解读一个js小游戏

    [前言] 离公司近的好处,就在于每天都能利用自己的下班时间,更好地查漏补缺,充实技能. 最近小编做的项目需要写js偏多,而我不擅长写前端js,利用工作之余,开始学习,下面通过一个成型的js小游戏,开始 ...

  7. 原生JS实现一个简单的打字小游戏

    原生JS实现一个简单的打字小游戏 利用javascript制作一个简单的打字小游戏 之前写了一个贪吃蛇小游戏好像反响不错 今天我来写一个比贪吃蛇更low更简单的打字小游戏 打字小游戏原理 接下来咋们直 ...

  8. 是男人就要坚持30秒:原生JS小游戏

    在继之前完成的数个JavaScript demo后,我发现我还没有写过JS小游戏,这次呢,我就来分享一个,非常经典的"是男人就要坚持30s"的小游戏.当然我肯定 is a man, ...

  9. js小游戏之经典炸弹人(1)--地图实现

    前段时间,写了一款js小游戏--经典炸弹人,因为这是第一次写游戏,对很多东西都不是很熟悉.于是,疯狂的上网找资料,结果发现,关于经典炸弹人的js资料少的可怜.所以,很是头疼了一段时间.在写完经典炸弹人 ...

最新文章

  1. springboot视图解析器
  2. 微信AI全面放开NLP能力
  3. linux python保存mp4
  4. 个人比较喜欢的JS网页跳转传值
  5. 算法 --- 删除数组中重复项
  6. 尽早查看针对Java 11的功能
  7. (48)FPGA三态多驱动(tri型)
  8. C语言1379最小公倍数,求最小公倍数的三种方法
  9. Quick Startup(电脑开机启动项管理软件)官方中文版V5.20.1.168 | 如何管理电脑开机启动项
  10. jpg格式图片怎么压缩?jpg图片如何压缩到最小?
  11. 新的我们、新的梦想、新的目标、新的未来 —— 44期开班贴
  12. chromium目录下各个dll的作用
  13. js实现-商城分类导航效果
  14. python使用背景图片做词云图
  15. 电脑用户须知 忘记分级审查密码怎么办(转)
  16. 不料官方降千元 经销商囤iPad被套急甩货
  17. node后台生成srt字幕文件
  18. 网文快捕保存网页并导出chm(360浏览器)
  19. Python三角形问题(计算周长与面积)
  20. Spark: py4j.protocol.Py4JJavaError: An error occurred while calling o91.showString.

热门文章

  1. 获取iPhone本机IP地址新方法
  2. 云计算中的计算虚拟化的发展史
  3. 创业公司打造顶级团队的七个方法
  4. springboot+vue租房网站(源码+文档)
  5. 电商运营个人简历范文
  6. java实现姓名按首字母排序
  7. JAVAWEB开发之Session的追踪创建和销毁、JSP具体解释(指令,标签,内置对象,动作即转发和包括)、JavaBean及内省技术以及EL表达式获取内容的使用...
  8. Endnote使用中的问题总结
  9. Android O新特性-Google Play Protect
  10. 人工智能研究中心快递柜——源码部署及分析综述