Closed33
InputSystemを利用した汎用ユーザーインプット作成の備忘録
ピン留めされたアイテム
顧客(自分)の要望: u1wだったりちょっとしたゲーム性の検証のために一からつくるのはめんどくさいのでオレオレインプットシステムが欲しい。
ピン留めされたアイテム
完成
container
using MessagePipe;
using UnityEngine.InputSystem;
using VContainer;
using VContainer.Unity;
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// MessagePipeの設定
var options = builder.RegisterMessagePipe();
builder.RegisterMessageBroker<InputAction.CallbackContext>(options);
// InputPlayer
builder.RegisterComponentInHierarchy<PlayerInput>();
// Message発信源
builder.RegisterEntryPoint<PlayerInputProvider>(Lifetime.Singleton);
// prefab生成用
builder.RegisterComponentInHierarchy<InstantiateTest>();
}
}
PlayerInput発信
using MessagePipe;
using UnityEngine;
using UnityEngine.InputSystem;
using VContainer.Unity;
public sealed class PlayerInputProvider : IStartable
{
/// <summary>
/// MessagePipeにメッセージを流す用のインタフェース
/// </summary>
private readonly IPublisher<InputAction.CallbackContext> _inputPublisher;
private readonly PlayerInput _playerInput;
void IStartable.Start()
{
this._playerInput.onActionTriggered += OnAction;
}
public PlayerInputProvider(
IPublisher<InputAction.CallbackContext> inputPublisher, PlayerInput playerInput
) {
this._inputPublisher = inputPublisher;
this._playerInput = playerInput;
}
private void OnAction(InputAction.CallbackContext context)
{
this._inputPublisher.Publish(context);
}
}
Message受信側
using Cysharp.Threading.Tasks;
using MessagePipe;
using UnityEngine;
using UnityEngine.InputSystem;
using VContainer;
public sealed class PlayerInputReceiverTest : MonoBehaviour
{
/// <summary>
/// MessagePipeからメッセージを受け取る用インタフェース
/// </summary>
[Inject] private readonly ISubscriber<InputAction.CallbackContext> _inputEventSubscriber;
private void Start()
{
// 入力イベントの受信を開始する
_inputEventSubscriber.Subscribe(OnInputEventReceived)
// MonoBehaviourに寿命を紐づける(これはUniTaskの機能)
.AddTo(this.GetCancellationTokenOnDestroy());
}
/// <summary>
/// 入力イベントを処理する
/// </summary>
private void OnInputEventReceived(InputAction.CallbackContext context)
{
Debug.Log("Sub: " + context);
}
}
いろいろと調べたが大体Unityが提供してくれているPlayerInputでよさそうだった。
参考文献↓
ただActionは自前で作る必要はありそう。
現状だとインプットを取得したいオブジェクトすべてにPlayerInputを入れないといけないのでちょっと密な気がするのでMessagePipeの学習がてらInputのメッセージを飛ばすやつを作成してみる
参考資料
受け取る側
using Cysharp.Threading.Tasks;
using MessagePipe;
using UnityEngine;
using VContainer;
public sealed class PlayerInputRecieverTest : MonoBehaviour
{
/// <summary>
/// MessagePipeからメッセージを受け取る用インタフェース
/// </summary>
[Inject] private ISubscriber<InputParams> _inputEventSubscriber;
// 各種フィールド
private void Start()
{
// 入力イベントの受信を開始する
_inputEventSubscriber.Subscribe(OnInputEventReceived)
// MonoBehaviourに寿命を紐づける(これはUniTaskの機能)
.AddTo(this.GetCancellationTokenOnDestroy());
}
/// <summary>
/// 入力イベントを処理する
/// </summary>
private void OnInputEventReceived(InputParams input)
{
Debug.Log("Fire: " + input.Fire);
Debug.Log("Move: " + input.Move);
}
}
発信側
using MessagePipe;
using UnityEngine;
using UnityEngine.InputSystem;
using VContainer.Unity;
[RequireComponent(typeof(PlayerInput))]
public sealed class PlayerInputProvider : IStartable
{
/// <summary>
/// MessagePipeにメッセージを流す用のインタフェース
/// </summary>
private readonly IPublisher<InputParams> _inputPublisher;
private readonly PlayerInput _playerInput;
void IStartable.Start()
{
_playerInput.onActionTriggered += OnMove;
}
public PlayerInputProvider(IPublisher<InputParams> inputPublisher, PlayerInput playerInput)
{
this._inputPublisher = inputPublisher;
this._playerInput = playerInput;
}
private void OnMove(InputAction.CallbackContext context)
{
if (context.action.name != "Move") {
return;
}
var axis = context.ReadValue<Vector2>();
Debug.Log(axis);
// メッセージを作成
var inputParams = new InputParams(false, axis);
// メッセージ送信
_inputPublisher.Publish(inputParams);
// Debug.Log(axis);
}
}
発信側と受け取る側をつなぐやつ
using MessagePipe;
using UnityEngine;
using UnityEngine.InputSystem;
using VContainer;
using VContainer.Unity;
public class GameLifetimeScope : LifetimeScope
{
// MoveCubeのPrefabへの参照
[SerializeField] private PlayerInputRecieverTest _playerInputReciever;
protected override void Configure(IContainerBuilder builder)
{
// MessagePipeの設定
var options = builder.RegisterMessagePipe();
// InputParamsを伝達できるように設定する
builder.RegisterMessageBroker<InputParams>(options);
// InputEventProviderを起動
builder.RegisterComponentInHierarchy<PlayerInput>();
builder.RegisterEntryPoint<PlayerInputProvider>();
// MoveCubeをDIしながらInstantiate
builder.RegisterBuildCallback(resolver =>
{
resolver.Instantiate(_playerInputReciever);
});
}
}
送るもの
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public readonly struct InputParams : System.IEquatable<InputParams>
{
/// <summary>
/// 撃つフラグ
/// </summary>
public bool Fire { get; }
/// <summary>
/// 移動操作
/// </summary>
public Vector2 Move { get; }
public InputParams(bool isFire, Vector2 move)
{
Fire = isFire;
Move = move;
}
public bool Equals(InputParams other)
{
return Fire == other.Fire && Move.Equals(other.Move);
}
public override bool Equals(object obj)
{
return obj is InputParams other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(Fire, Move);
}
}
Unityエディタ上
実行画面
builder.RegisterComponentInHierarchy<PlayerInput>();
をつかっているためシーンにPlayerInputをもったオブジェクトを置かないといけないのがちょっと気になる。
PlayerInputをもったオブジェクトも
builder.RegisterBuildCallback(resolver =>
{
resolver.Instantiate(_playerInputReciever);
});
で生成できるのだろうか。
明日はこれを調査するか
というかvcontainerをつかえばmessagepipeつかわんでもええのか?
でも使わなかった場合コンテナの煩雑度が上昇する気がする
vcontainerなんもわからん
動的に生成したGameObjectにはどうやってInjectすればいいんだ
インスタンスを注入されたFactoryから注入されたインスタンスをわたしてGameObjectを生成するのか?
できた。マジで感謝
↓参考
container
public class GameLifetimeScope : LifetimeScope
{
// MoveCubeのPrefabへの参照
protected override void Configure(IContainerBuilder builder)
{
// MessagePipeの設定
var options = builder.RegisterMessagePipe();
// InputParamsを伝達できるように設定する
builder.RegisterMessageBroker<Vector2>(options);
// InputEventProviderを起動
builder.RegisterComponentInHierarchy<PlayerInput>();
builder.RegisterEntryPoint<PlayerInputProvider>(Lifetime.Scoped);
builder.RegisterComponentInHierarchy<InstantiateTest>();
}
}
prefab生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using VContainer;
using VContainer.Unity;
public class InstantiateTest : MonoBehaviour
{
// MoveCubeのPrefabへの参照
[SerializeField] private GameObject _playerInputReciever;
private IObjectResolver _container;
[Inject]
public void InjectContainer(IObjectResolver container) {
this._container = container;
}
void Update()
{
var key = Input.GetKeyDown(KeyCode.Space);
if (key) {
_container.Instantiate(_playerInputReciever);
}
}
}
どういう関係になるかを図でまとめる
この機能が依存しているパッケージも導入したいため
を参考にコードを書くこのスクラップは5ヶ月前にクローズされました