Дамы и господа, давно не виделись, эта статья должна была встретиться с вами в 2018 году, но она была отложена до этого момента по разным причинам (мой горшок, мой горшок), Весенний фестиваль будет через неделю, Вот что сказать Всех с новым годом! ! !
Сегодня хороший день. Давайте воспользуемся socket.io и canvas для создания простой игры «Я рисую, угадай».
Китайский Новый год почти наступил, давайте расслабим нервы напряженного года и поиграем в маленькие игры, которые вы создали!
PS: Если вы не понимаете упомянутую далее реализацию обмена мгновенными сообщениями, вы можете просмотреть ее еще раз.здесь
Что ж, мир единоборств быстр и не ломается, поторопитесь первымСтруктура каталогов
Реализовать мгновенную передачу сообщений
Поскольку я написал статью, чтобы представить содержимое socket.io для реализации мгновенной связи, я постараюсь написать ее как можно быстрее.
Сначала запустите службу через экспресс и создайте соединение socket.io.
Запустите службу и установите соединение
Сервер
// app.js文件
const express = require('express');
const app = express();
// 设置静态文件夹
// 这样设置会自动识别当前文件夹下的index.html文件
app.use(express.static(__dirname));
const server = require('http').createServer(app);
const io = require('socket.io')(server);
server.listen(8888);
Запустите службу, а затем посетите localhost:8888, вы можете получить доступ к содержимому файла index.html.
Советы:
Иногда он будет сообщать, когда клиент получает доступ к ioio is not defined
Ошибка заключается в том, что вам нужно сначала запустить службу, прежде чем она автоматически сгенерируетsocket.io/socket.io.js
Файл, как показано на рисунке выше, можно сослаться на
клиент
// index.js文件
// 用来处理游戏对象数据
let gameObj = {};
// socket实例
let socket = io();
// 监听connect事件
socket.on('connect', () => {
console.log('客户端连接成功');
});
В этот момент обновите страницу, и вы увидите ее в консоли.客户端连接成功
эти 7 слов
отправлять/получать/отображать сообщения
Это всего лишь небольшой шаг, далее продолжим расписывать логику обработки сообщений.
Сервер
// app.js
...省略
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 区分是聊天还是在绘图
const LINE = 0;
const MESSAGE = 1;
const userList = ['皮卡丘', '巴大蝴', '比比鸟', '妙蛙种子', '小火龙', '杰尼龟'];
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
io.on('connection', socket => {
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 随机分配用户名并发送给所有人
const user = userList[Math.floor(Math.random() * userList.length)];
const message = `欢迎${user}加入游戏!!!`;
// 将数据封装成json对象
let data = {};
// 通过type来区分
data.type = MESSAGE;
data.sender = '系统';
data.message = message;
// 将消息分发出去
// 消息数据必须是字符串类型,so需要转换一下
io.emit('message', JSON.stringify(data));
socket.on('message', msg => {
// 传过来的消息也是json字符串格式的,需要JSON.parse转成json
let data = JSON.parse(msg);
// 如果是聊天类型,就给sender赋值为当前用户名
if (data.type === MESSAGE) {
data.sender = user;
}
io.emit('message', JSON.stringify(data));
});
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
});
server.listen(8888);
В приведенном выше коде данные сообщения упакованы в формат json для удобства, ведь данные сообщения могут принимать только строковый формат.
а затем передать его при отправкеJSON.stringify
превратить вjson-строка, чтобы не было ошибки
Конечно, при разборе соответствующих данных сообщения передатьJSON.parse
преобразовать в реальныйjsonВот и все
После того, как сервер закончил отправлять и получать сообщения, пришло время появиться клиенту.Помимо двух вышеперечисленных функций, клиент также будет отображать сообщения (звуки хер)
Итак, без лишних слов, приступим! ! !
клиент
// index.js文件
++++++++++++++++++++++++++++++++++++++++++++++++++++++
const LINE = 0;
const MESSAGE = 1;
++++++++++++++++++++++++++++++++++++++++++++++++++++++
let gameObj = {};
let socket = io();
++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 监听服务端发来的消息
socket.on('message', msg => {
// 需要先用JSON.parse转一下
let data = JSON.parse(msg);
console.log(data); // {type: 1, sender: "系统", message: "欢迎皮卡丘进入游戏"}
// 如果类型为聊天
if (data.type === MESSAGE) {
let li = `<li><span>${data.sender}: </span>${data.message}</li>`;
$('#history').append(li);
// 聊天区域滚动到最新聊天内容位置
$('#history-wrapper').scrollTop($('#history-wrapper')[0].scrollHeight);
}
});
// 点击发送按钮发消息
$('#btn').click(sendMsg);
// 按回车键发送消息
$('#input').keyup(e => {
let keyCode = e.keyCode;
if (keyCode === 13) {
sendMsg();
}
});
// 发送消息函数
function sendMsg() {
let value = $.trim($('#input').val());
if (value !== '') {
let data = {};
data.type = MESSAGE;
data.message = value;
gameObj.socket.send(JSON.stringify(data));
$('#input').val('');
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++
Что делает приведенный выше код на стороне клиента?
- Константа отличает чат от рисования
const LINE = 0; const MESSAGE = 1;
- Слушайте сообщения с сервера
- слушать сообщения
socket.on('message', msg => {});
- Преобразование сообщения в формат json
let data = JSON.parse(msg);
- Тип сообщения - чат
if (data.type === MESSAGE) { // 添加内容 ...省略 // 滚动到最新消息位置 ...省略 }
- отправлять сообщения
- способ отправки сообщения
function sendMsg() { ...省略 }
- Нажмите или введите, чтобы отправить
Выше приведена базовая реализация передачи сообщений.Теперь мы собираемся ввести следующую ссылку, холст на сцене, продолжайте читать
Холст для рисования артбордов
Элемент canvas долго ждал, и наконец настала его очередь показать свои таланты.Любой, кто использовал canvas, знает, что мы обычно выполняем операции рисования в 2D, поэтому давайте займемся этим раньше.
// index.js文件
const LINE = 0;
const MESSAGE = 1;
++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 用原生来获取,jq对象中并没有我们需要的2d
let cvs = document.getElementById('canvas');
let ctx = cvs.getContext('2d');
let gameObj = {
// 当前用户是否在绘图
isDrawing: false,
// 下一条线的起始点
startX: 0,
startY: 0
};
++++++++++++++++++++++++++++++++++++++++++++++++++++++
...省略
socket.on('message', msg => {
let data = JSON.parse(msg);
if (data.type === MESSAGE) {
...省略
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++
else if (data.type === LINE) {
// 这是画线函数,专门绘制所用
drawLine(ctx, data.startX, data.startY, data.endX, data.endY, 1);
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++
});
// 发送消息函数
...省略
++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 开始在画板上画画了
// 鼠标按下时的操作
$('#canvas').on('mousedown', function(e) {
let cvsPos = $(this).offset(),
mouseX = e.pageX - cvsPos.left || 0,
mouseY = e.pageY - cvsPos.top || 0;
// 更新一下startX和startY
gameObj.startX = mouseX;
gameObj.startY = mouseY;
// 更新为绘图状态
gameObj.isDrawing = true;
});
// 鼠标移动时的操作
$('#canvas').on('mousemove', function(e) {
// 当绘图状态为true的时候才可以绘制
if (gameObj.isDrawing) {
let cvsPos = $(this).offset(),
mouseX = e.pageX - cvsPos.left || 0,
mouseY = e.pageY - cvsPos.top || 0;
if (gameObj.startX !== mouseX && gameObj.startY !== mouseY) {
// 开始绘制线段,drawLine为画线函数
drawLine(ctx, gameObj.startX, gameObj.startY, mouseX, mouseY, 1, $('#color').val());
// 既然画线了,那就把画的线段数据也打包成json传给服务端
let data = {};
data.startX = gameObj.startX;
data.startY = gameObj.startY;
data.endX = mouseX;
data.endY = mouseY;
data.type = LINE;
// 别犹豫,直接通过socket发给服务端
socket.send(JSON.stringify(data));
// 这里还要更新一下startX和startY
gameObj.startX = mouseX;
gameObj.startY = mouseY;
}
}
});
// 鼠标抬起时的操作
$('#canvas').on('mouseup', function() {
gameObj.isDrawing = false;
});
// 画线函数
function drawLine(ctx, x1, y1, x2, y2, thick) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineWidth = thick;
ctx.strokeStyle = '#00a1f4';
ctx.stroke();
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++
Не смотрите на приведенный выше код вдруг намного больше, а по сути это не что иное, как следующие пункты, не паникуйте, давайте разберемся
- Различать сообщения как данные, нарисованные линиями
- В функции, которая прослушивает сообщения, передайте
data.type === LINE
Чтобы отличить данные, тип сообщения которых представляет собой рисование линий
- В функции, которая прослушивает сообщения, передайте
- мышь на холсте событие
- пресс-событие mousedown
- записать место щелчка
- Назначьте нажатую позицию для gameObj.startX и gameObj.startY
- Измените состояние gameObj.isDrawing на true
- событие перемещения мыши
- зафиксировать место перемещения
- рисовать отрезки линии
- Отправка данных рисования линий на сервер
- Обновить последнюю позицию хода в gameObj
- событие подъема мыши
- Изменить GameObj.isdrawing Состояние на начальное значение false
- пресс-событие mousedown
- Реализовать метод рисования линий
- ctx.beginPath() - нарисовать начальный путь
- ctx.moveTo(x1, y1) - точка штриха сегмента линии
- ctx.lineTo(x2, y2) - траектория движения сегмента линии
- ctx.lineWidth - ширина линии
- ctx.storkeStyle - цвет линии
- ctx.stroke() - нарисовать отрезок линии
Трудно сказать, легко сказать. Однако, в зависимости от уровня владения, будут разные ощущения.Если вы будете больше писать и больше практиковаться, то знания естественным образом попадут в ваши руки.
В принципе можно сказать, что основы socket.io и canvas были освоены на этом этапе.В конце концов, мы не должны забывать название статьи.Это многопользовательская игра, не только для простого развлечения, но и для все счастливы
Так что давайте работать усерднее и реализовывать многопользовательскую логику
Создайте многопользовательскую игру
игровая логика
Играя в игры, естественно есть логика и правила игры
Реализация игровой логики на стороне клиента относительно проста, и многие операции по-прежнему зависят от стороны сервера.
Итак, сначала пойдем от простого к сложному.
клиент
// index.js文件
const LINE = 0;
const MESSAGE = 1;
// 添加个游戏常量
const GAME = 2;
let gameObj = {
...省略
// 游戏状态
WAITTING: 0,
START: 1,
OVER: 2,
RESTART: 3,
// 当前轮到谁来绘图
isPlayer: false
};
...省略
socket.on('message', msg => {
let data = JSON.parse(msg);
if (data.type === MESSAGE) {
...省略
} else if (data.type === LINE) {
...省略
} else if (data.type === GAME) { // 如果进行游戏,传过来的type值必须是GAME
// 通过data.state来判断游戏当前的进度
// 游戏开始的逻辑
if (data.state === gameObj.START) {
// 游戏要是开始了就需要清空画布
ctx.clearRect(0, 0, cvs.width, cvs.height);
// 清空聊天记录和隐藏重新开始
$('#restart').hide();
$('#history').html('');
// 区分一下是当前画图的玩家还是猜图的玩家
if (data.isPlayer) {
gameObj.isPlayer = true;
$('#history').append(`<li>轮到你了,请你画出<span class="answer">${data.answer}</span></li>`);
} else {
$('#history').append(`<li>游戏即将开始,请准备,你们有一分钟的时间去猜答案哦</li>`);
}
}
// 游戏结束的逻辑
if (data.state === gameObj.OVER) {
gameObj.isPlayer = false;
$('#restart').show();
$('#history').append(`<li>本轮游戏的获胜者是<span class="winner">${data.winner}</span>,正确答案是: ${data.answer}</li>`);
}
if (data.state === gameObj.RESTART) {
$('#restart').hide();
ctx.clearRect(0, 0, cvs.width, cvs.height);
}
}
});
...省略
// 画线函数
...省略
// 重玩
$('#restart').on('click', function() {
let data = {};
data.type = GAME;
data.state = gameObj.RESTART;
socket.send(JSON.stringify(data));
});
Продолжаете разбираться, что делает приведенный выше код?
- Игровые константы различают типы сообщений и игровые состояния.
const GAME = 2; let gameObj = { ...省略 // 游戏状态 WAITTING: 0, START: 1, OVER: 2, RESTART: 3, // 当前轮到谁来绘图 isPlayer: false };
- data.type is GAME означает играть в игру
- data.state для оценки текущего прогресса игры
- Начало игр
- пустой холст
- Очистить историю чата и скрыть кнопку перезапуска
- Используйте data.isPlayer, чтобы различать, является ли это ящиком или угадывателем для отображения разных текстов.
- игра окончена
- Показать кнопку перезагрузки
- Показать победителя и ответить
- начать сначала
- Очистить холст, скрыть кнопки
- Начало игр
Вышеуказанные три пункта - это код для клиента для реализации многопользовательских игр, не останавливайтесь, это скоро закончится, и продолжайте писать логику сервера.
Сервер
// app.js文件
...省略
const LINE = 0;
const MESSAGE = 1;
// 添加游戏常量
const GAME = 2;
// 游戏状态和游戏逻辑
const WAITTING = 0;
const START = 1;
const OVER = 2;
const RESTART = 3;
let player = 0;
let wordsList = ['苹果', '运动鞋', '火箭', '足球', '小黄人', '汽车', '小鸟'];
let currentAnswer;
let currentState = WAITTING;
let timer;
// 连接的客户端数量
let len = 0;
io.on('connection', socket => {
...省略
// 将数据封装成json对象
...省略
// 把游戏的消息通知所有人
let game = {};
game.type = GAME;
game.state = WAITTING;
io.emit('message', JSON.stringify(game));
// 遍历客户端的连接
io.clients((err, client) => {
if (err) throw err;
len = client.length;
});
// 当前状态为等待并且连接数超过两个的时候才开始游戏
if (currentState === WAITTING && len > 2) {
startGame(socket);
}
socket.on('message', msg => {
...省略
// 判断是不是有玩家答对了
if (currentState === START && data.message === currentAnswer) {
let game = {};
game.type = GAME;
game.answer = currentAnswer;
game.winner = user;
game.state = OVER;
io.emit('message', JSON.stringify(game));
currentState = WAITTING;
clearTimeout(timer);
}
// 重新开始
if (data.state === RESTART && data.type === GAME) {
startGame(socket);
}
});
});
// 开始游戏方法
function startGame(socket) {
// 分配一个玩家来画画
player = (player + 1) % len;
// 随机分配个图案
let random = Math.floor(Math.random() * wordsList.length);
currentAnswer = wordsList[random];
// 通知所有玩家游戏开始
let data = {};
data.type = GAME;
data.isPlayer = false;
data.state = START;
io.emit('message', JSON.stringify(data));
// 遍历客户端,然后找到画画的那个用户告诉他相关data
let count = 0;
io.clients((err, client) => {
client.forEach(item => {
if (count === player) {
let game = {};
game.type = GAME;
game.state = START;
game.isPlayer = true;
game.answer = currentAnswer;
// 这条消息只有绘图的玩家才能看到
socket.send(JSON.stringify(game));
}
count++;
});
});
// 1分钟后游戏结束
timer = setTimeout(() => {
let obj = {};
obj.type = GAME;
obj.state = OVER;
obj.winner = '没有人啊!';
obj.answer = currentAnswer;
io.emit('message', JSON.stringify(obj));
}, 60 * 1000);
// 当前状态修改为START
currentState = START;
}
server.listen(8888);
Сервер, друг мой, что ты только что сделал?
- Добавьте игровые константы, состояние и логику игры.
// 游戏常量 const GAME = 2; // 游戏状态 const WAITTING = 0; ...省略 const RESTART = 3; // 游戏逻辑 let player = 0; ...省略 let len = 0;
- Инициализировать игру и количество игроков
// 把游戏的消息通知所有人 let game = {}; ...省略 io.emit('message', JSON.stringify(game)); // 遍历客户端的连接 io.clients((err, client) => { if (err) throw err; len = client.length; }); // 当前状态为等待并且连接数超过两个的时候才开始游戏 if (currentState === WAITTING && len > 2) { startGame(socket); }
- Запустить игру - StartGame
- Назначьте игроков нарисовать
player = (player + 1) % len;
- Случайно назначить шаблон
let random = Math.floor(Math.random() * wordsList.length); currentAnswer = wordsList[random];
- Уведомить всех игроков о начале игры
let data = {}; ...省略 io.emit('message', JSON.stringify(data));
- Пройдитесь по клиенту, а затем найдите пользователя, который рисовал, и сообщите ему соответствующие данные
let count = 0; io.clients((err, client) => { client.forEach(item => { // 匹配为分配的玩家才可以绘制答案 if (count === player) { let game = {}; ...省略 // 这条消息只有绘图的玩家才能看到 socket.send(JSON.stringify(game)); } count++; }); });
- 1 минута игры окончена и изменено текущее состояние
timer = setTimeout(() => { ...省略 }, 60 * 1000); currentState = START;
- Прослушайте сообщение, чтобы определить, правильно ли ответил кто-то
// 判断是不是有玩家答对了 if (currentState === START && data.message === currentAnswer) { let game = {}; game.type = GAME; game.answer = currentAnswer; game.winner = user; game.state = OVER; // 状态修改为OVER // 把该消息数据传递给所有玩家 io.emit('message', JSON.stringify(game)); // 恢复当前状态初始值 currentState = WAITTING; // 清空1分钟计时器 clearTimeout(timer); }
Ну а тут кончено, маленькие друзья могут подбежать и попробовать, конечно, это король стукнуть
Просто напишите это здесь, спасибо за вашу постоянную заботу, я желаю всем гладкой свиньи в 2019 году, ха-ха, 886последний последний: Адрес должен быть отправлен всем дляСсылаться накакие