2
\$\begingroup\$

I am currently making Pong in C++/SDL2 and after a while of tinkering, I have come up with a solution!

This function gets the hit position and returns the necessary direction change:

MoveDirection Ball::getHitPos(const Player& player) const
{
    MoveDirection dir; //Direction of movement

    //If ball is moving left
    if (m_dir == MoveDirection::LEFT)
    {   
        std::cout << "Yes";
        int hitPos = m_pos.y - player.getPos().y; //Get hit position
        int playerSize = player.getPos().h / 2; 
        int offSet = 25; //Enables to split up positions into three seperate parts
        //Calculate dir change
        if (hitPos <= playerSize - offSet) {
            dir = MoveDirection::RIGHTUP;
        }
        else if (hitPos >= playerSize + offSet) {
            dir = MoveDirection::RIGHTDOWN;
        }
        else {
            dir = MoveDirection::RIGHT;
        }
    }

    //If Ball is moving right
    if (m_dir == MoveDirection::RIGHT)
    {
        int hitPos = m_pos.y - player.getPos().y; //Get hit position
        int playerSize = player.getPos().h / 2;
        int offSet = 25; //Enables to split up positions into three seperate parts
        //Calculate dir change
        if (hitPos <= playerSize - offSet) {
            dir = MoveDirection::LEFTUP;
        }
        else if (hitPos >= playerSize + offSet) {
            dir = MoveDirection::LEFTDOWN;
        }
        else {
            dir = MoveDirection::LEFT;
        }
    }

    return dir;
}

Here are all of the possible movement directions:

enum class MoveDirection
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
    LEFTUP, //Ball diagonal movement
    LEFTDOWN, //Ball diagonal movement
    RIGHTUP, //Ball diagonal movement
    RIGHTDOWN //Ball diagonal movement
};

And here is the height of the racket:

static constexpr int SIZE_HEIGHT = 75;

I must also note that m_pos is SDL_RECT and player.getPos().y is getting the y element of SDL_RECT.

\$\endgroup\$
2
  • \$\begingroup\$ Is this real code? The function calculates dir and then discard the result. \$\endgroup\$ – Edward Mar 18 '16 at 20:24
  • \$\begingroup\$ Oh! Oops, sorry that was there for test purposes on the actual function its self. Sorry! I thought I uploaded the working one, my mistake. \$\endgroup\$ – Ryan Swann Mar 18 '16 at 21:09
4
\$\begingroup\$

Your ball can only move in the eight (semi)cardinal directions? It can't move at, say, a 30-degree angle?

Well, even so, it will simplify your code to treat the ball as if it can move in any direction. Either:

struct MoveDirection {
    double angle;
    double speed;
};

or more likely:

struct MoveDirection {
    double vx;
    double vy;
};

And then rewrite your getHitPos function as something more like

MoveDirection Ball::getHitPos(const Player& player) const
{
    MoveDirection dir; //Direction of movement

    //If ball is moving left
    if (m_dir.vx < 0)
    {   
        std::cout << "Yes";
        int hitPos = m_pos.y - player.getPos().y; //Get hit position
        int playerSize = player.getPos().h / 2; 
        int offSet = 25; //Enables to split up positions into three seperate parts
        //Calculate dir change
        dir.vx = +1;
        if (hitPos <= playerSize - offSet) {
            dir.vy = +1;
        }
        else if (hitPos >= playerSize + offSet) {
            dir.vy = -1;
        }
        else {
            dir.vy = 0;
        }
    }
    else if (m_dir.vx > 0)  // ball is moving right
...

Then just keep refactoring and turning branches into arithmetic expressions until you're happy. It might end up looking something like

MoveDirection result;
result.vx = -m_dir.vx;
const double hitPos = m_pos.y - player.getPos().y; //Get hit position
const double playerSize = player.getPos().h / 2;
result.vy = (hitPos > playerSize + 25) - (hitPos < playerSize - 25);
return result;
\$\endgroup\$
3
\$\begingroup\$

I see a few things that may help you improve your code.

Think carefully about object responsibilities

It seems peculiar to me that Ball is responsible for determining where on the Player it has hit. I'd suggest refactoring so that yo have the paddle divided into regions and have a Player member enum class and function like this:

enum class PaddleRegion { LOW, MID, HI};
PaddleRegion getPaddleHitRegion(int y_pos) const;

Make your class more data-centric

The resulting direction is a function of the original direction of the ball and the region in which it hit. None of these is dynamic. That is, given a direction and region, the output will always be the same. This suggests that perhaps the Ball class could instead incorporate a static table like this:

class Ball {
    // other stuff... 
    struct Rebound {
        MoveDirection incomingDir;
        Player::PaddleRegion region;
        MoveDirection outgoingDir;
    };
    const static Rebound bounce[]; 
};

It could then be populated like this:

const Ball::Rebound Ball::bounce[] 
{
    {MoveDirection::LEFT, Player::PaddleRegion::LOW, MoveDirection::RIGHTDOWN}, 
    {MoveDirection::LEFT, Player::PaddleRegion::MID, MoveDirection::RIGHT}, 
    {MoveDirection::LEFT, Player::PaddleRegion::HI, MoveDirection::RIGHTUP}, 
    {MoveDirection::RIGHT, Player::PaddleRegion::LOW, MoveDirection::LEFTDOWN}, 
    {MoveDirection::RIGHT, Player::PaddleRegion::MID, MoveDirection::LEFT}, 
    {MoveDirection::RIGHT, Player::PaddleRegion::HI, MoveDirection::LEFTUP}, 
};

Now the code for Ball::getHitPos() looks like this:

MoveDirection Ball::getHitPos(const Player& player) const {
    Player::PaddleRegion region = player.getPaddleHitRegion(m_pos.y);
    for (const auto &b : bounce) {
        if (b.incomingDir == m_dir && b.region == region) {
            return b.outgoingDir;
        }
    }
    // not found, so pick an arbitrary direction
    return MoveDirection::RIGHT;
}

The advantages are many. First, it's easy to add additional incoming ball directions. Second, all of the interactions are captured in a single compact table so it's easy to review and change.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.