XNA-关于世界空间和屏幕空间之间的关系 [英] XNA - About the relation between world space and the screen space

查看:72
本文介绍了XNA-关于世界空间和屏幕空间之间的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:只是想让我更清楚地回答这个问题.我几乎很难看到Matrix.CreateTransformationZ之类的东西不仅在矩阵乘法的上下文中起作用,而且更重要的是,这对屏幕空间/世界空间有什么作用,因此我可以获得更清晰的画面.因此,也许有人可以更改代码或给我一个简短的代码片段,以测试我可以在其中使用它绕轴旋转和/或绕轴旋转的地方.我也更改了示例.

Just wanted to make the question I have more clear. I pretty much am having trouble seeing how something like Matrix.CreateTransformationZ works in the context of not only matrix multiplication but more importantly what this does to the screen space/world space so I can get a clearer picture. So maybe someone could alter the code or give me a short snippet to test out where I can use this to either rotate around an axis and/or orbit around the axis. I have also changed the example.

因此,我仍然很难在可视化矩阵如何使用xna屏幕空间的情况下进行可视化.

So I'm still kind of having trouble visualizing how matrices work with the xna screen space.

我给你举个例子:

public class Game1 : Microsoft.Xna.Framework.Game
{
    Texture2D shipTexture, rockTexture;


    Vector2 shipPosition = new Vector2(100.0f, 100.0f);
    Vector2 rockPosition = new Vector2(100.0f, 29.0f);

    int count;

    float shipRotation, rockRotation;
    float rockSpeed, rockRotationSpeed;
    bool move = true;

    const int rock = 0;
    const int ship = 1;

    Color[] rockColor;
    Color[] shipColor;

