Advertisement
Guest User

infected

a guest
Apr 24th, 2019
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Latex 29.68 KB | None | 0 0
  1. \documentclass{article}
  2. \usepackage[utf8]{inputenc}
  3. \usepackage[english, russian]{babel}
  4. \usepackage{pgfplots}
  5.  
  6. \begin{document}
  7.     %Титульный лист
  8.     \begin{titlepage}
  9.        \centering
  10.        {\scshape\LARGE СУНЦ МГУ \par}
  11.        \vspace{1cm}
  12.        {\scshape\Large Проектная работа\par}
  13.        \vspace{1.5cm}
  14.        {\huge\bfseries Игра "Infection"\par}
  15.        \vspace{2cm}
  16.        {\Large\itshape Искандаров Лев\par}
  17.        \vfill
  18.  
  19.        {\large Москва, 2018 г.\par}
  20.    \end{titlepage}
  21.    
  22.     %Содержание
  23.     \tableofcontents
  24.     \newpage
  25.    
  26.     %Контент
  27.     \section{Постановка задачи}
  28.    
  29.    \subsection{Общее описание}
  30.    
  31.    \paragraph{Задача} Создать многопользовательскую браузерную игру в стиле RTS-платформера.
  32.    
  33.    \paragraph{Цель игры} Две команды с помощью различных построек и улучшений должны уничтожить друг друга. Выигрывает выжившая сторона.
  34.    
  35.    \paragraph{Правила игры} Каждый игрок, может изменять мир и строить различные постройки, дающие ему ресурсы или наносящие другой стороне урон.
  36.    
  37.    \paragraph{Требования к реализации} Игра должна быть реализована на языке JavaScript с использованием библиотек по необходимости.
  38.    
  39.    \subsection{Описание игровых процессов}
  40.    
  41.    Необходимые реализации:
  42.    \begin{itemize}  
  43.    \item Клиент-серверная архитектура
  44.    \item Физический движок
  45.    \item Оптимизированное хранение и передача игрового мира
  46.    \item Случайный генератор мира
  47.    \item Сторона людей
  48.    \item Сторона вируса
  49.    \item Пользовательский интерфейс
  50.    \end{itemize}
  51.    
  52.    \subsection{Описание действий, выполняемых игроком}
  53.    
  54.    \begin{itemize}  
  55.    \item Сначала, игроку следует зарегистрироваться, войти в комнату, выбрать сторону и нажать "готов" и ждать начала игры
  56.    \item Далее, игрок может перемещаться по миру, ставить и удалять блоки
  57.    \end{itemize}
  58.    
  59.    \section{Используемые модели и технологии}
  60.    
  61.    \subsection{Используемые технологии}
  62.    
  63.    \begin{itemize}  
  64.    \item \textbf{Node.js} --- программная платформа для сервера
  65.    \item \textbf{PixiJS} --- графическая библиотека для отрисовки
  66.    \item \textbf{Socket.IO} --- библиотека для клиент-серверного общения
  67.    \item \textbf{Bootstrap} --- библиотека для упрощения работы с HTML и создания GUI
  68.    \item \textbf{JSON Web Token} --- библиотека для создания зашифрованного ключа-идентификатора пользователя
  69.    \item Несколько вспомогательных библиотек
  70.    \end{itemize}
  71.    
  72.    \subsection{Используемые модели}
  73.    
  74.    Сервер использует собственный физический движок (\ref{section:physics}), генерацию мира с помощью шумов (\ref{section:worldgen}). Так же, используются методы оптимизации и реализована клиент-серверная архитектура: например разбиение мира на чанки для ускорения рендера и передачи данных (\ref{section:optimize}).
  75.    
  76.    \subsubsection{Физический движок}
  77.    \label{section:physics}
  78.    
  79.    \paragraph*{Два объекта.}
  80.    Допустим, есть два физических объекта $a$ и $b$ и время между двумя игровыми тиками $dt$. Мы хотим понять, столкнутся ли они за это время, и если да, что-то с этим сделать.
  81.    
  82.    Для начала, определим объект как прямоугольник, со сторонами, параллельными координатной сеткой, заданный левой верхней точкой и его размерами, а так же скоростью.
  83.    
  84.     Возвращать наша функция будет время, через которое два объекта столкнутся (или $dt + 1$, если за время $dt$ они не столкнутся) и нормаль ко второму объекту, которая характеризует ту сторону, с которой произошло столкновение.
  85.    
  86.    Воспользуемся теоремой о разделяющей оси, упрощенной для нашего случая: найдем минимальную и максимальную проекцию на координатные оси для каждого объекта. Если по хотя бы одной из осей мы можем найти точку, которая лежит между двумя объектами, то в данный момент столкновения нет. Иначе, объект находится внутри другого.
  87.    
  88.    Но это лишь проверка на пересечение. Усовершенствуем алгоритм: найдем расстояние между этими крайними точками, поделим на проекцию скорости на данную ось и получим время, через которое объекты столкнутся по данной оси. Выберем из двух времен максимум и это будет время столкновения. Нужно лишь проверить, что по другой оси в это время мы все еще пересекаемся, а не вышли за объект. Время найдено, а нормаль к стороне определяется довольно просто исходя из знаков скорости и оси, по которой происходит столкновение.
  89.    
  90.    Остается только реакция на столкновение. Подвинем объект на то время, которое вернула данная функция. Если столкновение было, то изменилась скорость, а именно по той оси, по которой произошло столкновение она стала равна 0 - эта ось задается вектором нормали. Изменим скорость и подвинем объект на оставшееся время.
  91.    
  92.    \paragraph*{Обработка одного объекта с массивом преград.}
  93.    Мы хотим за линейное время обработать один подвижный объект и массив неподвижных преград (например, игрок и объекты игрового мира).
  94.    
  95.    Пройдемся по всем объектам и найдем первый объект, с которым произойдет столкновение. Произведем обычную реакцию, за исключением того, что двигать на оставшееся время просто так не будем. У нас изменилась скорость, изменилось время, но задача осталась та же самая. Теперь, будем делать тоже самое, пока скорость и оставшееся время не будет равно 0.
  96.    
  97.    Заметим, что если при столкновении объектов скорость никак не изменяется извне (например, подъем игрока, если он столкнулся с шипами), то данный алгоритм будет проходится не более 2-х раз: если столкновений нет, то мы просто двигаем объект все оставшееся время. Иначе, после каждого из столкновений, по одной из осей скорость становится равной 0. Так как по этой же оси мы уже не сможем столкнуться еще раз, так как мы никуда не двигаемся, то остается только движение по второй оси, и, либо мы просто движемся оставшееся время, либо мы сталкиваемся по ней, и скорость равна 0, т.е. алгоритм прекращает свою работу.
  98.    
  99.    \subsubsection{Генерация мира с помощью шумов}
  100.    \label{section:worldgen}
  101.    
  102.    \paragraph*{Шум Перлина.}
  103.    По сути, мы хотим сгенерировать сглаженный набор случайных величин. Для это воспользуемся алгоритмом Перлина. Рассмотрим его двумерную реализацию.
  104.    
  105.    Создадим двумерную сетку с целыми координатами. В узлы сетки запишем единичный вектор случайного направления.
  106.    
  107.    Теперь, есть точка с координатами $(x, y)$ и мы хотим получить для нее значение шума $\left[-1, 1\right]$. Найдем единичный квадрат (границы сетки), в котором лежит наша точка (т.е. по сути, отбросим дробную часть координат). Найдем векторы из вершин этого квадрата в нашу точку. Теперь, есть 4 пары по два вектора: для каждой вершины есть два вектора: записанный в вершине и вектор до точки. Найдем для каждой вершины скалярное произведение этих двух векторов. Получим 4 числа.
  108.    
  109.    Далее, линейно интерполируем попарно, например, верхние и нижние числа, а затем получившиеся два. Это и будет результат функции шума.
  110.    
  111.    \paragraph*{Дополнения.}
  112.    
  113.    \begin{itemize}
  114.         \item Вместо генерации случайной сетки заведем 256 векторов случайного направления, создадим массив перестановки из 256 элементов, с помощью математических преобразований будем получать для каждой вершины псевдослучайный элемент этой перестановки, т.е. случайный вектор.
  115.        \item Вместо 256 случайных векторов возьмем 12, описанных Перлином (в каждой плоскости вектор в углы плоскости).
  116.        \item После подсчета каждого скалярного произведения, возьмем от него функцию сглаживания: например, smootherstep $= 6x^5-15x^4+10x^3$. Это примерно прямая, которая в окрестности 0 ближе к 0, а в окрестности 1 ближе к 1.    
  117.    \end{itemize}
  118.    
  119.    \begin{tikzpicture}
  120.    \begin{axis}[
  121.    xlabel=$x$,
  122.    ylabel={$smootherstep$}
  123.    ]
  124.    \addplot [draw=blue][domain=0:1]{x};
  125.    \addplot [domain=0:1]{6*x^5-15*x^4+10*x^3};
  126.    \end{axis}
  127.    \end{tikzpicture}    
  128.    
  129.    \paragraph*{Генерация мира}
  130.    Используя функцию от трех координат, где последняя (и, если нужно 1 измерение - последние две) является зерном можно сгенерировать карту.
  131.    
  132.    Сначала, генерируется карта высот (одномерный случай): простой проход по всем столбцам и генерация для них функции шума, которая будет высотой в данной точке.
  133.    
  134.    Затем, в этих высотах я ставлю блок травы, и под ним случайное (2-4) блока земли. Так же, случайно расставляю блоки травы поверх блока травы.
  135.    
  136.    Далее, генерация пещер. Для каждого блока ниже уровня карты высот генерируется функциюя шума. Если она больше границы пещер (с глубиной эта граница увеличивается), то остается блок воздуха, иначе - блок камня. После всего этого, идет проход по блокам пещер и проверка, если у блока есть соседний блок воздуха, то данный блок становится землей (границы пещер).
  137.    
  138.    В конце происходит добавление деревьев: заводится переменная "сколько блоков назад было дерево", и, если текущее случайное число меньше этой переменной, то поверх блока травы устанавливается блок дерева.
  139.    
  140.    \subsubsection{Оптимизации и клиент-серверная архитектура}
  141.    \label{section:optimize}
  142.    
  143.    \paragraph*{Деление мира на чанки}
  144.     В идее моей игры нет необходимости хранить бесконечный мир - по задумке, мир должен быть ограниченным, но большим. Хранить в памяти придется весь мир, но передавать и выводить на экран стоит только то, что игрок может видеть. Для этого, весь мир делится на чанки: области $8*8$ блоков. Таким образом, на клиентской стороне выводится на экран только то, что должно выводится, а так же сервер может отправить каждому игроку информацию только о той части карты, в которой он может находится.
  145.    
  146.    \paragraph*{Защита от читеров}
  147.    Т.к. игра по сети Интернет, то нужно было реализовать защиту от читеров. Единственный способ: обсчитывать всю информацию на сервере, а клиент только отправляет ввод и получает информацию, необходимую для вывода.
  148.    
  149.    \paragraph*{Переподключение}
  150.    Во время игры могут быть проблемы с соединением, а так же перезагрузка страницы браузера. Нужно сохранять информацию об игроке, чтобы можно было сразу вернуть его в игру. Для этого, каждого игрока нужно идентифицировать: проблема заключается в том, что о клиенте мы знаем только его ip-адрес, никнейм и номер подключения socket.io.
  151.    
  152.    Для решения данной проблемы используется библиотека JSON Web Token. Создается зашифрованный токен с информацией об игроке - никнейм и номер подключения. После этого, этот токен отсылается клиенту, который должен сохранить его (в cookie). Далее, любое действие игрока требует от него токен, по которому он и идентифицируется. Так как стандартный браузер должен сохранять cookie между сессиями, то после потери соединения клиент снова отсылает токен, и сервер понимает, что этот игрок уже зарегистрирован и подключает его обратно.
  153.    
  154.    Раз в 30 минут сервер удаляет всех игроков, которые покинули сервер более 30 минут назад.
  155.    
  156.    \subsection{Планы на будущее}
  157.    
  158.    Доделать геймплей:
  159.    
  160.    \begin{itemize}
  161.    \item Возможность строить здания
  162.    \item Здания генераторов ресурсов
  163.    \item Здания поля, наносящего урон
  164.     \item Здания, создающие юнитов
  165.    \end{itemize}
  166.    
  167.    \section{Руководство программиста}
  168.    \subsection{Сервер}
  169.    \subsubsection{Сетевая часть}
  170.    \paragraph*{Подключение}
  171.    Основные функции, связанные с сетевой частью описаны в событии \textbf{connection}.
  172.    
  173.    \paragraph*{Запрос токена}
  174.    При подключении нового клиента с серверу, сервер просит клиента отослать ему токен. Если клиент отсылает его, то сервер проверяет актуальность и корректность токена, и если все хорошо, возвращает в сессию.
  175.    
  176.    \paragraph*{Регистрация}
  177.    Данное событие отвечает на запрос регистрирования нового клиента. Если данное подключение уже зарегистрировано, игнорирует запрос, иначе создает новую запись и отсылает клиенту его токен.
  178.    
  179.   \paragraph*{Операции с комнатами}
  180.   События \textbf{joinRoom}, \textbf{leaveRoom}, \textbf{joinSide}, \textbf{ready} отвечают за перемещение по комнатам, смену сторон и кнопки "готов". Клиент должен отсылать его токен вместе с остальной информацией, иначе запрос будет проигнорирован.
  181.  
  182.   \paragraph*{Запрос комнат}
  183.   Событие \textbf{requestRooms} отсылает клиенту, который отправил данный запрос, список комнат на сервере.
  184.  
  185.   \paragraph*{Отключение}
  186.   При отключении клиента сервер помечает игрока как покинувшего и записывает время. Так же, если игра не началась, а клиент в комнате, удаляет его из комнаты.
  187.  
  188.   \subsubsection{Сетевая часть игры}
  189.   \paragraph*{Ввод}
  190.   События \textbf{keyboard} и \textbf{mouse} обрабатывают ввод игрока.
  191.  
  192.   \paragraph*{Запрос карты}
  193.   Событие \textbf{requestChunk} отсылает клиенту, который отправил данный запрос, чанк, только если клиент может этот чанк увидеть.
  194.  
  195.   \paragraph*{Передача игровых данных клиенту}
  196.   Передача данных клиенту происходит в функции \textbf{updateGame} класса \textbf{Room}
  197.  
  198.   \subsubsection{Игровая часть}
  199.   Описание основных классов игры
  200.  
  201.   \paragraph*{Vector2}
  202.   Класс физического двумерного вектора
  203.  
  204.   \paragraph*{NetworkPrimitive}
  205.   Класс, ответственный за все сетевое взаимодействие. Хранит всю необходимую о клиенте информацию.
  206.  
  207.   \paragraph*{PhysicsPrimitive}
  208.   Физический объект, заданный прямоугольником со скоростью. Имеет функции коллизии.
  209.  
  210.   \paragraph*{Player}
  211.   Класс, ответственный за сторону людей. Основная функция \textbf{tickUpdate} обновляет все внутренние состояния игрока.
  212.  
  213.   \paragraph*{Virus}
  214.   Класс, ответственный за сторону вируса. Основная функция \textbf{tickUpdate} обновляет все внутренние состояния вируса.
  215.  
  216.   \paragraph*{Block}
  217.   Класс, ответственный за хранение информации о игровом блоке. Функция \textbf{update} обновляет блок (разрушение блока). Функция \textbf{updateMultiTexture} обновляет информацию о том, какую текстуру следует поставить для блоков, у которых есть несколько спрайтов.
  218.  
  219.   \paragraph*{Chunk}
  220.   Класс-контейнер, в основном служит как промежуточный слой между картой и блоком.
  221.  
  222.   \paragraph*{GameMap}
  223.   Класс, хранящий игровую карту, а так же интерфейс, через который следует работать с игровым миром.
  224.  
  225.   \paragraph*{Room}
  226.   Класс, ответственный за отдельную сессию. Каждая комната должна иметь свой объект данного класса. Имеет две стадии: \textbf{preGame} и \textbf{game} - лобби и игровая стадия соответственно. Функции \textbf{update} нужной стадии обновляют состояние комнаты. \newline
  227.  
  228.   Описание основных функций
  229.  
  230.   \paragraph*{loadFiles}
  231.   Перед стартом основных функций, сервер должен загрузить некоторые файлы: информацию о блоках.
  232.  
  233.   \paragraph*{setup}
  234.   Запускает сервер: создает комнаты, запускает главный цикл.
  235.  
  236.   \paragraph*{tick}
  237.   Вызывает функцию обновления для каждой комнаты.
  238.  
  239.   \subsection{Клиент}
  240.   \subsubsection{Серверная часть}
  241.  
  242.   Описана в файле client.js
  243.  
  244.   \paragraph*{Ошибка регистрации}
  245.   Событие происходит, когда веденный никнейм или отправленный токен некорректные.
  246.  
  247.   \paragraph*{Удачная регистрация}
  248.   Если клиент успешно зарегистрировался, то нужно поменять формы GUI.
  249.  
  250.   \paragraph*{Комнаты}
  251.   Сервер прислал список комнат, следует обновить их.
  252.  
  253.   \paragraph*{Обновление}
  254.   Информация о игровом состоянии
  255.  
  256.   \paragraph*{Обновление вируса}
  257.   Информация о игровом состоянии для стороны вируса
  258.  
  259.   \paragraph*{Карта}
  260.   Сервер прислал чанк карты
  261.  
  262.   \paragraph*{Обновление лобби}
  263.   Находясь в лобби, произошло обновление
  264.  
  265.   \paragraph*{Начало игры}
  266.   Началась игра, следует убрать все GUI, связанные с лобби, включить рендер игры и показать инвентарь
  267.  
  268.   \paragraph*{Конец игры}
  269.   Игра закончилась, следует показать все GUI и убрать рендер игры.
  270.  
  271.   \subsubsection{Игровая часть}
  272.  
  273.   Основные классы остаются те же, но некоторые функции убраны, т.к. не используются.
  274.   Появился один новый класс
  275.  
  276.   \paragraph*{GraphicsPrimitive}
  277.   Класс, упрощающий работу с графическим движком. \newline
  278.  
  279.   Описание основных функций
  280.  
  281.   \paragraph*{setup}
  282.   Функция, инициализирующая графический движок
  283.  
  284.   \paragraph*{frame}
  285.   Функция, отвечающая за показ одного игрового кадра. Так же обновляет игровые данные.
  286.  
  287.   \paragraph*{keyboard}
  288.   Функция, отвечающая за обработку нажатия одной клавиши.
  289.  
  290.   \section{Руководство пользователя}
  291.   \subsection{Возможности}
  292.   При заходе на сайт игроку следует зарегистрироваться, выбрать комнату, нажать кнопку "Готов". Далее, игрок может перемещаться по миру с помощью кнопок WASD, ломать блоки в пределах своей досягаемости на ЛКМ и ставить на ПКМ. При уничтожении блоков игроку даются ресурсы, при установке - тратятся.
  293.  
  294.   \subsection{Описание ресурсов}
  295.   \paragraph*{Рабочие}
  296.   Для любого действия у игрока должен быть свободен хотя бы 1 рабочий, который ставится на данную задачу.
  297.  
  298.   \paragraph*{Энергия}
  299.   Аналогично, для любого действия у игрока должна быть энергия, чтобы его совершить.
  300.  
  301.   \paragraph*{Камень}
  302.   Основной ресурс для построек. Является дешевым, нужен для низкоуровневых построек.
  303.  
  304.   \paragraph*{Железо}
  305.   Дополнительный ресурс для построек. Является дорогим, нужен для высокоуровневых построек и улучшений.
  306.  
  307.   \subsection{Сторона людей}
  308.   Весь геймплей строится вокруг персонажа игрока. С помощью клавиш, игрок может перемещать своего персонажа по миру. Область досягаемости определяется как небольшая зона вокруг персонажа.
  309.  
  310.   \subsection{Сторона вируса}
  311.   Весь геймплей строится вокруг ядра и организма под землей. У каждого игрока есть свой центральный блок - блок ядра. При его уничтожении, игрок проигрывает. Камера может перемещаться в такой области, что недалеко от центра экрана был бы хотя бы один блок вируса. Область досягаемости определяется как все блоки вируса и соседние с ними.
  312.    
  313.    \begin{thebibliography}{9}
  314.  
  315.     \bibitem{1}
  316.   Англоязычная документация PIXI.js
  317. \\\texttt{http://pixijs.download/dev/docs/PIXI.html}
  318.  
  319.     \bibitem{2}
  320.    Learning PIXI: A step-by-step introduction to making games and interactive
  321. media with the Pixi rendering engine.
  322. \\\texttt{https://github.com/kittykatattack/learningPixi}
  323.    
  324.    \bibitem{3}
  325.    Англоязычная документация по socket.io
  326.    \\\texttt{https://socket.io/docs/}
  327.  
  328.     \bibitem{4}
  329.    Шум Перлина / Хабрахабр
  330.    \\\texttt{https://habrahabr.ru/post/342906/}
  331.    
  332.    \bibitem{5}
  333.    Perlin Noise / Scratchapixel
  334.    \\\texttt{https://www.gamedev.net/articles/programming/general-and-gameplay-programming/swept-aabb-collision-detection-and-response-r3084/}
  335.    
  336.    \bibitem{6}
  337.    Swept AABB Collision Detection and Response
  338.    \\\texttt{https://www.gamedev.net/articles/programming/general-and-gameplay-programming/swept-aabb-collision-detection-and-response-r3084/}
  339.  
  340.   \end{thebibliography}
  341.  
  342. \end{document}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement