弾幕風製作の基本その3 関数、変数のスコープと変数の記憶寿命 今回は関数、変数のスコープと変数の記憶寿命について学びましょう。 この2つはインクルードを利用した時にとても重要になります。 @関数のスコープ 「関数のスコープ」とはある関数が働く範囲の事です。 簡単にいえば、いつまで関数が機能しているかという事です。 例えばスクリプトAでインクルードしたヘッダファイルBがあります。 そのヘッダファイルBの中でもあるヘッダファイルCをインクルードしているとします。 (ヘッダファイルの中でインクルードを行うときは、#include_functionは最上段に書いておきましょう) ヘッダファイルBでtask waitを宣言し、スクリプトAでtask waitを宣言してみましょう。 結果はもちろんエラーが起こります。 エラー文は「同じスコープ内で複数のルーチンが選択されています」と出ます。 何故エラーが起こるかというと、ヘッダファイルBで既にtask waitを宣言し、 起動しているのに再びtask waitを起動しようとしたからです。 これを詳しく言うと、ヘッダファイルBのwaitのスコープ内で同名のwaitを起動しようとしたからです。 勿論中身が違っていても名前が同じならエラーになりますので、 関数は一つ一つ把握できるように名付けをしていきましょう。 またヘッダファイルCでwaitを宣言すると、ヘッダファイルB内、スクリプトA内でもwaitを使えます。 最後にインクルードしたファイル内で宣言した関数はそれ以前の全てのファイルをスコープとします。 最重要事項↓ ・「同じスコープ内で複数のルーチンが選択されています」と出たら、関数、関数名が重複していないか確認する。 ・関数は一つ一つ把握できるように名付けをする A変数のスコープ 「変数のスコープ」とはある変数が存在し続ける範囲の事です。 関数のスコープと定義的にはほぼ同じといえます。 あるスクリプト内で宣言した関数内でlet a=0;と宣言し、 スクリプトでもlet a=0;と宣言したとします。 この時、2つの変数aは全く別物です。 関数内で宣言した変数を「ローカル変数」、スクリプトで宣言した変数を「グローバル変数」といいます。 ここで覚えておきたいのは、 「グローバル変数はfunction、subをスコープに含めるが、taskはスコープに含めない」ということです。 (ただしグローバル変数とfunction、sub内で宣言したローカル変数の名前が同じだと、ローカル変数が優先されます。 C++では使い分ける事が出来ますが、弾幕風ではその機能はありません。) また「ローカル変数はfunction、sub、taskのどれで宣言しても元のスクリプトをスコープに指定しません」。 もしtask内のある要素に、グローバル変数を充てたければ、 let a=2;//グローバル変数aを宣言 task TShot(a){//この行のaはグローバル変数 let b=0;//ローカル変数bを宣言 b=a;//この行のaはグローバル変数aの値と同値のローカル変数 } のようにして、関数の実引数にグローバル変数を設定してローカル変数に変換します。 インクルードしたヘッダファイル内で宣言した変数はヘッダファイルで見るとグローバル変数扱いですが、 スクリプト全体でみるとローカル変数なので気をつけましょう。 最重要事項↓ ・グローバル変数はfunction、subをスコープに含めるが、taskはスコープに含めない ・ローカル変数はfunction、sub、taskのどれで宣言しても元のスクリプトをスコープに指定しない ・task内のある要素にグローバル変数を充てたければ、実引数にグローバル変数を設定してローカル変数に変換する。 B変数の記憶寿命とメモリ 「変数の記憶寿命」とはその変数が存在し続ける期間の事です。 スコープと似てますが微妙に違います。 グローバル変数、ローカル変数共に、スコープを超えるまでが記憶寿命となります。 記憶寿命を過ぎるまでの間、変数はコンピュータ上のある場所に格納されます。 各変数ごとに格納される場所は違い、それぞれその変数の「アドレス」と言います。 そして変数があるアドレスに格納される事を「メモリを確保する」と言います。 私達がプログラムを打たずに自動的にメモリを確保する事を「静的メモリ確保」といい、 私達が意図してメモリを確保する事を「動的メモリ確保」といいます。 また、変数をアドレス内から破棄する事を「メモリを解放する」と言います。 私達がプログラムを打たずに自動的にメモリを破棄する事を「静的メモリ解放」といい、 私達が意図してメモリを解放する事を「動的メモリ解放」といいます。 弾幕風ではアドレスを指定する「ポインタ」という物が使えないため、 動的メモリ確保はポインタ無しで行う事が出来ます。 (例) let a=0;//自動的に指定されたどこかのアドレスに動的メモリ確保 その為、メモリの確保については私達があれこれ考える必要がありません。 ここで重要なのはメモリの解放です。 弾幕風での静的メモリ解放は、スクリプトが終了した時にスクリプト内のグローバル変数に対して行われます。 なので実はDeleteGraphicは@Finalizeに書かなくてもよかったりします。 ただし、連続したステージ内である背景を削除したりするときはDeleteGraphicを使わなくてはなりません。 私達がDeleteGraphicで動的メモリ解放する必要があるのです。 弾幕風で処理が重くなる要因のうちの一つに「メモリを確保しすぎる」というのがあります。 少しでも処理落ちを軽減するには、動的メモリ確保したら必ず動的メモリ解放しましょう。 最重要事項↓ ・大型スクリプトではこまめな動的メモリ解放を心がける