【问题标题】:C# MemberwiseClone doesn't workC# MemberwiseClone 不起作用
【发布时间】:2014-12-14 20:18:31
【问题描述】:

我想做的是为我的游戏创建一个名为“Items”的类,其中包含游戏中存在的每个项目。由于我正在制作星际飞船游戏,因此我的班级中有“星际飞船”和“武器”之类的实例。现在,例如,当我初始化一个敌人时,我希望这个敌人的“Starship”-Instance 具有与“Items”中列出的 Starships 之一相同的值。我试着写了

this.Ship = Items.Testhip;

进入Enemy-Class,但它不起作用。所以我在互联网上搜索了另一种可能性,并找到了 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);
    }
}

这些是来自 Player 类的行:

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

对于 Enemy Class 也是如此,因为我希望他们都拥有类似的强舰来进行测试:

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”。敌人的武器被命名为“敌人武器”。但在那之后,玩家武器也会有“敌人武器”的名称,所以我猜这些值是参考?顺便说一句,所有属性和成员以及所有内容在被其他实例更改时都会更改其值。

希望你能理解我的问题并知道该怎么做:)

【问题讨论】:

  • 你能显示你的Starship的代码吗?
  • @VsevolodGoloviznin 完成 :)

标签: c# xna clone


【解决方案1】:

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

来自documentation of Object.MemberwiseClone

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

这意味着对象本身的字段被克隆,而不是它可能引用的任何对象的字段。

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

【讨论】:

  • 我尝试了线程中某人制作的使用序列化的复印机类,但我在 formatter.Serialize(stream,source); “程序集“Microsoft.Xna.Framework.Graphics,Version=4.0.0.0,Culture=neutral,PublicKeyToken=842cf8be1de50553”中的类型“Microsoft.Xna.Framework.Graphics.Texture2D”未标记为可序列化。”但是我在“Weapon”和“Starship”上面写了[Serializable],怎么了? :(
  • @Terrenay: 好吧,你自己的类可能是 [Serializable],但如果提到的类型 Microsoft.Xna.Framework.Graphics.Texture2Dnot 那么你要么不能拥有这种类型的任何属性您的班级,或者您必须将其从序列化中排除。不过,这意味着它不会被复制到克隆的实例中。
猜你喜欢
  • 2011-04-23
  • 1970-01-01
  • 2012-10-31
  • 2011-08-29
  • 1970-01-01
  • 2018-10-16
  • 2015-05-29
  • 2015-06-29
  • 2018-05-14
相关资源
最近更新 更多