代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘画猜词游戏</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.game-container {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.left-panel {
flex: 2;
min-width: 300px;
}
.right-panel {
flex: 1;
min-width: 250px;
display: flex;
flex-direction: column;
}
canvas {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
cursor: crosshair;
width: 100%;
max-width: 800px;
height: 500px;
}
.tools {
display: flex;
gap: 10px;
margin-top: 10px;
flex-wrap: wrap;
}
.color-picker {
display: flex;
gap: 5px;
margin: 10px 0;
flex-wrap: wrap;
}
.color {
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
}
.color.active {
border: 2px solid #333;
}
.chat-box {
height: 350px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
background-color: white;
margin-bottom: 10px;
}
.chat-input {
display: flex;
gap: 5px;
}
.message {
margin-bottom: 8px;
padding: 5px 10px;
border-radius: 8px;
max-width: 80%;
}
.system-message {
background-color: #e6e6e6;
text-align: center;
max-width: 100%;
font-style: italic;
color: #666;
}
.user-message {
background-color: #e3f2fd;
align-self: flex-start;
}
.my-message {
background-color: #e8f5e9;
align-self: flex-end;
margin-left: auto;
}
.correct-guess {
background-color: #dcedc8;
font-weight: bold;
}
.player-list {
margin-top: 20px;
background-color: white;
border-radius: 8px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.player-item {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
padding: 5px;
border-radius: 4px;
}
.player-item.drawer {
background-color: #fff9c4;
}
.game-info {
background-color: white;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
.word-display {
font-size: 24px;
margin: 10px 0;
letter-spacing: 3px;
}
.timer {
font-size: 18px;
margin-top: 10px;
color: #f44336;
}
button {
padding: 8px 15px;
border: none;
border-radius: 4px;
background-color: #4285f4;
color: white;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
button:hover {
background-color: #3367d6;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
flex-grow: 1;
}
.size-btn {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
border-radius: 4px;
cursor: pointer;
}
.size-btn.active {
background-color: #ddd;
}
.size-dot {
background-color: black;
border-radius: 50%;
}
.login-container {
max-width: 400px;
margin: 100px auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
max-width: 400px;
width: 100%;
text-align: center;
}
.word-choices {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px;
}
.word-choice {
padding: 10px;
background-color: #e8f5e9;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.word-choice:hover {
background-color: #c8e6c9;
}
</style>
</head>
<body>
<!-- 登录界面 -->
<div id="login-container" class="login-container">
<h2>绘画猜词游戏</h2>
<p>请输入您的昵称加入游戏</p>
<div style="display: flex; gap: 10px;">
<input type="text" id="nickname-input" placeholder="输入昵称">
<button id="join-btn">加入游戏</button>
</div>
</div>
<!-- 游戏主界面 -->
<div id="game-container" class="container" style="display: none;">
<div class="header">
<h1>绘画猜词游戏</h1>
</div>
<div class="game-info">
<div id="game-status">等待游戏开始...</div>
<div id="word-display" class="word-display">_ _ _ _</div>
<div id="timer" class="timer">60秒</div>
</div>
<div class="game-container">
<div class="left-panel">
<canvas id="drawing-canvas"></canvas>
<div class="tools">
<div class="color-picker">
<div class="color active" style="background-color: black;" data-color="black"></div>
<div class="color" style="background-color: red;" data-color="red"></div>
<div class="color" style="background-color: green;" data-color="green"></div>
<div class="color" style="background-color: blue;" data-color="blue"></div>
<div class="color" style="background-color: yellow;" data-color="yellow"></div>
<div class="color" style="background-color: orange;" data-color="orange"></div>
<div class="color" style="background-color: purple;" data-color="purple"></div>
<div class="color" style="background-color: brown;" data-color="brown"></div>
</div>
<div style="display: flex; gap: 5px;">
<div class="size-btn active" data-size="2"><div class="size-dot" style="width: 2px; height: 2px;"></div></div>
<div class="size-btn" data-size="5"><div class="size-dot" style="width: 5px; height: 5px;"></div></div>
<div class="size-btn" data-size="10"><div class="size-dot" style="width: 10px; height: 10px;"></div></div>
<div class="size-btn" data-size="15"><div class="size-dot" style="width: 15px; height: 15px;"></div></div>
</div>
<button id="clear-btn">清空画布</button>
</div>
</div>
<div class="right-panel">
<div class="chat-box" id="chat-box"></div>
<div class="chat-input">
<input type="text" id="chat-input" placeholder="输入您的猜测...">
<button id="send-btn">发送</button>
</div>
<div class="player-list">
<h3>玩家列表</h3>
<div id="player-container"></div>
</div>
<div style="margin-top: 20px;">
<button id="start-game-btn">开始游戏</button>
</div>
</div>
</div>
</div>
<!-- 选词模态框 -->
<div id="word-modal" class="modal">
<div class="modal-content">
<h2>请选择一个词语进行绘画</h2>
<div id="word-choices" class="word-choices">
<!-- 单词选项将在这里动态生成 -->
</div>
</div>
</div>
<script>
// 定义游戏常量
const GAME_STATES = {
WAITING: 'waiting',
CHOOSING: 'choosing',
DRAWING: 'drawing',
ROUND_END: 'roundEnd',
GAME_END: 'gameEnd'
};
const ROUND_TIME = 60; // 每轮游戏时间(秒)
const MIN_PLAYERS = 2; // 最小玩家数
// 游戏变量
let canvas, ctx;
let isDrawing = false;
let currentColor = 'black';
let currentSize = 2;
let currentX = 0;
let currentY = 0;
let socket;
let playerId = null;
let players = [];
let currentDrawer = null;
let gameState = GAME_STATES.WAITING;
let currentWord = '';
let timeLeft = ROUND_TIME;
let timerInterval = null;
// 可选词库
const wordList = [
'苹果', '香蕉', '西瓜', '猫咪', '狗狗', '老鼠',
'电脑', '手机', '电视', '钢琴', '吉他', '足球',
'篮球', '游泳', '跑步', '太阳', '月亮', '星星',
'眼镜', '手表', '书本', '铅笔', '雨伞', '汽车',
'飞机', '火车', '轮船', '自行车', '麦克风', '音响',
'冰箱', '空调', '洗衣机', '微波炉', '沙发', '椅子',
'床铺', '抽屉', '花朵', '树木', '草地', '河流',
'山峰', '大海', '沙滩', '风筝', '气球', '蛋糕'
];
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initializeUI();
setupEventListeners();
});
// 初始化UI元素
function initializeUI() {
canvas = document.getElementById('drawing-canvas');
ctx = canvas.getContext('2d');
// 设置Canvas的实际尺寸
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// 设置线条样式
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 清空画布
clearCanvas();
}
// 设置事件监听器
function setupEventListeners() {
// 登录按钮点击事件
document.getElementById('join-btn').addEventListener('click', joinGame);
document.getElementById('nickname-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') joinGame();
});
// 绘画相关事件
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// 触摸设备支持
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('touchend', handleTouchEnd);
// 颜色选择器事件
document.querySelectorAll('.color').forEach(colorEl => {
colorEl.addEventListener('click', function() {
document.querySelector('.color.active').classList.remove('active');
this.classList.add('active');
currentColor = this.getAttribute('data-color');
});
});
// 线条粗细选择事件
document.querySelectorAll('.size-btn').forEach(sizeBtn => {
sizeBtn.addEventListener('click', function() {
document.querySelector('.size-btn.active').classList.remove('active');
this.classList.add('active');
currentSize = parseInt(this.getAttribute('data-size'));
});
});
// 清空画布按钮
document.getElementById('clear-btn').addEventListener('click', function() {
if (currentDrawer === playerId) {
clearCanvas();
sendDrawingAction('clear');
}
});
// 聊天发送按钮
document.getElementById('send-btn').addEventListener('click', sendChatMessage);
document.getElementById('chat-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') sendChatMessage();
});
// 开始游戏按钮
document.getElementById('start-game-btn').addEventListener('click', startGame);
// 窗口大小改变事件
window.addEventListener('resize', function() {
const oldCanvas = canvas.toDataURL(); // 保存当前画布
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// 恢复画布内容
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = oldCanvas;
});
}
// 加入游戏
function joinGame() {
const nickname = document.getElementById('nickname-input').value.trim();
if (!nickname) {
alert('请输入昵称');
return;
}
// 模拟websocket连接
simulateWebSocketConnection(nickname);
}
// 模拟WebSocket连接
function simulateWebSocketConnection(nickname) {
// 在实际应用中,这里应该建立真正的WebSocket连接
console.log(`连接WebSocket服务器...`);
// 模拟服务器分配的玩家ID
playerId = 'player_' + Date.now();
// 显示游戏界面,隐藏登录界面
document.getElementById('login-container').style.display = 'none';
document.getElementById('game-container').style.display = 'block';
// 添加欢迎消息
addChatMessage({
type: 'system',
message: `欢迎 ${nickname} 加入游戏!`
});
// 模拟添加其他玩家
const existingPlayers = [
{ id: 'player_1', nickname: '玩家1', score: 0 },
{ id: 'player_2', nickname: '玩家2', score: 0 }
];
// 添加当前玩家
players = [...existingPlayers, { id: playerId, nickname: nickname, score: 0 }];
// 更新玩家列表
updatePlayerList();
// 实现模拟的WebSocket消息处理
initializeSimulatedWebSocket();
}
// 模拟WebSocket功能
function initializeSimulatedWebSocket() {
// 在实际应用中,这里应该初始化真正的WebSocket
socket = {
send: function(data) {
// 模拟发送消息到服务器
console.log('发送消息:', data);
handleSimulatedServerMessage(JSON.parse(data));
}
};
}
// 模拟服务器消息处理
function handleSimulatedServerMessage(message) {
switch (message.type) {
case 'chat':
// 处理聊天消息
if (gameState === GAME_STATES.DRAWING && message.playerId !== currentDrawer) {
const isCorrect = message.text.includes(currentWord);
if (isCorrect) {
// 正确猜测
addChatMessage({
type: 'system',
message: `${getPlayerById(message.playerId).nickname} 猜对了!`
});
// 更新分数
const guesserIndex = players.findIndex(p => p.id === message.playerId);
if (guesserIndex !== -1) {
players[guesserIndex].score += 10;
}
const drawerIndex = players.findIndex(p => p.id === currentDrawer);
if (drawerIndex !== -1) {
players[drawerIndex].score += 5;
}
// 更新玩家列表
updatePlayerList();
// 结束当前回合
endRound();
} else {
// 普通聊天消息
addChatMessage({
type: 'user',
playerId: message.playerId,
message: message.text
});
}
} else {
// 非绘画阶段或绘画者发送的消息
addChatMessage({
type: 'user',
playerId: message.playerId,
message: message.text
});
}
break;
case 'draw':
// 处理绘画操作
if (message.playerId !== playerId) {
handleDrawingAction(message.action, message.data);
}
break;
case 'start':
// 处理开始游戏
startNewRound();
break;
case 'word':
// 处理选择单词
currentWord = message.word;
handleWordSelection();
break;
}
}
// 发送聊天消息
function sendChatMessage() {
const inputElement = document.getElementById('chat-input');
const message = inputElement.value.trim();
if (message) {
// 发送消息
socket.send(JSON.stringify({
type: 'chat',
playerId: playerId,
text: message
}));
// 清空输入框
inputElement.value = '';
}
}
// 添加聊天消息到聊天框
function addChatMessage(messageData) {
const chatBox = document.getElementById('chat-box');
const messageElement = document.createElement('div');
if (messageData.type === 'system') {
// 系统消息
messageElement.className = 'message system-message';
messageElement.textContent = messageData.message;
} else {
// 用户消息
const player = getPlayerById(messageData.playerId);
messageElement.className = 'message user-message';
if (messageData.playerId === playerId) {
messageElement.className = 'message my-message';
}
// 如果是正确猜测,添加样式
if (messageData.correct) {
messageElement.className += ' correct-guess';
}
messageElement.textContent = `${player.nickname}: ${messageData.message}`;
}
chatBox.appendChild(messageElement);
// 滚动到底部
chatBox.scrollTop = chatBox.scrollHeight;
}
// 根据ID获取玩家信息
function getPlayerById(id) {
const player = players.find(p => p.id === id);
return player || { nickname: '未知玩家' };
}
// 更新玩家列表显示
function updatePlayerList() {
const playerContainer = document.getElementById('player-container');
playerContainer.innerHTML = '';
players.forEach(player => {
const playerElement = document.createElement('div');
playerElement.className = 'player-item';
if (player.id === currentDrawer) {
playerElement.className += ' drawer';
}
playerElement.innerHTML = `
<span>${player.nickname}</span>
<span>${player.score}分</span>
`;
playerContainer.appendChild(playerElement);
});
}
// 开始游戏
function startGame() {
if (players.length < MIN_PLAYERS) {
addChatMessage({
type: 'system',
message: `至少需要${MIN_PLAYERS}名玩家才能开始游戏`
});
return;
}
socket.send(JSON.stringify({
type: 'start'
}));
}
// 开始新回合
function startNewRound() {
clearCanvas();
clearInterval(timerInterval);
// 选择绘画者
const nextDrawerIndex = players.findIndex(p => p.id === currentDrawer) + 1;
currentDrawer = players[nextDrawerIndex >= players.length ? 0 : nextDrawerIndex].id;
// 更新玩家列表
updatePlayerList();
// 更新游戏状态
gameState = GAME_STATES.CHOOSING;
// 通知所有玩家
addChatMessage({
type: 'system',
message: `${getPlayerById(currentDrawer).nickname} 正在选择词语...`
});
document.getElementById('game-status').textContent = `${getPlayerById(currentDrawer).nickname} 正在选择词语...`;
// 如果当前玩家是绘画者,显示选词界面
if (currentDrawer === playerId) {
showWordSelectionModal();
} else {
document.getElementById('word-display').textContent = '_ _ _ _';
}
}
// 显示选词模态框
function showWordSelectionModal() {
const modal = document.getElementById('word-modal');
const wordChoices = document.getElementById('word-choices');
// 清空现有选项
wordChoices.innerHTML = '';
// 随机选择3个单词
const randomWords = getRandomWords(3);
// 创建选项
randomWords.forEach(word => {
const wordElement = document.createElement('div');
wordElement.className = 'word-choice';
wordElement.textContent = word;
wordElement.addEventListener('click', function() {
selectWord(word);
modal.style.display = 'none';
});
wordChoices.appendChild(wordElement);
});
// 显示模态框
modal.style.display = 'flex';
}
// 从词库中随机选择单词
function getRandomWords(count) {
const words = [];
const wordsCopy = [...wordList];
for (let i = 0; i < count; i++) {
if (wordsCopy.length === 0) break;
const randomIndex = Math.floor(Math.random() * wordsCopy.length);
words.push(wordsCopy.splice(randomIndex, 1)[0]);
}
return words;
}
// 选择单词
function selectWord(word) {
socket.send(JSON.stringify({
type: 'word',
playerId: playerId,
word: word
}));
}
// 处理单词选择
function handleWordSelection() {
gameState = GAME_STATES.DRAWING;
// 更新游戏状态
if (currentDrawer === playerId) {
document.getElementById('game-status').textContent = `你正在绘画: ${currentWord}`;
document.getElementById('word-display').textContent = currentWord;
} else {
document.getElementById('game-status').textContent = `${getPlayerById(currentDrawer).nickname} 正在绘画`;
// 显示单词长度的下划线
const hint = Array(currentWord.length).fill('_').join(' ');
document.getElementById('word-display').textContent = hint;
}
// 开始计时器
startTimer();
// 添加系统消息
addChatMessage({
type: 'system',
message: `${getPlayerById(currentDrawer).nickname} 开始绘画,请猜测词语!`
});
}
// 开始计时器
function startTimer() {
timeLeft = ROUND_TIME;
updateTimerDisplay();
clearInterval(timerInterval);
timerInterval = setInterval(() => {
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
endRound();
}
}, 1000);
}
// 更新计时器显示
function updateTimerDisplay() {
document.getElementById('timer').textContent = `${timeLeft}秒`;
}
// 结束回合
function endRound() {
clearInterval(timerInterval);
gameState = GAME_STATES.ROUND_END;
// 显示答案
document.getElementById('word-display').textContent = currentWord;
document.getElementById('game-status').textContent = `本轮结束,答案是:${currentWord}`;
// 添加系统消息
addChatMessage({
type: 'system',
message: `本轮结束,答案是:${currentWord}`
});
// 3秒后开始新回合
setTimeout(() => {
startNewRound();
}, 3000);
}
// 开始绘画
function startDrawing(e) {
if (gameState !== GAME_STATES.DRAWING || currentDrawer !== playerId) return;
isDrawing = true;
const rect = canvas.getBoundingClientRect();
currentX = e.clientX - rect.left;
currentY = e.clientY - rect.top;
}
// 绘画
function draw(e) {
if (!isDrawing || gameState !== GAME_STATES.DRAWING || currentDrawer !== playerId) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 绘制线条
ctx.strokeStyle = currentColor;
ctx.lineWidth = currentSize;
ctx.beginPath();
ctx.moveTo(currentX, currentY);
ctx.lineTo(x, y);
ctx.stroke();
// 发送绘画数据
sendDrawingAction('line', {
fromX: currentX,
fromY: currentY,
toX: x,
toY: y,
color: currentColor,
size: currentSize
});
currentX = x;
currentY = y;
}
// 停止绘画
function stopDrawing() {
isDrawing = false;
}
// 处理触摸开始事件
function handleTouchStart(e) {
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
}
// 处理触摸移动事件
// 处理触摸移动事件
function handleTouchMove(e) {
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}
}
// 处理触摸结束事件
function handleTouchEnd(e) {
e.preventDefault();
const mouseEvent = new MouseEvent('mouseup', {});
canvas.dispatchEvent(mouseEvent);
}
// 清空画布
function clearCanvas() {
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// 发送绘画动作到服务器
function sendDrawingAction(action, data = null) {
socket.send(JSON.stringify({
type: 'draw',
playerId: playerId,
action: action,
data: data
}));
}
// 处理接收到的绘画动作
function handleDrawingAction(action, data) {
switch (action) {
case 'line':
// 绘制线条
ctx.strokeStyle = data.color;
ctx.lineWidth = data.size;
ctx.beginPath();
ctx.moveTo(data.fromX, data.fromY);
ctx.lineTo(data.toX, data.toY);
ctx.stroke();
break;
case 'clear':
// 清空画布
clearCanvas();
break;
}
}
// 模拟服务器行为(在实际应用中应该由真正的WebSocket服务器处理)
// 这只是为了演示,创建一个能在单个浏览器中运行的模拟环境
class SimulatedServer {
constructor() {
this.players = [];
this.messages = [];
this.gameState = GAME_STATES.WAITING;
this.currentDrawer = null;
this.currentWord = '';
}
addPlayer(player) {
this.players.push(player);
this.broadcastPlayerList();
}
removePlayer(playerId) {
this.players = this.players.filter(p => p.id !== playerId);
this.broadcastPlayerList();
}
handleMessage(message) {
// 处理各种消息类型
}
broadcastPlayerList() {
// 广播玩家列表更新
}
startGame() {
// 开始游戏逻辑
}
}
// 在实际应用中,您需要替换上面的模拟代码,
// 使用真正的WebSocket服务器和客户端连接,例如:
// 真实的WebSocket初始化
function initializeWebSocket(nickname) {
// 连接WebSocket服务器
socket = new WebSocket('ws://your-server-url');
// 连接建立时
socket.onopen = function() {
// 发送加入游戏消息
socket.send(JSON.stringify({
type: 'join',
nickname: nickname
}));
};
// 接收消息
socket.onmessage = function(event) {
const message = JSON.parse(event.data);
// 处理不同类型的消息
switch (message.type) {
case 'player_joined':
// 处理玩家加入
break;
case 'player_left':
// 处理玩家离开
break;
case 'chat':
// 处理聊天消息
break;
case 'draw':
// 处理绘画操作
break;
case 'game_start':
// 处理游戏开始
break;
case 'round_start':
// 处理回合开始
break;
case 'round_end':
// 处理回合结束
break;
case 'word_reveal':
// 处理单词公布
break;
}
};
// 连接关闭
socket.onclose = function() {
// 处理连接关闭
};
// 连接错误
socket.onerror = function(error) {
// 处理连接错误
};
}
// 使用以下代码添加到HTML的末尾来完成页面
</script>
</body>
</html>