Texture won't rotate using RenderCopyEx


#1

Hi,

I’m very new to SDL2 (Uni student), so I apologise if this is something super-simple, which I suspect it will be, but here goes.

I am trying to render this texture at an angle relative to the Mouse cursor location. The texture renders in the correct place using the correct Rect, but it does not rotate :confused:. Here is my code so far:

Function for constantly updating:

void update()
{
	// SDL event is temporarily stored in this variable when processing each of the
	// SDL events on the SDL window events queue
	SDL_Event event;

	// Handle SDL events on queue, keep handling until no more events to process
	while (SDL_PollEvent(&event) != 0)
	{
		if (event.type == SDL_MOUSEMOTION)
		{
			switch (event.motion.x, event.motion.y)
			{
			case SDL_MOUSEMOTION:
				SDL_GetMouseState(&MouseX, &MouseY);
			break;
			}
		} 
        }
}

Function for constantly drawing:
void draw()
{
	// Clear the double buffered main window surface using the main window renderer
        SDL_RenderClear(pRenderer);

	// Render Challenger 1 Body & Turret
	Render_Challenger1();
	
        // Update GPU with Renderer
	SDL_RenderPresent(pRenderer);
}

Function to set rect and rendering for this asset (Challenger 1)
void Render_Challenger1() 
{
	SDL_Rect Challenger1TurretRect = { Challenger1.Turret_XPos, Challenger1.Turret_YPos, Challenger1.Turret_Width, Challenger1.Turret_Height }; // Store the location and size of the Texture in SDL_Rect type structure and set a point to rotate 

	SDL_RenderCopyEx(pRenderer, Challenger1.Turret_Texture, NULL, &Challenger1TurretRect, TrueAngle, NULL, SDL_FLIP_NONE);
}

There is more code than this but I cut it out so you can see what I’m trying to do.

This is the result :frowning: : The mouse was in the center of the window (Windows doesn’t capture mouse in PrintScreens :expressionless:)

Thanks for reading :slight_smile:


#2

To figure out the angle your turret should be rendered in, you can do this:

  1. Draw a 2D vector between the turret and the mouse cursor to get the direction from the turret to the mouse cursor.
  2. Calculate the angle, based on the direction vector
  3. Render the turret with the calculated angle

Code example below:

// Will be used later to convert from radians to degrees
#define	PI		(3.14f)
#define RAD_TO_DEG	((X) * (180.0f / PI))

//////////////////////////////////////////////////////////////////////////

// Member variables

CVector2D m_PlayerPosition	= CVector2D(200.0f, 200.0f);
CVector2D m_PlayerDirection	= CVector2D::Zero;

// This is the size of my turret, change this according to your turret's size
CVector2D m_PlayerSize		= CVector2D(128.0f, 64.0f);

float m_PlayerVelocity	= 100.0f;
float m_PlayerAngle	= 0.0f;

//////////////////////////////////////////////////////////////////////////

void Update(const float DeltaTime)
{
	// Draw a 2D vector between the turret's center position and the mouse position
	m_PlayerDirection = m_pInputManager->GetMousePosition() - (m_PlayerPosition + (PlayerSize * 0.5f));

	// Get the angle of the 2D vector
	m_PlayerAngle = atan2(m_PlayerDirection.y, m_PlayerDirection.x);

	//////////////////////////////////////////////////////////////////////////

	// For fun, lets also move the turret forward, in the direction of the mouse pointer, whenever the W key is held
	// And move the turret backward, in the direction of the mouse pointer, whenever the S key is held

	if(m_pInputManager->KeyHeld(SDL_SCANCODE_W) && !pInputManager->KeyHeld(SDL_SCANCODE_S))
		m_PlayerPosition += (CVector2D(cos(m_PlayerAngle), sin(m_PlayerAngle)) * m_PlayerVelocity) * DeltaTime;

	else if(pInputManager->KeyHeld(SDL_SCANCODE_S) && !pInputManager->KeyHeld(SDL_SCANCODE_W))
		m_PlayerPosition -= (CVector2D(cos(m_PlayerAngle), sin(m_PlayerAngle)) * m_PlayerVelocity) * DeltaTime;
}

