TPCの導入方法
マニアクス公式ページの「ダウンロード」からTPCの最新版をダウンロードし、
パッチを当てたエディタと同じディレクトリにtpc.exeを置くだけで導入できる。
なお、TPC本体および同ディレクトリにあるTPCを呼び出すためのファイル「cmdTos.dll」はウィルス対策ソフトの誤検知で削除されやすいため、
ダウンロードする際はウィルス対策ソフトを一時的に無効化してダウンロードし、
使用時はツクール2003のフォルダごとウィルス対策ソフトのホワイトリストに追加しておくことを推奨する。
起動しないときのチェックリスト:
- イベント編集画面でF3を押しても何も起こらない → cmdTos.dllが存在しない。パッチを再インストールしよう
- イベント編集画面でF3を押すと「TPCの起動に失敗しました」と出る → tpc.exeが存在しない。エディタに再配置しよう
- どちらもやったのに何度も失敗する → ウィルス対策ソフトにファイルが削除されている。ホワイトリストにファイルかフォルダを追加しよう
概要
TPCには大まかに三種類の使い方がある。
①ツクールのエディタから使う
イベント編集画面からのみ利用可能。
F3を押すと新規にコードを挿入、既に配置されたコマンドを1行~Shiftを押しながら複数行選択してF2でTPCコードとして編集できる。
そのイベントに対してのみ編集を行うため、後述の書き出しオプションなどは全て無効化される。
②クリップボード経由で使う
TPCの実行ファイルにソースファイルを読み込ませると、それがツクールの形式に変換される。
このときソースファイル内に「#toClip」と記述されていると出力先がクリップボードになり、イベント編集画面にて出力内容のペーストが行える。
単純にtpc.exeにソースファイルをドラッグ&ドロップしてもよいが、ソースコードにミスがあった場合であってもエラーを吐かないので、
ミスの原因がわからずクリップボードにも出力されないという困った状況になるため、何らかのコンソールを経由するのが望ましい。
③TPCからゲーム全体を直接書き出す
TPCに読み込ませたソースファイル内に「#apply」と記述されていると出力先がツクール形式のファイルそのものになる(書き出すには#apply以外にも設定が必要)。
これによりツクールのエディタを使わず制作を行うことが可能になる。
エディタ上でTPCをF2から編集したとき記法は自動的に適切なものに変換されるが、これを回避し自分の記法を維持するのにもこの方法は便利である。
②と同じくコンソールを経由するのが速やかな開発を行うに当たっては望ましい。
ゲーム全体をTPCで記述するので、機能が増えたことによりデフォルトの検索機能やデバッガー200Xが使えなくなった今では大規模開発においては有利な方法である。
ただしTPCによって書き換えを指示しなかったイベントについてはそのままにされるため、ツクールのエディタとも一応併用はできる。
また、マップや戦闘アニメなどTPCでは記述できないものについてはツクールのエディタを使って編集するのが前提である。
コンソールを利用してエラーログを出力させるには以下のようなソフト(テキストエディタ)を使うとよい。
- Notepad++ ( nppexecというプラグインを導入する )
- Sublime Text ( 公式サイトにある Sublime TPCを導入する → メニュー → View → Show Console から表示可 )
- Visual Studio Code ( 公式サイトにある TPC コーディング補助ファイル for Visual Studio Code を導入する )
文法の解説
以下にTPCにおいての文法の基礎や、ミスしやすい記法についての説明を行う。
コマンドの記法と引数
変数・スイッチ・文字列操作以外のコマンドは、基本的に
@(コマンドネーム).(サブネーム) (引数)
という形を取る。引数がなかったり、サブネームがないものもある。
@msg.show "あいうえお" // 文章の表示 : あいうえお @comment "テスト" // 注釈 : テスト @ev.abort // イベント処理の中断
引数(イベントコマンドが取る何らかの値)は空白やコンマで区切って記述する。
ブレース(波括弧、{})で囲えば引数の間に改行を入れられる。
基本的に引数は順不同である。
@wait 1 .frame @wait .frame 1 @wait (1 .frame) @wait { .frame 1 }
上記の例のウェイトコマンドは「1」と「.frame」の2つの引数を取っている。
この2つの引数はどういう順番で書いてもいいため、例示では全く同じコマンド (ウェイト:1フレーム) が4つ出力される。
ただし、引数の中には順不同でないものもある。
@pic.show [1] "picture" .pos( v[1], v[2] ) // ピクチャーの表示 : ID:1, "picture", 座標(v[1], v[2])
上記の例ではピクチャーが( v[1], v[2] ) の位置に表示されるが、.pos()が取る二つの引数はX, Yと順番が決まっている。
このように、引数が取る引数は同じ種類の複数の値である場合があり、値を区別するために順不同ではなくなっている。
.pos() が取る二つの値以外の引数は、以下のようにどのような順番で書いてもよい。
@pic.show .pos( v[1], v[2] ) [1] "picture"
なお各イベントコマンドは一定数の引数を要求するが、省略するとデフォルト値が代わりに適用される。
例えば、ピクチャーの表示の座標指定 (.pos(x, y) ) を省略した場合、(0, 0) が自動的に適用される。
デフォルト値には無効な値もあるので注意(例として、ピクチャーのIDを省略すると0が適用され、IDが0になるため実行時にエラーが起こる)。
変数やスイッチの操作コマンドは例外であり、コマンド・サブコマンドが存在せず、以下のように表記する。
v[1] = 50 // v[1]に50を代入 v[2] += 15 // v[2]に15を加算 v[v[3]] *= v[4] / 3 // v[3]の変数の番号にv[4]を3で除算した結果を乗算 s[1] .on // s[1]をオンに s[2] .off // s[2]をオフに s[3] .toggle // s[3]をオンオフ切り替え s[4] = 1 // s[4]をオンに s[5] = 0 // s[5]をオフに t[1] .asg "abc" // t[1]にabcを代入 t[2] .cat "test" // t[2]の末尾にtestを結合
コマンドネームは最後に配置したものが記憶され、その後に出てくるサブネームの親として解釈される。
以下のイベントは文字列の横幅を取得し、それを2倍するという処理である。
これは「文字列ピクチャの表示」「ピクチャ情報の取得」「変数の操作」「ピクチャの消去」を順に出力する。
defv x = 1, y, w, h // v[1] からv[4] にx, y, w, h をそれぞれ名前として割り当てる @pic [1] .strpic "取得する文字列" .trans(100) .noframe .noPadding .getInfo .xywh .baseRect(x, y, w, h) w *= 2 .erase
この省略記法を使う場合、対象となった [1] もまた記憶されるため、上記の3つのコマンドは全てピクチャID1を対象とする。
また、上記の「w *= 2」のように、変数の操作などのコマンドネームを必要としない処理なら省略記法の合間に挟むことができる。
注釈
ツクール上でコマンドとして扱われる「@comment」以外にも、コマンドとして扱われない注釈 (コメント) を書くことができる。
これはイベントコマンドに変換する際に消滅し、コード中のどこであっても書くことができる。
// 一行注釈。スラッシュ2つの後に書く文字列は全てコメントとして扱われる @msg.show "test" // このように一行注釈の左側に処理を書くこともできる /* 複数行注釈 長い説明を書くときや一気に大量の処理をコメントアウトする際に便利 (コメントアウトとはコメント記号を付け加えることでコメントに変え、コード上から一部の処理を除外すること 書いた処理を消さずに残しておく用途で使われる) */
ブロックに関する注意点
ブロックを示すブレース {} は、ブロックだけでなく引数のまとまりという意味もある。
また、最後に書いた@(コマンド名)は次のサブネームまで引き継がれるという仕様がある。
そのため、@(コマンド名) という記法をせず、かつ式ではないスイッチ・文字列変数・変数配列の操作では思いがけない解釈になる時がある。
@if v[1] == 1 { v[1] = 2 } .else { s[1] .on // 不明なコマンド: @if.on t[1] .asg "a" // 不明なコマンド: @if.asg v[1] .copy(v[2], 1) // 不明なコマンド: @if.copy }
これを回避するためには、ブロックの直前に引数ではなくブロックであることを明示する「bl」を書くと安全である。
@if v[1] == 1 bl { v[1] = 2 } .else bl { s[1] .on t[1] .asg "a" v[1] .copy(v[2], 1) }
@if だけでなく、ブロックを引数として要求するコマンドには全て bl をつけるとよい。
コードの取り込み
#include "ファイル名"
とすると、その位置にファイル名の中身を展開し、TPCのコードとして取り込むことができる。
例えば#applyからコモンイベント素材を使うとき、
cev [10] "コモン素材" .beCalled bl { #include "C:\プロジェクト\コモン素材.txt" }
とすれば、コモンイベント10番にコモン素材を「呼び出された時のみ」で取り込める。
メタ構文
TPCはコードからイベントコマンドを生成するが、書いたままのコマンドを出力するだけでなく、
場合に応じてイベントコマンドの各種引数を置き換えて出力することができる。
こういったイベントコマンドの形そのものを変化させる文法を「メタ構文」と呼ぶ。
TkoolBridgeではループで一気に大量のコマンドを生成できたが、あれと同じようなことが可能である。
注意点として、出力後のコマンド群にはメタ構文の情報は一切含まれないため、
メタ構文を用いて生成されたコマンド群をTPCで開いても元のコードの形にはならない。
つまり、メタ構文はツクールと連携してその場その場でTPCを呼び出す方式では元コードが失われるのであまり役に立たず、
TPCのコードから#applyを使ってゲームデータ (ldb, lmu) を直接書き出す場合に最も効果的である
(注釈などにメタ構文を含む元コードを保管しておく、という方法も取れなくはない)。
定義定数
defv (定義名) = 数値変数の番号 defs (定義名) = スイッチの番号 deft (定義名) = 文字列変数の番号 def (定義名) = 自由な値
と記述することで、コード内の定義名を右辺の値で置き換えることができる。
defv hensuu = 5 hensuu = 10 // v[5] = 10 def start = 100 defv hensuu2 = start + 8 // 100 + 8 hensuu2 = 83 // v[108] = 83 def コモン = .common(10) // 定義名には日本語も使える @call コモン // コモン10番を呼び出し
これは変数や定数がどういう用途に使われているのかを明確にする効果がある。
defv characterLevel = 11 // v[11] をキャラクターレベルを代入する用途として使う characterLevel = actor[1].level // ID:1の主人公のレベルを代入 @msg.show "ザックのレベルは" characterLevel "です。" // 文章の表示:「ザックのレベルは\v[11]です。」
@msg.show(文章の表示)に続けてdefv/defs/deftで定義した定義名を書くと、勝手に\v[N]などという形に展開される。
def/defv/defs/deftで定義した値は再定義できず、値をコードの途中で変更することができなくなる。
def (定義名) はメタ変数と同じく自由な値を入れられるが、値を変更できないのが違いである。
defに入れられる自由な値については後述のメタ変数の項目も参照。
ちなみに、上記の例では定義名は半角英数だが、日本語なども一応利用できる。
def 計算用 = v[1] 計算用 = 計算用 + 6 // v[1] 代入、v[1] + 6
メタ変数
$から始まる名前でメタ変数を定義できる(「$abc」など)。
メタ変数はツクール上での変数とは異なり、TPCがコードを変換してイベントコマンドを出力する際にのみ一時的に存在する変数である。
メタ変数には数値、文字列、式、コマンドそのもの(ブロック含む)など、様々なものを代入できる。
$asd = @msg.show $qwe $qwe = "alex" $asd // 文章の表示:「alex」 $qwe = 100 $qwe += 1 $asd // 文章の表示:「101」
defと違って再代入が行える他、代入されている数値の場合は計算が可能。
注意点として、$(定義名) = (代入する値)という形である限り、右辺がそのまま代入される。
従って、
$asd = 100 $qwe = $asd + 1 @msg.show $qwe // 文章の表示:「」
とした場合、「$asd + 1」は式であるので、「@msg.show $qwe」は何も表示しない
(@msg.showは最初の例のように文字列や数値は引数に取れるが、式を取ることはできないため)。
期待通りの動作をさせたい場合、以下のように書く必要がある。
$asd = 100 $asd += 1 @msg.show $asd // 文章の表示:「101」
また、以下のような記法も不可能である。
$asd = t[1] $asd .asg "aaa"
t[1] に"aaa"が代入されることが期待されるが、$asd の値はコンパイル時に初めて確定するため、
文字列変数の引数である.asg の直前は文字列変数であることが確定していなければならない。
そのため、以下のように書けば t[ ] によって文字列変数であることが確定するため、問題なく通る。
$asd = 1 t[$asd] .asg "aaa"
$asd = t[1] t[__id($asd)] .asg "aaa"
ちなみに、__id()は変数のIDを数値として返してくれる組み込み関数である(例:__id( v[1] ) -> 1、__id( t[2] ) -> 2)。
注意点として、メタ変数は関数内にはスコープが通らない。
詳しい説明は関数を参照。
関数
関数によって一連の処理をまとめ、定義名を書くだけで呼び出すことができる。
以下の例はID:1の主人公のレベルを取得し、それを文章に表示する処理の関数である。
「__fn (定義名) { (処理内容) }」という形で関数を定義できる。
__fn showZacksLevel { v[1] = actor[1].level // v[1]に主人公[1]のレベルを代入 @msg.show "ザックのレベルは\v[1]です。" }
以下のように () を付けて記述すると、その位置が関数の内容に置き換わる。
showZacksLevel()
イベントの呼び出しと同じようなものと思ってよいが、イベントの呼び出しとは
- 出力後は定義したイベント内容そのものに置き換わるため、イベントの呼び出しを使うより僅かに軽い
- 引数を利用してイベント内容に変更を加えられる
という点に違いがある。
例えば、上記の例を自由なIDの主人公のレベルを表示できるように書き換えてみよう。
__fn showActorLevel $actor_id { v[1] = actor[ $actor_id ].level // v[1]に主人公のレベルを代入 t[1] .asg .actor[ $actor_id ].name // t[1]に主人公の名前を代入 @msg.show "\t[1]のレベルは\v[1]です。" }
__fnの後に定義名を書いた後、引数として扱うメタ変数を記述する。
メタ変数の名前は何でもよい。この場合は主人公のIDが格納されるという用途から「$id」という名前にしている。
これを以下のように()内に引数を書くことで呼び出せる。
showActorLevel( 4 ) // ID:4の主人公のレベルを表示 showActorLevel( v[3] ) // ID:v[3]の主人公のレベルを表示
引数は複数使うこともできる。上記の例をさらに、使う変数を引数から変えられるようにしてみよう。
__fn showActorLevel $actor_id $v $t { v[$v] = actor[ $actor_id ].level // 主人公のレベルを代入 t[$t] .asg .actor[ $actor_id ].name // 主人公の名前を代入 @msg.show "\t[" $t "]のレベルは\v[" $v "]です。" }
これを以下のようにして呼び出すことができる。
showActorLevel( 2, 3, 4 ) // v[3] にレベル、t[4] に主人公[2] の名前を代入して表示
変わった使い方
◆ブロックを引数として渡す
特殊な用法として、先述の通りメタ変数にはブロックを代入できることを利用し、関数にブロックを渡すことができる。
以下の例では、条件に一致した場合は $true の処理を、そうでない場合は $false の処理を実行する。
__fn branch $true $false { @if v[1] == 5 $true .else $false } branch({ t[1] .asg "true" }, { t[1] .asg "false" })
以下のようにすれば、ピクチャーの表示の雛型にブロックで追加のオプションを挿入することができる。
__fn showPicture $options { // デフォルトでは(160, 120) にID1で表示する @pic.show [1] .pos(160, 120) $options } showPicture({ "picture_name" .mapLayer(6) .grid(2, 5) .cell( v[1] ) })
ただし、以下のような形では注意する必要がある。
__fn isZero $var $block { @if `$var == 0 $block } $a = 1 $b = v[$a + 5] $c = 3 isZero($b, { v[$c] = 100 })
上記のコードではv[6]が0ならv[3]に100が代入されるイベントが生成されるように思えるが、これは実際には全く機能しない。
関数isZero()の1つ目の引数にはv[$a + 5]という式が代入されているが、
関数の外にあるメタ変数$aは関数内からでは参照できず0となってしまい、v[0 + 5]という結果になる。
同じく$cも0となってしまうため、v[0]に100が代入されることになる。
変数が参照できる範囲はスコープと呼ばれ、TPCでは関数内にメタ変数のスコープは通らないと言える。
このような場合は通常の変数にメタ変数の値を代入して用いるしかない。
◆TPC連携でのコード取り込み
エディタと連携してTPCを使っているとき、ツクールが存在するドライブの直下が作業ディレクトリとなるらしく、
例えばCドライブ内にツクールが存在している場合に
#include "aaa.txt"
とTPCで入力すると、C:\aaa.txtをTPCのコードとして取り込むことができる。
よく使う関数などの定義をドライブ直下のテキストにまとめておき、includeで呼び出すようにしておくと便利かもしれない。
文字列内で値を展開する
メタ変数や定義定数を文字列内で展開して使いたい場合は「__str() 」を使うとよい。
__str()は引数を結合し、一つの文字列として返してくれる組み込み関数である。
内部に変数(定義変数含む)があった場合「v[N]」という形で出力されるため、展開には \ を付加する必要がある。
defv qwe = 251 def asd = "abc" $foo = 105 @pic.strpic __str("\" qwe " / " asd " / " $foo) // 文字列ピクチャの表示:「\v[251] / abc / 105」
なお、途中で改行したい場合はコンマが必要である(__strの引数であるという解釈が改行によって途切れないようにするため。詳しくは後述)。
__str("x" "y", "z") // -> 文章の表示:「xyz」
改行について
基本的には、改行を入れた時点でイベントコマンドは終わりと見なされ、そこで解釈は途切れる。
下記の例では座標 (1, 5) にあるイベントのIDをv[3]に取得しようとしているが、これは改行を入れているためエラーとなる。
@map.getEv .pos(1, 5) .dst( v[3] ) // エラー : 不明なコマンド : @map.dst (直前に使われた@mapのサブコマンドとして.dstが解釈されている)
ただし、コンマで区切って続きに引数があることを明示すると、次の行も同じイベントコマンドとして解釈してくれる。
以下の二つの例ではエラーは起こらず、期待通りの解釈が行われる。
@map .getEv .pos(1, 5) , .dst( v[3] )
@map .getEv .pos(1, 5) .dst( v[3] )
また、変数式も同じく「改行後に値があると思われる場合」には改行の先もまとめて解釈してくれる。
+などの二項演算子は両辺に値を要求するため、下記の例では「v[1] = 5 + v[3]」ときちんと解釈してくれる。
v[1] = 5 + v[3] // v[1] = 5 + v[3]
ただし、+演算子を二行目に移動させた場合、これは+v[3]と単独で解釈されてしまう。
v[1] = 5 // v[1] = 5 + v[3] // v[0] = v[3]
値の正負の意味もある+/-ではエラーまでは起きないが、それ以外の二項演算子の場合はエラーが発生しコンパイル自体が失敗する。
v[1] = 5 * v[3] // エラー : 「bend」が必要です
下記のように非常に長い条件文などは、二項論理演算子ごとに改行で区切ると見やすくなる。
@if `(v[1] + v[2] / 3) % 2 == rnd(0, 1) && v[3] * 3 - 1 < v[4] * 5 / 2 && max(v[5] + v[6], v[7] * 5) < sys.tick bl { // 条件一致時の処理 }
文字列変数で改行を扱うには、そのまま文字列内で改行を行うだけでよい。
@msg.show "改 行" t[1] .asg "改 行"
テキストエディタでは自動でインデントをしてくれるものがあるが、インデントを消さないと空白が入ることに注意。
@if v[1] == 1 bl { t[1] .asg "改 行" }
改行文字のみを個別に持っておくと扱いやすいかもしれない(文章の表示のみ .br で改行可能)。
// 定数定義を使う場合 def kaigyo = " " // 文字列変数に代入しておく場合 t[500] .asg " " def kaigyo_t = t[500] t[1] .asg __str( "改" kaigyo "行" ) t[1] .asg __str( "改\" kaigyo_t "行" ) // 文字列変数に格納しておく場合は展開に \ が必要 @msg.show "a" kaigyo "b" kaigyo_t "c" .br "d" // 文章の表示では \ は必要ない(__strではないため)
文字列内でダブルクォート ( " ) を利用する
ダブルクォート二つ、あるいは「」で括ったものは文字列として扱われる。
このとき、ダブルクォートそのものを文字列内で文字として使いたい場合、
ダブルクォートを二連続で使うか、ダブルクォートの代わりに「」で括る必要がある。
t[1] .asg """a""" t[1] .asg 「"a"」
上記の例では、どちらもt[1]には"a"という文字列が代入される。
エラーが出たら
コンパイル時に文法ミスがある場合にTPCはエラーを出力してくれる。
「未定義です:***」
予約語 (文法上使用するキーワード) に属さず、かつ何らかのリテラル (数値、文字列、式) に属さない文字列が
ソースコード内に存在する場合、このエラーが発生する。
定数として定義していない値に代入などの操作を行おうとした場合に発生するので、定義部分をよく見直してみること。
// defv qwe = 1 などと記述するとエラーは消える qwe = 1 // -> 未定義です: qwe
「bendが必要です」
bendとはブロックの終端を示すbracket end ( } ) のこと。
ブラケットの始端と終端の記号が対になっておらず、ネストが正常になっていない場合に発生する。
このエラーで指示される行は仕様上ソースコードの終端になるため、変更箇所を見失うと修正が困難になりやすいので注意。
また、ブラケット内に演算子などの記号がある場合にもこのエラーは発生する。
「不明な要素: ***」
文法上意味のある記号が正常な用途で置かれていない場合に発生する。
例えば「)」を単独で置くと「不明な要素: parr」と表示される (parrはparenthesis rightの略) 。
) // -> 不明な要素: parr // 括弧は対にする必要がある } // -> 不明な要素: bend * // -> 不明な要素: mul // 演算子の左辺あるいは右辺がない ^ // -> 不明な要素: xor = // -> 不明な要素: asg
コメント
- 記事大変参考になります。ありがとうございます。こちらの方法で導入したのですが、テストプレイをするとゲーム内の日本語が文字化けしてしまいます。(エディターは日本語化されている)パッチをあてた際に「正常終了」の表示後に「コピーに失敗しました 元:~~略~~\patch_jp_im\RPG_RT.exe.dat」と表示され、いくつかのファイルがコピーされていないのですが、それが関係あるのでしょうか。 -- maker? 2023-11-20 (月) 00:12:44
- おそらくRPG_RT.exeにパッチが当たらず古いままになっていると思われます。手動コピーしてみてください。[RPG_RT.exe.dat」を「RPG_RT.exe」にリネームすれば実行ファイルになるはずなのでそれを入れたいプロジェクトのフォルダへコピーし実行ファイル上書きするだけです。 -- 2023-11-21 (火) 01:50:44