2.26M
Category: programmingprogramming

Организация пользовательского ввода: Командный ввод

1.

Название программы
Unity
2023
Тема занятия
Организация
пользовательского
ввода: Командный ввод
Многоуровневое хранение данных ч.1

2.

2023
План
занятия
Unity
Введение
Практика Командный ввод
Практика Первое тестирование
Домашнее задание
Итоги
Изображение от Storyset на Freepik.com

3.

2023
Unity
Проверяем
домашнее задание
1.
Какие трудности возникли
при выполнении задания?
3.
2.
Как можно было бы
избежать трудностей?
4.
Какой информации не хватило
для выполнения задания?
Что больше всего понравилось/не
понравилось в процессе
выполнения задания?

4.

2023
Unity
Введение
Это предпоследний урок темы
«Организация пользовательского ввода».
На данном уроке мы придем
к кульминационному финалу
всех наших наработок и создадим
удобную систему командного ввода.
Let's Go!
Изображение от Storyset на Freepik.com

5.

2023
Unity
Практика
Командный ввод
Изображение от Storyset на Freepik.com

6.

2023
Практика
Первый скрипт, который мы создали
в рамках данной темы,
был «CommandInputHandler».
Все это время он ждал того момент,
как мы дойдем до него.
И этот момент настал!
Unity

7.

2023
Unity
Практика
Создадим внутри класса CommandInputHandler
5 методов, которые нам потребуется
доработать в течении сегодняшнего урока:
public static class CommandInputHandler
{
public static float GetAxis(PlayerIndex player, ControlCommand negativeCommand,
ControlCommand positiveCommand)
{
}
public static bool GetCommand(PlayerIndex player, ControlCommand command)
{
GetAxis — аналог Input.GetAxis.
}
GetCommand — аналог Input.GetKey.
public static bool GetCommandDown(PlayerIndex player, ControlCommand command)
{
GetCommandDown — аналог Input.GetKeyDown.
}
GetCommandUp — аналог Input.GetKetUp.
public static bool GetCommandUp(PlayerIndex player, ControlCommand command)
{
ConvertToAxis — метод для объединения
двух значений в ось.
Разница только в том, что наши методы будут
автоматически учитывать используемый
игроком девайс и настройки управления!
}
public static float ConvertToAxis(float negativeValue, float positiveValue)
{
}
}

8.

2023
Unity
Практика
public static float ConvertToAxis(float negativeValue, float positiveValue)
{
float axisValue = 0f;
if (negativeValue < 0) negativeValue = Mathf.Abs(negativeValue);
if (positiveValue < 0) positiveValue = Mathf.Abs(positiveValue);
if (negativeValue > positiveValue)
{
return -Mathf.Abs(negativeValue);
}
else if(negativeValue < positiveValue)
{
return Mathf.Abs(positiveValue);
}
else
{
return axisValue;
}
Начнем с метода ConvertToAxis.
В целом здесь нет ничего нового.
Напомню: Mathf.Abs принудительно меняет
значение числа на положительное,
а -Mathf.Abs на отрицательное.
}

9.

2023
Unity
Практика
public static float GetAxis(PlayerIndex player, ControlCommand
negativeCommand, ControlCommand positiveCommand)
{
if(player == PlayerIndex.Any)
{
Перейдем к методу GetAxis
и разделим его логику на 2 течения:
}
else
{
// Основа метода GetAxis
}
Индекс игрока Any.
Любой другой индекс.
}

10.

2023
Unity
Практика
if(player == PlayerIndex.Any)
{
float value = 0f;
При индексе Any мы производим
запрос на каждого игрока по очереди.
Первый девайс с которого поступит
обратная связь и вернет значение.
В приоритете девайс первого игрока
и дальше по очереди.
value = GetAxis(PlayerIndex.One, negativeCommand,
positiveCommand);
if (value != 0) return value;
value = GetAxis(PlayerIndex.Two, negativeCommand,
positiveCommand);
if (value != 0) return value;
value = GetAxis(PlayerIndex.Three, negativeCommand,
positiveCommand);
if (value != 0) return value;
value = GetAxis(PlayerIndex.Four, negativeCommand,
positiveCommand);
if (value != 0) return value;
return value;
}

11.

