小ネタ。
表題の問題を解こうとネットを検索するとたいてい平面の数式に遭遇し、オブジェクトの回転に変換する方法を見つけにくいので書いてみる。大した話ではなく、メモ程度。
要件として
ここでは
「ある物体の角度に応じて、その物体の正面の障害物を壁で覆うようにQuadオブジェクトを配置したい」
とする。
なお Plane を使う場合は面の方向が90度異なるので、以下の説明に補正が必要になる。「正面」でない場合も同様。
作戦は
「物体から正面に3点のレイキャストを発行し、ぶつかった3点に沿うように Quad オブジェクトを移動する」
としよう。このとき、検出された3点に沿うように矩形オブジェクトの姿勢を指定するのが今回のテーマ。
外積から法線を得る
3点あると三角形ができるが、そのうちの二辺のベクトルで外積を作ると、平面に垂直なベクトルを得られる。じっさい平面の式というのは法線ベクトルそのものなので、これを知ってればこの問題は解けたも同然。
LookRotation
Unity にはベクトルに向かうための Quaternion.LookRotation という関数があるので、先程の法線に適用すれば完成。なお安定性のため、第二引数の up ベクトルも指定する。法線ベクトルとワールド空間の右ベクトルの外積で fwd というベクトルを作っておく(状況に応じて対応は分かれる)。
code
以下。Quadのpositionは3点の重心とした。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Wall : MonoBehaviour
{
public Transform Target;
void FixedUpdate()
{
var orgPoints = new Vector3[] {
new Vector3( 1f, 1f, 0f),
new Vector3(-1f, 1f, 0f),
new Vector3( 0f,-1f, 0f),
};
var hitPoints = new Vector3[3];
bool existsCollider = true;
for (var i = 0; i < orgPoints.Length; ++i) {
var org = Target.TransformPoint(orgPoints[i]);
var dir = Target.TransformDirection(Vector3.forward);
RaycastHit hit;
if (Physics.Raycast(org, dir, out hit, Mathf.Infinity, 1 << 3 /* layerMask */)) {
hitPoints[i] = hit.point;
} else {
existsCollider = false;
}
}
if (existsCollider) {
var norm = Vector3.Cross(hitPoints[1] - hitPoints[0], hitPoints[2] - hitPoints[0]).normalized;
var up = Vector3.Cross(norm, Vector3.right);
var center = (hitPoints[0] + hitPoints[1] + hitPoints[2])/3f;
var rot = Quaternion.LookRotation(norm, up);
transform.position = center;
transform.rotation = rot;
}
}
}
このスクリプトを Quad オブジェクトに適用し、Target には判定するためのプレイヤーなどの物体を入れておく。レイキャストを投じる対象はレイヤー3を指定。
以上です。