2010-08-21 48 views
2

編輯:只是想讓問題更清楚。我幾乎無法看到Matrix.CreateTransformationZ如何在不僅是矩陣乘法的情況下工作,更重要的是這對屏幕空間/世界空間有什麼影響,因此我可以得到更清晰的圖像。所以也許有人可以改變代碼或給我一小段代碼來測試我可以在哪裏使用它來圍繞軸旋轉和/或圍繞軸旋轉。我也改變了這個例子。XNA - 關於世界空間與屏幕空間的關係

所以我仍然很難想象如何矩陣與xna屏幕空間一起工作。

我給你舉個例子:

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處,它只是在做像素檢測的方法。

  1. 在上面的變換方法中,矩陣乘法發生在我猜矩形上。屏幕空間/世界空間究竟發生了什麼?

  2. 爲什麼作者將矩陣乘以另一個矩陣的逆? (他提到,不知怎的,這使得它相對於其他資產)

回答

5

屏幕空間大概與客戶端空間相同。客戶端空間從左上角的(0,0)到右下角的(寬度,高度)。 「上」是Y-。

投影空間從左下角的(-1,-1)到右上角的(1,1)。這是GPU用於最終渲染的內容。 SpriteBatch爲你處理這個問題(相反:BasicEffect要求你提供一個投影矩陣)。

世界空間就是你想要的東西。這是您的遊戲進行的座標系。在您的示例中,它似乎是與客戶端空間相同的

傳統上,當做這樣的事情時,你有一個對象在自己的空間中定義。在您的示例中,岩石和船的矩形被硬編碼到函數TransformRectangle中,作爲變量topLeftbottomRight的初始值。

然後,您對每個對象都有一個世界矩陣。這將該物體從其自己的空間移動到其在世界空間中的位置。在你的例子中,這是shipTransformrockTransform。根據您傳入的參數(使用紋理本身作爲初始對象),在SpriteBatch.Draw內完成的世界轉換爲

然後你有一個查看矩陣 - 你可以認爲你的相機。你的例子沒有其中之一。但是,例如,如果您想要平移視角以跟隨玩家,您可以在此處使用一個由玩家位置創建的翻譯矩陣(並將其傳遞給SpriteBatch.Begin)。

最後你有一個投影矩陣將你的世界空間轉換爲投影空間,以便GPU可以渲染你的場景。

現在可能存在的一個問題是,SpriteBatch在內部定義了一個將客戶端空間轉換爲投影空間的投影矩陣(因此它基本上「假設」世界空間客戶端空間)。你的例子中沒有問題,因爲兩個空格都是一樣。

如果你的世界空間同樣的事情,客戶空間,並要使用SpriteBatch,你必須創建一個額外的矩陣,從世界空間轉換到客戶端的空間,將其插入視圖和項目矩陣之間(即:將它與View相乘並傳遞給SpriteBatch.Begin)。

如果您的世界空間定義了與SpriteBatch不同的「向上」(或「正確」)方式,那麼您必須記住SpriteBatch.Draw使用的原始對象將「向上」定義爲Y- 。

+0

你和Steve的回答對我很有幫助,現在認爲工作很好。 – Ilya 2010-08-22 02:54:17

0

在TestMatrix():

shipPosition = Vector2.Transform(shipPosition, rotZ);

應該

shipPosition = Vector2.Transform(shipPosition, comboMatrix);

0

的概念: - 翻譯,旋轉,+翻譯。是一種引起某物旋轉或旋轉的機制。 但是你正在將它應用到一個點(vector2)。有一個點旋轉的地方沒有什麼用處。我相信你真的想要的是船舶精靈在原地旋轉。這通常是通過更改shipRotation變量完成的,該變量是一個浮點數,用於描述您希望精靈旋轉的角度差(從0開始)的數量。

出於某種原因,你糊塗船舶對象的旋轉點(shipPosition)的旋轉...

在2D,雖然在矩陣數學工程,以及它在3D中,spritebatch .Draw()方法使用單個浮點來描述旋轉,並且不直接與Matrix生成的旋轉數據相關。

有趣的是,在2D中使用矩陣來轉換點是很好的,許多人不理解這一點。在這裏,你正試圖理解它,但真的希望它對一個點以外的對象起作用。所以,不要放棄矩陣。但只需更改Draw調用中的旋轉值即可將Sprite旋轉到位。

現在,如果我錯過了解釋你的目標,請讓我知道,我會盡力幫助進一步。

+0

好吧,但或多或​​少,我想知道當我做這些矩陣乘法時,我所看到的屏幕空間和世界空間之間的關係。 – Ilya 2010-08-21 22:10:29

1

我不相信這是造成你所看到的空間關係(在你的第一版問題中)。矩陣在它所處的空間方面是靈巧的。如果給它提供屏幕空間值,它將返回屏幕空間值。所以這種關係(屏幕/世界)並不相關,也不存在。

舉例來說,如果你想你的軌道繞船使用矩陣的2D畫面的中心點:

Vector2 screenCenter = new Vec2(width/2, h/2);// abbreviated 
Vector2 shipPosition = screenCenter; 
shipPosition.X += 25;//offset ship from center slightly 

shipPosition = Vector2.Transform(shipPosition, Matrix.CreateTranslation(-screenCenter)); 
shipPosition = Vector2.Transform(shipPosition , Matrix.CreateRotationZ(someRadians)); 
shipPosition = Vector2.Transform(shipPosition , Matrix.CreateTranslation(screenCenter)); 


//although the above would typically be written as: 
shipPosition = Vector2.Transform(shipPosition - screenCenter, Matrix.CreateRotationZ(someAngle)) + screenCenter; 

通知,所有的值僅屏幕空間值。沒有世界/屏幕空間的關係。所以這就是如何使用矩陣在二維屏幕空間中圍繞另一點旋轉一個點。對於3D,它將是確切的代碼,但帶有Z分量(vector3)並使用3d世界空間。

你的comboMatrix(來自更早的代碼)和你的新代碼片段中的transform()可能會讓你失望。將矩陣相乘時,就像向另一個旋轉一樣。所以你的comboMatrix就像3 + 5 +( - 3)......你所做的全部相當於5.你所有的comboMatrix所做的都是rotZ的平等......它沒有翻譯。和你的Transform()類似。當我將這三個矩陣應用於上面的shipPosition時,我確信每個Matrix都被應用到shipPosition,以便移動到下一個操作。有時你可以在應用之前連接矩陣,但在你的情況下,不是。

這是否幫助或我仍然錯過了你的問題?

+0

2.)哦,關於倒置的矩陣。 如果您認爲矩陣表示方向,假設MatrixA表示90度左方向,MatrixB表示130度方向。 A和B之間的差異是45度左方向。 現在Matrix.Invert()反轉矩陣,所以MatrixA的逆矩陣是90度** Right **旋轉。 所以MatrixB(剩下135個)* InvA(90右)導致45左邊。 因此,乘以一倍的差異其他結果的倒數。差別可以認爲是相對於第一個。 – 2010-08-22 00:24:34

+0

這主要幫助我。那麼,如果我想旋轉那個2d火箭飛船,而不是圍繞空間中的任意點旋轉,但是讓我們假設圍繞世界空間中的z軸和/或精靈的局部空間旋轉?使用CreateRotation矩陣仍然很簡單嗎? – Ilya 2010-08-22 00:45:55

+0

另一件事。你說:「comboMatrix就像3 + 5 +( - 3)......你真的做的只有5個。你所有的comboMatrix都是rotZ的等價物......它沒有翻譯,你的Transform()是類似...「 另外,你的意思是當我做旋轉-Z之前,因爲我正在餵它的屏幕座標,我基本上圍繞屏幕座標原點旋轉? 好的,但在我的程序中,無論是旋轉發生(意味着圖像位置從未改變)或旋轉一些奇怪的程度,我猜想屏幕座標原點發生。 – Ilya 2010-08-22 00:48:35

相關問題