Перейти к содержимому

Как создать виджет для стримов с удалённым управлением

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

Подготовка

Все виджеты являются обычными веб-сайтами, которые работают во встроенном браузере OBS Studio. Поэтому для создания виджета вам понадобится стандартный набор инструментов для веб-разработки.

Для упрощения статьи мы не будет использовать никакие веб-фреймворки и реализуем всё на чистом HTML, CSS и JavaScript. Однако, всё ещё будет использовать сборку с помощью Vite (это такая программа, которая упрощает разработку и сборку проектов для последующей публикации).

BunJS

Установите BunJS - современный менеджер пакетов и среду выполнения JavaScript. Инструкции по установке можно найти на официальном сайте.

powershell
powershell -c "irm bun.sh/install.ps1 | iex"
bash
curl -fsSL https://bun.sh/install | bash

Visual Studio Code

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

Создание проекта

Создайте новую папку для вашего проекта и откройте её в VSCode. Затем откройте терминал в VSCode (меню TerminalNew Terminal) и выполните команду:

bash
bun create vite . --template vanilla

После чего несколько раз нажмите Enter, чтобы принять значения по умолчанию.

Вывод терминала после выполнения команды

vite-create-terminal-output

У вас будет настроен проект, установленные зависимости и запущен локальный сервер для разработки (обычно по адресу http://localhost:5174/).

Откройте этот адрес в браузере, вы должны увидеть стартовую страницу Hello Vite!.

Очистить стартовую страницу, для этого, в VSCode удалим папку public со всем её содержимым, а так же файлы src/counter.js и src/javascript.svg.

Отредактируем файл src/main.js, удалив из него всё содержимое кроме подключения стилей:

src/main.js
javascript
import './style.css'

А так же, полностью очистим файл src/style.css, оставив его пустым.

Готово! Каждое изменение в файлах будет автоматически отображаться в браузере благодаря Vite. Теперь мы видим пустую белую страницу.

Создание виджета

Разработка виджета будет состоять из двух этапов:

  • Создадим визуальное оформление виджета
  • Добавим функционал удалённого управления

Визуальное оформление виджета

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

Единственное отличие от обычного веб-сайта - это размеры виджета. Мы будем делать автоматическую подгонку под ширину экрана, это позволит стримеру использовать виджет на на любых разрешениях. Для этого мы установим размер шрифта равным 1% от ширины экрана, а все стили будем указывать в em.

src/style.css
css
:root {
  /* Прозрачный фон */
  color-scheme: dark;
  background: transparent;
}

body {
  /* Устанавливаем размер шрифта в зависимости от ширины окна */
  font-size: 1vw;

  /* Убираем отступ и скрываем скролл */
  margin: 0;
  overflow: hidden;
}

Теперь создадим общую структуру виджета в файле index.html:

index.html
html
...
<div id="app">
  <div class="item item-1">
    <header>
      <h2>VI - VII уровень</h2>
      <p><span class="current">18</span> / 25</p>
      <img src="./src/assets/complete.png">
    </header>
    <div class="progress-bar">
      <div class="progress-bar-background"></div>
      <div class="progress-bar-pattern"></div>
      <div class="progress-bar-blink"></div>
    </div>
  </div>
</div>
...

И дважды продублируем блок div.item, изменив текст заголовка h2, на последний div.item добавим класс completed.

Для оформления нам понадобятся некоторые аасеты из игры, найти их можно в пакете gui.pkg или в репозитории Kurzdor/wot.assets

Скопируйте нужные ассеты в папку src/assets вашего проекта.

Настройте стили в файле style.css, чтобы получить следующий результат:

Полный код файла style.css
css
:root {
  /* Прозрачный фон */
  color-scheme: dark;
  background: transparent;
}

@font-face {
  font-family: Warhelios;
  src: url(./assets/Warhelios-Regular.ttf) format("truetype");
}

body {
  /* Устанавливаем размер шрифта в зависимости от ширины окна */
  font-size: 1vw;

  /* Убираем отступ и скрываем скролл */
  margin: 0;
  overflow: hidden;

  font-family: Warhelios, sans-serif;
}

#app {
  display: flex;
  gap: 1em;
  padding: 1em;
}

