Similar presentations:
Организация пользовательского ввода: Мини - игра «TRON»
1.
Название программыUnity
2023
Тема занятия
Организация
пользовательского ввода:
Мини-игра «TRON»
Многоуровневое хранение данных ч.1
2.
2023План
занятия
Unity
Введение
Теория
TRON
Практика Светоциклы
Практика Световой менеджер
Практика Световая дуга
Домашнее задание
Итоги
Изображение от Storyset на Freepik.com
3.
2023Unity
Проверяем
домашнее задание
1.
Какие трудности возникли
при выполнении задания?
3.
2.
Как можно было бы
избежать трудностей?
4.
Какой информации не хватило
для выполнения задания?
Что больше всего понравилось/не
понравилось в процессе
выполнения задания?
4.
2023Unity
Введение
Вот так можно было реализовать метод «GetSlope»:
public static float GetSlope(PlayerIndex player, ControlCommand
positiveCommand)
{
if (player == PlayerIndex.Any)
{
float value = 0f;
value = GetPositiveSlope(PlayerIndex.One, positiveCommand);
if (value != 0) return value;
value = GetPositiveSlope(PlayerIndex.Two, positiveCommand);
if (value != 0) return value;
value = GetPositiveSlope(PlayerIndex.Three, positiveCommand);
if (value != 0) return value;
value = GetPositiveSlope(PlayerIndex.Four, positiveCommand);
if (value != 0) return value;
return value;
}
else
{
int pIndex = (int)player;
if (ControlSetup.Player[pIndex].Device == ControlDevice.None)
{
return 0f;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Any)
{
// Любой девайс...
float positiveValue = 0f;
if
(Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(positiveCommand)
))
{
positiveValue = 1f;
}
if (positiveValue != 0f) return positiveValue;
JoystickIndex jIndex =
ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode posKey =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(positiveCommand);
float deadP =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(positiveCommand);
positiveValue = JoystickInput.GetSlope(posKey, jIndex,
deadP);
return Mathf.Abs(positiveValue);
}
else
{
float positiveValue = 0f;
5.
2023Unity
Введение
if (ControlSetup.Player[pIndex].Device == ControlDevice.Keyboard)
{
if
(Input.GetKey(ControlSetup.Player[pIndex].Keyboard.GetBindTo(positiveCommand)))
{
positiveValue = 1f;
}
else positiveValue = 0f;
}
else if (ControlSetup.Player[pIndex].Device == ControlDevice.Gamepad)
{
JoystickIndex jIndex = ControlSetup.Player[pIndex].Gamepad.Index;
JoystickKeyCode posKey =
ControlSetup.Player[pIndex].Gamepad.GetBindTo(positiveCommand);
float deadP =
ControlSetup.Player[pIndex].Gamepad.GetDeadZoneBy(positiveCommand);
positiveValue = JoystickInput.GetSlope(posKey, jIndex, deadP);
}
return Mathf.Abs(positiveValue);
}
}
}
6.
2023Введение
Перепиши его,
т.к. он понадобится нам в будущем.
Unity
7.
2023Unity
Введение
Сегодня будет завершающий урок по теме
«Организация пользовательского ввода».
На данном уроке мы создадим небольшой
проект, на основе которого протестируем
работу нашей новой системы ввода!
Изображение от Storyset на Freepik.com
8.
2023Теория
TRON
Для начала давай узнаем историю игры,
аналог которой мы будем создавать
на этом уроке.
Unity
9.
2023Unity
Теория
TRON — аркадная игра,
разработанная в 1982 году, производимая
и распространяемая Bally Midway.
Смотреть
10.
2023Теория
Она была создана на основе одноименного
фильма Walt Disney Productions «TRON»,
который был выпущен в том же году.
Фильм считается «культовой классикой»,
ведь он практически один из первых
открыл жанр «цифровой мир»,
выйдя гораздо раньше «Матрицы».
Unity
11.
2023Теория
Главная фишка серии трон
— светоциклы.
Это разновидность мотоциклов будущего,
который были придуманы и показаны
в фильме 1982 года.
Unity
12.
2023Теория
Главная особенность светоциклов в том, что
они могли оставлять за собой длинную линию,
которая называется «световой дугой».
Световая дуга не только осязаема,
но и является основным видом
оружия на «виртуальной арене».
Она опасна как для противника,
так и для самого райдера,
который может разбится о нее.
Unity
13.
2023Теория
На основе этого и был придуман
основной геймплей игры 1982 года.
Он был очень простой и захватывающий.
Игрок выбирал одно из четырех направлений
движения светоцикла, оставляя
за собой световую дугу.
Цель игры — заставить опонента
разбится о световую дугу,
оставшись при этом живым.
Unity
14.
2023Теория
Сейчас геймплей данной игры может
показаться тебе скучным, но игре TRON
в свое время была присвоена награда
«Coin-Operated Game of the Year».
Аркадная игра стала не менее культовой
чем фильм, и отсылки
к ней можно увидеть и по сей день.
Unity
15.
2023Теория
Например, для GTA 5: Online было
выпущено целое обновление под заголовком
«Deadline», в которое были добавлены
светоциклы и полноценный режим
сражения световой дугой на арене.
Unity
16.
2023Теория
В 2010 году серию попытались возродить,
выпустив фильм «Трон: Наследие».
Он должен был перезапустить серию
и стать для нее новым началом.
Однако, фильм подвергся жесткой критике
и провалился в прокате.
Unity
17.
2023Теория
В 2010 году была выпущена игра
«Трон: Эволюция», которая была связана
сюжетно с фильмом того же года.
Ее раскритиковали за неудобное
управление и большое количество багов.
Более того, в игре акцент был сделан
на рукопашный бой, что сильно
огорчило фанатов.
А в 2012 году был выпущен мультсериал
«Трон: Восстание», который не возымел
должной популярности, но остался в сердцах
многих поклонников серии.
Unity
18.
2023Unity
Теория
После этого серию «TRON» было принято
вновь отправить на заморозку,
где она находится и по сей день.
В некоторых источниках ходят слухи о съемках
фильма «Трон: Арес», который должен стать
продолжением фильма 2010 года.
Однако, никаких конкретных
подтверждений этому нет.
На этой печальной ноте мы перейдем
к разработке аналога классической игры
«TRON», чем отдадим ей дань уважения.
Изображение от Storyset на Freepik.com
19.
2023Практика
Светоциклы
Unity
20.
2023Unity
Практика
Итак, на данный момент
у нас уже должно быть:
Подготовленная сцена с UI элементами.
Prefab LightCycle.
Скрипт TronManager.
Скрипт LightCycleController.
Скрипт LightArc.
Начнем мы с того, что разработаем
полноценный выбор управления и сразу
реализуем логику менеджера игры,
т.к., по сути, они будут взаимосвязаны
в данном проекте.
Изображение от Storyset на Freepik.com
21.
2023Unity
Практика
Перейдем в скрипт LightCycleController
и добавим новые параметры светоцикла:
OnCrash — событие разбития.
Crashed — текущее состояние.
rb — ссылка на Rigidbody 2D.
collider — ссылка на коллайдер.
С поменяем значение параметров
Player и Speed с прошлого урока.
Также добавим проверочный
атрибут RequireComponent.
using UnityEngine;
using UnityEngine.Events;
namespace TronGame
{
[RequireComponent (typeof(Rigidbody2D),
typeof(Collider2D))]
public class LightCycleController : MonoBehaviour
{
public UnityEvent OnCrash;
public bool Crashed = false;
public PlayerIndex Player = PlayerIndex.Any;
public float Speed = 0f;
private Vector2 moveDirection = Vector2.up;
private Rigidbody2D rb;
private Collider2D collider;
22.
2023Unity
Практика
Опустимся в метод Awake.
Уберем все что писали ранее
и опишем логику кэширования
и пред настроек.
Как ты понимаешь, наш светоцикл
будет единственным триггером в игре.
Именно поэтому мы добавляем
ему физику в кинематическом состоянии.
void Awake()
{
rb = GetComponent<Rigidbody2D>();
rb.isKinematic = true;
collider = GetComponent<Collider2D>();
collider.isTrigger = true;
}
23.
2023Unity
Практика
Методы Update и FixedUpdate
пока не трогаем.
Создадим под ними новый метод
«Crash», в котором будем производить
разбиение светоцикла.
public void Crash()
{
Crashed = true;
OnCrash.Invoke();
}
24.
2023Unity
Практика
В конце вызовем метод
«OnTriggerEnter2D»,
в котором пока проверим пересечение
с другими светоциклами.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.GetComponent<LightCycleController>())
{
Crash();
}
}
25.
2023Практика
Сохраним скрипт
и прикрепим его к LightCycle.
Если требуется особый вид коллайдера
(например BoxCollider2D или CircleCollider2D),
то его можно добавить заранее, или поменять
их местами после добавления скрипта.
Удалить последний коллайдер
совсем скрипт просто так не позволит.
Unity
26.
2023Unity
Практика
И на этом моменте основной
функционал светоциклов готов!
Но теперь требуется
где-то закреплять за ними управление.
Для этого перейдем в TronManager.
Изображение от Storyset на Freepik.com
27.
2023Практика
Световой менеджер
Unity
28.
2023Unity
Практика
В TronManager необходимо
будет добавить следующие параметры:
MaunTextMessage — основной текст на экране.
PlayerOneText — текст для игрока 1.
PlayerTwoText — текст для игрока 2.
PlayerOneScore — счет игрока 1.
PlayerTwoScore — счет игрока 2.
FirstLightCycle — светоцикл игрока 1.
SecondLightCycle — светоцикл игрока 2.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
namespace TronGame
{
public class TronManager : MonoBehaviour
{
public Text MainTextMassage;
public Text PlayerOneText;
public Text PlayerTwoText;
public static int PlayerOneScore = 0;
public static int PlayerTwoScore = 0;
public LightCycleController FirstLightCycle;
public LightCycleController SecondLightCycle;
LightCyclesSpeed — скорость всех светоциклов.
public float LightCyclesSpeed = 3f;
gameBegined — игра начала/не началась.
private bool gameBegined = false;
private bool gameOver = false;
gameOver — игра окончена/не окончена.
29.
2023Unity
Практика
Ниже создадим метод окончания игры,
который будет запускать корутину
«BattleResult».
Саму корутину мы реализуем позже.
public void GameOver()
{
if (gameOver == false)
{
StartCoroutine(BattleResult());
}
}
30.
2023Unity
Практика
private void Awake()
{
MainTextMassage.text = "";
if (FirstLightCycle != null && SecondLightCycle != null)
{
FirstLightCycle.Speed = 0f;
SecondLightCycle.Speed = 0f;
Ниже вызовем метод Awake
с делаем следующее:
FirstLightCycle.Player = PlayerIndex.One;
SecondLightCycle.Player = PlayerIndex.Two;
Остановим все светоциклы.
FirstLightCycle.OnCrash.AddListener(GameOver);
SecondLightCycle.OnCrash.AddListener(GameOver);
Закрепим каждого игрока за своим светоциклом.
}
Подпишем метод GameOver
на события разбивания светоциклов.
Запустим проверку геймпадов.
StartCoroutine(ControlSetup.GamepadObserver());
}
31.
2023Unity
Практика
IEnumerator BattleResult()
{
gameOver = true;
yield return new WaitForSeconds(0.01f);
Теперь напишим саму корутину
«BattleResult», которая будет
вызываться при окончании игры.
FirstLightCycle.Speed = 0f;
SecondLightCycle.Speed = 0f;
if(FirstLightCycle.Crashed && SecondLightCycle.Crashed)
{
PlayerOneScore++;
PlayerTwoScore++;
}
else
{
if (FirstLightCycle.Crashed == false)
{
PlayerOneScore++;
}
if (SecondLightCycle.Crashed == false)
{
PlayerTwoScore++;
}
}
Логика в ней проста:
Разбились оба — каждому по одному очку.
Разбился первый игрок — отдаем очко второму.
Разбился второй игрок — отдаем очко первому.
Далее выведение текста, ожидание
в 3 секунды и перезагрузка сцены.
Ожидание 0.1 секунду вначале мы делаем для того,
чтобы зафиксировать что разбились оба игрока.
MainTextMassage.text = $"{PlayerOneScore} | {PlayerTwoScore}";
yield return new WaitForSeconds(3f);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
yield break;
}
32.
2023Практика
Unity
IEnumerator StartGame()
{
gameBegined = true;
PlayerOneText.text = ControlSetup.Player[0].Device.ToString();
PlayerTwoText.text = ControlSetup.Player[1].Device.ToString();
yield return new WaitForSeconds(1f);
PlayerOneText.text = "";
PlayerTwoText.text = "";
Также, нам понадобися создать корутину
«StartGame», где мы будем
производить запуск игры.
MainTextMassage.text = "3";
yield return new WaitForSeconds(1f);
Здесь тоже все очень просто!
MainTextMassage.text = "1";
yield return new WaitForSeconds(1f);
MainTextMassage.text = "2";
yield return new WaitForSeconds(1f);
FirstLightCycle.Speed = LightCyclesSpeed;
SecondLightCycle.Speed = LightCyclesSpeed;
Одну секунду мы показываем на экране устройства
управления, после чего убираем их с экрана
и начинаем обратный отсчет в 3 секунды.
По завершению обратного отсчета мы возвращаем
скорость светоциклам и игра начинается!
MainTextMassage.text = "GO";
yield return new WaitForSeconds(1f);
MainTextMassage.text = "";
yield break;
}
33.
2023Unity
Практика
Так, по логике запуска и окончания вроде все.
Ничего не забыли?
Ах да, назначение управления!
Изображение от Storyset на Freepik.com
34.
2023Unity
Практика
В данном методе мы должны будем фиксировать
нажатия игрока, чтобы определить
его девайс управления.
На клавиатуре сначала необходимо проверить,
какую конкретно клавишу нажал игрок,
перебрав весь доступный в Unity KeyCode.
Далее отравить этот KeyCode на проверку
нашему методу GetKeyboardLayoutByKey.
Если данный KeyCode будет закреплен за одной
из раскладок клавиатуры — метод вернет ее индекс.
В ином случае метод вернет значение
KeyboardIndex.Any, что нам совсем не нужно.
private void DefinePlayerControll(PlayerIndex player)
{
int pIndex = (int)player;
// Клавиатура...
KeyCode keyCode = KeyCode.None;
foreach (KeyCode key in
KeyCode.GetValues(typeof(KeyCode)))
{
if (key != KeyCode.None)
{
if (Input.GetKeyDown(key))
{
keyCode = key;
}
}
}
35.
2023Unity
Практика
KeyboardIndex keyboard =
ControlSetup.GetKeyboardLayoutByKey(keyCode);
if (keyboard != KeyboardIndex.Any)
{
if
(ControlSetup.CheckKeyboardLayoutBusy(keyboard) == false)
{
ControlSetup.Player[pIndex].SetKeyboard(keyboard);
}
}
// Геймпады...
}
36.
2023Практика
Unity
private void DefinePlayerControll(PlayerIndex player)
{
int pIndex = (int)player;
// Клавиатура...
С геймпадами будет чуть проще, т.к. для них
мы заранее предусмотрели удобные методы.
Сначала получаем JoystickIndex
через наш метод GetJoystickIndexByKey.
Дальше мы передаем индекс данного
джойстика в наш метод GetGamepadSetup,
чтобы получить GameController.
Если он не будет пустым
— присваиваем его игроку.
// Геймпады...
JoystickIndex jId =
JoystickInput.GetJoystickIndexByKey();
GameController newGamepad =
ControlSetup.GetGamepadSetup(jId);
if (newGamepad != null)
{
if (ControlSetup.CheckGamepadBusy(newGamepad)
== false)
{
ControlSetup.Player[pIndex].SetGamepad(newGamepad);
}
}
}
37.
2023Unity
Практика
В обоих случаях мы делаем дополнительные
проверки на то, что контроллеры
уже не заняты другими игроками.
Это, пожалуй, самая важная проверка
и пропускать ее нельзя!
Изображение от Storyset на Freepik.com
38.
2023Unity
Практика
Как ты видишь, проверка клавиатуры
получилась не очень интуитивной и длинной.
Если тебе захочется улучшить
этот процесс — создай
в будущем скрипт «KeyboardInput».
Сделай его статическим и опиши
в нем все необходимы методы,
которые могут тебе понадобится для работы.
Например: «Вернуть первую нажатую
пользователем клавишу»
или «Вернуть индекс раскладки клавиатуры
через определенное командное действие».
Здесь все в твоих руках!
39.
2023Unity
Практика
Но и это еще не все!
Данный метод еще нигде не вызван.
Производить данное действие необходимо
в методе Update и делать
это в правильном порядке!
Изображение от Storyset на Freepik.com
40.
2023Unity
Практика
Вот как это можно реализовать проще всего:
В данном случае мы просим каждого игрока
по очереди выбрать свой девайс, выводя
это текстовым уведомлением.
Как только все игроки выберут девайсы
— игра запустится.
Повторное назначение уже не будет
требоваться, т.к. данные о контроллерах
мы сделали статическими!
void Update()
{
if (ControlSetup.Player[0].Device ==
ControlDevice.None || ControlSetup.Player[1].Device ==
ControlDevice.None)
{
MainTextMassage.text = "";
if (ControlSetup.Player[0].Device ==
ControlDevice.None)
{
PlayerOneText.text = "Player One \n
Please, select control devace!";
DefinePlayerControll(PlayerIndex.One);
}
else
{
PlayerOneText.text =
ControlSetup.Player[0].Device.ToString();
}
41.
2023Unity
Практика
if (ControlSetup.Player[1].Device == ControlDevice.None)
{
PlayerTwoText.text = "Player Two \n Please, select control
devace!";
DefinePlayerControll(PlayerIndex.Two);
}
else
{
PlayerTwoText.text =
ControlSetup.Player[1].Device.ToString();
}
}
else if (ControlSetup.Player[0].Device != ControlDevice.None &&
ControlSetup.Player[1].Device != ControlDevice.None)
{
if (gameBegined == false)
{
StartCoroutine(StartGame());
}
}
}
42.
2023Практика
На этом TronManager готов!
Сохраним его и перейдем на сцену.
Разместим наши светоциклы (кубики)
на стартовые позиции.
Unity
43.
2023Практика
Сам TronManager прикрепим к пустому
объекту на сцене и обозначим ему все данные.
Unity
44.
2023Практика
Теперь ты можешь запустить тест, выбрать
управление для каждого игрока и поуправлять
кубиками светоциклами!
Наличие геймпада не обязательно.
Ты можешь использовать первую
и вторую раскладку клавиатуры!
Unity
45.
2023Практика
Рубрика «забавные факты»: говорят,
что развитие навыков «амбидекстра»
улучшает работоспособность мозга.
programming