今回は銃を撃った後に弾痕を当たった位置に残したいと思います。
リアルな世界であれば物に銃弾が当たった場合に弾の当たった跡が残ります。
そんな事どうやってやるんだ!?
と思いますが、何の事はない弾痕をゲームオブジェクトとして作成しておき、それをインスタンス化して、当たった位置に表示するだけです。
今回の機能を作成すると
↑のような感じで銃で撃った場所に弾痕が残るようになります。
敵の当たり判定のコライダが大ざっぱだと弾痕が宙に浮いて見えてしまいます。
まずは弾痕をゲームオブジェクトとして用意したいので、それ用の弾痕の画像を探したんですが、無料で配布されているものが見つからなかったので、自分で作成しました。(^_^;)
ご自分で作成される場合はWindowsならペイントで絵を作り、GIMPで透過するといいです。
用意するのが面倒な方は上の画像をご自由にお使いください。(右クリックから名前をつけて保存)
弾痕をプレハブにする
UnityのAssetsメニューからImport New Assetを選択し、弾痕画像をインポートしてください。
ヒエラルキー上で右クリックから3D Object→Planeを選択し、名前をBulletHoleとします。
BulletHoleのインスペクタでMesh Colliderコンポーネントは必要ないので削除しておきます。
BulletHoleが出来たらBulletHoleにインポートした弾痕の画像をドラッグ&ドロップします。
BulletHoleのインスペクタでBulletHoleのShaderを↑のように変更します。
これで透過処理した画像が反映されます。
Shaderは全然詳しくないので、解説は出来ません・・・・。
ある程度BulletHoleのサイズを小さくした後で、銃のゲームオブジェクトの近くへ持っていき、およその弾のサイズと一致するようにサイズを調整します。
Shaderを変更する前にこちらの画像を保存してしまったので、背景が表示されてしまっていますが・・・、こんな感じでサイズを調整します。
X、Y、Zのサイズをそれぞれ0.005にしました。
これで弾痕のゲームオブジェクトが出来上がりましたので、Assetsフォルダにドラッグ&ドロップしてプレハブにしておきます。
プレハブにしたらシーン上の弾痕オブジェクトを削除します。
弾痕を残す為の処理を追加する
次に銃を撃った時に当たった位置に弾痕のゲームオブジェクトを配置する処理を記述します。
今までの記事をご覧いただいた方は、銃を撃った時の処理Shotスクリプトを修正していきます。
それ以外の方はご自分の銃を撃った時の処理のところに追加してください。
まずはShotスクリプトに先ほど作った弾痕を設定するフィールドを作成します。
1 2 3 4 5 | // 弾痕のプレハブ [SerializeField] private GameObject bulletHolePrefab; |
インスペクタには弾痕のプレハブを設定します。
次に銃を撃った時の処理に弾痕を残す処理を追加します。
Shotスクリプトで銃を撃った時に何かと当たったかどうかを判断しているのはJudgeShotメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | // 敵に弾が当たったかどうか public void JudgeShot() { // 弾がなければ弾がない時の効果音を鳴らしreturnで以降の処理をさせない if (myStatus.GetNumberOfBullet() <= 0) { PlaySE(noBulletSE); return; } // 銃のアニメーション gunAnimator.SetTrigger("Shot"); // 弾をインスタンス化し飛ばす var bulletInstance = Instantiate<GameObject>(bulletPrefab, bulletExitPosition.position, equip.transform.rotation); var bulletRigid = bulletInstance.GetComponent<Rigidbody>(); bulletRigid.AddForce(bulletExitPosition.forward * 150f); bulletRigid.AddTorque(Random.insideUnitSphere * 360f); Destroy(bulletInstance, 3f); // 銃の発砲の光を表示 AbleLight(); // 銃の発砲音を鳴らす PlaySE(shotSE); // 弾の数を減らし、戻り値をテキストに設定 bulletText.text = myStatus.SetNumberOfBullet(myStatus.GetNumberOfBullet() - 1).ToString(); Ray ray = new Ray(muzzle.position, muzzle.forward); RaycastHit hitPointField; RaycastHit hitPointEnemy; distance = rayRange; if (Physics.Raycast(ray, out hitPointField, myStatus.GetWeaponStatus().GetWeaponRange(), LayerMask.GetMask("Field"))) { distance = Vector3.Distance(muzzle.position, hitPointField.point); } if (Physics.Raycast(ray, out hitPointEnemy, myStatus.GetWeaponStatus().GetWeaponRange(), LayerMask.GetMask("EnemyHit"))) { if (distance > Vector3.Distance(muzzle.position, hitPointEnemy.point)) { var enemyObj = hitPointEnemy.collider.gameObject.transform.root; if (enemyObj.GetComponent<Enemy>().GetState() != Enemy.EnemyState.Dead) { var enemy = enemyObj.GetComponent<Enemy>(); enemy.TakeDamage(myStatus.GetWeaponStatus().GetShotPower(), hitPointEnemy.point, hitPointEnemy.collider.transform); } } } if (hitPointEnemy.transform != null) { // 敵に接触してる場合は敵に弾痕を残す var bulletHoleInstance = Instantiate<GameObject>(bulletHolePrefab, hitPointEnemy.point - muzzle.forward * 0.001f, Quaternion.FromToRotation(Vector3.up, hitPointEnemy.normal), hitPointEnemy.collider.transform); } else if (hitPointField.transform != null) { var bulletHoleInstance = Instantiate<GameObject>(bulletHolePrefab, hitPointField.point - muzzle.forward * 0.001f, Quaternion.FromToRotation(Vector3.up, hitPointField.normal), hitPointField.collider.transform); } } |
穴を開けたいゲームオブジェクトとレイがヒットした時は、InstantiateでbulletHolePrefabを作成します。
配置する位置は当たった位置から銃口の向いている方向 * 0.001を引いた位置に設定します。
ヒットした位置をまったく同じにすると画像が点滅してしまうので、ほんの少しだけ主人公側に移動させます。
角度は
1 2 3 | Quaternion.FromToRotation(Vector3.up, hitPoint.normal); |
で上方向Vector3.upとヒットした面の角度hitPoint.nomalの間の角度を算出します。
Quaternion.FromToRotationは二つの方向から角度を算出してくれます。
敵に設定した当たり判定用のコライダによっては弾痕がうまく残りません。
なぜならコライダのヒットした位置と角度で弾痕の位置を指定しているので、敵の見た目のメッシュと当たり判定で使用しているコライダとが完全には一致しないからです。
大雑把な当たり判定をしている時は弾痕を残さない方がいいかもしれません。
そんな事情で敵キャラに弾痕を残す場合は当たり判定のコライダを細かく作成しないと、
空中に浮いているようになってしまいます。
の記事で細かく当たり判定する方法を書いていますが、さらに細かくする必要もあるかもしれません。
敵と接触していた場合は敵に弾痕を残し、それ以外で地面や壁等のFieldレイヤーを設定したゲームオブジェクトに接触した場合はそこに弾痕を残すようにしてます。
上がブロックに弾を撃ち込んだ時です。
ブロックにはRigidbodyを付け、Is Kinematicにチェックを入れておく必要があります(Rigidbodyは特に必要ないかもしれません)。
マニュアルには
凸状じゃないメッシュコライダーはリジッドボディがついてないゲームオブジェクトにだけ作用する
という事らしいんですが、今回のブロックはBox Colliderを使用していますが、形状が同じだからMesh Colliderと
同じになってるんですかね。エラーになります。
Mesh Colliderについては詳しく調べてみる必要がありそうです。
敵キャラであるゾンビを撃った時の弾痕は上のようになります。
ゾンビの血の色を表現する場合は弾痕の中心を赤っぽくするといいかもしれません。