void Render(void) 
{
	// Render the turret
	// The angle is in radians so the angle is converted into degrees
	m_pMyTexture->Render(m_PlayerPosition, RAD_TO_DEG(m_PlayerAngle));
}

This will require knowledge about vectors and some linear algebra.
Let me know if you need example code for a 2D vector class.

Hope it helps :slight_smile:


#3

switch (event.motion.x, event.motion.y)

It’s just a quirk of C and C++ that this compiled; a list of comma separated values like this will take the last item, so in this case, it’s equivalent to writing…

switch (event.motion.y)

…but that wasn’t what you wanted here either. And the SDL_GetMouseState call will give you the same values that are already in event.motion.x and event.motion.y, but you never hit this because it’s unlikely you ever landed in that case statement, since SDL_MOUSEMOTION happens to be 1024, and you probably didn’t ever get that Y coordinate.

C and C++ can be cruel, since they let these things go without even a warning sometimes.

Let’s try this:

// Handle SDL events on queue, keep handling until no more events to process
while (SDL_PollEvent(&event) != 0)
{
    if (event.type == SDL_MOUSEMOTION)
    {
        Challenger1.Turret_XPos = event.motion.x;
        Challenger1.Turret_YPos = event.motion.y;
    }
}

Now the tank will be exactly where the mouse cursor is when you draw, which might not be what you wanted, but it will tell you if you’re getting correct values. From there, @Naith gave a good overview of calculating an angle, so you can swap out the tank position for a calculation that gives you what you want for TrueAngle.

Good luck!


#4

Hi,

I have a few questions as I am not familiar with some terms used in your reply.

When I enter CVector2D m_PlayerPosition, I get CVector2D is undefined. I’m new to SDL, C/C++ and only familiar with C# and I’ve never learned Vectors. Do I need a declaration to get this working?

Is DeltaTime pre-defined? I don’t see it declared. It’s new to me also :smiley:

I’ve not been introduce to -> or f labeled in some of your declarations.
After reading up, I found -> means into or to. e.g string->list would mean “string to/into list” and f means “format/formatted”. I’m a but unclear on the latter. Please correct me if I’m wrong.

Also, what is the use of (void) as a parameter in functions? I was taught to leave it blank as in void Render() if no parameters are passed.

Sorry for the question, but I’m not familiar with those yet. Thanks a lot :blush:


#5

CVector2D is external class which I’ve written to help out with positioning (among other things) the object in the game. So in my code I have several CVector2D objects holding the player’s (the turret’s) position, size and such.

The DeltaTime varible is a float (decimal) variable that is passed to the update function, which holds the time between the current frame and the previous frame. By multiplying the position update with this variable, I get the same speed on different computer, since I don’t want the game to run faster/slower on a faster/slower computer.

The -> keyword/prefix means it’s a pointer to something. In my case, I have a pointer to an InputManager class object, and whenever I want to call a function (or something else) inside that class object, I need to write InputManager->, followed by (for example) a function name, in my case ‘KeyHeld’.

The ‘f’ prefix in some of my variables means it’s a float (decimal) value. For example, when I write ‘CVector2D(200.0f, 200.0f)’ I set a vector’s x-position and y-position to 200.0f, which is a decimal value of 200.0, and I insert an ‘f’ afterwards to let the compiler know it’s a float value and not a double value (which it is without the ‘f’ prefix).

Whether to use ‘void’ as a function parameter or not is up to the programmer, since it’s only a matter of personal taste. I always insert ‘void’ in a function without any parameters to note that it’s a function without parameters.
This means that ‘MyFunction(void)’ and ‘MyFunction()’ does the same thing.

And finally, here are some links to information you might need to read up on:

Classes:
https://www.tutorialspoint.com/cplusplus/cpp_classes_objects.htm

