2003/テクニック

Last-modified: 2024-04-15 (月) 01:02:14
 

2003での制作において有用な方法を模索していくページです。
旧日本語版ではなく、Steam版2003 + Maniac Patchで利用可能な小技をメインとします。

 

◆目次

 

 

便利な記法

開発する上でイベントの書き方を合理的な形に統一すると、イベントの読みやすさ(可読性)が上がり、開発中に混乱することがなくなる。
以下にその記法の例を記述する。

 
詳細

注釈を細かく書く

非常に当たり前であるが、いくらイベントが読み易かったとしても注釈が無ければ後から見返した時に何の処理かわからなくなる。
とにかくしつこいくらいに注釈を書こう。注釈の処理は非常に軽いのでパフォーマンスを気にして省かないようにしよう
(あまり何百回も繰り返し処理をするような処理は繰り返しの前にまとめて書こう)。
注釈の書き方にもさらに自分なりのルールを作るとなおよい(例:一連の長いひとまとまりの処理は「>>>」「<<<」の注釈で囲うなど)。

 

計算用の一時変数を作る

v[1]から始めて20~100個ほど、汎用的に計算に使う変数を用意しておくとイベントごとに無駄に変数を作る必要がなく便利である。
一時変数を使うときは以下のルールを守ろう:

  • イベントやフレームをまたいで一時変数の内容を保持する必要があるような処理を書かないこと
  • 使った一時変数はイベントの一番上に注釈で何に使ったか一覧を書いておくこと
 

一時変数を用いると便利な状況は特に以下である:

  • 繰り返し処理におけるカウンタ・ポインタ
  • 論理演算の結果代入用(後述)
  • 複雑で長い変数操作の区切り
  • デバッグ用に文字列ピクチャで変数の番号の変数の内容などを出力する際
     

複数の条件をひとつの条件分岐にまとめる

上述の一時変数と変数の操作の式を使うと、分岐を重ねて見づらくなることを避けることができる。

@> 注釈: v[2]が0より大きくv[3]が5と同値、あるいはv[4]が4以下の場合
@> 条件分岐: V[0002] > 0 && V[0003] == 5 || V[0004] <= 4
  @> 注釈: // 何らかの処理
  @>
 : 分岐終了
 

