Сетевая игра Морской бой

исходники++ / Язык программирования C++ / Сетевая игра Морской бой
Оглавление:
  1. C++ сетевая игра
  2. Состав исходника сетевой игры Морской бой
  3. Интерфейс на основе диалоговых окон MFC
  4. Класс производный от CAsyncSocket
  5. Сетевой обмен игровых данных
  6. Кодировка символов UNICODE
  7. Обработчики сообщений поля игры
  8. Двойная буферизация графики GDI+
  9. Демо программа игры Морской бой
  10. Исходный код сетевой игры Морской бой

C++ сетевая игра

Исходник сетевой игры Морской бой написан на языке С++ и платформе быстрой разработки MFC (Microsoft Foundation Class). Программа-игра представляет несколько изменённый вариант классической версии. Вместо кораблей: крейсеров, линкоров, эсминцев, боевую флотилию представляют только одноклеточные лодки. Но одноклеточные боевые корабли делают игру не менее интересной, в данном случае повышается коэффициент случайности ходов и тем самым повышается сложность игры.

Сетевой обмен Морского боя построен на протоколе TCP. В качестве класса сокетов применяются асинхронный CAsyncSocket библиотеки MFC. Класс CAsyncSocket инкапсулирует функциональность сокетов Windows на очень низком уровне и предоставляет удобный объектно-ориентированный интерфейс.

Сетевое приложение Морской бой имеет совмещенную сетевую функциональность: сервер и клиент в одной программе. Любой из игроков может быть сервером, либо клиентом.

Состав исходника сетевой игры Морской бой

Представление классов приложения SeaBattle
Представление классов приложения SeaBattle

Исходник состоит из 6-ти классов: CAboutDlg, CFuncSocket, CGameCanvas, CMFCSeaBattleApp, CMFCSeaBattleDlg, TypeMessage и 3-х структур: CGameCanvas::SHOTDATA, CGameCanvas::SHIPDATA, CGameCanvas::MESSAGEDATA. Структуры ограничены областью видимости класса CGameCanvas для организации программного кода и упрощения поиска объектов в окне подсказок Visual Studio IntelliSense.

CAboutDlg – вспомогательный оконный класс для представления пользователю информации о приложении и разработчике.

CFuncSocket – класс асинхронных сокетов для организации сетевого обмена данными между приложениями игры.

CGameCanvas – оконный класс графики игрового поля. Отправляет необходимые сообщения в родительский класс CMFCSeaBattleDlg.

CMFCSeaBattleApp – класс, представляющий приложение, основной поток выполнения программы. В каркасах C++ MFC объект класса приложения существует в единственном экземпляре.

CMFCSeaBattleDlg – класс основной игровой и сетевой логики. Содержит в себе объекты классов CFuncSocket и CGameCanvas. Организовывает обмен данными по сети и вносит в графику моря битвы соответствующие изменения.

TypeMessage – вспомогательный enum class (появился в С++ 11 версии) для распознавания и последующей обработки сетевых сообщений между приложениями игры Морской бой. Почему enum class, но не просто перечисление enum - для ограничения области видимости членов перечисления с целью организации кода и удобства поиска в окне автодополнения Visual Studio IntelliSense.

Интерфейс на основе диалоговых окон MFC

 Форма диалогового окна C++ MFC
Форма диалогового окна в дизайнерском режиме

Для быстрой и визуальной разработки С++ Windows приложения сетевая игра Морской бой написана используя программный код библиотеки MFC. Приложение Морской бой создано на основе диалоговых окон с помощью мастера MS Visual Studio. Работа с данным типом приложения напоминает визуальную работу с формами платформы C# Windows Forms.

Графика игры определена в классе CGameCanvas производном от CWnd. CGameCanvas связан с пользовательским элементом управления CustomControls, который размещен на форме диалогового окна, на скриншоте выше данные элементы серого цвета. Элемент управления CustomControls визуализирует расположение игрового поля, определяет размер окна игровой графики и перенаправляет уведомления о действиях пользователя в методы-обработчики CGameCanvas.

Объявление класса CGameCanvas:
#pragma once
#include 

