0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

回転するオブジェクトが伸び縮みするんじゃー(親オブジェクトに引っ張られてしまうお話)

Last updated at Posted at 2025-12-23

ご挨拶

はいどうも。
普通の記事を書こうとしたら何故かクォータニオンが少し絡んでいるので
クォータニオン強化週間にねじ込んでもらったぞ!(クォータニオン成分低めなのは許して)

何かVCIを作ろうとまず思ったとき、自分だけの武器や時計が欲しいと思うよね(諸説)
今回はその時計を作ろうとしたときのお話。

○素材の準備

・ということでUnity上になんだか見たことのある時計をスクショの構成で準備。
 スケール比はそれっぽくなるよう調整。

☆フォルダー構成
 clockというVCIオブジェクト
 └Planeという文字盤兼VCIサブアイテム(Unity標準の平面オブジェクトを90度傾け)
  └短針、長針、秒針それぞれの孫オブジェクト(これもUnity標準の平面オブジェクト)

1-1 - コピー.jpg

○コードを書こう

一先ず回転の挙動を確認したいので毎フレームにcountを+1して長針を回転させてみよう。

--各オブジェクトの取得
local Plane = vci.assets.GetTransform("Plane")
local tyousin = vci.assets.GetTransform("tyousin")

--各変数
local count=0

function update()
    count=count+1

    --ベースオブジェクト
    rot_base=Plane.GetRotation()

    --針の座標設定 ベースとなるオブジェクトのrotationと針の角度をかけ算
    rot_tyousin=rot_base*Quaternion.Euler(0,count,0)

    --針のローテーション
    tyousin.SetRotation(rot_tyousin)
end

前回の記事で触れたとおり、クォータニオンの計算は基準×回転運動
ということで今回はrot_base(基準)*Quaternion.Euler(0,count,0)(回転)だ!

注意!
VCIではサブアイテムの下、孫オブジェクトは場所や回転などが同期されません。

今回はfunction update()
自分目線では針が回転していますが、他の人が見ると針は動いていません。
他の人にも見せたいときはfunction updateAll() としましょう(n敗)。

2.gif
なんか長針が伸び縮みしてる・・。

○原因は?

原因は簡単で針の親である「Plane」オブジェクトのスケール比が1:1:1で無いから。
針が回転するたびに親オブジェクトのスケール比を参照した結果伸び縮みしてしまうのだ。
※スケール比だけでなく回転値でも影響します。

○対応その1(ケースバイケース 今回は失敗する)

 親オブジェクトのスケール比に影響されるのであれば空オブジェクト(フォルダ)を
作成し、それをサブアイテムとしよう。

 オブジェクトは位置を変更しても見た目が変わらないように座標等がUnity側で再計算
されるので便利ね。

☆フォルダー構成
 clockというVCIオブジェクト
 └SubItemという空フォルダ(サブアイテム) スケール比は1:1:1
  ├Planeという文字盤
  ├短針の孫オブジェクト
  ├長針の孫オブジェクト
  └秒針の孫オブジェクト
2-1 - コピー.jpg

注意!
VCIサブアイテムはVCIオブジェクトの下に配置すると言う決まりがあります。
今回の場合「SubItem」という空フォルダをサブアイテムにするため、
「Plane」内に元々あったVCI Sub Item、Rigidbody、BoxColliderは削除しましょう。

また、「SubItem」にはBoxColliderを追加しましょう。持てなくなるからね。

--各オブジェクトの取得
local Plane = vci.assets.GetTransform("Plane")
local SubItem = vci.assets.GetTransform("SubItem") --追加
local tyousin = vci.assets.GetTransform("tyousin")

--各変数
local count=0

function update()
    count=count+1

    --針の座標設定 ベースとなるオブジェクトのrotationと針の角度をかけ算
    --rot_base=Plane.GetRotation() ※コメントアウト
    rot_base=SubItem.GetRotation() --追加

    --表側の座標設定 ボードのrotationと針の角度をかけ算
    rot_tyousin=rot_base*Quaternion.Euler(0,count,0)

    --針のローテーション
    tyousin.SetRotation(rot_tyousin)
end

回転.gif

あれれーおかしいぞー
ちなみにX回転、Y回転、Z回転それぞれ行っても文字盤に沿って回転してくれませんでした。

○原因は?

・今回の場合、親オブジェクトがX軸に90度傾いた状態で、針をY軸回転させることで
 文字盤に沿って回転してくれていました。
・「SubItem」のX軸を0度としたため回転軸が合わなくなりました。
・構成によっては、例えばX軸とY軸を回転させても同じ回転をするジンバルロックという
 事象が起きることも。

○対応その2(プログラムでの解決方法)

