Basically I'm trying to implement a game engine using C++ and SDL2, and for the most part things are going smooth.
Collision detection works (using Separation Axis Theorem), movement, etc. but when I'm drawing the vertices of my collision box, and my sprite moves at an angle, the sprite and collision box move away from each other.
I'm not sure what's going on, I have two different angles to represent the angle of the image itself and the movement angle, and I've tried adjusting them with constants, adjusting which one changes and how with the movement, etc, but they still don't move together (when moving at an angle).
It's not a drawing error as the when the collision box hits another sprite's collision box, it detects that collision.
I'll leave code I think is relevant below, but I think it's a lot so I'll also leave a link to the repo if someone would rather look at that. Repo link.
Relevant classes are probably Sprite and Ship, relevant functions: Sprite::VectorProjection, Sprite::UpdateVertices, Sprite::MoveSprite, Ship::PlayerInput
Code:
//Sprite.cpp
void Sprite::VectorProjection(double Speed)
{
Velocity.x = Speed * cos(MoveAngle) + Acceleration.x;
Velocity.y = Speed * sin(MoveAngle) + Acceleration.y;
this->Speed = sqrt((Velocity.x * Velocity.x) + (Velocity.y * Velocity.y));
MoveAngle = atan2(Velocity.y, Velocity.x);
}
void Sprite::UpdateVertices()
{
std::vector<Vec2D> NewVerts;
Vec2D vert;
for (int i = 0; i < vertices.size(); ++i)
{
vert.x = ((vertices[i].x - Center.x) * cos(ImageAngle - LastAngle)) - ((vertices[i].y - Center.y) * sin(ImageAngle - LastAngle)) + Center.x;
vert.y = ((vertices[i].x - Center.x)*sin(ImageAngle - LastAngle)) + ((vertices[i].y - Center.y)*cos(ImageAngle - LastAngle)) + Center.y;
NewVerts.push_back(vert);
}
LastAngle = ImageAngle;
vertices = NewVerts;
}
void Sprite::MoveSprite()
{
std::vector<Vec2D> NewVerts;
Vec2D vert;
texture.x += Velocity.x;
texture.y += Velocity.y;
Position.x += Velocity.x;
Position.y += Velocity.y;
Center.x += Velocity.x;
Center.y += Velocity.y;
for (int i = 0; i < vertices.size(); ++i)
{
vert.x = vertices[i].x + Velocity.x;
vert.y = vertices[i].y + Velocity.y;
NewVerts.push_back(vert);
}
vertices = NewVerts;
}
--------------------------------------------------
//Ship.cpp
void Ship::PlayerInput(SDL_Event Event, SDL_Renderer* renderer)
{
double IA;
double MA;
if(Event.type == SDL_MOUSEBUTTONDOWN)
{
if (Event.button.button == SDL_BUTTON_LEFT)
{
Fire();
}
}
if (Event.type == SDL_KEYDOWN)
{
if (Event.key.keysym.sym == SDLK_w)
{
//MoveAngle = -M_PI/2;
SetSpeed(2.00);
MoveSprite();
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
if (Event.key.keysym.sym == SDLK_a)
{
IA = ConvertToDegrees(ImageAngle);
MA = ConvertToDegrees(MoveAngle);
SetImageAngle(IA - 1);
SetMoveAngle(MA - 1);
UpdateVertices();
}
if (Event.key.keysym.sym == SDLK_d)
{
IA = ConvertToDegrees(ImageAngle);
MA = ConvertToDegrees(MoveAngle);
SetImageAngle(IA + 1);
SetMoveAngle(MA + 1);
UpdateVertices();
}
}
if (Event.type == SDL_KEYUP)
{
SetSpeed(0.00);
}
}
Any help would be appreciated. Image of what's happening: The blue dots are the vertices and the image of the sprite should be inside of it. When moving left/right, up/down, it stays inside, but at an angle this happens.
EDIT: was asked for code where I calculate vertices, and render them, here's that:
//Initial set of vertices:
void Sprite::SetVertices()
{
Vec2D vert1{(double)texture.x, (double)texture.y};
Vec2D vert2{(double)texture.x + (double)texture.w, (double)texture.y};
Vec2D vert3{(double)texture.x, (double)texture.y + (double)texture.h};
Vec2D vert4{(double)texture.x + (double)texture.w, (double)texture.y + (double)texture.h};
//Order of insertion of vertices matters
vertices.push_back(vert1);
vertices.push_back(vert3);
vertices.push_back(vert4);
vertices.push_back(vert2);
}
-------------------------------------------
//Vertices are drawn in the Tick function of the Scene class
void Scene::Tick()
{
bool bWindowVisible = true;
SDL_Event Event;
while (bPlay)
{
CurrentTime = std::chrono::system_clock::now();
DeltaTime = CurrentTime - LastTime;
if (DeltaTime.count() < RefreshSeconds)
{
continue; //only used here to clean up code and help alleviate several layers of nesting
}
LastTime = CurrentTime;
if (!bPaused)
{
SDL_PollEvent(&Event);
EventHandler(Event, OUT bPlay, OUT bPaused);
SDL_RenderClear(Renderer);
for (Sprite* s : Sprites)
{
s->Update(Renderer);
SDL_RenderCopyEx(Renderer, s->Image, NULL, &s->texture, s->ConvertToDegrees(s->ImageAngle), NULL, SDL_FLIP_NONE);
//Draws vertices for debugging
for (Vec2D vert : s->vertices)
{
SDL_SetRenderDrawColor(Renderer, 0, 0, 255, 255);
SDL_RenderDrawPoint(Renderer, vert.x, vert.y);
}
}
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
SDL_RenderPresent(Renderer);
}
else
{
PausedEventHandler(OUT bPlay, OUT bPaused); // this function was also created to help alleviate
}
}
return;
}
Since an image in SDL is defined by it's point in the upperleft corner, it does not rotate with the image (so if you rotate the image, the point remains the same). However when moving at an angle, all the other points in the image will move relative to that angle, but not that point, which would cause a separation of the collision box and the sprite itself. The issue was in this code block
void Sprite::MoveSprite()
{
std::vector<Vec2D> NewVerts;
Vec2D vert;
texture.x += Velocity.x;
texture.y += Velocity.y;
Position.x += Velocity.x;
Position.y += Velocity.y;
Center.x += Velocity.x;
Center.y += Velocity.y;
for (int i = 0; i < vertices.size(); ++i)
{
vert.x = vertices[i].x + Velocity.x;
vert.y = vertices[i].y + Velocity.y;
NewVerts.push_back(vert);
}
vertices = NewVerts;
}
Where I assumed the texture coordinates should move with the velocity. To fix this, I created a variable called D and defined it as the distance between the texture coordinate and the center
D.x = Center.x - texture.x;
D.y = Center.y - texture.y;
This will work since the texture coordinate is always in the upperleft corner then we change the movesprite method to say this:
void Sprite::MoveSprite()
{
std::vector<Vec2D> NewVerts;
Vec2D vert;
Position.x += Velocity.x;
Position.y += Velocity.y;
Center.x += Velocity.x;
Center.y += Velocity.y;
texture.x = Center.x - D.x;
texture.y = Center.y - D.y;
for (int i = 0; i < vertices.size(); ++i)
{
vert.x = vertices[i].x + Velocity.x;
vert.y = vertices[i].y + Velocity.y;
NewVerts.push_back(vert);
}
vertices = NewVerts;
}
and problem is resolved!