让我首先尝试解释一下我想要实现的目标。
我正在制作一款游戏,玩家可以在其中拾取能量提升(由于某种原因,我在游戏中称之为“Loot”)。所以这是我的Loot课程的一个非常基本的形式:
public class Loot : Sprite
{
//Properties and fields...
public Loot(/*parameters...*/)
{
}
public virtual OnPickUp(Player player)
{
}
}
播放器和精灵是来自我的课程。这是我的问题:我的精灵类持有一个位置(一个Vector2,一个来自XNA的结构代表一个向量),根据我对C#如何工作的理解,复制两个精灵将复制彼此的参考,因此,每当我改变一个人的位置时,它也会改变另一个人的位置。
每当一个战利品应该产卵时,我有一个类可以产生可以产生的可能的战利品,它会返回一个随机的战利品并将战利品复制并显示在屏幕上,但是只要我得到多个相同类型的战利品由于参考副本问题(两者将共享相同的位置并且只有一个可见),它会被弄乱。
然后,当我需要生成一个新的战利品时,我尝试创建一个新的Loot对象,所以我必须提供一个带有多个参数的构造函数,以便以这种方式轻松复制战利品: (让我们假装toSpawn可以是从Loot类派生的任何类型,比如HealthPack,或者其他什么)
Loot spawning = new Loot(toSpawn.Position, toSpawn.Color /*etc...*/);
在我决定实施“OnPickUp”之前看起来是正确的。由于我创建了一个新的 Loot 对象而不是正确的子类,因此 toSpawn 的 OnPickUp 函数的功能将消失并拾取战利品最终什么都不做(它只会调用基类Loot的空函数)。
显然,我在这里做错了。我没有太多的编程经验,甚至没有C#,所以我真的不知道如何解决这个问题。我尝试使用Action<播放器>表示OnPickUp功能的对象,它可以工作但仍然限制了我很多(因为我必须将静态函数作为Action< Player>传递,因此限制我使用player参数的信息并阻止我使用sub - 特定信息)。
所以,最后,我的问题是:什么是更好的设计,允许子类具有重载函数,但仍然能够克隆基类及其属性?
感谢您宝贵的时间。
如果有什么不够清楚(我知道不是),那么请在评论中问我,我会尝试更详细地描述我的问题。
答案 0 :(得分:4)
让你的Loot类实现ICloneable
public abstract class Loot : ICloneable
{
public virtual object Clone()
{
Type type = this.GetType();
Loot newLoot = (Loot) Activator.CreateInstance(type);
//do copying here
return newLoot;
}
}
答案 1 :(得分:3)
Vector2
是struct
而不是class
,因此您的原始问题听起来像是由于对同一个Loot对象持有两个引用,而不是两个不同的Loot对象引用相同的位置。
负责创建Loot对象的类应该创建相关Loot子类的新实例,而不是创建基类。在你的设计中,Loot可能应该是一个抽象类甚至是一个接口,你应该使用abstract factory pattern来创建所需类型的Loot对象。
例如:
class LootFactory
{
const int numLootTypes = 3;
public Loot CreateLoot(Vector2 position)
{
Random rand = new Random();
int lootIndex = rand.Next(numLootTypes);
if (lootIndex == 0)
{
return new HealthPack(position);
}
if (lootIndex == 1)
{
...
}
...
}
}
答案 2 :(得分:3)
我认为我必须错过这个问题,因为Vector2
是一个结构,而不是一个类。将Vector2
直接分配给另一个var v2a = new Vector2(10.0f, 20.0f);
var v2b = v2a; //`v2b` is a new instance of `Vector2` distinct from `v2a`.
会自动创建副本。
PossibleLoots
您的问题听起来好像Loot
类没有随机化所创建的每个PossibleLoots
实例的位置。
我认为你的游戏逻辑说“每个级别最多会产生10个战利品”,所以你的Loot
类会在启动时创建所有可能的public class PossibleLoots
{
private IList<Loot> _loots = new List<Loot>();
public PossibleLoots(int maxLoots)
{
for (var i = 0; i < maxLoots; i++)
{
_loots.Add(new Loot(this.GetRandomPosition()));
}
}
private Vector2 GetRandomPosition()
{
// Your logic here to create suitable locations for loot to appear
}
// Rest of your `PossibleLoots` code
// to spawn each `Loot` and to deplete the `_loots`
// collection when the player picks up each `Loot`
}
个对象,但不会除非要求“产生战利品”,否则显示一个。
所以,如果这是对的,这不是你需要的那种吗?
{{1}}
如果我错过了这个问题,请告诉我。
答案 3 :(得分:0)
对我而言,听起来你仍然是以错误的方式解决这个问题。虽然给出的答案是有效的,但这不是你应该做的事情。
如果你想要产生一个新的战利品,并且你希望它是可能的战利品类型的随机数,那么你的Loot类的构造函数应该完成所有工作。不需要克隆或ICloneable。
当创建一个新的战利品对象时,它应该随机变成它将要成为的战利品类型,不需要在某处保留所有可能类型的列表并从中克隆它们。然后新的战利品对象将拥有自己的位置和行为,并且完全像你期望的那样工作。
或者我只是误解了你想要完成的事情?