C#MemberwiseClone不起作用

时间:2014-12-14 20:18:31

标签: c# xna clone

我要做的是为我的游戏设置一个名为“Items”的类,并包含游戏中存在的所有项目。由于我正在制作一个星舰游戏,所以我的班级中有“星舰”和“武器”等实例。现在,当我初始化敌人时,我希望这个敌人拥有他的“星舰” - 实例具有与“物品”中列出的星舰之一相同的值。我试着写

this.Ship = Items.Testhip;

进入敌人级别,但它不起作用。所以我搜索了互联网的另一种可能性,并找到了IClonable接口。但它对我不起作用。这是一些重要的代码:

项目类

public static class Items
{ 
    private static Starship _testship; 
    public static Weapon _testWeapon;
    public static List<Weapon> Weaponlist = new List<Weapon>();
    public static List<Weapon> WeaponList { get { if (!_initialized) return null; Weaponlist.RemoveAll(c => c.BulletTexture != null); Weaponlist.Add((Weapon) TestWeapon); return Weaponlist; } }
    private static bool _initialized;

    public static object Testship
    {
        get { if (!_initialized) return null; return _testship.Clone(); }
    }

    public static object TestWeapon
    {
        get { if (!_initialized) return null; return _testWeapon.Clone(); }
    }




    public static void Initialize()
    {
        _initialized = true; 
        _testWeapon = new Weapon(WeaponType.Projectil, Graphics.BulletTexture, 25, 250, 10000, 1000, 15000, 3000, 400, 1200, 4, new Vector2(0, 0));
        _testship = new Starship("test", 12000, 5000, Graphics.StarfighterXI, WeaponList, new Shield(), new Motor(), 1, 0, 0, 0, 0.1f, AttackAngle.Small);
    }
}

这些是来自玩家类的行:

public static void Initialize()
    {
        Ship = (Starship) Items.Testship;
        Ship.Weapons.First(c => c.BulletTexture != null).Name = "PLAYER WEAPON!";
    }

敌人类也是如此,因为我希望他们两个都有类似的强大的测试船:

public void Initialize()
    {
        Ship = (Starship) Items.Testship;
        Ship.Weapons.First(c => c.BulletTexture != null).Name = "ENEMY WEAPON!";
    }

来自星舰:

public class Starship : ICloneable
{
    public string Name { get; set; }
    public Vector3 LoadedWorld { get; set; }
    public Texture2D Texture { get; set; }
    public Vector2 Position { get; set; }
    public Vector2 Origin { get; private set; }
    public int MaxHP { get; set; }
    private int HP { get; set; }
    public int Cost { get; set; }

    public List<Weapon> Weapons = new List<Weapon>();
    public Shield Shield { get; set; }
    public Motor Motor { get; set; }
    public int MaxProjectilLaunchers { get; set; }
    public int MaxRocketLaunchers { get; set; }
    public int MaxPlasmaWeapons { get; set; }
    public int MaxHEWs { get; set; }
    public int ProjectilLaunchers { get; set; }
    public int RocketLaunchers { get; set; }
    public int PlasmaWeapons { get; set; }
    public int HEWs { get; set; }
    public float Rotation { get; set; } //Rotation, die das Schiff braucht, um sich zu bewegen
    public float FinalRotation { get; set; } // Rotation, die das Schiff aktuell hat
    public float RotationVelocity { get; set; } //Rotations-Velocity, um die sich die Rotation pro Frame ändern soll
    public bool? IsRotationg { get; set; }
    public float AttackAngle { get; set; }

    //-----------------------------------------------------------------------------------------------------
    public Starship(string name, int cost, int baseHP, Texture2D texture, List<Weapon> weapons, Shield shield, Motor motor, int maxProjectilLaunchers, int maxRocketLaunchers, int maxPlasmaWeapons, int maxHEWs, float rotationVelocity, AttackAngle attackAngle)
    {
        Name = name;
        Cost = cost;
        MaxHP = baseHP;
        Position = new Vector2(0, 0); //todo: position muss richtig gesetzt werden, auch wenn das schiff feindlich ist!!!
        Texture = texture;
        Weapons = weapons;
        Shield = shield;
        Motor = motor;
        MaxProjectilLaunchers = maxProjectilLaunchers;
        MaxRocketLaunchers = maxRocketLaunchers;
        MaxPlasmaWeapons = maxPlasmaWeapons;
        MaxHEWs = maxHEWs;
        RotationVelocity = rotationVelocity;
        IsRotationg = null;
        AttackAngle = attackAngle == StarshipsRevolution.AttackAngle.Small ? 1.0f : 1.5f;
        Origin = new Vector2(Texture.Width / 2, Texture.Height / 2);

        foreach (var item in weapons)
        {
            if (item.WeaponType == WeaponType.Projectil) ProjectilLaunchers++;
            if (item.WeaponType == WeaponType.Rocket) RocketLaunchers++;
            if (item.WeaponType == WeaponType.Plasma) PlasmaWeapons++;
            if (item.WeaponType == WeaponType.HEW) HEWs++;
            MaxHP += item.MaxHP; //todo wenn die waffe gewechselt wird wert verändern
        }

        if (ProjectilLaunchers > MaxProjectilLaunchers || RocketLaunchers > MaxRocketLaunchers || PlasmaWeapons > MaxPlasmaWeapons || HEWs > MaxHEWs)
            throw new Exception(String.Format("Das Raumschiff {0} wurde mit zu vielen Waffen initialisiert.", name));

        HP = MaxHP;
    }

