Gravity using CircleShape SFML - c++

I am currently trying to set gravity for a split-screen game. The problem with my game is that the circles will rise once W or Up is pressed but the gravity does not seem to apply to them. Also when I move them left or right whilst in the air they disappear until I press W or Up again.
sf::Vector2f position(screenDimensions.x /2, screenDimensions.y / 2);
sf::Vector2f position2(position);
sf::Clock clock;
float moveSpeed = 0.5f , jumpSpeed = 0.3f;
while(Game.isOpen())
{
clock.restart();
sf::Event Event;
while(Game.pollEvent(Event))
{
switch(Event.type)
{
case sf::Event::Closed:
Game.close();
break;
case sf::Event::KeyPressed:
if(Event.key.code == sf::Keyboard::Escape)
Game.close();
break;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
velocity.x = moveSpeed;
circ1.move(velocity.x, velocity.y);
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
velocity.x = -moveSpeed;
circ1.move(velocity.x, velocity.y);
}
else
velocity.x = 0;
if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
velocity.x = moveSpeed;
circ2.move(velocity.x, velocity.y);
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
velocity.x = -moveSpeed;
circ2.move(velocity.x, velocity.y);
}
else
velocity.x = 0;
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
velocity.y = -jumpSpeed;
circ1.move(velocity.x, velocity.y);
}
else
if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
velocity.y = -jumpSpeed;
circ2.move(velocity.x, velocity.y);
}
if (circ1.getPosition().x + 10 >= view1.getSize().x / 2)
position.x = circ1.getPosition().x + 10;
else
position.x = view1.getSize().x / 2;
if (circ2.getPosition().x + 10 >= view2.getSize().x / 2)
position2.x = circ2.getPosition().x + 10;
else
position2.x = view2.getSize().x / 2;
if (circ1.getPosition().y + circ1.getRadius() < groundHeight || velocity.y > 0)
{
velocity.y += Gravity;
}
else
{
circ1.setPosition(circ1.getPosition().x, groundHeight - circ1.getRadius());
velocity.y = 0;
}
view1.setCenter(position);
view2.setCenter(position2);
Game.setView(view1);
Game.draw(bImage);
Game.draw(circ1);
Game.draw(circ2);
Game.setView(view2);
Game.draw(bImage);
Game.draw(circ2);
Game.draw(circ1);
Game.display();
Game.clear();
}
}
Help is appreciated Thank you.

If you want "jumping" per se, then you need to keep the user from jumping while already in the air. This could be accomplished by creating a bool jumping=false; at the beginning and then setting jumpingto true when a jump is initialized. Then, each time the program loops, if (jumping && floor.getGlobalBounds().intersects(circ1) || circ.getPosition().y<0){jumping=false;velocity.y=0;}.
Otherwise, you might try changing
if (circ1.getPosition().y + circ1.getRadius() < groundHeight || velocity.y > 0)
{
velocity.y += Gravity;
}
to
if (circ1.getPosition().y + circ1.getRadius() > groundHeight || velocity.y > 0)
{
velocity.y += Gravity;
}

Related

Is there a way I can keep something at a constant speed in C++ game?

