XNA имитирует игровой объект или разъединяет вашу игру

StackOverflow https://stackoverflow.com/questions/804904

  •  03-07-2019
  •  | 
  •  

Вопрос

Я размышлял, возможно ли смоделировать игровой объект, чтобы протестировать мой компонент DrawableGameComponent?

Я знаю, что имитирующим фреймворкам нужен интерфейс для функционирования, но мне нужно имитировать фактический Игра объект.

Редактировать:Вот такой Ссылка к соответствующему обсуждению на форумах сообщества XNA.Какая-нибудь помощь?

Это было полезно?

Решение

На этом форуме есть несколько хороших постов на тему модульного тестирования.Вот мой личный подход к модульному тестированию в XNA:

  • Игнорируйте метод Draw()
  • Изолируйте сложное поведение в методах вашего собственного класса
  • Протестируйте самые сложные вещи, а об остальном не беспокойтесь

Вот пример теста, подтверждающего, что мой метод Update перемещает объекты на нужное расстояние между вызовами Update().(Я использую НАнит.) Я вырезал пару строк с разными векторами перемещения, но вы поняли идею:вам не должна понадобиться Игра для проведения ваших тестов.

[TestFixture]
public class EntityTest {
    [Test]
    public void testMovement() {
        float speed = 1.0f; // units per second
        float updateDuration = 1.0f; // seconds
        Vector2 moveVector = new Vector2(0f, 1f);
        Vector2 originalPosition = new Vector2(8f, 12f);

        Entity entity = new Entity("testGuy");
        entity.NextStep = moveVector;
        entity.Position = originalPosition;
        entity.Speed = speed;

        /*** Look ma, no Game! ***/
        entity.Update(updateDuration);

        Vector2 moveVectorDirection = moveVector;
        moveVectorDirection.Normalize();
        Vector2 expected = originalPosition +
            (speed * updateDuration * moveVectorDirection);

        float epsilon = 0.0001f; // using == on floats: bad idea
        Assert.Less(Math.Abs(expected.X - entity.Position.X), epsilon);
        Assert.Less(Math.Abs(expected.Y - entity.Position.Y), epsilon);
    }
}

Редактировать:Некоторые другие заметки из комментариев:

Мой Класс Сущности:Я решил объединить все мои игровые объекты в централизованный класс сущностей, который выглядит примерно так:

public class Entity {
    public Vector2 Position { get; set; }
    public Drawable Drawable { get; set; }

    public void Update(double seconds) {
        // Entity Update logic...
        if (Drawable != null) {
            Drawable.Update(seconds);
        }
    }

    public void LoadContent(/* I forget the args */) {
        // Entity LoadContent logic...
        if (Drawable != null) {
            Drawable.LoadContent(seconds);
        }
    }
}

Это дает мне большую гибкость для создания подклассов Entity (AIEntity, NonInteractiveEntity ...), которые, вероятно, переопределяют Update().Это также позволяет мне свободно создавать подклассы Drawable, без множества n ^ 2 подклассов, таких как AnimatedSpriteAIEntity, ParticleEffectNonInteractiveEntity и AnimatedSpriteNoninteractiveEntity.Вместо этого я могу сделать это:

Entity torch = new NonInteractiveEntity();
torch.Drawable = new AnimatedSpriteDrawable("Animations\litTorch");
SomeGameScreen.AddEntity(torch);

// let's say you can load an enemy AI script like this
Entity enemy = new AIEntity("AIScritps\hostile");
enemy.Drawable = new AnimatedSpriteDrawable("Animations\ogre");
SomeGameScreen.AddEntity(enemy);

Мой класс рисования:У меня есть абстрактный класс, из которого производны все мои нарисованные объекты.Я выбрал абстрактный класс, потому что часть поведения будет общей.Было бы вполне приемлемо определить это как интерфейс вместо этого, если это не относится к вашему коду.

public abstract class Drawable {
    // my game is 2d, so I use a Point to draw...
    public Point Coordinates { get; set; }
    // But I usually store my game state in a Vector2,
    // so I need a convenient way to convert. If this
    // were an interface, I'd have to write this code everywhere
    public void SetPosition(Vector2 value) {
        Coordinates = new Point((int)value.X, (int)value.Y);
    }

    // This is overridden by subclasses like AnimatedSprite and ParticleEffect
    public abstract void Draw(SpriteBatch spriteBatch, Rectangle visibleArea);
}

