UE4 で Yaw 軸に回転し続ける Actor を作る
上記の画像のように Yaw (Z) 軸でその場でぐるぐる回り続けるだけ
BluePrint
Component としては Cube があるだけ。
なんとか収まったので 1 枚で。
内容
Delta Seconds 毎に Yaw 軸 180° の Rotator
を作成し、AddRelativeRotator
で Cube
に対して相対的に追加。この時点で期待通りにくるくる回る。
その後、その時点の Yaw 値を World 座標に設定して終わりです。実は World 座標に設定し直す理由はよく分かっていません。。
Game Builder : July update: Share actors, custom terrain textures, and more!
Actor の共有
‘CREATE` からできるようになってる。
まだ共有されているものは少ないが、簡単にインポートできるし、LOGIC を見ることもできる。
Terrain Texture のインポート
TERRAIN
の Texture を選ぶ場所にフォルダ icon が追加されており、そこからローカルにある png を追加できる。
最初に Upload する時に Steam の規約に再度同意する必要があるがそれだけ。
インポートした Texture は先頭に追加される。アップロードが終わるまではステータスバーのようなものが出るので、それが消えるまで待つ。 あとは普通に使える。
Terrain の Rotate
今までそういえばできなかったのかという。
ちょっと分かりにくいが、ROTATE SHAPE (R)
というボタンが TERRAIN に追加されている。これを押すよりは R
のショートカットキーを使うほうが早い。
Terrain の CREATE 中に Ctrl を押すと DELETE に
これ、前は CREATE / DELETE でそれぞれ Shift を押すと切り替えになってた気がするんだけど、その挙動は今はないので記憶が定かではない。まあ、こっちのが便利か。
Change Camera CARD
試すと確かにカメラを切り替えられる、そして、そのカメラが他の Actor の子であっても自 Actor の子に移動している。と書きつつ、まあ普通は切り替え対象のカメラも対象 Actor がまとめて持っているんだろうと思う。
Cameras without camera cards no longer reparent (and thus no longer follow) the target actor, for flexibility.
これもリリースノートには入っているが、確かに camera cards を持たないカメラの場合は CAMERA Actor が親を移動することはなかった。
CAMERA PANEL 自体にも Target Actor を指定できるので、Actor の親子関係も含めて、カメラを使うようになったら整理してみたい。
offstage と onstage 関連
・IF-THEN panel can now be made to work offstage.
・Added a Go Onstage card to allow an actor to return to the stage upon receiving an event, for instance.
実際試していないが、これも今までできなかったんだなぁという感じ。
その他
共有カードのインポートにアップされてるものが増えてるよっていう宣伝(というか、このリリースノートが他のもまとめてるのか)が入っていた。確かに覗いてみるとちょっと増えてた。
Game Builder を起動したときに出てくる Workshop にアップされてる一覧も結構面白そうなのが増えてる。ボンバーマン作ってみたいなぁと思ってたら、すでに作ってる人がいた。
Actor の属性みたいなものを初期状態で設定する
「初期状態」というのを表現するのに以下の選択肢がある。
- GAME START Panel で trigger する Action CARD を作る
onInit
を持つ CARD を作る
初期状態の設定
CARD の設定として簡単に付けられるように props
を利用しておくと便利。
以下では onInit
を使っているが、 1 の GAME START panel で使う場合には onInit
を onAction
に変えるだけである。
export const PROPS = [ propActor('target', '') ]; export function onInit() { mem.target= props.target setVar('target', target); } export function onResetGame() { delete mem.target; }
mem
と setVar
http://gamebuilder.area120.com/setVar.html
Sets a custom variable on the current actor. This is just a key/value pair that gets attached to the actor and can be read by other actors with getVar. All variables get deleted on game reset.
http://gamebuilder.area120.com/mem.html
Memory object for the current actor. This is an object behaviors can freely use to store their state. The contents of this object will depend on what the actor's behaviors have stored it in.
You can use this to store anything that the actor needs to remember from one tick to the next, such as the state it is in, any counters, etc.
Remember to reset any data to its default state on onResetGame.
Note: an actor's memory is private to the actor, so other actors can't access it. If you want to publish a value that other actors can read, you can set it with setAttrib.
card
もあるが、 card
は CARD 内でしか読めないので、 Actor に対する属性として利用するのは難しい。
mem
の最後の説明にあるが、「値を他の Actor からも読めるようにするなら setVar
を使う」ということになる。
log(JSON.stringify(mem));
で見てみるとわかるが、 setVar
で設定した値は mem
の中に __variables
として格納されている。
onInit
http://gamebuilder.area120.com/onInit.html
If you implement this function, it will get called when the game is reset, and also when the card has just been added to an actor. You can use this function to initialize your card memory variables.
Game Builder : July 11th, 2019 Release notes
雑多
- Alt+Click a panel's header to duplicate it
- Press CTRL+L to toggle Actor List
どちらも地味に便利。
共有カードのインポート
これは結構大きそう。これまでも Steam Community の方で共有されているものはあったけど、それが import をサクッとできるのはありがたい。
Logic の Library から import/export を選べるようになっている。
上記の一覧に出ている Debug Screen は試してみたが、以下のガイドにあるものが import できるようになったものだと思う。
UI Features
自分が UI を作ることが今のところないので、あまり注目できないが機能追加されているらしい。これを機に試してみたい。
Dead Cells
- 環境: Nintendo Switch
- プレイした時間: 30 時間ぐらい
- 進行状況: 王の手は倒したが、それより先にはいけていない
感想
友達に薦められてやってみた。
難易度の高いアクションゲームに苦手意識があったが、2D ということとローグライクゲームというジャンルをやったことが無かったので試してみようという感じ。(後、仮に全然合わなくても値段が手頃なので許容できそう)
やってみては、、とにかく最初はビビりまくっていて、すごい時間をかけて最初の牢獄をクリアした。ただ、結局次のステージであっさり死んでしまった。
何回か繰り返していると、ステージの進行も少しずつするのと、牢獄の最初の部分が変化していったりで「おー、なんか面白い」と少しずつなっていった。
何時間かやってると、死ぬことは慣れてきて、一つのステージを早くクリアすることや、ノーダメージで n 体倒すというのにチャレンジできるようになってきた。
どこかで、ある程度操作も手慣れてきたのか、時計塔から王の手の部分まで一気に進んでしまい、おおおおおっとなったがあっさり死亡。
その後も、何回かチャレンジしていたが、全然勝てそうになく、もっと武器とかアンロックしないといかんなぁと思っていたら、偶然レジェンダリー装備が 2 つ手に入った状態で王の手まで行くことができ、気がつけば倒していた。
ただ、その後はやはりなかなか王の手には勝てず、現在に至る。。
薦めてくれた友達と話していたが、このゲームのストーリーは僕は結構気になったりしたのだけど、友達は途中から気にならなくなったみたいな感じで、ゲームに対するスタンスも変化していて面白いなと思った。
この記事が、考察系では特に面白かった。 jp.ign.com
Collision した Actor からデータをもらう
Actor A と Actor B で Collision した場合に、B から A にデータを受け渡すというケースです。以下の 2 つを例にしてみます。
- Actor B から A に
send
する- A が B に Collision した時に、 B 側から A にメッセージを送る形でデータを渡します
- Actor B の custom variable から取得する
- A が B に Collision した時に、A が B を参照し、custom variable の値を読み出す形でデータを受け取る
Actor B から A に send
する
Actor B
Damage とかとだいたい同じです。
Send Any Message の CODE
export const PROPS = [ propCardTargetActor("Target", { label: "Send to:" }) ]; /** * @param {GActionMessage} actionMessage */ export function onAction(actionMessage) { const target = getCardTargetActor("Target", actionMessage); if (!target) { return; } send(target, "ReceiveAnyMessage", "送りたいデータ"); }
Actor A
単に send あれたイベントを Receive するだけだとデータが取れないので、以下のようなイベントを作成します。
まず、Receive Any Message CARD です。
もともとの ReceiveMessage CARD の CODE では card.triggeredEvent
は {}
が入っていて、後続の Action に値を渡せません。よって、以下のように onReceiveAnyMessage
で渡ってきた値をそのまま渡しています。
/** * @return {GEvent|undefined} The event, if one occurred. */ export function onCheck() { if (card.triggeredEvent !== undefined) { const rv = card.triggeredEvent; delete card.triggeredEvent; return rv; } else { return undefined; } } export function onReceiveAnyMessage(properties) { card.triggeredEvent = properties; } export function onResetGame() { delete card.triggeredEvent; }
次に Say Message CARD です。Say Something CARD とほぼ同じなので、受け取ったデータをどう取り出すかだけ。
export function onAction(actionMessage) { const receivedData = actionMessage.event; }
Actor B の custom variable から取得する
Actor A
custom variable は他の actor からも読めるので、それを利用します。
逆にどの actor からも読めてしまいます
一つ目の例と同じように Collision Event と組み合わせた場合の Action 側の CARD の CODE です。
export const PROPS = [ propCardTargetActor("Target", { label: "Receive from:" }) ]; /** * @param {GActionMessage} actionMessage */ export function onAction(actionMessage) { const target = getCardTargetActor("Target", actionMessage); if (!target) { return; } const receivedData = getVar('受け取りたいデータの key', target) }
Actor B
渡すデータをあらかじめ設定しておきたいとします。
GAME START panel で設定するような Action の CARD を用意します。
export function onAction() { setVar('渡したいデータの key', '渡したいデータ'); }
setVar
に関しては object も渡せます。
http://gamebuilder.area120.com/setVar.html
props を使って、CARD の properties として setVar
に渡す値を設定できるようにしてもよさそうです。
Game Builder で CARD の CODE を編集するときのログ出力
Debug Action CARD
とりあえず CARD だけで LOGIC を作っているときにログを出したいときに使う CARD です。 これは Actor が Flush もしますがログメッセージも出します。
実装はとても単純な Action の実装になっています。 ログも二つ出しているだけです。
export function onResetGame() { } /** * @param {GActionMessage} actionMessage */ export function onAction(actionMessage) { log(`Action message received`); log(actionMessage); }
ログの確認
~
を押すことで有効になります。赤枠で囲っている部分が Debug Action の表示結果です。
Collision Event に対する Player の Action の場合です。
actionMessage の表示
場合によるとは思いますが、上記の場合は actionMessage のログが空で表示されています。 log
はオブジェクトをプリントできないらしいので、以下のように Debug Action を編集します。
export function onAction(actionMessage) { log(`Action message received`); - log(actionMessage); + log(JSON.stringify(actionMessage)); }
こうすることで以下のような出力を得ることができます。
CARD の CODE でのログ出力
はい、Debug Action と同じようにできます。 log
を使い確認するだけです。また、オブジェクトの場合は (現状) JSON.stringify
することでプリントできます。
ログ Viewer で空の表示なったからと焦らず JSON.stringify
してみましょう。
Appendix
Game Builder のログ
C:\Users\<user>\AppData\LocalLow\Area 120\Game Builder\output_log.txt
にあります。