C # полиморфизм с клонированием - PullRequest
1 голос
/ 12 января 2011

Позвольте мне сначала попытаться объяснить, чего я пытаюсь достичь.

Я делаю игру, в которой игрок может получить бонусы (которые я почему-то назвал «добычей» в моей игре). Итак, вот действительно базовая форма моего лута класса:

public class Loot : Sprite
{
    //Properties and fields...

    public Loot(/*parameters...*/)
    {
    }

    public virtual OnPickUp(Player player)
    {
    }
}

Player и Sprite - это мои классы. Вот моя проблема: мой класс спрайтов содержит позицию (Vector2, структуру из XNA, представляющую вектор), и из моего понимания того, как работает C #, копирование двух спрайтов будет копировать ссылку каждого другого, поэтому Я меняю свою позицию, она меняет и другую.

Всякий раз, когда должен появляться лут, у меня есть класс, содержащий возможные лоты, которые могут появиться, который возвращает случайный, и лут копируется и отображается на экране, но как только я получаю больше, чем один из того же типа из-за проблемы с эталонным копированием (оба будут иметь одинаковую позицию, и будет видна только одна).

Итак, я пытался создать новый объект Loot всякий раз, когда мне нужно было создать новый лут, поэтому мне пришлось предоставить конструктор с несколькими параметрами для простого копирования лута следующим образом: (Давайте представим, что toSpawn может быть любым типом, производным от класса Loot, например HealthPack или любым другим)

Loot spawning = new Loot(toSpawn.Position, toSpawn.Color /*etc...*/);

Это выглядело правильно, пока я не решил реализовать "OnPickUp". Поскольку я создаю новый объект Loot , а не соответствующий дочерний класс, функциональность функции toSpawn *1023* OnPickUp исчезнет, ​​и получение добычи закончится ничего не делать (это вызовет только пустую функцию базового класса Loot).

Очевидно, я здесь что-то не так делаю. У меня нет большого опыта программирования, и тем более с C #, поэтому я действительно не представляю, как я могу это исправить. Я попытался использовать объект Action , представляющий функциональность OnPickUp, он работал бы, но все же сильно ограничивал меня (так как я должен передать статическую функцию как Action , поэтому я не могу использовать информацию об игроке параметр и не позволяет мне использовать информацию подкласса).

Итак, наконец, мой вопрос на самом деле: Что было бы лучше, чтобы иметь подклассы с перегруженными функциями, но при этом иметь возможность клонировать базовый класс и его свойства?

Спасибо за ваше драгоценное время.

Если что-то недостаточно ясно (и я знаю, что это не так), просто спросите меня в комментариях, и я постараюсь описать мою проблему более подробно.

Ответы [ 4 ]

4 голосов
/ 12 января 2011

ваш класс 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;
    }
}
3 голосов
/ 12 января 2011

Я думаю, что я должен что-то упустить в этом вопросе, потому что 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.

Я предполагаю, что ваша игровая логика говорит что-то вроде "на каждом уровне должно быть не более 10 созданных лотов", поэтому ваш класс PossibleLoots создает все возможные 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`
}

Пожалуйста, дайте мне знать, если яупустил смысл этого вопроса.

3 голосов
/ 12 января 2011

Vector2 - это struct, а не class, поэтому ваша первоначальная проблема звучит так, как будто она связана с двумя ссылками на один и тот же объект Loot, а не с двумя разными объектами Loot со ссылками на одну и ту же позицию.

Класс, отвечающий за создание объектов Loot, должен создавать новые экземпляры соответствующего подкласса Loot, а не создавать базовый класс. В вашем проекте Loot, вероятно, должен быть абстрактным классом или даже интерфейсом, и вы должны использовать шаблон абстрактной фабрики для создания желаемого типа объекта 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)
        {
            ...
        }
        ...
    }
}
0 голосов
/ 12 января 2011

Для меня это действительно звучит так, как будто вы все еще делаете это неправильно. Хотя данный ответ будет работать, это не то, что вы должны делать.

Если вы хотите создать новый лут и хотите, чтобы он был случайным из возможных типов лута, ваш конструктор класса Loot должен выполнить всю эту работу. Не должно быть необходимости в клонировании или ICloneable.

Когда создается новый объект добычи, он должен случайным образом становиться тем типом добычи, который не будет, не нужно где-то хранить список всех возможных типов и клонировать их. Новый объект добычи будет иметь свою собственную позицию и поведение и работать точно так, как вы ожидаете.

Или я просто неправильно понимаю, чего вы пытаетесь достичь?

...