- Đã chỉnh sửa
Rendering using only SpriteBatch in XNA
Hi,
I've been toying around with the XNA runtime and due to the way my rendering code is set up I'm not able to easily integrate the existing SkeletonRenderer. All my drawing is done between a single Begin and End using the camera position / zoom as the transform.
Here's the SpriteBatch extension method I have so far. It's based on the "XNA/MonoGame with content pipeline" third-party runtime. Does anyone notice anything wrong with it?
public static void Draw(this SpriteBatch spriteBatch, Skeleton skeleton, Vector2 position, float rotation, Vector2 scale,
Color tintColor, bool flipHorizontal, bool flipVertical)
{
SpriteEffects spriteEffects = SpriteEffects.None;
if(flipHorizontal) { spriteEffects |= SpriteEffects.FlipHorizontally; }
if(flipVertical) { spriteEffects |= SpriteEffects.FlipVertically; }
// Draw individual components
for (int i = 0, n = skeleton.DrawOrder.Count; i < n; i++)
{
Slot slot = skeleton.DrawOrder[i];
RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
if (regionAttachment != null)
{
AtlasRegion region = regionAttachment.RendererObject as AtlasRegion;
byte r = (byte)(skeleton.R * slot.R * 255);
byte g = (byte)(skeleton.G * slot.G * 255);
byte b = (byte)(skeleton.B * slot.B * 255);
byte a = (byte)(skeleton.A * slot.A * 255);
float offsetX = (regionAttachment.Offset[X1] + regionAttachment.Offset[X3]) / 2;
float offsetY = (regionAttachment.Offset[Y1] + regionAttachment.Offset[Y3]) / 2;
spriteBatch.Draw(region.page.rendererObject as Texture2D,
new Vector2(
(offsetX * slot.Bone.M00) + (offsetY * slot.Bone.M01) + (slot.Bone.WorldX + skeleton.X),
(offsetX * slot.Bone.M10) + (offsetY * slot.Bone.M11) + (slot.Bone.WorldY + skeleton.Y)
),
new Rectangle(region.x, region.y, region.width, region.height),
new Color(r * tintColor.R, g * tintColor.G, b * tintColor.B, a * tintColor.A),
-(slot.Bone.WorldRotation + regionAttachment.Rotation + rotation) * 3.14159f / 180.0f,
new Vector2(region.width * 0.5f, region.height * 0.5f),
new Vector2(slot.Bone.WorldScaleX * scale.X * regionAttachment.ScaleX,
slot.Bone.WorldScaleY * scale.Y * regionAttachment.ScaleY),
spriteEffects, 0.0f);
}
}
And the result... As you can see in the second image, some of the images (parts of the arms and legs) have the incorrect rotation compared to how they are displayed in Spine (first image).
I'm also unsure if the following is the proper way to flip the animation or not. This is done in an Update method.
m_Skeleton.FlipX = (m_FacingDirection == HorizontalDirection.Left);
m_Skeleton.FlipY = false;
m_AnimationState.Update(gameTime.ElapsedGameTime.Milliseconds * 0.001f * gameSpeed);
m_AnimationState.Apply(m_Skeleton);
m_Skeleton.UpdateWorldTransform();
Thank you.
After playing around with a few different things, I disabled rotation for the texture atlas export and that seems to have corrected most of the problems.
Figured out most of the problem. I'm sure I'll encounter some issues when flipVertical = true, but it works for now. Here's the modified code in case anyone finds it useful.
/// <summary>
/// Draw a Spine skeleton.
/// </summary>
/// <param name="spriteBatch">Sprite batch.</param>
/// <param name="skeleton">Skeleton to draw.</param>
/// <param name="position">Base screen position.</param>
/// <param name="rotation">Base rotation.</param>
/// <param name="scale">Base scale factor.</param>
/// <param name="tintColor">Tinting color.</param>
/// <param name="flipHorizontal">Indicates if sprite components should be flipped horizontally when drawn.</param>
/// <param name="flipVertical">Indicates if sprite components should be flipped vertically when drawn.</param>
public static void Draw(this SpriteBatch spriteBatch, Skeleton skeleton, Vector2 position, float rotation, Vector2 scale,
Color tintColor, bool flipHorizontal, bool flipVertical)
{
SpriteEffects spriteEffects = SpriteEffects.None;
if(flipHorizontal) { spriteEffects |= SpriteEffects.FlipHorizontally; }
if(flipVertical) { spriteEffects |= SpriteEffects.FlipVertically; }
// Draw individual components
for (int i = 0, n = skeleton.DrawOrder.Count; i < n; i++)
{
Slot slot = skeleton.DrawOrder[i];
RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
if (regionAttachment != null)
{
AtlasRegion region = regionAttachment.RendererObject as AtlasRegion;
float r = (skeleton.R * slot.R);
float g = (skeleton.G * slot.G);
float b = (skeleton.B * slot.B);
float a = (skeleton.A * slot.A);
float offsetX = (regionAttachment.Offset[X1] + regionAttachment.Offset[X3]) / 2;
float offsetY = (regionAttachment.Offset[Y1] + regionAttachment.Offset[Y3]) / 2;
spriteBatch.Draw(region.page.rendererObject as Texture2D,
new Vector2((offsetX * slot.Bone.M00) + (offsetY * slot.Bone.M01) + (slot.Bone.WorldX + skeleton.X),
(offsetX * slot.Bone.M10) + (offsetY * slot.Bone.M11) + (slot.Bone.WorldY + skeleton.Y)),
new Rectangle(region.x, region.y, region.width, region.height),
Utility.BlendColors(new Color(r, g, b, a), tintColor),
(flipHorizontal ? 1 : -1) * (slot.Bone.WorldRotation + regionAttachment.Rotation + rotation) * 3.14159f / 180.0f,
new Vector2(region.width * 0.5f, region.height * 0.5f),
new Vector2(slot.Bone.WorldScaleX * scale.X * regionAttachment.ScaleX,
slot.Bone.WorldScaleY * scale.Y * regionAttachment.ScaleY),
spriteEffects, 0.0f);
}
}
}
And the code to fix up the vertical flip issue I previously mentioned:
(flipHorizontal ? 1 : -1) * (flipVertical ? -1 : 1) * (slot.Bone.WorldRotation + regionAttachment.Rotation + rotation) * 3.14159f / 180.0f,
This line replaces the rotation parameter for the Draw call at the bottom of the method.
Sorry I wasn't able to jump in earlier and help, but I'm glad you got it sorted.