2014年12月12日金曜日

UMGで電卓を作る

この記事は,Unreal Engine 4 Advent Calendar 2014の12日目の記事です.
昨日の記事は,@aizen76さんのUE4で使えるプラグインいろいろでした.

先週の記事では,UMGのレイアウト用ウィジェットを紹介しました.
今日は実践編として,UMGを使って電卓を作ってみます.なお,UE4のバージョンは4.6となっています.

まず,適当なプロジェクトを作って,UMGのブループリントを追加します.コンテンツブラウザ上で右クリックメニューを開き,ユーザーインターフェースからWidgetブループリントを選びます.名前はとりあえずCalcとしておきましょう.

いったん解説に使う予定の画像だけ掲載します.この画像を見るだけでも何となく電卓は作れるかもしれません.
間に合えば今晩に解説を載せます.

間に合いませんでしたが,解説はちゃんと書きます.細かいことは画像の中に書いてたりするので,画像の方を参照してください.なお,ブループリントの基本は理解しているものとして話を進めます.

コンテンツブラウザを右クリックし,ユーザーインターフェースからWidgetブループリントを追加します.


メニューのブループリントの横の三角からレベルブループリントを開き,ゲーム開始時にウィジェットが表示されるようにします.


レベルブループリントに書き込む内容は,次の図のようにCreate Widgetノードとadd To Viewportノードです.


先ほど追加したWidgetブループリントをダブルクリックし,まずはデザインを決定します.電卓はマス目状にボタンを配置すればできるので,Grid Panelを利用します.ルートのCanvas Panelを削除して,Grid Panelを配置します.


Grid Panelを配置したら,詳細から4列(Column)と5行(Row)を追加します.追加した行や列の重みは1にしておいて,均等な幅になるようにします.

列と行の追加ができたら,ボタン(共通の中のButton)を配置していきます.ボタンの中にはテキストを追加して,それぞれの数字や演算子(+,-,/,*,=)を入力します.最後に,結果表示用のテキストを一番上に配置します.



デザインが終わったら,グラフエディタに移り,変数タイプIntegerの変数resultを追加します.この変数には,計算結果を格納します.


変数を追加したらコンパイルしておきましょう.


コンパイルが終わったらデザイナに戻り,結果表示用のテキストを選びます.テキストの表示内容として,先ほど追加したresultを読み取るように,プロパティバインドを作成します.


バインドを作成するとグラフエディタに移るので,単純にマイブループリントからresultを引っ張ってきて,取得するようにし,resultをReturn Valueにつなぎます.


この時点で実行してみると,見た目はそれっぽくなります.


では,まずは数を入力できるようにします.数字のボタンを選択し,詳細の下の方からAdd OnClickedを押しましょう.


グラフエディタに移り,OnClicked(ボタン名)が表示されていると思います.電卓の挙動を見てみると,数字のボタンを押す度に右側に数字が追加されていきます.これは,resultに入っている値を10倍して押したボタンに対応する数を足した値をresultに設定することと同じです.これを書いたのが次のブループリントになります.


まずは0のボタンで作ってみました.次は1のボタンを押した場合を作ってみましょう.


上の図の赤で囲った部分以外違いがありません.同じような処理を2度作った場合,関数やマクロにすることを検討しましょう.今回は,関数化しておきます.マイブループリントから関数追加を選び,Add Valueという名前を付けます.


関数を作った場合,何か入力が必要な場合は自分で追加する必要があります.この場合,どのボタンを押したのか,という情報が必要なので,外からvalueという名前で入力できるようにします.後は,最初に0のボタンを押した場合に作ったブループリントと同じになります.

これを全ての数字のボタンに適用すると,次のようなブループリントになります.


この時点で画面にウィジェットを表示させて数字のボタンを押すと,電卓のように数字が表示されます.ただし,扱える数値の上限を超えている場合の処理が無いので,途中から変な数値に変わっていきます.

