Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > разработка игр, графический дизайн и моделирование > Gamedev - cоздание игр: Unity, OpenGL, DirectX
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 30.05.2017, 23:54   #51
_Михаил_
Форумчанин
 
Аватар для _Михаил_
 
Регистрация: 11.04.2015
Сообщений: 221
По умолчанию

Цитата:
Сообщение от 8Observer8 Посмотреть сообщение
На моём браузере танк проезжает меньше чем за 2 секунды. Вы не могли бы ещё раз проверить и сравнить с этой гифкой:
4 секунды уходит
_Михаил_ вне форума Ответить с цитированием
Старый 19.12.2020, 01:19   #52
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Ничего себе! Значит, перед тем, как я забросил эту игру, в мае 2017, у меня были наброски на WebGL-движке Phaser, где я реализовал механику движения танчика. Демку можно запустить в браузере по ссылке: https://8observer8.bitbucket.io/Phaser/BattleCity/dist/

Цитата:
Сообщение от _Михаил_ Посмотреть сообщение
либо оставить так как есть и указать те браузеры в которых игра пойдет корректно
Это был бы не вариант, потому что у пользователей разные браузеры.

Цитата:
Сообщение от _Михаил_ Посмотреть сообщение
искать решение, чтобы игра шла на всех браузерах одинаково.
Цитата:
Сообщение от _Михаил_ Посмотреть сообщение
4 секунды уходит
А это уже вариант. Надо было делать мне привязку к DeltaTime. На WebGL/JavaScript и на OpenGL/Qt/C++ это проблемы не будет.

После большого перерыва возвращаюсь к написанию клона Battle City по сети. Многие вещи глобально переосмыслил. Одно время была мысль, что выгодно взять Unity и C#, а ещё также реализовать версию на C# и OpenGL, чтобы за одно тренироваться с C# и практиковаться с шейдерами на чистом OpenGL. Но всегда зудела мысль, что C++ быстрее. Чистый C++ не так удобен, как C#, но зато есть такой же удобный Qt C++. Qt C++ гораздо более удобный для OpenGL, чем C#, потому что на C# нужно использовать отдельную библиотеку OpenTK от группы энтузиастов, а Qt включает в себя поддержку OpenGL на официальном уровне. Qt включает в себя: парсеры Json/Xml, загрузку текстур, матрицы, сеть, базы данных и многое другое. По Qt много вакансий и поэтому на него выгодно тратить время в отличие от других библиотек для окон: GLFW, SDL2 и т.д. Это сугубо моя точка зрения. На Qt можно легко собирать в APK и под Android и iOS. Второй важный момент - это версии игр для веб на WebGL, чтобы была возможность запустить игру в один клик прямов в браузере, без скачиваний, проверок на вирус, распаковок и т.д. Если кто-то захочет начать изучать шейдерный OpenGL и WebGL, то советую начать с этой книги: WebGL. Программирование трехмерной графики | Мацуда Коичи, Ли Роджер. Здесь примеры: Небольшие примеры на WebGL Игра должна быть в трёх версиях: Desktop, Mobile и Web.

В Qt встроена поддержка вебсокетов. Сервер я буду писать на Node.js с хостингом на бесплатном Heroku. Для сервера я использую пакет для вебсокетов: https://www.npmjs.com/package/ws Этот пакет очень популярен - за неделю более 35 млн. установок. Для упаковки спрайтов в текстурный атлас я использую Texture Packer Pro. Я купил эту программу несколько лет назад. Это программа упаковывает спрайты на атлас и генерирует два файла: атлас (png) и данные о спрайтах (.json). В JSON хранятся характеристики спрайтов: координаты на атласе, ширина, высота и т.д. Парсер JSON встроен в Qt и в JavaScript.

Оставлю здесь полезный пример парсинга JSON на Qt из консольного приложения:

Входной файл: Person.json

Код:
{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null
}
main.cpp

Код:
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtCore/QDebug>
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    QFile jsonFile(":/JsonFiles/Person.json");
    jsonFile.open(QIODevice::ReadOnly);
 
    // Get a first name and a last name
    QByteArray data = jsonFile.readAll();
    QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
    QJsonObject jsonObj = jsonDoc.object();
    qDebug() << jsonObj["firstName"].toString();
    qDebug() << jsonObj["lastName"].toString();
 
    // Get a phone number
    QJsonArray jsonArray = jsonObj["phoneNumbers"].toArray();
    qDebug() << jsonArray[0].toObject()["number"].toString();
 
    jsonFile.close();
    return a.exec();
}
Вывод программы в консоль:

"John"
"Smith"
"212 555-1234"