論理演算子というものを使うことで、複数の条件を一つの条件分岐にまとめることができる。
条件分岐で論理演算子による複数条件の一括判定を行うには、TPC経由で「@if `(条件) { }」と記述する。
ツクールのエディタのデフォルトの条件分岐のウィンドウからは使えないので注意。

 

&& : AND(論理積) 左辺と右辺の条件がどちらも真である場合に真を返す
|| : OR(論理和) 左辺と右辺の条件のどちらかが真である場合に真を返す
! : NOT(否定) 直後の条件の真と偽を反転させる

 

内部的には偽は0、真は0以外として扱われており、例えば 100 && 50 と書いた場合、1が返ってくる。
条件を0以外の数値のみにした場合、その条件は常に真として扱われる。

 

なお、XOR(排他的論理和。両辺のうちどちらかが真でもう片方は偽の場合に真を返す)はない。
[条件A] XOR [条件B] と書きたい場合、( [条件A] + [条件B] ) == 1 などのように書く必要がある。
条件が比較演算子を使っていない単なる数値であれば、( !v[1] + !v[2] ) == 1 と否定演算子を使って数値を0か1に絞るといいかもしれない。

 

変数の操作の式を活用する

「オプション→新しい「変数の操作」を使う」を選択で利用できるようになる変数式では、複雑な計算を一気に行うことが可能である。
ここでは変数式に使える記法をいくつか紹介する。

 
詳細
 

一時配列

[ (値), (値), ... ][(インデックス)]
[]で囲み、コンマで区切った値の並びを扱うことができる機能。
インデックスは配列の中の何番目の値を取り出すかということ。0を指定すると配列の一番最初の値が返ってくる。
例えば、v[1] = [3, 6, v[2], 4 - v[3]][v[4]]とした場合、[v[4]]0だった場合はv[1]に3が、3だった場合は4 - v[3]が代入される。
インデックスの値が配列の長さをオーバーしている場合は0が返ってくる。
場合分けを行う際などに極めて便利。

 

複数の変数に一括で代入する

v[0] = v[1..3] = [83, 472]

操作対象に0を指定し、オペランドに「変数範囲 = 一時配列」と入力すると変数範囲にそれぞれ違う値を代入できる。
上記の例ではv[1] = 83, v[2] = 472, v[3] = 0 となる(一時配列の長さが足りていない場合は0で補完される)。
操作対象を0にせず、範囲変数を対象にしてv[1..3] = [83, 472] としたとき、一時配列を直接代入する場合は未定義になるため、v[1..3]には0が代入されることに注意。

補足

変数操作におけるオペランドは必ず「特定の数値」を返すものである。
一時配列は数値などの並びであり、これ自体を数値として解釈することはできないため、代わりとして0が代入されてしまう。
ただし、一つの式の中では代入演算子を用いて複数代入を行える仕様になっている。
なお、v[1..3] = v[4..6] とした場合は先頭であるv[4]がv[1..3]に代入される模様。

 

操作対象にv[0]を指定しているのは余計な変数を操作しないためである。
また、TPCで「v[1..3] = [2, 5, 6]」と入力した場合、自動的に「v[0] = v[1..3] = [2, 5, 6]」に変換される。便利。

 

三項演算子

(条件式) ? (条件が真の場合に解釈される式) : (条件が偽の場合に解釈される式)

条件分岐で簡単な変数操作のみをしたい場合、それをまとめて変数操作の中に突っ込める記法。
v[1] = v[2] == v[3] ? v[4] : v[5] とした場合、v[2]がv[3]と同値ならv[1]にv[4]が、違えばv[1]にv[5]が代入される。
使いすぎると可読性が著しく低下するため、少なくとも入れ子にして使うのは非推奨。

 

条件文の結果そのものを計算に利用する

v[1] += v[2] == v[3]

条件文において、条件を満たした場合は1、満たしていない場合は0が返ってくる。
上記の例ではv[2]がv[3]と同値であれば1を加算、そうでなければ加算しない、というものである。
この条件文を括弧でくくって(条件) * Nとすれば、任意の値Nを加算するようにもできる。
短く書けるが、これも可読性が下がりかねないので注意。

 

ゲーム全体のキー入力を制御する

キー入力はゲーム中の様々なシーンで汎用的に用いられる処理だが、「キー入力の処理(EX)」を随所に挟むような作り方をした場合、
判読性が低下する、キー入力の方法に一貫性がなくなる、キーコンフィグが設定しづらい、などの不利益が発生する可能性がある。
そのため、ゲーム中のあらゆるシーンにおいて特定の変数を参照するだけでキー入力を判定できるようにすることが望ましい。

 
イベント例
 

以下のイベントをコモンイベントの一番目に配置しておく。
実行条件は「定期的に並列処理する」とし、v[1..2]は計算などに汎用的に使う一時変数とする。

@> キー入力の処理EX: キーボード -> V[0101 .. 0150]
@> キー入力の処理EX: ジョイパッド -> V[0151 .. 0166]
@> 変数の操作: [0001] 代入, 101
@> 変数の操作: [0002] 代入, 201
@> 繰り返し処理: 66 回
  @> 条件分岐: 変数 [V[0001]] == 0
    @> 条件分岐: 変数 [V[0002]] < 0
      @> 変数の操作: [V[0002]] 減算, 1
      @>
     : それ以外の場合
      @> 変数の操作: [V[0002]] 代入, -1
      @>
     : 分岐終了
    @>
   : それ以外の場合
    @> 条件分岐: 変数 [V[0002]] > 0
      @> 変数の操作: [V[0002]] 加算, 1
      @>
     : それ以外の場合
      @> 変数の操作: [V[0002]] 代入, 1
      @>
     : 分岐終了
    @>
   : 分岐終了
  @> 変数の操作: [0001~0002] 加算, 1
  @>
 : 以上繰り返し
 

このイベントでは以下のような結果が得られる。

  • v[101..166]に各キーないしジョイパッドのボタン等の状態が代入される(0:押されていない/1:押されている)
  • v[201..266]に各キーないしジョイパッドのボタン等の入力時間が代入される(-1以下:離してからの時間/1以上:押してからの時間)
    例として、v[201]が1ならAが押された瞬間であること、30ならAが0.5秒押しっぱなしであること、-60ならAが離されてから1秒経過していることを示している。
    これにより、「キーを押した瞬間のみ実行される処理」や「一定時間キーを押し続けると実行される処理」などを容易に作ることが可能となる。
 

キーコンフィグを作りたい場合は「決定ボタン」「キャンセルボタン」などの変数を用意し、それぞれに201~266のどれかを代入する
(その上で「条件分岐:v[v[決定ボタン]] == 1」などのように変数番号経由で分岐を行う)。
キーコンフィグを作らない場合はv[201..266]を直接指定して分岐してもよい(サンプルプロジェクトに使用例あり)。

 

◆サンプルプロジェクトはこちら ※Maniac Patch ver.200128の実行ファイルが必要です
fileサンプル - ゲーム全体のキー入力を制御する.zip

 

なお、コモンイベント集にこのイベントを簡単に既存のプロジェクトに導入できるコモン素材がある。
こちらの方が高機能なので、実際に使うならこちらを推奨する。

 

マウス入力を受け付ける

マウス座標の取得/マウス座標の設定/キー入力の処理を用い、マウスによる入力を受け付けられるようにする。

 

SampleMouse.png

 
イベント例

下のサンプルにはマウスを画面外に出さないようにする処理も同梱している。
該当部分のイベントは以下の通り。

@> マウス座標の取得: V[0008], V[0009]
@> 注釈: // 画面内固定する場合 - (160, 120)からのマウスの移動距離をポインタの表示座標に加算
@> 注釈: // ポインタの表示座標を(0 - 320, 0 - 240)の範囲に丸める
@> 変数の操作: [0001:マウスX] 代入, clamp(v[1] + v[8] - 160, 0, 320)
@> 変数の操作: [0002:マウスY] 代入, clamp(v[2] + v[9] - 120, 0, 240)
@> 注釈: // 実際のマウスポインタの位置は中央に戻しておく
@> マウス座標の設定: 160, 120
 - 中略 -
@> ピクチャ表示: 1, ..\Chipset\World, (V[0001:マウスX], V[0002:マウスY]), 30x16, セル 260, V[0007:カーソル拡大率]%, 40%, M9

マウスをあらかじめ(毎フレーム)画面中央(160, 120)に移動させておき、プレイヤーがマウスを動かした際に(160, 120)からの移動距離をポインタの表示座標に加算する。
さらに「clamp(X, 0, 320)」とすることで、Xの値を0 - 320の範囲に収めることが可能である。これを用いて座標の範囲を制限し、
その座標にポインタのピクチャを表示することでマウスを画面外に出ないようにしている。
ただし、マウスの速度があまりに速すぎる場合は一瞬だけカーソルがウィンドウ外に出てしまう。

 

このサンプルでは「キー入力の処理」を用いているが、「キー入力の処理EX」でも同じくマウスのボタン/ホイールの状態を得ることができる。
上述の「ゲーム全体のキー入力を制御する」と組み合わせて用いるとよい。

 

◆サンプルプロジェクトはこちら ※Maniac Patch ver.200128の実行ファイルが必要です
fileサンプル - マウス入力.zip

 

戦闘中のログを表示する

戦闘処理の制御を用いるとダメージや回復などの表示に改変を加えることができる。
これにより、通常は一瞬で消えてしまう表示を画面にログとして表示し続けるなどが可能である。

 

SampleBattleProc.png

 
イベント例
 

コモンイベントに「戦闘開始」を実行条件としたイベントを一つ作成し、これに戦闘の初期設定処理を記述していく。
今回利用した戦闘処理の制御の項目は以下の通り。

  • ダメージポップ → CEv[2:ポップ]
    敵味方へのダメージ・回復・回避処理を拾い、それをログとして出力する。
     
  • ターゲッティング → CEv[4:ターゲット確認]
    敵味方が行動する直前に誰が行動するかを取得する。
     
    CEv[4:ターゲット確認] で行動ユニットのID取得 → CEv[2:ポップ] でピクチャ表示 → CEv[3:ポップ流し]で毎フレームピクチャを下に流す、という流れになる。
    ターゲッティングおよびダメージポップでは対象となるユニットの順番が取得できるので、主人公については
    @> 変数の操作: [0007] 代入, メンバー [V[0002]]のID
    とすれば、v[2]に保存されている味方ユニットの順番からv[7]に主人公IDを取得することができ、これを文字列ピクチャで「\n[\v[7]]」とすれば主人公名を表示できる。
    敵の名前を取得する場合にも同じく主人公名から参照する必要があるため、戦闘開始直後に敵グループごとに主人公「敵1~敵5」の名前をリネームする力技を行っている。
    敵1は11番目の主人公にしているので、
    @> 変数の操作: [0007] 代入, v[2] + 11
    とすれば、v[7]にその敵に対応する主人公IDを取得できるため、後は同じように「\n[\v[7]]」と表示するだけである。
     
    ピクチャは座標を(320, 0)とし、表示する基準を「右下」とすることで、画面右上外に表示できる。
    ただし複数のピクチャが一挙に表示される場合(全体攻撃した時など)は同じ座標に表示されてメッセージが重なってしまうので、ピクチャが画面外にスタックしている分Y座標をずらす処理が必要となる。
    これは以下の処理で行っている。
    @> ピクチャ情報の取得: V[0009](中心座標X, 中心座標Y, W, H) -> V[0011, 0011, 0011, 0010]
    @> 変数の操作: [0022:ポップ用ピクチャ最上Y] 減算, V[0010]
    ピクチャ情報の取得によりv[10]に生成した文字列ピクチャの高さが代入されるので、これをピクチャを表示するY軸の基準から減算し、次に生成されるピクチャの座標をさらに上にずらしている。
    生成されたピクチャは下にずらして画面内に入れてやる必要があるので、以下のイベントを「戦闘中に並列処理する」で実行する。
    @> 条件分岐: 変数 [0022:ポップ用ピクチャ最上Y] < 0
      @> 注釈: // 毎フレーム最大3ドットずつ移動
      @> 変数の操作: [0010] 代入, min(abs(v[22]), 3)
      @> 繰り返し処理: カウントアップ (101~120) -> V[0005]
        @> 注釈: // ピクチャの現在の座標を取得
        @> ピクチャ情報の取得: V[0005] -> V[0001, 0002, 0003, 0004]
        @> 注釈: // ピクチャを下に相対移動
        @> 変数の操作: [0002] 加算, V[0010]
        @> 注釈: // 下に行くほど透明度アップ(最大100)
        @> 変数の操作: [0006] 代入, min(v[2] / 2, 100)
        @> ピクチャ移動: V[0005], (V[0001], V[0002]), 100%, V[0006]%, 0.0秒
        @>
       : 以上繰り返し
      @> 変数の操作: [0022:ポップ用ピクチャ最上Y] 加算, V[0010]
      @>
     : 分岐終了
    v[22:ポップ用ピクチャ最上Y] が0よりも小さいとき、新しく生成されたピクチャが画面外にはみ出していることを示している。
    従ってv[22] < 0のときで条件分岐し、v[22]が0になるまでピクチャ全体を下にずらしていく。
     
    利用するピクチャIDは101~120の間で順繰りに使っていくようになっている。
    @> 変数の操作: [0021:ポップ用ピクチャID] 代入, (v[21] + 1) % 20
    とすることで、v[21]を0~19の間でループさせ、実際に表示する際には+101すると101~120のどれかになる、ということである。
     
     
    今回はダメージ/回復/回避のみしか拾っていないが、戦闘処理の制御の状態付与や能力値変動の項目を用いて同じように作ればログの種類を増やすことができる。
    2003の戦闘は2000のそれに比べて表示が不十分なものも多いため、戦闘処理の制御を用いて表示する情報を補っていきたいところである。
     
    ◆サンプルプロジェクトはこちら ※Maniac Patch ver.200128の実行ファイルが必要です
    fileサンプル - 戦闘処理の制御・ログ表示.zip
 
 

関数の引数に指定した変数式に代入する

TPCの関数で、引数に取った変数に値を代入したい場合、以下のように書くと失敗する。

__fn assign $target $value {
    $target = $value
}
 
assign(v[1], v[2])  // -> 何も出力されない

これは引数を受け取る対象のメタ変数に対しイコールを使うことで、メタ変数に対する代入となってしまうためである。
これを回避するには、例として以下のような記法が用いられる。

__fn assign $target $value {
    v[__id($target)] = $value
}
 
assign(v[1], v[2])  // -> v[1] = v[2]

しかし、例えば「assign(v[v[1]], v[2])」や「assign(v[1 + v[5]], v[2])」など、変数式を対象に取ることはできない。
なぜなら__id()は定数番号指定の変数の番号を取り出すもので、式には対応していないためである。
これの解決策として、以下のような方法が採れる。

__fn assign $target $value {
    v[0] = $target = $value
}
 
assign(v[1 + v[5] * 3], v[2])  // -> v[0] = v[1 + v[5] * 3] = v[2]

操作対象をv[0]として変数操作の「操作内容」の代入を使わず、式内で代入を行うことでメタ変数に対する代入という解釈を防ぐことができる。
ただし、「assign(v[1], v[2])」といった単純な代入も「v[0] = v[1] = v[2]」となり、「v[1] = v[2]」より処理負荷が多少増加する点に注意。

変数の動きをリアルタイムで確認する

変数代入がうまくいってないようだけど、どのタイミングで間違えているか分からない時に
オマケで指定位置のおおまかな座標を取得する機能付き

@loop .inf() {
   @comment "マウスからおおまかなピクチャ位置の座標の当たりをつけたい場合"
   @mouse.getPos v[4999], v[5000]
   @pic[999].strpic {
       " ↓
→ ←
 ↑"
       .pos v[4999], v[5000] .center
       .size 0, 0        .chromakey 1
       .scale 100
       .trans 10
       .rgbs 100, 100, 100, 100
       .font "", 10 .noShadow
       .spacing 0, 0
       .skin "" .nobg .noframe .noGradation .noPadding
       .mapLayer 7
       .eraseWhenTransfer
       .affectedByTint
       .affectedByFlash
       .affectedByShake
   }
   @comment "マウスからおおまかなピクチャ位置の座標の当たりをつけたい場合"
   @pic[1000].strpic {
       "t1= \t[1]
v1= \v[1] v2= \v[2] v3= \v[3]
  
x= \v[4999] y= \v[5000]"
       .pos 320, 240 .bottomRight
       .size 0, 0        .chromakey 0
       .scale 100
       .trans 10
       .rgbs 100, 100, 100, 100
       .font "", 10 .noShadow
       .spacing 0, 0
       .skin "" .stretch .noframe .noGradation .noPadding
       .mapLayer 7
       .eraseWhenTransfer
       .affectedByTint
       .affectedByFlash
       .affectedByShake
   }
   @wait 0
    
}

※よくわからなかったのでこちらに。コモンイベント素材化出来る方、お願いします。

 

 

コメント

  • 具体的なイベント書いてくれるのたすかる~~~ -- 2021-08-22 (日) 03:04:20
  • マウス入力を受け付けるのサンプル間違ってませんか?可能なら差し替え求む。 -- 2024-02-13 (火) 00:49:49