detected collision, what now? [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
i'm checking collision between the player and every other object and it works, but what do i do now.
this is what i do now, but when the player hits something from below it teleports on top of the hit object.
do i say: teleport on top if fell onto object, set max y velocity to 0 when hit from below and max x velocity for the sides?
is that the way to do it?
but this wouldnt work with circle colliders, so how do i get my objects to stop normally on hit?
for (GameObject &g : gameObjects)
{
if (BoxCollision(player, &g))
{
player->velocity.y = 0;
// set player feet location to the top of hit object
player->transform.pos.y = g.transform.pos.y - player->sprite.height * player->transform.size.y;
player->canJump = true;
}
}
collision check:
bool BoxCollision(GameObject* g1, GameObject* g2)
{
if (g1 != g2)
{
bool left = 0, right = 0, top = 0, bottom = 0;
left = g1->transform.pos.x < g2->transform.pos.x + g2->sprite.width * g2->transform.size.x ? true : false;
right = g1->transform.pos.x + g1->sprite.width * g1->transform.size.x >= g2->transform.pos.x ? true : false;
bottom = g1->transform.pos.y + g1->sprite.height * g1->transform.size.y > g2->transform.pos.y ? true : false;
top = g1->transform.pos.y < g2->transform.pos.y + g2->sprite.height * g2->transform.size.y ? true : false;
return left && right && bottom && top ? true : false;
}
else return false;
}

When you detect a collision, it is because the bounding volumes of two objects O1 and O2 intersect after a time step of your engine. In other words, your object O1 started at position x in the previous timestep, and now at x+v*dt the two objects intersect. (where v=velocity and dt is your timestep)
The first course of business is to find a dt' (<dt) where objects O1 and O2 just touch. Once you have that, the decision is yours what to do:
You can set v=0 after the collision to have object O1 stop dead;
You can invert v (after reflection along the bounding box) to have O1 bounce away
You can transfer some velocity to O2 and calculate motion for the remainder of the timestep
You can destroy O2 and have O1 continue along its path.

Related

Simple yet realistic billiard ball acceleration [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 10 months ago.
Improve this question
I have a simple 2D game of pool. You can give a ball some speed, and it'll move around and hit other balls. But I want a ball to stop eventually, so I added some acceleration, by running this code every frame:
balls[i].ax = -balls[i].vx * 0.1;
balls[i].ay = -balls[i].vy * 0.1;
...
if(hypot(balls[i].vx, balls[i].vy) < 0.2){
balls[i].vx = 0;
balls[i].vy = 0;
}
And it works... But I find it weird, not realistic. I have no physics knowledge, but I'm pretty sure friction should not depend on speed.
How can I improve physics of slowing down without too much complexity?
The rolling friction formula is this: F_k,r​=μ_k,r_​Fn. It only factors in the properties of the surface (μ_k) and the force on the ball (r_​Fn). This should decelerate with a constant value, just adjust it until it looks roughly correct.
Example code:
x = 1 // mess around with this until it looks right
if (ball.xVelocity > x) { ball.xVelocity -= x } else { ball.xVelocity = 0 }
if (ball.yVelocity > x) { ball.yVelocity -= x } else { ball.yVelocity = 0 }

Set linear velocity of a moving box2d object ( own velocity + player velocity )

I'm just trying to program a game with Box2D/ SDL2 & C++. Unfortunately, I am not really getting on with a problem. In the game there are boxes (b2_dynamicBody) that are generated at the click of a mouse. These then fall down. The character is in the middle and when it is moved I create speed X and Y with
m_pCamera->SetLinearVelocity(b2Vec2(z.B. x = 0.0f, y = 10.0f));
I then add within the game loop to any speed of my own box the speed of the character.
ObjectIterator>SetLinearVelocity(ObjectIterator->GetLinearVelocity() + m_pCamera->GetLinearVelocity());
Unfortunately, there are always funny results and I don't know exactly where I need to look.
When I move the player the falling boxes velocity is slowing down!
Here is a source code excerpt :
for (boxit = m_pBoxList.begin(); boxit != m_pBoxList.end(); boxit++) {
if ((*boxit)->getBody()->GetType() == b2_kinematicBody) {
(*boxit)->getBody()->SetLinearVelocity(b2Vec2(0,0));
}
if((*boxit)->getBody()->GetType() == b2_dynamicBody && m_pCamera->getBody()->GetLinearVelocity().x != 0.0f) {
(*boxit)->getBody()->SetLinearVelocity(m_pCamera->getBody()->GetLinearVelocity());
}
else {
(*boxit)->getBody()->SetLinearVelocity(b2Vec2((*boxit)->getBody()->GetLinearVelocity().x + m_pCamera->getBody()->GetLinearVelocity().x, (*boxit)->getBody()->GetLinearVelocity().y + m_pCamera->getBody()->GetLinearVelocity().y));
}
(*boxit)->Update();
(*boxit)->Render();
(*boxit)->RenderOutline();
if ((*boxit)->getBody()->GetLinearVelocity().y == 0.0f) {
(*boxit)->getBody()->SetType(b2_kinematicBody);
}
}
Screenshot:
screenshot / error description

Why is my collision response not stopping my player from going through walls (SDL2, C++)?

While I found many problems that are similar too mine, none of the solutions solved my problem.
I've been experimenting with SDL2 in C++ (Visual C++) and the entity-component-system (ECS). But I just can't figure out the bug in my collision response.
So here it is: My player sometimes gets set back to its origin when it encounters something like a rock (a simple gray tile). But sometimes it goes right through and gets stuck or ends up on the other side.
I can only assume it has something to do with the data changed in between frames, so it isn't always caught. But for the life of me I can't figure it out.
Here is my rectangular detection method:
bool Collision::RectIntersect(const SDL_Rect& a, const SDL_Rect& b, SDL_Rect& intersect)
{
intersect = { 0, 0, 0, 0 };
int leftX = std::max(a.x, b.x);
int rightX = std::min(a.x + a.w, b.x + b.w);
int topY = std::max(a.y, b.y);
int bottomY = std::min(a.y + a.h, b.y + b.h);
if (leftX < rightX && topY < bottomY)
{
intersect = { leftX, topY, rightX - leftX, bottomY - topY };
return true;
}
return false;
}
Here is my snippet where my inputs are handled and subsequently any collision detections are resolved before the code actually moves anything:
void InputComponent::handleEvents(SDL_Event* e)
{
const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
if (e != nullptr)
{
/*
keyHeld: array of 4 for each direction (+/- x, +/- y (WASD))
hold value true, if pressed down, otherwise false
*/
if (keyboardState[SDL_SCANCODE_A])
{
keyHeld[0] = true;
}
else
{
keyHeld[0] = false;
}
if (keyboardState[SDL_SCANCODE_D])
{
keyHeld[1] = true;
}
else
{
keyHeld[1] = false;
}
if (keyboardState[SDL_SCANCODE_W])
{
keyHeld[2] = true;
}
else
{
keyHeld[2] = false;
}
if (keyboardState[SDL_SCANCODE_S])
{
keyHeld[3] = true;
}
else
{
keyHeld[3] = false;
}
}
/*
tmpVel: Vector to store the assumed velocity in x- and y-direction
*/
Vector2D tmpVel(0.0f, 0.0f);
// left and right (A and D)
if (keyHeld[0] && !keyHeld[1]) // left
{
tmpVel.x = -1.0f;
}
else if (!keyHeld[0] && keyHeld[1]) // right
{
tmpVel.x = 1.0f;
}
else
{
tmpVel.x = 0.0f; // left and right cancel each other out
}
// up and down (W and S)
if (keyHeld[2] && !keyHeld[3]) // up
{
tmpVel.y = -1.0f;
}
else if (!keyHeld[2] && keyHeld[3]) // down
{
tmpVel.y = 1.0f;
}
else
{
tmpVel.y = 0.0f; // up and down cancel each other out
}
/*
check for collision with presumed direction according to tmpVel
*/
SDL_Rect intersection;
// get current player position
SDL_Rect movedPlayer = entity->getComponent<CollisionComponent>().getCollider();
// add trajectory of theoretical movement
movedPlayer.x += static_cast<int>(tmpVel.x * vel_->getSpeed());
movedPlayer.y += static_cast<int>(tmpVel.y * vel_->getSpeed());
bool hasCollided = false;
// collect all collidable objects
for (auto& c : manager_->getGroup(GroupLabel::GR_COLLIDERS))
{
// check player against each collidable tile
//if (SDL_IntersectRect(&movedPlayer, &c->getComponent<CollisionComponent>().getCollider(), &intersection))
if (Collision::RectIntersect(movedPlayer, c->getComponent<CollisionComponent>().getCollider(), intersection))
{
// collision on x-axis
if (intersection.w > 0)
{
// set velocity on x-axis to 0
vel_->setVelocityX(0.0f);
// reset player position back according to width of intersected rectangle
pos_->setPosX(pos_->getPos().x + (static_cast<float>(intersection.w) * (-tmpVel.x)));
}
// collision on y-axis
if (intersection.h > 0)
{
// set velocity on y-axis to 0
vel_->setVelocityY(0.0f);
// reset player position back according to height of intersected rectangle
pos_->setPosY(pos_->getPos().y + (static_cast<float>(intersection.h) * (-tmpVel.y)));
}
hasCollided = true;
}
}
if (!hasCollided)
{
vel_->setVelocity(tmpVel);
}
}
Can anybody put me in the right direction?
What happens when the right edge of the player exactly equals the left edge of the rock? It looks like the collision is not detected, since the test is for (leftX < rightX). So the velocity is updated and the player is moved by the velocity. (It's odd that you simply update the velocity and later move the player instead of just moving them to the new already calculated position.) If you change the check to (leftX <= rightX), does the problem persist?
As far as I can see there are two things wrong with your collision detection. The first is that you're testing (leftX < rightX && topY < bottomY) when you should be testing (leftX <= rightX && topY <= bottomY). If you fix this your code will work in most situations.
The second problem you've got, which may not become apparent straight away, is that your are performing collision detection for discreet points in time. If your player has a large enough velocity vector you may end up with this situation:
Update 1: Player is in front of wall travelling towards it. AABB test shows no collision.
Update 2: Player is behind wall travelling away from it. AABB test shows no collision.
Your AABB test is correct and yet the player has passed through the wall. The naive approach to fixing this is to test more often (update 1.5 may have shown a collision), or to limit player velocity. Both approaches will require a lot of fine tuning especially if you're dealing with objects that can move at different speeds and walls with differing thickness.
A more robust approach is to take account of velocity in your test. Since you know the velocity of your AABB you can project this shape along its velocity vector. If you do this for both AABBs you'll end up with two elongated shapes which you can test against each other. If they overlap then you know that their paths cross and that there may be a collision.
Of course, knowing that there might be a collision is not hugely helpful. The problem is one AABB may be moving very slowly and the other very quickly so even though they both pass through the same space (their elongated shapes intersect) they don't pass through it at the same time.
Figuring out whether they both pass through the same space at the same time is hard, so instead we cheat. If you subtract the velocity of B from the velocity of A and then use this modified velocity to project the elongated shape of A, you can effectively treat B as a stationary object and still get the correct result. Knowing this, your test is now "does B overlap the elongated shape of A?". This is just a simple AABB vs Ngon problem.
While the above will give you a boolean as to whether two moving AABBs collide it will not tell you when they collide which is also useful for calculating things like rebounds.
I would very much recommend the book Real Time Collision Detection by Christer Ericson which is pretty much the go to book on collision detection for any aspiring game developer.
The following is a code snippet from the CD-ROM which accompanies the book. It tests a moving AABB against another moving AABB and also provides a time of first contact.
// Intersect AABBs ‘a’ and ‘b’ moving with constant velocities va and vb.
// On intersection, return time of first and last contact in tfirst and tlast
int IntersectMovingAABBAABB(AABB a, AABB b, Vector va, Vector vb, float &tfirst, float &tlast)
{
// Exit early if ‘a’ and ‘b’ initially overlapping
if (TestAABBAABB(a, b)) {
tfirst = tlast = 0.0f;
return 1;
}
// Use relative velocity; effectively treating ’a’ as stationary
Vector v = vb - va;
// Initialize times of first and last contact
tfirst = 0.0f;
tlast = 1.0f;
// For each axis, determine times of first and last contact, if any
for (int i = 0; i < 3; i++) {
if (v[i] < 0.0f) {
if (b.max[i] < a.min[i]) return 0;
// Nonintersecting and moving apart
if (a.max[i] < b.min[i]) tfirst = Max((a.max[i] - b.min[i]) / v[i], tfirst);
if (b.max[i] > a.min[i]) tlast = Min((a.min[i] - b.max[i]) / v[i], tlast);
}
if (v[i] > 0.0f) {
if (b.min[i] > a.max[i]) return 0;
// Nonintersecting and moving apart
if (b.max[i] < a.min[i]) tfirst = Max((a.min[i] - b.max[i]) / v[i], tfirst);
if (a.max[i] > b.min[i]) tlast = Min((a.max[i] - b.min[i]) / v[i], tlast);
}
// No overlap possible if time of first contact occurs after time of last contact
if (tfirst > tlast) return 0;
}
return 1;
}
The following attribution is required by Elsevier's Software License Agreement:
“from Real-Time Collision Detection by Christer Ericson, published by Morgan Kaufmann Publishers, © 2005 Elsevier Inc”

Circular collision rebound not working properly

I'm writing a little physics simulation in C++ that basically moves circles across the screen and when two circles collide, they should ricochet in the same manner as billiard balls would. When the circles do collide with each other, most of the time they will practically slow down infinitely/they appear to stick to each other and become static. Sometimes only one ball will rebound in the collision and the other will retain it's trajectory. This is just a simple 2D simulation.
So here's what I have for the detection/ricochet logic:
bool Ball::BallCollision(Ball &b2)
{
if (sqrt(pow(b2.x - x, 2) + pow(b2.y - y, 2)) <= b2.radius + radius) // Test for collision
{
normal[0] = (x - (x + b2.x) / 2) / radius; // Finds normal vector from point of collision to radius
normal[1] = (y - (y + b2.y) / 2) / radius;
xvel = xvel - 2 * (xvel * normal[0]) * normal[0]; // Sets the velocity vector to the reflection vector
yvel = yvel - 2 * (yvel * normal[1]) * normal[1];
////x = xprev; // These just move the circle back a 'frame' so the collision
////y = yprev; // detection doesn't detect collision more than once.
// Not sure if working?
}
}
I can't figure out what is wrong with my function. Thanks for any help in advance!
Edit:
Every variable is declared as a float
The functions:
void Ball::Move()
{
xprev = x;
yprev = y;
x += xvel;
y += yvel;
}
void Ball::DrawCircle()
{
glColor3ub(100, 230, 150);
glBegin(GL_POLYGON);
for (int i = 0; i < 10; i++)
{
angle = i * (2*3.1415/10);
newx = x + r*cos(angle);
newy = y + r*sin(angle);
glVertex2f(newx, newy);
}
glEnd();
}
The loop:
run_prev.clear(); // A vector, cleared every loop, that holds the Ball objects that collided
for (int i = 0; i < num_balls; i++)
{
b[i].Move();
}
for (int i = 0; i < num_balls; i++)
{
b[i].WallCollision(); // Just wall collision detecting, that is working just fine
}
//The loop that checks for collisions... Am I executing this properly?
for (int i = 0; i < num_balls; i++)
{
for (int j = 0; j < num_balls; j++)
{
if (i == j) continue;
if (b[i].BallCollision(b[j]) == true)
{
run_prev.push_back(b[i]);
}
}
}
for (int i = 0; i < num_balls; i++)
{
b[i].DrawCircle();
}
//xprev and yprev are the x and y values of the frame before for each circle
for (int i = 0; i < run_prev.size(); i++)
{
run_prev[i].x = run_prev[i].xprev;
run_prev[i].y = run_prev[i].yprev;
}
Makes balls collide (reflect movement vector) only if they're moving towards each other. Do not process collision if they're moving away from each other. Break this rule, and they'll be glued together.
When processing collision, update both balls at once. Do not update one ball at a time.
Your move vector adjustment is incorrect. Balls don't reflect against each other, because they can be moving at different speeds.
Correct movement adjustment (assuming balls have equal mass) should look something like that:
pos1 and pos2 = positions;
v1 and v2 are movement vector (speed);
n is collision normal == normalize(pos1 - pos2);
collisionSpeed = dot((v2-v1), n);
collisionSpeed *= elasticy; (0.0..1.0);
v1 = v1 - dot(v1, n);
v2 = v2 - dot(v2, n);
v1 -= scale(n, collisionSpeed * 0.5);
v2 += scale(n, collisionSpeed * 0.5);
To understand the formula, check newtons law (impulses in particular). Or check Chris Hecker's papers on game physics.
It's not clear how you're calling this function, but I think I see the problem.
Say you have Ball ballA and Ball ballB, which are colliding in the current frame, and then you run ballA.BallCollision(ballB).
This will update the member variables of ballA, and move it back a frame. But it doesn't update the position or trajectory of ballB.
Even if you call the converse as well (ballB.BallCollision(ballA)), it won't detect the collision because when you called ballA.BallCollision(ballB), it moved ballA back a frame.
I haven't looked at your code in detail, but it doesn't take into consideration that this type of collision can only work in center of momentum frames. Now, I assume your balls are of equal masses. What you do is take the average of the two momentums (or velocities since they have the same masses) and subtract that average from the velocities. Perform your calculations, and add the average back. Here is the question I asked that may relate to this.
I know this question is quite old, but it's still relevant, especially to students. Something that wasn't mentioned in the answers made me want to contribute.
One thing that I ran into when solving a similar problem was overlap. That is, if the moving balls overlap by any amount at all, the collision detection will trigger continuously, giving the sticking behavior the OP referred to.
There was an attempt here to prevent this by moving the balls to the previous frame, but that can occasionally fail if the movement was enough that the balls enmeshed more than a single frame can account for, or if the movement velocity is just right so that the frame before doesn't trigger collision but the frame after is too far overlapped.
Since the original check was for center distance less than or equal to the sum of the radii, the detection triggers on both collision AND overlap.
One way to fix this is to separate the test into checking for collision (equals only) or overlap (less than only). For the collision, proceed as normal. But for the overlap condition, you can physically move one ball or the other (or both by half) the amount of overlap. This positions them at correct "collision" position, which allows for the correct behavior of the bounce function.
An overlap function that only moves one ball at a time might look something like this(not real code):
if (distanceBetweenBallCenters < sumOfRadii){
currentPosition = oldPosition - (distanceBetweenBallCenters - sumOfRadii) * (unitVectorFromSecondBallToFirstBall);
}
One could easily move both balls by half, but I found that moving one at a time gave satisfactory results for my uses, and allowed me to keep the parameter as a const.
I hope this helps future students! (I am also a student, and new to this, so take my advice with the proverbial grain of salt)
Your way of calculating the normal is wrong. (x + b2.x)/2 doesn't have to be the point of collision, if the radii of the balls aren't equal.

2D Platformer Collision Handling

I am trying to create a 2D platformer (Mario-type) game and I am some having some issues with handling collisions properly. I am writing this game in C++, using SDL for input, image loading, font loading, etcetera. I am also using OpenGL via the FreeGLUT library in conjunction with SDL to display graphics.
My method of collision detection is AABB (Axis-Aligned Bounding Box), which is really all I need to start with. What I need is an easy way to both detect which side the collision occurred on and handle the collisions properly. So, basically, if the player collides with the top of the platform, reposition him to the top; if there is a collision to the sides, reposition the player back to the side of the object; if there is a collision to the bottom, reposition the player under the platform.
I have tried many different ways of doing this, such as trying to find the penetration depth and repositioning the player backwards by the penetration depth. Sadly, nothing I've tried seems to work correctly. Player movement ends up being very glitchy and repositions the player when I don't want it to. Part of the reason is probably because I feel like this is something so simple but I'm over-thinking it.
If anyone thinks they can help, please take a look at the code below and help me try to improve on this if you can. I would like to refrain from using a library to handle this (as I want to learn on my own) or the something like the SAT (Separating Axis Theorem) if at all possible. Thank you in advance for your help!
void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
{
de2dObj ballPrev;
ballPrev.coords[0] = ball.coords[0];
ballPrev.coords[1] = ball.coords[1];
ballPrev.coords[2] = ball.coords[2];
ballPrev.coords[3] = ball.coords[3];
ballPrev.coords[0] -= ball.xspeed;
ballPrev.coords[1] -= ball.yspeed;
ballPrev.coords[2] -= ball.xspeed;
ballPrev.coords[3] -= ball.yspeed;
int up = 0;
int left = 0;
int right = 0;
int down = 0;
if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
{
left = 1;
}
if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
{
right = 1;
}
if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
{
up = 1;
}
if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
{
down = 1;
}
cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;
if (left == 1)
{
ball.coords[0] = block[i].coords[0] - 18.0f;
ball.coords[2] = block[i].coords[0] - 2.0f;
}
else if (right == 1)
{
ball.coords[0] = block[i].coords[2] + 2.0f;
ball.coords[2] = block[i].coords[2] + 18.0f;
}
else if (down == 1)
{
ball.coords[1] = block[i].coords[3] + 4.0f;
ball.coords[3] = block[i].coords[3] + 20.0f;
}
else if (up == 1)
{
ball.yspeed = 0.0f;
ball.gravity = 0.0f;
ball.coords[1] = block[i].coords[1] - 17.0f;
ball.coords[3] = block[i].coords[1] - 1.0f;
}
}
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
{
ball.gravity = -0.5f;
}
}
}
To explain what some of this code means:
The blocks variable is basically an integer that is storing the amount of blocks, or platforms. I am checking all of the blocks using a for loop, and the number that the loop is currently on is represented by integer i.
The coordinate system might seem a little weird, so that's worth explaining.
coords[0] represents the x position (left) of the object (where it starts on the x axis).
coords[1] represents the y position (top) of the object (where it starts on the y axis).
coords[2] represents the width of the object plus coords[0] (right).
coords[3] represents the height of the object plus coords[1] (bottom).
de2dCheckCollision performs an AABB collision detection.
Up is negative y and down is positive y, as it is in most games.
Hopefully I have provided enough information for someone to help me successfully. If there is something I left out that might be crucial, let me know and I'll provide the necessary information. Finally, for anyone who can help, providing code would be very helpful and much appreciated.
Thank you again for your help!
Edit 2: I have updated my code with a new algorithm that checks where the ball was previously before collision. Corner cases work on that single platform correctly now, and when I have a wall of objects, I can slide against it correctly now. The only remaining problem is that there is a small jittering effect that happens when I am on the ground, where the ball is constantly going up and down as if it is being pulled by gravity and then the ball falls back into the object again.
Edit: Here is a URL to an image trying to show the kinds of problems I am having:
http://img8.imageshack.us/img8/4603/collisionproblem.png
In case the explanation in the picture doesn't make too much sense, the ball cannot move left past the corner of an object unless I jump over it. However, the ball can move right, but it gets repositioned to the right of the object while moving, which is not needed. This creates a skipping movement essentially, where it appears as the the ball is skipping over half of the object or so when I move right. If this doesn't make sense, please ask me and I'll try to clarify more.
One problem with your code is that you only detect situations like this:
If the circle happens to be fully inside the block, you don't reposition at all. And that's a problem.
You're trying to think about your simulation as if it were continuous, but keep in mind it's discrete. In general, if you only look at the current state of the ball, you really cannot know which side it collided with. Look at these two possibilities:
The first solution that comes to mind is to look at the last position of the ball as well; more precisely, look at the delta vector. See if the delta vector intersects a wall. If it does, reposition in an axis-aligned direction towards the wall intersected by the delta vector.
Edit: When I said "delta vector", I forgot that you're moving a square and not a single point. So, if you just look at the delta vector of the top-left corner, that's not going to be enough because it may not detect that part of the ball entered a block. Instead, you can look at the delta vectors of all 4 corners.