С геймпадом всё становится лучше

Сб 27 февраля 2016

Всё никак не доходят руки пощупать Unreal. И вот недавно таки запустил его. А запустив, совершенно случайно, я обнаружил, что по сцене редактора можно перемещаться с помощью геймпада.

И немедленно ~~выпил~~ закрыл Unreal…

…и открыл Unity. Очень уж мне захотелось реализовать такую же фигню в юнити. Зачем мне джойстик в редакторе? Понятия не имею, вещь совершенно бесполезная.

Чем можно воспользоваться для реализации этой задумки? Ну например OnGUI, вызывается каждый раз, когда прилетает Event от устройств ввода, или при перерисовке. Только есть проблема, геймпад не шлёт событий, нам нужно опрашивать его состояние. Update в ExecuteInEditMode, вызывается если в сцене что-то меняется. Не подходит. Всякого рода OnRenderObject я даже пробовать не стал.

Что бы опрашивать состояние джойстика нам нужно какое-то подобие Update'а. Методом научного тыка я пришёл к такому решению:

using UnityEngine;
using UnityEditor;
using System;

[CustomEditor(typeof(LevelDesigner))]
public class LevelDesigner : Editor
{
    void OnSceneGUI()
    {
        if (!EditorApplication.isPlaying)
        {
            Update();
            SceneView.RepaintAll();
        }
    }
}

Добавляем скрипт на пустышку в сцене. Теперь, если выбрать объект в иерархии, у нас начинает работать Update, функция так названа исключительно из-за лени.

Update это хорошо, вот только состояние джойстика мы так и не можем получить. Input-то в режиме редактора не работает. Что, закрываем лавочку и расходимся? Не торопитесь, сначала я хочу задать вам вопрос.

Приходилось ли вам вибрировать?

Мне вот приходилось. Всего пару раз, но этого хватило чтобы запомнить вот эту хабростатью: Вибрация геймпада XboxOne для Unity3d. Берём от туда обёртку над XInput'ом и радуемся жизни.

Давайте полетаем камерой по сцене.

void Update () {
    XInputWrapper.XInputState _state = new XInputWrapper.XInputState();
    XInputWrapper.XInput.XInputGetState(0, ref _state);
    GameObject _pointer = GameObject.Find("pointer");
    if (_state.Gamepad.sThumbRX / 32767.0f > .5f)
    {
        if (DateTime.Now.Ticks > curent_time)
        {
            SceneView.lastActiveSceneView.pivot = new Vector3(SceneView.lastActiveSceneView.pivot.x + .5f, SceneView.lastActiveSceneView.pivot.y, SceneView.lastActiveSceneView.pivot.z);
            curent_time = DateTime.Now.Ticks + 400000;
        }
    }
}

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

Зум камеры я повесил на курки, это например наезд:

if (_state.Gamepad.bRightTrigger > 10)
{
    if (DateTime.Now.Ticks > curent_time)
    {
        SceneView.lastActiveSceneView.size += 1;
        curent_time = DateTime.Now.Ticks + 400000;
    }
}

При желании можно сделать его с регулируемой скоростью.

Поскольку прикручивал всё это хозяйство к 2д прототипу, то поворотов камеры реализовывать не стал. Зато сделал тайловый редактор уровня. Левый стик - перемещение указателя, A - создать тайл, B - стереть, D-Pad - переключение спрайта и слоя. Можно на Start повесить запуск игры, и тестировать только что созданный кусок уровня, предварительно переместив персонажа. Так, кроме фана, появляется хоть какой-то смысл в этом начинании. (Хотя в Tiled рисовать уровень и потом парсить json, всё же проще). Приводить код редактора смысла не вижу, его нужно затачивать под конкретные проекты.

Вот так моё 2х-минутное знакомство с анрилом превратилось в бесполезное расширение для юнити. Теперь я могу, откинувшись в кресле, играть в игры, пока делаю игры.

on the top