こんにちは、開発担当の杉原です。
先日、近くの神社のお祭りに行ってきました。
といいますか、町内会の代表として手伝いをしてきた、というのが正解ですね。
正直仕事だけでも精一杯なのに、土日までこうして駆り出されるのはたまったもんじゃないんですけども、神事に出たり、茅の輪をくぐったりと、いろいろやれることはあるんで、まあ前向きに考えればいい経験にはなりますね。
ちなみに、茅の輪はお金さえ払えば誰でもくぐれるんですけども、これをやると半年は健康に過ごせるらしいです(冷房で体調崩す前にやりたかった……)。
さて本題。
今回も引き続きMessageListenerについてご説明させていただきます。
前回はMessageListenerへの登録について説明いたしました。
上図のような状況で、Visual AのMessageListenersにVisual Bが自身を登録するという内容でしたね。
これでVisual Aはイベントが発生するたびにVisual Bに対して通知を行うようになります。
となると、次はVisual Bが通知を受けた時に処理をしたくなりますね。
Visual Bで通知を受けたことによるイベント処理を行う方法は2つあります。
まずはそのうちの1つ、OnMessageイベントによる処理をご紹介します。
function Box1_OnMessage( sender : Demo3D.Visuals.BoxVisual, message : Demo3D.Visuals.ScriptingObject ) { print(message); }
[Auto] void OnMessage(BoxVisual sender, ScriptingObject message) { print(message.ToString()); }
このイベントではイベントの発生元を表す引数senderのほかに、もう一つの引数messageを受け取ります。
上記サンプルコードでは、Demo3Dのログウィンドウにこのmessageを出力しています。
このコードを実装してResetボタンを押すと下図のようにログ出力されます。
(※実際には1行だけ出力される想定です。上図はJScriptとC#を同じモデル上で検証しているのでログが2つ出ています)
そして試しにMessageの送信元であるVisual Aの上にカーソルを当ててみたり、ドラッグして移動してみたりすると、どんどんメッセージログが出力されるのが確認できるかと思います。
しかし、「x items」(xは数字)と出力されるだけで、どのようなメッセージが送られているのかがわかりません。
そこで、以下のようなコードに変更してみます。
function Box1_OnMessage( sender : Demo3D.Visuals.BoxVisual, message : Demo3D.Visuals.ScriptingObject ) { for(var obj in message) { print(obj); } }
[Auto] void OnMessage(BoxVisual sender, ScriptingObject message) { foreach(object obj in message) { print(obj.ToString()); } }
このコードでは、配列状になっている引数messageから1つずつ要素を取り出してその内容をログ出力します。
そしてリセットし、Visual Aをいろいろ動かしてみます。すると下図のようにログ出力されます。
イベントによって要素数はまちまちですが、ざっと見たところMessageSender、MessageTypeの2つの値が出現するのが確認できるかと思います。
それぞれの値をよく見てみると、MessageSenderにはメッセージの送信元(Visual A)、MessageTypeにはイベント名が格納されているのがわかります。
さて、このようにMessageListenerへの登録を行うことで、Visual Aからイベント発生の通知が届くようにはなるのですが、ログを見ての通り、OnMouseOver(マウスカーソルがVisual上に乗った)イベントなんかも通知してきます。
通知を受け取るVisual Bとしては、Visual Aのイベント発生を知りたいとは言え、そのすべてが知りたいわけではないというケースがほとんどのハズです。
そこで今回は、ドラッグアンドドロップのドロップ時(OnDragEnd)だけメッセージログを出力するように変更してみます。
function Box1_OnMessage( sender : Demo3D.Visuals.BoxVisual, message : Demo3D.Visuals.ScriptingObject ) { if (message["MessageType"] == "OnDragEnd") { print("DragEnd"); } // 以下でもOK /* if (message.MessageType == "OnDragEnd") { print("DragEnd"); } */ }
[Auto] void OnMessage(BoxVisual sender, ScriptingObject message) { if (message["MessageType"].Equals("OnDragEnd")) { print("DragEnd"); } // 以下でも正しく評価される /* if (message["MessageType"].ToString() == "OnDragEnd") { print("DragEnd"); } */ }
ここで、言語ごとにちょっとしたポイントがあります。
まずはJScriptの場合。上記サンプルコードにコメントでも書きましたが、JScriptはわりとゆるーい感じなので、配列要素MessageTypeを取得するときに、配列らしくmessage["MessageType"]と取得する以外に、あたかもプロパティかのようにmessage.MessageTypeと書いても値が正しく取得できます。
一方でC#の場合、MessageTypeはString型として取得できるのですが、message["MessageType"] == "OnDragEnd"の書き方だと正しく評価ができません。
そのため、サンプルコードのようにEqualsメソッドやToStringメソッドを使って評価する必要があります。
これで任意のイベントを検知することができるようになりましたが、冒頭の方でも述べたように、検知にはもう1つの方法がありますので、こちらも紹介します。
[Message("OnDragStart")] function OnDragStart(sender : Visual, messageSender : Visual) { print("OnDragStart"); }
[Message("OnDragStart")] void OnDragStart(Visual sender, Visual messageSender) { print("DragStart"); }
このように、関数の属性としてMessage属性を付与することで、Message属性の引数に指定したイベント発生の通知を受けたときに、該当の関数が実行されるという仕組みになっています。
OnMessageイベントで分岐してもいいのですが、こちらのほうがシンプルでいいと個人的には思っています。
ちなみに、QuickStartカタログにありますPalletizerはOnMessageイベントを使った方法、VehicleManagerはMessage属性を使った方法でイベントをキャッチしています。
MessageListenerの使い方については、これらが参考になるかと思いますので、気になる方はコードを見てみるといいかと思います。
MessageListenerの使い方については以上でしょうか?
なんかもうちょっとあるような気もしつつも、ちょっと来週から考えていることもあるのでここで区切りたいと思います。
次回からはまたしばらくHDSはお休みしまして、夏休み特別企画「自由研究①:概要とベース作成」をお送りできればと思います。
正直やり切れるかとても心配な行き当たりばったり企画ですが、頑張って進めていきたいと思ってます。
更新は8/2(木)です。お楽しみに。
コメントをお書きください