C++ SDL Angular motion point to point by a click - c++

Im trying to move a object from A point to B point by a click. Each frame the object will move a litlle to the B point. My code works but its doesnt do exactly what I want.
This is what I mean:
I have two code, this is the first one...
class tank {
public:
void setMoveTank(tank &_tank, int x, int y) {
_tank.tanke.moving = true;
_tank.tanke.x2 = x;
_tank.tanke.y2 = y;
int vx = _tank.tanke.x2 - _tank.tanke.x;
int vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
}
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}
};
...and do something like this
I calculate angle once and I move object every frame, but it never arrive to destination
This is my second code...
class tank {
public:
void setMoveTank(tank &_tank, int x, int y) {
_tank.tanke.moving = true;
_tank.tanke.x2 = x;
_tank.tanke.y2 = y;
}
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
int vx = _tank.tanke.x2 - _tank.tanke.x;
int vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}
};
... and do something like this
this code do what I want but I must recalculate de angle each frame
The problem is that, in the second code, the object doesnt draw a rect line and in the first code, the object never arrive to the B point.
Im sure that the problem is in the ROUND, but how can I fix it? What would the best way to make this?
int main() {
if(event.type == SDL_MOUSEBUTTONDOWN) {
switch(event.button.button) {
case SDL_BUTTON_LEFT: _tank.setMoveTank(_tank, event.button.x, event.button.y); break;
}
}
}
SDL_RenderClear(_render.ren);
_tank.renderTank(_render, _tank);
SDL_RenderPresent(_render.ren);
}
}
hiii
EDIT: this is the fixed code
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
double vx = _tank.tanke.x2 - _tank.tanke.x;
double vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}

The problem is that you are storing the velocity as an integer. This causes a lot of precision to be lost, and this is why it goes at a slightly different angle in the first example. In the second example you try to adjust the velocity every frame, which causes it to flip between 2 directions which are both off because of a lack of precision.
Store both the position and velocity as floating point values, and only cast them to integers when you need to draw the tank on the screen.

Related

Smooth movement of player SFML

void update(RenderWindow& window)
{
if (Keyboard::isKeyPressed(Keyboard::W))
{
dy = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
dx = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
dy = 0.3;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
dx = 0.3;
}
x += dx;
y += dy;
dx = dy = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}
When the movement is described in such a code, the player moves angularly: either to the left, or up, or to the right, or down, or diagonally at an angle of 45 degrees by pressing two buttons together, for example S and D. Can this angle be made smoother so that the movement itself was carried out not only to the left, right, diagonally, etc.? My knowledge of geometry is not enough here, so I ask for your help).
In your code, the user input directly modifies the player position. This is probably the reason why the player movement looks so abrupt to you. Technically, in your code, the user input is what is determining the player velocity at any given moment.
A more realistic approach would be to let the player have a velocity property instead –
that represents the player position's rate of change – and then have the player position updated only through this velocity, not directly from the user input. Instead, the velocity would be what is directly modified by the input, but not entirely determined by the current input as it will also depend on its previous value.
Following this approach, the user input is used to calculate the player acceleration in each call to update(). This acceleration – the rate of change of the velocity – is used to update the player velocity directly. Finally, the player velocity is, in turn, used to update the player position.
The following code implements this approach by introducing the velocity_ data member and the acceleration local variable:
void update(RenderWindow& window) {
sf::Vector2f acceleration;
// adjust this at will
const float dAcc = 0.3f;
// set acceleration
if (Keyboard::isKeyPressed(Keyboard::W))
acceleration.y -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::A))
acceleration.x -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::S))
acceleration.y += dAcc;
if (Keyboard::isKeyPressed(Keyboard::D))
acceleration.x += dAcc;
// update velocity through acceleration
velocity_ += acceleration;
// update position through velocity
x += velocity_.x;
y += velocity_.y;
// apply damping to the velocity
velocity_ = 0.99f * velocity_;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
};
This way, the player possesses some kind of inertia, and its movement looks smoother.
Note that you may want to have some damping for the velocity as in:
velocity_ = 0.99f * velocity_;
This will resemble the effect of drag forces.
with help of that man I did it like this, but some better)
void update(RenderWindow& window)
{
float decceleration = 0.3;
if (Keyboard::isKeyPressed(Keyboard::W))
{
accelerationY -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
accelerationY += decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
accelerationX -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
accelerationX += decceleration;
}
dx += accelerationX;
dy += accelerationY;
speed = sqrt(dx * dx + dy * dy);
if (speed > maxSpeed)
{
dx *= maxSpeed / speed;
dy *= maxSpeed / speed;
}
x += dx;
y += dy;
dx *= 0.9;
dy *= 0.9;
accelerationX = 0;
accelerationY = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}

