持ち物の概念をつくる　その4

「VキャスのVCIで◯ine◯raftっぽいもの作る Advent Calendar 2024」12日目

Vキャス内で、穴を掘って、石を砕き、木を切って、組み合わせて武器を作って、何なら出てくる敵を倒して・・・
そんなことをやりたいなぁっていう試みを12月でできるところまで行こうというカレンダーの12日目です！

今日は装着した状態でも持ち運べるようにしていきます。

# 持ち物の概念をつくる　その4

装着のコードも書いていくので今日はコード整理をメインにやっています。
手順は以下の通り。

① 追従コードを変更する
② 装着しているときと持っているときで追従オブジェクトを切り替える

さっそくやっていきます。


# 手順

## ① 追従コードを変更する
一昨日の時点では、CubeとCylinderを掴んだときだけに追従したら良いと思っていたのですが、
よくよく考えると手につけた状態では、Sphereに追従する必要がありました。
つまり場合によって3種類のオブジェクトのどれを追従するということがあり得るということです。

そこで、変数へのオブジェクトの格納の書き方や追従の書き方を整理します。
```lua
SubItems = {
    Cube = {item=vci.assets.GetTransform("Cube")},
    Cylinder = {item=vci.assets.GetTransform("Cylinder")},
    Sphere = {item=vci.assets.GetTransform("Sphere")},
    slot1 = {item=vci.assets.GetTransform("slot1")},
    slot2 = {item=vci.assets.GetTransform("slot2")},
    slot3 = {item=vci.assets.GetTransform("slot3")},
    slot4 = {item=vci.assets.GetTransform("slot4")},
    slot5 = {item=vci.assets.GetTransform("slot5")},
    slot6 = {item=vci.assets.GetTransform("slot6")},
    slot7 = {item=vci.assets.GetTransform("slot7")},
    slot8 = {item=vci.assets.GetTransform("slot8")},
    select = {item=vci.assets.GetTransform("select")},
    l_v = {item=vci.assets.GetTransform("l_v")}
}
```
宣言は全てのオブジェクトをまとめてにしました。
わざわざ変数を分けて記述が増えるのも良くないのでまとめてしまいます。


そして、CubeとCylinder、Sphereの3つのオブジェクトからの距離と角度を格納します。
サイズも直書きするより、元のサイズを取得しておいたほうが正確だしコードが減るので、変数内に格納しておきます。
```lua
for key, value in pairs(SubItems) do
    if key ~= "Cube" then
        SubItems[key].cubedist = SubItems[key].item.GetPosition() - SubItems.Cube.item.GetPosition()
        SubItems[key].cuberoat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Cube.item.GetRotation()
    end
    if key ~= "Cylinder" then
        SubItems[key].cylinderdist = SubItems[key].item.GetPosition() - SubItems.Cylinder.item.GetPosition()
        SubItems[key].cylinderroat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Cylinder.item.GetRotation()
    end
    if key ~= "Sphere" then
        SubItems[key].spheredist = SubItems[key].item.GetPosition() - SubItems.Sphere.item.GetPosition()
        SubItems[key].sphereroat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Sphere.item.GetRotation()
    end
    SubItems[key].Scale = SubItems[key].item.GetLocalScale()
end
SubItems.Cube["roat"] = Quaternion.Inverse(SubItems.Cube.item.GetRotation())
SubItems.Cylinder["roat"] = Quaternion.Inverse(SubItems.Cylinder.item.GetRotation())
SubItems.Sphere["roat"] = Quaternion.Inverse(SubItems.Sphere.item.GetRotation())
```