Последний раз редактировалось 8Observer8; 19.12.2020 в 01:23.
8Observer8 вне форума Ответить с цитированием
Старый 19.12.2020, 01:28   #53
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

TexturePacker оказывается написан на Qt:

09a8828b-7939-4ca8-ae07-ae197d406545.png

Я создал Sprite Sheet следующим образом. Нашёл в интернете набор тайлов игры (Tile Set). Нарезал в GIMP спрайты:

47b0af58-e8c3-4168-8ff7-2f001d72ee35.png

Эти спрайты перенёс мышкой в TexturePacker:

c07ec43a-c371-454d-9445-360d91d9c161.png

TexturePacker упаковал спрайты в один Sprite Sheet. Я нажал кнопку "Publish sprite sheet" и было сгенерировано два файла: сам Sprite Sheet, в виде png-файла, и файл json, по которому можно узнать текстурные координаты и размеры спрайтов:

b08e7f08-a80a-4f07-97a7-e00ce5f0e4af.png

Код:
{"frames": {
 
"tank_yellow_small_down_01.png":
{
    "frame": {"x":0,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_down_02.png":
{
    "frame": {"x":32,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_left_01.png":
{
    "frame": {"x":64,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_left_02.png":
{
    "frame": {"x":96,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_right_01.png":
{
    "frame": {"x":128,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_right_02.png":
{
    "frame": {"x":160,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_up_01.png":
{
    "frame": {"x":192,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
},
"tank_yellow_small_up_02.png":
{
    "frame": {"x":224,"y":0,"w":32,"h":32},
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32},
    "sourceSize": {"w":32,"h":32},
    "pivot": {"x":0.5,"y":0.5}
}},
"meta": {
    "app": "http://www.codeandweb.com/texturepacker",
    "version": "1.0",
    "image": "battle_city_tiles.png",
    "format": "RGBA8888",
    "size": {"w":256,"h":32},
    "scale": "1",
    "smartupdate": "$TexturePacker:SmartUpdate:ae1aaf8438413a614c6bc943a72c42c1:dde6a5be5dfe368d565574779e47cbd7:e2a7b58ac8aa2d4914a37feebbfe8a71$"
}
}
8Observer8 вне форума Ответить с цитированием
Старый 19.12.2020, 01:29   #54
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Пример кода, как из JSON-файла выше (то есть из файла описания Sprite Sheet, который хранит описания фреймов и другую информацию) извлечь размеры Sprite Sheet. Эти размеры нужны, чтобы пересчитать текстурные координаты. OpenGL будет воспринимать текстуру, как квадрат 1x1 юнитов.

Код:
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>

/* ... */

void Widget::initVertexBuffers()
{
    QFile jsonFile(":/Sprites/battle_city_spritesheet.json");
    jsonFile.open(QIODevice::ReadOnly);
 
    QByteArray data = jsonFile.readAll();
    QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
    QJsonObject jsonObj = jsonDoc.object();
 
    int w = jsonObj["meta"]
            .toObject()["size"]
            .toObject()["w"].toInt();
 
    int h = jsonObj["meta"]
            .toObject()["size"]
            .toObject()["h"].toInt();
 
    qDebug() << "w = " << w << ", h = " << h;
}
Вывод программы в консоль:

Цитата:
w = 256 , h = 32
8Observer8 вне форума Ответить с цитированием
Старый 19.12.2020, 01:32   #55
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Кроме сохранения в JSON, TexturePacker умеет сохранять Sprite Sheet в XML. В Qt из коробки есть модуль для работы с XML, как и с JSON. Модуль подключается в файле pro: QT += xml

Тоже самое, что и сообщением выше, но загрузка Sprite Sheet из XML. Комментарии в XML не я писал, они сами генерируются в TexturePacker. За одно у меня в коде происходит перевод пиксельных координат спрайта в текстурные координаты:

ea2ba7cc-5bb2-4b14-9e75-e4002f881ac0.png

Код:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with TexturePacker http://www.codeandweb.com/texturepacker-->
<!-- $TexturePacker:SmartUpdate:110c02a4b5ffaedca0fdfd7882cd424f:dde6a5be5dfe368d565574779e47cbd7:84e2e08cbb080f94c14db0f9b9c220e8$ -->
<!--Format:
n  => name of the sprite
x  => sprite x pos in texture
y  => sprite y pos in texture
w  => sprite width (may be trimmed)
h  => sprite height (may be trimmed)
pX => x pos of the pivot point (relative to sprite width)
pY => y pos of the pivot point (relative to sprite height)
oX => sprite's x-corner offset (only available if trimmed)
oY => sprite's y-corner offset (only available if trimmed)
oW => sprite's original width (only available if trimmed)
oH => sprite's original height (only available if trimmed)
r => 'y' only set if sprite is rotated
with polygon mode enabled:
vertices   => points in sprite coordinate system (x0,y0,x1,y1,x2,y2, ...)
verticesUV => points in sheet coordinate system (x0,y0,x1,y1,x2,y2, ...)
triangles  => sprite triangulation, 3 vertex indices per triangle
-->
<TextureAtlas imagePath="battle_city_spritesheet.png" width="102" height="102">
    <sprite n="tank_yellow_small_down_01.png" x="1" y="1" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_down_02.png" x="1" y="35" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_left_01.png" x="1" y="69" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_left_02.png" x="35" y="1" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_right_01.png" x="69" y="1" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_right_02.png" x="35" y="35" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_up_01.png" x="35" y="69" w="32" h="32" pX="0.5" pY="0.5"/>
    <sprite n="tank_yellow_small_up_02.png" x="69" y="35" w="32" h="32" pX="0.5" pY="0.5"/>
</TextureAtlas>
Код:
#include <QtXml/QDomDocument>
#include <QtCore/QFile>
 
/* ... */
 
void Widget::initVertexBuffers()
{
    QFile xmlFile(":/Sprites/battle_city_spritesheet.xml");
    xmlFile.open(QIODevice::ReadOnly);
 
    QDomDocument xmlDoc;
    xmlDoc.setContent(&xmlFile);
    xmlFile.close();
 
    QDomElement root = xmlDoc.documentElement();
    float spriteSheetWidth = root.attribute("width").toFloat();
    float spriteSheetHeight = root.attribute("height").toFloat();
 
    QDomElement sprite = root.firstChildElement();
    while(!sprite.isNull())
    {
        float x = 1.f / (spriteSheetWidth / sprite.attribute("x").toFloat());
        float y = 1.f / (spriteSheetHeight / sprite.attribute("y").toFloat());
        float w = 1.f / (spriteSheetWidth / sprite.attribute("w").toFloat());
        float h = 1.f / (spriteSheetHeight / sprite.attribute("h").toFloat());
 
        qDebug() << QString("x = %1, y = %2, w = %3, h = %4")
                    .arg(x).arg(y).arg(w).arg(h);
 
        sprite = sprite.nextSiblingElement();
    }
}
Вывод программы:

Цитата:
"x = 0.00980392, y = 0.00980392, w = 0.313726, h = 0.313726"
"x = 0.00980392, y = 0.343137, w = 0.313726, h = 0.313726"
"x = 0.00980392, y = 0.676471, w = 0.313726, h = 0.313726"
"x = 0.343137, y = 0.00980392, w = 0.313726, h = 0.313726"
"x = 0.676471, y = 0.00980392, w = 0.313726, h = 0.313726"
"x = 0.343137, y = 0.343137, w = 0.313726, h = 0.313726"
"x = 0.343137, y = 0.676471, w = 0.313726, h = 0.313726"
"x = 0.676471, y = 0.343137, w = 0.313726, h = 0.313726"
8Observer8 вне форума Ответить с цитированием
Старый 28.12.2020, 02:40   #56
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Самый простой (из известных мне) и бесплатный способ соединиться с удалённым сервером - это писать сервер с использованием Node.js/Express/WebSocket и использовать хостинг на Heroku или OpenShift. Официальная пошаговая инструкция для развёртывания на Heroku: Getting Started on Heroku with Node.js

Пример, в котором после соединения клиента с сервером - клиент и сервер выводят сообщения о соединении на экран. Используется бесплатный тариф, поэтому сервер засыпает если им не пользуются 30 минут, а чтобы разбудить, нужно подождать 10-15 секунд.

Содержимое архива desktop-клиента, собранного в релиз для Windows на Qt C++:

c9e9a689-7818-48c4-bfe6-85ed1276609b.png

Результат работы desktop-клиента на Qt C++:

80e5afbe-68fc-44f2-87cf-e70b17ba8f1e.png

Для отладки и релиз нужно на web-клиенте и на desktop-клиенте раскомментировать и закомментировать соответствующие строки:

На веб-клиенте:

Код:
    // const socket = new WebSocket("ws://localhost:3000");
    const socket = new WebSocket("wss://connection-websocket-js.herokuapp.com");
На на desktop-клиенте:

Код:
//    m_socket.open(QUrl("ws://localhost:3000"));
     m_socket.open(QUrl("wss://connection-websocket-js.herokuapp.com"));

Исходники web-клиента и сервера:

src/client/main.js
Код:
function main()
{
    const socket = new WebSocket("ws://localhost:3000");
    // const socket = new WebSocket("wss://connection-websocket-js.herokuapp.com");

    const output = document.getElementById("output");
    output.innerHTML = "Please, wait for connection..."

    socket.onopen = () =>
    {
        const message = "Client was connected to server";
        console.log(message);
        output.innerHTML = message;
    };
}

window.onload = main();
src/server/controllers/homeController.js

Код:
exports.index = (request, response) =>
{
    response.render("home.hbs");
};
src/server/routers/homeRouter.js

Код:
const express = require("express");
const homeController = require("../controllers/homeController");

const homeRouter = express.Router();
homeRouter.get("/", homeController.index);
module.exports = homeRouter;
src/server/views/layouts/layout.hbs

Код:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rectangle Coordinates. WebSockets, JavaScript</title>
</head>

<body>
    {{{body}}}
    <script src="js/bundle.js"></script>
    {{!-- <script src="js/bundle.min.js"></script> --}}
</body>

</html>
src/server/views/home.hbs

Код:
<div id="output"></div>
src/server/app.js

Код:
const express = require("express");
const http = require("http");
const path = require("path");
const ws = require("ws");
const homeRouter = require("./routers/homeRouter");

const app = express();
app.set("view engine", "hbs");
app.set("view options", { layout: "layouts/layout" });
app.set("views", path.join(__dirname, "/views"));
app.use(express.static(path.join(__dirname, "../../public")));
app.use("/", homeRouter);

const httpServer = http.createServer(app);
const wss = new ws.Server({ server: httpServer });

wss.on("connection", (socket) =>
{
    console.log("Client was coonected");
});

const port = process.env.PORT || 3000;
httpServer.listen(port, () => { console.log("Server started at port: " + port) });
package.json

Код:
{
  "name": "connection-websocket-js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node src/server/app.js",
    "clear": "del /f /q /s .\\public\\js\\*.*",
    "del-bundle": "del /f /q /s .\\src\\bundle.js",
    "bundle-debug": "browserify --debug src/client/main.js -o public/js/bundle.js",
    "bundle-release": "browserify src/client/main.js -o src/client/bundle.js",
    "uglify": "uglifyjs src/client/bundle.js -o public/js/bundle.min.js",
    "debug": "npm run bundle-debug",
    "release": "npm run clear && npm run bundle-release && npm run uglify && npm run del-bundle"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "hbs": "^4.1.1",
    "ws": "^7.3.1"
  }
}
Исходники desktop-клиента на Qt C++

main.cpp

Код:
// Add this line to .pro
// QT += websockets

#include "Widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
Widget.h

Код:
#ifndef WIDGET_H
#define WIDGET_H

#include <QtWidgets/QWidget>
#include <QtWidgets/QLabel>
#include <QtWebSockets/QWebSocket>

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget();
private slots:
    void onConnection();
private:
    QWebSocket m_socket;
    QLabel *m_pLabel;
};
#endif // WIDGET_H
Widget.cpp

Код:
#include <QtWidgets/QVBoxLayout>
#include <QtCore/QDebug>
#include "Widget.h"

Widget::Widget()
{
    setWindowTitle("Qt C++ Client");
    resize(300, 300);
    QFont font("Arial", 14);
    QVBoxLayout *vbox = new QVBoxLayout(this);
    m_pLabel = new QLabel("Please, wait for connection...", this);
    m_pLabel->setFont(font);
    vbox->addWidget(m_pLabel);
    vbox->addStretch(1);
    connect(&m_socket, &QWebSocket::connected, this, &Widget::onConnection);
//    m_socket.open(QUrl("ws://localhost:3000"));
    m_socket.open(QUrl("wss://connection-websocket-js.herokuapp.com"));
}

void Widget::onConnection()
{
    QString message = "Client was connected to server";
    qDebug() << message;
    m_pLabel->setText(message);
}
8Observer8 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Создание мультиплеера для GTA Vice City Mobile Kenix Мобильные ОС (Android, iOS, Windows Phone) 0 04.05.2013 15:13
Создание онлайн игры на движке Unity Entaro Фриланс 4 30.09.2010 10:03
Танчики PVD Gamedev - cоздание игр: Unity, OpenGL, DirectX 24 10.01.2010 21:25
Battle City NightWolfin Gamedev - cоздание игр: Unity, OpenGL, DirectX 0 17.05.2009 13:38
Ошибка в прорисовке карты для игры в танчики NSvirus Паскаль, Turbo Pascal, PascalABC.NET 1 27.02.2009 21:08