Linear algebra + vectors:
http://blog.wolfire.com/2009/07/linear-algebra-for-game-developers-part-1/

Pointers:
https://www.tutorialspoint.com/cplusplus/cpp_pointers.htm

Deltatime (sorry for the awful choice of background color vs text color):
http://www.aaroncox.net/tutorials/2dtutorials/sdltimer.html

I will post a simple 2D vector class below, which you can just add to your project and use.
Don’t forget to add #include “CVector2D” at the top of your project when you’ve added the new class and wish to use it.


#6

Simple 2D vector class:

CVector2D.h

#ifndef CVECTOR2D_H
#define CVECTOR2D_H

class CVector2D
{
public:

CVector2D (void);
CVector2D (const float X, const float Y);
~CVector2D (void);

//////////////////////////////////////////////////////////////////////////

CVector2D operator + (const CVector2D& rIn) const {return CVector2D(x + rIn.x, y + rIn.y);}
CVector2D operator - (const CVector2D& rIn) const {return CVector2D(x - rIn.x, y - rIn.y);}
CVector2D operator * (const float Scalar) const {return CVector2D(x * Scalar, y * Scalar);}
CVector2D operator * (const CVector2D& rIn) const {return CVector2D(x * rIn.x, y * rIn.y);}
CVector2D& operator += (const CVector2D& rIn) {x += rIn.x; y += rIn.y; return *this;}
CVector2D& operator -= (const CVector2D& rIn) {x -= rIn.x; y -= rIn.y; return *this;}

/////////////////////////////////////////////////////////////////////////

float Length (void) const;

float Normalize (void);

//////////////////////////////////////////////////////////////////////////

float x;
float y;

//////////////////////////////////////////////////////////////////////////

};

#endif // CVECTOR2D_H

And

CVector2D.cpp

#include “CVector2D.h”

#include <math.h>

CVector2D::CVector2D(void)
: x(0.0f)
, y(0.0f)
{

}

CVector2D::CVector2D(const float X, const float Y)
: x(X)
, y(Y)
{

}

CVector2D::~CVector2D(void)
{

}

float CVector2D::Length(void) const
{
return sqrtf((x * x) + (y * y));
}

float CVector2D::Normalize(void)
{
const float VectorLength = this->Length();

if(VectorLength != 0.0f)
{
this->x = this->x / VectorLength;
this->y = this->y / VectorLength;
}

return VectorLength;
}

Just create a new class inside your project and copy paste the code above into your header- and source file.
The indentation screwed up a bit, sorry about that.


#7

Thanks, I will try and implement this :grinning:

Edit:
CVector2D m_PlayerDirection = CVector2D::Zero; gave me an error, so I made it:
CVector2D m_PlayerDirection = CVector2D();
Not sure if that is correct :slight_smile:


#8

After putting in the code I get these errors:
identifier "m_pInputManager" is undefined
identifier "pInputManager" is undefined
identifier "m_pMyTexture" is undefined
identifier "X" is undefined

When calling this line:

m_pMyTexture->Render(m_PlayerPosition, RAD_TO_DEG(m_PlayerAngle));

I get Identifier "X" is undefined

I fixed the “PlayerSize” error.


#9

You need to understand that my code above (my first reply, showing how to calculate the angle and such) is just example code on how you can do it. Psuedo code if you will.
The ‘m_pInputManager->GetMousePosition()’ is just my representation of getting the mouse position and ‘MyTexture->Render()’ is just my representation of rendering an SDL_Texture.
You need to use the code you’ve shown in your first post and work from it.
Since you now have the 2D vector class (which I assume you’ve implemented correctly in your project), you can rotate your texture properly if you first fix the issues that @icculus have stated above and then use the calculation which I’ve given you in my first post.
Once again, you can not just copy-paste my code above and assume it to work.
You need to have your own way of getting the mouse position, calculate the direction and the angle of your turret and then render your texture (which you already do in your Render_Challenger1() function). :slight_smile: