Современные веб-фреймворки в основном используются для написания корпоративных приложений, но они давно уже достаточно гибки и функциональны и для других областей. Попробуем написать логическую казуальную игру на ASP.NET Core 2. Рассмотрим процесс создания игрового проекта, а так же новшества фреймворка и сопутствующих инструментов.

ДЕМО
Исходники

Идея


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

Реализация


Создаем проект


Последняя версия ASP.NET Core вышла совсем недавно (в середине августа), поэтому если вам интересно пройти путь создания проекта с нуля, не забудьте обновить как минимум .NET Core (http://dot.net).
Для создания проект можно использовать как IDE (Visual Studio, Visual Studio for Mac, Rider). В последнем обновлении Visual Studio диалог создания нового веб приложения был немного переделан:



Или же создать проект можно в обычной консоли:



и открыть его в том редакторе что вам нравится.

Из консоли теперь можно также выбрать шаблон проекта и, например, добавить авторизацию:



Создадим обычное пустое приложение без авторизации.

dotnet new web
И откроем его в Visual Studio (или вашем любимом редакторе). Если посмотреть на структуру проекта



то можно заметить, что вместо кучи стандартных nuget пакетов, сейчас добавлен лишь один — Microsoft.AspNetCore.All, который является неким контейнером всего. Это очень удобно во время разработки, так как нет надобности постоянно проверять, подключен ли пакет что нам нужен, искать как он сегодня называется и т.д.

Если запустить проект, результатом будет фраза «Hello World!» в браузере.
GitHub Чек-поинт 0

Настраиваем MVC


Большинство ASP.NET проектов пишутся с помощью набора библиотек ASP.NET MVC, так как они добавляют значительную гибкость при разработке. Начнем с того, что добавим папку Controllers в проект и попробуем добавить новый контроллер из контекстного меню.



В отличии от предыдущих версий, теперь при первом добавлении контроллера появляется диалог:



Поскольку мы впервые попробовали добавить элемент по шаблону(scaffold) Visual Studio предлагает нам настроить шаблонизацию. Выберем минимальные зависимости для того, чтобы лучше контролировать что происходит с кодом. После автоматической настройки шаблонизатора нам подсказывают как добавить MVC в проект. Воспользуемся подсказкой и модифицируем класс Startup.cs
GitHub Чек-поинт 1

При запуске проекта, получаем в ответ 404, потому что маршрут по умолчанию ищет контроллер Home и метод Index внутри него. Добавим стартовую страницу, создав контроллер Home и представление в новой папке Views.
GitHub Чек-поинт 2

Добавляем базу данных


Как было сказано выше: структурные единицы нашей игры — это комнаты. Добавим класс, что будет отвечать за предоставление данных о комнате. Для начала ограничимся следующими данными:

namespace TheRooms.Models
{
    public class Room
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Task { get; set; }
        public string Answer { get; set; }

        [InverseProperty("RoomTo")]
        public ICollection<Door> DoorsIn { get; set; }

        [InverseProperty("RoomFrom")]
        public ICollection<Door> DoorsOut { get; set; }
    }
}


Для перехода между комнатами опишем класс дверь:
namespace TheRooms.Models
{
    public class Door
    {
        public int Id { get; set; }

        [ForeignKey("RoomFrom")]
        public int RoomFromId { get; set; }
        public Room RoomFrom { get; set; }

        [ForeignKey("RoomTo")]
        public int RoomToId { get; set; }
        public Room RoomTo { get; set; }
    }
}

В классе Door мы используем атрибуты ForeignKey, которые подсказывают зависимости между дверьми и комнатами. В классе Room добавлены атрибуты InverseProperty, которые так же показывают к какому свойству другой сущности относится эта коллекция. InverseProperty нужен только в случае нескольких «похожих» отношений (в данном случае у комнаты есть два типа дверей — для входа в комнату и для выхода).
GitHub Чек-поинт 3

Для работы с базой данных попробуем Entity Framework Core. Для этого нам надо добавить контекст данных (связь между кодом и базой).

using Microsoft.EntityFrameworkCore;
using TheRooms.Models;

namespace TheRooms.Data
{
    public class DataContext : DbContext
    {
        public DbSet<Room> Rooms { get; set; }
        public DbSet<Door> Doors { get; set; }

        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            // disable cascade delete
            foreach (var relationship in builder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
            {
                relationship.DeleteBehavior = DeleteBehavior.Restrict;
            }
        }
    }
}

А так же добавим файл настр��ек со строкой подключения к базе данных и свяжем его с контекстом в методе ConfigureServices.

services.AddDbContext<DataContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

GitHub Чек-поинт 4

Следующим шагом создадим саму базу из классов, что мы описали выше. Для этого нужно выполнить 2 команды в Package Manger Console:

Add-Migration Initial
Update-Database


Первая команда добавит код для миграции базы данных из предыдущего состояния (когда её не существовало) до последнего состояния (где у нас есть 2 таблицы — Rooms и Doors). Соответственно, вторая команда обновит базу данных с помощью скриптов миграций.
GitHub Чек-поинт 5
База данных готова.

Показываем комнату


Для того, чтобы показать комнату, надо, чтобы контроллер вытянул детали комнаты из базы и передал их на представление. Начнем с контроллера. Cоздадим новый класс — RoomsController с методом, который будет доставать нужные данные и отправлять в браузер.

public async Task<IActionResult> Show(int id)
        {
            var room = await _context.Rooms.SingleAsync(r => r.Id == id);
            return View(room);
        }

GitHub Чек-поинт 6

Но для того, чтобы зайти в комнату, нужно сначала её создать. Добавим контроллеры для управления комнатами и дверьми с помощью шаблонизатора (scaffolding):





GitHub Чек-поинт 7

Добавляем стили, кнопочки, надписи.
GitHub Чек-поинт 8

У нас готов некий прототип, теперь надо наполнить его жизнью.

Описываем игроков


Каждый пользователь нашего сайта должен быть уникальным образом идентифицирован. Например, для того, чтобы знать кто какие комнаты уже прошел и может двигаться дальше. Для того, чтобы запоминать игроков, создадим класс Player и будем добавлять уникальный идентификатор в Cookies для каждого игрока. К нему в придачу, добавим сущность CompletedRooms, где будем хранить информацию об уже пройденных комнатах.

Для того, чтобы эти классы можно было использовать, их также надо добавить в контекст данных и добавить миграцию для нашей базы.
GitHub Чек-поинт 8

Добавим логику для проверки пользователя.
GitHub Чек-поинт 9

Ну и несколько мелочей для того, чтобы все это выглядело не совсем страшно. Первая версия игры готова. К сожалению корованы грабить пока нельзя.

Выводы


Как и ожидалось ASP.NET Core 2 отлично справился с заданием. Конечно полноценной игрой это назвать сложно и хочется еще проверить возможности интерактивности и взаимодействий в реальном времени. Но, похоже что современные веб фреймворки уже очень близко к тому рубежу где не так уж важно какая тематика проекта. Можно заметить что с новыми версиями Visual Studio во время разработки появляется все больше диалогов, которые а��томатически генерируют код. Уже сейчас это используется для создания проекта, настройки шаблонизатора, автоматического создания «админки». Этот подход может со временем эволюционировать все больше и можно будет кодить мышкой.

ДЕМО
Исходники