スライドバー的なUIを作りたい

#スライドバーを作りたい
スライドバーを使いたいときに、そういうコンポーネントが用意されているわけでもないので、各人作らないとなりません。
いちいちUIで時間を消費するのもあまり良くないと思いますので、ここで共有したいと思います。

作るのは↓みたいな感じです。
<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/VCsakkinonana?src=hash&amp;ref_src=twsrc%5Etfw">#VCsakkinonana</a><a href="https://t.co/3ItSmatSrM">https://t.co/3ItSmatSrM</a><a href="https://twitter.com/hashtag/%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88?src=hash&amp;ref_src=twsrc%5Etfw">#バーチャルキャスト</a> <a href="https://twitter.com/hashtag/virtualcast?src=hash&amp;ref_src=twsrc%5Etfw">#virtualcast</a> <a href="https://t.co/HYag02PDd0">pic.twitter.com/HYag02PDd0</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1292879328653348868?ref_src=twsrc%5Etfw">August 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#モデル
モデルは特にモデリングしてません。unityのプリセット図形を使ってます。以下のとおりです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/66c1aaf3-e621-a35e-20f7-ad8548e44d6f.png)
この内"high" "low"は文字ですので、実質"controll_bar","Sphere","dummy"の3つだけです。
dummyは透明にしてあります。
バーチャルキャストではある経路に沿ってだけ動く、みたいな拘束はかけられないため、掴みたいスライドバーのつまみと、実際に掴んでいるものとを別にします。その上でスライドバーの座標を計算してスクリプトで移動させるという形式を取ります。

#スクリプト

以下スクリプトを貼り付けておきます。このスクリプトは、スライダーの位置が温度(Teff)を表していて、この変数が変化します。そしてTeff2rgb()で色に変換してマテリアルの色を変えるという単純なものです。Teff2rgb()の中に書いてある計算式は今回の目的にあまり関係ないので単純にしておきました。

```lua:main.lua
local ctr = vci.assets.GetTransform("Sphere")
local dummy = vci.assets.GetSubItem("dummy")
local bar = vci.assets.GetSubItem("controll_bar")

local flggrabdummy = False
local bufdummypos = dummy.GetPosition()
local bufctrpos = ctr.GetPosition()

local cnst_max = 20000
local cnst_min = 2000
local r=0
local g=0
local b=0
local nowctrpos = bufctrpos

function updateAll()
    --持っているときはコントローラーをコライダーダミーに追従させ、放したときはダミーをコントローラーの位置に戻す
    if flggrabdummy == false and dummy.IsMine then
        dummy.SetPosition(Vector3.MoveTowards(dummy.GetPosition(),ctr.GetPosition(),1))
    else
        nowctrpos =  Vector3.ClampMagnitude(Vector3.Project(dummy.GetPosition() - bar.GetPosition(),bar.GetRotation()*Vector3.up),0.5)
        ctr.SetPosition(bar.GetPosition() +nowctrpos)
        --bufctrpos = ctr.GetPosition() <<これをやるとふっとぶ；上の式に再帰的にたされてしまうので。持っている間は値固定が基本
    end
    -- 星の色をつける
    if dummy.IsMine then
        local Teff = (-Vector3.Dot(nowctrpos,bar.GetRotation()*Vector3.up)+0.5)*2*(cnst_max-cnst_min)/2 + cnst_min
        vci.assets._ALL_SetMaterialColorFromName("blackbody", Teff2rgb(Teff))
    end
end

function onGrab(target)
    if target == "dummy" then
        flggrabdummy = true
        print("grabbing")
        bufdummypos = dummy.GetPosition()
        bufctrpos = ctr.GetPosition()
    end
end

function onUngrab(target)
    if target == "dummy" then
        flggrabdummy = false
        print("ungrabed")
    end
end

function Teff2rgb(Teff)
    --変曲点の前後か判断する
    local x = Teff/10000
    local r,g,b
    r = 1
    g = x*10000
    b = x*10000
    return Color.__new(r,g,b,1)
end
```

ポイントは

```lua:
    if flggrabdummy == false and dummy.IsMine then
        dummy.SetPosition(Vector3.MoveTowards(dummy.GetPosition(),ctr.GetPosition(),1))
    else
        nowctrpos =  Vector3.ClampMagnitude(Vector3.Project(dummy.GetPosition() - bar.GetPosition(),bar.GetRotation()*Vector3.up),0.5)
        ctr.SetPosition(bar.GetPosition() +nowctrpos)

    end
```
ここのProject()関数ですね。これは、与えられた軸方向に、あるベクトルが移動した成分を計算します。今回でいうと、ツマミを持ってから動かした際に、スライダーの方向と完全には一致しなくて少しずれるわけですけども、そのずれる成分を無視した際にどれだけスライドされたのかを計算してくれます。あまり使われていないように感じますが、私は割と使用頻度高いです（昔のスクリプトだと存在を知らなくて自力で書いてたりするくらい）。

それでこのときつまみとして扱うのは、見えてるツマミ(sphere)ではなく、dummyの方を取ります。Sphereの方は計算で位置を設定します。これによってあたかも拘束されて動いてるかのように見えます。

ClampMagnitude()もあまり使われていない関数だと思います。これは移動の範囲を制限します。これでスライダーから飛び出さなくなります。

#最後に
実際UIをイチから作るのは以外に大変で、こういうのを逐一やっている運営さんはすごいなと思いしらされます。VCI製作者としては他にも使いたいUIはたくさんあると思うので、ぜひ皆さんも作ったら共有していってください。よろしくおねがいします。
読んでいただきありがとうございました。