2D Box Collisions - What am I doing wrong? (C++) - c++

I'm trying to make a platformer game in C++ and I have made a vector of blocks,
and I simply loop through the vector and check for the collision individually:
//Pseudo code
class Block{
...int x
...int y
...int width
...int height
};
class Player{
int x
int y
int width
int height
int hsp //horizontal speed
int vsp //vertical speed
int facing //0 = no direction, -1 = left, 1 = right
...
void loop()
{
if(keyboard_pressed(key_left) { x-=hsp; facing = -1;}
if(keyboard_pressed(key_right) {x+=hsp; facing = 1;}
if(keyboard_pressed(key_up) {y-=vsp;}
if(keyboard_pressed(key_down) {y+=vsp;}
if(keyboard_released(key_left | key_right) {facing = 0;}
for(int i = 0; i < blocks.size(); i++)
{
Block b = blocks.at(i);
check_Collision(b);
}
}
};
As you can see, my player simply moves according to hsp and vsp. Simple enough.
The main portion of my question is in check_Collision(). First I check to see if the player
is on top of the block, and if he is, let him stay there.
Then I check if the player is at the sides of the block.
But for some reason there's a problem. For some reason when I go under the top of the block,
he stays at the top, but then he gets shifted to the left side.
I honestly don't know where to go with this.
The following code only checks for the top and the left side:
check_Collision(){
///////////////////////////////////
var myLeft, myRight, myTop, myBot;
var bLeft, bRight, bTop, bBot;
myLeft = x;
myRight = x + width;
myTop = y;
myBot = y + height;
/////////////////////
bLeft = b.x;
bRight = b.x + b.width;
bTop = b.y;
bBot = b.y + b.height;
//////////////////////////////////
//Check if we are at the top
if(myBot + vsp > bTop+1){
y = bTop - height;
}
//Check if we are at the sides
if(myBot > bTop+2){
if(myRight + hsp > bLeft)
{
x = bLeft - width;
}
}
}
If anyone can point me into some tutorial on 2D box collision that would be great.

The logic you're using doesn't make sense to me. It's not enough just to check that the player is under the block: don't you also need to make sure that the player is standing on it, ie, isn't too far to the right or left of the block? Similarly, in your second check, you've got to make sure that the player isn't jumping over the block (or standing on it). Your if partially checks this, but doesn't take into account the fact that the first check might have modified player position.
Can you assume that the player can't ever walk under the block? Can you assume that the player will never move fast enough to "tunnel" completely through the block (hsp > b.width, etc)?
If the answer to either of these is no, you will need significantly more sophisticated collision detection.

Related

p5.js - get a rectangle to move left and right repeatedly (bounce)

I'm trying out some sample code for a bigger project, and I'm having trouble getting my rectangle to bounce between two lines.
function draw() {
print(frameCount)
background(255)
var x = 150 + frameCount;
rect(x,200,15,15);
line(150,0,150,400);
line(250,0,250,400);
if (x >= 250) {
background(255)
x = 350-frameCount;
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
} if (x <= 145) {
background(255)
x = 145 + (frameCount % 100);
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
}
}
I'm getting the feeling that after the first instance, it's disregarding the original if statement, which dictates a bounce to the left. I'm really not sure what's going wrong, and any help would be appreciated.
You probably just want to store the current position and speed in a set of variables, and then move the rectangle based on those. Here's an example:
var x = 0;
var speed = 1;
function draw(){
x += speed;
if(x < 0 || x > width){
speed *= -1;
}
background(64);
line(x, 0, x, height);
}
I've written a tutorial on this available here. That's for regular Processing, but the ideas are the same in P5.js.

Chess Validation Move input wanted

So, I have gotten quite far in my mission to finish a chess game in c++. However, I have hit a bit of a small issue I would like to get some input on, please.
SITUATION:
My PAWN, KING, KNIGHT move validations work perfect. But;
When moving a piece(such as a white ROOK) it follows most of the rules. For example, it will only move vertical or horizontal, it will not pass another white piece, it will not replace a white piece, and lastly it WILL replace a black (opposing) piece.
The problem is when moving it past a another black piece, it allows passing in order to replace a piece that's past it. So lets say we have a white piece at x=2,y=6 and black piece at x=2,y=4, and another black piece at x=2,y=3. The White piece will be allowed to move to move to x=2,y=3, which should not be allowed. Would love to get some input on how to fix this. Current code below.
bool Rook:: canMove(int startx, int starty, int endx, int endy)
{
int i;
if(board[endx][endy] !=NULL && board[endx][endy]->color==color)
return false;
if (startx == ends) //Collision Detection...
{
// Horizontal move
if (starty < endy)
{
// Move down
for (i = starty + 1; i <= endy; ++i)
if (board[startx][i] != NULL && board[startx][i]->color==color)
return false;
}
else
{
// Move up
for (i = starty - 1; i >= endy; --i)
if (board[startx][i] != NULL && board[startx][i]->color==color) //cant allow passing of non color piece
return false;
}
}
else if (starty == endy)
{
// Vertical move
if (startx < endx)
{
// Move right
for (i = startx + 1; i <= endx; ++i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
else
{
// Move left
for (i = startx - 1; i >= endx; --i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
}
else
{
// Not a valid rook move (neither horizontal nor vertical)
return false;
}
return true;
}
your function has refers to a lot of member variables in the class, e.g. ends, color, board, which isn't good, and makes the function hard to test at a unit level
can you test that function standalone? No you can't.
but it looks like your loops aren't breaking when they should (when they have found a valid move perhaps?)
if the function is allowing move to (2,3) as well as (2,4), then it is looping past (2,4) to (2,3)
also, just using an array and ints for indexing the board isn't very good.
i would have expected a higher-level board class and maybe a coordinate class so you can easily iterate and index the board.

Map joystick x and y values to left and right motor speeds

I have a joystick, It provides an x and y value ranging from + to - JOYSTICK_RANGE.
I also have a robot with 2 motors with a max speed from + to - MAX_SPEED.
const int JOYSTICK_RANGE = 127;
const int MAX_SPEED = 400;
int leftSpeed = 0;
int rightSpeed = 0;
void calculateSpeed(int x, int y) {
leftSpeed = ?;
rightSpeed = ?;
}
The end goal would be:
if the joystick is up, left and right are both max speed.
if the joystick is left it would spin counter clockwise.
if the joystick is between left and up it will turn left while moving forward.
ect
I am coding this on an Arduino Uno but all I really need help with is the algorithm unless there is some cool function or lib for this.
Thanks in advance!

Determine which tile is clicked in a window

I am drawing a tilemap on a SFML renderwindow. I want to determine which tile is clicked by the user, but I just cant seem to find a solution. First of all, each tile has 32 width and height.
What i try at the moment : Get the position of the click. Loop trough the tilemap until a tile is found which position is between 100. So if I click on (100,100) the tile should begin at (96,96) but this does not seem to work.
Here is my code snippet from the function getTile(mousepos x,mousepos y)
Tile* TileMap::getTile(int x, int y)
{
Tile *t = NULL;
for(int i = 0; i < tilemap.size(); i++)
{
for(int j = 0; j < tilemap[i].size(); j++)
{
if(x > tilemap[i][j].sprite.getPosition().x
&& x < (tilemap[i][j].sprite.getPosition().x+32))
{
if(y > tilemap[i][j].sprite.getPosition().y
&& y < (tilemap[i][j].sprite.getPosition().y+32))
{
t = &tilemap[i][j];
break;
}
}
}
}
return t;
}
Based on your code, I am going to assume that you are basing your tilemap on a 2d array of Tiles: tilemap[x][y]. I am also going to assume that tilemap[0][0] is the top left tile.
There should be a much easier way to find out which tile is being clicked on instead of testing every single tile.
If you are at 100,100 and tiles are 32x32, then we can get the x and y of the tile within the tilemap by doing something as simple as:
x = 100 / 32 = 3
y = 100 / 32 = 3
Therefor the tile in your tilemap that corresponds to a mouse position of (100,100) is tilemap[3][3].

how to avoid clutch billiard balls?

I'm working on the simple behaviour of billiard balls in a collision with each other. All works normal, but there was a problem when facing a few easy balls is the effect of coupling balls and they're cool with each other. Tell me how to prevent this.
bool MGBilliard::CollisingBall(CCPoint curr_point, CCPoint next_point)
{
float dx = next_point.x - (curr_point.x + dvdt.x);
float dy = next_point.y - (curr_point.y - dvdt.y);
float d = dx*dx+dy*dy;
return d <= BALL_RADIUS * BALL_RADIUS;
}
double MGBilliard::angleCollisionBalls(Ball* current, Ball* next)
{
double na;
double dx = fabs(next->location.x - current->location.x);
double dy = fabs(next->location.y - current->location.y);
na = atan(fabs(dy/dx));
if(atan(fabs(current->location.y/current->location.x)) < atan(fabs(next->location.y/next->location.x)))
na = current->angle - na;
else if(atan(fabs(current->location.y/current->location.x)) > atan(fabs(next->location.y/next->location.x)))
na = current->angle + na;
return na;
}
for(unsigned int i = 0;i<BALL_COUNT;++i)
{
if(vBalls[i]->speed > 0){
vBalls[i]->speed += vBalls[i]->acceleration;
float dsdt = vBalls[i]->speed*dt;
dvdt.x = dsdt*cos(vBalls[i]->angle);
dvdt.y = dsdt*sin(vBalls[i]->angle);
vBalls[i]->location.x += dvdt.x;
vBalls[i]->location.y += dvdt.y;
for(unsigned int j = 1; j < BALL_COUNT; ++j)
{
if(i == j) continue;
if(CollisingBall(vBalls[i]->spriteBall->getPosition(),vBalls[j]->spriteBall->getPosition()))
{
vBalls[j]->speed = 600;
double angle;
angle = angleCollisionBalls(vBalls[i],vBalls[j]);
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
}
}
}
}
There are two straightforward bugs that spring to my attention with a quick look at your code.
Firstly, this:
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
is not the correct way to calculate opposing angles. For example, it will not do what you want it to do when angle is zero (or 180 degrees, for that matter).
Secondly, you iterate over your whole vBalls array multiple times, once with the index i and an inner loop with the index j. This means collisions will be calculated twice, and the speed of both balls would be set to 600! Changing your inner loop to be this:
for(unsigned int j = i + 1; j < BALL_COUNT; ++j)
should prevent this occurring.
There's also a more subtle bug. Your collision detection does not take into account time. Each ball moves a particular distance each iteration of your game loop. This means that if a collision does not occur in one 'tick', a ball could pass straight through another ball and then trigger the collision code on the far side of the ball. You cannot do a simple radius-based collision test in this situation, as if a ball moves more than (BALL_RADIUS * BALL_RADIUS) in a single step, your system will behave strangely or not work at all.
I personally would use vectors to describe speed and direction for each ball rather than angles and speeds, but refactoring your code to do this is a bit outside the scope of this question.