2010-08-21 2 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, это просто способ сделать Detection Pixel.

  1. В приведенном выше методе преобразования умножается матрица на я думаю прямоугольник. Что именно происходит с точки зрения пространства экрана/мирового пространства?

  2. Почему автор умножает матрицу на обратную матрицу? (Он упоминает, что как-то это делает его относительно другого актива)

ответ

5

Экранированное пространство, по-видимому, такое же, как и «Клиентское пространство». Клиентское пространство идет от (0,0) в верхнем левом углу до (ширина, высота) в правом нижнем углу. «Вверх» - Y-.

Пространство проекции идет от (-1, -1) в нижнем левом углу до (1,1) в правом верхнем углу. Это то, что GPU использует для его окончательного рендеринга. SpriteBatch обрабатывает это для вас (в отличие от этого: BasicEffect требует, чтобы вы предоставили матрицу проецирования).

Мировое пространство - это то, что вы хотите, чтобы оно было. Это система координат, в которой ваш игровой процесс имеет место. В вашем примере кажется, что это то же самое как клиентское пространство.

Традиционно, когда вы делаете подобные вещи, у вас есть объект, определенный в своем собственном пространстве. В вашем примере прямоугольники рок и корабль жестко закодированы в функции TransformRectangle в качестве начальных значений переменных topLeft - bottomRight.

У вас есть Всемирная матрица для каждого объекта. Это перемещает этот объект из своего собственного пространства в его положение в Мировом пространстве. В вашем примере это shipTransform и rockTransform. Преобразование мира также сделано внутри SpriteBatch.Draw, основываясь на аргументах, которые вы передаете (используя саму текстуру как исходный объект).

Тогда у вас есть Показать матрицу, о которой вы можете думать как о своей камере. У вас, например, нет ни одного из них. Но если вы хотите, например, панорамировать вид вокруг, чтобы следить за игроком, вы должны использовать здесь матрицу перевода, созданную из позиции игрока (и передайте ее на SpriteBatch.Begin).

И, наконец, у вас есть матрица проекции , которая преобразует ваше пространство мира в пространство проекций, чтобы графический объект мог визуализировать вашу сцену.

Теперь проблема заключается в том, что SpriteBatch внутренне определяет матрицу проекции, которая преобразует пространство клиента в пространство проекции (так что в основном «предполагает» пространство мира - это Клиентское пространство).Не проблема в вашем примере, потому что два пробела :.

Если Мир пространство не то же самое, как клиент пространство, и вы хотите использовать SpriteBatch, необходимо создать дополнительную матрицу преобразования из мирового пространства клиента пространства и вставить его между видом и матрицами проекта (т.е.: умножьте его на View и передайте его в SpriteBatch.Begin).

Если ваше пространство в мире определяет, какой путь «вверх» (или «справа», если на то пошло) по-разному, чтобы SpriteBatch, то вы должны иметь в виду, что исходный объект, используемый SpriteBatch.Draw, определяет «вверх» как Y- ,

+0

Ответы от Тейса и Стива очень помогли мне, теперь думаю, что это нормально. – Ilya

0

В TestMatrix():

shipPosition = Vector2.Transform(shipPosition, rotZ);

должен быть

shipPosition = Vector2.Transform(shipPosition, comboMatrix);

0

Концепция: -трансляция, ротация, + перевод. является механизмом, чтобы заставить что-то вращаться или вращаться на месте. Но вы применяете его к точке (вектор2). Существует мало пользы, когда точка вращения на месте. Я считаю, что вы действительно хотите, чтобы корабль спрайта вращался на месте. Обычно это делается путем изменения переменной shipRotation, которая представляет собой поплавок, описывающий величину угловой разности (от 0), которую вы хотите, чтобы спрайт был повернут.

По какой-то причине вы спутать вращение точки (shipPosition) для вращения объекта корабля ...

В 2d, хотя по математике в матрицах работает так же, как и в 3D, SpriteBatch Метод .Draw() использует один float для описания вращения и который напрямую не связан с данными вращения, генерируемыми матрицей.

Самое смешное, что полезно использовать Матрицы в 2d для преобразования точек, и многие люди этого не понимают. Здесь вы пытаетесь понять это, но действительно хотите, чтобы он воздействовал на объект, отличный от точки. Итак, не сдавайся Матрицам. Но просто измените значение вращения внутри вызова Draw, чтобы повернуть спрайт на место.

Теперь, если я пропустил интерпретацию вашей цели, просьба сообщить мне, и я постараюсь помочь дальше.

+0

Хорошо, но более или менее, я хотел знать связь между пространством экрана, которое я вижу, и мировым пространством, когда я делаю эти матричные умножения. – Ilya

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; 

Обратите внимание, все значения являются только значениями размера рабочего стола. Не было никакого отношения к миру/экрану. Так вот как поворачивать точку вокруг другой точки в экранах 2d с помощью матриц. Для 3d это будет точный код, но с компонентом Z (vector3) и использовать 3D-пространство мира.

Ваша comboMatrix (из более раннего кода), а в вашем новом фрагменте кода преобразование() может отключить вас. При умножении матриц вместе, например, добавление одного вращения в другое. Итак, ваша comboMatrix была похожа на 3 + 5 + (- 3) ... все, что вы действительно делали, было эквивалентно 5. Вся ваша comboMatrix была равнозначной rotZ ... она не делала никаких переводов. и ваш Transform() аналогичен. Когда я применил три матрицы к отправке, описанным выше, я убедился, что каждая матрица была применена к отправке на перенос для перехода к следующей операции. Иногда вы можете конкатенировать матрицы перед подачей заявки, но в вашем случае нет.

Помогло ли это, или я все еще пропустил ваш вопрос?

+0

2.) о, и о перевернутых матрицах. Если вы считаете, что матрица представляет собой ориентацию, допустим, что матрица А представляет 90 градусов. Левая ориентация и матрица составляют 130 градусов. Левая ориентация. Разница между A & B составляет 45 градусов. Левая ориентация. Теперь Matrix.Invert() меняет матрицу так, чтобы инверсия MatrixA составляла 90 градусов ** Правое ** вращение. Итак, MatrixB (на 135 слева) * InvA (справа - 90) приводит к 45 левым. Таким образом, умножая один раз на другой, результат отличается от разницы. Разницу можно отнести к первому. –

+0

Это в основном помогло мне. Так что, если бы я хотел повернуть это 2d ракетное судно не вокруг произвольной точки в пространстве sceeen, но скажем, вокруг оси z в мировом пространстве и/или в локальном пространстве спрайта? Все еще просто сделать, используя матрицы CreateRotation? – Ilya

+0

Еще одна вещь. Вы сказали, что «comboMatrix была похожа на 3 + 5 + (- 3) ... все, что вы действительно сделали, было эквивалентно 5. Вся ваша comboMatrix была равнозначной rotZ ... она не выполняла никаких переводов, а ваш Transform() подобный ... » Кроме того, вы имели в виду, когда я делал поворот-z раньше, потому что я кормил его координатами экрана, я по существу вращался вокруг начала координат экрана? Хорошо, но в моей программе ни одно вращение не произошло (это означает, что положение изображения никогда не менялось) или вращение нечетных градусов вокруг. Я предполагаю, что происхождение координат экрана произошло. – Ilya

Смежные вопросы