#define GAMEWNDCLASS _T("GameWindow")

// Типы сообщений по сети.
enum TypeMessage : char { NewGame, ResponseNewGame, Shot, ResulShot, Message };


class CGameCanvas : public CWnd
{
	DECLARE_DYNAMIC(CGameCanvas)

protected:
	DECLARE_MESSAGE_MAP()

public:
	CGameCanvas();
	BOOL RegisterWndClass();
	virtual ~CGameCanvas();

	static const int CGameCanvas::s_CellsHorizontal = 10;
	static const int CGameCanvas::s_CellsVertical = 10;
	static const int CGameCanvas::s_ShipsNumber = 10;
	static BOOL s_CanMove;
	static BOOL s_IsSound;

private:
	// Закрытые вспомогательные структуры

	struct SHOTDATA
	{
		BOOL checked = FALSE;
		BOOL goal = FALSE;
	};
	
	struct SHIPDATA
	{
		POINT pos = { -1,-1 };
		BOOL destroyed = FALSE;
		BOOL visible = FALSE;
	};

public:
	// Открытые вспомогательные структуры

	struct MESSAGEDATA
	{
		// Тип сообщения
		TypeMessage typeMessage;
		BOOL destroyed = FALSE;
		POINT shotCoordinates{};
		TCHAR message[128] = { 0 };
	};

private:
	// События 
	
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);

private:
	// Закрытые переменные и методы логики игры.

	// Габариты игрового поля
	int m_width;
	int m_Height;

	BOOL m_disableLButton;

	SHIPDATA m_ships[CGameCanvas::s_ShipsNumber];
	SHOTDATA m_shotsData[CGameCanvas::s_CellsHorizontal][CGameCanvas::s_CellsVertical];
	PointF m_CoordinateCells[CGameCanvas::s_CellsHorizontal][CGameCanvas::s_CellsVertical];

	float m_CellWidth = 0;
	float m_CellHeight = 0;

	void DistributeShips();
	int CheckDestroyed(int col, int row);

	// Проигрывание звука с разрешения.
	void PlayWave(unsigned int idWave)
	{
		if (CGameCanvas::s_IsSound == TRUE) 
			PlaySound(MAKEINTRESOURCE(idWave), 
				GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
	}
	

public:
	// Открытые переменные и методы логики игры. 

	void InitGameCanvas(BOOL disablelbutton);
	BOOL DoShot(int col, int row, BOOL send);
	void NewGame(BOOL distrships);
	BOOL IsGameOver();
	BOOL PositionShipEmptyPlace(int col, int row);
};

Класс производный от CAsyncSocket

В качестве сокетов для сетевого взаимодействия в игровом приложении используются н асинхронные сокеты класса CAsyncSocket. Работа асинхронных сокетов CAsyncSocket построена на уведомлениях о сетевых событиях, для этого класс имеет защищенные виртуальные методы: OnAccept(), OnConnect(), OnSend(), OnReceive(), OnClose().

Его методы устанавливающие сетевое соединение (например, CAsyncSocket::Accept, CAsyncSocket::Connect) и обмена данными по сети (CAsyncSocket::Send, CAsyncSocket::Receive) не блокируют пользовательский интерфейс и выполняют свою работу только после соответствующего уведомления для сокета.

Чтобы получать уведомления о сетевых событиях необходимо создать производный от CAsyncSocket собственный класс. В сетевой игре Морской бой производный класс CFuncSocket отправляет уведомления для сокетов в родительское окно, где и происходит обработка сетевых событий. Передача уведомлений выполнятся прямым вызовом соответствующих методов родительского окна. Для этого класс CFuncSocket при вызове конструктора получает указатель на родительское окно.

Объявление класса сокетов:
class CFuncSocket : public CAsyncSocket
{
private:
    CWnd* m_pWnd;

public:
    CFuncSocket(CWnd* pWnd) :
        m_pWnd(NULL)
    {
        m_pWnd = pWnd;
    }