I currently have a game where there is a player, enemies, and projectiles. The user can spawn projectiles to damage enemies. However, the problem that I am having, is that the enemies speed up the more projectiles the user spawns in a row. I tried to use a game clock to keep everything constant and my game loop is below.
while (window.isOpen())
{
std::chrono::high_resolution_clock::time_point begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < enemies.size(); i++)
{
enemies[i].move(begin, HEIGHT, WIDTH, player);
}
for (size_t i = 0; i < player.projectiles.size();)
{
if (player.projectiles[i].move(begin, WIDTH))
{
player.projectiles.erase(player.projectiles.begin() + i);
}
else
{
i++;
}
}
for (int i = 0; i < enemies.size();)
{
for (int j = 0; j < player.projectiles.size();)
{
if (enemies[i].hit(player.projectiles[j]))
{
player.projectiles.erase(player.projectiles.begin() + j);
}
else
{
j++;
}
}
if (enemies[i].hp <= 0)
{
enemies.erase(enemies.begin() + i);
}
else
{
i++;
}
}
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
break;
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Right))
{
enemies.push_back(Enemy(100.0, 1.0, Mouse::getPosition().x, Mouse::getPosition().y, Color::White));
}
if (event.type == Event::KeyPressed)
{
if (event.key.code == player.up)
{
player.moving_up = true;
}
else if (event.key.code == player.down)
{
player.moving_down = true;
}
else if (event.key.code == player.right)
{
player.moving_right = true;
}
else if (event.key.code == player.left)
{
player.moving_left = true;
}
else if (event.key.code == player.shoot)
{
player.shooting = true;
}
if (event.key.code == player.exit)
{
window.close();
}
}
if (event.type == Event::KeyReleased)
{
if (event.key.code == player.up)
{
player.moving_up = false;
}
else if (event.key.code == player.down)
{
player.moving_down = false;
}
else if (event.key.code == player.right)
{
player.moving_right = false;
}
else if (event.key.code == player.left)
{
player.moving_left = false;
}
else if (event.key.code == player.shoot)
{
player.shooting = false;
}
}
player.input(begin, HEIGHT, WIDTH);
for (int i = 0; i < enemies.size();)
{
for (int j = 0; j < player.projectiles.size();)
{
if (enemies[i].hit(player.projectiles[j]))
{
player.projectiles.erase(player.projectiles.begin() + j);
}
else
{
j++;
}
}
if (enemies[i].hp <= 0)
{
enemies.erase(enemies.begin() + i);
}
else
{
i++;
}
}
}
window.clear();
player.draw(window);
for (int i = 0; i < enemies.size(); i++)
{
enemies[i].draw(window);
}
window.display();
}
The enemy.move function looks like this, where speed is set to 1.0:
void Enemy::move(std::chrono::high_resolution_clock::time_point begin, float height, float width, Player player)
{
std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
if (player.x < this->x)
{
if (this->x - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) < 0)
{
this->x = 0;
}
else
{
this->x = this->x - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
if (player.y < this->y)
{
if (this->y - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) < 0)
{
this->y = 0;
}
else
{
this->y = this->y - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
}
else
{
if (this->y + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) > (height - double(this->sprite.getSize().y)))
{
this->y = height - double(this->sprite.getSize().y);
}
else
{
this->y = this->y + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
}
}
else
{
if (this->x + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) > (width - double(this->sprite.getSize().x)))
{
this->x = width - double(this->sprite.getSize().x);
}
else
{
this->x = this->x + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
if (player.y < this->y)
{
if (this->y - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) < 0)
{
this->y = 0;
}
else
{
this->y = this->y - (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
}
else
{
if (this->y + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0) > (height - double(this->sprite.getSize().y)))
{
this->y = height - double(this->sprite.getSize().y);
}
else
{
this->y = this->y + (enemy_speed * (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) / 1000.0);
}
}
}
this->sprite.setPosition(this->x, this->y);
}
I apologize for the long segments of code but these are the relative parts and I don't know specifically what pieces of code you may need. Any help is greatly appreciated.
Bascially, to move something at a constant rate, you want to base the distance moved based on the elapsed time since you previously moved that object, not the elapsed time since the beginning of the current cycle, like you are doing here.
So your move function should be something like:
void Enemy::move(float height, float width, Player player) {
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
// various code that moves by enemy_speed * (now - prev_time)
prev_time = now;
}
where prev_time is a field in the enemy object where you store the previous timestamp of movement. You need to make sure you initialize it to an inital time properly, or the first move will "jump" unexpectedly.
You can reduce the number of times you query the clock by having a single timestamp for all the enemies/objects and passing in the elapsed time since the previous timestamp to all the move routines.

C++/SFML Super Mario game collision problems with blocks