How can I make angles not act weird?

I'm new to programming and SFML. I'm trying to make something like a canon. It's gonna fire balls that will be flying in an arc. Sounds like a very simple task to accomplish, yet I cannot seem to figure out how angles work in SFML. For example, with ang_const = 0.13 Rad (7.44 Deg), my balls flies in a beautiful arc. However, when I change the value of ang_const to 0.14 Rad (8.021 Deg), the ball flies in the opposite direction! If I change the angle to 0.19 Rad (10.88 Deg), it flies downwards for whatever reason.
So here's my code:
#include <SFML/Graphics.hpp>
#include <math.h>
int WIDTH = 1024, HEIGHT = 704;
class Ball {
private:
float radius = 16.00;
public:
sf::CircleShape shape;
Ball () {
shape.setPosition(0 + radius*2, HEIGHT - radius*2);
shape.setRadius(radius);
shape.setFillColor(sf::Color::Cyan);
shape.setOrigin(radius, radius);
}
void update() {
if (x() - radius > WIDTH) {
this->shape.setPosition(0 - radius, y());
}
if (x() + radius < 0) {
this->shape.setPosition(WIDTH + radius, y());
}
if (y() - radius > HEIGHT) {
this->shape.setPosition(x(), 0 - radius);
}
if (y() + radius < 0) {
this->shape.setPosition(x(), HEIGHT + radius);
}
}
float RadToDeg (float radian) {
double pi = 3.14159;
return radian * (180 / pi);
}
float x() { return shape.getPosition().x; }
float y() { return shape.getPosition().y; }
float getRadius() { return radius; }
};
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "del");
// Some variables
float ang_const = 0.13;
float velX_const = 3.5, velY_const = 3.5;
float grav_const = -0.02;
float ang = ang_const;
float velX = velX_const, velY = velY_const;
float grav = grav_const;
// Text
int size_for_text = 64;
sf::Font f;
f.loadFromFile("Keyboard.ttf");
sf::Text text1;
text1.setFont(f);
text1.setCharacterSize(27);
text1.setFillColor(sf::Color::White);
text1.setPosition(size_for_text, size_for_text);
// Ball
Ball ball;
while (window.isOpen())
{
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed) {
window.close();
}
// Escape pressed: exit
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
window.close();
}
// Restart
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space) {
ang = ang_const;
velX = velX_const, velY = velY_const;
grav = grav_const;
ball.shape.setPosition(0 + ball.getRadius()*2, HEIGHT - ball.getRadius()*2);
}
}
// Ball movement
ball.update();
velY += grav;
ball.shape.move(velX * cos(ball.RadToDeg(ang)),
velY * -sin(ball.RadToDeg(ang)));
// Clear screen
window.clear(sf::Color(0,0,80,100));
// Draw ball
window.draw(ball.shape);
// Draw text
text1.setString("ang " + std::to_string(ang));
window.draw(text1);
// Update the window
window.display();
}
return EXIT_SUCCESS;
}
The main lines are these:
Variables:
float ang_const = 0.13;
float velX_const = 3.5, velY_const = 3.5;
float grav_const = -0.02;
Ball movement:
velY += grav;
ball.shape.move(velX * cos(ball.RadToDeg(ang)), velY * -
sin(ball.RadToDeg(ang)));
Radians to Degrees function:
float RadToDeg (float radian) {
double pi = 3.14159;
return radian * (180 / pi);
}
Could someone explain what's wrong with my code and how angles work in SFML? I'd be appreciated for your help guys.
All the trigonometric functions defined in <cmath> expect their parameters to be values representing angles in radians (see e.g. std::cos).
So, when you write something like
cos(ball.RadToDeg(ang))
where ang is equal to, say 0.13, RadToDeg will convert it to 7.44, but, even if your intention is to pass an angle in degrees, that value is interpreted by std::cos (and std::sin) as an angle of 7.44 radians (or 66.28°).
That leads to your unexpected results:
cosr(7.44) = 0.505 (instead of cosd(7.44°) = 0.993) and
cosr(8.021) = -0.166 (instead of cosd(8.021°) = 0.992)

Fractal Tree - branches not drawn

