Table of Contents

Интерфейс для геймпада

В этом руководстве экран делается полностью управляемым геймпадом (или стрелками клавиатуры): фокус, который начинается в нужном месте, компоновка, по которой можно ходить крестовиной, диалоги, удерживающие фокус, и переключатель для игрового процесса. Экран — страница экипировки: сетка кнопок, ряд переключателей, флажок, поле со списком и кнопка «Старт».

Вам понадобится страница, загруженная из разметки (Первый экран). Как работает фокус, описано на странице Ввод, фокус и навигация.

1. Компоновка, по которой можно ходить крестовиной

Стрелки и крестовина переводят фокус на ближайший фокусируемый элемент в этом направлении, поэтому порядок табуляции не нужен. Обычные панели и сетки работают как есть:

<!-- A loadout screen a gamepad can fully use: a grid of buttons, a row of toggles, a check box, a combo box and a button.
     The data context is the view model with a Difficulties list. -->
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="360">
  <TextBlock FontSize="28" Margin="0,0,0,16">Loadout</TextBlock>

  <!-- Arrows and the D-pad move to the nearest control in that direction, so a grid needs no order. -->
  <Grid Margin="0,0,0,16">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Button x:Name="Sword" Grid.Row="0" Grid.Column="0" Margin="0,0,8,8" HorizontalAlignment="Stretch">Sword</Button>
    <Button x:Name="Bow" Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" HorizontalAlignment="Stretch">Bow</Button>
    <Button x:Name="Shield" Grid.Row="1" Grid.Column="0" Margin="0,0,8,0" HorizontalAlignment="Stretch">Shield</Button>
    <Button x:Name="Potion" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch">Potion</Button>
  </Grid>

  <StackPanel Orientation="Horizontal" Margin="0,0,0,16">
    <ToggleButton x:Name="Easy" Margin="0,0,8,0">Easy</ToggleButton>
    <ToggleButton x:Name="Normal" Margin="0,0,8,0">Normal</ToggleButton>
    <ToggleButton x:Name="Hard">Hard</ToggleButton>
  </StackPanel>

  <CheckBox x:Name="Subtitles" Margin="0,0,0,16">Subtitles</CheckBox>
  <ComboBox x:Name="Difficulty" ItemsSource="{Binding Difficulties}" SelectedIndex="1" Margin="0,0,0,16" />
  <Button x:Name="Start" Padding="24,8" HorizontalAlignment="Left">Start</Button>
</StackPanel>

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

2. Сфокусируйте первый элемент при открытии

Если ничего не сфокусировано, крестовине нечего двигать. Сфокусируйте первую кнопку при показе страницы:

/// <summary>Shows a menu page and puts the focus on its first button, so the D-pad has somewhere to start.</summary>
public static void Show(Canvas canvas, UIElement menu)
{
    canvas.Add(menu);
    canvas.Focus(menu.FindRequiredControl<Button>("Start"));
}

3. Сделайте фокус заметным

У игрока с геймпадом нет указателя, за которым можно следить, поэтому сфокусированный элемент должен выделяться. Встроенная тема даёт каждому элементу группу FocusStates с синей рамкой; тема игры может сделать её ярче неявным стилем, как описано в Своих темах. Проверьте с дивана: если фокус не виден с метра, усильте его.

4. Диалоги

Открытые Window или Dialog — область фокуса: крестовина остаётся внутри, B закрывает её, а фокус возвращается на элемент, который его имел:

/// <summary>A dialog is a focus scope: the D-pad stays inside it, B closes it, and the focus returns to the menu.</summary>
public static async Task<bool> ConfirmQuitAsync(Canvas canvas)
{
    DialogResult result = await MessageBox.ShowAsync(canvas, "Quit to desktop?", DialogButtons.YesNo);
    return result == DialogResult.Yes;
}

Для собственной панели с тем же поведением установите ей IsFocusScope.

5. Списки и прокрутка

Список или область прокрутки прокручивает сфокусированный элемент в видимую область, а поле со списком открывается по A и пока открыто принимает крестовину вверх и вниз; B закрывает его.

Примечание

Пункты списка пока нельзя выбирать с клавиатуры или геймпада; см. Известные проблемы.

6. Игровой процесс

Пока игрок управляет игрой, крестовина и стик не должны ещё и двигать фокус меню. Отключайте навигацию на время игры и включайте снова для меню:

/// <summary>While the player controls the game, the D-pad and sticks belong to gameplay, not to the menu focus.</summary>
public static void SetGameplay(Canvas canvas, bool playing) => canvas.IsKeyboardNavigationEnabled = !playing;

Сфокусированный элемент по-прежнему получает стрелки, пока навигация отключена (текстовое поле двигает курсор ввода); отключён только перенос фокуса на другой элемент.

Клавиши

Клавиатура Геймпад Действие
Стрелки Крестовина Перевести фокус на ближайший фокусируемый элемент в этом направлении.
Tab, Shift+Tab Нажатие правого / левого стика Перевести фокус на следующий / предыдущий элемент.
Enter A Активировать сфокусированный элемент.
Escape B Закрыть открытый диалог или список.
Примечание

Эти клавиши пока нельзя переназначить; настраиваемые привязки есть в планах.

Что вы построили

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

Дальше