Qiitaの記事書くの初めてです。
Defoldというゲームエンジンを使ってたらLuaでちょっと気になることがあったのでメモ書き。
完全な自己理解なので間違ってたら教えてください。
問題
自力で書いた物理判定を使いたくて、Lua Moduleを使ってゲームオブジェクトとは別の物理判定用のテーブルを用意してそこで設置などの判定をしていた。
で、そのテーブルに基本の情報(速度とか摩擦とか)を持つメタテーブルを設定したんだけど、一部うまくいったりいかなかったりした。
具体的には、メタテーブル内のテーブルへのアクセスうまくいかなかった。例えば、速度はvmath.vector3()で設定したんだけど、Bの速度を変更したらAの速度のみ変更になったりする感じ。
原因の予測
これはシンプルにLuaがtableを代入したときにすべて参照渡しになることと、メタテーブルの仕様が(たぶん)原因だった。
普段↓こんな感じで書いてたんだけど
objs = {}
obj_meta = {}
obj_meta.name = "name"
obj_meta.max_spd =10
obj_meta.spd ={x =0,y=0}
function make_objects(name)
local o = {}
o.name = name
setmetatable(o,{__index = obj_meta})
table.insert(objs,o)
return o
end
メタテーブルはこの時、新しいobjのmax_spdにアクセスしようとしたときに、obj_metaのmax_spdにアクセスするので、10が返る。spdも同様。
ただし、新たな値を代入するとき。
local p = make_object("player")
p.max_spd = 9
p.spd.x = 100
とかするとき、max_spdのような値はobjのpの中に新しいmax_spdを作って、以後それにアクセスするようになる。
でもspdのようにテーブル内の要素を変更するときは、metatable内のspdを変更することになってしまっていた。なので、spd.xを変更したときに別の要素のも変更されてしまっていたんだ、と思う。
解決法
メタテーブルの中にテーブルを入れないか、初期化の時に
local p = make_object("player")
p.spd = {x =0,y=0}
ってするみたいな感じにして解決した。
参照渡しと値渡しって混乱するなあ。