面白そうな問題だと思いまして、ちょっと考えてみました。
まず、シーン上には下図のようにオブジェクトを配置しました。
そしてPistolに下記のようなスクリプトをアタッチし...
lang
1using UnityEngine;
2
3public class Pistol : MonoBehaviour
4{
5 [SerializeField] private Transform target;
6 [SerializeField] private Transform muzzle;
7 [SerializeField] private Transform breech;
8
9 private void Update()
10 {
11 // ターゲット、銃口、銃尾の銃ローカル座標を求める
12 var localTargetPosition = this.transform.InverseTransformPoint(this.target.position);
13 var localMuzzlePosition = this.muzzle.localPosition;
14 var localBreechPosition = this.breech.localPosition;
15
16 // 射線およびターゲットが銃の回転中心からどれだけ離れているかを求める
17 var localRay = new Ray(localBreechPosition, localMuzzlePosition - localBreechPosition);
18 var localRaySqrDistance = Vector3.Cross(localRay.direction, -localRay.origin).sqrMagnitude;
19 var localTargetSqrRadius = localTargetPosition.sqrMagnitude;
20 if (localRaySqrDistance > localTargetSqrRadius)
21 {
22 // もし射線の距離がターゲットの距離より大きければ、どの向きに回してもターゲットを狙うことはできないはず
23 // 極端な話だと、もしターゲットが銃を握っている自分の手だとすると、手首をどうひねろうが
24 // 自分の手を撃つことはできないかと思います
25 Debug.Log($"Can't aim {this.target.name}!");
26 return;
27 }
28
29 // 二次方程式の解の公式によりターゲット距離を半径とする球と射線の交点を求める
30 var a = localRay.direction.sqrMagnitude;
31 var b = Vector3.Dot(localRay.origin, localRay.direction);
32 var c = localRay.origin.sqrMagnitude - localTargetSqrRadius;
33 var d = Mathf.Sqrt((b * b) - (a * c)) / a;
34 var e = -b / a;
35 var t0 = e - d;
36 var t1 = e + d;
37 var t = t0 >= 0.0f ? Mathf.Min(t0, t1) : t1;
38
39 // ターゲットをこの位置に捉えたいので...
40 var desiredLocalTargetPosition = localRay.GetPoint(t);
41
42 // 銃に適用する回転はこうなる
43 this.transform.rotation = Quaternion.FromToRotation(desiredLocalTargetPosition, localTargetPosition) * this.transform.rotation;
44 }
45}
インスペクター上でtarget
、muzzle
、breech
にシーン上の各オブジェクトをセットして動かすと、下図のようになりました。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。