I was making a super-mario-like game in C++/SFML and while trying to write some code for Mario collisions, I had some problems: The character, when collides with a vertical stack of blocks while moving along the horizontal axis, get stuck on one side of the blocks, like if it is walking on an invisible block. I tried to modify the collision function like making mario to collide with blocks horizontally only if his position related to blocks is contained into the block coordinates.
I include some code for the movement(keyPressed is a function that returns the key pressed):
void Player::movement(Time& gameTime) {
player.move(v * gameTime.asSeconds());
view.setCenter(player.getPosition().x + 16, 590);
if (keyPressed(up) && !jumping) {
jumping = true;
v.y = jumpSpeed;
}
if (v.y > 200) {
jumping = true;
}
else {
crouch = false;
}
if (keyPressed(left)) {
if (v.x > -moveSpeed) {
v.x -= 100;
}
else {
v.x = -moveSpeed;
}
}
else if (keyPressed(right)) {
noKeyPressed = false;
if (v.x < moveSpeed) {
v.x += 100;
}
else {
v.x = moveSpeed;
}
}
else {
if (v.x < -100) {
v.x += 100;
}
else if (v.x > 100) {
v.x -= 100;
}
else {
v.x = 0;
}
}
gravity();
if (big) { //Big is a variable that tells me if mario is big or small
heightValue = 33; //heightValue is a variable that stores the sprite height in pixels
}
else {
heightValue = 16;
}
}
void Player::gravity() {
if (!big) {
if (player.getPosition().y + 32 < 1100) {
if (v.y < maxSpeed) {
v.y += 100;
}
}
if (alive) { //This is useful to check if big mario has fallen
if (player.getPosition().y + 32 >= 1200) {
player.setPosition(player.getPosition().x, 1200 - 32);
jumping = false;
alive = false;
}
}
}
else {
if (player.getPosition().y + 64 < 1100) {
if (v.y < maxSpeed) {
v.y += 100;
}
}
if (alive) { //This is useful to check if small mario has fallen
if (player.getPosition().y + 64 >= 1200) {
player.setPosition(player.getPosition().x, 1200 - 64);
jumping = false;
alive = false;
}
}
}
}
And the collision function, where the block class has 4 small blocks around the block sprite that simplify collisions:
void Player::collisions(Block* block) {
if (this->player.getGlobalBounds().intersects(block->up.getGlobalBounds())) {
if (!big) {
if (this->player.getPosition().y + heightValue <= block->block.getPosition().y) {
this->player.setPosition(this->player.getPosition().x, block->up.getPosition().y - 32);
v.y = 0;
jumping = false;
score = 100;
}
}
else {
if (this->player.getPosition().y + heightValue <= block->block.getPosition().y) {
this->player.setPosition(this->player.getPosition().x, block->up.getPosition().y - 64);
v.y = 0;
jumping = false;
score = 100;
}
}
}
if (this->player.getGlobalBounds().intersects(block->down.getGlobalBounds())) {
this->player.setPosition(this->player.getPosition().x, block->down.getPosition().y + 1);
v.y = 0;
}
if (this->player.getGlobalBounds().intersects(block->left.getGlobalBounds()) && v.x > 0) {
this->player.setPosition(block->left.getPosition().x - 32, this->player.getPosition().y);
}
else if (this->player.getGlobalBounds().intersects(block->right.getGlobalBounds()) && v.x < 0) {
this->player.setPosition(block->right.getPosition().x + 1, this->player.getPosition().y);
}
}
I hope I explained accurately the problem.
simply do your collision detection one axis at a time, this allows nice smooth movement along tiles
pseudocode example:
//X axis
oldPos = position
if(leftKey):
position.x -= speed
if(rightKey):
position.x += speed
if(collision):
position = oldPos
//Y axis
oldPos = position
if(upKey):
position.y -= speed
if(downKey):
position.y += speed
if(collision):
position = oldPos

Gravity and collision detection in SFML