Currently, I'm trying to draw a symmetric binary tree through IFS (Iterated Function Systems):
but the result is always only the branch tips:
.
I can't figure out what I'm doing wrong or what I'm missing.
This is the IFS:
This is my code:
RenderWindow window(VideoMode(480, 640), "fractals everywhere");
CircleShape point(1);
int chance;
float x, y, w, h, nx, ny, px, py;
void SymmetricBinaryTrees()
{
float r = 0.57f;
float o = 0.785f;
chance = rand() % 3;
switch (chance)
{
case 0:
nx = r * cos(o) * x + (-1 * r * sin(o) * y);
ny = r * sin(o) * x + r * cos(o) * y + 1;
break;
case 1:
nx = r * cos(o) * x + r * sin(o) * y;
ny = -1 * r * sin(o) * x + r * cos(o) * y + 1;
break;
case 2:
nx = x;
ny = y;
break;
}
}
void nextPoint()
{
SymmetricBinaryTrees();
x = nx; y = ny;
}
void drawPoint()
{
px = _map(x, -1.078, 1.078f, 0, w); py = _map(y, 0.f, 2.078f, h, 0); // maps the position accordingly
point.setPosition(px, py);
window.draw(point);
}
int main()
{
srand(time(NULL));
w = window.getSize().x * 1.f;
h = window.getSize().y * 1.f;
x = 0.f; y = 0.f;
window.setFramerateLimit(60);
while (window.isOpen())
{
Event e;
while (window.pollEvent(e))
if (e.type == Event::Closed) window.close();
for (int i = 1; i <= 500; i++)
{
drawPoint();
nextPoint();
}
window.display();
}
return 0;
}
This is the website that I'm using for my code.
If anyone could help me or has any idea I'd be very grateful, thank you.
I share #beyond opinion, I think you're complicating things too much. It will be easier with a different approach. Let's make things easier.
With a recursive function, we can easily understand what should be done each step.
Consider we start from a initial point, then trace a line on an angle of a given lenght, so we need a function like:
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght)
Where tree will be our line set, which compose the tree itself.
First thing we can do, is to set the first point, which is already known:
// Add first point
tree.append(sf::Vertex(point, treeColor));
Now we need to calculate our next point, to form a line. With simple trigonometric functions we can determine that point:
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
So we add this second point, and then split the tree into 2 new branches, each one rotated some certain degrees:
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
We need a base case for our recursive function, in this case, I choose 3 as minimum lenght:
if (lenght < 3)
// End condition, can be modified
return;
this must be out first check.
So we're done, we only need the initial call:
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at botton-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
And the result is:
Full code
#include <SFML/Graphics.hpp>
const double PI = 3.141592;
const double R = 0.57; // Reduction factor
const double O = 45; // Degree rotation each time
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
// End condition, can be modified
return;
// Add first point
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
sf::Vector2f nextPoint(newX, newY);
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at bottom-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
RenderWindow window({ 500, 500 }, "SFML Tree", Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (Event event; window.pollEvent(event);){
if (event.type == Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}
I would advice you to use recursion with a function that 1) draws the current branch (as a line), and then 2) creates two new branches from the current branch. Using global variables doesn't help you either. Looks like you should rethink your approach.
For Linux is:
#include <SFML/Graphics.hpp>
#include <cmath>
const double PI = 3.141592;
const double R = 0.57;
const double O = 45;
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
return;
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght);
sf::Vector2f nextPoint(newX, newY);
tree.append(sf::Vertex(nextPoint, treeColor));
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
sf::VertexArray ret(sf::PrimitiveType::Lines);
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
sf::RenderWindow window({ 500, 500 }, "SFML Tree", sf::Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (sf::Event event; window.pollEvent(event);){
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}

How to scale the rotation of a quaternion

I am trying to do the equivalent of multiplying the velocity by the time between frames. I would imagine that doing this for quaternions would be done by raising them to a power. I have code to rotate an object based on my mouse movements. It has a main loop running at one frame rate and a physics loop running at a fixed frame rate. Here is the relevant part of the main loop:
glfwPollEvents();
Input::update();
window.clear(0,0,0,1);
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().x, glm::vec3(0,1,0));
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().y, glm::vec3(1,0,0));
if(Input::getKey(Input::KEY_A))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_D))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_W))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_S))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_LCONTROL))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,1,0);
}
if(Input::getKey(Input::KEY_LSHIFT))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,1,0);
}
Here is the relevant part of the physics loop:
for(int i = 0; i < *numRigidBodies; i++)
{
rigidBodies[i].transform->getPos() += rigidBodies[i].velocity;
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
}
rigidBodies[0].angularVelocity = glm::quat();
rigidBodies[0].velocity = glm::vec3();
This works fine, but when I try raising angular velocity to a power with glm::pow, the object rotates randomly and does not follow my mouse. I realize I could do this with a line of code like
rigidBodies[i].transform->getRot() *= glm::angleAxis((float)Time::getFixedDelta() * glm::angle(rigidBodies[i].angularVelocity), glm::axis(rigidBodies[i].angularVelocity));
but this seems needlessly complicated for the task. What is causing this issue, and how can I fix it?
Not sure exactly how to do it with the API you're using, but basically, you would use Quaternion::Slerp(). Slerp means "spherical linear interpolation".
Something like this(pseudocode) should work:
auto& rot = rigidBodies[i].transform->getRot();
auto goal = rigidBodies[i].angularVelocity * rot;
rot = rot.slerp(rot, goal, Time::deltaTime);
Edit:
I should note that this is not how I would approach this problem. I would just store the rotation around the X and Y axis as scalars and construct a new quaternion from them each frame.
Please excuse the sloppy pseudo code:
// previous x and y positions, could probably be set in MouseDown event
float lastX = ...;
float lastY = ...;
float xRotation = 0;
float yRotation = 0;
float rotationSpeed = 1.0;
void OnMouseMove(float x, float y) {
float dx = x - lastX;
float dy = y - lastY;
lastX = x;
lastY = y;
xRotation += dy * rotationSpeed * Time::deltaTime;
yRotation += dx * rotationSpeed * Time::deltaTime;
rigidBodies[i].transform->getRot() = eulerQuat(xRotation, yRotation, 0);
}
Turns out angular velocity is usually represented as a 3d vector where the direction is the axis and the magnitude is the angular speed. Replace this line of code:
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
with this:
if(rigidBodies[i].angularVelocity != glm::vec3())
rigidBodies[i].transform->getRot() *= glm::quat(rigidBodies[i].angularVelocity * float(Time::getFixedDelta()));
and the physics system works as expected. The if check makes sure that angular speed is not 0.