    float testRot = 0.0f;
    Vector2 shipCenter; int shipWidth, shipHeight;
    Vector2 rockCenter; int rockWidth, rockHeight;

    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    #region maincontent
    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
        // TODO: Add your initialization logic here
        rockSpeed = 0.16f;
        rockRotationSpeed = 0.3f;
        base.Initialize();
    }



    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
        shipTexture = Content.Load<Texture2D>("Images\\ship");
        rockTexture = Content.Load<Texture2D>("Images\\asteroid");

        rockWidth = rockTexture.Width; rockHeight = rockTexture.Height;
        shipWidth = shipTexture.Width; shipHeight = shipTexture.Height;

        rockCenter = new Vector2(rockWidth / 2, rockHeight / 2);
        shipCenter = new Vector2(shipWidth / 2, shipHeight / 2);



        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        // TODO: use this.Content to load your game content here
        rockColor = new Color[rockTexture.Width * rockTexture.Height];
        rockTexture.GetData(rockColor);
        shipColor = new Color[shipTexture.Width * shipTexture.Height];
        shipTexture.GetData(shipColor);
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

            /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

        spriteBatch.Draw(rockTexture, rockPosition,
            null, Color.White, testRot, rockCenter, 1.0f,
            SpriteEffects.None, 0.0f);

        spriteBatch.Draw(shipTexture, shipPosition,
            null, Color.White, shipRotation, shipCenter,
            1.0f, SpriteEffects.None, 0.0f);

        spriteBatch.End();
        // TODO: Add your drawing code here

        base.Draw(gameTime);
    }
    #endregion

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
        testRot += 0.034906585f;
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        UpdateAsteroid(gameTime);
        RotateShip(gameTime);
        MoveShip(gameTime);
        // TODO: Add your update logic here
        CheckCollisions();
        base.Update(gameTime);
    }

    #region Collisions

    public Color PixelColor(int objectNum, int pixelNum)
    {
        switch (objectNum)
        {
            case rock:
                return rockColor[pixelNum];
            case ship:
                return shipColor[pixelNum];
        }

        return Color.White;
    }

    public bool PixelCollision(Matrix transformA, int pixelWidthA, int pixelHeightA, int A,
        Matrix transformB, int pixelWidthB, int pixelHeightB, int B)
    {
        Matrix temp = Matrix.Invert(transformB);
        Matrix AtoB = transformA * Matrix.Invert(transformB);

        Vector2 columnStep, rowStep, rowStartPosition;

        columnStep = Vector2.TransformNormal(Vector2.UnitX, AtoB);
        rowStep = Vector2.TransformNormal(Vector2.UnitY, AtoB);

        rowStartPosition = Vector2.Transform(Vector2.Zero, AtoB);

        for (int rowA = 0; rowA < pixelHeightA; rowA++)
        {
            // begin at the left
            Vector2 pixelPositionA = rowStartPosition;

            // for each column in the row (move left to right)
            for (int colA = 0; colA < pixelWidthA; colA++)
            {
                // get the pixel position
                int X = (int)Math.Round(pixelPositionA.X);
                int Y = (int)Math.Round(pixelPositionA.Y);

                // if the pixel is within the bounds of B
                if (X >= 0 && X < pixelWidthB && Y >= 0 && Y < pixelHeightB)
                {

                    // get colors of overlapping pixels
                    Color colorA = PixelColor(A, colA + rowA * pixelWidthA);
                    Color colorB = PixelColor(B, X + Y * pixelWidthB);

                    // if both pixels are not completely transparent,
                    if (colorA.A != 0 && colorB.A != 0)
                        return true; // collision
                }
                // move to the next pixel in the row of A
                pixelPositionA += columnStep;
            }

            // move to the next row of A
            rowStartPosition += rowStep;
        }

        return false; // no collision
    }
    public Matrix Transform(Vector2 center, float rotation, Vector2 position)
    {

        return Matrix.CreateTranslation(new Vector3(-center, 0.0f)) *
            Matrix.CreateRotationZ(rotation) *
            Matrix.CreateTranslation(new Vector3(position, 0.0f));
    }

    public static Rectangle TransformRectangle(Matrix transform, int width, int height)
    {
        Vector2 leftTop = new Vector2(0.0f, 0.0f);
        Vector2 rightTop = new Vector2(width, 0.0f);
        Vector2 leftBottom = new Vector2(0.0f, height);
        Vector2 rightBottom = new Vector2(width, height);

        Vector2.Transform(ref leftTop, ref transform, out leftTop);
        Vector2.Transform(ref rightTop, ref transform, out rightTop);
        Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
        Vector2.Transform(ref rightBottom, ref transform, out rightBottom);

        Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop), Vector2.Min(leftBottom, rightBottom));
        Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop), Vector2.Max(leftBottom, rightBottom));

        return new Rectangle((int)min.X, (int)min.Y,
            (int)(max.X - min.X), (int)(max.Y - min.Y));
    }

    private void CheckCollisions()
    {
        Matrix shipTransform, rockTransform;

        Rectangle shipRectangle, rockRectangle;

        rockTransform = Transform(rockCenter, rockRotation, rockPosition);
        rockRectangle = TransformRectangle(rockTransform, rockWidth, rockHeight);
        shipTransform = Transform(shipCenter, shipRotation, shipPosition);
        shipRectangle = TransformRectangle(shipTransform, shipWidth, shipHeight);

        if (rockRectangle.Intersects(shipRectangle)) // rough collision check
            if (PixelCollision( // exact collision check
            rockTransform, rockWidth, rockHeight, rock,
            shipTransform, shipWidth, shipHeight, ship))
                move = false;
    }
    #endregion

    #region Moves_and_Rotations

    private void UpdateAsteroid(GameTime gameTime)
    {
        float timeLapse = (float)gameTime.ElapsedGameTime.Milliseconds;

        if (move == true)
        {
            if ((rockWidth + rockPosition.X >= Window.ClientBounds.Width))
            {
                rockSpeed *= -1.0f;
                rockPosition.X += rockSpeed * timeLapse;
            }
            else if ((rockPosition.X <= 0))
            {
                rockSpeed *= -1.0f;
                rockPosition.X += rockSpeed * timeLapse;

            }
            else
                rockPosition.X += rockSpeed * timeLapse;

            const float SCALE = 50.0f;
            rockRotation += rockRotationSpeed * timeLapse / SCALE;

            rockRotation = rockRotation % (MathHelper.Pi * 2.0f);
        }
    }

    private float RotateShip(GameTime gameTime)
    {
        float rotation = 0.0f;
        float speed = gameTime.ElapsedGameTime.Milliseconds / 300.0f;

        if (!move)
            return rotation;

        KeyboardState keyboard = Keyboard.GetState();

        if (keyboard.IsKeyDown(Keys.Right))
            rotation = speed;
        else if (keyboard.IsKeyDown(Keys.Left))
            rotation = -speed;

        shipRotation += rotation;

        shipRotation = shipRotation % (MathHelper.Pi * 2.0f);
        return shipRotation;
    }

    private void MoveShip(GameTime gameTime)
    {
        const float SCALE = 20.0f;
        float speed = gameTime.ElapsedGameTime.Milliseconds / 100.0f;

        KeyboardState keyboard = Keyboard.GetState();

        if (keyboard.IsKeyDown(Keys.Up))
        {

            shipPosition.X += (float)Math.Sin(shipRotation) * speed * SCALE;
            shipPosition.Y -= (float)Math.Cos(shipRotation) * speed * SCALE;
        }
        else if (keyboard.IsKeyDown(Keys.Down))
        {
            shipPosition.X -= (float)Math.Sin(shipRotation) * speed * SCALE;
            shipPosition.Y += (float)Math.Cos(shipRotation) * speed * SCALE;
        }
    }