・前回の記事で書いたとおりQuaternion.Euler(0,count,0)をQuaternion.Euler(90,count,0)
 などで書き換えてもうまくいかない、ならどうするか?
 
 もう一個かけ算をするんだよ!

・ということで今回は
 rot_base* Quaternion.Euler(90,0,0) *Quaternion.Euler(0,count,0)のように、
 基準×X軸90度回転×回転運動とすればうまくいきます。

2025112601133667.gif

・うまくいったのですが・・今回作りたい物は時計、果たして短針、長針、秒針それぞれに
 Quaternion.Euler(90,0,0)のコードを書くのはスマートなのか???

・共通事項となるrot_base=SubItem.GetRotation()を
 rot_base=SubItem.GetRotation()*Quaternion.Euler(90,0,0)としても良いのだが・・。

○対応その3(Unityでの解決方法)

・Unity上で解決すべく新たにX軸を90度回転させた「Hand_Object」を作成し、
 そこに針のオブジェクトを格納しましょう。

 ※どの軸を傾けるべきかは構成次第なのでうまくいかないときは組み合わせを変えよう。

☆フォルダー構成
 clockというVCIオブジェクト
 └SubItemという空フォルダ(サブアイテム) スケール比は1:1:1
  ├Planeという文字盤
  └Hand_Objectという空フォルダ X軸だけ90度傾け
   ├短針の孫オブジェクト
   ├長針の孫オブジェクト
   └秒針の孫オブジェクト
3-1 - コピー.jpg

--各オブジェクトの取得
local Plane = vci.assets.GetTransform("Plane")
local SubItem = vci.assets.GetTransform("SubItem")
local Hand_Object = vci.assets.GetTransform("Hand_Object") --追加
local tyousin = vci.assets.GetTransform("tyousin")

--各変数
local count=0

function update()
    count=count+1

    --針の座標設定 ベースとなるオブジェクトのrotationと針の角度をかけ算
    --rot_base=Plane.GetRotation() ※コメントアウト
    --rot_base=SubItem.GetRotation() ※コメントアウト
    rot_base=Hand_Object.GetRotation() --追加

    --表側の座標設定 ボードのrotationと針の角度をかけ算
    rot_tyousin=rot_base*Quaternion.Euler(0,count,0)

    --針のローテーション
    tyousin.SetRotation(rot_tyousin)
end

・こうすることでオブジェクト構成とLuaでのオブジェクトの指定を変更素だけで
 同じ結果を得られます。コードもすっきり。

○まとめ

・なんだか針を回転させるためにコードにクォータニオン計算を1文追加してで解決するか、
 オブジェクト構成によって解決するかという話になってしまいました。

・コードは少なく見やすい方が良いのでオブジェクト構成で解決したいのですが
 そこは人それぞれ、やりやすい方法で取り組みましょう。

・まぁ偉そうなことを言ったところでUnityでVCI出力→アップロード→修正→VCI出力・・・
 を繰り返すのが面倒でコード上で何とかしてしまうことも多々ありますが。

○おまけ

・因みに時分秒を取得して360度の角度を求めるのに以下の感じで書いています。

    byou_rot_offset=os.date("!%S")*6
    fun_rot_offset=os.date("!%M")*6+os.date("!%S")/10
    ji_rot_offset=(os.date("!%H")+9)*30+os.date("!%M")/2

・os.date( "!%S" )で世界標準時の秒、os.date( "!%M" )で世界標準時の分が取れます。
 os.date( "!%H" )では世界標準時の時であり、日本は世界標準時+9時間なので+9します。

・なぜ世界標準時から算出しているか?
 os.date( "%H" )だけでも一見すると同じ動きをしますが、これは自PCの時間を返します。
 つまり外国からログインしてくれているユーザーが居るとタイムゾーンが違うため、
 違う時刻を表示してしまうからです。
 (JSTを押しつけるのは良いのか?という問題はあるけど・・)

・秒針は60秒で360度となってほしいので秒×6
・分針は60分で360度となってほしいので分×6、なめらかに動かすのに+秒/10
・短針は12時間で360度となってほしいので時×30、なめらかに動かすのに+分/2
 となります。

おまけのおまけ

・バーチャルキャストではVCIを手に持った際に行えるスケール変更は
 最小値0.2、最大値10と決まっています。

 このため、細部アイテムのサイズが0.01等で設定されている場合にスケール変更すると
 0.01→0.2 といきなりサイズが大きくなります(逆もしかり)。

・今回の記事ではサブアイテムにスケールや角度が入っていると~から始まりましたが、
 このように極端な大きさのアイテムを扱う際にも空フォルダをサブアイテムとして挟む
 ことで綺麗に納まります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?