2023
Unity
Практика
else
{
// Основа метода GetAxis
int pIndex = (int)player;
if (ControlSetup.Player[pIndex].Device == ControlDevice.None)
{
return 0f;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
}
else
{
float axisValue = 0f;
Ну а если индекс игрока не Any то происходит
основная логика работы метода.
Основа каждого метода будет разделена
на несколько течений:
ControlDevice.None — игроку
не назначен девайс для управления.
ControlDevice.Any — игрок использует сразу все девайсы.
if (ControlSetup.Player[pIndex].Device == ControlDevice.Keyboard)
{
}
else if (ControlSetup.Player[pIndex].Device ==
ControlDevice.Gamepad)
{
}
ControlDevice.Keyboard — игрок использует клавиатуру.
return axisValue;
}
ControlDevice.Gamepad — игрок использует геймпад.
}

12.

2023
Unity
Практика
Сначала будем заполнять ввод управления
с клавиатуры и геймпадов,
а после это уже будем дописывать
ввод с любого девайса.
После метода GetAxis логика будет
очень похожа, поэтому ты можешь
ее копировать и изменять.
Изображение от Storyset на Freepik.com

13.

2023
Unity
Практика
else
{
// Основа метода GetAxis
int pIndex = (int)player;
Первым делом проверяется что девайс игроку
назначен, иначе метод прерывает работу.
Далее определяется какой конкретно
девайс использует игрок.
if (ControlSetup.Player[pIndex].Device ==
ControlDevice.None)
{
return 0f;
}
else if (ControlSetup.Player[pIndex].Device ==
ControlDevice.None)
{
// Любой девайс...
}

14.

2023
Unity
else
{
float axisValue = 0f;
else if (ControlSetup.Player[pIndex].Device ==
ControlDevice.Gamepad)
{
JoystickIndex jIndex =
ControlSetup.Player[pIndex].Gamepad.Index;
if (ControlSetup.Player[pIndex].Device ==
ControlDevice.Keyboard)
{
float negative = 0f;
if
(Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(negativ
eCommand)))
{
negative = 1f;
}
JoystickKeyCode negKey =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(negativeCommand);
JoystickKeyCode posKey =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(positiveCommand);
float positive = 0f;
if
(Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(positiv
eCommand)))
{
positive = 1f;
}
jIndex, deadN);
float deadN =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(negativeCommand);
float deadP =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(positiveCommand);
float negative = JoystickInput.GetSlope(negKey,
float positive = JoystickInput.GetSlope(posKey,
jIndex, deadP);
axisValue = ConvertToAxis(negative, positive);
}
return axisValue;
}
axisValue = ConvertToAxis(negative, positive);
}
}

15.

2023
Unity
Практика
На основе используемого девайса
мы получаем данные негативного
и позитивного значения и конвертируем их в ось.
Изображение от Storyset на Freepik.com

16.

2023
Практика
Unity
else if(ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
float axisValue = 0f;
float negative = 0f;
float positive = 0f;
if (Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(negativeCommand)))
{
negative = 1f;
}
if (Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(positiveCommand)))
{
positive = 1f;
}
axisValue = ConvertToAxis(negative, positive);
if (axisValue != 0f) return axisValue;
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode negKey = ControlSetup.Player[pIndex].Gamepad.GetBindTo(negativeCommand);
JoystickKeyCode posKey = ControlSetup.Player[pIndex].Gamepad.GetBindTo(positiveCommand);
float deadN = ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(negativeCommand);
float deadP = ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(positiveCommand);
Вот так будет выглядеть
ввод с ControlDevice.Any:
negative = JoystickInput.GetSlope(negKey, jIndex, deadN);
positive = JoystickInput.GetSlope(posKey, jIndex, deadP);
axisValue = ConvertToAxis(negative, positive);
return axisValue;
}

17.

2023
Unity
Практика
public static bool GetCommand(PlayerIndex player, ControlCommand command)
{
if (player == PlayerIndex.Any)
{
bool value = false;
value = GetCommand(PlayerIndex.One, command);
if (value == true) return value;
value = GetCommand(PlayerIndex.Two, command);
if (value == true) return value;
value = GetCommand(PlayerIndex.Three, command);
if (value == true) return value;
value = GetCommand(PlayerIndex.Four, command);
if (value == true) return value;
return value;
}
else
{
// Основа метода GetCommand
}
Следующим идет метод GetCommand.
Начало всех методов
будет практически одинаковым.
}

18.

