В предыдущей статье получилось сделать прототип игры с вопросами. Но не получилось сделать это полноценно на WebAssembly. В этот раз предлагаю конвертировать этот прототип, сделав с него полноценное WebAssembly приложение, а так же добавить к нему атрибуты большинства игр — опыт и уровни игрока. А так же простую логику — при переходе на некоторые уровни будем открывать новые навыки.
Demo
Создадим новый проект, который будет хостить WebAssembly приложение, внутри Web приложения (ASP.NET Core ). Если быть точнее, то Web приложение возвращает WebAssembly приложение, которое остается в браузере у пользователя и взаимодействует с сервером посредством http (или веб сокетов). Для этого, нам нужно создать BlazorApp проект и выбрать пункт ASP.NET Core hosted
Или же, из консоли:
Поcкольку у нас уже есть Blazor проект с предыдущей статьи, а синтаксис одинаковый у проектов с серверным хостингом и WebAssembly, то все страницы можно просто скопировать. Но с классами для логики так не получится — нужно создать новый контроллер, как часть backend-а. Это обычный asp.net контроллер, с которым наш клиент будет коммуницировать.
Логику общения с сервером хотелось бы переделать. Например, чтобы не было надобности на каждое действие пользователя слать запрос на сервер. Было бы интересно, как вариант, скачать несколько вопросов с запасом, чтобы при правильном ответе показывать следующий вопрос без запроса на сервер. Но с этим поиграемся в следующий раз.
Срочной необходимости это делать нет. Но, поскольку предварительная версия .NET 5 уже доросла до шестой, поддерживает Blazor и в ней есть некоторые новые фичи (например, extension методы для запросов на сервер), то почему бы и не перейти на неё сейчас. Мигрировать довольно просто, если знать что делать. В нашем случае — это замена версий target framework в серверном проекте и переход на новую версию всех nuget пакетов.
Github commit: Update to .NET 5 preview 6
Для связи между уровнем и опытом будем использовать простую формулу: минимум опыта для уровня = 2^(уровень). Для того, чтобы игрок мог понимать сколько опыта он накопил и какого он уровня, добавим в его модель эти два параметра. А так же, добавим поле в таблицу с вопросами для индикации сколько очков опыта даст один вопрос. Пока все вопросы будут добавлять 1 очко опыта.
Для отображения на пользовательском интерфейсе, используем простые компоненты Bootstrap.
Результат:
Github commit: Add level and experience to UI
Пока в нашей игре не так уж много функционала, но тем не менее некоторый из них мы можем показывать постепенно. Добавим приветствие при достижении первого уровня и нотификацию о новых фичах для второго и четвертого уровней.
Github commit: Show features depending on level
Так же добавим рейтинг игроков с наивысшим уровнем. Это послужит примером того, как выглядит комит с отдельной компонентой, которая касается разных существующих частей. На мой взгляд, выглядит очень даже понятно.
Github commit: Add leader board
В этом же изменении, при редактировании фидбека на неправильный ответ, наткнулся на интересный момент. Попробовал одновременно подписаться на события нажатия клавиши (если это Enter — отправить на сервер) и изменения значения контрола (если оно пустое — убрать сообщение о не верном ответе). Оказалось, что такой вариант не поддерживается. Что, в свою очередь спровоцировало как-то это обойти. В описном тикете есть варианты, но я попробовал еще один:
Работает именно так, как мне нужно — при изменении ответа, сообщение пропадает. Хотя и таймер на 1 миллисекунду. Вообще, использования .NET таймера внутри браузера звучит странно. Но, похоже, что если пользователь не активен, то таймер автоматически замораживается.
Приятным бонусом использования Blazor внутри ASP.NET Core приложения является возможность использования инструментов зрелого фреймворка. В данном случае — мы можем сделать простую админку для просмотра\редактирования вопросов с помощью кодо-генерации.
Добавим несколько типичных строк в логику конфигурации авторизации (Startup.cs) и сгенерируем пару стандартных частей — Identity страницы для авторизации и контроллер с представлениями для просмотра вопросов. Получилась простыня кода, из которой руками писалось всего несколько строк.
Github commit: add admin part
Так как большая часть логики находится на клиенте, например проверка правильности ответа, который отправил пользователь, то это и большая проблема с точки зрения безопасности. На данный момент продвинутый пользователь (который знает как посмотреть детали трафика в браузере) без проблем может проверить ответ во время загрузки вопроса с сервера. Наверное, оптимальным решением этой проблемы будет использование одностороннего шифрования для проверки правильности ответа. Но оставим это на позже, дав возможность юным хакерам накрутить себе игрового опыта.
На сегодняшний день есть как позитивные так и негативные моменты разработки на Blazor. Самое приятное — это видеть как интерактивно меняется пользовательский интерфейс, при этом использовать только C# как язык программирования. Как ни странно, не смотря на простоту фреймворка, требуемый пользовательский интерфейс получается сделать довольно просто и при этом всё работает. Из негативных моментов — главная проблема, это ощущение сыроватости. Возможно при использовании стабильных версий всё выглядит лучше.
В общем, ощущения очень интересные. Приходится использовать старые добрые инструменты для написания веб приложения совсем по-новому. Мне кажется, что пока развитие Blazor идет в очень правильном русле — просто компилируемый код, который работает в браузере без всяких странных прибамбасов.
Результат:
Github
Demo
Demo
Создаем WebAssembly проект
Создадим новый проект, который будет хостить WebAssembly приложение, внутри Web приложения (ASP.NET Core ). Если быть точнее, то Web приложение возвращает WebAssembly приложение, которое остается в браузере у пользователя и взаимодействует с сервером посредством http (или веб сокетов). Для этого, нам нужно создать BlazorApp проект и выбрать пункт ASP.NET Core hosted
Или же, из консоли:
dotnet new blazorwasm --hosted
Поcкольку у нас уже есть Blazor проект с предыдущей статьи, а синтаксис одинаковый у проектов с серверным хостингом и WebAssembly, то все страницы можно просто скопировать. Но с классами для логики так не получится — нужно создать новый контроллер, как часть backend-а. Это обычный asp.net контроллер, с которым наш клиент будет коммуницировать.
Логику общения с сервером хотелось бы переделать. Например, чтобы не было надобности на каждое действие пользователя слать запрос на сервер. Было бы интересно, как вариант, скачать несколько вопросов с запасом, чтобы при правильном ответе показывать следующий вопрос без запроса на сервер. Но с этим поиграемся в следующий раз.
Меняем версию на .NET 5 preview 6
Срочной необходимости это делать нет. Но, поскольку предварительная версия .NET 5 уже доросла до шестой, поддерживает Blazor и в ней есть некоторые новые фичи (например, extension методы для запросов на сервер), то почему бы и не перейти на неё сейчас. Мигрировать довольно просто, если знать что делать. В нашем случае — это замена версий target framework в серверном проекте и переход на новую версию всех nuget пакетов.
Github commit: Update to .NET 5 preview 6
Добавляем опыт и уровень
Для связи между уровнем и опытом будем использовать простую формулу: минимум опыта для уровня = 2^(уровень). Для того, чтобы игрок мог понимать сколько опыта он накопил и какого он уровня, добавим в его модель эти два параметра. А так же, добавим поле в таблицу с вопросами для индикации сколько очков опыта даст один вопрос. Пока все вопросы будут добавлять 1 очко опыта.
Для отображения на пользовательском интерфейсе, используем простые компоненты Bootstrap.
<div class="row">
<div class="col-md-auto">
<span class="badge badge-warning">
Уровень <span class="badge badge-light">@state.Level</span>
</span>
</div>
<div class="col">
<div class="progress mt-1">
<div class="progress-bar" role="progressbar" style="width: @GetExperienceWidgetWidth()" aria-valuenow="@state.Experience" aria-valuemin="0" aria-valuemax="@experienceDiff">
XP: @state.Experience
</div>
</div>
</div>
</div>
Результат:
Github commit: Add level and experience to UI
Навыки уровней
Пока в нашей игре не так уж много функционала, но тем не менее некоторый из них мы можем показывать постепенно. Добавим приветствие при достижении первого уровня и нотификацию о новых фичах для второго и четвертого уровней.
Github commit: Show features depending on level
Так же добавим рейтинг игроков с наивысшим уровнем. Это послужит примером того, как выглядит комит с отдельной компонентой, которая касается разных существующих частей. На мой взгляд, выглядит очень даже понятно.
Github commit: Add leader board
В этом же изменении, при редактировании фидбека на неправильный ответ, наткнулся на интересный момент. Попробовал одновременно подписаться на события нажатия клавиши (если это Enter — отправить на сервер) и изменения значения контрола (если оно пустое — убрать сообщение о не верном ответе). Оказалось, что такой вариант не поддерживается. Что, в свою очередь спровоцировало как-то это обойти. В описном тикете есть варианты, но я попробовал еще один:
var timer = new Timer(1);
timer.Elapsed += (object sender, ElapsedEventArgs e) => { wrongStyle = "visibility:hidden"; };
timer.Start();
Работает именно так, как мне нужно — при изменении ответа, сообщение пропадает. Хотя и таймер на 1 миллисекунду. Вообще, использования .NET таймера внутри браузера звучит странно. Но, похоже, что если пользователь не активен, то таймер автоматически замораживается.
Админка
Приятным бонусом использования Blazor внутри ASP.NET Core приложения является возможность использования инструментов зрелого фреймворка. В данном случае — мы можем сделать простую админку для просмотра\редактирования вопросов с помощью кодо-генерации.
Добавим несколько типичных строк в логику конфигурации авторизации (Startup.cs) и сгенерируем пару стандартных частей — Identity страницы для авторизации и контроллер с представлениями для просмотра вопросов. Получилась простыня кода, из которой руками писалось всего несколько строк.
Github commit: add admin part
Проверка ответов и безопасность
Так как большая часть логики находится на клиенте, например проверка правильности ответа, который отправил пользователь, то это и большая проблема с точки зрения безопасности. На данный момент продвинутый пользователь (который знает как посмотреть детали трафика в браузере) без проблем может проверить ответ во время загрузки вопроса с сервера. Наверное, оптимальным решением этой проблемы будет использование одностороннего шифрования для проверки правильности ответа. Но оставим это на позже, дав возможность юным хакерам накрутить себе игрового опыта.
Выводы
На сегодняшний день есть как позитивные так и негативные моменты разработки на Blazor. Самое приятное — это видеть как интерактивно меняется пользовательский интерфейс, при этом использовать только C# как язык программирования. Как ни странно, не смотря на простоту фреймворка, требуемый пользовательский интерфейс получается сделать довольно просто и при этом всё работает. Из негативных моментов — главная проблема, это ощущение сыроватости. Возможно при использовании стабильных версий всё выглядит лучше.
В общем, ощущения очень интересные. Приходится использовать старые добрые инструменты для написания веб приложения совсем по-новому. Мне кажется, что пока развитие Blazor идет в очень правильном русле — просто компилируемый код, который работает в браузере без всяких странных прибамбасов.
Результат:
Github
Demo
windrider
Спасибо за пример, было интересно увидеть как это работает вживую. ЕМНИП, в какой-то ранней бете Blazor app (hello world) грузился с пол минуты. Представленная в посте демка — где-то 5 секунд.
Вопрос:
Здесь Blazor обновит лиш значения на уже существующих элементах (по биндингах)? То-есть компоненты QuestionViewer полностью перегенериваться не будут?
В связи с этим (?), значения, котрые не биндятся, надо обновлять/обнулять вручную? Например, если в представленной демке ввести «whatever» в инпут #2, затем дать правильный ответ на вопрос #1 — QuizViewer обновится, но значение «whatever» так и останется в инпуте #2 (хотя вопрос там уже будет другой).
Тут и 42-ой level не поможет :) На самом деле, это ж ответ на главный вопрос. А вот собственно вопрос нам ещё предстоит узнать (если повезёт).
Gbdrm Автор
да, интересный момент с whatever, надо будет подумать как это красиво обработать. Ну и судя по тому, что он присутствует, то обновляется только часть из QuestionViewer.
вопрос придумал не я, но исправил :)