    virtual void OnAccept(int nErrorCode);
    virtual void OnClose(int nErrorCode);
    virtual void OnConnect(int nErrorCode);
    virtual void OnReceive(int nErrorCode);
    virtual void OnSend(int nErrorCode);
};

Сетевой обмен игровых данных

Интерфейс подключения игроков унифицирован для сервера и клиента. Логика событий нажатия на радиокнопки сервер или клиент задает режим работы приложения и выбирает соответствующую функцию для подключения игроков друг к другу.

Сервер прослушивает все IP-адреса устройств установленных на компьютере. Редактировать IP-адрес сервера нет необходимости. На клиентском приложении вводится IP-адрес сервера. Оба приложение должны использовать один и тот же номер порта.

Отправка по сети и получение данных приложением другого игрока как правило происходит с временно́й разницей, зависящей от скорости и загруженности сети. Из-за этого факта синхронность игрового интерфейсов сетевых приложений может нарушаться. В исходном коде игры Морской бой, для синхронизации, сетевой обмен данными построен на подтверждениях другим приложением принятия и обработки полученной информации.

Структура обмена данными по сети имеет размер не более 300 байт. Структура включает минимальный набор необходимых переменных для сетевого обмена игровыми данными между приложениями. Основной объем занимает массив символов для текстовых сообщений.

Структура обмена данными по сети между приложениями игры Морской бой:
// Структура обмена данными по сети. 
struct MESSAGEDATA
{
    TypeMessage typeMessage;
    BOOL destroyed = FALSE;
    POINT shotCoordinates{};
    TCHAR message[128] = { 0 };
};

Кодировка символов UNICODE

В приложении применена кодировка текстовых символов UNICODE, так что общение между игроками может происходить на любом языке (в т. ч. и иероглифами). Программный код имеет универсальные определения символов и переключиться на многобайтовый набор символов (MBCS) можно без редактирования кода, изменяя только настройки проекта. Надо заметить, что многобайтовая кодировка не поддерживает расширенные символы.

Обработчики сообщений поля игры

Класс CGameCanvas включает три обработчика сообщений Windows:
- OnEraseBkgnd(CDC* pDC) – обработка сообщений стирания фона окна для рисования графики игры;
- OnLButtonDown(UINT nFlags, CPoint point) – обработка сообщений нажатия левой кнопки мыши для взаимодействия пользователя с морем битвы;
- OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) – обработка сообщений определения курсора окна для установки собственного курсора (перекрестие с мушкой) над морем битвы соперника.

Двойная буферизация графики GDI+

Графика игрового поля игры Морской бой создана на API Windows GDI+ . Продвинутая библиотека GDI+ предназначена для С/С++ программистов и серьезно отличается от просто GDI графики. Написание графического интерфейса на GDI+ очень похоже на создание графики в C# Windows Forms.

Рисование моря битвы многоэтапное: фон окна, координатная сетка, корабли, отметки выстрелов и т. д., при стандартном построении, каждый этап неизбежно вносит свой минимум в мерцание окна при обновлении. В программном коде игры используется способ предварительного объединения всей графики в памяти и последующим выводом на поверхность окна, что исключает мерцание при обновлении.

Демо программа игры Морской бой

Сетевую игру, в качестве демо-программы, можно скачать в разделе Программ и игр, на странице Сетевая игра Морской бой .

Исходный код сетевой игры Морской бой

Архивный файл исходника содержит программный код и файл скомпилированной программы Морской бой. Исходный код написан на языке С++ с применением библиотеки быстрой разработки приложений MFC.

Приложение С++ создано в среде программирования MS Visual Studio 2022. Графика игры использует продвинутую библиотеку GDI+.

Доступ к исходнику

Для скачивания исходника необходимо ввести код:

Оплата кода доступа

Стоимость доступа к исходнику - 340 ₽

Сразу после оплаты активируется ссылка на скачивание исходника. Также, на указанный e-mail высылается код доступа. Код действует 2 суток. Внимание: письмо может попасть в папку спама.

Скачать исходник

Тема: «Сетевая игра Морской бой» Сетевой Морской бой💾MFCSeaBattle-vs17.zipРазмер:3536 КбайтЗагрузки:21