#endregion
}

我是从XNA Game Creators那里获得的,这只是像素检测的一种方法.

I took this from XNA Game Creators, it's simply a method of doing Pixel Detection.

  1. 在上面的变换"方法中,我猜对一个矩形进行矩阵乘法.在屏幕空间/世界空间方面到底发生了什么?

  1. In the Transform method above, matrix multiplication occurs against I guess a rectangle. What exactly is happening in terms of the screen space/world space?

为什么作者将矩阵乘以另一个矩阵的逆? (他提到以某种方式使它相对于其他资产而言)

Why is author multiplying the matrix by the inverse of another matrix? (He mentions that somehow this makes it relative to the other asset)

推荐答案

屏幕空间与客户端空间大概是同一回事.客户空间从左上角的(0,0)到右下角的(宽度,高度). 上"是Y-.

Screen space is presumably the same thing as Client Space. Client space goes from (0,0) in the top left corner to (width, height) in the bottom right. "Up" is Y-.

投影空间从左下角的(-1,-1)到右上角的(1,1).这就是GPU用于最终渲染的方式. SpriteBatch为您处理此问题(相反:BasicEffect要求您提供投影矩阵).

Projection space goes from (-1,-1) in the bottom-left corner to (1,1) in the top-right. This is what the GPU uses for its final rendering. SpriteBatch handles this for you (by contrast: BasicEffect requires you to provide a projection matrix).

世界空间是您想要的.这是游戏玩法所在的坐标系.在您的示例中,这似乎与相同客户空间.

World space is whatever you want it to be. This is the coordinate system that your gameplay takes place in. In your example it seems that this is the same as Client space.

传统上,在执行此类操作时,您在其自己的空间中定义了一个对象.在您的示例中,岩石和舰船矩形被硬编码为函数TransformRectangle作为变量topLeftbottomRight的初始值.

Traditionally, when doing this sort of thing, you have an object defined in its own space. In your example the rock and ship rectangles are hard coded into the function TransformRectangle as the initial values of the variables topLeft through bottomRight.

然后,每个对象都有一个世界矩阵.这会将对象从其自身的空间移到其在世界空间中的位置.在您的示例中,这是shipTransformrockTransform.根据输入的参数(使用纹理本身作为初始对象),在SpriteBatch.Draw内部也进行了 世界转换.

You then have a World matrix for each object. This moves that object from its own space into its position in World space. In your example this is shipTransform and rockTransform. A World transform is also done inside SpriteBatch.Draw, based on the arguments you pass in (using the texture itself as the initial object).

然后您有一个视图矩阵-您可以将其视为您的相机.您的示例没有其中之一.但是,例如,如果您想平移视图以跟随玩家,则可以在此处使用根据玩家的位置创建的转换矩阵(并将其传递给SpriteBatch.Begin).

Then you have a View matrix - which you can think of as your camera. You example doesn't have one of these. But if you wanted, for example, to pan the view around to follow the player, you would use a translation matrix here, created from the player's position (and pass it to SpriteBatch.Begin).

最后,您有了一个 Projection矩阵,该矩阵将您的World空间转换为Projection空间,以便GPU可以渲染您的场景.

And finally you have a Projection matrix that converts your World space into Projection space so that the GPU can render your scene.

现在,这里可能存在的问题是SpriteBatch在内部定义了一个投影矩阵,该矩阵将客户空间转换为投影空间(因此,它基本上假定"世界空间是客户空间).在您的示例中这不是问题,因为两个空格 相同.

Now a possible problem here is that SpriteBatch internally defines a projection matrix that converts a Client space to Projection space (so it basically "assumes" World space is Client space). Not a problem in your example because the two spaces are the same.

如果您的世界空间与客户空间不是相同,并且您想使用SpriteBatch,则必须创建一个附加矩阵以将世界空间转换为客户空间并将其插入视图之间和Project矩阵(即:将其与View相乘并将其传递到SpriteBatch.Begin).

If your World space is not the same thing as Client space, and you want to use SpriteBatch, you must create an additional matrix to convert from World space to Client space and insert it between the View and Project matrices (ie: multiply it with View and pass it into SpriteBatch.Begin).

如果您的世界空间定义的方式不同于SpriteBatch,则向上"(或正确")的方式,则必须记住,SpriteBatch.Draw使用的原始对象将向上"定义为Y. -.

If your World space defines which way is "up" (or "right", for that matter) differently to SpriteBatch, then you must keep in mind that the original object used by SpriteBatch.Draw defines "up" to be Y-.

这篇关于XNA-关于世界空间和屏幕空间之间的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