如何在 Unity 中有效地使用继承?
How can I use inheritance in Unity effectively?
我对继承有基本的了解,但由于我想要作为游戏对象组件的每个脚本都必须继承自 MonoBehaviour,因此事情变得棘手。
假设我想要一个名为 Character 的基础 class,并且我希望 Player 和 Enemy 派生自 Character。我应该如何将 Player 和 Enemy 脚本添加到 Player 和 Enemy 对象而不会出现错误?
我还尝试创建空游戏对象并向其添加 CharacterStats,然后用具有不同统计数据的不同角色对象填充此 class。你猜怎么着,如果脚本派生自 Monobehaviour,则不能使用 new 关键字来创建对象。
是否有一些关于这个主题的教程可以使它更清楚?
用好继承来自于规划和迭代。也许你的角色 class 不能很好地作为你的敌人的 class。我倾向于让玩家在他们自己的 class 中(除非是多人游戏),而敌人使用他们自己的基地 class。下面是我在我正在开发的 2D 游戏中使用的一些继承的示例。
基地Class:
Enemy.cs
public abstract class Enemy : MonoBehaviour
{
public Vector2 flyInPos;
public Vector2 startPos;
public bool isFlyingIn;
public float flyInDuration = 5000f;
public float flyInTime = 0f;
public abstract void Destroy();
public abstract void DestroyWithoutScore();
public abstract void Attack();
public abstract void Attack2();
public abstract void Attack3();
public abstract void FlyOut();
public virtual void FlyIn()
{
isFlyingIn = true;
}
}
每个敌人都需要有这些元素。你会注意到我的基础 class 中的大多数方法都被标记为 abstract 这样每个 class 继承的方法都可以有一组独特的攻击和飞行模式.需要注意的一件事是基础 class 继承自 MonoBehaviour。以下是我如何在我的一个敌人身上实现继承。抱歉,它仍然是 WIP。
继承Class:
ShootingDrone.cs
public class ShootingDrone : Enemy
{
public GameObject projectileSpawner;
public GameObject projectile;
public void Start()
{
startPos = transform.position;
Scheduler.Invoke(Attack, 5f, gameObject);
}
public void Update()
{
if(isFlyingIn)
{
transform.position = Vector3.Lerp(startPos, flyInPos, flyInTime / flyInDuration);
flyInTime += Time.deltaTime;
}
}
public override void Attack()
{
Shoot();
Scheduler.Invoke(Shoot, 0.25f, gameObject);
Scheduler.Invoke(Shoot, 0.5f, gameObject);
}
public override void Attack2()
{
Attack();
}
public override void Attack3()
{
Attack();
}
public void Shoot()
{
GameObject newProjectile = Instantiate(projectile, projectileSpawner.transform);
newProjectile.transform.parent = null;
}
public override void FlyOut()
{
throw new System.NotImplementedException();
}
public override void DestroyWithoutScore()
{
throw new System.NotImplementedException();
}
public override void Destroy()
{
Destroy(gameObject);
}
}
Enemy 的基础 class 继承自 MonoBehaviour。因此,当 Shooting Drone 继承自 Enemy 时,它也继承自 MonoBehaviour。
针对您尝试添加新的 MonoBehaviour 的问题。这样做的原因是 MonoBehavior 必须附加到某些东西上。想象一个没有 GameObject 的刚体,它是行不通的。如果你想制作一个“新的”CharacterStats,你会这样做:
CharacterStats stats = gameObject.AddComponent<CharacterStats>();
如果您不想将 CharacterStats 作为一个组件,那么只需从 class 中删除 MonoBehavior 继承并将其实例化为新的 CharacterStats。
有多种教程介绍了继承,但是根据您对这个主题的了解程度,我会从 Unity official inheritance tutorial. I do think this tutorial is too brief but I also like CircutStreams tutorial 开始,因为它还提到了实现接口,这可以更好地解决继承问题许多案例。
大概要理解的是MonoBehaviour
本身继承了Component
- MonoBehaviour IS a Component.
当您将其视为组件是事物的一部分时,至少在某种程度上您不能 new
一个组件 - 我可以制作一个 new Arm
吗?如果我这样做了,它会从哪里得到?但是我可以做一个 new Body body
然后做 body.AddComponent<Arm>
,对吧?因为这样就很清楚 Arm 发生了什么 - 它 附加到 body.
同样,您不能只是 new
组件,因为它们应该是游戏对象的 部分。您可以 做的是制作一个新的游戏对象,然后.AddComponent<>()
object。同样,现在很清楚 组件的去向。
我大体上同意 UnholySheep's comment 你应该更喜欢组合而不是继承,因为这通常会使你的代码更加模块化。也就是说,我肯定也使用继承。
当你概述你的 class 时,你应该问自己的问题是,“这个新的 class 是 _____ 的一种还是这个新的 class 有一个 _____。”我想说玩家是角色,敌人是角色很容易,但是你需要 subclass吗?或者 Player 只是一个也有 PlayerController 的 Character 吗?也许你可以有一个像 IPlayerController
这样的界面,并为用户提供一个 LocalPlayerController
,为敌人提供一个 AIPlayerController
? problem-solve 编程的很多方法。
或者,也许所有角色都有一个 PlayerController,而 non-player 个角色只有 .activeControl
位为假。然后你可以做ghost/spectate NPC之类的事情
但具体针对您的问题:
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
您将从一个 Character
脚本开始,该脚本继承 MonoBehaviour 并实现两个 classes:
通用的任何逻辑
public class Character : MonoBehaviour
{
// Your common character logic
}
那么你会让子class继承Character
:
public class Player : Character
{
// Your Player-specific logic here
}
和
public class Enemy : Character
{
// Your Enemy-specific logic here
}
然后您可以将 Enemy
和 Player
脚本添加到编辑器中的游戏对象,或者您可以通过获取对目标游戏对象的引用(通过在编辑器中设置引用或通过在脚本中创建一个新的 GameObject)然后调用 targetGameObject.AddComponent<YourComponentToAdd>();
.
我对继承有基本的了解,但由于我想要作为游戏对象组件的每个脚本都必须继承自 MonoBehaviour,因此事情变得棘手。
假设我想要一个名为 Character 的基础 class,并且我希望 Player 和 Enemy 派生自 Character。我应该如何将 Player 和 Enemy 脚本添加到 Player 和 Enemy 对象而不会出现错误?
我还尝试创建空游戏对象并向其添加 CharacterStats,然后用具有不同统计数据的不同角色对象填充此 class。你猜怎么着,如果脚本派生自 Monobehaviour,则不能使用 new 关键字来创建对象。
是否有一些关于这个主题的教程可以使它更清楚?
用好继承来自于规划和迭代。也许你的角色 class 不能很好地作为你的敌人的 class。我倾向于让玩家在他们自己的 class 中(除非是多人游戏),而敌人使用他们自己的基地 class。下面是我在我正在开发的 2D 游戏中使用的一些继承的示例。
基地Class: Enemy.cs
public abstract class Enemy : MonoBehaviour
{
public Vector2 flyInPos;
public Vector2 startPos;
public bool isFlyingIn;
public float flyInDuration = 5000f;
public float flyInTime = 0f;
public abstract void Destroy();
public abstract void DestroyWithoutScore();
public abstract void Attack();
public abstract void Attack2();
public abstract void Attack3();
public abstract void FlyOut();
public virtual void FlyIn()
{
isFlyingIn = true;
}
}
每个敌人都需要有这些元素。你会注意到我的基础 class 中的大多数方法都被标记为 abstract 这样每个 class 继承的方法都可以有一组独特的攻击和飞行模式.需要注意的一件事是基础 class 继承自 MonoBehaviour。以下是我如何在我的一个敌人身上实现继承。抱歉,它仍然是 WIP。
继承Class: ShootingDrone.cs
public class ShootingDrone : Enemy
{
public GameObject projectileSpawner;
public GameObject projectile;
public void Start()
{
startPos = transform.position;
Scheduler.Invoke(Attack, 5f, gameObject);
}
public void Update()
{
if(isFlyingIn)
{
transform.position = Vector3.Lerp(startPos, flyInPos, flyInTime / flyInDuration);
flyInTime += Time.deltaTime;
}
}
public override void Attack()
{
Shoot();
Scheduler.Invoke(Shoot, 0.25f, gameObject);
Scheduler.Invoke(Shoot, 0.5f, gameObject);
}
public override void Attack2()
{
Attack();
}
public override void Attack3()
{
Attack();
}
public void Shoot()
{
GameObject newProjectile = Instantiate(projectile, projectileSpawner.transform);
newProjectile.transform.parent = null;
}
public override void FlyOut()
{
throw new System.NotImplementedException();
}
public override void DestroyWithoutScore()
{
throw new System.NotImplementedException();
}
public override void Destroy()
{
Destroy(gameObject);
}
}
Enemy 的基础 class 继承自 MonoBehaviour。因此,当 Shooting Drone 继承自 Enemy 时,它也继承自 MonoBehaviour。
针对您尝试添加新的 MonoBehaviour 的问题。这样做的原因是 MonoBehavior 必须附加到某些东西上。想象一个没有 GameObject 的刚体,它是行不通的。如果你想制作一个“新的”CharacterStats,你会这样做:
CharacterStats stats = gameObject.AddComponent<CharacterStats>();
如果您不想将 CharacterStats 作为一个组件,那么只需从 class 中删除 MonoBehavior 继承并将其实例化为新的 CharacterStats。
有多种教程介绍了继承,但是根据您对这个主题的了解程度,我会从 Unity official inheritance tutorial. I do think this tutorial is too brief but I also like CircutStreams tutorial 开始,因为它还提到了实现接口,这可以更好地解决继承问题许多案例。
大概要理解的是MonoBehaviour
本身继承了Component
- MonoBehaviour IS a Component.
当您将其视为组件是事物的一部分时,至少在某种程度上您不能 new
一个组件 - 我可以制作一个 new Arm
吗?如果我这样做了,它会从哪里得到?但是我可以做一个 new Body body
然后做 body.AddComponent<Arm>
,对吧?因为这样就很清楚 Arm 发生了什么 - 它 附加到 body.
同样,您不能只是 new
组件,因为它们应该是游戏对象的 部分。您可以 做的是制作一个新的游戏对象,然后.AddComponent<>()
object。同样,现在很清楚 组件的去向。
我大体上同意 UnholySheep's comment 你应该更喜欢组合而不是继承,因为这通常会使你的代码更加模块化。也就是说,我肯定也使用继承。
当你概述你的 class 时,你应该问自己的问题是,“这个新的 class 是 _____ 的一种还是这个新的 class 有一个 _____。”我想说玩家是角色,敌人是角色很容易,但是你需要 subclass吗?或者 Player 只是一个也有 PlayerController 的 Character 吗?也许你可以有一个像 IPlayerController
这样的界面,并为用户提供一个 LocalPlayerController
,为敌人提供一个 AIPlayerController
? problem-solve 编程的很多方法。
或者,也许所有角色都有一个 PlayerController,而 non-player 个角色只有 .activeControl
位为假。然后你可以做ghost/spectate NPC之类的事情
但具体针对您的问题:
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
您将从一个 Character
脚本开始,该脚本继承 MonoBehaviour 并实现两个 classes:
public class Character : MonoBehaviour
{
// Your common character logic
}
那么你会让子class继承Character
:
public class Player : Character
{
// Your Player-specific logic here
}
和
public class Enemy : Character
{
// Your Enemy-specific logic here
}
然后您可以将 Enemy
和 Player
脚本添加到编辑器中的游戏对象,或者您可以通过获取对目标游戏对象的引用(通过在编辑器中设置引用或通过在脚本中创建一个新的 GameObject)然后调用 targetGameObject.AddComponent<YourComponentToAdd>();
.