我有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个图像看起来可以按比例缩放和旋转。以前有人做过这样的事吗?如果有什么不清楚,很乐意根据反馈修改问题。如果图像有帮助我可以将它们发布到某个地方......
我看过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();
答案 0 :(得分:2)
如果您打算使用SpriteBatch.Draw来绘制纹理,我建议您放弃尝试操作原点,缩放参数以尝试实现此目的,我只是怀疑它是否可以这样做。但你确实有另一种选择,你可以操纵SpriteBatch Matrix。
这是一个快速而肮脏的例子,请注意我在这里使用的纹理是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);
}
}
}
}