Reputation: 49
I am learning Unity by this Japanese Website. https://feynman.co.jp/unityforest/game-create-lesson/fps-game/enemy/
I want to use "inheritance" in Unity. I made a base class in the following.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBase : MonoBehaviour
{
// 最大HP.
[SerializeField] protected int maxHp = 3;
// 現在のHP.
protected int hp = 0;
// 攻撃を受けるフラグ.
protected bool canHit = true;
protected virtual void Start()
{ Init();
}
protected virtual void Update()
{
}
// ---------------------------------------------------------------
/// <summary>
/// 初期化処理.
/// </summary>
// ---------------------------------------------------------------
public virtual void Init()
{ hp = maxHp;
}
// ---------------------------------------------------------------
/// <summary>
/// コライダーエンター処理.
/// </summary>
/// <param name="col"></param>
// ---------------------------------------------------------------
public virtual void OnEnemyColliderEnter(Collision col)
{
Debug.Log("Even this one does not call" + col.gameObject.name);
if (col.gameObject.tag == "Arrow" && canHit == true)
{
// Arrowを取得して「Arrow」の敵にヒットした時の処理を実行.
var arrow = col.gameObject.GetComponent<Arrow>();
arrow.OnEnemyHit();
// HPを矢の攻撃力分マイナス.
hp -= arrow.Attack;
if (hp <= 0)
{
// 死亡時処理.
OnDead();
}
else
{
Debug.Log(gameObject.name + " に攻撃がヒット。残りHP " + hp);
// 次回ヒットまでの待機時間.
StartCoroutine(HitWait());
}
}
}
// ---------------------------------------------------------------
/// <summary>
/// 死亡時処理.
/// </summary>
// ---------------------------------------------------------------
protected virtual void OnDead()
{
Debug.Log(gameObject.name + "を倒しました");
Destroy(gameObject);
}
// ---------------------------------------------------------------
/// <summary>
/// 攻撃ヒット後次の攻撃が当たるまでの待機処理.
/// </summary>
// ---------------------------------------------------------------
IEnumerator HitWait()
{
// 指定時間待機してフラグを戻す.
canHit = false;
yield return new WaitForSeconds(0.5f);
canHit = true;
}
}
And I made a derived class here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyCrocodile : EnemyBase
{
// Start is called before the first frame update
protected override void Start()
{
base.Start();
}
protected override void Update()
{
base.Update();
}
}
And I put a picture of the inspector of Enemy.
My problem is that public virtual void OnEnemyColliderEnter(Collision col)
does not start, when the arrow hit the enemy.
And I made an arrow.cs
here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Arrow : MonoBehaviour
{
// リジッドボディ.
Rigidbody rigid = null;
// 攻撃処理フラグ.
bool isAttack = true;
// 攻撃力.
public int Attack = 1;
// 自動破壊コル-チン.
Coroutine autoDestroyCor = null;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// --------------------------------------------------------------------------
/// <summary>
/// 生成時コールバック.
/// </summary>
// --------------------------------------------------------------------------
public void OnCreated()
{
Init();
}
// --------------------------------------------------------------------------
/// <summary>
/// 発射.
/// </summary>
/// <param name="direction"> 発射方向. </param>
/// <param name="forceMode"> フォースモード. </param>
// --------------------------------------------------------------------------
public void Shoot(Vector3 direction, ForceMode forceMode)
{
rigid.useGravity = false;
rigid.isKinematic = false;
rigid.AddForce(direction, forceMode);
// 元の記載↓は削除して、それをそのまま変数に代入する形に変更.
StartCoroutine(AutoDestroy(1f));
autoDestroyCor = StartCoroutine(AutoDestroy(1f));
}
// --------------------------------------------------------------------------
/// <summary>
/// 初期化処理.
/// </summary>
// --------------------------------------------------------------------------
void Init()
{
if (rigid == null) rigid = GetComponent<Rigidbody>();
}
// --------------------------------------------------------------------------
/// <summary>
/// 自動破壊コルーチン.
/// </summary>
/// <param name="time"> 破棄までの時間. </param>
// --------------------------------------------------------------------------
IEnumerator AutoDestroy(float time)
{
yield return new WaitForSeconds(time);
Destroy(gameObject);
}
// --------------------------------------------------------------------------
/// <summary>
/// コライダーエンター.
/// </summary>
/// <param name="col"></param>
// --------------------------------------------------------------------------
public void OnArrowCollisionEnter(Collision col)
{
//if (col.gameObject.tag == "Ground" || col.gameObject.tag == "Enemy")
if (col.gameObject.tag == "Ground")
{
if (isAttack == true)
{
Debug.Log("Ground!!!!!" + col.gameObject.name);
rigid.isKinematic = true;
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
isAttack = false;
Destroy(gameObject);
}
}
}
// --------------------------------------------------------------------------
/// <summary>
/// 敵にヒットした時の処理.
/// </summary>
// --------------------------------------------------------------------------
public void OnEnemyHit()
{
if (autoDestroyCor != null)
{
StopCoroutine(autoDestroyCor);
}
rigid.isKinematic = true;
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
isAttack = false;
Destroy(gameObject);
}
}
When the arrow hit the ground, the console window shows
Ground!!!!!
So the
Debug.Log("Ground!!!!!" + col.gameObject.name);
works well.
Could you give me some tips for this problem? Thank you for reading this.
I checked "arrow" and "enemy" have a RigidBody component. I checked "arrow" and "enemy" respectively are tagged "Arrow" and "Enemy".
Upvotes: 1
Views: 87
Reputation: 90813
I think the main issue would be: I don't see where you would ever call OnEnemyColliderEnter
or OnArrowCollisionEnter
.
In Unity there is only OnTriggerEnter
and OnCollisionEnter
These two are event messages that are automatically triggered by the physics engine - but only if they have the exact correct name and signature, and if their respective conditions are met (see Layer Collision Matrix and Collider Interactions)
Otherwise you would have to call that method yourself from somewhere which I don't see no code for.
In general there are a lot of small API, syntax and logic issues in your code.
I will do my best to point them out and how I would improve/correct them
First of all as mentioned
This
public virtual void OnEnemyColliderEnter(Collision col)
and this
public void OnArrowCollisionEnter(Collision col)
should both be
protected virtual void OnCollisionEnter(Collision col)
In order to make them get recognized by the engine and called at all.
Further this (a minor API detail)
col.gameObject.tag == "Arrow"
should rather be
col.gameObject.CompareTag("Arrow")
which is not only slightly faster but also prevents silent fails if the tag is misspelled or doesn't exist at all (see CompareTag
).
(Another optional API detail) Instead of checking the tag at all and this
var arrow = col.gameObject.GetComponent<Arrow>();
you could even simply use
if(col.gameObject.TryGetComponent<Arrow>(out var arrow))
{
...
}
The tag check is redundant if you check for a specific component anyway (see TryGetComponent
).
Instead of the entire Arrow.OnEnemyHit
you could simply do
Destroy(arrow.gameObject);
There is actually no use at all to make adjustments to your Rigidbody
component if you are anyway destroying this object.
The same also goes for the Arrow.OnCollisionEnter
. If you anyway Destroy
this arrow object you can get rid of this entire code which only modifies this arrow instance you are about to destroy
rigid.isKinematic = true;
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
isAttack = false;
And then actually the isAttack
seems to become redundant as well.
Instead of your Coroutine AutoDestroy
not that Destroy
already has a built-in optional parameter float t
The optional amount of time to delay before destroying the object.
so you can simply do
private void Start()
{
Destroy(gameObbject, 1f);
}
And in general, if you don't need Unity message methods like Start
, Update
etc, then don't implement them.
Empty message methods only cost resources.
And if you don't extend/override the behavior of a virtual
method then there is also no need to explicitly have a
protected override void Start()
{
base.Start();
}
you can completely delete the methods from EnemyCrocodile
.
Upvotes: 1