阅读需知
这个游戏是给小白练手的,大神请饶命
只有一条蛇,别说我骗你
目的是为了练习 JS + Canvas 的逻辑
代码虽然不难,但由于本人能力有限,所以计算过程还是有一丢烧脑,所以 … 谁抄咬谁,转载啥的,注明出处
效果
蛇的小眼睛看到了吗,很 Q 有没有
代码如下
html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
* {
margin: 0;
padding: 0;
}
.btn {
width: 250px;
height: 100px;
line-height: 100px;
text-align: center;
position: fixed;
font-size: 30px;
color: #fff;
left: calc((100vw - 200px) / 2);
top: calc((100vh - 200px) / 2);
cursor: pointer;
background: #ff2255;
opacity: .8;
border-radius: 8px;
box-shadow: 0 0 5px #ff0022;
}
style>
head>
<body>
<div class="btn">开始游戏div>
<canvas id="cv">canvas>
<script src="snake.js">script>
body>
html>
JS
const btn = document.getElementsByClassName('btn')[0];
const cv = document.getElementById('cv');
const ctx = cv.getContext('2d');
const PI = Math.PI;
const net = 20;
const dr = 80;
const dcr = 50;
const initNum = 5;
const snakeR = 20;
const snakeV = 2;
const snakeDis = 15;
const foodR = 3;
const initFoodNum = 200;
const eyesDeg = PI / 3;
const eyesR = 4;
let ec = getColor();
let snakeHasFoodNum = 0;
let snakeDeg = getDeg();
let snakeArr = [];
let pointArr = [];
let foodArr = [];
let cw, ch;
cv.width = cw = window.innerWidth;
cv.height = ch = window.innerHeight;
const controlX = cw - 50 - dr, controlY = ch - 50 - dr;
let centerX = controlX, centerY = controlY;
const controlBg = `rgba(80, 80, 80, .2)`, centerBg = `rgba(0, 0, 0, .2)`;
let timer;
class Snake {
constructor(x, y) {
this.x = x;
this.y = y;
this.bg = getColor();
}
draw() {
drawCircle(this.x, this.y, this.bg, snakeR);
}
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Food {
constructor() {
this.x = rn(foodR, cw - foodR);
this.y = rn(foodR, ch - foodR);
this.bg = getColor();
}
draw() {
drawCircle(this.x, this.y, this.bg, foodR);
}
}
function rn(x, y) {
return Math.round(Math.random() * (y - x) + x);
}
function getColor() {
return `rgb(${rn(80, 255)},${rn(80, 255)},${rn(80, 255)})`;
}
function getDeg() {
return (Math.random() * PI).toFixed(2) - 0;
}
function drawCircle(x, y, bg, r) {
ctx.beginPath();
ctx.fillStyle = bg;
ctx.arc(x, y, r, 0, 2 * PI);
ctx.fill();
}
function draw(arr) {
for(let item of arr) {
item.draw();
}
}
function drawControl() {
drawCircle(controlX, controlY, controlBg, dr);
drawCircle(centerX, centerY, centerBg, dcr);
}
function drawNet() {
const xc = cw / net, yc = ch / net;
const c = xc > yc ? xc : yc;
ctx.beginPath();
ctx.lineWidth = .1;
ctx.fillStyle = '#aaa';
for(let i = 0; i < c; i++) {
if (i < xc) {
ctx.moveTo(i * net, 0);
ctx.lineTo(i * net, ch);
}
if (i < yc) {
ctx.moveTo(0, i * net);
ctx.lineTo(cw, i * net);
}
}
ctx.stroke();
}
function initBody() {
let initX = cw / 2, initY = ch / 2;
const sh = new Snake(initX, initY);
snakeArr.push(sh);
for(let i = 1; i < initNum; i++) {
let sb = new Snake();
snakeArr.push(sb);
}
}
function snakeEyes() {
let sh = snakeArr[0];
let
x = Math.cos(eyesDeg) * snakeR,
y1 = Math.sin(eyesDeg) * snakeR,
y2 = -y1;
ctx.save();
ctx.translate(sh.x, sh.y);
ctx.rotate(snakeDeg);
drawCircle(x, y1, ec, eyesR);
drawCircle(x, y2, ec, eyesR);
ctx.restore();
}
function drawSnake() {
for(let i = snakeArr.length - 1; i >= 0; i--) {
snakeArr[i].draw();
}
snakeEyes();
}
function changePosition() {
let t = 0;
for(let i = 0; i < snakeArr.length; i++) {
if(!pointArr[t]) break;
snakeArr[i].x = pointArr[t].x;
snakeArr[i].y = pointArr[t].y;
t += snakeDis;
}
}
function pointSplice() {
let needPointNum = snakeArr.length * snakeDis;
let pointNum = pointArr.length;
if(pointNum > needPointNum) {
pointArr.splice(needPointNum, pointNum - needPointNum);
}
}
function snakeMove() {
const vx = snakeV * Math.cos(snakeDeg),
vy = snakeV * Math.sin(snakeDeg);
const oldHead = pointArr[0] ? pointArr[0] : snakeArr[0];
const nx = oldHead.x + vx,
ny = oldHead.y + vy;
pointArr.unshift(new Point(nx, ny));
}
function snakeDebris() {
for(var i = 1; i < snakeArr.length; i++) {
let x = snakeArr[i].x, y = snakeArr[i].y;
let _x = x + rn(-30, 30),
_y = y + rn(-30, 30);
snakeArr[i].x = _x < snakeR ? snakeR : _x > cw - snakeR ? cw - snakeR : _x;
snakeArr[i].y = _y < snakeR ? snakeR : _y > ch - snakeR ? ch - snakeR : _y;
}
}
function initFood() {
for(let i = 0; i < initFoodNum; i++) {
let food = new Food();
foodArr.push(food);
}
}
function controlDirection(x, y) {
const a = x - controlX, b = y - controlY;
const dis = Math.sqrt(a * a + b * b);
centerX = dis > dr ? controlX : x;
centerY = dis > dr ? controlY : y;
if(dis > dr) {
cv.onmousemove = null;
}
let sindeg = Math.abs(b / dis);
if(a > 0 && b > 0) snakeDeg = Math.asin(sindeg);
if(a < 0 && b > 0) snakeDeg = PI - Math.asin(sindeg);
if(a < 0 && b < 0) snakeDeg = PI + Math.asin(sindeg);
if(a > 0 && b < 0) snakeDeg = -Math.asin(sindeg);
}
function hit() {
const sh = snakeArr[0];
for(let i = 0; i < foodArr.length; i++) {
let a = sh.x - foodArr[i].x, b = sh.y - foodArr[i].y;
let dis = Math.sqrt(a * a + b * b);
if(dis <= snakeR + foodR) {
foodArr.splice(i, 1);
i--;
snakeHasFoodNum++;
if(snakeHasFoodNum === 10) {
snakeArr.push(new Snake());
snakeHasFoodNum = 0;
}
}
}
}
function dieJudge() {
let sh = snakeArr[0];
if(sh.x < snakeR || sh.x > cw - snakeR || sh.y < snakeR || sh.y > ch - snakeR) {
gameOver();
return true;
}
}
function gameOver() {
btn.style.display = 'block';
clearInterval(timer);
snakeHasFoodNum = 0;
snakeDebris();
}
function initAll() {
initFood();
draw(foodArr);
drawControl();
drawNet();
}
initAll();
btn.onclick = function() {
btn.style.display = 'none';
pointArr = [];
snakeArr = [];
foodArr = [];
initBody();
initFood();
ec = getColor();
snakeDeg = getDeg();
let hz = 0;
timer = setInterval(function() {
hz++;
ctx.clearRect(0, 0, cw, ch);
drawNet();
draw(foodArr);
dieJudge();
drawSnake();
drawControl();
changePosition();
hit();
pointSplice();
if(hz % 1 === 0) {
snakeMove();
}
if(hz % 50 === 0) {
foodArr.push(new Food());
}
}, 10);
};
cv.onmousedown = function() {
cv.onmousemove = function(ev) {
let e = ev || event;
let x = e.offsetX, y = e.offsetY;
controlDirection(x, y);
};
};
cv.onmouseup = function() {
centerX = controlX;
centerY = controlY;
cv.onmousemove = null;
};
document.onselectstart = function() {
return false;
};