Pergunta

Eu estava vagando se é possível para zombar um objeto do jogo, a fim de testar a minha componente DrawableGameComponent?

Eu sei que os quadros de zombaria precisa de uma interface, a fim de função, mas eu preciso para zombar o real Jogo objeto.

Edit: Aqui é um ligação a respectiva discussão em fóruns XNA comunitários . Qualquer ajuda?

Foi útil?

Solução

Há alguns bons lugares em que fórum sobre o tema de testes de unidade. Aqui está a minha abordagem pessoal para o teste de unidade em XNA:

  • Ignorar o método Draw ()
  • Isolar complicado comportamento em seus próprios métodos de classe
  • Teste o material complicado, não se preocupe o resto

Aqui está um exemplo de um teste para confirmar que o meu método update move Entidades a distância certa entre Update () chama. (Estou usando NUnit .) I aparado para fora um par de linhas com diferentes vectores de movimento, mas você começa a idéia: você não deve precisar de um jogo para conduzir seus testes

.
[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);
    }
}

Edit: Algumas outras notas dos comentários:

Minha Entity Class : Eu escolhi para envolver todo o meu jogo de objetos em uma classe entidade centralizada, que é algo como isto:

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);
        }
    }
}

Isso me dá muita flexibilidade para fazer subclasses de Entidade (AIEntity, NonInteractiveEntity ...) que provavelmente substituir Update (). Ele também permite que me subclasse Drawable livremente, sem o inferno de n ^ 2 subclasses como AnimatedSpriteAIEntity, ParticleEffectNonInteractiveEntity e AnimatedSpriteNoninteractiveEntity. Em vez disso, eu posso fazer isso:

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);

classe Meu Drawable : Eu tenho uma classe abstrata a partir do qual todos os meus objetos desenhados são derivados. Eu escolhi uma classe abstrata porque alguns dos comportamentos será compartilhado. Seria perfeitamente aceitável para definir isso como uma interface de em vez disso, Se isso não é verdade de seu código.

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);
}

As subclasses definir a sua própria lógica Draw. No seu exemplo do tanque, você poderia fazer algumas coisas:

  • Adicionar uma nova entidade para cada bala
  • Faça uma classe TankEntity que define uma lista, e substituições Draw () para iterar sobre os Bullets (que definem um método Draw própria)
  • Faça um ListDrawable

Aqui está um exemplo de implementação de ListDrawable, ignorando a pergunta de como administrar a própria lista.

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);
        }
    }
}

Outras dicas

MOQ e Rhino Mocks especificamente não precisa de uma interface. Eles podem zombar de qualquer classe não vedado e / ou abstrato bem. Jogo é uma classe abstrata, então você não deve ter nenhum problema zombando-lo: -)

A única coisa a nota com pelo menos essas duas estruturas é que para definir qualquer expectativa sobre métodos ou propriedades, devem ser virtual ou abstrato. A razão para isto é que a instância zombado gera necessidades para ser capaz de substituir. O typemock mencionado por IAmCodeMonkey Eu acredito que tem uma maneira de contornar isso, mas eu não acho que typemock é gratuito, enquanto os dois que eu mencionei são.

Como um aparte, você pode também verificar-se um projeto meu que poderia ajudar na criação de testes de unidade para jogos XNA, sem a necessidade de fazer simulações: http://scurvytest.codeplex.com/

Você não tem a zombar dele. Por que não fazer um objeto jogo falso?

Herdar do jogo e substituir os métodos que você pretende usar em seus testes para retornar valores enlatados ou cálculos de atalho para quaisquer métodos / propriedades que você precisa. Em seguida, passe o falso para seus testes.

Antes zombando de estruturas, as pessoas rolou seus próprios zomba / topos / falsificações - Talvez não seja tão rápido e fácil, mas você ainda pode

.

Você pode usar uma ferramenta chamada TypeMock que eu acredito que não exigem que você tenha uma interface. Seu método diferente e mais comumente seguido é o de criar uma nova classe que herda de jogo e também implementa uma interface que você criar que coincide com o objeto do jogo. Você pode então código contra essa interface e passar o seu 'personalizado' objeto jogo.

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...
}

É um pouco tedioso, mas ele permite que você a atingir mockability.

Eu vou voltar piggy em seu post, se você não se importa desde que meu parece ser menos ativa e você já colocou o seu representante na linha;)

Ao ler as suas mensagens (ambos XNAForum aqui &) Eu estou pensando que é tanto a estrutura que poderia ser mais acessível e meu (nosso) projeto que não é impecável.

O quadro poderia ter ser projetado para ser mais fácil estendida. Eu estou tendo um momento difícil de acreditar Shawn 's O principal argumento de um href="https://stackoverflow.com/questions/828476/does-coding-towards-an-interface-rather-then-an-implementation-imply-a-performanc"> hit . Para me apoiar seu colega diz o hit perf poderia ser easely evadido.
Faça nota que o quadro já tem uma interface IUpdatable e IDrawable. Por que não ir até o fim?

Por outro lado, eu também acho que na verdade o meu (e seu) projeto não são impecável. Onde eu não dependem do objeto Jogo, eu faço dependem do objeto GraphicsDevice muito. Vou ver como posso contornar isso. Vai código de fazer mais complicado, mas acho que pode realmente quebrar essas dependências.

Para um ponto de partida em algo como isso, eu teria atingido o Sample XNA WinForms . Usando este exemplo como modelo, parece que uma maneira de visualizar os componentes em um WinForm é criar um controle para ele no mesmo estilo do SpinningTriangleControl da amostra. Isso demonstra como processar código XNA sem uma instância de jogo. Realmente o jogo não é importante, suas o que ele faz para você o que importa. Então, o que você gostaria de fazer é criar um projeto de biblioteca que tem o Load / Desenhe lógica do componente em uma classe e em seus outros projetos, criar uma classe de controle e uma classe Component que são invólucros para a biblioteca de código em seus respectivos ambientes. Desta forma, o código de seu teste não é duplicado e você não precisa se preocupar em escrever código que sempre será viável em dois cenários diferentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top