    //-----------------------------------------------------------------------------------------------------
    public void Update(GameTime gameTime)
    {
        foreach (var item in Weapons)
            item.Update(gameTime);

        //Schild und Motor updaten
    }

    //-----------------------------------------------------------------------------------------------------
    public void Shoot(WeaponType weaponType, Vector2 position)
    {
        Vector2 direction = Position + Origin - position; //todo muss eventuell noch in die schleife verschoben werden, weil man sonst vielleicht nach hinten schiessen kann
        direction.Normalize();
        float rotation = (float)Math.Atan2(-direction.X, direction.Y);
        if (rotation >= Rotation - AttackAngle && rotation <= Rotation + AttackAngle)
        {
            foreach (var item in Weapons.Where(c => c.WeaponType == weaponType && c.FirerateTimer >= c.Firerate))
                item.Shoot(position);
        }
    }

    /// <summary>
    /// Sets a new Position for the ship or rotate it if it doesn't look into the right direction.
    /// </summary>
    /// <param name="position">The position the ship should be set at</param>
    /// <returns>True, if it could directly set a new position, and false, if it had to rotate</returns>
    public bool RotateOrMove(Vector2 position)
    {
        //Rotation setzen, die das Raumschiff am Ende haben soll
        if (IsRotationg == null)
        {
            Vector2 direction = Position - position;
            direction.Normalize();
            FinalRotation = (float)Math.Atan2(-direction.X, direction.Y);
            IsRotationg = true;
        }

        //Wenn die Rotation erreicht wurde, setze FinalRotation auf null
        if (Equals(FinalRotation, Rotation))
            IsRotationg = false;

        //Wenn FinalRotation auf null ist, darf die Position gesetzt werden, da die Rotation ja dann stimmt
        if (IsRotationg == false)
        {
            Position = position;
            return true;
        }
        else
        { //Wenn 
            Rotation = CurveAngle(Rotation, FinalRotation, RotationVelocity);
            return false;
        }


    }

    //-----------------------------------------------------------------------------------------------------
    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Texture, Position + Origin, null, Color.White, Rotation, Origin, 1, SpriteEffects.None, 0);
    }

    private float CurveAngle(float from, float to, float step)
    {
        if (step == 0) return from;
        if (from == to || step == 1) return to;

        Vector2 fromVector = new Vector2((float)Math.Cos(from), (float)Math.Sin(from));
        Vector2 toVector = new Vector2((float)Math.Cos(to), (float)Math.Sin(to));

        Vector2 currentVector = Slerp(fromVector, toVector, step);

        return (float)Math.Atan2(currentVector.Y, currentVector.X);
    }

    private Vector2 Slerp(Vector2 from, Vector2 to, float step)
    {
        if (step == 0) return from;
        if (from == to || step == 1) return to;

        double theta = Math.Acos(Vector2.Dot(from, to));
        if (theta == 0) return to;

        double sinTheta = Math.Sin(theta);
        return (float)(Math.Sin((1 - step) * theta) / sinTheta) * from + (float)(Math.Sin(step * theta) / sinTheta) * to;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

和武器:

public object Clone()
    {
        return MemberwiseClone();
    }

如上所述,玩家的武器在初始化时被命名为“PLAYER WEAPON”。敌人的武器被命名为“ENEMY WEAPON”。但在那之后,玩家武器也会有“ENEMY WEAPON”的名字,所以我猜这些值是参考?顺便说一句,所有属性和成员以及所有属性在被另一个实例更改时都会更改它们的值。

希望您了解我的问题并知道该做些什么:)

1 个答案:

答案 0 :(得分:2)

这是因为MemberwiseClone创建了一个副本,而不是“深层”副本。

来自documentation of Object.MemberwiseClone

  

MemberwiseClone方法通过创建新对象,然后将当前对象的非静态字段复制到新对象来创建浅表副本。

这个版本是什么,对象本身的字段被克隆,但不是它可能引用的任何对象的字段。

这是一个讨论在C#中创建对象深层副本的问题:link