変数を変えたので、それに従ってCubeとCylinderの追従コードの記述も更新。
```lua
if mode then
    local pos = SubItems.Cube.item.GetPosition()
    local roat = SubItems.Cube.item.GetRotation()
    for key, value in pairs(SubItems) do
        if key ~= "Cube" then
            local offset = roat * SubItems.Cube["roat"] * SubItems[key].cubedist
            local pos_d = pos + offset
            SubItems[key].item.SetPosition(pos_d)
            SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].cuberoat))
        end
    end
else
    local pos = SubItems.Cylinder.item.GetPosition()
    local roat = SubItems.Cylinder.item.GetRotation()
    for key, value in pairs(SubItems) do
        if key ~= "Cylinder" then
            local offset = roat * SubItems.Cylinder["roat"] * SubItems[key].cylinderdist
            local pos_d = pos + offset
            SubItems[key].item.SetPosition(pos_d)
            SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].cylinderroat))
        end
    end
end
```
変数にまとめたので記述は短くてすむようになりました。

そして、ON/OFFの関数での拡大縮小も変数に格納したものを利用するように書き換えます。
```lua
function setON()
    for key, value in pairs(SubItems) do
        if key == "Cylinder" then
            SubItems[key].item.SetLocalScale(Vector3.__new(0.001,0.001,0.001))
        else
            SubItems[key].item.SetLocalScale(SubItems[key].Scale)
        end
    end
end

function setOFF()
    for key, value in pairs(SubItems) do
        if key == "Cylinder" or key == "Sphere" then
            SubItems[key].item.SetLocalScale(SubItems[key].Scale)
        else
            SubItems[key].item.SetLocalScale(Vector3.__new(0.001,0.001,0.001))
        end
    end
end
```
こちらも短くなりました。


## ② 装着しているときと持っているときで追従オブジェクトを切り替える
あとは、装着オブジェクトを装着しているときは、装着したオブジェクトを追従させ、CubeかCylinderを持っているときはそちらを追従するように切り替えるコードを書きます。

まず、CubeかCylinderを持っているとき、という状態を見る変数「grab」を作ります。
初期は掴んでないので、値は`false`とします。
```lua
grab = false
```

そして、オブジェクトを掴んだタイミングで掴んだフラグをONにします。
CubeもしくはCylinderをみ始め(onGrab関数が発火する)たら、掴んだ状態（grab = true）。
```lua
function onGrab(subItemName)
    if subItemName=="Cube" or subItemName=="Cylinder" then
        grab = true
    end
end
```

そして、CubeもしくはCylinderを掴み終え(onUnGrab関数が発火する)たら、掴んだ状態解除（grab = false）に切り替わる処理を書きます。
```lua
function onUngrab(subItemName)
    if subItemName=="Cube" or subItemName=="Cylinder" then
        grab = false
    end
end
```

