Thymeは初心者やプログラム経験のない方にとってはハードルが高い。思いつきではあるが、ここにThyme講座を書きなぐっていきたいと思う。
尚思いつきかつ、筆者も独学にちかい部分があるため、不明瞭な部分や間違い等があると予想される。
そのような部分を発見した場合、コメントにて指摘いただけるとありがたい。
まずスクリプトがどのようなものなのか、見ていただこう。
物質のテキスト文章部分にシミュレーション開始からの経過時間を表示させるスクリプト
text = { "" + sim.time };
常に物質の色を変え続けるスクリプト
color = { [ sim.time % 1.0 , sim.time * 0.5 % 1.0 , sim.time * 0.3 % 1.0 , 1.0 ] };
このような1処理分の塊を「式」という。
式1つは「○○○ = ○○○」の形をとる。
ただし、スクリプトメニューをよく見ると「text =」や「color =」は既に記述されている。
このため、1つ目を例を実際に使用したい場合は「{ "" + sim.time }」を「text =」項目の内容部分に記述すれば良い。
尚、各例文の最後についてある「;」は「式がここで終わり」という意味の記号である
1つの式のみをスクリプトメニューに記載した場合、決定した時に「;」が消えてしまうが、これはPhunの仕様である。
どうやら、「式の数にかかわらず、最後の;は省略する」というルールが存在しているようである。
この解説においては、式の数にかかわらず;を表記すると、断りを入れておく。
Thymeスクリプトを実行出来る場所は2箇所ある。
スクリプト言語Thymeのページには、onCollide属性内以外への記述については特に述べられていないが
書式さえ守ればどの属性内にも直接スクリプトを記述できる。
onCollide以外の属性へ書き込む場合の注意点としては以下の2つ。
式に用いる数値や文字のことを総称して値(あたい)と言う。値には定数と変数の2種がある。
行番号 | 内容 | 説明 |
1行目 | x := 3; | xという変数を内容「 3 」で作成 |
2行目 | x = x + 4 - 5; | xに『 x + 4 - 5 』の計算結果を入れる |
3行目 | y := x + x; | yという変数を内容『 x + x 』で作成 |
4行目 | y = x + y; | yに『 x + y 』の計算結果を入れる |
1行目
x := 3;
「xを3という内容で作成します」という意味の式。
変数を始めに作成する演算子「:=」を用いている。
変数には必ず初期値が必要であり、この場合は「3」である。
2行目
x = x + 4 - 5;
「xに『 x + 4 - 5 』の計算結果を入れる」という意味の式。
変数の値を変更する演算子「=」を用いている。
xの値は3であるため『x+4-5』は2となり、xの値が2に変更される。
3行目
y := x + x;
「yという変数を内容『 x + x 』で作成します」という意味の式。
初期値に「すでに作成済みである変数を用いた計算式」を使用しても問題ない。
つまりこの式は「 y := 2 + 2; 」と同義であり、yの初期値は4になる。
4行目
y = x + y;
「yに『 x + y 』の計算結果を入れる」という意味の式。
xは2でありyは4であるため、この式は「y= 2 + 4;」と同義である。
結果としてyの値は6になる。
Scene.my.x := 3; Scene.my.x = Scene.my.x + 4 - 5; Scene.my.y := Scene.my.x + Scene.my.x; Scene.my.y = Scene.my.x + Scene.my.y;
変数の種類として「配列」がある。
変数は1つに対して1つの値しか持てないが、配列は複数個の値を持つことが出来る。
イメージとしては「1つの名前を共有する複数の箱があり、それは番号で区別される」といったものである。
実際に配列を使用している例を以下に示す。
Scene.my.x := [ 1 , 5 , 10 , 999 , 7 ]; Scene.my.y := Scene.my.x(0); Scene.my.z := Scene.my.x(2) + Scene.my.x(3);
順を追って解説していこう。
scene.my.x(0) | scene.my.x(1) | scene.my.x(2) | scene.my.x(3) | scene.my.x(4) |
1 | 5 | 10 | 999 | 7 |
尚、配列の内容を書き換える際には注意が必要である。
scene.my.x := [ 1 , 2 , 3 ]; scene.my.x(2) = 99;
上記のスクリプトは「scene.my.x の2番目の内容を99に書き換える」ことを意図したものであるが
これはその意図通りには動かない。
配列の中身を部分的に書き換える際には、非常に面倒くさい式になるが
scene.my.x := [ 1 , 2 , 3 ]; scene.my.x = [ scene.my.x(0) , scene.my.x(1) , 99 ];
と、このように書くしかない。
面倒な話だが、これはどうしても避けて通れない
スクリプトで扱う値は4つに分類されている。 分類のされ方は以下のとおり
型名 | 内容 | 例 |
文字列型 | 1文字以上の文字列 | "" "A" "B" "AABB" "あいうえお" |
浮動小数点型 | 小数点以下を含む数字 | 0.0 1.5 1.0001 10000.0 |
整数型 | 小数点以下がない数字 | 0 1 100 10000 |
ブール型 | 正か偽を表現 | true false |
以下に物質のスクリプトメニュー内の各項目について型を示す。
項目名 | データ型 |
airFrictionrMult = | 浮動小数点 |
attraction = | 浮動小数点 |
collideSet = | 整数 |
collideWater = | ブール |
color = | 浮動小数点(配列、要素数4) |
controllerAcc = | 浮動小数点 |
controllerInvertX = | ブール |
controllerInvertY = | ブール |
controllerReverseXY = | ブール |
density = | 浮動小数点 |
drawBorder = | ブール |
friction = | 浮動小数点 |
heteroCollide = | ブール |
immortal = | ブール |
killer = | ブール |
onCollide = | 関数 |
opaqueBorders = | ブール |
restitution = | 浮動小数点 |
ruler = | ブール |
text = | 文字列 |
textColor = | 浮動小数点(配列、要素数4) |
textScale | 浮動小数点 |
texture | 文字列 |
textureMatrix | 浮動小数点(配列、要素数9) |
式には細かな処理の順番がある。
たとえば「 x = 1 + 2 + 3 + 4 + 5; 」という式は
「 x = (((( 1 + 2) + 3 ) + 4 ) + 5 ); 」この括弧のもっとも内側から順に処理される。
型にも優先順位があり、以下のようになっている。
式 | xに入る値 | 結果のデータ型 |
x = 1 + 2 + 3; | 6 | 整数 |
x = 1 + 2 + 3.0; | 6.0 | 浮動小数点 |
式 | xに入る値 | 結果のデータ型 |
x = 1 + "A"; | "1A" | 文字列 |
x = 1.0 + "A"; | "1A" | 文字列 |
x = "A" + 1.0; | "A1" | 文字列 |
式 | xに入る値 | 結果のデータ型 |
x = 1 + 2 + 3 + "A"; | "6A" | 文字列 |
x = 1 + "A" + 2 + 3; | "1A23" | 文字列 |
式 | xに入る値 | 結果のデータ型 |
x = 1 + 2.2 + "A" + "B"; | "3.2AB" | 文字列 |
x = 1 + 2.2 + "A" + true; | "3.2Atrue" | 文字列 |
式 | xに入る値 | 結果のデータ型 |
x = 1 + true; | (演算不可) | - |
x = 1 + "" + true; | "1ture" | 文字列 |
x = true + 1.0; | (演算不可) | - |
x = true + "" + 1.0; | "true1" | 文字列 |
x = true + true + "" + 1 | (演算不可) | - |
x = true + "" + true + 1 | "truetrue1" | 文字列 |
演算に使用する記号を総称して「演算子」と呼ぶ。
演算子 | 意味 | 例文 | 演算結果 |
+ | 加算 | 3 + 4 | 7 |
3 + 4.0 | 7.0 | ||
- | 減算 | 5 - 2 | 3 |
5 - 2.0 | 3.0 | ||
* | 乗算 | 4 * 5 | 20 |
4 * 5.0 | 20.0 | ||
/ | 除算 | 8 / 4 | 2 |
8 / 5 | 1 | ||
8 / 5.0 | 1.6 | ||
% | 剰余 | 8 % 5 | 3 |
8 % 5.0 | 3.0 | ||
^ | 累乗 | 2 ^ 3 | 8 |
10 ^ 3 | 1000 |
演算子 | 意味 | 例文 | 演算結果 |
== | 等しい | 1 == 1 | true |
1 == 2 | false | ||
!= | 等しくない | 1 != 1 | false |
1 != 2 | true | ||
> | 大なり | 1 > 2 | false |
2 > 1 | true | ||
< | 小なり | 1 < 2 | true |
2 < 1 | false | ||
>= | 以上 | 1 >= 1 | true |
1 >= 2 | false | ||
<= | 以下 | 1 <= 1 | true |
2 <= 1 | false |
演算子 | 意味 | 例文 | 演算結果 |
+ | 加算 | "ABC" + "DE" | "ABCDE" |
== | 等しい | "AB" == "AB" | true |
!= | 等しくない | "AB" != "ab" | true |
細かい話は抜きにして「ブール型にのみ用いられる演算でありその結果もすべてブール型である」と覚えておけば良い。
true=真 か false=偽 を判断する際に用いる。
以下の表が、その演算内容のすべてである。
演算子 | 意味 | 例文 | 演算結果 |
&& | 論理積(and) | true && true | true |
true && false | false | ||
false && true | false | ||
false && false | false | ||
|| | 論理和(or) | true || true | true |
true || false | true | ||
false || true | true | ||
false||false | false | ||
! | 否定(not) | !true | false |
!false | true |
演算子 | 意味 | 例文 | 演算結果 |
+ | 加算 | [1,2,3] + [1,3,5] | [2,5,8] |
- | 減算 | [4,5,6] - [1,2,3] | [3,3,3] |
* | 乗算 | [1,2,3] * [2,3,4] | [2,6,12] |
/ | 除算 | [12,12,12] / [2,3,4] | [6,4,3] |
&& | 論理積 | [true,true,false] && [true,false,true] | [true,false,false] |
|| | 論理和 | [true,true,false]||[true,false,false] | [true,true,false] |
演算子 | 意味 | 例文 | 演算結果 |
* | 乗算 | 2 * [1,2,3] | [2,4,6] |
* | [2,3,4] * 2 | [4,6,8] | |
/ | 除算 | 2 / [1,2,3] | (error) |
/ | [2,4,8] / 2 | [1,2,3] | |
== | 等しい | [1,2,3] == [1,2,3] | true |
!= | 等しくない | [1,2,3] != [1,2,3] | false |
++ | 結合 | [1,2,3] ++ [4,5,6] | [1,2,3,4,5,6] |
スクリプト内で使用できる変数は「そのスクリプトがどこに書かれているものか」によって変わる。
それらを3つに分類し、それぞれについて使用可能な変数をまとめたものが以下。
表記 | 意味 |
decimal | 十進数 |
integer | 整数 |
value | 値 |
positive | 正の(0より大きい) |
true or false | 真か偽 |
function | 関数(※後述参照) |
array of | 配列である |
string | 文字列 |
constant | 定数 |
sub-group | 内部に変数や関数を持つグループ名 |
Read only | 読み取りのみ可能(値を変更不可) |
これ抜きには複雑なThymeスクリプトなど組めない、と言っても過言ではない。
ある変数Aがあり、Aが1ならば処理1を実行し、Aが2なら処理2を実行する。端的に言うとそういうことである。
何はともあれ実例を見ていただきたい
scene.my.x := 2; scene.my.y := ( scene.my.x == 3 )?( scene.my.x + 3 ):( scene.my.x + 10 );
1行目は特に解説の必要はないと思われる。
2行目はscene.my.y変数を作成しているのだが、その中身に条件分岐を用いている。
2行目の「:=」以降の部分が条件分岐構造になっており
「 『scene.my.x==3』が真ならば『scene.my.x + 3』、そうでなければ『scene.my.x + 10』 」という意味である。
この場合 scene.my.x は 2 なので、『scene.my.x + 10』が選ばれ、scene.my.yの値は12となる。
先ほどの例で条件分岐のために用いた演算子を「三項演算子」と言う。
使用方法は以下のとおりである。
判定条件 ? 条件が真の場合の処理内容 : 条件が偽の場合の処理内容
(scene.my.x == 2) ? scene.my.y = 5; scene.my.z = 3; : scene.my.y = 1; scene.my.z = 10; ; (×エラー) (scene.my.x == 2) ?{ scene.my.y = 5; scene.my.z = 3 }:{ scene.my.y = 1; scene.my.z = 10 }; (○正常動作)
onCollide = (e) =>{ e.this.density = (( e.other.collideSet == 2 )&&( e.other.density == 100.0 ))?{ 50.0 }:{ 2.0 }; }条件分岐部分は「『衝突相手の衝突判定がBのみであり、かつ密度が100.0』ならば 50.0 、違うならば 2.0 」という意味である。
さて、だいぶ小難しい話になるところなので、腰を落ち着けてじっくりと読んでいただきたい。
「関数」といきなり言っても通じないであろうから、まずPhun抜きにして以下の事例をごらんいただこう。
「引数(ひきすう)」とは「Aを頼む際には大きな書類2枚と小さな書類1枚を渡すよ」といった場合の、その書類である。
関数を実行する際には「0個以上の引数」が必要である。これは関数を作成する際に取り決める。
「戻り値(もどりち)」とは「仕事が終わったら、その完成品を渡してね」といった場合の、その完成品である。
関数は「必ず1つの戻り値」を持つ。
それでは、実際に関数を作成し、使用する例を見てみよう
Scene.my.func1 := (var) => { var % 2 == 0 ? true : false }; Scene.my.x = Scene.my.func1(2);
1行目で関数を製作し、2行目で使用している。
関数の内容としては「引数が偶数ならばtrue、奇数ならfalseを戻り値とする」である。
つまりは偶数か奇数かを判定するための関数だ。
関数の書式は以下のとおりである
関数名 = ( 引数名 ) => { 処理内容 }
◇関数 scene.my.triangle := ( width , height ) => { width * height * 0.5 }; ◇使用例 scene.my.x = scene.my.triangle( 3.0 , 5.0 );scene.my.x には 3.0 * 5.0 * 0.5 の演算結果 7.5 が入る。
◇関数 scene.my.blink := () => { sim.time % 1.0 >= 0.5 ? true : false }; ◇使用例 onCollide = (e) =>{ text = { "" + ( scene.my.blink ? "Yes" : "No" ) }; }書かれた物質のtext属性値が0.5秒ごとに "Yes" と "No" 交互に変わる。
あらかじめPhunのシステム上に作成されていて、使用可能な関数はいくつもある。
かなりの量であるが、実際に使用する可能性のあるものは限られる。
使用頻度の高い関数をいくつか書き留めておこう。
関数名 | 引数 | 戻り値 |
math.asin(x) | 浮動小数点型 | 逆三角関数 arcsin(x)の値 |
math.acos(x) | 浮動小数点型 | 逆三角関数 arccos(x)の値 |
math.atan(x) | 浮動小数点型 | 逆三角関数 arctan(x)の値 |
math.sin(x) | 浮動小数点型、単位は[rad] | 三角関数 sin(x)の値 |
math.cos(x) | 浮動小数点型、単位は[rad] | 三角関数 cos(x)の値 |
math.tan(x) | 浮動小数点型、単位は[rad] | 三角関数 tan(x)の値 |
関数名 | 引数 | 戻り値 |
scene.addBox{ x } | (後述) | "box" |
scene.addCircle{ x } | (後述) | "circle" |
scene.addFixjoint{ x } | (後述) | "fixjoint" |
scene.addHinge{ x } | (後述) | "hinge" |
scene.addPen{ x } | (後述) | "pen" |
scene.addPlane{ x } | (後述) | "plane" |
scene.addPolygon{ x } | (後述) | "polygon" |
scene.addSpring{ x } | (後述) | "spring" |
scene.addWater{ x } | (後述) | "water" |
scene.EraseWater | (なし) | (なし) |
scene.addBox{ pos = [ 10.0 , 25.5 ]; size = [ 15.0 , 20.0 ]; density = 10.0 };
scene.addBox{ pos = [ 0.0 , 0.0 ]; size = [ 6.0 , 2.0 ]; geomID = 100; }; scene.addCircle{ pos = [ 2.0 , -0.5 ]; radius = 0.8; geomID = 101 }; scene.addCircle{ pos = [-2.0 , -0.5 ]; radius = 0.8; geomID = 102 }; scene.addHinge{ geom0 = 100; geom0pos = [ 2.0 , -0.5 ]; geom1 = 101; geom1pos = [ 0.0 , 0.0 ] }; scene.addHinge{ geom0 = 100; geom0pos = [-2.0 , -0.5 ]; geom1 = 102; geom1pos = [ 0.0 , 0.0 ] };
※追記 Thymeで物体を発生させる場合、速度と角速度も指定できる。両方共に「じょうほう」メニューで表示されている単位(速度 m/s, 角速度 rad/s)で指定する。
scene.addBox({vel := [20.0, 10.0]; angVel := 30});
配列の解説部分で作った配列のような1列しかない配列は、1次元配列と言う。
1次元という呼び名があるからには、2次元配列ももちろん存在する。
作成の仕方と使用の仕方を以下に示す。
scene.my.x := [ [1,2,3] , [4,5,6] ];
これが2行3列の2次元配列である。
配列の0番目の値として「要素3の配列」が入っており、1番目の値にも「要素3の配列」が入っている。
これを使用する際には以下のように書く
scene.my.y := scene.my.x(0)(1);
scene.my.xの0行目の1番目を指定している。
この時scene.my.yに入る値は「2」である。
scene.my.xの内容を視覚的にわかりやすく表にまとめると、以下のようになる。
\ | B=0 | B=1 | B=2 |
A=0 | 1 | 2 | 3 |
A=1 | 4 | 5 | 6 |
ちなみに、2次元配列を作る際に、列の値は必ずしも全て揃える必要はない。
更に、データ型はそれぞれの要素ごとに記録されるため、揃える必要はない。(これは1次元配列でも同じく)
つまり、以下のような配列も生成可能である。
scene.my.x := [ [ 11 , 12 , 13 ],[ "welcome" , 2 , "phun"],[ 21 , 22 , 23 , 24 , 25 ] ]; scene.my.y := [ [ "one" , 2 , "three" ],"welcome",[ true , "or" , false ],[ "Let's" , "phun" ] ];
それぞれ内容は以下のようになる。
\ | B=0 | B=1 | B=2 | B=3 | B=4 |
A=0 | 11 | 12 | 13 | (error) | (error) |
A=1 | "welcome" | 2 | "phun" | (error) | (error) |
A=2 | 21 | 22 | 23 | 24 | 25 |
\ | B=0 | B=1 | B=2 |
A=0 | "one" | 2 | "three" |
A=1 | (error) | (error) | (error) |
A=2 | true | "or" | false |
A=3 | "Let's" | "phun" | error |
講座の書き出し時点で「ThymeスクリプトをonCollide属性以外にも書き込み可能だ」と書きましたが間違いであることが分かりました。
書いたそのときは動きはすれど、Phunを終了させたりファイルに保存する際に、onCollide以外の属性に書いたスクリプトは保存されない
ことが多いという事実が判明しました。
紛らわしい文章を載せてしまい、まことに申し訳ありませんでした。(現在は訂正済み) 2010/08/23 08:08 作成主