編集時Ver3.1.1.4
はじめに
本ページは、FtDのLUAをやってみたいけどプログラムとかさっぱりわからない、といった人に向けて
LUAの初歩を解説したものです。
随時サンプルコードを提示しているので、実際に動かしてみるのも良いと思います。
リンク
FtDのLUAのバージョンは不明。恐らく5.2
(table.maxnが無く、math.atan2が有る)
準備
FtDのLUA boxはエディタを持っていますが、長文だと全文が表示されなかったり、
改行がおかしくなったりするので、別途メモ帳などのテキストエディタで編集し
コピペでFtDのLUA boxに張り付けることを強くお勧めします。
LUA boxの使い方
LUA boxはインベントリのControlタブにあります。
設置し、Qキーを押すとエディタが開きます。
試しに下記のコードをコピペしてみてください。
function Update(I) I:ClearLogs() I:Log("hello world") end
画面右側のApply changes (SAVE)(F8)を押して、画面下部にhello worldと表示されればOKです。
編集を終了する場合、画面右上のExit (don't save changes)を押します。
Tabキーを押すとエディタが一時的に消えます。何かキーを押したり、マウスを動かしたりすると戻ります。
Options
画面右側のOptionsについて
Stop running | 処理を一時停止します。再開する場合はRestart runningを押します |
---|---|
Apply changes (SAVE)(F8) | 現在のコードを適用します |
Cahcelchanges (LOAD PREVIOUS) | 最後にApply changesした状態に戻します |
Clear Log | 画面下部のErrors/Logsをクリアします(が、動いているとまたログで埋まります) |
Select All (CTRL+A) | コードを全選択します |
Errors/Logs
エディタ画面下部の「Errors/Logs」の欄は、ログやエラーが出力されます。
最大100行です。
ログを出力するたびに1行目に挿入されるので、下が古く、上が新しいです。
エラーの確認
LUAのエラーが発生すると、「Errors/Logs」の欄に内容が出力されます。
エラーの内容は一番下に出力されるのでスクロールしましょう。
何かおかしいなと思ったら確認するのが完成への早道です。
ただし、アプデでエラーが出力されなくなった時期があるので、またあるかもしれません。
LUA boxの実行頻度
LUA boxに書かれたコードは1フレーム(1/40秒)ごとに実行されます。
具体的には、Update関数がFtDクライアントから呼ばれます。
つまり、Update関数は1秒間に40回実行されます。
関数については後述します。
プログラム言語「LUA」の基礎
プログラム言語LUAを扱うための基礎知識。
ビークルなどFtD的なことはこの項ではあまり関係ありません。
コメントアウト
ハイフン2つ(--)は、行末までの文字を無視します。
メモに使ったり、処理したくない行を一時的に無効化するのに使います。
自分が書いたコードでも1か月もすれば忘れるのでメモとしてよく使います。
function Update(I) I:ClearLogs() I:Log("hello world") -- ハイフン2つ以降のここは処理に関係ない end
ハイフン2つと大かっこ2重で複数行コメントができます。
function Update(I) I:ClearLogs() --[[ 複数行コメント 大かっこで囲まれた部分がコメント ]] I:Log("hello world") end
変数
変数とは箱のようなもので、値を入れることができます。
命名は英字で始まり、英字・数字・アンダーバー「_」を使用できます。
例)name, position1, from_the_depths
また、大文字小文字は区別されます。
例)name と Name は別物
変数は最初に登場した時(宣言時)に何らかの値を入れる必要があります。
function Update(I) I:ClearLogs() x -- 初期化していないのでエラー end
function Update(I) I:ClearLogs() x = 0 -- 初期化しているのでエラーは出ない I:Log(x) -- ログ欄に0が出力される end
FtDのLUAでは、値のデータ型には下記の6種類があります。
number | 数値 | 0や1や1.1など数値なら何でも。例)x = 1 |
---|---|---|
string | 文字列 | ダブルクォーテーションやシングルクォーテーションで囲みます。例)x = "test" |
boolean | 真偽 | ture か false。有りか無しかのどちらか。 |
function | 関数 | LUAは関数を変数に入れられる。関数については後述。 |
table | テーブル | 複数の値をまとめて1つの変数に入れられる。 |
nil | 無 | NULLやfalse。0ではない。ラテン語で「無」に由来する。RPGで例えると「無属性」ではなく「属性なし」 |
※プログラム言語習得者向け解説
LUAは変数が型を持たず、値が型を持つ動的型付け言語です。
型定義の構文はありません。
また、ユーザーデータとスレッドはFtDでは使えません。
つまり、メタテーブルとコルーチンがありません。
number
数値です。整数、小数、指数などがあります。
function Update(I) I:ClearLogs() x = 1 -- 変数xに1を代入 I:Log(x) -- ログ欄に1が出力される y = 1.5 -- 変数yに1.5を代入 I:Log(y) -- ログ欄に1.5が出力される z = -2 -- 変数zに-2を代入 I:Log(z) -- ログ欄に-2が出力される e = 2e+1 -- 変数eに2e+1を代入 I:Log(e) -- ログ欄に20が出力される end
string
ダブルクォーテーションまたはシングルクォーテーションで囲みます。
また、大かっこ2つで囲むと複数行の文字列を扱えます。
FtDではログ出力に使ったり、名前付けしたブロックを扱う際に使います。
function Update(I) I:ClearLogs() x = "a" -- 変数xにaを代入 I:Log(x) -- ログ欄にaが出力される y = 'b' -- 変数yにbを代入 I:Log(y) -- ログ欄にbが出力される z = [[いろはに ほへと]] -- 変数zに「いろはに[改行]ほへと」を代入。 I:Log(z) -- ログ欄に「いろはに[改行]ほへと」が出力される end
boolean
真(true)か偽(false)のどちらかを持つデータ型です。
後述するif文の条件判定に使ったり、状態を管理するのに使ったりします。
そのままではログ出力されません。
function Update(I) I:ClearLogs() x = true -- 変数xにtrueを代入 if x == true then I:Log("true") -- ログ欄に文字列trueが出力される else I:Log("false") -- ここは通らない end end
function
「関数」の項にて後述
table
テーブルを含む6種類のデータ型を複数持つデータ型です。
中かっこで囲みます。
「配列」と「連想配列」があります。
配列は、複数の値を順番に並べて持ちます。
複数の値のうち、どれを使うかを1以上の数字で指定します。
これらテーブルに入っている複数の値の事を「要素」といいます。
function Update(I) I:ClearLogs() x = {10, 11, 12, 13} -- 10~13の数値をまとめてxに代入 y = x[1] I:Log(y) -- ログ欄に10が出力される I:Log(x[2]) -- ログ欄に11が出力される I:Log(x[3]) -- ログ欄に12が出力される I:Log(x[4]) -- ログ欄に13が出力される end
連想配列は、キーと値のペアを複数持ちます。
複数の値のうち、どれを使うかをキーで指定します。
function Update(I) I:ClearLogs() x = {a = 10, b = "name", c = true} I:Log(x["a"]) -- ログ欄に10が出力される I:Log(x["b"]) -- ログ欄にnameが出力される I:Log(x["c"]) -- booleanなのでログ欄に出力されない I:Log(x.a) -- この書き方でもログ欄に10が出力される end
配列と連想配列を混ぜてもOKです。
function Update(I) I:ClearLogs() x = {100, a = 10, b = "name", 200, c = true, 300, 400} I:Log(x["a"]) -- ログ欄に10が出力される I:Log(x["b"]) -- ログ欄にnameが出力される I:Log(x["c"]) -- booleanなのでログ欄に出力されない I:Log(x[1]) -- ログ欄に100が出力される I:Log(x[2]) -- ログ欄に200が出力される I:Log(x[3]) -- ログ欄に300が出力される I:Log(x[4]) -- ログ欄に400が出力される end
テーブルを持つこともできるので、ツリー構造を作れます。
function Update(I) I:ClearLogs() x = { 100, a = 10, b = "name", 200, sub = { 11, 12, 13 }, sub2 = { red = 1, blue = 0.5, green = 0.11 }, } I:Log(x["sub"][1]) -- ログ欄に11が出力される I:Log(x.sub[1]) -- ログ欄に11が出力される --I:Log(x.sub.1) -- この書き方はできない I:Log(x.sub2.red) -- ログ欄に1が出力される end
宣言時に初期化せず、後から値を代入することもできます。
function Update(I) I:ClearLogs() x = {} x["a"] = 10 I:Log(x["a"]) -- ログ欄に10が出力される x.b = "name" I:Log(x["b"]) -- ログ欄にnameが出力される x[1] = 20 I:Log(x[1]) -- ログ欄に20が出力される end
テーブル名の先頭に#を付けると、中身の件数(要素数)を返します。
function Update(I) I:ClearLogs() x = {10, 11, 12, 13} -- 10~13の数値をまとめてxに代入 y = #x I:Log(y) -- ログ欄に4が出力される end
nil
「値が無い」を表すデータ型です。
また値を設定していない事を明示するのに使ったりします。
function Update(I) I:ClearLogs() y = nil -- 変数yを宣言し、明示的にnilで初期化している I:Log(y) -- ログ欄に空行が出力される y = 1 I:Log(y) -- ログ欄に1が出力される end
テーブルで存在しない要素を指定するとnilが返ってきます。
function Update(I) I:ClearLogs() x = {1, 2, 3} I:Log(x[1]) -- ログ欄に1が出力される I:Log(x[4]) -- ログ欄に空行が出力される I:Log(x[2]) -- ログ欄に2が出力される end
テーブルの要素にnilがあると、要素数が予期しない結果になる場合があります。
function Update(I) I:ClearLogs() x = {1, nil, 3} I:Log(#x) -- ログ欄に1が出力される x = {1, nil, 3, 4} I:Log(#x) -- ログ欄に4が出力される x = {1, 2, nil} I:Log(#x) -- ログ欄に2が出力される x = {1, 2, nil, 4} I:Log(#x) -- ログ欄に4が出力される end
関数
関数は、箱を受け取って箱の中身を取り出し、
何かの処理をします。
書式はfunctionで始まり、endで終わります。
function 関数名(引数) -- 何か処理 end
関数名には英字・数字・アンダーバーを使えます。
LUA boxにデフォルトで書いてある、
function Update(I) -- put your code here end
もUpdateという名前の関数です。
関数は独自で作れ、Update(I)や作成した関数から呼び出すことができます。
function Update(I) I:ClearLogs() MyMethodAaa(I, 1, 2) end function MyMethodAaa(I, a, b) I:Log(a) -- ログ欄に1が出力される MyMethodBbb(I, b) end function MyMethodBbb(I, b) I:Log(b) -- ログ欄に2が出力される end
引数
引数は、関数が呼び出し元から値を受け取ります。
引数はカンマ区切りで複数指定できます。
function Update(I) I:ClearLogs() MyMethodAaa(I, 10, "a") end function MyMethodAaa(I, arg1, arg2) I:Log(arg1) -- ログ欄に10が出力される I:Log(arg2) -- ログ欄にaが出力される end
関数に渡すものがなければ引数は不要です。
api = nil function Update(I) I:ClearLogs() api = I MyMethodAaa() end function MyMethodAaa() api:Log("aaa") -- ログ欄にaaaが出力される end
呼び出し側で引数を渡し忘れると、関数にはnilが渡ります。
function Update(I) I:ClearLogs() MyMethodAaa(I) end function MyMethodAaa(I, arg) if arg == nil then I:Log("nil") -- ログ欄にnilと出力される end end
戻り値
戻り値は、引数とは逆に呼び出し元に値を返します。
returnで指定します。
function Update(I) I:ClearLogs() x = GetAaa() I:Log(x) -- ログ欄にresultが出力される end function GetAaa() return "result" end
変数への代入
関数は変数に匿名(関数名なし)で代入できます。
function Update(I) I:ClearLogs() x = function() return "result" end y = x() I:Log(y) -- ログ欄にresultが出力される end
演算子
算術演算子
number型の値に使えます。
+ | 加算 |
---|---|
- | 減算 |
* | 乗算 |
/ | 浮動小数点数除算 |
% | 剰余 |
^ | 累乗 |
- | 単項マイナス |
function Update(I) I:ClearLogs() x = 10 I:Log(x + 1) -- ログ欄に11が出力される I:Log(x - 1) -- ログ欄に9が出力される I:Log(x * 2) -- ログ欄に20が出力される I:Log(x / 2) -- ログ欄に5が出力される I:Log(x % 7) -- ログ欄に3が出力される I:Log(x ^ 2) -- ログ欄に100が出力される I:Log(-x) -- ログ欄に-10が出力される end
※切り捨て除算//はありません。
関係演算子
tureかfalseを返します。
== | 等しい |
---|---|
= | 等しくない |
< | より小さい |
> | より大きい |
<= | 小さいまたは等しい |
>= | 大きいまたは等しい |
function Update(I) I:ClearLogs() x = 10 if x < 11 then I:Log("1") -- ログ欄に1が出力される end if x < 10 then I:Log("2") -- ここは通らない end x = false if x == false then I:Log("3") -- ログ欄に3が出力される end if x ~= false then I:Log("4") -- ここは通らない end end
論理演算子
falseとnilを偽、それ以外を真とみなします。
and | 左項と右項が真のとき真を返す |
---|---|
or | 左項または右項が真のとき真を返す |
not | 真(偽)のとき偽(真)を返す |
function Update(I) I:ClearLogs() I:Log(10 or 20) --> 10 I:Log(nil or "a") --> "a" if (false and nil) == false then --> false I:Log("false and nil") end I:Log(10 and 20) --> 20 if (nil and 10) == nil then --> nil I:Log("nil and 10") end if (false or nil) == nil then --> nil I:Log("false or nil") end end
制御構文
if文
分岐。条件が真の時に中の処理を実行します。
条件はfalseとnilを偽、それ以外を真とみなします。
書式は以下の通り
if 条件 then 何か処理 end
function Update(I) I:ClearLogs() x = true if x then I:Log("1") -- ログ欄に1が出力される end x = false if x then I:Log("2") -- ここは通らない end end
if、else、elseifで分岐を増やせます。
function Update(I) I:ClearLogs() x = true if x then I:Log("1") -- ログ欄に1が出力される else I:Log("2") -- ここは通らない end x = false if x then I:Log("1") -- ここは通らない else I:Log("2") -- ログ欄に2が出力される end x = 2 if x == 1 then I:Log("11") -- ここは通らない elseif x == 2 then I:Log("12") -- ログ欄に12が出力される else I:Log("13") -- ここは通らない end end
for文
ループ。指定した回数、処理を繰り返します。
function Update(I) I:ClearLogs() for i = 1, 10, 2 do -- 変数iに1から10まで、2ずつ増やしながら繰り返す I:Log(i) -- 1, 3, 5, 7, 9 が出力される end for i = 11, 15 do -- 変数iに11から15まで、1ずつ増やしながら繰り返す。増分が1の場合省略できる I:Log(i) -- 11, 12, 13, 14, 15 が出力される end for i = 20, 25 do -- 変数iに20から25まで、1ずつ増やしながら繰り返す。増分が1の場合省略できる if i == 23 then break -- break文でループから抜け出す end I:Log(i) -- 21, 22 が出力される end end
テーブルの中身をループで処理できます。
function Update(I) I:ClearLogs() x = {11, 12, 13, 14} for i = 1, #x do I:Log(x[i]) -- 11, 12, 13, 14 が出力される end end
連想配列の中身を処理する書き方もあります。(ただし比較的重い)
function Update(I) I:ClearLogs() x = {a = 11, b = 12, c = 13, d = 14} for k,v in pairs(x) do I:Log(k) -- a, b, c, d が出力される I:Log(v) -- 11, 12, 13, 14 が出力される end end
while文
ループ。条件が真の間、処理を繰り返します。
function Update(I) I:ClearLogs() i = 1 while i < 10 do i = i + 1 I:Log(i) -- 1から10が出力される end while i < 20 do i = i + 1 if i == 13 then break -- break文でループから抜け出す end I:Log(i) -- 11, 12が出力される end end
repeat文
条件の判定が処理の後に来るループです。
function Update(I) I:ClearLogs()
i = 1 repeat i = i + 1 I:Log(i) -- 2から11が出力される until i > 10 -- ループから抜ける条件 end
スコープとlocal
function, if, while, forなどからendまで(とrepeat~until)をブロックと言います。
変数にはローカル変数とグローバル変数があります。
ローカル変数は、宣言されたブロック内でしか使えません。
グローバル変数はどこでも使えます。
ローカル変数
変数の宣言時、変数の前にlocal を付けるとローカル変数になります。
function Update(I) I:ClearLogs() if true then -- ブロック local a = 1 I:Log(a) -- 1が出力される end I:Log(a) -- 出力されない end
グローバル変数
x = 1 -- グローバル変数 function Update(I) I:ClearLogs() y = 2 -- グローバル変数 if true then -- ブロック z = 3 -- グローバル変数 end Log1(I) end function Log1(I) I:Log(x) -- 1が出力される I:Log(y) -- 2が出力される I:Log(z) -- 3が出力される end
実行される順番
function Local_A(I) -- Updateから呼ばれる I:Log("loca_a") end global_a = "global_a" -- Updateより先に実行される function Update(I) -- functionのうち最初に実行される I:ClearLogs() I:Log(global_a) I:Log(global_b) Local_A(I) Local_B(I) end global_b = "global_b" -- Updateより先に実行される function Local_B(I) -- Updateから呼ばれる I:Log("loca_b") end
関数を記述する順番に決まりは無いので、
Updateを最初に書く人と、最後に書く人がいます。
LUAの標準関数
LUAには自作しなくても使える標準の関数があります。
LUAのリファレンスマニュアルを参照してください。
一部がFtDで使用できます。
以下はよく使うかもしれません。
- math.~
- string.~
- table.~
self
特殊な変数。
テーブルに関数を持たせた時、その関数がそのテーブルを参照するのに使います。
関数の引数の1個目にselfを定義します。
table = { id = 10, LogId = function (self, I) I:Log(self.id) end } function Update(I) I:ClearLogs() table.LogId(table, I) -- 10が出力される table:LogId(I) -- コロンを使うと第1引数のselfを省略できます end
上記と同じ内容で別の書き方その1
table = {} table.id = 10 table.LogId = function (self, I) -- 上記と同じ内容で別の書き方 I:Log(self.id) end function Update(I) I:ClearLogs() table.LogId(table, I) -- 10が出力される table:LogId(I) -- コロンを使うと第1引数のselfを省略できます end
上記と同じ内容で別の書き方その2
table = {} table.id = 10 function table.LogId(self, I) -- 上記と同じ内容で別の書き方 I:Log(self.id) end function Update(I) I:ClearLogs() table.LogId(table, I) -- 10が出力される table:LogId(I) -- コロンを使うと第1引数のselfを省略できます end
上記と同じ内容で別の書き方その3
table = {} table.id = 10 function table:LogId(I) -- 上記と同じ内容で別の書き方。コロンでselfを省略 I:Log(self.id) end function Update(I) I:ClearLogs() table.LogId(table, I) -- 10が出力される table:LogId(I) -- コロンを使うと第1引数のselfを省略できます end
FtDのAPI
ここからやっとLUA Boxの外と戯れることができます。
APIとは一般IT用語で、Application Programming Interfaceの略です。
FtDとLuaコードがやり取りをするための窓口です。
function Update(I) end
の関数Updateの引数IがAPIにアクセスするためのインターフェイスです(多分InterfaceのI)
ここまで使って来たI:Log()もAPIです。
APIを使用するにはこのIが必要なので、関数の引数にはIを指定しておくことをお勧めします。
function Update(I) I:ClearLogs() LogAaa(I) -- Iを引数で渡す end function LogAaa(I) --Iを引数で受け取る I:Log("aaa") end
詳しくはLUA_ヘルプドキュメントを参照してください。
I.とI:
I.Fleet などのI.(ドット)で始まるものと、
I:Log()などのI:(コロン)で始まるものがありますのでご注意ください。
I.~は変数(に似たプロパティというもの)
I:~は関数です。
ざっくりしたLUAでできること
LUA_ヘルプドキュメントのカテゴリごとに、それぞれどんなことができるのかをざっくりまとめてみました。
- Logging/Debugging
- ログ関連。LUA boxのエディタや、HUDにログを出力します。
- Fleet Awareness
- 艦隊関連。艦隊を組んでいる時の僚艦の情報やどれが旗艦かなど。
- FleetInfo
- 艦隊のビークル一つ分の情報
- Resources
- リソースゾーン関連。
- ResourceZoneInfo
- リソースゾーンの情報
- ResourceInfo
- 自機のリソース情報。資源統合前のもので、今はもう使わない。
- AI
- AI関連。Cキーで開いた画面でON/OFFするのと同じことができる。
- BlueprintSpawnerInfo
- BlueprintSpawnerの情報
- Propulsion
- 推進関連。AIと同じように、前進後退姿勢制御などの推進要求を出せる。
- TargetInfo
- 敵関連。Mainframeが認識している敵の情報を取得できる。
- TargetInfo
- 敵の情報
- TargetPositionInfo
- 敵の位置情報。
- Misc
- その他。ゲーム時間や任意の位置の地表の高さ、ある高度の重力、風向きと風速などなど。
- Self awareness
- 自機関連。位置、姿勢、資源、出力、BP名など。
- Components
- コンポーネント関連。バルーンやエアポン、ギャザラーにエンジン、ワープなど35種のパーツを操作できる。
- components type and their logic
- コンポーネントの種類。
- BlockInfo
- ブロックの情報。位置、向き、名前など。
- Weapons
- 武装関連。狙ったり撃ったり。
- WeaponInfo
- 武装情報。位置や弾速、武器種など。
- WeaponConstraints
- 射角制限の情報。
- Missile Warning
- ミサイル関連。ワーナーで検知した敵ミサイル。
- MissileWarningInfo
- ミサイル情報。位置、速度、距離など。
- MissileGuidance
- 自機のミサイル関連。LuaTranceiverを付けたミサイルを操作する。
- SpinBlocks and pistons
- スピブロ、ピストン、デディケイテッドブレード関連。回したり伸ばしたり。
- SubConstructs
- サブジェクト関連。サブオブジェクトの各種情報を取得する。
- Friendlies
- 友軍関連。友軍の情報を取得する。
- FriendlyInfo
- 友軍の情報。位置、姿勢、速さ、大きさ、資源など。
- Math and Other
- 3次元情報をいじるのに便利な関数群
複数のLUA box
LUA boxはそれぞれ独立して実行されます。実行順は不明(多分置いた順)
独立しているので、複数のLUA boxで変数の共有などはできません。
別のLUA boxに同じ名前の変数があっても、上書きされることはありません。
LUA box間でデータをやり取りしたい場合、spotlightやair pumpなどのコンポーネントで中継すればできなくもないです。多分。
よくある命名規約
変数や関数の命名は何でも良いのですが、後から読みやすくしたり、他人が読みやすいように、
暗黙的なルールがあります。
ただし人によって異なります。
変数
略語を使うと半年後の自分が困ります。
- スネークケース
- 小文字とアンダーバーで構成される。例)from_the_depths
- キャメルケースまたはローワーキャメルケース
- 最初の単語は小文字で、以降の単語は大文字始まり。例)fromTheDepths
定数
LUAの仕様上定数はありませんが、処理の中で値が書き換わらない、つまり固定値のことを定数と呼んだりします。
大文字とアンダーバーで構成される。例)FROM_THE_DEPTHS
関数
- パスカルケースまたはアッパーキャメルケース)
- 単語は大文字始まり。例)FromTheDepths
- スネークケース
- 小文字とアンダーバーで構成される。例)from_the_depths
FtDのAPIは、一部アンダーバーがあるものの大体はパスカルケース。
恐らくマイクロソフトのガイドラインに準拠。
よくあるハマりポイント
indexが0始まりと1始まりがある
LUAのテーブルは数字指定でアクセスする際、1から始まる連番を指定しますが、
FtD APIの○○Index(I:GetNumberOfTargets(mainframeIndex)など)は0から始まる連番です。
function Update(I) I:ClearLogs() local a = {1, 2, 3} for i = 1, #a do I:Log(i) -- 1~3が出力される end local mainframeCount = I:GetNumberOfMainframes() for mainframeIndex = 0, mainframeCount - 1 do local targetCount = I:GetNumberOfTargets(mainframeIndex) I:Log(targetCount) -- Mainframeが認識しているターゲットの数が出力される end end
if文で==のつもりが= / ~=のつもりが=~
function Update(I) I:ClearLogs() a = 10 if a == 10 then -- OK end if a = 10 then -- NG。エラー end if a ~= 8 then -- OK end if a =~ 8 then -- NG。エラー end end
F8押しても適用されていない?
気のせいなのかバグなのか、たまにF8を押してもコードが適用されていないような気がするときがあります。
その場合は、適当に空行を追加してF8を押しましょう。
何度かやればきっと適用されています。
その他
文字列連結
ピリオド2つ(..)で文字列を連結できます。
function Update(I) I:ClearLogs() a = "a" x = "name" I:Log(a .. ", " .. x) -- a, name と出力される -- 数値が混じってもOK b = 100 I:Log(b .. "sec") -- 100sec と出力される end