これで`grab`を見ると、掴んでいるかいないかわかるので、updateAllの処理に分岐を書きます。
grabがfalseの場合は、Sphere（装着オブジェクト）を追従するという記述です。
```lua
function updateAll()
    if grab == false then
        local pos = SubItems.Sphere.item.GetPosition()
        local roat = SubItems.Sphere.item.GetRotation()
        for key, value in pairs(SubItems) do
            if key ~= "Sphere" then
                local offset = roat * SubItems.Sphere["roat"] * SubItems[key].spheredist
                local pos_d = pos + offset
                SubItems[key].item.SetPosition(pos_d)
                SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].sphereroat))
            end
        end
    else
```
最後のelseにCubeとCylinderの追従コードの記述をそのままくっつけて、これで完成です。
コードの全体は以下の通り。
```lua
SubItems = {
    Cube = {item=vci.assets.GetTransform("Cube")},
    Cylinder = {item=vci.assets.GetTransform("Cylinder")},
    Sphere = {item=vci.assets.GetTransform("Sphere")},
    slot1 = {item=vci.assets.GetTransform("slot1")},
    slot2 = {item=vci.assets.GetTransform("slot2")},
    slot3 = {item=vci.assets.GetTransform("slot3")},
    slot4 = {item=vci.assets.GetTransform("slot4")},
    slot5 = {item=vci.assets.GetTransform("slot5")},
    slot6 = {item=vci.assets.GetTransform("slot6")},
    slot7 = {item=vci.assets.GetTransform("slot7")},
    slot8 = {item=vci.assets.GetTransform("slot8")},
    select = {item=vci.assets.GetTransform("select")},
    l_v = {item=vci.assets.GetTransform("l_v")}
}
for key, value in pairs(SubItems) do
    if key ~= "Cube" then
        SubItems[key].cubedist = SubItems[key].item.GetPosition() - SubItems.Cube.item.GetPosition()
        SubItems[key].cuberoat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Cube.item.GetRotation()
    end
    if key ~= "Cylinder" then
        SubItems[key].cylinderdist = SubItems[key].item.GetPosition() - SubItems.Cylinder.item.GetPosition()
        SubItems[key].cylinderroat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Cylinder.item.GetRotation()
    end
    if key ~= "Sphere" then
        SubItems[key].spheredist = SubItems[key].item.GetPosition() - SubItems.Sphere.item.GetPosition()
        SubItems[key].sphereroat = Quaternion.Inverse(SubItems[key].item.GetRotation()) * SubItems.Sphere.item.GetRotation()
    end
    SubItems[key].Scale = SubItems[key].item.GetLocalScale()
end
SubItems.Cube["roat"] = Quaternion.Inverse(SubItems.Cube.item.GetRotation())
SubItems.Cylinder["roat"] = Quaternion.Inverse(SubItems.Cylinder.item.GetRotation())
SubItems.Sphere["roat"] = Quaternion.Inverse(SubItems.Sphere.item.GetRotation())

function setON()
    for key, value in pairs(SubItems) do
        if key == "Cylinder" then
            SubItems[key].item.SetLocalScale(Vector3.__new(0.001,0.001,0.001))
        else
            SubItems[key].item.SetLocalScale(SubItems[key].Scale)
        end
    end
end

function setOFF()
    for key, value in pairs(SubItems) do
        if key == "Cylinder" or key == "Sphere" then
            SubItems[key].item.SetLocalScale(SubItems[key].Scale)
        else
            SubItems[key].item.SetLocalScale(Vector3.__new(0.001,0.001,0.001))
        end
    end
end

mode = true
grab = false
setON()

function updateAll()
    if grab == false then
        local pos = SubItems.Sphere.item.GetPosition()
        local roat = SubItems.Sphere.item.GetRotation()
        for key, value in pairs(SubItems) do
            if key ~= "Sphere" then
                local offset = roat * SubItems.Sphere["roat"] * SubItems[key].spheredist
                local pos_d = pos + offset
                SubItems[key].item.SetPosition(pos_d)
                SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].sphereroat))
            end
        end
    elseif mode then
        local pos = SubItems.Cube.item.GetPosition()
        local roat = SubItems.Cube.item.GetRotation()
        for key, value in pairs(SubItems) do
            if key ~= "Cube" then
                local offset = roat * SubItems.Cube["roat"] * SubItems[key].cubedist
                local pos_d = pos + offset
                SubItems[key].item.SetPosition(pos_d)
                SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].cuberoat))
            end
        end
    else
        local pos = SubItems.Cylinder.item.GetPosition()
        local roat = SubItems.Cylinder.item.GetRotation()
        for key, value in pairs(SubItems) do
            if key ~= "Cylinder" then
                local offset = roat * SubItems.Cylinder["roat"] * SubItems[key].cylinderdist
                local pos_d = pos + offset
                SubItems[key].item.SetPosition(pos_d)
                SubItems[key].item.SetRotation(roat * Quaternion.Inverse(SubItems[key].cylinderroat))
            end
        end
    end
end

function onGrab(subItemName)
    if subItemName=="Cube" or subItemName=="Cylinder" then
        grab = true
    end
end

function onUngrab(subItemName)
    if subItemName=="Cube" or subItemName=="Cylinder" then
        grab = false
    end
end

function onUnuse(subItemName)
    if subItemName == "l_v" and mode==true then
        mode = false
        setOFF()
    elseif subItemName == "Cylinder" and mode==false then
        mode = true
        setON()
    end
end
```

# おわりに

今日までで、やっと情報表示板を持ち運び、そして手につけて追従させられるようになりました。
![20241212224905_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222644/6ddde64b-0b7e-2277-85f2-ad86db83ae3e.jpeg)


明日はやっと、もともとやりたかったアイテムの所有ということをこの情報表示版に実装していきます。