2023
Unity
Практика
Основная логика работы метода GetCommand.
Здесь данных гораздо меньше,
т.к. теперь требуется лишь одно значение.
else
{
// Основа метода GetCommand
int pIndex = (int)player;
if (ControlSetup.Player[pIndex].Device == ControlDevice.None)
{
return false;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
}
else
{
bool value = false;
if (ControlSetup.Player[pIndex].Device == ControlDevice.Keyboard)
{
value =
Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Gamepad)
{
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKey(key, jIndex, dead);
}
return value;
}
}

19.

2023
Unity
Практика
else if(ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
bool value = false;
value =
Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
if (value != false) return value;
JoystickIndex jIndex =
ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
Логика для ControlDevice.Any
в методе GetCommand.
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKey(key, jIndex, dead);
return value;
}

20.

2023
Unity
Практика
public static bool GetCommandDown(PlayerIndex player, ControlCommand command)
{
if (player == PlayerIndex.Any)
{
bool value = false;
value = GetCommandDown(PlayerIndex.One, command);
if (value == true) return value;
value = GetCommandDown(PlayerIndex.Two, command);
if (value == true) return value;
value = GetCommandDown(PlayerIndex.Three, command);
if (value == true) return value;
value = GetCommandDown(PlayerIndex.Four, command);
if (value == true) return value;
return value;
Метод GetCommandDown.
}
else
{
// Основа метода GetCommandDown
}
}

21.

