Draw size and rotation

Hello
everyone,

I have
worked with Monogame a couple of times. Each time I try to make a game with
more gameplay and more programming challenges.

I have rotated
an image, a process that was harder than I expected, but how do I control the
image size? I wasted hours on my collusion system, because I didn’t realize the
image size was not controlled by me.

So, my
question is: how can I flip an image and chose the image size?

I currently
have this:

SpriteBatch.Draw(Enemy1, new Vector2(enemy.Position.X, enemy.Position.Y), null, Color.White, (float)Math.PI, new Vector2((float)enemy.Position.Width, (float)enemy.Position.Height), 1, SpriteEffects.FlipVertically, 0);

Thanks a head of time.

Gebruikersnaam

Draw ( 
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effect,
float depth 
)

This is one of the full parameter SpriteBatch Draw() methods. The rotation parameter uses mathematics radians (https://en.wikipedia.org/wiki/Radian). Math.PI doesn’t make much sense to be putting in there unless you are doing other stuff to it.

There is a class in Monogame called MathHelper which contains lots of useful functions.

For example,

float RadiansOf180Degrees = MathHelper.ToRadians(180); // this will turn degrees 90,180,270,360,etc... into radians.

You can rotate your image this way. Just remember it rotates around the “origin” parameter.

Now for the size. One way you can do this is via the “scale” parameter in the draw. The scale parameter can be a float or Vector2. I like Vector2 because you can size on the X or Y.

For example

Vector2 NormalScale = Vector2(1,1); // your image will be original size
Vector2 DoubleScale = Vector2(2,2); // your image will be double in size
Vector2 TallStretchedScale = Vector2(1,2); // your image will be stretched in height and stay the same width.
1 Like

Thank you.
Clear explanation! But do I understand correctly, that it is not possible to do
the following: height = 500px? Or what I
prefer to do is: screenSizeHeight * 0.02 or something like that.

Yes, you can’t do that with the native SpriteBatch Draw. With the standard SpriteBatch Draw, you can’t make an image a new fixed height by a parameter. However, you can probably come up with a math equation to figure out how to determine float to px with scale parameter, I’ve never tried.

There is another parameter you’ve left out of your original Draw() which might help you. The section you left null is actually very powerful, the sourceRectangle. With that parameter, you can assign a Rectangle and it can make the image you are drawing repeat. So you could have a 32x32 size image and have it drawn repeatably across the whole screen.

For example,

new Rectangle(0,0, screenSizeWidth, screenSizeHeight)

That would make for example a 32x32 image repeat over and over for the whole screen. Very similar to how web development CSS background-repeat works

Might be helpful to read through some of these tuts: http://rbwhitaker.wikidot.com/monogame-2d-tutorials

David

Thank you. I will simply flip the image and load it twice. Although, I is really dirty way
of programming.

You can do this with the full spriteBatch overload.

I wrote two additional DrawRectangle methods wrapping up the full version to show you as a example. I included a properly center rotating version.

Sprite batch has rectangle overloads that specify…
The destination area in the drawing window to plot pixels to.
The source area to take texels out of the image from.
The origin parameter relates to the source area texels.

The offset is the way xna did it but it isn’t done right so this example below corrects for that.
The monogame guys didn’t correct it as that is how xna did it and they were trying to keep it the same.

This example shows 2 rectangles under rotation one is centered to rotate on its top left point.
The other at its properly positioned center.
You don’t need to load a image for this example.
You can copy paste it over a newly created projects game1 text and run it.

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

namespace Game1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D createdCheckerBoardTexture;
        float imageRotationInUnits = 0;
        float imageRotationInRadians = 0f;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            createdCheckerBoardTexture = CreateCheckerBoard(GraphicsDevice, 8, 8, Color.Moccasin, Color.Green);
        }

        /// <summary>
        /// Created a texture programmatically so you can just copy paste and run it.
        /// </summary>
        public static Texture2D CreateCheckerBoard(GraphicsDevice device, int w, int h, Color c0, Color c1)
        {
            Color[] data = new Color[w * h];
            for (int x = 0; x < w; x++)
            {
                for (int y = 0; y < h; y++)
                {
                    int index = y * w + x;
                    Color c = c0;
                    if ((y % 2 == 0))
                        if ((x % 2 == 0)) c = c0;
                        else c = c1;
                    else
                        if ((x % 2 == 0)) c = c1;
                    else c = c0;
                    data[index] = c;
                }
            }
            Texture2D tex = new Texture2D(device, w, h);
            tex.SetData<Color>(data);
            return tex;
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            float rotationSpeed = .25f;

            imageRotationInUnits += (float)gameTime.ElapsedGameTime.TotalSeconds * rotationSpeed;
            if (imageRotationInUnits > 1.0f)
                imageRotationInUnits -= 1.0f;

            imageRotationInRadians = imageRotationInUnits * MathHelper.Pi * 2f;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            SamplerState ss = new SamplerState() { Filter = TextureFilter.Point };

            spriteBatch.Begin(SpriteSortMode.Immediate, null, ss, null,null,null,null);

            // Define both the position and the size in a rectangle.
            Rectangle imagesDrawingRectangle = new Rectangle(200, 200, 200, 150);

            // Draw the rectangle using a wrapped up draw method.
            DrawRectangle(createdCheckerBoardTexture, imagesDrawingRectangle, Color.White, imageRotationInRadians, false, false);

            // Draw a properly centered rotated rectangle using a wrapped up draw method.
            DrawRectangleCenteredRotation(createdCheckerBoardTexture, imagesDrawingRectangle, Color.Blue, imageRotationInRadians, false, false);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// A wrapped up draw method.
        /// </summary>
        public void DrawRectangle(Texture2D textureImage, Rectangle rectangleAreaToDrawAt, Color color, float rotationInRadians, bool flipVertically, bool flipHorizontally)
        {
            SpriteEffects seffects = SpriteEffects.None;
            if (flipHorizontally)
                seffects = seffects | SpriteEffects.FlipHorizontally;
            if (flipVertically)
                seffects = seffects | SpriteEffects.FlipVertically;

            // This is a full spriteBatch.Draw method it has lots of parameters to fully control the draw.
            spriteBatch.Draw(textureImage, rectangleAreaToDrawAt, new Rectangle(0, 0, textureImage.Width, textureImage.Height), color, rotationInRadians, Vector2.Zero, seffects, 0);
        }

        /// <summary>
        /// A additional method which shows how to offset the drawing.
        /// </summary>
        public void DrawRectangleCenteredRotation(Texture2D textureImage, Rectangle rectangleAreaToDrawAt, Color color, float rotationInRadians, bool flipVertically, bool flipHorizontally)
        {
            SpriteEffects seffects = SpriteEffects.None;
            if (flipHorizontally)
                seffects = seffects | SpriteEffects.FlipHorizontally;
            if (flipVertically)
                seffects = seffects | SpriteEffects.FlipVertically;
            
            // We must make a couple adjustments in order to properly center this.
            Rectangle r = rectangleAreaToDrawAt;
            Rectangle destination = new Rectangle(r.X + r.Width /2, r.Y + r.Height /2 , r.Width, r.Height);
            Vector2 originOffset = new Vector2(textureImage.Width / 2, textureImage.Height / 2);
            
            // This is a full spriteBatch.Draw method it has lots of parameters to fully control the draw.
            spriteBatch.Draw(textureImage, destination, new Rectangle(0, 0, textureImage.Width, textureImage.Height), color, rotationInRadians, originOffset, seffects, 0);
        }

    }
}

If however you don’t want a true origin rotation you can just pass half the image width and height to the offset in a regular spritebatch.Draw however that will make things much more complicated.

2 Likes

Edit: Fixed a typo…
I had typed Vector2.One instead of a Zero for the offset on the first method. Corrected it.