HeliScriptで振り子を動かす【コピペで使えるサンプルコード付き】

:::note info
本記事は、[HeliScriptでリッチなアスレチックワールドを作ろう！【コピペで使えるサンプルコード付き】](https://qiita.com/IwahanaFuku/private/63e6bbca2bc1f13ed57e)の記事の一部です

VR法人HIKKYのorganization下のQiita/Zenn両方に投稿しております。
:::
## はじめに
[HeliScriptでアイテムを反復して動かす【コピペで使えるサンプルコード付き】](https://qiita.com/IwahanaFuku/items/dfc38371ad0ed7cc034d)で作成した、
[hsMathSine(float radian)](https://vrhikky.github.io/VketCloudSDK_Documents/12.3/hs/hs_system_function_math.html#hsmathsinfloat)関数による反復する動きと、[HeliScriptでアイテムを回転させる【コピペで使えるサンプルコード付き】](https://qiita.com/IwahanaFuku/private/48611f49b875529cedf0)で作成した、[SetQuaternion(Quaternion Rotate)](https://vrhikky.github.io/VketCloudSDK_Documents/12.3/hs/hs_class_item.html#setquaternion)関数による、回転の動きを組み合わせることで、振り子のような動きを作ることが出来ます。
機械仕掛けっぽい動作を作ることが出来るので、単純な回転や移動にはない雰囲気のある動きを作ることが出来ます。

プロパティとしては、Vector3のオイラー角で振り子が揺れるベクトルと、floatで揺れる幅の大きさ。Vector3でオフセットの値を設定できるようにすることで、振り子の大きさによって振り子の周波数が変化する表現や、連続して並べることで一気に移動しないといけないエリアを作ることが出来るようになります。

![2024-04-22 20-29-44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629942/c3c8fd93-954c-57b0-581f-3211975b9e27.gif)

**Unity設定画面**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629942/ed32ef5f-1d9f-251e-ad05-6931f1f38356.png)


**コード全文**
```C#:ItemPendulum.hs
component ItemPendulum
{
	Utility m_UT; //Utilityクラスを宣言する
	Item m_item;
	
	Vector3 m_oneRoundDurationEuler; //反復するスピード
	float m_amplitude;  //反復の開始地点
	Vector3 m_offset; //反復の開始地点

	Quaternion m_itemInitialQua;
	Vector3 m_timeCounter;

	public ItemPendulum()
	{
		m_UT = new Utility(); //Utilityクラスを初期化する
		m_item = hsItemGetSelf();

		Vector3 beforOneRoundDurationEuler = m_UT.StrToVector3(m_item.GetProperty("oneRoundDurationEuler(Vector3)"));
		m_oneRoundDurationEuler = m_UT.Vector3MulFloat(beforOneRoundDurationEuler, 2 * PI);
		m_amplitude = m_item.GetProperty("amplitude(float)").ToFloat();

		Vector3 beforOffset = m_UT.StrToVector3(m_item.GetProperty("offset(Vector3)"));
		m_offset = m_UT.Vector3MulFloat(beforOffset, 2 * PI);

		m_itemInitialQua = m_item.GetQuaternion();
		m_timeCounter = new Vector3();
	}

    //毎フレーム呼びだされる
	public void Update()
	{
        PendulumItem();
	}

	//振り子の動き
	void PendulumItem()
	{
		//反復する方向と大きさに経過時間をかけて、Sinカーブに設定する値を求める
		m_timeCounter.Add(m_UT.Vector3MulFloat(m_oneRoundDurationEuler, hsSystemGetDeltaTime()));

		//オフセットに設定した値を足し合わせる
		Vector3 m_offsetTimeCounter = makeVector3(m_timeCounter.x + m_offset.x, m_timeCounter.y + m_offset.y, m_timeCounter.z + m_offset.z);

		//ベクトルの要素ごとにSinカーブを求める
		Vector3 sinRotateEuler = makeVector3(hsMathSin(m_offsetTimeCounter.x), hsMathSin(m_offsetTimeCounter.y), hsMathSin(m_offsetTimeCounter.z));

		//振幅の大きさを求める
		Vector3 resultRotateEuler = m_UT.Vector3MulFloat(sinRotateEuler, m_amplitude);

		//反復した位置に対して、最初の配置位置を加える
		Quaternion resultQuaternion = makeQuaternionMul(m_itemInitialQua, makeQuaternionEuler(resultRotateEuler.x, resultRotateEuler.y, resultRotateEuler.z));

		//回転を設定する
		m_item.SetQuaternion(resultQuaternion);
	}
}
```
## コード解説

今回もstring型のpropertyを、Vector3に変換するため、前回の記事で作成した、[Utilityクラス](https://qiita.com/IwahanaFuku/private/dfc38371ad0ed7cc034d#%E3%82%B3%E3%83%BC%E3%83%89%E8%A7%A3%E8%AA%AC)の、`StrToVector3(string str)`を使用しています。


### アイテムを回転させる
```C#
	//振り子の動き
	void PendulumItem()
	{
		//反復する方向と大きさに経過時間をかけて、Sinカーブに設定する値を求める
		m_timeCounter.Add(m_UT.Vector3MulFloat(m_oneRoundDurationEuler, hsSystemGetDeltaTime()));

		//オフセットに設定した値を足し合わせる
		Vector3 m_offsetTimeCounter = makeVector3(m_timeCounter.x + m_offset.x, m_timeCounter.y + m_offset.y, m_timeCounter.z + m_offset.z);

		//ベクトルの要素ごとにSinカーブを求める
		Vector3 sinRotateEuler = makeVector3(hsMathSin(m_offsetTimeCounter.x), hsMathSin(m_offsetTimeCounter.y), hsMathSin(m_offsetTimeCounter.z));

		//振幅の大きさを求める
		Vector3 resultRotateEuler = m_UT.Vector3MulFloat(sinRotateEuler, m_amplitude);

		//求めた振幅の回転と、元の回転を合成する
		Quaternion resultQuaternion = makeQuaternionMul(m_itemInitialQua, makeQuaternionEuler(resultRotateEuler.x, resultRotateEuler.y, resultRotateEuler.z));

		//回転を設定する
		m_item.SetQuaternion(resultQuaternion);
	}
}
 ```
おおよそのベースとなるスクリプトは、[反復した動きを作る](https://qiita.com/IwahanaFuku/items/dfc38371ad0ed7cc034d#%E5%8F%8D%E5%BE%A9%E3%81%97%E3%81%9F%E5%8B%95%E3%81%8D%E3%82%92%E4%BD%9C%E3%82%8B) で実装した内容で、移動として実装していた内容が、オイラー角の回転と、それをクオータニオンに変換して、アイテムに設定するスクリプトとなっています。

[hsMathSin(float radian)](https://vrhikky.github.io/VketCloudSDK_Documents/12.3/hs/hs_system_function_math.html#hsmathsinfloat)関数をもとにした反復する動きを実現するための関数です。hsMathSin関数は、Unity C#だと[Mathf-Sin](https://docs.unity3d.com/ja/current/ScriptReference/Mathf.Sin.html)関数と同じ挙動を行う関数であり、引数として渡した値をラジアンの角度とした、円周のx座標位置を返す関数です。入力に対して反復するような値を取得することが出来るため、この性質を利用して反復する動きを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629942/1c6d460a-bb99-0b20-5e58-1a10f7cdc127.png)

Sin関数について馴染みのない方は、UnityでSin関数についてより詳しく解説されている記事などをご参照ください。
[【Unity】Sin関数の使い方](https://qiita.com/Nekomasu/items/f526b9392fd16f2bd243)
[Unity C#で学ぶ数学 #三角関数](https://qiita.com/miya-z/items/e5134687712ecff9104b#%E4%B8%89%E8%A7%92%E9%96%A2%E6%95%B0)

これでこのシリーズでの回転や移動を使用したコンポーネントは以上となります！
HeliScriptによる移動や回転の実装は、UnityのC#とは少し違った記載方法となっていたりしますが、記述方法を学ぶことによって同様の動作を実装できるようになります。

次回は複数のコンポーネントを使って、キャラクターが足場に触れると、その足場が揺れ始めて、数秒後に足場が落下するコンポーネントを作っていきます。

今まで作ったコンポーネントを活用しながら、より複雑な条件を持つギミックを作ります。

 **前 :** [HeliScriptでアイテムを回転させる【コピペで使えるサンプルコード付き】](https://qiita.com/IwahanaFuku/private/48611f49b875529cedf0) 
 **次 :** [HeliScriptでアイテムを揺らす【コピペで使えるサンプルコード付き】](https://qiita.com/IwahanaFuku/private/607d73db767362c0162f)