※2017/10/26 修正: @kajimura さんのコメントをもとにイベントリスナ側のコードを修正しました、ご指摘ありがとうございました!
サンプルについて(2017/11/3追記)
この記事にて作ったScriptを、サンプル的なそれと一緒にGitHubに上げてみました.
前置き
イベント駆動なプログラミングに浸かってると、Unityでもキー入力等をイベント駆動ぽく処理とかしてみたくなってくることもあります。1
ですが、Unityでは各種のキー入力をイベント駆動的に処理する仕組みが、Unity側で用意されていない...ように見受けられます。2
そこで、Unityの諸々を利用して、キー入力をイベント駆動ぽく処理する試みをしてみます。
以下、Unity 5.6f3で実装しました。
参考文献とか
以下のWebページを参考にしました。
SendMessageに変わる新しいメッセージシステム、ExecuteEvents.Execute - テラシュールブログ
Unity - スクリプトリファレンス: EventSystems.ExecuteEvents.Execute
実装する
インタフェース構築
通知用のInterfaceを組み立てます。
キーの押下と押上のときに、UnityEngine.EvemtSystems.ExecuteEvents.Execute<>()
でイベントを発行する想定で組んでみます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// キー押下時イベントのInterface
/// </summary>
public interface IKeyDownEvent : IEventSystemHandler {
void OnKeyDown(List<KeyCode> downKeyList);
}
/// <summary>
/// キー押上時イベントのInterface
/// </summary>
public interface IKeyUpEvent : IEventSystemHandler {
void OnKeyUp(List<KeyCode> upKeyList);
}
UnityEngine.EvemtSystems.ExecuteEvents.Execute<>()
がイベントを発火できるように、IEventSystemHandler
を継承するのがミソです。
発行役の構築
イベントの発行役を実装します。
ComponentとしてGameObjectにAttachする想定です。
仕組みは、
- 毎フレーム、
Keycode
列挙型で登録されているキー全部に押下と押上の瞬間をチェック - 1つでもチェックに引っ掛かったら、イベントを
UnityEngine.EvemtSystems.ExecuteEvents.Execute<>()
経由でイベント発行
といった感じに。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.EventSystems;
/// <summary>
/// 各種のキー入力イベントの発行役
/// </summary>
public class KeyEventDispatcher : MonoBehaviour {
/// <summary>
/// 処理負荷軽減のため、KeyCodeの全ての値を保持する配列
/// </summary>
static Array KeyCodeList;
// Use this for initialization
void Start () {
//全てのKeyCode列挙型の値を控える
KeyCodeList = Enum.GetValues(typeof(KeyCode));
}
// Update is called once per frame
void Update () {
var tmpUpList = new List<KeyCode>();
foreach (KeyCode code in KeyCodeList) {//押上キーがあるかチェック
if (Input.GetKeyUp(code)) {
tmpUpList.Add(code);
}
}
if (tmpUpList.Count != 0) {//1つでも押上キーがあれば
ExecuteEvents.Execute<IKeyUpEvent>(this.gameObject,
null,
(handler, eventData) => handler.OnKeyUp(tmpUpList) //押上キー全てを込めてリスナを叩く
);
}
var tmpDownList = new List<KeyCode>();
foreach (KeyCode code in KeyCodeList) {//押下キーがあるかチェック
if (Input.GetKeyDown(code)) {
tmpDownList.Add(code);
}
}
if (tmpDownList.Count != 0) {//1つでも押下キーがあれば
ExecuteEvents.Execute<IKeyDownEvent>(this.gameObject,
null,
(handler, eventData) => handler.OnKeyDown(tmpDownList) //押下キー全てを込めてリスナを叩く
);
}
}
}
この例では、発行役と同じGameObjectにAttachされているComponentに対して、イベントを発行しています。
リスナ側実装
イベントを受け取るComponentを作ります。
先の作業で作ったInterfaceを実装し、通知役と同じGameObjectにAttachします。
すると、そのComponentはキー入力イベントを受け取ることができるようになります。
以下は、イベント発行の際に送られてきたKeyCode
をそのままデバッグコンソールに出力するだけの実装です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KeyEventListener : MonoBehaviour, IKeyUpEvent, IKeyDownEvent {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnKeyUp(List<KeyCode> upKeyList) {
foreach (KeyCode code in upKeyList) {
Debug.Log("Key up:" + code.ToString());
}
}
public void OnKeyDown(List<KeyCode> downKeyList) {
foreach (KeyCode code in downKeyList) {
Debug.Log("Key down:" + code.ToString());
}
}
}
どのキーを受け付けるか、などは全てリスナでよしなに処理し、発行役はどんなキーでもイベントを発行するような実装です。
#あとがき
PCで遊ぶ音楽ゲームとか、タイピングゲームとかに活用できそうかな...と考えています。