Unity で三角比を使って辺の長さや角度を求める
3D空間で、Shaderのテカリを制御したり、カメラやオブジェクトの挙動などを制御したい際には、とにかく角度や2点間の距離を求めることが多い。
そして残念ながら、使う側の人間(私)は度数法に染まりきっているため、ラジアンだとどれくらいの角度かパッとイメージできない。くやしい。 (「最大角度 0.2 rad まで許容」みたいな入力項目には「具体的にどんくらいだよ・・」ってなる残念な頭である。とはいえ、物理カメラの画角なども一般的には度数法で表すので、一般的・・のハズ)
なので、わたしでもビャッと使える関数群を用意してみた。よかったらお使いください(意外とネットにはこういったサンプルコードが転がってなかったので・・)
using UnityEngine; public static class Util { // 斜辺 public static float HypotenuseByBoAn(float bottom, float angle) => bottom / Mathf.Cos(angle * Mathf.Deg2Rad); public static float HypotenuseByBoHe(float bottom, float height) => bottom / Mathf.Cos(Mathf.Atan2(height, bottom)); public static float HypotenuseByHeAn(float height, float angle) => height / Mathf.Sin(angle * Mathf.Deg2Rad); // 底辺 public static float BottomByHeAn(float height, float angle) => height / Mathf.Tan(angle * Mathf.Deg2Rad); public static float BottomByHyAn(float hypotenuse, float angle) => hypotenuse * Mathf.Cos(angle * Mathf.Deg2Rad); public static float BottomByHeHy(float height, float hypotenuse) => Mathf.Sqrt(Mathf.Pow(hypotenuse, 2) - Mathf.Pow(height, 2)); // 高さ public static float HeightByBoAn(float bottom, float angle) => bottom * Mathf.Tan(angle * Mathf.Deg2Rad); public static float HeightByHyAn(float hypotenuse, float angle) => hypotenuse * Mathf.Sin(angle * Mathf.Deg2Rad); public static float HeightByBoHy(float bottom, float hypotenuse) => Mathf.Sqrt(Mathf.Pow(hypotenuse, 2) - Mathf.Pow(bottom, 2)); // 角度 public static float AngleByBoHe(float bottom, float height) => Mathf.Atan2(height, bottom) * Mathf.Rad2Deg; public static float AngleByBoHy(float bottom, float hypotenuse) => Mathf.Acos(bottom / hypotenuse) * Mathf.Rad2Deg; public static float AngleByHeHy(float height, float hypotenuse) => Mathf.Asin(height / hypotenuse) * Mathf.Rad2Deg; }
浮動小数点の演算なので、許容誤差 0.00001
に設定、直角三角形を題材にしてテストを実施している。
[Test] public void HypotenuseByBoAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(2, Util.HypotenuseByBoAn(route3, 30), delta); Assert.AreEqual(route2, Util.HypotenuseByBoAn(1, 45), delta); Assert.AreEqual(2, Util.HypotenuseByBoAn(1, 60), delta); } [Test] public void HypotenuseByBoHe() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(2, Util.HypotenuseByBoHe(route3, 1), delta); Assert.AreEqual(route2, Util.HypotenuseByBoHe(1, 1), delta); Assert.AreEqual(2, Util.HypotenuseByBoHe(1, route3), delta); } [Test] public void HypotenuseByHeAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(2, Util.HypotenuseByHeAn(1, 30), delta); Assert.AreEqual(route2, Util.HypotenuseByHeAn(1, 45), delta); Assert.AreEqual(2, Util.HypotenuseByHeAn(route3, 60), delta); } [Test] public void BottomByHeAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(route3, Util.BottomByHeAn(1, 30), delta); Assert.AreEqual(1, Util.BottomByHeAn(1, 45), delta); Assert.AreEqual(1, Util.BottomByHeAn(route3, 60), delta); } [Test] public void BottomByHyAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(route3, Util.BottomByHyAn(2, 30), delta); Assert.AreEqual(1, Util.BottomByHyAn(route2, 45), delta); Assert.AreEqual(1, Util.BottomByHyAn(2, 60), delta); } [Test] public void BottomByHeHy() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(route3, Util.BottomByHeHy(1, 2), delta); Assert.AreEqual(1, Util.BottomByHeHy(1, route2), delta); Assert.AreEqual(1, Util.BottomByHeHy(route3, 2), delta); } [Test] public void HeightByBoAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(1, Util.HeightByBoAn(route3, 30), delta); Assert.AreEqual(1, Util.HeightByBoAn(1, 45), delta); Assert.AreEqual(route3, Util.HeightByBoAn(1, 60), delta); } [Test] public void HeightByHyAn() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(1, Util.HeightByHyAn(2, 30), delta); Assert.AreEqual(1, Util.HeightByHyAn(route2, 45), delta); Assert.AreEqual(route3, Util.HeightByHyAn(2, 60), delta); } [Test] public void HeightByBoHy() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(1, Util.HeightByBoHy(route3, 2), delta); Assert.AreEqual(1, Util.HeightByBoHy(1, route2), delta); Assert.AreEqual(route3, Util.HeightByBoHy(1, 2), delta); } [Test] public void AngleByBoHe() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(30f, Util.AngleByBoHe(route3, 1), delta); Assert.AreEqual(45f, Util.AngleByBoHe(1, 1), delta); Assert.AreEqual(60f, Util.AngleByBoHe(1, route3), delta); } [Test] public void AngleByBoHy() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(30f, Util.AngleByBoHy(route3, 2), delta); Assert.AreEqual(45f, Util.AngleByBoHy(1, route2), delta); Assert.AreEqual(60f, Util.AngleByBoHy(1, 2), delta); } [Test] public void AngleByHeHy() { var delta = 0.00001f; var route2 = Mathf.Sqrt(2); var route3 = Mathf.Sqrt(3); Assert.AreEqual(30f, Util.AngleByHeHy(1, 2), delta); Assert.AreEqual(45f, Util.AngleByHeHy(1, route2), delta); Assert.AreEqual(60f, Util.ngleByHeHy(route3, 2), delta); }