XNA 2D 카메라 스크롤-왜 매트릭스 변환을 사용합니까?


18

레벨을 계속 스크롤하고 싶은 테스트 게임을 만들고 있습니다. 이 효과를 만들기 위해 단순히 vector2 위치와 열거 방향을 저장하는 카메라 클래스를 만들었습니다. 또한 고정 된 속도로 위치를 변경하는 '이동'에 대한 공개 방법도 포함되어 있습니다. 그런 다음 그릴 때 타일 배열을 반복 할 때이 위치를 사용합니다. 이 모든 것이 잘 작동합니다.

그러나 변환 매트릭스를 사용하여 카메라를 이동해야하며 스프라이트 배치를 시작할 때이를 제공해야한다는 말을 들었습니다. 나는 약간 혼란 스럽다.) 어떻게 작동합니까? 스프라이트 배치가 시작될 때만주는 것처럼 마치 위치를 계속 바꾸는 것을 어떻게 알 수 있습니까? b.) 타일을 반복 할 때 카메라 위치가 여전히 필요한 이유는 무엇입니까?

현재로서는 작동시킬 수 없지만 작동 방식을 완전히 이해하지 못해 놀라운 것은 아닙니다. 현재 내 시도 (따라 오는 코드)에서 그려진 타일이 변경되어 카메라 위치가 변경되지만 뷰포트의 위치는 변경되지 않습니다 (예 : 카메라 원점). 나는 그것이 어떻게 사용되어야하는지에 대한 조언 / 지침을 정말로 고맙게 생각할 것입니까?

카메라:

 class Camera {

    // The position of the camera.
    public Vector2 Position {
        get { return mCameraPosition; }
        set { mCameraPosition = value; }
    }
    Vector2 mCameraPosition;

    public Vector2 Origin { get; set; }

    public float Zoom { get; set; }

    public float Rotation { get; set; }

    public ScrollDirection Direction { get; set; }

    private Vector2 mScrollSpeed = new Vector2(20, 18);

    public Camera() {
        Position = Vector2.Zero;
        Origin = Vector2.Zero;
        Zoom = 1;
        Rotation = 0;
    }


    public Matrix GetTransform() {
        return Matrix.CreateTranslation(new Vector3(mCameraPosition, 0.0f)) *
               Matrix.CreateRotationZ(Rotation) *
               Matrix.CreateScale(Zoom, Zoom, 1.0f) *
               Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
    }




    public void MoveCamera(Level level) {
        if (Direction == ScrollDirection.Up)
        {
            mCameraPosition.Y = MathHelper.Clamp(mCameraPosition.Y - mScrollSpeed.Y, 0, (level.Height * Tile.Height - level.mViewport.Height));
        }
    }

수평:

 public void Update(GameTime gameTime, TouchCollection touchState) {

            Camera.MoveCamera(this);
 }


 public void Draw(SpriteBatch spriteBatch) {
        //spriteBatch.Begin();
        spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise, null, mCamera.GetTransform());


        DrawTiles(spriteBatch);

        spriteBatch.End();
    }

게임-레벨 내에서 드로우를 호출합니다.

  protected override void Draw(GameTime gameTime)  {
        mGraphics.GraphicsDevice.Clear(Color.Black);

        //mSpriteBatch.Begin();

        // Draw the level.
        mLevel.Draw(mSpriteBatch);

        //mSpriteBatch.End();

        base.Draw(gameTime);
    }

===================================================== =============================== 편집 :

먼저, 지금까지 도와 주신 공예품 게임에 감사드립니다.

나는 제안을 가지고 놀았습니다. 모든 타일을 그릴 때 fr은 30에서 15로 약 15 수준으로 늘어났습니다. 아마도 레벨이 상당히 크기 때문일 것입니다.

그래서 내가 한 것은 매트릭스를 적용하고 업데이트로 이동하는 것입니다 (제안대로)하지만 그릴 때 카메라 위치를 사용하여 타일을 반복합니다 (즉, 왼쪽의 시작 카운터와 오른쪽 타일의 끝). 이 모든 것이 잘 작동하고 행복합니다 :-)

내 새로운 문제는 플레이어에 있습니다. 분명히 레벨이 아닌 카메라를 움직일 때 플레이어는 위치가 고정 된 채로 캠에 의해 남겨집니다. 이 문제에 대한 두 가지 해결책을 생각했습니다. 첫 번째는 플레이어를 그릴 때 카메라 위치를 단순히 고려하는 것입니다. 즉, 그리기 기능에서 단순히 카메라 위치를 플레이어 위치에 추가하십시오. 두 번째는 변형이없는 플레이어를위한 새로운 스프라이트 배치를 시작하는 것입니다. 즉, 타일을 그린 후 스프라이트 배치를 끝내고 플레이어를 그릴 때 새 타일을 시작하십시오. 둘 다 작동하지만 알고 있지만 성능 / 좋은 코딩 측면에서 더 나은 꼬리를 만들 수는 없습니까? 배치를 두 번 시작하면 성능에 어떤 영향이 있는지 잘 모르겠습니다.


1
"제 새로운 문제는 플레이어에게 있습니다. 분명히 레벨이 아닌 카메라를 움직이면 플레이어는 위치가 고정되어있는 동안 플레이어가 카메라를 사용하게됩니다." 이 글을 읽고 왜 지구상에서 플레이어를 레벨의 코디네이터 시스템 내로 옮기지 않겠습니까?
ClassicThunder

답변:


15

카메라 매트릭스 변환이 쉽다

기본 카메라를 만드는 것은 쉽습니다. 아래는 기본 사항을 시작하는 것입니다. 주위를 움직이고 회전하고 스케일링합니다. 모든 2D 스프라이트를 움직이는 것은 큰 문제가 아니지만 스케일링이나 회전을 고려하면 모든 스프라이트에 개별적으로 적용하기가 실제로 어렵습니다.

class Camera2D 
{
    public float Zoom { get; set; }
    public Vector2 Location { get; set; }
    public float Rotation { get; set;}

    private Rectangle Bounds { get; set; }

    private Matrix TransformMatrix
    { 
        get: {
            return 
                Matrix.CreateTranslation(new Vector3(-Location.X, -Location.Y, 0)) *
                Matrix.CreateRotationZ(Rotation) *
                Matrix.CreateScale(Zoom) *
                Matrix.CreateTranslation(new Vector3(Bounds.Width * 0.5f, Bounds.Height * 0.5f, 0));
        }
    };

    public Camera2D(Viewport viewport) 
    {
        Bounds = viewport.Bounds;
    }
}

좌표계 정의를 쉽게 변환 할 수 있습니다.

단순히 화면에서 세계 공간으로 이동합니다. 이것은 일반적으로 객체 선택을 위해 세계에서 마우스의 위치를 ​​얻는 데 사용됩니다.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

세계에서 화면 공간으로 이동하려면 단순히 반대를 수행하십시오.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

약간의 학습이 필요한 것 이외의 매트릭스를 사용하는 것에 대한 결점은 없습니다.

가시 영역을 쉽게 얻을 수 있습니다.

public Rectangle VisibleArea {
    get {
        var inverseViewMatrix = Matrix.Invert(View);
        var tl = Vector2.Transform(Vector2.Zero, inverseViewMatrix);
        var tr = Vector2.Transform(new Vector2(_screenSize.X, 0), inverseViewMatrix);
        var bl = Vector2.Transform(new Vector2(0, _screenSize.Y), inverseViewMatrix);
        var br = Vector2.Transform(_screenSize, inverseViewMatrix);
        var min = new Vector2(
            MathHelper.Min(tl.X, MathHelper.Min(tr.X, MathHelper.Min(bl.X, br.X))), 
            MathHelper.Min(tl.Y, MathHelper.Min(tr.Y, MathHelper.Min(bl.Y, br.Y))));
        var max = new Vector2(
            MathHelper.Max(tl.X, MathHelper.Max(tr.X, MathHelper.Max(bl.X, br.X))), 
            MathHelper.Max(tl.Y, MathHelper.Max(tr.Y, MathHelper.Max(bl.Y, br.Y))));
        return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
    }
}

카메라 모서리를 간단히 변형하고 월드 공간에서 해당 위치를 얻을 수 있습니다. 최소 x, y 값을 최대로 표시하면 볼 수있는 공간 주위에 직사각형을 얻을 수 있습니다. 드로우 콜 컬링 및 최적화에 매우 유용합니다.


1
감사. MonoGame.Extended 라이브러리 에서 카메라를 구현할 때 도움이되었습니다 .
craftworkgames

6

SpriteBatch에 행렬을 적용하면 전체 그리기 호출이 한 번에 변환됩니다. 즉, DrawTiles 방식으로 카메라를 사용할 필요가 없습니다.

