предисловие
В первых двух главах руководства мы использовали базовые функции WebSocket для создания небольшого чата и группировали его во второй главе.
Обзор серии учебников:
[WebSocket] Глава 1. Создание многопользовательского онлайн-чата WebSocket (SpringBoot + WebSocket)
В этой статье я расскажу, как использовать WebSocket для предоставления сервера для многопользовательской игры-викторины в реальном времени, а также подробно представлю дизайн коммуникационного интерфейса.
Это небольшой проект, который я разработал на недавнем конкурсе домашних заданий. Я разработал весь игровой процесс и внутренний код с моими друзьями. Страница внешнего интерфейса пока не будет вам предоставлена. Вы можете обратиться к первому две главы и напишите сами.
Краткое содержание этой статьи:
- Общие схемы коммуникации для онлайн-игр
- Как использовать WebSocket для реализации связи в реальном времени в игровых сражениях
- Экранная демонстрация шагов игры и соответствующий дизайн интерфейса WebSocket
Исходный код этой статьи:(Маме больше не нужно беспокоиться о том, что я не могу воспроизвести код статьи)
GitHub.com/QQ Xianxia6661/Башня…
текст
WebSocket реализует многопользовательские онлайн-игры - боевая викторина
Общие схемы коммуникации для онлайн-игр
Ссылаться на:
HTTP
Преимущества: протокол является относительно зрелым, широко используется, основан на TCP/IP, имеет преимущества TCP, низкие затраты на исследования и разработки, быструю разработку и множество программ с открытым исходным кодом, таких как nginx, apache, tomact и т. д.
Недостатки: без сохранения состояния, без подключения, только режим PULL, не поддерживает PUSH, большие пакеты данных
Характеристики: на основе протокола прикладного уровня TCP/IP, без сохранения состояния, без установления соединения, поддержка режима C/S, подходит для передачи текста
TCP
Преимущества: надежность, полнодуплексный протокол, поддержка открытого исходного кода, широкое применение, ориентированность на соединение, низкая стоимость НИОКР, неограниченное содержимое пакета (автоматическое субконтрактирование на уровне IP, повторная передача, не более 1452 байт).
Недостатки: операционная система: больший расход памяти, ограниченное количество поддерживаемых подключений, дизайн: более сложный протокол, настраиваемый протокол прикладного уровня, сеть: более высокая задержка при плохих сетевых условиях, передача: менее эффективна, чем протокол UDP.
Особенности: ориентированный на соединение, надежный, полнодуплексный протокол, основанный на уровне IP, эталонная модель OSI расположена на транспортном уровне, подходит для двоичной передачи
WebScoket
Преимущества: более зрелый протокол, основанный на TCP/IP, с преимуществами TCP, пакет данных небольшой, заголовок пакета очень маленький, ориентированный на соединение протокол с отслеживанием состояния, более открытый исходный код, более быстрая разработка.
недостаток:
Особенности: Stateful, ориентированный на соединение, небольшой заголовок данных, подходящий для WEB3.0 и других мгновенных сетевых коммуникаций.
UDP
Преимущества: Операционная система: высокий уровень параллелизма, низкое потребление памяти, передача: высокая эффективность, низкая задержка в сети, простая модель передачи, низкие затраты на НИОКР.
Недостатки: ненадежный протокол, односторонний протокол, меньшая поддержка открытого исходного кода, ограниченное содержимое сообщения, не более 1464 байт, дизайн: дизайн протокола более сложный, сеть: плохая сеть, пакеты данных теряются.
Особенности: без установления соединения, ненадежный, основанный на уровне протокола IP, эталонная модель OSI на транспортном уровне, доставка с максимальной эффективностью, подходит для двоичного транспорта
Суммировать
- Для слабых сетевых игр необходимо исключить тип, а тип карты может напрямую использовать протокол HTTP.Если рассматривается безопасность, напрямую HTTPS или выполнять симметричное шифрование тела контента;
- Для работы в реальном времени интерактивные требования высоки, вы можете сначала выбрать Websocket, а затем протокол TCP;
- Для чрезвычайно высоких требований к реальному времени и доступности обычно можно выбрать протокол UDP;
- Для сражений и гонок по локальной сети просто используйте протокол UDP;
WebSocket реализует связь в режиме реального времени для онлайн-игр для двух игроков.
Мы используем веб-сокет в качестве нашей схемы связи, главным образом потому, что мы хотим, чтобы обе стороны отображали результаты друг друга в режиме реального времени.
В этом разделе подробно описано определение конкретного метода связи через веб-сокет в нашей онлайн-викторине.
Правила этой викторины следующие:
- После того, как пользователь открывает страницу h5, он вводит свой никнейм и отправляет его на сервер, сервер сохраняет никнейм пользователя в хэш-карту и фиксирует статус пользователя (неактивен, в игре), после чего пользователь попадает в лобби.
- Пользователи в лобби могут выбирать друг друга. Как только пользователь выбирает другого пользователя, игра запускается, и обе стороны переходят в режим ответа.
- Каждый из двух пользователей, ответивших на вопрос, ответил на 10 вопросов, и каждый правильный ответ приносит 10 баллов, всего 100. На верхней левой странице отображаются их собственные баллы, а в верхнем правом углу — баллы другой стороны, а оценка другой стороны получена через веб-сокет в режиме реального времени.
- В конце 10 вопросов обе стороны ждут общего счета друг друга и, наконец, определяют победителя и проигравшего и отображают интерфейс результатов.
Итак, нам нужно разработать три протокола WebSocket:
- Пользователь создает никнейм и входит в лобби игрока
- Пользователь выбирает соперника, и обе стороны вступают в игру.
- Во время боя в режиме реального времени отображаются очки обеих сторон.
Далее мы подробно представим эти три интерфейса WebSocket.
Пользователь создает никнейм и входит в лобби игрока
Откройте интерфейс и войдите в игру:
Мы используем HashMap для хранения состояния пользователя,
private Map<String, StatusEnum> userToStatus = new HashMap<>();
Пользовательское состояние делится на простое и внутриигровое:
public enum StatusEnum {
IDLE,
IN_GAME
}
Интерфейс WebSocket устроен следующим образом:
Код интерфейса WebSocket выглядит следующим образом:
@MessageMapping("/game.add_user")
@SendTo("/topic/game")
public MessageReply addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) throws JsonProcessingException {
MessageReply message = new MessageReply();
String sender = chatMessage.getSender();
ChatMessage result = new ChatMessage();
result.setType(MessageTypeEnum.ADD_USER);
result.setReceiver(Collections.singletonList(sender));
if (userToStatus.containsKey(sender)) {
message.setCode(201);
message.setStatus("该用户名已存在");
message.setChatMessage(result);
log.warn("addUser[" + sender + "]: " + message.toString());
} else {
result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));
message.setCode(200);
message.setStatus("成功");
message.setChatMessage(result);
userToStatus.put(sender, StatusEnum.IDLE);
headerAccessor.getSessionAttributes().put("username",sender);
log.warn("addUser[" + sender + "]: " + message.toString());
}
return message;
}
Пользователь выбирает соперника, и обе стороны вступают в игру.
Выберите игрока в лобби, а затем войдите в бой:
Мы используем HashMap для хранения пользователей, которые сражаются, и соединяем две стороны.
private Map<String, String> userToPlay = new HashMap<>();
Интерфейс WebSocket устроен следующим образом:
Код интерфейса WebSocket выглядит следующим образом:
@MessageMapping("/game.choose_user")
@SendTo("/topic/game")
public MessageReply chooseUser(@Payload ChatMessage chatMessage) throws JsonProcessingException {
MessageReply message = new MessageReply();
String receiver = chatMessage.getContent();
String sender = chatMessage.getSender();
ChatMessage result = new ChatMessage();
result.setType(MessageTypeEnum.CHOOSE_USER);
if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IDLE)) {
List<QuestionRelayDTO> list=new ArrayList<>();
questionService.getQuestions(limit).forEach(item->{
QuestionRelayDTO relayDTO=new QuestionRelayDTO();
relayDTO.setTopic_id(item.getId());
relayDTO.setTopic_name(item.getQuestion());
List<Answer> answers=new ArrayList<>();
answers.add(new Answer(1,item.getId(),item.getOptionA(),item.getResult()==1?1:0));
answers.add(new Answer(2,item.getId(),item.getOptionB(),item.getResult()==2?1:0));
answers.add(new Answer(3,item.getId(),item.getOptionC(),item.getResult()==3?1:0));
answers.add(new Answer(4,item.getId(),item.getOptionD(),item.getResult()==4?1:0));
relayDTO.setTopic_answer(answers);
list.add(relayDTO);
});
result.setContent(mapper.writeValueAsString(list));
result.setReceiver(Arrays.asList(sender, receiver));
message.setCode(200);
message.setStatus("匹配成功");
message.setChatMessage(result);
userToStatus.put(receiver, StatusEnum.IN_GAME);
userToStatus.put(sender, StatusEnum.IN_GAME);
userToPlay.put(receiver,sender);
userToPlay.put(sender,receiver);
log.warn("chooseUser[" + sender + "," + receiver + "]: " + message.toString());
} else {
result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));
result.setReceiver(Collections.singletonList(sender));
message.setCode(202);
message.setStatus("该用户不存在或已在游戏中");
message.setChatMessage(result);
log.warn("chooseUser[" + sender + "]: " + message.toString());
}
return message;
}
Во время боя в режиме реального времени отображаются очки обеих сторон.
Демонстрационная карта во время боя: левая сторона показывает наш счет, правая сторона показывает счет противника
Интерфейс WebSocket устроен следующим образом:
Код интерфейса WebSocket выглядит следующим образом:
@MessageMapping("/game.do_exam")
@SendTo("/topic/game")
public MessageReply doExam(@Payload ChatMessage chatMessage) throws JsonProcessingException {
MessageReply message = new MessageReply();
String sender = chatMessage.getSender();
String receiver = userToPlay.get(sender);
ChatMessage result = new ChatMessage();
result.setType(MessageTypeEnum.DO_EXAM);
log.warn("userToStatus:" + mapper.writeValueAsString(userToStatus));
if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IN_GAME)) {
result.setContent(chatMessage.getContent());
result.setSender(sender);
result.setReceiver(Collections.singletonList(receiver));
message.setCode(200);
message.setStatus("成功");
message.setChatMessage(result);
log.warn("doExam[" + receiver + "]: " + message.toString());
}else{
result.setReceiver(Collections.singletonList(sender));
message.setCode(203);
message.setStatus("该用户不存在或已退出游戏");
message.setChatMessage(result);
log.warn("doExam[" + sender + "]: " + message.toString());
}
return message;
}
дальше
Это всего лишь демо, которое вышло за два дня, конечно, между готовыми продуктами еще большой разрыв. Вот несколько вещей, над которыми нужно продолжать работать:
- Реализовать автоматическое сопоставление/таблицу лидеров
- Оптимизация связи через WebSocket: в некоторых местах используйте двухточечную связь, а не всю широковещательную связь.
Мы можем использовать метод convertAndSendToUser(), о чем можно судить по названию, а метод convertAndSendToUser() позволяет нам отправить сообщение конкретному пользователю.
Spring webscoket может идентифицировать и обрабатывать пути подписки с помощью «/user», например, если клиент браузера подписывается на путь «/user/topic/greetings»,
stompClient.subscribe('/user/topic/greetings', function(data) {
//...
});
Он будет преобразован в «/topic/greetings-usererbgz2rq», «usererbgz2rq» весенним веб-сокетом с использованием UserDestinationMessageHandler, user — ключевое слово, erbgz2rq — идентификатор сеанса, так что пользователь и путь подписки однозначно совпадают.
использованная литература
Одноранговая связь:
blog.CSDN.net/Следует уволить/Ах…
Суммировать
В этой статье мы реализуем дизайн интерфейса WebSocket на стороне сервера для многопользовательских боевых онлайн-игр, что еще больше укрепляет понимание основы и области применения WebSocket.
Исходный код этого проекта:
GitHub.com/QQ Xianxia6661/Башня…
Подписывайтесь на меня
В настоящее время я backend-разработчик. Основное внимание уделяется back-end разработке, безопасности данных, поисковым роботам, граничным вычислениям и другим направлениям.
WeChat: yangzd1102 (укажите цель)
Гитхаб:@qqxx6661
личный блог:
- CSDN:@Rude3Knife
- Знаю почти:@Zhendong
- Краткая книга:@Многие три ножа и ножи
- Самородки:@Многие три ножа и ножи
Оригинальное основное содержание блога
- Полное руководство по обзору знаний Java
- Анализ вопросов алгоритма Leetcode
- Меч относится к анализу проблемы алгоритма предложения.
- Боевая серия для новичков SpringCloud
- Боевая серия для новичков SpringBoot
- Технические статьи о краулерах
- Технические статьи, связанные с бэкенд-разработкой
Личный публичный аккаунт: разговор о технологиях back-end
Если статья была вам полезна, пожалуйста, соберите ее и перешлите своим друзьям~