fix: 修复了冰块惯性问题和死亡效果
This commit is contained in:
1 parent
47a462be01
commit
4b3dfd635b
15 files changed
+633
-38
No files matched your search
+32
-12
@@ -6,6 +6,7 @@ import { Camera } from './camera.js';
|
||||
import { Input } from './input.js';
|
||||
import { UI } from './ui.js';
|
||||
import { Save } from './save.js';
|
||||
import { Touch } from './touch.js';
|
||||
import { sfx, unlockAudio, Audio2 } from './audio.js';
|
||||
import { Editor } from './editor.js';
|
||||
import { clamp } from './util.js';
|
||||
@@ -13,6 +14,16 @@ import { clamp } from './util.js';
|
||||
const W = 800, H = 608;
|
||||
const STEP = 1000 / 60;
|
||||
|
||||
// 移动端检测:触屏设备自动启用虚拟手柄;?touch=1 强制开启,?touch=0 强制关闭(桌面调试用)
|
||||
// 判定条件:① 是移动端(存在 ontouchstart)/ 支持触屏(maxTouchPoints>0)/ 粗指针(触屏笔或手指)
|
||||
// 只有命中才给 <html> 加 .touch 类;CSS 里 html:not(.touch) #touch 会强制隐藏,桌面端永远不显示按钮
|
||||
const _qp = new URLSearchParams(location.search);
|
||||
const _force = _qp.get('touch');
|
||||
const _isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints > 0 || matchMedia('(pointer: coarse)').matches;
|
||||
const useTouch = _force === '1' || (_force !== '0' && _isTouch);
|
||||
if (useTouch) document.documentElement.classList.add('touch');
|
||||
function showTouch() { if (useTouch) Touch.show(); }
|
||||
|
||||
const canvas = document.getElementById('game');
|
||||
const ctx = canvas.getContext('2d', { alpha: false });
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
@@ -40,6 +51,7 @@ function startLevel(data, idx = -1) {
|
||||
G.deaths = 0; G.ms = 0; G.hudTick = 0;
|
||||
G.state = 'playing';
|
||||
UI.showGame();
|
||||
showTouch();
|
||||
UI.toast('');
|
||||
if (data.hint) UI.toast(data.hint);
|
||||
}
|
||||
@@ -58,9 +70,17 @@ function suicide() {
|
||||
G.player.kill();
|
||||
}
|
||||
|
||||
/** 触屏射击:仅在持枪、未死亡且游戏进行中时发射(受玩家自身冷却限制) */
|
||||
function shoot() {
|
||||
const P = G.player;
|
||||
if (G.state !== 'playing' || !P || !P.alive || !P.hasGun || P.shootCool !== 0) return;
|
||||
P.shoot();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
if (G.state !== 'playing') return;
|
||||
G.state = 'result';
|
||||
Touch.hide();
|
||||
sfx.goal();
|
||||
const apples = G.player.apples || 0;
|
||||
const total = G.level.totalApples || 0;
|
||||
@@ -76,10 +96,10 @@ function nextLevel() {
|
||||
if (G.idx >= 0 && G.idx + 1 < LEVELS.length) startLevel(LEVELS[G.idx + 1], G.idx + 1);
|
||||
else toLevels();
|
||||
}
|
||||
function toLevels() { G.state = 'menu'; UI.show('scr-levels'); }
|
||||
function toTitle() { G.state = 'menu'; UI.show('scr-title'); }
|
||||
function pause() { if (G.state === 'playing') { G.state = 'paused'; UI.show('scr-pause'); } }
|
||||
function resume() { if (G.state === 'paused') { G.state = 'playing'; UI.showGame(); } }
|
||||
function toLevels() { G.state = 'menu'; UI.show('scr-levels'); Touch.hide(); }
|
||||
function toTitle() { G.state = 'menu'; UI.show('scr-title'); Touch.hide(); }
|
||||
function pause() { if (G.state === 'playing') { G.state = 'paused'; UI.show('scr-pause'); Touch.hide(); } }
|
||||
function resume() { if (G.state === 'paused') { G.state = 'playing'; UI.showGame(); showTouch(); } }
|
||||
|
||||
// ---------------- 更新 ----------------
|
||||
function update() {
|
||||
@@ -94,7 +114,8 @@ function update() {
|
||||
for (const b of L.bosses) b.update(1, P);
|
||||
P.update(1);
|
||||
// 刚从死亡中复活:镜头立刻对准重生点,别慢慢飘过去
|
||||
if (wasDead && !P.dead) G.cam.snapTo(P, L);
|
||||
if (P.justRespawned || (wasDead && !P.dead)) G.cam.snapTo(P, L);
|
||||
P.justRespawned = false;
|
||||
G.cam.update(L, P, 1);
|
||||
G.ms += STEP;
|
||||
G.time++;
|
||||
@@ -105,6 +126,7 @@ function update() {
|
||||
apples: P.apples, totalApples: L.totalApples, hasGun: P.hasGun,
|
||||
save: P.saveIdx || 0, totalSaves: L.totalSaves || 0,
|
||||
});
|
||||
Touch.setGun(P.hasGun);
|
||||
const boss = L.bosses.find(b => !b.dead) || L.bosses[0];
|
||||
UI.bossBar(L.bosses.length ? boss : null);
|
||||
}
|
||||
@@ -126,11 +148,6 @@ function render() {
|
||||
ctx.restore();
|
||||
L.drawOverlay(ctx, C, t);
|
||||
UI.toast(L.toast || '');
|
||||
// 死亡时的血色闪烁
|
||||
if (P.dead) {
|
||||
ctx.fillStyle = 'rgba(200,0,40,.18)';
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
}
|
||||
} else {
|
||||
drawTitleBg(t);
|
||||
}
|
||||
@@ -188,7 +205,7 @@ UI.init({
|
||||
play: (lv) => startLevel(lv, lv.builtin === false ? -1 : (lv.index ?? -1)),
|
||||
levels: toLevels,
|
||||
help: () => UI.show('scr-help'),
|
||||
editor: () => { G.state = 'editor'; UI.show('scr-editor'); Editor.open(); },
|
||||
editor: () => { G.state = 'editor'; UI.show('scr-editor'); Touch.hide(); Editor.open(); },
|
||||
back: toTitle,
|
||||
resume,
|
||||
restart: () => { restart(); },
|
||||
@@ -210,8 +227,11 @@ const unlock = () => { unlockAudio(); removeEventListener('pointerdown', unlock)
|
||||
addEventListener('pointerdown', unlock);
|
||||
addEventListener('keydown', unlock);
|
||||
|
||||
// 移动端虚拟手柄
|
||||
Touch.init({ pause, suicide, shoot });
|
||||
|
||||
UI.show('scr-title');
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
// 调试钩子
|
||||
window.__IW = { G, LEVELS, Save, startLevel };
|
||||
window.__IW = { G, LEVELS, Save, startLevel, pause, resume, restart, suicide };
|
||||
Reference in new issue
Block a user