Unity 与对象交互
Unity interacting with objects
我正在使用 unity 构建一个简单的自上而下的 rouge-like 游戏。我有开发经验,但我是统一框架和 C# 的初学者。是否有一种干净优雅的方式来处理玩家对象与场景中其他各种类型的对象的交互?
例如,场景中到处都是墙壁、敌人和其他阻挡路径的物体,玩家可以通过尝试朝他们的方向移动来与之互动。站在箱子附近并朝它的方向移动不会移动玩家,但会打开箱子。如果撞到墙上,它会损坏它,等等。
目前我正在使用 raycast2d 来获取阻挡路径的对象,但无法找到如何与其交互的解决方案,而不必检查它是什么类型的对象(是墙; 是一个箱子;等等)。我制作了一个接口,所有可交互对象都使用方法 interact() 实现,但不同类型的对象需要不同的信息。一堵墙需要它应该承受的伤害量(取决于玩家 class 中的玩家统计数据),胸部需要 none。因此,它们都很难实现相同的接口。让墙问玩家他的伤害是多少也是糟糕的编程习惯。
我不久前在这个帖子上发现了一个类似的问题on stackowerflow。它建议使用观察者模式,但我无法想象场景中的每个对象都订阅了玩家移动事件,并且在每次移动后检查它是否被击中。
这种交互有没有标准的解决方案?一种是松散耦合的、干净的并遵循良好的编程实践?
如果我遇到你的情况,我会向对象(墙壁、箱子等)添加一个带有 Collider2D
的空游戏对象,然后切换 isTrigger
的框。 (请注意,这将要求该对象派生自 Monobehaviour
,这可能并不总是令人满意的。)
从那里我会使用专门函数 OnTriggerStay
、OnTriggerEnter
和 OnTriggerExit
的一些组合:
//in Chest.cs
void OnTriggerEnter() //http://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
{
OpenChest();
}
//in Wall.cs
void OnTriggerStay() //http://docs.unity3d.com/ScriptReference/Collider.OnTriggerStay.html
{
DamageWall();
}
从这里开始,您有两种选择来确保某些游戏对象不会触发错误的对撞机。一种方法使用代码:
//in Chest.cs
void OnTriggerEnter(Collider other)
{
//notice this won't be very efficient
if(other.gameObject.GetComponent<PlayerScript>() != null) OpenChest();
//if(other.name == "The Player") OpenChest(); //probably less efficient at runtime
}
另一种解决方案使用层,效率更高。要向 Unity 添加图层,请通过 edit
-> project settings
-> tags and layers
(左上角的选项卡)并添加一些图层名称(例如 Wall、Chest、Player、Enemy) .接下来找到触发器(最好是触发器的父级)并通过从 Default
层切换到所需标签(例如 Wall),将对象标记为 Inspector
中该层的一部分。 =30=]
从这里开始,您可以使用另一个技巧,通过 edit
-> project settings
-> physics
并查看 Layer Collision Matrix
层之间的选择性碰撞。如果两个对象需要碰撞,请保留复选框;如果它们永远不会发生碰撞,请取消选中该框;如果他们应该根据情况发生碰撞,要么将层分成两个或更多层,要么添加确定是否发生碰撞的代码。
另一个提示:如果玩家应该只在拿着电锯时砍树,你可以这样做,所以拿着电锯会创建一个特殊的触发器来摧毁树木。
编辑:其他问题:
1) two triggers cannot collide (one must be a trigger and the other must be a collider)
2) triggers can move (they do not need to be stationary)
3) if one collider-trigger pair seems illogical, switching which object is the trigger and which is the collider might make the code simpler.
edit2:去除耦合:
OnTriggerStay(Collider other)
将在每个实例中调用,如果所有怪物和玩家造成相同的伤害,它会很好地工作,但显然情况并非如此。如果每个实例造成的伤害量不同,您可以执行以下操作:
void OnCollisionEnter(Collision other)
{
addSelfDPS(other.gameObject.GetComponent<WallDamager>().getDPS());
}
void OnCollisionExit(Collision other)
{
addSelfDPS(-1*other.gameObject.GetComponent<WallDamager>().getDPS());
}
但是,如果您使用此方法,则在触发器内部生成和销毁对象时会出现问题,因此您可能需要在任何构造函数 (Awake/Start) 或析构函数 (OnDestroy) 中解决该问题.
我正在使用 unity 构建一个简单的自上而下的 rouge-like 游戏。我有开发经验,但我是统一框架和 C# 的初学者。是否有一种干净优雅的方式来处理玩家对象与场景中其他各种类型的对象的交互?
例如,场景中到处都是墙壁、敌人和其他阻挡路径的物体,玩家可以通过尝试朝他们的方向移动来与之互动。站在箱子附近并朝它的方向移动不会移动玩家,但会打开箱子。如果撞到墙上,它会损坏它,等等。
目前我正在使用 raycast2d 来获取阻挡路径的对象,但无法找到如何与其交互的解决方案,而不必检查它是什么类型的对象(是墙; 是一个箱子;等等)。我制作了一个接口,所有可交互对象都使用方法 interact() 实现,但不同类型的对象需要不同的信息。一堵墙需要它应该承受的伤害量(取决于玩家 class 中的玩家统计数据),胸部需要 none。因此,它们都很难实现相同的接口。让墙问玩家他的伤害是多少也是糟糕的编程习惯。
我不久前在这个帖子上发现了一个类似的问题on stackowerflow。它建议使用观察者模式,但我无法想象场景中的每个对象都订阅了玩家移动事件,并且在每次移动后检查它是否被击中。
这种交互有没有标准的解决方案?一种是松散耦合的、干净的并遵循良好的编程实践?
如果我遇到你的情况,我会向对象(墙壁、箱子等)添加一个带有 Collider2D
的空游戏对象,然后切换 isTrigger
的框。 (请注意,这将要求该对象派生自 Monobehaviour
,这可能并不总是令人满意的。)
从那里我会使用专门函数 OnTriggerStay
、OnTriggerEnter
和 OnTriggerExit
的一些组合:
//in Chest.cs
void OnTriggerEnter() //http://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
{
OpenChest();
}
//in Wall.cs
void OnTriggerStay() //http://docs.unity3d.com/ScriptReference/Collider.OnTriggerStay.html
{
DamageWall();
}
从这里开始,您有两种选择来确保某些游戏对象不会触发错误的对撞机。一种方法使用代码:
//in Chest.cs
void OnTriggerEnter(Collider other)
{
//notice this won't be very efficient
if(other.gameObject.GetComponent<PlayerScript>() != null) OpenChest();
//if(other.name == "The Player") OpenChest(); //probably less efficient at runtime
}
另一种解决方案使用层,效率更高。要向 Unity 添加图层,请通过 edit
-> project settings
-> tags and layers
(左上角的选项卡)并添加一些图层名称(例如 Wall、Chest、Player、Enemy) .接下来找到触发器(最好是触发器的父级)并通过从 Default
层切换到所需标签(例如 Wall),将对象标记为 Inspector
中该层的一部分。 =30=]
从这里开始,您可以使用另一个技巧,通过 edit
-> project settings
-> physics
并查看 Layer Collision Matrix
层之间的选择性碰撞。如果两个对象需要碰撞,请保留复选框;如果它们永远不会发生碰撞,请取消选中该框;如果他们应该根据情况发生碰撞,要么将层分成两个或更多层,要么添加确定是否发生碰撞的代码。
另一个提示:如果玩家应该只在拿着电锯时砍树,你可以这样做,所以拿着电锯会创建一个特殊的触发器来摧毁树木。
编辑:其他问题:
1) two triggers cannot collide (one must be a trigger and the other must be a collider)
2) triggers can move (they do not need to be stationary)
3) if one collider-trigger pair seems illogical, switching which object is the trigger and which is the collider might make the code simpler.
edit2:去除耦合:
OnTriggerStay(Collider other)
将在每个实例中调用,如果所有怪物和玩家造成相同的伤害,它会很好地工作,但显然情况并非如此。如果每个实例造成的伤害量不同,您可以执行以下操作:
void OnCollisionEnter(Collision other)
{
addSelfDPS(other.gameObject.GetComponent<WallDamager>().getDPS());
}
void OnCollisionExit(Collision other)
{
addSelfDPS(-1*other.gameObject.GetComponent<WallDamager>().getDPS());
}
但是,如果您使用此方法,则在触发器内部生成和销毁对象时会出现问题,因此您可能需要在任何构造函数 (Awake/Start) 或析构函数 (OnDestroy) 中解决该问题.