437 lines
13 KiB
JavaScript
Executable File
437 lines
13 KiB
JavaScript
Executable File
// 游戏控制模块 - 处理键盘和触摸输入
|
||
class GameControls {
|
||
constructor() {
|
||
this.touchStartX = 0;
|
||
this.touchStartY = 0;
|
||
this.touchEndX = 0;
|
||
this.touchEndY = 0;
|
||
this.minSwipeDistance = 30; // 最小滑动距离
|
||
this.isGameActive = true;
|
||
|
||
this.initializeControls();
|
||
}
|
||
|
||
initializeControls() {
|
||
// 键盘控制
|
||
this.initKeyboardControls();
|
||
|
||
// 触摸控制
|
||
this.initTouchControls();
|
||
|
||
// 鼠标控制(用于电脑端测试)
|
||
this.initMouseControls();
|
||
|
||
// 防止页面滚动
|
||
this.preventScrolling();
|
||
}
|
||
|
||
initKeyboardControls() {
|
||
// 确保iframe能够获得焦点并接收键盘事件
|
||
const gameContainer = document.querySelector('.container');
|
||
if (gameContainer) {
|
||
gameContainer.setAttribute('tabindex', '0');
|
||
gameContainer.focus();
|
||
}
|
||
|
||
// 为document和window都添加键盘事件监听器,确保在iframe中也能工作
|
||
const handleKeyDown = (e) => {
|
||
if (!this.isGameActive || !window.game2048) {
|
||
console.log('Game not ready:', { isGameActive: this.isGameActive, game2048: !!window.game2048 });
|
||
return;
|
||
}
|
||
|
||
// 防止默认行为
|
||
const preventKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'a', 's', 'd', 'W', 'A', 'S', 'D'];
|
||
if (preventKeys.includes(e.key)) {
|
||
e.preventDefault();
|
||
}
|
||
|
||
switch (e.key) {
|
||
case 'ArrowUp':
|
||
case 'w':
|
||
case 'W':
|
||
window.game2048.move('up');
|
||
break;
|
||
case 'ArrowDown':
|
||
case 's':
|
||
case 'S':
|
||
window.game2048.move('down');
|
||
break;
|
||
case 'ArrowLeft':
|
||
case 'a':
|
||
case 'A':
|
||
window.game2048.move('left');
|
||
break;
|
||
case 'ArrowRight':
|
||
case 'd':
|
||
case 'D':
|
||
window.game2048.move('right');
|
||
break;
|
||
case 'r':
|
||
case 'R':
|
||
// R键重新开始游戏
|
||
window.game2048.restart();
|
||
break;
|
||
case 'Escape':
|
||
// ESC键暂停/继续游戏
|
||
this.togglePause();
|
||
break;
|
||
}
|
||
};
|
||
|
||
// 同时监听document和window的键盘事件
|
||
document.addEventListener('keydown', handleKeyDown);
|
||
window.addEventListener('keydown', handleKeyDown);
|
||
|
||
// 确保游戏容器在点击时获得焦点
|
||
if (gameContainer) {
|
||
gameContainer.addEventListener('click', () => {
|
||
gameContainer.focus();
|
||
});
|
||
}
|
||
}
|
||
|
||
initTouchControls() {
|
||
const gameContainer = document.querySelector('.game-container');
|
||
|
||
// 触摸开始
|
||
gameContainer.addEventListener('touchstart', (e) => {
|
||
if (!this.isGameActive) return;
|
||
|
||
e.preventDefault();
|
||
const touch = e.touches[0];
|
||
this.touchStartX = touch.clientX;
|
||
this.touchStartY = touch.clientY;
|
||
}, { passive: false });
|
||
|
||
// 触摸移动(可选:显示滑动方向提示)
|
||
gameContainer.addEventListener('touchmove', (e) => {
|
||
e.preventDefault();
|
||
}, { passive: false });
|
||
|
||
// 触摸结束
|
||
gameContainer.addEventListener('touchend', (e) => {
|
||
if (!this.isGameActive || !window.game2048) return;
|
||
|
||
e.preventDefault();
|
||
const touch = e.changedTouches[0];
|
||
this.touchEndX = touch.clientX;
|
||
this.touchEndY = touch.clientY;
|
||
|
||
this.handleSwipe();
|
||
}, { passive: false });
|
||
|
||
// 触摸取消
|
||
gameContainer.addEventListener('touchcancel', (e) => {
|
||
e.preventDefault();
|
||
this.resetTouch();
|
||
}, { passive: false });
|
||
}
|
||
|
||
initMouseControls() {
|
||
const gameContainer = document.querySelector('.game-container');
|
||
let isMouseDown = false;
|
||
let mouseStartX = 0;
|
||
let mouseStartY = 0;
|
||
|
||
// 鼠标按下
|
||
gameContainer.addEventListener('mousedown', (e) => {
|
||
if (!this.isGameActive) return;
|
||
|
||
isMouseDown = true;
|
||
mouseStartX = e.clientX;
|
||
mouseStartY = e.clientY;
|
||
e.preventDefault();
|
||
});
|
||
|
||
// 鼠标释放
|
||
gameContainer.addEventListener('mouseup', (e) => {
|
||
if (!this.isGameActive || !isMouseDown || !window.game2048) return;
|
||
|
||
isMouseDown = false;
|
||
const mouseEndX = e.clientX;
|
||
const mouseEndY = e.clientY;
|
||
|
||
// 使用鼠标坐标模拟触摸
|
||
this.touchStartX = mouseStartX;
|
||
this.touchStartY = mouseStartY;
|
||
this.touchEndX = mouseEndX;
|
||
this.touchEndY = mouseEndY;
|
||
|
||
this.handleSwipe();
|
||
e.preventDefault();
|
||
});
|
||
|
||
// 鼠标离开游戏区域
|
||
gameContainer.addEventListener('mouseleave', () => {
|
||
isMouseDown = false;
|
||
});
|
||
|
||
// 防止右键菜单
|
||
gameContainer.addEventListener('contextmenu', (e) => {
|
||
e.preventDefault();
|
||
});
|
||
}
|
||
|
||
handleSwipe() {
|
||
const deltaX = this.touchEndX - this.touchStartX;
|
||
const deltaY = this.touchEndY - this.touchStartY;
|
||
const absDeltaX = Math.abs(deltaX);
|
||
const absDeltaY = Math.abs(deltaY);
|
||
|
||
// 检查是否达到最小滑动距离
|
||
if (Math.max(absDeltaX, absDeltaY) < this.minSwipeDistance) {
|
||
this.resetTouch();
|
||
return;
|
||
}
|
||
|
||
// 确定滑动方向
|
||
let direction = null;
|
||
|
||
if (absDeltaX > absDeltaY) {
|
||
// 水平滑动
|
||
direction = deltaX > 0 ? 'right' : 'left';
|
||
} else {
|
||
// 垂直滑动
|
||
direction = deltaY > 0 ? 'down' : 'up';
|
||
}
|
||
|
||
// 执行移动
|
||
if (direction && window.game2048) {
|
||
window.game2048.move(direction);
|
||
|
||
// 添加触觉反馈(如果支持)
|
||
this.addHapticFeedback();
|
||
|
||
// 添加视觉反馈
|
||
this.addVisualFeedback(direction);
|
||
}
|
||
|
||
this.resetTouch();
|
||
}
|
||
|
||
resetTouch() {
|
||
this.touchStartX = 0;
|
||
this.touchStartY = 0;
|
||
this.touchEndX = 0;
|
||
this.touchEndY = 0;
|
||
}
|
||
|
||
addHapticFeedback() {
|
||
// 添加触觉反馈(仅在支持的设备上)
|
||
if (navigator.vibrate) {
|
||
navigator.vibrate(50); // 50ms的轻微震动
|
||
}
|
||
}
|
||
|
||
addVisualFeedback(direction) {
|
||
// 添加方向指示的视觉反馈
|
||
const gameContainer = document.querySelector('.game-container');
|
||
const feedback = document.createElement('div');
|
||
|
||
feedback.className = 'swipe-feedback';
|
||
feedback.textContent = this.getDirectionArrow(direction);
|
||
|
||
// 设置样式
|
||
feedback.style.cssText = `
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 48px;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
pointer-events: none;
|
||
z-index: 1000;
|
||
animation: swipeFeedback 0.3s ease-out;
|
||
`;
|
||
|
||
gameContainer.appendChild(feedback);
|
||
|
||
// 添加动画样式(如果不存在)
|
||
if (!document.getElementById('swipe-feedback-styles')) {
|
||
const style = document.createElement('style');
|
||
style.id = 'swipe-feedback-styles';
|
||
style.textContent = `
|
||
@keyframes swipeFeedback {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translate(-50%, -50%) scale(0.5);
|
||
}
|
||
50% {
|
||
opacity: 1;
|
||
transform: translate(-50%, -50%) scale(1.2);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
// 移除反馈元素
|
||
setTimeout(() => {
|
||
if (feedback.parentNode) {
|
||
feedback.remove();
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
getDirectionArrow(direction) {
|
||
const arrows = {
|
||
'up': '↑',
|
||
'down': '↓',
|
||
'left': '←',
|
||
'right': '→'
|
||
};
|
||
return arrows[direction] || '';
|
||
}
|
||
|
||
preventScrolling() {
|
||
// 防止页面滚动,特别是在移动设备上
|
||
document.addEventListener('touchmove', (e) => {
|
||
// 只在游戏容器内防止滚动
|
||
const gameContainer = document.querySelector('.game-container');
|
||
if (gameContainer && gameContainer.contains(e.target)) {
|
||
e.preventDefault();
|
||
}
|
||
}, { passive: false });
|
||
|
||
// 防止双击缩放
|
||
document.addEventListener('touchstart', (e) => {
|
||
if (e.touches.length > 1) {
|
||
e.preventDefault();
|
||
}
|
||
}, { passive: false });
|
||
|
||
// 防止长按选择文本
|
||
document.addEventListener('selectstart', (e) => {
|
||
const gameContainer = document.querySelector('.game-container');
|
||
if (gameContainer && gameContainer.contains(e.target)) {
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
}
|
||
|
||
togglePause() {
|
||
this.isGameActive = !this.isGameActive;
|
||
|
||
const pauseOverlay = document.getElementById('pause-overlay') || this.createPauseOverlay();
|
||
|
||
if (this.isGameActive) {
|
||
pauseOverlay.style.display = 'none';
|
||
// 恢复计时器
|
||
if (window.game2048 && window.game2048.stats.startTime) {
|
||
const pausedTime = Date.now() - window.game2048.pauseStartTime;
|
||
window.game2048.stats.startTime += pausedTime;
|
||
}
|
||
} else {
|
||
pauseOverlay.style.display = 'flex';
|
||
// 记录暂停时间
|
||
if (window.game2048) {
|
||
window.game2048.pauseStartTime = Date.now();
|
||
}
|
||
}
|
||
}
|
||
|
||
createPauseOverlay() {
|
||
const overlay = document.createElement('div');
|
||
overlay.id = 'pause-overlay';
|
||
overlay.innerHTML = `
|
||
<div class="pause-content">
|
||
<h2>游戏暂停</h2>
|
||
<p>按ESC键或点击继续游戏</p>
|
||
<button class="resume-btn">继续游戏</button>
|
||
</div>
|
||
`;
|
||
|
||
overlay.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
display: none;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 10000;
|
||
backdrop-filter: blur(5px);
|
||
`;
|
||
|
||
const pauseContent = overlay.querySelector('.pause-content');
|
||
pauseContent.style.cssText = `
|
||
background: white;
|
||
padding: 40px;
|
||
border-radius: 15px;
|
||
text-align: center;
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||
`;
|
||
|
||
const resumeBtn = overlay.querySelector('.resume-btn');
|
||
resumeBtn.style.cssText = `
|
||
background: linear-gradient(45deg, #4ecdc4, #44a08d);
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 25px;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
margin-top: 20px;
|
||
transition: all 0.3s ease;
|
||
`;
|
||
|
||
// 继续游戏按钮事件
|
||
resumeBtn.addEventListener('click', () => {
|
||
this.togglePause();
|
||
});
|
||
|
||
// 点击背景继续游戏
|
||
overlay.addEventListener('click', (e) => {
|
||
if (e.target === overlay) {
|
||
this.togglePause();
|
||
}
|
||
});
|
||
|
||
document.body.appendChild(overlay);
|
||
return overlay;
|
||
}
|
||
|
||
// 禁用控制(游戏结束时调用)
|
||
disable() {
|
||
this.isGameActive = false;
|
||
}
|
||
|
||
// 启用控制(游戏开始时调用)
|
||
enable() {
|
||
this.isGameActive = true;
|
||
}
|
||
}
|
||
|
||
// 创建全局控制实例
|
||
let gameControls;
|
||
|
||
// 页面加载完成后初始化控制
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// 等待游戏对象初始化完成
|
||
const initControls = () => {
|
||
if (window.game2048) {
|
||
gameControls = new GameControls();
|
||
console.log('Game controls initialized successfully');
|
||
|
||
|
||
} else {
|
||
console.log('Waiting for game2048 to initialize...');
|
||
setTimeout(initControls, 100);
|
||
}
|
||
};
|
||
|
||
initControls();
|
||
});
|
||
|
||
|
||
|
||
// 导出控制实例
|
||
window.gameControls = gameControls; |