Выстраивание спрайтов со шкалой и вращением вокруг произвольной точки - PullRequest
0 голосов
/ 27 мая 2011

У меня есть 2 спрайта, которые при объединении составляют правильное изображение на экране. Рисовать их одновременно нельзя.

Представьте себе этот класс:

class MyImage
{
    Vector2 drawOffset;  // this gets added before the image is drawn
    Vector2 sourceRect;  // this is where it is on the source texturepage

    void Draw(Vector2 position)
    {
        position = position + drawOffset;
        spriteBatch.Draw(sourceTexture, position, sourceRect, Color.White);
    }
}

И этот код зовет в него:

MyImage a = new MyImage();  // assume they get initialised correctly
MyImage b = new MyImage();  // with different drawOffsets and sourceRects

a.Draw(position);  // this composes the final
b.Draw(position);  // screen image from the 2 source images

Теперь я хотел бы добавить масштаб и вращение к функции Draw (), но у меня возникли реальные проблемы с получением параметров для функции SpriteBatch.Draw. Это будет версия, которая принимает масштаб, поворот и происхождение. Мне нужно, чтобы окончательно составленное изображение корректно масштабировалось и вращалось (вокруг некоторого произвольного центра), но я не могу понять, как манипулировать параметрами масштабирования, поворота и исходного положения, чтобы 2 изображения отображались в масштабе и вращались согласованно , Кто-нибудь делал что-то подобное раньше? С удовольствием отредактирую вопрос, основываясь на отзывах, если что-то неясно. Если изображения помогут, я могу выложить их куда-нибудь ...

Я смотрел на вращение вокруг точки xna 2D , но все еще в тупике.

Ура, Чарли.


Большое спасибо за ответ ниже - с его помощью мне удалось правильно отобразить изображения. Остается еще одна проблема, которая заключается в том, что мне нужно использовать много пар spritebatch.Begin / End (по одной на рендеринг изображения). У меня пока нет способа измерить производительность на этом устройстве, и частота кадров не увеличивается, так что, думаю, это не проблема.

Вот мой код:

// gr is the graphic object:
// gr.position is the location of the image in the atlas
// gr.DrawOffset is the draw offset so the image is placed correctly in it's virtual box
// gr.pageIndex is the index into the texture/atlas array
// hw,hh are half the width/height of the image (it always rotates around it's centre in fact)

Matrix m = Matrix.CreateTranslation(-hw, -hh, 0) *
    Matrix.CreateRotationZ(rotation) *                   // rotation : parameter
    Matrix.CreateScale(scale) *                          // scale : parameter
    Matrix.CreateTranslation(pos.X + hw, pos.Y + hh, 0); // pos : parameter!

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, m);
spriteBatch.Draw(page[gr.pageIndex].texture, gr.DrawOffset, gr.position, color);
spriteBatch.End();

1 Ответ

2 голосов
/ 27 мая 2011

Если вы собираетесь работать с SpriteBatch.Draw для рисования текстур, я бы посоветовал вам отказаться от попыток манипулировать источником, масштабировать аргументы, чтобы попытаться достичь этого, просто я сомневаюсь, что это можно сделать таким образом.Но у вас есть альтернатива, вы можете манипулировать матрицей SpriteBatch.

Вот быстрый и грязный пример, обратите внимание, что текстура, которую я здесь использовал, имеет размер 128x96, поэтому я жестко закодировал значения для этого размера изображения.Не ищите никаких лучших практик в этом коде, я написал его, чтобы попытаться показать концепцию как можно более чисто.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace WindowsGame1
{
  /// <summary>
  /// This is the main type for your game
  /// </summary>
  public class Game1 : Microsoft.Xna.Framework.Game
  {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    private Texture2D _texture;

    private MyImage _image1;
    private MyImage _image2;

    // Attributes of the composed sprite
    private float _angle = 0.0f;
    private Vector2 _position = new Vector2(100, 100);
    private Vector2 _rotationPoint = new Vector2(96, 48);

    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

      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()
    {
      // Create a new SpriteBatch, which can be used to draw textures.
      spriteBatch = new SpriteBatch(GraphicsDevice);

      _texture = Content.Load<Texture2D>("Gravitar");

      // Create the two MyImage instances
      _image1 = new MyImage(_texture, Vector2.Zero, Vector2.Zero);
      _image2 = new MyImage(_texture, new Vector2(64, 0), new Vector2(64, 0));
    }

    /// <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>
    /// 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)
    {
      // Allows the game to exit
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();

      float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

      _angle += 0.5f * elapsedTime;

      if (Mouse.GetState().LeftButton == ButtonState.Pressed)
      {
        _angle = 0.0f;
      }

      if (Keyboard.GetState().IsKeyDown(Keys.Left)) 
        _position += new Vector2(-10, 0)*elapsedTime;

      if (Keyboard.GetState().IsKeyDown(Keys.Right)) 
        _position += new Vector2(10, 0) * elapsedTime;

      base.Update(gameTime);
    }

    /// <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);

      // Setup the sprite batch matrix      
      // Notice that we first translate to the point or rotation
      // then rotate and when we translate to the desired position we
      // need to compensate for the first translation so that the texture
      // appears at the correct location
      Matrix m = 
        Matrix.CreateScale(1.5f) 
        * Matrix.CreateTranslation(-_rotationPoint.X, -_rotationPoint.Y, 0) 
        * Matrix.CreateRotationZ(_angle) 
        * Matrix.CreateTranslation(_position.X + _rotationPoint.X, _position.Y + _rotationPoint.Y, 0);

      // Begin the SpriteBatch passing the matrix
      spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, m);
      _image1.Draw(spriteBatch);
      _image2.Draw(spriteBatch);            
      spriteBatch.End();

      base.Draw(gameTime);
    }

    class MyImage
    {
      Vector2 _drawOffset;
      Vector2 _sourcePoint;
      Texture2D _sourceTexture;      

      public MyImage(Texture2D sourceTexture, Vector2 sourcePoint, Vector2 drawOffset)
      {
        _drawOffset = drawOffset;
        _sourcePoint = sourcePoint;
        _sourceTexture = sourceTexture;
      }

      public void Draw(SpriteBatch spriteBatch)
      {      
        spriteBatch.Draw(_sourceTexture, _drawOffset, 
          new Rectangle((int)_sourcePoint.X, (int)_sourcePoint.Y, 64, 96), 
          Color.White);
      }
    }
  }
}
...