Browse Source

贪吃蛇

master
jiannibang 6 years ago
parent
commit
0ed5e00980
  1. 8
      h5/snake/core/control.js
  2. 4
      h5/snake/core/model.js
  3. 461
      h5/snake/core/view.js
  4. BIN
      h5/snake/eye.png
  5. BIN
      h5/snake/font.ttf
  6. 301
      h5/snake/lib/utils/timer.js
  7. BIN
      h5/snake/mouth.png
  8. 14
      h5/snake/snake.css
  9. 22
      h5/snake/snake.js
  10. 0
      h5/snake/stars.png
  11. BIN
      h5/snake/tear.png
  12. BIN
      h5/snake/蛇-原始状态.png
  13. BIN
      h5/snake/蛇-张嘴.png
  14. BIN
      h5/snake/蛇-张嘴一半.png
  15. BIN
      h5/snake/蛇-闭眼哭泣.png
  16. BIN
      h5/snake/蛇整体效果.png
  17. 1
      package.json

8
h5/snake/core/control.js

@ -31,8 +31,10 @@ export default class control {
if (this.speedScalar !== value) {
this.speedScalar = value;
this.interval = 300 / this.speedScalar;
// 更新 timer
timer.set(this.intervalID, { delay: this.interval });
this.view.setInterval(this.interval, timer);
}
},
});
@ -192,7 +194,11 @@ export default class control {
// gameover
gameover(type) {
if (this.GAMEOVER) return;
this.event.dispatch("gameover", type);
this.view.renderLose();
setTimeout(() => {
this.event.dispatch("gameover", type);
}, 500);
this.pause();
this.GAMEOVER = true;
}

4
h5/snake/core/model.js

@ -76,7 +76,9 @@ export default class model {
down: row < config.row - 1 ? i + config.column : -1,
};
}
this.snake.push(
Math.floor(config.column / 2) + Math.floor(config.row / 2) * config.column
);
// 初始蛇的长度
while (this.snake.length < config.min) {
let index = this.snake.length

461
h5/snake/core/view.js

@ -5,22 +5,15 @@
// 链表类
import Chain from "../lib/utils/Chain";
import food from "../food.png";
import TWEEN from "@tweenjs/tween.js";
import eye from "../eye.png";
import mouth from "../mouth.png";
import tear from "../tear.png";
import stars from "../stars.png";
const startColor = 0xfec321;
const endColor = 0xe29b01;
const rotate = (cx, cy, point, rad) => {
const { x, y } = point;
let cos = Math.cos(rad),
sin = Math.sin(rad),
nx = cos * (x - cx) + sin * (y - cy) + cx,
ny = cos * (y - cy) - sin * (x - cx) + cy;
point.x = nx;
point.y = ny;
};
const startColor = "#fec321";
const endColor = "#e29b01";
const getDir = (x1, y1, x2, y2) => {
return x2 > x1 ? "right" : x2 < x1 ? "left" : y2 > y1 ? "bottom" : "top";
};
// view 类
export default class view {
// 构造函数
@ -44,6 +37,14 @@ export default class view {
this.ctx = ctx;
this.foodImg = new Image(120, 120);
this.foodImg.src = food;
this.eyeImg = new Image(143, 65);
this.eyeImg.src = eye;
this.mouthImg = new Image(131, 67);
this.mouthImg.src = mouth;
this.tearImg = new Image(129, 154);
this.tearImg.src = tear;
this.starsImg = new Image(224, 162);
this.starsImg.src = stars;
}
// 初始化
@ -62,16 +63,19 @@ export default class view {
// 食物
this.food = {};
this.food.visible = false;
this.lastFood = null;
// 通过 model.zone 创建一张快速定位表
this.createQuickMap(this.data.zone);
this.tail = null;
// 同步 model 的初始数据
for (let { data } of this.data.snake) {
this.snake.push(data);
}
}
setInterval(interval, timer) {
this.interval = interval;
this.timer = timer;
}
destroy() {
this.snake.clean();
}
@ -97,7 +101,7 @@ export default class view {
this.food.dirty++;
this.food.clearDirty++;
const [left, top] = this.getPosition(index);
Object.assign(this.food, { left, top });
Object.assign(this.food, { index, left, top });
}
// ticker update
@ -108,7 +112,11 @@ export default class view {
// 状态更新
update(data) {
// 食物更新
this.food !== data.food && this.feed(data.food);
if (this.food.index !== data.food) {
this.lastFood = this.food.index;
this.feed(data.food);
}
this.tail = this.snake.last();
this.updateDelta(data.snake);
}
@ -168,7 +176,7 @@ export default class view {
}
// 不匹配
else {
snakeB.pop();
this.tail = snakeB.pop();
}
}
// 尾指针未匹配上,走 catch 通道
@ -189,226 +197,239 @@ export default class view {
}
}
renderFood() {
renderFood(t) {
let { width, height } = this.config.size;
const ratio = Math.abs(t - 0.5) * 0.5 + 1;
if (this.food && this.food.visible) {
this.ctx.drawImage(
this.foodImg,
this.food.left,
this.food.top,
width,
height
this.food.left - (width * (ratio - 1)) / 2,
this.food.top - (height * (ratio - 1)) / 2,
width * ratio,
height * ratio
);
}
}
drawHeadTail(x, y, dir, index, length) {
const { ctx } = this;
renderSnake(t, lose) {
let { length } = this.snake;
let { width, height } = this.config.size;
const sWidth = (width / 120) * 90;
const r = sWidth / 2;
const gap = (width - sWidth) / 2;
const a = { x, y: y + height };
const b = { x: x + gap, y: a.y };
const c = { x: x + width / 2, y: a.y };
const d = { x: x + gap + sWidth, y: a.y };
const e = { x: x + width, y: a.y };
const f = { x: b.x, y: y + r };
const g = { x: b.x, y };
const h = { x: c.x, y };
const i = { x: d.x, y };
const j = { x: d.x, y: f.y };
const k = { x: x + width / 2, y: y - index * height };
const l = { x: k.x, y: y + (length - index) * height };
let rad =
dir === "bottom"
? 0
: dir === "top"
? Math.PI
: dir === "left"
? -Math.PI / 2
: Math.PI / 2;
if (index) rad += Math.PI;
[a, b, c, d, e, f, g, h, i, j, k, l].forEach((point) =>
rotate(x + width / 2, y + height / 2, point, rad)
);
if (lose) t = 1 - lose;
const list = [];
for (let body of this.snake) {
list.push(body);
}
let lastFoodPos =
this.lastFood === null ? this.lastFood : this.getPosition(this.lastFood);
lastFoodPos = lastFoodPos
? { x: lastFoodPos[0] + width / 2, y: lastFoodPos[1] + height / 2 }
: lastFoodPos;
const points = list.map(({ data }) => {
const [x, y] = this.getPosition(data);
return { x: x + width / 2, y: y + height / 2 };
});
const { ctx } = this;
// k
// |
// |
// |
// g---h---i
// | / \ |
// f j
// | |
// a-b---c---d-e
// |
// |
// |
// l
const lineWidth = (width / 120) * 90;
const gradient = ctx.createLinearGradient(k.x, k.y, l.x, l.y);
gradient.addColorStop(0, "#" + startColor.toString(16));
gradient.addColorStop(1, "#" + endColor.toString(16));
ctx.fillStyle = gradient;
ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(b.x, b.y);
ctx.lineTo(f.x, f.y);
ctx.arcTo(g.x, g.y, h.x, h.y, r);
ctx.arcTo(i.x, i.y, j.x, j.y, r);
ctx.lineTo(d.x, d.y);
ctx.closePath();
ctx.fill();
ctx.stroke();
let pre = null;
let headPos = null;
let headRad = null;
if (this.tail !== null) {
let [x, y] = this.getPosition(this.tail.data);
x = x + width / 2;
y = y + height / 2;
points.push({ x, y });
length++;
}
points.forEach(({ x, y }, i) => {
if (!pre) {
pre = { x, y };
} else {
const start =
i === 1 ? { x: (pre.x - x) * t + x, y: (pre.y - y) * t + y } : pre;
const end =
i === length - 1
? {
x: (x - pre.x) * (1 - t) + pre.x,
y: (y - pre.y) * (1 - t) + pre.y,
}
: { x, y };
const isV = pre.x === x;
const gStart = {
x: isV ? x : x - i * (x - pre.x),
y: isV ? y - i * (y - pre.y) : y,
};
const gEnd = {
x: isV ? x : x + (length - i) * (x - pre.x),
y: isV ? y + (length - i) * (y - pre.y) : y,
};
const gradient = ctx.createLinearGradient(
gStart.x,
gStart.y,
gEnd.x,
gEnd.y
);
gradient.addColorStop(0, startColor);
gradient.addColorStop(1, endColor);
ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineCap = "round";
ctx.lineWidth = lineWidth;
ctx.lineTo(end.x, end.y);
ctx.stroke();
ctx.closePath();
if (
lastFoodPos &&
Math.abs(lastFoodPos.x - x) < 1 &&
Math.abs(lastFoodPos.y - y) < 1
) {
if (i === length - 2) this.lastFood = null;
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(
x,
y,
(lineWidth + (width - lineWidth) * (1 - i / length)) / 2,
0,
Math.PI * 2
);
ctx.fill();
}
if (i === 1) {
headPos = start;
headRad = Math.atan2(end.y - pre.y, end.x - pre.x) - Math.PI / 2;
}
pre = { x, y };
}
});
lose
? this.renderTear(headPos, headRad, lose)
: this.renderEye(headPos, headRad);
const foodPos = {
x: this.food.left + width / 2,
y: this.food.top + height / 2,
};
const foodInRange =
Math.abs(foodPos.x - headPos.x) <= width * 2 &&
Math.abs(foodPos.y - headPos.y) <= width * 2;
if (foodInRange && !lose) this.renderMouth(headPos, headRad, t);
}
drawRec(x, y, dir, i, length) {
const { ctx } = this;
renderMouth(pos, rad, t) {
t = 1;
let { width, height } = this.config.size;
const sWidth = (width / 120) * 90;
const gap = (width - sWidth) / 2;
const a = { x: x + gap, y: y + height };
const b = { x: a.x, y };
const c = { x: x + gap + sWidth, y };
const d = { x: c.x, y: a.y };
const e = { x: x + width / 2, y: y - i * height };
const f = { x: e.x, y: y + (length - i) * height };
let rad =
dir === "bottom"
? 0
: dir === "top"
? Math.PI
: dir === "left"
? -Math.PI / 2
: Math.PI / 2;
[a, b, c, d, e, f].forEach((point) =>
rotate(x + width / 2, y + height / 2, point, rad)
const r = (width / 120) * 90;
const { ctx } = this;
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(rad);
const imgHeight = (width / this.mouthImg.width) * this.mouthImg.height;
ctx.drawImage(
this.mouthImg,
-width / 2,
-height / 2 + imgHeight / 4,
width,
imgHeight * t
);
// e
// |
// b---c
// | |
// a---d
// |
// f
const gradient = ctx.createLinearGradient(e.x, e.y, f.x, f.y);
gradient.addColorStop(0, "#" + startColor.toString(16));
gradient.addColorStop(1, "#" + endColor.toString(16));
ctx.fillStyle = gradient;
ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.lineTo(c.x, c.y);
ctx.lineTo(d.x, d.y);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
}
drawCorner(x, y, dir, i, length) {
renderTear(pos, rad, t) {
let { width } = this.config.size;
const r = ((width / 120) * 90) / 2;
const { ctx } = this;
let { width, height } = this.config.size;
const sWidth = (width / 120) * 90;
const gap = (width - sWidth) / 2;
const sqrt2 = width / 2 / Math.sqrt(2);
const a = { x: x + gap, y: y + height };
const b = { x: x + gap + sWidth, y: a.y };
const c = { x: b.x, y: y + gap + sWidth };
const d = { x: x + width, y: c.y };
const e = { x: d.x, y: y + gap };
const f = { x: a.x, y: e.y };
let g = {
x: x + width / 2 + ((length - i) * 2 + 1) * sqrt2,
y: y + height / 2 + ((length - i) * 2 + 1) * sqrt2,
};
let h = {
x: x + width / 2 - (i * 2 + 1) * sqrt2,
y: y + height / 2 - (i * 2 + 1) * sqrt2,
};
// if (["rightbottom", "lefttop", "topright", "bottomleft"].includes(dir)) {
// let tmp = h;
// h = g;
// g = tmp;
// }
let rad =
dir === "bottomright" || dir === "rightbottom"
? 0
: dir === "topleft" || dir === "lefttop"
? Math.PI
: dir === "righttop" || dir === "topright"
? Math.PI / 2
: -Math.PI / 2;
[a, b, c, d, e, f, g, h].forEach((point) =>
rotate(x + width / 2, y + height / 2, point, rad)
{
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(rad);
const displayWidth = (width / 120) * this.tearImg.width;
const displayHeight = (width / 120) * this.tearImg.height;
ctx.drawImage(
this.tearImg,
-displayWidth / 2,
-displayHeight / 2 + (width / 120) * 100 - r,
displayWidth,
displayHeight
);
ctx.restore();
}
{
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(rad);
const displayWidth = (width / 120) * this.starsImg.width * t;
const displayHeight = (width / 120) * this.starsImg.height * t;
ctx.drawImage(
this.starsImg,
-displayWidth / 2,
-displayHeight / 2 + (width / 120) * 100 - r,
displayWidth,
displayHeight
);
ctx.restore();
}
}
renderEye(pos, rad) {
let { width } = this.config.size;
const r = ((width / 120) * 90) / 2;
const { ctx } = this;
ctx.save();
ctx.translate(pos.x, pos.y);
ctx.rotate(rad);
const displayWidth = (width / 120) * this.eyeImg.width;
const displayHeight = (width / 120) * this.eyeImg.height;
ctx.drawImage(
this.eyeImg,
-displayWidth / 2,
-displayHeight / 2 + (width / 120) * 100 - r,
displayWidth,
displayHeight
);
//
// g
// /
// -------------
// | |
// ----f-------e
// | | |
// | | c---d
// | | | |
// ----a---b----
// /
// h
//
const gradient = ctx.createLinearGradient(h.x, h.y, g.x, g.y);
gradient.addColorStop(0, "#" + startColor.toString(16));
gradient.addColorStop(1, "#" + endColor.toString(16));
ctx.fillStyle = gradient;
ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.arcTo(c.x, c.y, d.x, d.y, gap);
ctx.lineTo(e.x, e.y);
ctx.arcTo(f.x, f.y, a.x, a.y, gap + sWidth);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
}
renderSnake() {
const { length } = this.snake;
const list = [];
for (let body of this.snake) {
list.push(body);
}
list.forEach(({ data }, i) => {
const pre = list[i - 1];
const nxt = list[i + 1];
const [x, y] = this.getPosition(data);
if (i === 0) {
// head
const [xNxt, yNxt] = this.getPosition(nxt.data);
const dir = getDir(x, y, xNxt, yNxt);
this.drawHeadTail(x, y, dir, i, list.length);
} else if (i === length - 1) {
const [xPre, yPre] = this.getPosition(pre.data);
const dir = getDir(xPre, yPre, x, y);
this.drawHeadTail(x, y, dir, i, list.length);
} else {
const [xNxt, yNxt] = this.getPosition(nxt.data);
const dir1 = getDir(x, y, xNxt, yNxt);
const [xPre, yPre] = this.getPosition(pre.data);
const dir2 = getDir(xPre, yPre, x, y);
if (dir1 === dir2) {
this.drawRec(x, y, dir1, i, length);
} else {
const dir3 = getDir(x, y, xPre, yPre);
this.drawCorner(x, y, dir1 + dir3, i, length);
}
}
renderLose() {
return new Promise((resolve) => {
this.currentTween && this.currentTween.stop();
const tween = new TWEEN.Tween({ t: 0 });
tween.easing(TWEEN.Easing.Elastic.Out);
tween.to({ t: 1 }, 500);
tween.onComplete(resolve);
tween.onUpdate(({ t }) => {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.renderFood(1);
this.renderSnake(1, t);
requestAnimationFrame(() => {
TWEEN.update();
});
});
requestAnimationFrame(() => {
TWEEN.update();
});
tween.start();
});
}
// 渲染
render() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.renderFood();
this.renderSnake();
if (this.interval) {
const tween = new TWEEN.Tween({ t: 0 });
this.currentTween = tween;
tween.to({ t: 1 }, this.interval);
tween.onUpdate(({ t }) => {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.renderFood(t);
this.renderSnake(t);
requestAnimationFrame(() => {
TWEEN.update();
});
});
requestAnimationFrame(() => {
TWEEN.update();
});
tween.start();
} else {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.renderFood(0);
this.renderSnake(0);
}
}
}

BIN
h5/snake/eye.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
h5/snake/font.ttf

Binary file not shown.

301
h5/snake/lib/utils/timer.js

@ -13,164 +13,163 @@
*/
class Timer {
// 构造函数
constructor() {
// 暂停状态 - 这是个公共状态,由外部 Ticker 决定。
this.paused = true;
// 队列
this.queue = new Map();
// 正在使用 timer 的 RAF
this.usingRAF = false;
// useRAF 触发器
Reflect.defineProperty(this, "useRAF", {
set: function(value) {
Reflect.set(this, "usingRAF", value);
value ? Timer.RAF.enable() : Timer.RAF.disable();
}
});
}
// setTimeout 的实现
setTimeout(fn, delay, id = Symbol("timeoutID")) {
// 存入队列
this.queue.set(id, {
fn: fn,
type: 0,
paused: 0,
elapsed: 0,
delay: delay
});
return id;
}
// clearTimeout
clearTimeout(id) {
this.delete(id);
}
// setInterval 的实现
setInterval(fn, delay, id = Symbol("intervalID")) {
// 存入队列
this.queue.set(id, {
fn: fn,
type: 1,
paused: 0,
elapsed: 0,
delay: delay
});
return id;
}
// clearInterval
clearInterval(id) {
this.delete(id);
}
// 修改指定id的 delay/fn
set(id, config = {}) {
let item = this.queue.get(id) || {};
for(let key in config) {
item[key] = config[key];
}
}
// 删除 queue 上的成员
delete(id) {
this.queue.delete(id);
}
// 暂停指定id
pause(id) {
id === undefined ? this.pauseAll() : (this.queue.get(id).paused = 1);
}
// 恢复指定id
resume(id) {
this.play(id);
}
// 播放指定id
play(id) {
id === undefined ? this.playAll() : (this.queue.get(id).paused = 0);
}
// 清空timer
clean() {
this.queue = new Map();
}
// 暂停全部 id
pauseAll() {
this.queue.forEach((item) => item.paused = 1);
}
// 播放全部 id
playAll() {
this.queue.forEach((item) => item.paused = 0);
}
// tick
tick(delta) {
this.paused || this.updateQueue(delta);
}
// 更新 map 队列
updateQueue(delta) {
this.queue.forEach((item, id) => {
if(item.paused === 1) return;
item.elapsed += delta;
if(item.elapsed >= item.delay) {
item.fn();
item.type === 0 ? this.delete(id) : (item.elapsed = 0);
}
});
}
// 状态更新
update() {
// 第一次调用 update 时主动停用原生接口
this.useRAF = false;
// 下面是真正的 update
this.update = (paused, delta) => {
if(this.usingRAF) return;
// 播放开关
this.paused == paused || (this.paused = paused);
this.tick(delta);
}
}
// 构造函数
constructor() {
// 暂停状态 - 这是个公共状态,由外部 Ticker 决定。
this.paused = true;
// 队列
this.queue = new Map();
// 正在使用 timer 的 RAF
this.usingRAF = false;
// useRAF 触发器
Reflect.defineProperty(this, "useRAF", {
set: function (value) {
Reflect.set(this, "usingRAF", value);
value ? Timer.RAF.enable() : Timer.RAF.disable();
},
});
}
// setTimeout 的实现
setTimeout(fn, delay, id = Symbol("timeoutID")) {
// 存入队列
this.queue.set(id, {
fn: fn,
type: 0,
paused: 0,
elapsed: 0,
delay: delay,
});
return id;
}
// clearTimeout
clearTimeout(id) {
this.delete(id);
}
// setInterval 的实现
setInterval(fn, delay, id = Symbol("intervalID")) {
// 存入队列
this.queue.set(id, {
fn: fn,
type: 1,
paused: 0,
elapsed: 0,
delay: delay,
});
return id;
}
// clearInterval
clearInterval(id) {
this.delete(id);
}
// 修改指定id的 delay/fn
set(id, config = {}) {
let item = this.queue.get(id) || {};
for (let key in config) {
item[key] = config[key];
}
}
// 删除 queue 上的成员
delete(id) {
this.queue.delete(id);
}
// 暂停指定id
pause(id) {
id === undefined ? this.pauseAll() : (this.queue.get(id).paused = 1);
}
// 恢复指定id
resume(id) {
this.play(id);
}
// 播放指定id
play(id) {
id === undefined ? this.playAll() : (this.queue.get(id).paused = 0);
}
// 清空timer
clean() {
this.queue = new Map();
}
// 暂停全部 id
pauseAll() {
this.queue.forEach((item) => (item.paused = 1));
}
// 播放全部 id
playAll() {
this.queue.forEach((item) => (item.paused = 0));
}
// tick
tick(delta) {
this.paused || this.updateQueue(delta);
}
// 更新 map 队列
updateQueue(delta) {
this.queue.forEach((item, id) => {
if (item.paused === 1) return;
item.elapsed += delta;
if (item.elapsed >= item.delay) {
item.fn();
item.type === 0 ? this.delete(id) : (item.elapsed = 0);
}
});
}
// 状态更新
update() {
// 第一次调用 update 时主动停用原生接口
this.useRAF = false;
// 下面是真正的 update
this.update = (paused, delta) => {
if (this.usingRAF) return;
// 播放开关
this.paused == paused || (this.paused = paused);
this.tick(delta);
};
}
}
class AnimationFrame {
constructor() {
this.time = 0;
this.auto = this.auto.bind(this);
}
auto(elapsed) {
timer.tick(elapsed - this.time);
this.time = elapsed;
this.id = requestAnimationFrame(this.auto);
}
enable() {
timer.paused = false;
this.id = requestAnimationFrame(this.auto);
}
disable() {
cancelAnimationFrame(this.id);
}
class AnimationFrame {
constructor() {
this.time = 0;
this.auto = this.auto.bind(this);
}
auto(elapsed) {
timer.tick(elapsed - this.time);
this.time = elapsed;
this.id = requestAnimationFrame(this.auto);
}
enable() {
timer.paused = false;
this.id = requestAnimationFrame(this.auto);
}
disable() {
cancelAnimationFrame(this.id);
}
}
// 原生RAF
Timer.RAF = new AnimationFrame();
Timer.RAF = new AnimationFrame();
// 对外接口
let timer = new Timer();
let timer = new Timer();
// 默认使用原生 RAF
timer.useRAF = true;
timer.useRAF = true;
// 导出timer
export default timer;
export default timer;

BIN
h5/snake/mouth.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

14
h5/snake/snake.css

@ -1,3 +1,7 @@
@font-face {
font-family: "game";
src: url("font.ttf") format("truetype");
}
.snake {
position: relative;
width: 100%;
@ -15,6 +19,9 @@
background-image: url(./buttons.png);
background-size: cover;
}
.snake > .buttons:active {
opacity: 0.8;
}
.snake > .modal {
display: none;
position: absolute;
@ -41,3 +48,10 @@
background-image: url(./modal.png);
background-size: cover;
}
.snake > .score {
position: absolute;
font-family: game;
top: 3.5%;
left: 25%;
color: #fff;
}

22
h5/snake/snake.js

@ -32,6 +32,13 @@ export default class SnakeClass extends SnakeControl {
containerWidth: width,
});
const score = document.createElement("div");
score.className = "score";
score.textContent = 0;
score.style.fontSize = (container.clientWidth / 2160) * 120 + "px";
score.style.lineHeight = (container.clientWidth / 2160) * 95 + "px";
console.log(score.style);
bg.appendChild(score);
const modal = document.createElement("div");
modal.className = "modal show";
modal.addEventListener("click", (e) => {
@ -44,9 +51,8 @@ export default class SnakeClass extends SnakeControl {
}
});
bg.appendChild(modal);
const min = 4;
this.init({
time: 3000000, // 总时间
width,
height,
row,
@ -55,19 +61,20 @@ export default class SnakeClass extends SnakeControl {
border: 0x414042,
color: 0x414042, // 蛇的节点颜色
food: 0x990000, // 食物的颜色
min: 2, // 初始长度
speed: 0.5, // 速度标量
min, // 初始长度
speed: 1, // 速度标量
});
this.event.on("gameover", (res) => {
onLose(res);
});
this.event.on("restart", () => {
this.showModal = true;
score.textContent = 0;
modal.className = "modal show";
});
// snakeGame.event.on("eat", (food) => {
// console.log("吃到食物,当前长度: " + snakeGame.length);
// });
this.event.on("eat", () => {
score.textContent = (this.length - min) * 10;
});
this.start();
this.addControl();
}
@ -75,7 +82,6 @@ export default class SnakeClass extends SnakeControl {
addControl() {
let controller = this.buttons;
let curDirection;
const { containerWidth } = this;
let { top, left, width, height } = controller.getBoundingClientRect(),
x = left + width / 2,
y = top + height / 2,

0
h5/snake/星星.png → h5/snake/stars.png

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
h5/snake/tear.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
h5/snake/蛇-原始状态.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
h5/snake/蛇-张嘴.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
h5/snake/蛇-张嘴一半.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
h5/snake/蛇-闭眼哭泣.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
h5/snake/蛇整体效果.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

1
package.json

@ -4,6 +4,7 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"@tweenjs/tween.js": "18",
"phaser": "^3.24.1",
"vue": "^2.6.12"
}

Loading…
Cancel
Save