見出し画像

3.タイマーを作ろう, 変数の値を変化させる, トリガーを組み合わせる, 変数宣言の制約・スコープ, $.state変数

前回Cluster Javascriptで変数の値を変化させるには踏み込んだ話が必要.
そんな大層なことを言ったが, 実は第1回で学んだ簡単な文法だけでも一応実現はできる.

タイマーを作ろう

$.onUpdate(deltaTime => {
  let time = $.getStateCompat("this", 'time', 'float')
  time += deltaTime
  $.setStateCompat("this", "time", time);
});

getStateCompatでアイテム変数'this'の'time'(種類は'float')を読み込み,
毎フレームdeltaTimeを足して,
setStateCompatでアイテム変数'this'の'time'に出力する.
(deltaTimeはonUpdate関数の実行間隔である. 特に宣言とか気にする必要はない)

関数内の変数をアイテム変数と紐付ければ, 何の問題もなく変数を操作できる.

変数宣言の制約

宣言ループ

だが, これをスクリプト内の変数だけでやろうすると, 制約がある.
第1回の記事にonUpdateトリガー内で変数aを宣言し, 自身に1を加える処理を載せた.

これの結果は変数aに延々1が加えられ続ける…ではなく,
変数aを初期値1で宣言し1を加え2となることを繰り返し続ける, である.

このようにonUpdateトリガー内で変数の宣言と処理をセットで実行している以上 
スクリプト変数の値を変化させることはできない.

変数の宣言が適用される範囲 (スコープ)

また, 第2回で様々なトリガーの使い方を紹介したが, それでトリガー同士を組み合わせたギミックを作ってみたくなった方もいるだろう.
しかしここにも注意が必要である. 演算に使う変数は最初にletで宣言をする必要があることは第1回で述べたとおりだが, この宣言には適用範囲がある.

宣言を行った括弧{}の中でしかその変数は使えないのだ.

簡単な例を紹介しよう.

$.onUpdate(deltaTime => {
  let a = 1
});

$.onInteract(() => {
  $.setStateCompat("this", "a", a);
});

onUpdate関数内で宣言した変数aをonInteract関数で出力しようとしている.
非常に単純なロジックだが, 結果はこちらである.

出力されない

コンソールを見ると, 何かエラーを吐いている.
拡大図がこちら. 

参照エラー : aは<[6,32..33]>で定義されていません

onUpdate関数での変数aの宣言が適用されていないことが分かる.

$.state 変数を使う

こうした変数宣言の制約も, 最初のタイマーのようにアイテム変数と結びつけてうまく読み込み・出力すれば回避できそうではあるが, 面倒である. 

そしてCluster Javascriptにはこの制約を回避するための仕組みが用意されている.
$.state 変数だ.

$.onUpdate(deltaTime => {
  if (!$.state.initialized) {
    $.state.initialized = true;
    $.state.a = 1;
  } 
});

$.onInteract(() => {
  $.setStateCompat("this", "a", $.state.a);
});

onUpdateトリガーの中に何やらif文があるが,
これは$.state 変数を使う上での初期設定である.

$.state.initializedの中身が空ならば、$.state.aに1を代入して$.state.initializedをtrueにするという処理で, 2回目以降は$.state.initializedにtrueが入っているので何も起こらない.

$.state 変数が宣言を必要としないという特性を持つためにできる処理で,
普通の変数で同じことをやろうとしても,
If(a == null)の段階で a が宣言されていないというエラーが出て,
if文の中でaを宣言しても括弧{}の中なので外に適用されない.

宣言を必要としないためスコープの制限もなく,
初期設定の後は好きに使えるわけだ.

できた

冒頭でアイテム変数と結びつけることで実現したタイマーも,
$.state 変数を使えばいちいち読み込み・出力をせずに実現できる.

$.onUpdate(deltaTime => {
  if (!$.state.initialized) {
    $.state.initialized = true; 
    $.state.time = 0;
  }
  $.state.time += deltaTime
  $.setStateCompat("this", "time", $.state.time);
});
わーい

とりあえず,

  • $.state 変数は以下の形で初期設定する.

  if (!$.state.initialized) {
    $.state.initialized = true; 
    $.state.変数名 = 初期値;
  }
  • $.state 変数は宣言ループの制限を受けず, 値の変更を記憶できる.

  • $.state 変数はスコープの制限を受けず, トリガー間で共用できる. 

を頭に入れればいいと思う.

次回へ

さて, ここまでCluster Javascriptの使い方を理解すれば,
トリガーやロジックを組み合わせた様々なギミックを作ることができる.
クールタイム付きの銃だって実装できる.

次回は, ワールドクラフトでの動作確認や, ベクトルの扱い,
またスクリプトに伴って加わった位置・回転を操作するギミックについて説明する.
こちらだ.

補足(読み飛ばし可)

今回は変数宣言が持つ制約について説明した.
スコープの制限を受け, 宣言とセットでしか使えないから値の変更, 他トリガーとの共用ができないというものだった.

ところで変数宣言が,
繰り返されるトリガーの中であるために値の変更ができない,
括弧{}の中でしか適用されず共用が効かない, 
というならば, 

トリガーの外で宣言すればいいのでは?

と思う方もいるかもしれない.

こんな感じ

ん? こっちの方が分かりやすい?

この変数の外部宣言(たぶん正式名称はグローバル宣言), 
一般のJavascriptでは普通に行われることらしい.

$.state 変数を使うより見た目的にもロジック的にもシンプルで,
変数に$.state 変数とほとんど同等の機能を持たせて利用できる.
(宣言ループもなければ括弧{}の中でもない)
そしてテストプレイしてみると大体問題なく動く.

罠である

グローバルでの変数宣言は定期的に実行される仕様らしい.
つまり、変数をグローバルで宣言すると定期的に初期値に戻される.

ちなみに、定数についてはグローバルで宣言しても問題ないので,
公式のスクリプトでも普通に行われている.

この記事が気に入ったらサポートをしてみませんか?