.item {
  border-radius: 1.3em;
  padding: 1.2em;
  border: 0.25em solid rgba(255, 173, 65, 0.4);
  background:
    linear-gradient(0deg, rgba(195, 90, 4, 0.1), rgba(255, 255, 255, 0) 50%),
    radial-gradient(farthest-corner at 50% 200%, rgba(251, 166, 20, 0.4), rgba(218, 139, 1, 0.3) 90%);

  display: flex;
  flex-direction: column;
  flex: 1;

  max-width: 30%;
  box-shadow: 0 0 1em rgba(0, 0, 0, 0.2);
}

.completed {
  border: 0.25em solid rgba(106, 255, 65, 0.3);
  background:
    linear-gradient(0deg, rgba(80, 255, 27, 0.3), rgba(26, 255, 0, 0) 50%),
    radial-gradient(farthest-corner at 50% 200%, rgba(4, 102, 5, 0.4), rgba(13, 114, 0, 0.3) 90%);
}

header {
  font-size: 1em;
  display: flex;
}

header h2 {
  flex: 1;
}

header h2,
header p {
  font-size: 2.7em;
  margin: 0;
  font-weight: normal;
}

.current {
  color: #ffd28f;
}

img {
  width: 9em;
  height: 9em;
  margin: -3em;
  display: none;
}

/* Прогресс бар */
.progress-bar {
  height: 0.3em;
  width: 100%;
  margin-top: 1em;
  position: relative;
}

/* Фоновая часть прогресс бара (серые прямоугольники) */
.progress-bar-background {
  background-image: url(./assets/pattern_grey.png);
  background-repeat: repeat;
  background-position: 0 50%;
  background-size: 0.4em 1em;

  position: absolute;
  width: 100%;
  height: 100%;
  box-shadow: 0 0 2em rgba(0, 0, 0, 0.5);
}

/* Текущее значение прогресса (блик) */
.progress-bar-blink {
  height: 5em;
  width: 5em;

  background-image: url(./assets//glow_small.png);
  background-size: contain;
  mix-blend-mode: lighten;
  position: absolute;
  transform: translate(-49%, -50%);
  top: 50%;
  left: var(--progress, 60%);
  transition: left 0.2s ease;
}

/* Заполненная часть прогресс бара */
.progress-bar-pattern {
  background-image: url(./assets/pattern_orange.png);
  background-repeat: repeat;
  background-position: 0 50%;
  background-size: 0.4em 1em;

  position: absolute;
  height: 100%;
  width: var(--progress, 60%);
  transition: width 0.2s ease;
}

/* Градиентная подсветка прогресс бара */
.progress-bar-pattern::after {
  content: "";
  position: absolute;
  right: 0;
  top: 0;
  width: 100%;
  height: 100%;

  background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.6), rgba(255, 206, 122, 0.5));
  mix-blend-mode: overlay;
}

/* Переопределяет для завершенных этапов */
.completed .progress-bar-pattern {
  background-image: url(./assets/pattern_green.png);
  width: 100%;
}

.completed .progress-bar-pattern::after {
  background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.6), rgba(187, 255, 78, 0.519), rgba(0, 0, 0, 0.6));
}

.completed .progress-bar-blink,
.completed header p {
  display: none;
}

.completed img {
  display: block;
}
Полный код файла index.html
html
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>article-pm-widget</title>
</head>

<body>
  <div id="app">
    <div class="item item-1">
      <header>
        <h2>VI - VII уровень</h2>
        <p><span class="current">18</span> / 25</p>
        <img src="./src/assets/complete.png">
      </header>
      <div class="progress-bar">
        <div class="progress-bar-background"></div>
        <div class="progress-bar-pattern"></div>
        <div class="progress-bar-blink"></div>
      </div>
    </div>

    <div class="item item-2">
      <header>
        <h2>VII - IX уровень</h2>
        <p><span class="current">18</span> / 25</p>
        <img src="./src/assets/complete.png">
      </header>
      <div class="progress-bar">
        <div class="progress-bar-background"></div>
        <div class="progress-bar-pattern"></div>
        <div class="progress-bar-blink"></div>
      </div>
    </div>

    <div class="item item-3 completed">
      <header>
        <h2>X - XI уровень</h2>
        <p><span class="current">18</span> / 25</p>
        <img src="./src/assets/complete.png">
      </header>
      <div class="progress-bar">
        <div class="progress-bar-background"></div>
        <div class="progress-bar-pattern"></div>
        <div class="progress-bar-blink"></div>
      </div>
    </div>

    <script type="module" src="/src/main.js"></script>
