XNA는 게임 객체를 조롱하거나 게임을 분리합니다
-
03-07-2019 - |
해결책
해당 포럼에는 단위 테스트 주제에 대한 좋은 게시물이 있습니다. 다음은 XNA의 단위 테스트에 대한 개인적인 접근 방식입니다.
- Draw () 메소드를 무시하십시오
- 자신의 계급 방법에서 복잡한 행동을 분리하십시오
- 까다로운 물건을 테스트하고 나머지는 땀을 흘리지 마십시오
다음은 내 업데이트 메소드가 Entities가 update () 호출 사이의 적절한 거리를 이동하도록 확인하기위한 테스트의 예입니다. (사용 중입니다 NUNIT.) 나는 다른 움직임 벡터로 몇 줄을 다듬었지만 아이디어를 얻습니다. 테스트를 주도하기 위해 게임이 필요하지 않습니다.
[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);
}
}
}
이것은 나에게 update ()를 무시하는 엔티티 (aientity, nonincintactive…)의 서브 클래스를 만들 수있는 많은 유연성을 제공합니다. 또한 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);
내 그림자 수업: 나는 내 그린 모든 객체가 파생되는 추상 클래스가 있습니다. 일부 행동이 공유되기 때문에 추상 수업을 선택했습니다. 이것을 an으로 정의하는 것은 완벽하게 허용됩니다 상호 작용 대신, 그것이 당신의 코드에 맞지 않는다면.
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);
}
서브 클래스는 자체 드로우 로직을 정의합니다. 탱크 예에서 몇 가지 작업을 수행 할 수 있습니다.
- 각 총알에 새 엔티티를 추가하십시오
- 목록을 정의하는 탱크 클래스를 만들고 Draw ()를 재정의하여 총알을 반복합니다 (자신의 드로우 방법을 정의)
- ListRudable을 만듭니다
다음은 목록 자체를 관리하는 방법에 대한 질문을 무시하고 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);
}
}
}
다른 팁
프레임 워크와 같은 모크 그리고 코뿔소 조롱 특히 인터페이스가 필요하지 않습니다. 그들은 비정규 및/또는 추상 클래스도 조롱 할 수 있습니다. 게임은 추상적 인 클래스이므로 조롱하는 데 어려움이 없어야합니다 :-)
적어도 두 가지 프레임 워크로 주목해야 할 것은 방법이나 속성에 대한 기대치를 설정하려면 가상 또는 추상이어야한다는 것입니다. 그 이유는 생성 된 모의 인스턴스가 무시할 수 있어야하기 때문입니다. iAmcodemonkey가 언급 한 타입 록 나는 이것에 대한 방법을 가지고 있다고 생각하지만 TypeMock은 무료라고 생각하지는 않지만 내가 언급 한 두 가지는 그렇지 않다고 생각합니다.
제쳐두고, 당신은 또한 모의를 만들 필요없이 XNA 게임에 대한 단위 테스트를 만드는 데 도움이 될 수있는 나의 프로젝트를 확인할 수 있습니다. http://scurvytest.codeplex.com/
당신은 그것을 조롱 할 필요가 없습니다. 왜 가짜 게임 객체를 만들지 않습니까?
게임에서 상속하고 테스트에서 사용하려는 방법을 무시하여 필요한 메소드/속성에 대한 통조림 값 또는 단축키 계산을 반환하십시오. 그런 다음 가짜를 테스트에 전달하십시오.
프레임 워크를 조롱하기 전에 사람들은 자신의 모의/스터브/가짜를 굴 렸습니다. 아마도 빠르고 쉽지는 않지만 여전히 가능합니다.
인터페이스가 필요하지 않은 TypEmock이라는 도구를 사용할 수 있습니다. 더 일반적으로 따르는 방법은 게임에서 상속되는 새로운 클래스를 만드는 것입니다. 또한 게임 객체와 일치하는 인터페이스를 구현하는 것입니다. 그런 다음 해당 인터페이스에 대해 코딩하고 '사용자 정의'게임 객체를 전달할 수 있습니다.
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...
}
약간 지루하지만 조롱 가능성을 달성 할 수 있습니다.
당신이 그 이후로 신경 쓰지 않으면 나는 당신의 게시물에 돼지를 돌려 줄거야 나의 것 덜 활동적인 것 같고 이미 담당자를 라인에 올려 놓았습니다.)
귀하의 게시물 (Here & Xnaforum)을 읽을 때 더 접근하기 쉬운 프레임 워크와 완벽한 디자인이 아닙니다.
프레임 워크는 더 쉽게 확장되도록 설계 될 수 있습니다. 나는 믿기 어려운 시간을 보내고 있습니다 숀a의 주요 인수 성능은 인터페이스에 적중합니다. 나를 백업하기 위해 그의 동료 PERT 히트는 쉽게 회피 될 수 있다고 말합니다.
프레임 워크에는 이미 iupdatable 및 idrawable 인터페이스가 있습니다. 왜 끝까지 가지 않습니까?
반면에 나는 또한 내 (그리고 당신의) 디자인이 완벽하지 않다고 생각합니다. 내가 게임 객체에 의존하지 않는 경우, 나는 GraphicsDevice 객체에 크게 의존합니다. 나는 이것을 어떻게 우회 할 수 있는지 보게 될 것이다. 코드를 더 복잡하게 만들지 만 실제로 그 종속성을 깨뜨릴 수 있다고 생각합니다.
이와 같은 시작점을 위해 나는 XNA Winforms 샘플. 이 샘플을 모델로 사용하면 Winform에서 구성 요소를 시각화하는 한 가지 방법은 샘플에서 SpinningTriangleControl과 동일한 스타일로 컨트롤을 만드는 것 같습니다. 이것은 게임 인스턴스없이 XNA 코드를 렌더링하는 방법을 보여줍니다. 실제로 게임은 중요하지 않습니다. 중요한 것은 중요한 일입니다. 따라서 클래스와 다른 프로젝트에서 구성 요소의로드/드로우 로직을 가진 라이브러리 프로젝트를 만드는 것입니다. 각 환경에서 라이브러리 코드의 래퍼 인 제어 클래스 및 구성 요소 클래스를 만듭니다. 이렇게하면 테스트 코드가 복제되지 않았으며 두 가지 시나리오에서 항상 실행 가능한 코드 작성에 대해 걱정할 필요가 없습니다.