Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Inner-Circle Collision / Calculate New Angle.  (Read 1735 times)

0 Members and 1 Guest are viewing this topic.

Jycerian

  • Newbie
  • *
  • Posts: 49
  • Weakness of attitude becomes weakness of character
    • View Profile
Inner-Circle Collision / Calculate New Angle.
« on: June 03, 2014, 11:59:18 am »
Hello,

I am having trouble calculating the new angle of the ball because I have not learned this kind of math at school.
How would I get the correct new angle? Can someone explain it to me how it is done? I have a feeling it is simpler than I think but I have no clue on how to make it work. Maby someone can give me some example code.



#include <SFML/Graphics.hpp>
#include <iostream>

#include <cmath>

#define PI 3.14159265359

unsigned const int SCREEN_WIDTH  = 640;
unsigned const int SCREEN_HEIGHT = 480;

int main()
{
    sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32), "C-Long", sf::Style::Close);
    window.setFramerateLimit(60);

    // Loading textures
    sf::Texture ballTexture;
    if (!ballTexture.loadFromFile("Assets/Textures/ball.png")) return 0;

        sf::Texture backgroundTex;
        if (!backgroundTex.loadFromFile("Assets/Textures/background1.png")) return 0;

        sf::Sprite background;
        background.setTexture(backgroundTex);
        background.setOrigin(0,0);
        background.setPosition(0,0);

        // Create ball sprite
    sf::Sprite ball;
    ball.setTexture(ballTexture);
    ball.setOrigin(ball.getGlobalBounds().width / 2, ball.getGlobalBounds().height / 2);
    ball.setPosition(SCREEN_WIDTH / 2 - 50, SCREEN_HEIGHT / 2 + 100);

    // Ball variables
    float ballSpeed = 200.f;
    float ballRadius = ball.getGlobalBounds().width / 2;
    float initBallAngle = 10.f;
    float ballAngle = 0.f;

    ballAngle = initBallAngle * PI / 180.f;

        float xBegin = 0;
        int hitAmmount = 0;
    sf::Clock clock;
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        float deltaTime = clock.restart().asSeconds();

        // Move the ball
        float factor = ballSpeed * deltaTime;
                ball.move(cos(ballAngle) * factor, sin(ballAngle) * factor);

                // Distance center / ball
                int ballDistanceFromCenter = (sqrt(pow((SCREEN_WIDTH / 2) - ball.getPosition().x, 2) +
                pow(ball.getPosition().y - (SCREEN_HEIGHT/2), 2)));
               
                if (ballDistanceFromCenter > 230)
                {
                        int difference = (ballDistanceFromCenter - 230);
                       
                        int x,y;
                        // ball out of bounds? set it back x pixels.
                        if (ball.getPosition().x < (SCREEN_WIDTH / 2) && ball.getPosition().y < (SCREEN_HEIGHT / 2))
                        {
                                x = ball.getPosition().x + difference;
                                y = ball.getPosition().y + difference;
                        }
                        else if (ball.getPosition().x > (SCREEN_WIDTH / 2) && ball.getPosition().y > (SCREEN_HEIGHT / 2))
                        {
                                x = ball.getPosition().x - difference;
                                y = ball.getPosition().y - difference;
                        }
                        else if (ball.getPosition().x < (SCREEN_WIDTH / 2) && ball.getPosition().y > (SCREEN_HEIGHT / 2))
                        {
                                x = ball.getPosition().x + difference;
                                y = ball.getPosition().y - difference;
                        }
                        else if (ball.getPosition().x > (SCREEN_WIDTH / 2) && ball.getPosition().y < (SCREEN_HEIGHT / 2))
                        {
                                x = ball.getPosition().x - difference;
                                y = ball.getPosition().y + difference;
                        }
                        ball.setPosition(x, y);
                       
                        //ignore this bad angle calculation.
                        ballAngle = (ballAngle + PI) + ((90 - 30) * PI / 180);
                        if((ballAngle * 180 / PI) > 360)
                                ballAngle =  ((ballAngle * 180 / PI) - 360) * PI / 180;
                        std::cout << ballAngle * 180 / PI << "\n\n";   
                }

                sf::Vertex line1[] =
                {
                        sf::Vertex(sf::Vector2f(SCREEN_WIDTH/2, SCREEN_HEIGHT/2)),
                        sf::Vertex(sf::Vector2f(ball.getPosition().x, ball.getPosition().y))
                };

                sf::Vertex line2[] =
                {
                        sf::Vertex(sf::Vector2f(SCREEN_WIDTH/2, SCREEN_HEIGHT/2)),
                        sf::Vertex(sf::Vector2f(ball.getPosition().x, SCREEN_HEIGHT/2))
                };

                sf::Vertex line3[] =
                {
                        sf::Vertex(sf::Vector2f(ball.getPosition().x, SCREEN_HEIGHT/2)),
                        sf::Vertex(sf::Vector2f(ball.getPosition().x, ball.getPosition().y))
                };

        window.clear();
                window.draw(background);
                window.draw(line1, 2, sf::Lines);
                window.draw(line2, 2, sf::Lines);
                window.draw(line3, 2, sf::Lines);
        window.draw(ball);
        window.display();
    }
    return 0;
}

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Inner-Circle Collision / Calculate New Angle.
« Reply #1 on: June 03, 2014, 12:31:47 pm »
What "new angle" do you mean? When the ball repels from the circle boundary?

Generally, the angles of incidence and reflection must be equal. To know these angles, you have to construct a tangent (or alternatively a normal) to the circle boundary, and measure the angle between it and the ball's velocity vector. This can be done using trigonometry (std::atan2 or std::acos, if you use the dot product).

I have developed the Thor.Vectors module which simplifies such tasks tremendously. It abstracts from trigonometry and provides more functionality on top of vectors. You can also try to solve your problem with Thor, and then look at its implementation to understand how you would have done it from scratch.

Using Thor, your problem could be approached as follows (the attached sketch shows the vectors):
// Given:
const float circleRadius = ...;
const sf::Vector2f circleCenter = ...;
sf::Vector2f ballPosition = ...;
sf::Vector2f ballVelocity = ...;

// Check if ball is outside circle
// i.e. distance from center is larger than radius
if (thor::length(ballPosition - circleCenter) > circleRadius)
{
    // Get approximated outward normal    
    sf::Vector2f normal = ballPosition - circleCenter;

    // Measure angle between velocity and normal (= incidence angle w.r.t. normal)
    float inAngle = thor::signedAngle(normal, ballVelocity);

    // Rotated/mirrored velocity on the other side of the normal
    sf::Vector2f rotatedVelocity = thor::rotatedVector(ballVelocity, -2.f * inAngle);

    // New final velocity in other direction
    sf::Vector2f newVelocity = -rotatedVelocity;
}

And the best of it: we have not needed a single sine, cosine, square root, arc tangens... Everything is simple :)
« Last Edit: June 03, 2014, 12:34:08 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development: