เลื่อนกล้อง XNA 2d - ทำไมต้องใช้การแปลงเมทริกซ์?


18

ฉันกำลังทำเกมทดสอบที่ฉันต้องการให้เลื่อนระดับอย่างต่อเนื่อง ในการสร้างเอฟเฟกต์นี้ฉันได้สร้างคลาสกล้องที่เก็บตำแหน่งเวกเตอร์ 2 และทิศทาง Enum นอกจากนี้ยังมีวิธีการสาธารณะเพื่อ 'ย้าย' ซึ่งเพียงเปลี่ยนตำแหน่งในอัตราคงที่ จากนั้นฉันจะใช้ตำแหน่งนี้เมื่อวนลูปผ่านอาร์เรย์ของกระเบื้องเมื่อวาดภาพ ทั้งหมดนี้ทำงานได้ดี

อย่างไรก็ตามฉันได้รับแจ้งว่าฉันควรใช้ Transform matrix เพื่อย้ายกล้องและฉันควรให้สิ่งนี้เมื่อฉันเริ่ม spritebatch ฉันสับสนเล็กน้อย a.) วิธีนี้ทำงานอย่างไร ราวกับว่าฉันจะให้มันก็ต่อเมื่อ spritebatch เริ่มต้นจะรู้ได้อย่างไรว่าจะเปลี่ยนตำแหน่งได้อย่างไร 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);
    }

================================================== =============================== แก้ไข:

ประการแรกขอขอบคุณ craftworkgames สำหรับความช่วยเหลือของคุณ

ฉันเล่นด้วยคำแนะนำ เมื่อฉันวาดไพ่ทั้งหมด fr tanked ไปประมาณ 15 จาก 30 - อาจเป็นเพราะระดับที่ค่อนข้างใหญ่

ดังนั้นสิ่งที่ฉันได้ทำคือใช้เมทริกซ์และย้ายในการปรับปรุง (ตามที่แนะนำ) แต่ในการวาดฉันใช้ตำแหน่งกล้องสำหรับการวนลูปผ่านกระเบื้อง (เช่นเริ่มนับที่ด้านซ้ายและสิ้นสุดที่ไทล์ขวา) ทั้งหมดนี้ทำงานได้ดีและฉันมีความสุขกับมัน :-)

ปัญหาใหม่ของฉันอยู่ในโปรแกรมเล่น เห็นได้ชัดว่าตอนนี้ฉันกำลังขยับกล้องมากกว่าระดับผู้เล่นจะได้รับการทิ้งไว้ข้างหลังโดยผู้คุมเนื่องจากตำแหน่งของมันคงที่ ฉันคิดว่าจะแก้ปัญหานี้ได้สองวิธีอย่างแรกคือการพิจารณาตำแหน่งกล้องเมื่อวาดภาพผู้เล่น เช่นในฟังก์ชั่นการวาดเพียงเพิ่มตำแหน่งกล้องไปยังตำแหน่งผู้เล่น ประการที่สองคือการเริ่มชุดเทพดาใหม่สำหรับผู้เล่นที่ไม่มีการแปลง คือจบ spritebatch หลังจากวาดรูปแล้วเริ่มอันใหม่เมื่อทำการวาดภาพผู้เล่น ฉันรู้ว่าทั้งสองจะทำงานได้ แต่ฉันไม่สามารถทำหัวก้อยซึ่งจะดีกว่าในแง่ของประสิทธิภาพ / การเขียนโปรแกรมที่ดี? ฉันไม่แน่ใจว่าสิ่งที่เกี่ยวข้องกับประสิทธิภาพนั้นมาจากการเริ่มแบตช์สองครั้งหรือไม่


1
“ ปัญหาใหม่ของฉันอยู่ที่ผู้เล่นเห็นได้ชัดว่าขณะนี้ฉันกำลังเคลื่อนกล้องมากกว่าระดับผู้เล่นจะถูกทิ้งไว้ข้างหลังโดยผู้เล่นที่ตำแหน่งยังคงได้รับการแก้ไข” แค่อ่านสิ่งนี้และฉันก็สับสนว่าทำไมบนโลกนี้คุณจะไม่ย้ายผู้เล่นภายในระบบ coordiante ระดับ?
ClassicThunder

คำตอบ:


15

การแปลงเมทริกซ์กล้องเป็นเรื่องง่าย

การสร้างกล้องพื้นฐานนั้นง่าย ด้านล่างนี้จะช่วยให้คุณเริ่มต้นด้วยพื้นฐาน ย้ายไปมาหมุนและปรับขนาด การเคลื่อนย้ายสไปรท์ 2 มิติทุกครั้งนั้นไม่ได้เป็นปัญหามากนัก แต่ถ้าคุณคำนึงถึงการปรับสเกลหรือการหมุนเวียน

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

จากนั้นในวิธีการอัปเดตของคุณคุณอาจทำสิ่งนี้:

    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

ขอบคุณฉันรับคำแนะนำของคุณและไปกับตัวเลือกในการใช้ชุดสไปรต์เดียวกันและเพิ่งรับตำแหน่งกล้องในการอัปเดตและนำไปใช้กับตำแหน่งผู้เล่นเมื่อวาด ดูเหมือนว่าจะทำงานได้ดี ขอบคุณสำหรับความช่วยเหลือของคุณติดอยู่กับกล้องซักครู่ ตอนนี้ชัดเจนมากขึ้น :-)
Pectus Excavatum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.