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;
Related
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
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
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;
}
}
I've implemented tilemap collision into my game, it works but the problem comes when I'm colliding on one axis and trying to move on the other. I can't slide along the wall.
in Player.cpp
void Player::update(float delta, std::vector<Tile>& tiles) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) < -20) {
newPos.y -= speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) < -20) {
newPos.x -= speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) > 20) {
newPos.y += speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) > 20) {
newPos.x += speed * delta;
}
sf::Vector2f oldPos = sprite.getPosition();
move(delta, newPos);
for (int i = 0; i < tiles.size(); i++) {
if (Collision::PixelPerfectTest(sprite, tiles[i].sprite) && tiles[i].collision) {
sprite.setPosition(oldPos);
newPos = oldPos;
}
}
}
void Player::move(float delta, sf::Vector2f position) {
sprite.setPosition(position);
}
In Collision.cpp
bool PixelPerfectTest(const sf::Sprite& Object1, const sf::Sprite& Object2, sf::Uint8 AlphaLimit) {
sf::FloatRect Intersection;
if (Object1.getGlobalBounds().intersects(Object2.getGlobalBounds(), Intersection)) {
sf::IntRect O1SubRect = Object1.getTextureRect();
sf::IntRect O2SubRect = Object2.getTextureRect();
sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());
sf::Uint8* mask2 = Bitmasks.GetMask(Object2.getTexture());
// Loop through our pixels
for (int i = Intersection.left; i < Intersection.left + Intersection.width; i++) {
for (int j = Intersection.top; j < Intersection.top + Intersection.height; j++) {
sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(i, j);
sf::Vector2f o2v = Object2.getInverseTransform().transformPoint(i, j);
// Make sure pixels fall within the sprite's subrect
if (o1v.x > 0 && o1v.y > 0 && o2v.x > 0 && o2v.y > 0 &&
o1v.x < O1SubRect.width && o1v.y < O1SubRect.height &&
o2v.x < O2SubRect.width && o2v.y < O2SubRect.height) {
if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x) + O1SubRect.left, (int)(o1v.y) + O1SubRect.top) > AlphaLimit &&
Bitmasks.GetPixel(mask2, Object2.getTexture(), (int)(o2v.x) + O2SubRect.left, (int)(o2v.y) + O2SubRect.top) > AlphaLimit)
return true;
}
}
}
}
return false;
}
That's because your collision test is all or nothing. I would do extra collision tests to see if the x or y new position is valid or not, something like:
if (tiles[i].collision && Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
sf::Vector2f checkPosX = newPos;
sf::Vector2f checkPosY = newPos;
checkPosX.y = oldPos.y;
checkPosY.x = oldPos.x;
sprite.setPosition(checkPosX);
if (!Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
newPos = checkPosX;
}
else
{
sprite.setPosition(checkPosY);
if (!Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
newPos = checkPosY;
}
else
{
sprite.setPosition(oldPos);
newPos = oldPos;
}
}
}
As an aside, if you do test tiles[i].collision first you will skip the more expensive PixelPerfectTest() test for non-collision tiles due to the expression short-circuiting.
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;
}