Подклассы определяют свою собственную логику рисования.В вашем примере с танком вы могли бы сделать несколько вещей:

  • Добавьте новый объект для каждого маркера
  • Создайте класс TankEntity, который определяет список и переопределяет функцию Draw() для перебора маркеров (которые определяют собственный метод рисования)
  • Сделайте список доступным для рисования

Вот пример реализации ListDrawable, игнорирующий вопрос о том, как управлять самим списком.

public class ListDrawable : Drawable {
    private List<Drawable> Children;
    // ...
    public override void Draw(SpriteBatch spriteBatch, Rectangle visibleArea) {
        if (Children == null) {
            return;
        }

        foreach (Drawable child in children) {
            child.Draw(spriteBatch, visibleArea);
        }
    }
}

Другие советы

фреймворки, подобные MOQ и Носорог Издевается мне специально не нужен интерфейс.Они также могут издеваться над любым незапечатанным и / или абстрактным классом.Game - это абстрактный класс, так что у вас не должно возникнуть никаких проблем с издевательствами над ним :-)

Единственное, что следует отметить, по крайней мере, в отношении этих двух фреймворков, - это то, что для установления каких-либо ожиданий от методов или свойств они должны быть виртуальными или абстрактными.Причина этого заключается в том, что созданный им экземпляр mocked должен иметь возможность переопределения.У typemock, упомянутого IAmCodeMonkey, я полагаю, есть способ обойти это, но я не думаю, что typemock бесплатен, в то время как два, о которых я упомянул, бесплатны.

В качестве отступления, вы также можете ознакомиться с моим проектом, который мог бы помочь в создании модульных тестов для игр XNA без необходимости создавать макеты: http://scurvytest.codeplex.com/

Тебе не нужно издеваться над этим.Почему бы не создать поддельный игровой объект?

Наследуйте от Game и переопределяйте методы, которые вы собираетесь использовать в своих тестах, чтобы возвращать готовые значения или вычисления ярлыков для любых методов / свойств, которые вам нужны.Затем передайте подделку на ваши тесты.

До создания mocking frameworks люди создавали свои собственные mocks / заглушки / фейки - возможно, это не так быстро и легко, но вы все равно можете.

Вы можете использовать инструмент под названием TypeMock, который, как я полагаю, не требует от вас наличия интерфейса.Ваш другой и более распространенный метод заключается в создании нового класса, который наследуется от Game, а также реализует созданный вами интерфейс, соответствующий игровому объекту.Затем вы можете создать код для этого интерфейса и передать свой "пользовательский" игровой объект.

public class MyGameObject : Game, IGame
{
    //you can leave this empty since you are inheriting from Game.    
}

public IGame
{
    public GameComponentCollection Components { get; set; }
    public ContentManager Content { get; set; }
    //etc...
}

Это немного утомительно, но позволяет добиться макетируемости.

Я вернусь на твой пост, если ты не возражаешь, поскольку мой кажется, он менее активен, а вы уже поставили на кон свою репутацию ;)

Когда я читаю ваши сообщения (как здесь, так и на XNAForum) Я думаю, что это как фреймворк, который мог бы быть более доступным, так и мой (наш) дизайн, который не безупречен.

Фреймворк можно было бы разработать таким образом, чтобы его было легче расширять.Мне трудно в это поверить Шонглавный аргумент a точный удар по интерфейсам.Чтобы поддержать меня его коллега говорит, что от удара перфоратором можно было легко уклониться.
Обратите внимание, что фреймворк уже имеет IUpdatable и IDrawable интерфейс.Почему бы не пройти весь путь до конца?

С другой стороны, я также думаю, что на самом деле мой (и ваш) дизайн не безупречен.Там, где я не завишу от игрового объекта, я сильно завишу от объекта GraphicsDevice.Я собираюсь посмотреть, как я могу обойти это.Это усложнит код, но я думаю, что я действительно могу разорвать эти зависимости.

В качестве отправной точки для чего-то подобного я бы обратился к Пример XNA WinForms.Используя этот образец в качестве модели, оказывается, что одним из способов визуализации компонентов в WinForm является создание для него элемента управления в том же стиле, что и SpinningTriangleControl из образца.Это демонстрирует, как отрисовывать XNA-код без экземпляра Игры.На самом деле важна не Игра, а то, что она делает для вас.Итак, что вы могли бы сделать, это создать проект библиотеки, который имеет логику загрузки / отрисовки компонента в классе, а в других ваших проектах создайте класс управления и класс компонента, которые являются оболочками для кода библиотеки в их соответствующих средах.Таким образом, код, который вы тестируете, не дублируется, и вам не нужно беспокоиться о написании кода, который всегда будет жизнеспособен в двух разных сценариях.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top