Inverting an angle on the Y-axis, X works

I've asked this question over at GameDev but got not response so far and this question is a bit time sensitive unfortunately.
I'm pretty sure this is just me doing something stupid or not understanding something that I should but I cannot figure out what is wrong here.
I'm having a problem bouncing a projectile off a sprite, we've been asked to move the projectile using the equations of motions which makes things a little more difficult but as far as I can see what I have should work.
What I'm trying to do is change the angle of the collided projectile depending on which direction it is coming from.
Here is a video that is hopefully not too laggy for you to see what is happening:
Link
When the projectile collides with the left or right hand side of the sprite everything works as expected, it just switches X direction.
When it hit's the top or bottom of the sprite however it doesn't change, it just sort of rolls along the top and the shoots off.
Here is the movement code:
float nX = get_x() + cos(nGetAngle() * 3.14 / 180) * getU() * getT();
float nY = get_y() - sin(nGetAngle() * 3.14 / 180) * getU() * getT() + 0.5 * 9.8 * getT() * getT();
set_world_position(nX, nY);
Where U is initial velocity, T is time and nGetAngle() is the angle in degrees (which is set to radians whenever the angle is set).
Here is my collision for the top of the player:
//if the projectile is colliding in any way with the player sprite
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
and here is my collision for the left of the player:
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
The left side collision works, the top does not and I have no idea why.
If necessary I can post the entire project somewhere.
Full collision code for player:
void Game::playerCollision()
{
if (projectiles[currProj]->bb_collision(player))
{
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
else if (projectiles[currProj]->get_y() + projectiles[currProj]->get_height() > player->get_y() + player->get_height() + 1) // bottom of player
{
projectiles[currProj]->set_world_position_y(player->get_y() + player->get_height());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() > player->get_x()) // right of player
{
projectiles[currProj]->set_world_position_x(player->get_x() + player->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
}
}
I think your collision detection is not sufficient. without knowing your representation in detail
you do not check where the projectile (pr) came from. a collision top left within the player (pl) might have entered through the top or from the left
you do not bounce the pr immediately, you just alter the direction. depending on the entry depth it might not be able to exit with the next iteration. this happens especially on the top where the pr accelerates downwards but slows down upwards.
so you must
detect the entry surface (determines angle)
and most important rebounce immediately