</body>

</html>

В результате получаем следующий виджет: widget-prepare-result.png

Функционал удалённого управления

Теперь добавим функционал удалённого управления. Для этого установим библиотеку wotstat-widgets-sdk, и объявим в ней удалённые параметры нашего виджета.

В терминале выполните команду:

powershell
bun add wotstat-widgets-sdk
bash
bun add wotstat-widgets-sdk

В файле src/main.js подключим библиотеку и объявим параметры виджета:

src/main.js
javascript
import './style.css'
import { WidgetsRemote } from 'wotstat-widgets-sdk';

const remote = new WidgetsRemote();

remote.defineState('VI - VII уровень', 0, { elementHelper: '.item-1' });
remote.defineState('VII - IX уровень', 0, { elementHelper: '.item-2' });
remote.defineState('X - XI уровень', 0, { elementHelper: '.item-3' });

Теперь можем открыть нашу ссылку в панели управления виджетами на сайте ru.widgets.wotstat.info/remote-control и убедиться, что параметры виджета появились в списке, а при наведение мышки обводится нужный блок виджета: widget-remote-control-params.png

Обводка работает благодаря свойству elementHelper, в котором мы указали CSS-селектор нужного блока.

Далее нам нужно добавить обработку изменения параметров. Для создадим функцию updateState, и подпишемся на изменения каждого параметра с помощью метода watch:

src/main.js
javascript
...
function updateState(selector, value) {
  const element = document.querySelector(selector);

  // Устанавливаем значение счетчика
  element.querySelector('.current').textContent = value;

  // Переключаем класс завершенного этапа
  if (value >= 25) element.classList.add('completed');
  else element.classList.remove('completed');

  // Добавляем CSS-переменную для прогресс-бара
  element.style.setProperty('--progress', `${100 * value / 25}%`);
}

remote.defineState('VI - VII уровень', 0, { elementHelper: '.item-1' })
  .watch((v) => updateState('.item-1', v));

remote.defineState('VII - IX уровень', 0, { elementHelper: '.item-2' })
  .watch((v) => updateState('.item-2', v));

remote.defineState('X - XI уровень', 0, { elementHelper: '.item-3' })
  .watch((v) => updateState('.item-3', v));

Готово! Теперь при изменении параметров в панели управления, виджет будет обновляться автоматически.

Публикация виджета

Остался последний шаг - публикация виджета, что бы он был доступен для использования через интернет.

Для начала нужно скомпилировать проект. В терминале выполните команду:

powershell
bun run build
bash
bun run build

В результате чего будет создана папка dist, в которой будет находиться готовый к публикации сайт.

terminal-build-output

Этот сайт можно разместить на любом хостинге статических сайтов, например:

  • wasmer.io бесплатно после регистрации (можно через Google аккаунт)
  • Яндекс Облако в режиме S3 хостига, бесплатно, но регистрация сложная
  • GitHub Pages бесплатно, но требует регистрации и создания репозитория на GitHub

Добавление в OBS

После публикации, вставьте ссылку на виджет в панель удалённого управления, сгенерируйте ключ доступа и добавьте виджет в OBS Studio как Browser Source, указав сгенерированную ссылку.

Не забудьте выбрать Blending Method -> SRGB off, подробнее можно изучить руководстве

Исходный код

Весь исходный код виджета доступен в репозитории на GitHub, а результат опубликован в GitHub Pages по ссылке: soprachevak.github.io/mt-pm3-progress-widget и в панели управления ru.widgets.wotstat.info/remote-control

Авторы

История изменений