다음과 같이 훨씬 간단해질 수 있습니다.

    // Loop through the number of visible tiles.
    for (int y = 0; y <= tiles.GetUpperBound(1); y++) {
        for (int x = 0; x <= tiles.GetUpperBound(0); x++) {

                // If the tile is not an empty space.
                if (tiles[x, y].Texture != null) {
                     // Get the position of the visible tile within the viewport by multiplying the counters by the tile dimensions
                     // and subtracting the camera offset values incase the position of the camera means only part of a tile is visible.
                     Vector2 tilePosition = new Vector2(x * Tile.Width, y * Tile.Height);
                     // Draw the correct tile
                     spriteBatch.Draw(tiles[x, y].Texture, tilePosition, Color.White);
                }
        }
    }

따라서 매트릭스를 사용하는 요점은 그것에 대해 생각할 필요가 없다는 것입니다. 사물을 그리고 카메라를 독립적으로 움직입니다.

또한 MoveCamera 메서드는 약간 이상하게 보입니다. 레벨을 종속성으로 사용하는 카메라 클래스를 갖는 것은 매우 드문 일입니다. 보다 일반적인 구현은 다음과 같습니다.

public void MoveCamera(float deltaX, float deltaY) {
    mCameraPosition.X += deltaX;
    mCameraPosition.Y += deltaY;
}

그런 다음 Update 메소드에서 다음과 같이 할 수 있습니다.

    if (Direction == ScrollDirection.Up)
    {
        mCamera.MoveCamera(mScrollSpeed.Y, 0);
    }

전반적으로, 내 제안은 간단하게 유지하는 것입니다. 가장 간단한 방법으로 주먹을 펴고 빌드하십시오. 기본 사항이 먼저 작동 할 때까지 고도로 최적화 된 코드를 작성하지 마십시오. 매 프레임마다 모든 타일을 렌더링하는 것이 그리 나쁘지 않다는 것을 알 수 있습니다.

편집 : 질문의 두 번째 부분.

배치 카운트를 낮게 유지하려는 것이 사실이지만 2 또는 3을 갖는 것이 전혀 문제가되지 않습니다. 따라서 두 번째 스프라이트 배치를 만들어야 할 충분한 이유가 있다면 그렇게하십시오.

즉,이 경우 두 번째 스프라이트 배치를 사용해야 할 이유가 없을 것입니다. 카메라 변환을 적용한 상태에서 동일한 스프라이트 배치로 타일을 그리는 것과 동일한 방식으로 플레이어를 그릴 가능성이 높습니다.

플레이어가 코드를 보지 않고 왜 떠난지 말하기는 조금 어렵지만 플레이어가 타일과 동일한 위치에 플레이어를 그리면 같은 위치에 같은 위치에 나타날 것입니다 스프라이트 배치.

예를 들어 플레이어가 타일 10, 10에 나타나게하려면 다음과 같이하십시오.

var playerPosition = new Vector2(10 * Tile.Width, 10 * Tile.Height);
spriteBatch.Draw(player.Texture, playerPosition, Color.White);

자신이있는 곳에 물건을 그리고 카메라가 문자 그대로 전체 "장면"을 시야로 이동시키는 것에 대한 사고의 사고 방식을 시도하십시오. 이것이 매트릭스 변환이하는 일입니다.


그것은 정말 도움이되고 물건을 정리했습니다! 정보 주셔서 감사합니다; 매우 감사. 오늘 후반에 귀하의 조언을 이행하려고 노력할 것입니다. 다시 감사합니다.
Pectus Excavatum

또한 흥미롭게도 매트릭스 변환을 사용하여 뷰포트에 타일 만 그리는 방법이 있습니까?
Pectus Excavatum

그렇게하는 방법이 있지만 머리 꼭대기를 알지 못합니다. 카메라 변환을 적용 한 후 뷰포트 사각형을 사용하는 것과 관련이 있다고 생각합니다. 몰라, 실험 해봐 디버거를 사용하십시오.
craftworkgames

알았어 고마워. 나는 그것을 가지고 놀았고 어딘가에 있었지만 플레이어와 함께해야 할 일에 대해 질문을했습니다. 질문 편집을 참조하십시오.
Pectus Excavatum

고마워, 나는 당신의 조언을 받아 같은 스프라이트 배치를 사용하고 업데이트에서 카메라 위치를 가져 와서 그릴 때 플레이어 위치에 적용하는 옵션을 사용했습니다. 잘 작동하는 것 같습니다. 모든 도움에 감사드립니다. 잠시 동안 카메라에 붙어있었습니다. 훨씬 명확
해짐
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.