Decal Projectorコンポーネントを使って、オブジェクトの表面に弾痕を残してみました。
まず、背景が透明な画像を用意して、デカールシェーダーを選択したマテリアルにアタッチしました。
Decal Projectorオブジェクトを新規作成して、インスペクタでこのマテリアルをアタッチします。新規スクリプトも付けました。
すると、シーンビューで表示される白枠のボックスの中で、他のオブジェクトの表面にこの画像が表示されます。
Decal Projectorを動かすと、画像はオブジェクトの表面に沿って動きます。
ボックスの外側には表示されません。
そこで、Decal Projectorをプレハブ化して、球が当たった時にそのポイントに弾痕が投影されるように、プロジェクターをインスタンス化することで弾痕を残します。
まずプレイヤーの頭に付けたスクリプトで弾を飛ばします。弾はSphereをプレハブ化したもので、スクリプトにアタッチされます。
スクリプトでは、マウスの左クリックで弾をインスタンス化して、発射メソッドを呼びます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun2 : MonoBehaviour
{
[SerializeField] Ball2 ballPrefab;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Shoot();
}
}
void Shoot()
{
Ball2 ball = Instantiate(ballPrefab, transform.position + transform.forward * 2f, transform.rotation).GetComponent<Ball2>();
ball.Fire(transform.forward);
}
}
弾のプレハブにはRigidbodyを付けます。スクリプトでは、プロジェクターのプレハブをアタッチして、OnCollisionEnterの中で、接触点を求めてインスタンス化します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball2 : MonoBehaviour
{
[SerializeField] BulletHoles bulletHoles;
// Start is called before the first frame update
void Start()
{
Destroy(gameObject, 3f);
}
public void Fire(Vector3 dir)
{
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(dir * 15f, ForceMode.Impulse);
}
private void OnCollisionEnter(Collision collision)
{
Destroy(gameObject);
ContactPoint cp = collision.GetContact(0);
Instantiate(bulletHoles, cp.point + cp.normal * 0.8f, Quaternion.LookRotation(-cp.normal));
}
}
弾がインスタンス化するとすぐにDestroyメソッドが呼ばれて、3秒後に破壊されます。それまでに弾が衝突するとDestroyメソッドですぐに破壊されて、ContactPointから接触点と、接触点の法線からプロジェクターをインスタンス化する位置と回転を求めます。
インスタンス化する位置は接触点から法線方向へ少し動かした位置です。プロジェクターのボックスのサイズが1なので、法線方向をそのまま使うと、面が重なったときのようなノイズがでます。
回転は、接触点の方を向くときのもので、Quaternion.LookRotationメソッドで簡単に得られます。
これで簡単に弾痕を残せました。
プロジェクターのスクリプトでは、インスタンス化されて4秒後に破壊されるようにしています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletHoles : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Destroy(gameObject, 4f);
}
}
オブジェクトの面の端を狙うと、画像が引き伸ばされてしまいます。
そこで、プロジェクターをインスタンス化する位置を接触点に近づけて、プロジェクターのボックスもZ軸方向に短くしました。
// Ball2
private void OnCollisionEnter(Collision collision)
{
Destroy(gameObject);
ContactPoint cp = collision.GetContact(0);
Instantiate(bulletHoles, cp.point + cp.normal * 0.1f, Quaternion.LookRotation(-cp.normal));
}
しかし、位置が近すぎたりボックスが小さすぎると、曲がった面へはうまく投影されなくなります。