2011-05-27 297 views
0

我有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函數的參數。這將是需要縮放,旋轉和原點的版本。我需要最終合成的圖像來正確縮放和旋轉(圍繞某個任意中心),但不能在我的生活中找到如何操作縮放比例,旋轉和原點參數以使兩幅圖像呈現比例縮放和旋轉一致。有沒有人做過這樣的事情?如果有什麼不清楚的話,很樂意根據反饋對問題進行修改。如果圖像可以幫助我可以讓他們張貼在某處...

我看過rotation around point 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(); 

回答

2

如果你想與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); 
     } 
    } 
    } 
} 
+0

這是否比通過渲染目標渲染目標然後再旋轉紋理更好或更容易? – smelch 2011-05-27 18:40:18

+0

@Smelch,這是我第一次展示,但是後來我想到OP的聲明「同時繪製它們不是一個選項」。這促使我提出這個建議。我知道在我的例子中,我把它們放在一起:),但將上述兩個調用分解爲單獨的精靈批次並將它們分開繪製,只要兩個精靈批次都獲得相同的矩陣就可以了。 – 2011-05-27 18:48:47

+0

很好的答案。雖然在XNA 4.0中默認是'SpriteSortMode.Deferred' - 應該可以使用它。 – 2011-05-28 03:34:09