小ワザ|Math?
RPG等のゲーム製作の副産物、と言うより壁。これを解決しないと条件依存型のイベントを製作修正していくのが著しく大変。要するにお使いイベントの修正すら困難を極める事になる。
0.現状 †
現在、文字列計算モジュールの第3段階が完成しました。モジュールの試験にご協力ください。(23/10/04)
☆試験方法
下のファイルをダウンロードして解凍した後、中の実行ファイルを実行してください。
左のテキストエディタにHSP風の代入式を入力してから、ボタンを押すと、右のテキストエディタに変数の状態を示した結果が表示されます。
仕様等については『2−3−0.現版の仕様』を御覧ください。
他に実装した方が良さそうな機能がありましたら、コメント欄にご記入ください。
不具合やバグ等がありましたら、以下のコメント欄に記録してくださるとありがたいです。
- A123とかb123とだけ書くと0と代入されるけど=123とか-123とkaだけ書くと123と代入される -- hexa.hemi?
☆前回からの更新点
――Tranche2からの修正点――
- 代入式を解析してVarlist型モジュール変数の内部状態を更新する[set_formula_substitude]命令を実装
- 上記に連動して[get_formula_anser]と[iget_formula_separate]の両関数の引数にVarlist型モジュール変数を追加
- 16進数表記の$と0x、先端1バイト表示の''の形式を実装
――Tranche1からの修正点――
- 関数の一部を実装、現在HSP標準関数18種、独自関数2種、の計20種を実装
- 文字列返却型関数[strmid,strtrim]
- 小数値返却型関数[expf,logf,sqrt]
- 整数値返却型関数[vartype,strlen,instr]
- 整数小数変動型関数[abs,rnd,limit,min,max]
- 三角関数(小数値返却)[atan,sin,cos,tan]
- 型変換関数[str,double,int]
- 負の実数を記述しようとした場合、整数化してしまう不具合を修正
「数式の文字列化? 何のこっちゃい??」
……と、思う方もいらっしゃると思うので、ここで少し説明します。が、言葉で説明するのは少し面倒なので実例を上げます。以下がその実例
+
| | 普通の場合
|
1
2
3
4
5
6
|
| a = "円周率の近似値は"+(355.0/113)+"です"
mes a
|
|
+
| | 文字列化した場合
|
1
2
3
4
5
|
| a = get_formula_answer("\"円周率の近似値は\"+(355.0/113)+\"です\"")
mes a
|
|
御覧の通り、後者は数式を文字通り文字列化しています。(これで分からなかったらごめんなさい。これ以上に上手くは説明できません)
これが、『数式を文字列化する』という事です。
しかし、ここで疑問に思う方も多いでしょう。
「でも、数式を文字列化しなくても、普通に数式を使えば十分に事足りるよね。文字列形式にする意味なんて無いじゃん」
はい、おっしゃる通り。本来ならこんな事する必要は全くありません。こんな珍妙な事をする状況は限られます。
その状況とはRPGゲームの製作です。一例をあげます。
RPGには膨大な量のイベントがあります。マップ変更、台詞表示、バトル突入、色々とあります。
今回、試しにメッセージ表示をHSP上で簡単に実装してみる事にします。
+
| | 実装例その1
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
| #include "hsp3util.as"
台詞表示命令0001
台詞表示命令0002
stop
#deffunc 台詞表示命令0001 local br
gradf 0, 400, 640, 480, 0, $FF00FF, $FF80FF
color 64, 0, 64
font msmincho, 20, 1
pos 64, 400
mes "雷電"
text 100
color 128, 128, 128
textmode 1
color
font msgothic, 20, 3
pos 0, 420
emes "「ほう、中々筋がいいぞ」"
repeat
getkey br, 13
if br : break
wait 10
loop
return
#deffunc 台詞表示命令0002 local br
gradf 0, 400, 640, 480, 0, $00FF00, $80FF80
color 0, 96, 0
font msmincho, 20, 1
pos 64, 400
mes "太刀風"
text 100
color 128, 128, 128
textmode 1
color
font msgothic, 20, 3
pos 0, 420
emes "「こりゃあ、まいった\n あ、降参、降参」"
repeat
getkey br, 13
if br : break
wait 10
loop
return
|
|
見て分かる通り2つ台詞を流すだけでも結構な文量になります。
こんなのを台詞の数だけ一文一文毎に複写していくのでは面倒以前に管理が追い付きません。
ここは共通部分を統合し、各々に違っている部分は引数を介して調整する手法で処理します。
+
| | 実装例その2
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
| #include "hsp3util.as"
汎用台詞表示命令 $FF00FF, $FF80FF, $400040, "雷電", "「ほう、中々筋がいいぞ」"
汎用台詞表示命令 $00FF00, $80FF80, $006000, "太刀風", "「こりゃあ、まいった\n あ、降参、降参」"
stop
#deffunc 汎用台詞表示命令 int grad1, int grad2, int namecol, str name, str sent \
, local br
gradf 0, 400, 640, 480, 0, grad1, grad2
color (namecol&$FF0000)>>16, (namecol&$00FF00)>>8, namecol&$0000FF
font msmincho, 20, 1
pos 64, 400
mes name
text 100
color 128, 128, 128
textmode 1
color
font msgothic, 20, 3
pos 0, 420
emes sent
repeat
getkey br, 13
if br : break
wait 10
loop
return
|
|
文量を4割近く削れました。台詞の数が10、20、100、200と増えるにつれ、この圧縮は更に活きていきます。
ここで上記のスクリプトを更に修正してみます。
+
| | 実装例その3
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
| #include "hsp3util.as"
note = "$FF00FF,$FF80FF,$400040,雷電,「ほう、中々筋がいいぞ」ぎぇぴー"
note += "$00FF00,$80FF80,$006000,太刀風,「こりゃあ、まいった\n あ、降参、降参」"
split note, "ぎぇぴー", event_list
repeat length(event_list)
汎用台詞表示命令改 event_list(cnt)
loop
stop
#deffunc 汎用台詞表示命令改 str command, local m, local cmd, local br
m = command
split m, ",", cmd
gradf 0, 400, 640, 480, 0, int(cmd(0)), int(cmd(1))
color (int(cmd(2))&$FF0000)>>16, (int(cmd(2))&$00FF00)>>8, int(cmd(2))&$0000FF
font msmincho, 20, 1
pos 64, 400
mes cmd(3)
text 100
color 128, 128, 128
textmode 1
color
font msgothic, 20, 3
pos 0, 420
emes cmd(4)
repeat
getkey br, 13
if br : break
wait 10
loop
return
|
|
今回はsplit命令を利用して命令に与える引数を文字列に一本化しました。
一見すると前回より文量が増えているように見えますし事実増えていますが、その代償に命令の規格を文字列型に統一できました。
これの利点は、複数の異なる命令を一つの文章に連続して記載できる点にあります。例えば、こんな事もできるわけです。
+
| | 実装例その4
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
| #include "hsp3util.as"
note = "背景変更,$0000FF,$8000FF,$0080FF,$4040FFぎぇぴー"
note += "待機時間,5ぎぇぴー"
note += "台詞表示,$FF00FF,$FF80FF,$400040,雷電,「ほう、中々筋がいいぞ」ぎぇぴー"
note += "待機時間,5ぎぇぴー"
note += "背景変更,$FF0000,$FF4040,$FF8000,$FF0080ぎぇぴー"
note += "待機時間,5ぎぇぴー"
note += "台詞表示,$00FF00,$80FF80,$006000,太刀風,「こりゃあ、まいった\n あ、降参、降参」ぎぇぴー"
note += "待機時間,5ぎぇぴー"
note += "背景変更,$00FF00,$80FF00,$40FF40,$00FF80"
split note, "ぎぇぴー", event_list
repeat length(event_list)
split event_list(cnt), ",", elem
switch elem(0)
case "台詞表示"
汎用台詞表示命令 int(elem(1)), int(elem(2)), int(elem(3)), elem(4), elem(5)
swbreak
case "待機時間"
待機時間命令 int(elem(1))
swbreak
case "背景変更"
背景変更命令 int(elem(1)), int(elem(2)), int(elem(3)), int(elem(4))
swbreak
swend
loop
stop
#deffunc 背景変更命令 int c1, int c2, int c3, int c4, local c, local x, local y
x = 0, 640, 640, 0
y = 0, 0, 480, 480
c = c1, c2, c3, c4
gsquare gsquare_grad, x, y, c
return
#deffunc 待機時間命令 int time
wait time*10
return
#deffunc 汎用台詞表示命令 int grad1, int grad2, int namecol, str name, str sent \
, local br
gradf 0, 400, 640, 480, 0, grad1, grad2
color (namecol&$FF0000)>>16, (namecol&$00FF00)>>8, namecol&$0000FF
font msmincho, 20, 1
pos 64, 400
mes name
text 100
color 128, 128, 128
textmode 1
color
font msgothic, 20, 3
pos 0, 420
emes sent
repeat
getkey br, 13
if br : break
wait 10
loop
return
|
|
実質指示を全て文字列にした事で、全く挙動の違う命令に対して同じ形式で指示を与える事ができるようになります。
なので、スクリプト自体をいじくらずとも、スクリプトに与えてやる指令書を書き換えるだけで挙動を修正できるようになります。
今回は指令書をスクリプトに内蔵させましたが、notesel〜noteload構文を利用して外部のテキストデータを指令書にする事もできます。
また、指令書をスクリプトから抜き出した事で、指令書の修正も容易になります。指令書の作成、つまりイベントの作成を補助するエディタも作り易くなります。
前述のスクリプトではsplit命令とint関数を利用して比較的容易にイベント指示を文字列化できました。
しかし、条件分岐命令はこの2つの命令と関数だけで文字列化する事ができません。
条件分岐命令が指令書に書けないとなると、条件分岐命令を含む部分だけは、絶対にスクリプト内部に置いておかなくてはなりません。
そうなると、イベントの発生条件を定義するにも条件分岐が必要ですから、新しいイベントを作る度にスクリプトに手を入れなければなりません。
他の部分は全て指令書でどうにかなるのに、条件分岐命令だけが指令書に織り込めないばかりに面倒が増していく。
そんな事が起きる為、RPGを作る上では条件分岐命令、つまりその条件分岐を表現する数式の文字列化が重大な課題になるわけです。
上記で述べた通り、数式の文字列化は重要ですが、重要だからといって勝手に文字列化が成功するわけではありません。
ある程度の工程を組んで、段階を踏んで、システムを作っていく必要があります。
この節ではその工程をどう組んでいくかを考えます。
まず第一段階として、箇条書きに数式文字列化に必要な要素を書き出していきます。
- 括弧 『()』で囲まれた部分を優先的に処理する仕組み
- 演算子 『A?B』の形で処理される演算法則
- 関数 『?(A0,A1,A2,,,,An)』の形で処理される演算法則
- 変数 スクリプト内部の変数データへのアクセス、開きたい
- 種類の判別 文字列か小数値か整数値か変数かの判別
- 優先度 関数や演算子の優先度
- 数値配列 あると便利そう。変数さえ実装できればHSPの数値配列を流用できそう
- 連想配列 八州値と二分検索法で実装?
- マクロ あると便利そう
- 構造体orクラス 便利そうだがそんなに需要は無い気もする
- システム変数 スクリプト内部の変数データをシステム変数として閉じたい
- 樹構造 非常に便利そう。ダンジョン->仕掛け1->スイッチ1〜3みたいに定義できれば整理が楽そう
他にも必要そうな物があったら以下のコメント欄にコメントしてもらえると助かります。
- 木構造は入れ子の連想配列として再現できますし、構造体も連想配列を用いることで再現できるため、連想配列があればほぼ全て事足りるでしょう。キーと値のペアがソートされない連想配列型はスタックやキュー(デキューも可)として扱え、更にキーのみで考えればセットとしても使うことが出来る非常に便利な型の一つです。 -- dist?
あらかた必要そうな要素が出揃ったら、それぞれの繋がりを把握して、製作順番決定の判断材料にします。
まず、括弧と関数が似ています。どちらも、いかなる演算子よりも最優先で処理されます。よって、まず括弧の最優先処理を作り、その最優先処理を流用して関数の処理を作った方が良いでしょう。
続いて、演算子ですが、この処理自体は演算子を検索してその前後同士を演算すれば良いので非常に簡単です。ですが、逆ポーランドでも無い限り演算子同士の順位は複雑に絡み合っているので、まず優先順位を決める処理を作る必要があります。また、演算子の中には[<<]と[<]、[!=]と[=]のように単純検索では紛らわしい物もあるので、最初に置換処理を考えた方が良さそうです。
変数の処理は比較的関数に似ている気がします。問題は、括弧前の文字列をどう判別するかですが、登録されている関数名以外は全て変数と考えるようにすれば問題無い気がします。よって、変数より関数の方を先に作るべきで、また当然ながら開いた変数より閉じたシステム変数の実装の方が優先されるべきと思われます。
数値配列、連想配列、構造体、クラス、樹構造。変数の拡張という意味で似通ったこれらの内どれを実装すべきかですが、数値配列と樹構造さえあれば大概の物はどうにかなる気がします。樹構造の方が面倒そうなので、HSPに流用元のある数値配列から実装する事にします。
最後に残った種類の判別ですが、式を演算子でバラバラにすれば、各要素は文字列、数値、括弧乃至関数乃至変数のどれかに分類されるはずです。括弧乃至関数乃至変数なら尻尾が『)』になり、小数値なら内部のどこかに『.』があるはずなので区別は容易なはずです。問題は文字列と整数値の区別ですが、一番最初の段階で文字列を遊離する事ができれば、遊離されていない物は整数値と消去法で判別する事ができるはずです。よって、種類の判別では文字列の遊離が優先されます。
前述の考察から暫定的に以下のような工程表を組み立てます。
- Tranche1 演算子、括弧、優先順位の実装
- Tranche2 括弧の処理を拡張して関数の実装
- Tranche3 関数の処理を拡張してシステム変数の実装
- Tranche4 システム変数の処理を拡張して変数の実装
- Tranche5 演算子の処理を拡張してマクロの実装
- Tranche6 変数の処理を拡張して配列変数の実装
- Tranche7 配列変数の処理を拡張して変数の樹構造(構造体気味?)の実装
以下ではこの工程表に従って文字列化のモジュールを作っていきます。
関数を実装した際にスクリプトが更に肥大化した為、直にページへスクリプトを記載する方式は止めました。
スクリプトが欲しい方は、リンクを使ってダウンロードしてください。
HSP形式の式を放り込むと値を返してくれる関数[get_formula_answer]と、同形式の代入式を放り込むと所定のvarlist型モジュール変数の内部状態を更新する[set_formula_substitude]命令があります。
変数に使える文字は半角英大小文字と半角数字、そして『_』の63種類に加えShift_JISで定義されている全角文字全てです。
HSPと同様に変数の名前の頭に半角数字を使う事はできません。
HSPと違い、大文字と小文字が区別されます。
代入式の区切りはHSPと同じく『:(半角コロン)』と『\n(改行)』で行っています。
代入は『=』や『==』のような通常代入の他に四則剰余算の特殊代入を実装しています。『+』や『+=』や『+==』のように、演算子+[=]×0〜2の形の特殊代入を『+-*/\\』の四則余について実装しています。
エラーを吐く所までは実装していないので、不正な入力をしても普通におかしな値を返してしまいます。
現時点(Tranche3)で配列変数はまだ実装していません。
変数内容はHSPの実際の変数の内容を変えているわけではありません。
あらかじめ、Varlist型という独自の変数状態を記憶するモジュール変数を定義しておき、そのモジュール変数の内部状態を変更する事で擬似的に変数っぽい挙動をさせています。
現時点で使える関数は
- 整数値返却型関数[vartype,strlen,instr]
- 整数小数変動型関数[abs,rnd,limit,min,max]
- 三角関数(小数値返却)[atan,sin,cos,tan]
……の、20種類です。
返り値が整数と小数で変動するものは引数の初項の型で返り値を決めます。vartype関数で与えられる型が3(=double型)なら小数値を、それ以外なら整数値を返します。
HSPに存在しないmin、maxの両関数はExcelの同名関数と同じような挙動をするはずです。
現時点で使える演算子は[Lv1:&,&&,|,||,^]、[Lv2:=,==,!,!=,<,<=,>,>=]、[Lv2.5:<<,>>]、[Lv3:+,-]、[Lv4:*,/,\]です。
演算優先度はHSPと同じく『論理<比較<ビット<加減<乗除余』の通りです。
同レベル同士の演算子は左に優先権が付くようにしてあります。
文字列はHSPの通り『"』で囲えば判別してくれるはずです。
式中の半角空白とタブは無視されます。
改行は代入分の区切りとしての意味合いに変更されました。『:』も同様です。
括弧にも対応しているはずですが、括弧が非対称だとしてもエラーなどは返してくれないはずなので注意してください。
元として認められるのは文字列、小数値、整数値の3種類のみです。『"』で囲まれている元は文字列、式中に『.』がある元は小数値、それ以外は整数値として扱われているはずです。
『@』と『ぎぇぴー』はモジュール内部で特別な意味を与えてあるので使わないでください。
その他、何かありましたら、以下のコメント欄にご記入ください。
コメントはありません。 コメント/Math/文字列化した数式の判別?