さて,ここからが本番です.電卓なので,ちゃんと足し算やかけ算ができるようにしていきます.電卓の挙動を見てみると,演算子(+や-など)を押した後,次の数字を押すまでは前に入力した値が表示され,数字のボタンを押した瞬間に新しい値に表示が変わります.つまり,演算子ボタンを押した後に数字のボタンを押したかどうかを覚えておく必要があります.また,ある演算子ボタンを押した後に数値を入力し,再度何かの演算子ボタンを押すと,計算が実行されます.そのため,直前に押されている演算子ボタンが何かといった情報が必要です.

そこで,次のような変数を追加します.

mode (Integer)
直前に押した演算子ボタンの種類を覚えておくための場所です.
stack (Integer)
途中までの計算結果を保存しておく場所です.
stackUsed (Boolean)
stackに何か格納しているかを表します.何か値が入っているならtrue,入っていなければfalseになります.
digitPushed (Boolean)
数字のボタンを押したかどうかを表します.何かしらのイベント後に一度でも数字のボタンを押していればtrueになります.


追加した変数を組み込んでいきます.まず,Add Value関数の中で,数字を押した後には必ずdigitPushedがtrueになるように,digitPushedの設定ノードを組み込みます.


結果を表示するためのテキストのプロパティバインドに使っている関数は,次のように変更します.
まず,resultだけでなくstackの結果も取得し,Select Intノードを使ってどちらかを選んだ上でReturn Valueにつなぐようにします.


選択の条件は,スタックに何か入っていて,数字ボタンを押していない場合にstackを選ぶことなので,そのようにstackUsedとdigitPushedを取得して,上記を参考にノードをつないでいきます.NOTは否定用のノードで,digitPushedに対してNOTを使うと,ボタンを押していない時にtrueになり,ボタンを押した後はfalseになります.

ANDノードは,入力の両方がtrueの場合にtrue,それ以外の場合はfalseになるので,stackUsedかつdigitPushedではない,という条件ができあがります.あとは,ANDの結果をSelect Intノードにつなぎます.

ここまできたらあと一息です.最後に,modeに合わせてstackの内容などを更新するUpdate By Mode関数を追加します.

まず,modeの値によって条件分岐をするため,モードがどの数値かを判定する処理を作ります.
modeを取得し,Equalノードで値を比較します.


  • 0は何も押していない状態
  • 1は+を押した状態
  • 2は-を押した状態
  • 3は*を押した状態
  • 4は/を押した状態

まず,何も押していない状態の場合,resultの値をstackに退避させます.



次は,+が押されている場合と-が押されている場合です.それぞれ,stackの値とresultの値を+ノードで足したり,-ノードで引いたりして,結果をstackに戻します.-の場合,stack - resultとresult - stackは結果が違うので,ノードをつなぐ順番に注意しましょう.


最後は,* (かけ算)と/(割り算)です.こちらも処理は+や/と変わりませんが,割り算は順序に気をつけましょう.


最後に,それぞれの計算結果をstackに設定した後に,stackUsedをtrueにし,resultを0に再設定し,digitPushedをfalseにする共通の終了処理につなぎます.


デザイナでそれぞれの演算子ボタンから,Add OnClickedでイベントを追加し,以下の図のようにUpdate By Modeを呼び出した後にmodeを適宜設定するようにノードをつなぎます.


最後に,=を押した場合,stackに何かしらデータが入っていれば,Update By Modeを呼び出して,計算などを終わらせておき,stackの値をresultに戻し,stack,stackUsed,modeをリセットするようにします.

これで,電卓はいったん完成です.実行して,簡単な計算をしてみましょう.

正直なところ,電卓なんて簡単だろう,と思っていたのですが,結構色々と考えることがあって,ちゃんと作るとなると結構面倒でした.UMGの実践例としてはあまり良くなかったかな,という気もするのですが,無駄にハイスペックを要求する単なる電卓,という誘惑には勝てませんでした.

ここで作った電卓はあくまでも簡易的なものなので,もっとUMGを弄って色々な機能を追加してみてください.


明日というか今日は,@monsho1977さんで,何かしらネタになるマテリアルを作るそうです.
楽しみですね.

0 件のコメント:

コメントを投稿