This is my first attempt with SFML and game development, and I'm having issues with the collision and gravity.
I'm making a 2D platformer game using a tilemap system.
Collision seems to be working(slightly) but is very choppy and I just can't seem to get gravity to work properly. I've tried a few different tutorials but I cant get anything working and I'm at a bit of a loss right now.
If someone could point out what I'm missing here it'd be greatly appreciated!!
Heres the code I'm using for these elements:
In PlayerSprite class(attempt at gravity)
PlayerSprite::PlayerSprite(const sf::Vector2f& size) : AnimatedSprite(size)
{
playerPos = (sf::Vector2f(300.0f, 400.0f));
dead = false;
jumpHeight = 5.f;
scale = 50.f;
accelGravity = 0.5f;
maxGravity = 5.f;
velocity.x = 2.0f;
velocity.y = 2.0f;
playerTexture.loadFromFile("gfx/spritemansheet.png");
setSize(sf::Vector2f(48, 48));
setPosition(playerPos);
setTexture(&playerTexture);
}
PlayerSprite::~PlayerSprite()
{
}
void PlayerSprite::update(float dt)
{
onGround = false;
if (input->isKeyDown(sf::Keyboard::A)) {
input->setKeyUp(sf::Keyboard::A);
playerPos.x -= (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &walkBack;
}
if (input->isKeyDown(sf::Keyboard::D)) {
input->setKeyUp(sf::Keyboard::D);
playerPos.x += (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &walk;
}
if (input->isKeyDown(sf::Keyboard::W) ) {
input->setKeyUp(sf::Keyboard::Space);
playerPos.x += (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &jump;
velocity.y = -5.f * -1;
}
if (onGround == false) {
velocity.y += accelGravity;
if (velocity.y > maxGravity) {
velocity.y = maxGravity;
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
input->setMouseLeftUp(sf::Mouse::Left);
currentAnimation = &attack;
}
}
void PlayerSprite::setInput(Input* newInp)
{
input = newInp;
}
void PlayerSprite::collisionResponse(Sprite* sp)
{
if (velocity.x > 0) {
velocity.x = 0.f;
}
if (velocity.x < 0) {
velocity.x = 0.f;
}
if (velocity.y > 0) {
velocity.y = 0.f;
onGround = true;
}
if (velocity.y < 0) {
velocity.y = 0.f;
}
setPosition(getPosition().x, sp->getPosition().y - getSize().y);
}
Collision Detection in Game.cpp
void Game::update(float dt)
{
player.update(dt);
manager.update(dt);
std::vector<Tile>* world = worldMap.getScene();
for (int i = 0; i < (int)world->size(); i++) {
if ((*world)[i].isAlive()) {
if (checkGroundBounding(&player, &(*world)[i])) {
player.collisionResponse(&(*world)[i]);
}
}
}
void Game::render() {
beginDraw();
window->draw(bg);
window->draw(player);
manager.render(window);
worldMap.render(window);
endDraw();
}
bool Game::checkGroundBounding(PlayerSprite* b1, Tile* b2)
{
//get radius of sprites
float r1 = b1->getSize().x / 2;
float r2 = b2->getSize().x / 2;
float xposb1 = b1->getPosition().x + r1;
float xposb2 = b2->getPosition().x + r2;
float yposb1 = b1->getPosition().y + r1;
float yposb2 = b2->getPosition().y + r2;
if (pow(xposb2 - xposb1, 2) + pow(yposb2 - yposb1, 2) < pow(r1 + r2, 2)) {
return true;
}
return false;
}
you're collision is super wonky, simply set a oldPos variable to your player position, then do your movement code but only for the X axis, if collision is detected, set the position to oldPos, then set oldPos to the current position again, and repeat for the Y axis:
Pseudocode example:
//X axis
oldPos = position
if(leftKey):
position.x -= speed
if(rightKey):
position.x += speed
if(collision):
position = oldPos
//Y axis
oldPos = position
if(upKey):
position.y -= speed
if(downKey):
position.y += speed
if(collision):
position = oldPos
also you can replace your tile iteration with:
for(Tile t : *world){
t->doStuff();
}
I also don't recommend declaring your tile vector in your update function unless you hate performance

Implementing a collision in 2D platform game ( Icy Tower )

I'm fighting with this for like few days, and I have no idea, how to do that, so I'd like to ask you for help. I've got no idea how collision should look like right here, so player could jump through 'down zone' of the block, and stay right on the block.
block.cpp
bool block::CollidingWithPlayer(character& player) {
for (int i = 1; i < MAX_BLOCKS; i++) {
if (player.x + player.width >= coordinateX[i] && player.x <= coordinateX[i] + width[i] && player.y + player.height >= coordinateY[i] && player.y <= coordinateY[i] + block_height) {
player.onGround = true;
return true;
}
}
}
character.cpp
void character::startJump(map& Map, character& player) {
if (onGround)
{
vel[1] = -11.0;
onGround = false;
}
}
void character::updateJump(block& Block, character& player) {
if (!onGround) {
Block.CollidingWithPlayer(player);
vel[1] += 0.5;
y += vel[1];
x += vel[0];
}
if (y > 460){
y = 460;
vel[1] = 0.0;
onGround = true;
vel[0] = 0.0;
}
if ((x + width >= START_OF_RIGHT_WALL && x <= WALL_WIDTH + START_OF_RIGHT_WALL) || (x + width >= START_OF_LEFT_WALL &&x <= START_OF_LEFT_WALL + WALL_WIDTH)){
vel[0] *= -1;
bound = true;
if (direction == 1)
direction = 2;
else if (direction == 2)
direction = 1;
}
}

Velocity increases

When I move my object around its starts really slow and then begins to accelerate. I want a constant velocity but I have no idea what is wrong
const float m_Walkspeed = 0.1;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) && m_position.x > 0)
{
m_velocity.x -= m_Walkspeed;
CurrentAnimation = &AnimationLeft;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && m_position.x < 800)
{
m_velocity.x += m_Walkspeed;
CurrentAnimation = &AnimationRight;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) && m_position.y > 0)
{
m_velocity.y -= m_Walkspeed;
CurrentAnimation = &AnimationUp;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) && m_position.y < 560)
{
m_velocity.y += m_Walkspeed;
CurrentAnimation = &AnimationDown;
}
m_position += m_velocity;
m_velocity = sf::Vector2f(0, 0);
CurrentAnimation->setPosition(m_position);
}
Supposing that you call this code at each frame, you have to consider the time since last frame:
m_velocity += timeSinceLastFrame;
m_position += m_velocity;