diff --git a/h5/snake/core/control.js b/h5/snake/core/control.js index 1dec00d..1fb6b28 100644 --- a/h5/snake/core/control.js +++ b/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; } diff --git a/h5/snake/core/model.js b/h5/snake/core/model.js index 83285e8..5a8958a 100644 --- a/h5/snake/core/model.js +++ b/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 diff --git a/h5/snake/core/view.js b/h5/snake/core/view.js index 09b9e81..b65b620 100644 --- a/h5/snake/core/view.js +++ b/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); + } } } diff --git a/h5/snake/eye.png b/h5/snake/eye.png new file mode 100644 index 0000000..4183681 Binary files /dev/null and b/h5/snake/eye.png differ diff --git a/h5/snake/font.ttf b/h5/snake/font.ttf new file mode 100644 index 0000000..3729151 Binary files /dev/null and b/h5/snake/font.ttf differ diff --git a/h5/snake/lib/utils/timer.js b/h5/snake/lib/utils/timer.js index 8816e25..48b9bac 100644 --- a/h5/snake/lib/utils/timer.js +++ b/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; diff --git a/h5/snake/mouth.png b/h5/snake/mouth.png new file mode 100644 index 0000000..172da37 Binary files /dev/null and b/h5/snake/mouth.png differ diff --git a/h5/snake/snake.css b/h5/snake/snake.css index e9b5903..898955a 100644 --- a/h5/snake/snake.css +++ b/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; +} diff --git a/h5/snake/snake.js b/h5/snake/snake.js index 9de88d8..3891e43 100644 --- a/h5/snake/snake.js +++ b/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, diff --git a/h5/snake/星星.png b/h5/snake/stars.png similarity index 100% rename from h5/snake/星星.png rename to h5/snake/stars.png diff --git a/h5/snake/tear.png b/h5/snake/tear.png new file mode 100644 index 0000000..cd9a0d4 Binary files /dev/null and b/h5/snake/tear.png differ diff --git a/h5/snake/蛇-原始状态.png b/h5/snake/蛇-原始状态.png deleted file mode 100644 index 0cd7ab3..0000000 Binary files a/h5/snake/蛇-原始状态.png and /dev/null differ diff --git a/h5/snake/蛇-张嘴.png b/h5/snake/蛇-张嘴.png deleted file mode 100644 index 22db8ff..0000000 Binary files a/h5/snake/蛇-张嘴.png and /dev/null differ diff --git a/h5/snake/蛇-张嘴一半.png b/h5/snake/蛇-张嘴一半.png deleted file mode 100644 index 39fe5ef..0000000 Binary files a/h5/snake/蛇-张嘴一半.png and /dev/null differ diff --git a/h5/snake/蛇-闭眼哭泣.png b/h5/snake/蛇-闭眼哭泣.png deleted file mode 100644 index b5b6ddd..0000000 Binary files a/h5/snake/蛇-闭眼哭泣.png and /dev/null differ diff --git a/h5/snake/蛇整体效果.png b/h5/snake/蛇整体效果.png deleted file mode 100644 index 0682ab0..0000000 Binary files a/h5/snake/蛇整体效果.png and /dev/null differ diff --git a/package.json b/package.json index 6eb4caf..998b059 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "main": "index.js", "license": "MIT", "dependencies": { + "@tweenjs/tween.js": "18", "phaser": "^3.24.1", "vue": "^2.6.12" }