2023
Unity
Практика
else
{
bool value = false;
Основа метода GetCommandDown.
else
{
// Основа метода GetCommandDown
int pIndex = (int)player;
if (ControlSetup.Player[pIndex].Device == ControlDevice.None)
{
return false;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
}
if (ControlSetup.Player[pIndex].Device == ControlDevice.Keyboard)
{
value =
Input.GetKeyDown(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Gamepad)
{
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKeyDown(key, jIndex, dead);
}
return value;
}
}

22.

2023
Unity
Практика
else if(ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
bool value = false;
value =
Input.GetKeyDown(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
if (value != false) return value;
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
Любой девайс GetCommandDown.
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKeyDown(key, jIndex, dead);
return value;
}

23.

2023
Unity
Практика
public static bool GetCommandUp(PlayerIndex player, ControlCommand command)
{
if (player == PlayerIndex.Any)
{
bool value = false;
value = GetCommandUp(PlayerIndex.One, command);
if (value == true) return value;
value = GetCommandUp(PlayerIndex.Two, command);
if (value == true) return value;
value = GetCommandUp(PlayerIndex.Three, command);
if (value == true) return value;
value = GetCommandUp(PlayerIndex.Four, command);
if (value == true) return value;
return value;
}
else
{
// Основа метода GetCommandUp
}
Метод GetCommandUp.
}

24.

2023
Unity
Практика
else
{
bool value = false;
Основа метода GetCommandUp.
else
{
// Основа метода GetCommandUp
int pIndex = (int)player;
if (ControlSetup.Player[pIndex].Device == ControlDevice.None)
{
return false;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
}
if (ControlSetup.Player[pIndex].Device ==
ControlDevice.Keyboard)
{
value =
Input.GetKeyUp(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
}
else if (ControlSetup.Player[pIndex].Device ==
ControlDevice.Gamepad)
{
JoystickIndex jIndex =
ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKeyUp(key, jIndex, dead);
}
return value;
}
}

25.

2023
Unity
Практика
else if(ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
bool value = false;
value =
Input.GetKeyUp(ControlSetup.Player[pIndex].Keyboard.GetBindTo(command));
if (value != false) return value;
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode key =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(command);
Любой девайс GetCommandUp.
float dead =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(command);
value = JoystickInput.GetKeyUp(key, jIndex, dead);
return value;
}

26.

2023
Unity
Практика
Ну вот и все!
Система полностью готова к использованию.
Остается лишь научится ей пользоваться.
Изображение от Storyset на Freepik.com

27.

2023
Unity
Практика
Первое тестирование
На предыдущем уроке тебе требовалось
подготовить сцену «TronArena».
Если это задание еще не выполнено,
то у тебя остается фора в 1 урок,
т.к. сегодня это не критично!
Изображение от Storyset на Freepik.com

28.

2023
Unity
Практика
Однако, кроме игровой сцены тебе
требовалось создать 3 скрипта:
LightCycleController.
TronManager.
LightArc.
И поместить их
в пространство имен «TronGame».
Вот они нам сейчас понадобятся!
Изображение от Storyset на Freepik.com

29.

2023
Unity
Практика
Зайдем в LifeCycleController
и добавь в него три поля:
Player — индекс игрока, за которым закреплено
управление (во время тестирования будем
использовать игрока 1).
Speed — скорость.
moveDirection — направление движения.
using System.Collections.Generic;
using UnityEngine;
namespace TronGame
{
public class LightCycleController : MonoBehaviour
{
public PlayerIndex Player = PlayerIndex.One;
public float Speed = 3f;
private Vector2 moveDirection = Vector2.up;

30.

2023
Unity
Практика
Ниже вызовем метод Awake
и принудительно назначим первому игроку
управление клавиатурой 1.
Но стоит оговорится: сейчас мы
так делаем только в рамках тестирования,
чтобы проверить функциональность
CommandInputHandler.
Со следующего урока управление
игроки будут выбирать самостоятельно!
void Awake()
{
ControlSetup.Player[0].SetKeyboard(KeyboardIndex.One);
}

31.

2023
Unity
Практика
Далее, в методе Update
мы уже задействуем наш
CommandInputHandler,
чтобы назначить направление для движения.
Обрати внимание на логику работы:
игроку запрещено выбирать направление
по оси движения, чтобы он не мог делать
резкий разворот назад.
Также мы используем «Forward»
как команду вверх, а «Backward»
как команду вниз.
void Update()
{
if (Player != PlayerIndex.Any)
{
if (moveDirection == Vector2.down || moveDirection == Vector2.up)
{
if (CommandInputHandler.GetCommandDown(Player, ControlCommand.Left))
{
moveDirection = Vector2.left;
}
else if (CommandInputHandler.GetCommandDown(Player,
ControlCommand.Right))
{
moveDirection = Vector2.right;
}
}
else if (moveDirection == Vector2.left || moveDirection == Vector2.right)
{
if (CommandInputHandler.GetCommandDown(Player,
ControlCommand.Backward))
{
moveDirection = Vector2.down;
}
else if (CommandInputHandler.GetCommandDown(Player,
ControlCommand.Forward))
{
moveDirection = Vector2.up;
}
}
}
}

32.

2023
Unity
Практика
В конце вызываем метод FixedUpdate,
где производим смещение объекта
методом Translate.
private void FixedUpdate()
{
transform.Translate(moveDirection * Speed *
Time.fixedDeltaTime);
}

33.

2023
Unity
Практика
Готово!
Теперь необходимо сохранить скрипт
и назначить его на prefab «LightCycle».
Изображение от Storyset на Freepik.com

34.

2023
Unity
Практика
Если не понимаешь о чем идет речь,
то напомню что было в ДЗ:
Иерархия префаба LightCycle
должна быть следующей:
LightCycleSprite
— визуальное отображение (можно кубик).
LightArcEffect
— эффект световой дуги (можешь оставить пустым).

35.

2023
Практика
Теперь ты можешь попробовать разместить
LightCycle на сцене и запустить тестирование.
Объект должен принимать команды с первой
раскладки клавиатуры, двигаясь по двум осям.
Unity

36.

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

37.

2023
Unity
Практика
После урока ты можешь протестировать
и другие раскладки клавиатуры, принудительно
назначив их первому игроку в методе Awake.
А вот как назначать геймпады
мы уже научимся на следующем уроке!
Изображение от Storyset на Freepik.com

38.

2023
Unity
Домашнее
задание
В некоторых случая использование
целой оси может быть не удобно.
Чтобы это исправить добавь метод
«GetSlope» в CommandInputHandler,
который будет возвращать значение
от 0 до 1 по команде управления.
Также не помешает наличие методов
GetAxisRaw и GetSlopeRaw,
которые будут возвращать
заранее округленные значения.
Добавь их!

39.

2023
Unity
Итоги
1
2
3
Что нового сегодня было на занятии?
4
Как мы создаем ось из двух
неизвестных заранее кнопок?
Какое задание было сложным
на твой взгляд?
5
Зачем мы принудительно назначали
первому игроку раскладку клавиатуры?
Что будет если передать методам
CommandInputHandler аргумент PlayerIndex.Any?
6
Как ты думаешь, где еще ты бы мог
применить эти знания?
English     Русский Rules