I have been making a side scroller game and am having some issues with moving the character and updating the game world. The movement seems choppy. Right now my game loop looks like this:
void Game::gameLoop()
{
sf::Event currentEvent;
while(_gameState != Game::Exiting)
{
_mainWindow.setView(camera);
playerMove(playerPosition);
applyGravity(playerPosition);
if(_mainWindow.pollEvent(currentEvent)){
switch(_gameState)
{
case Game::ShowingSplash:
{
showSplashScreen();
break;
}
case Game::ShowingMenu:
{
_mainWindow.setView(mainView);
showMenu();
break;
}
case Game::Playing:
{
//Clear display and render world.
Game::Play();
if(currentEvent.type == sf::Event::Closed)
{
_gameState = Game::Exiting;
}
if(currentEvent.type == sf::Event::KeyPressed)
{
cameraV.x = 0;
cameraV.y = 0;
if(currentEvent.key.code == sf::Keyboard::Escape)
{
_gameState = Game::ShowingMenu;
//showMenu();
}
else if(currentEvent.key.code == sf::Keyboard::Left){
//cameraV.x = -1;
playerPosition.x -= playerSpeed;
}
else if(currentEvent.key.code == sf::Keyboard::Right){
//cameraV.x = 1;
playerPosition.x += playerSpeed;
}
else if(currentEvent.key.code == sf::Keyboard::Space){
jumping = true;
jumpPlayer(playerPosition);
jumping = false;
}
}
break;
}
}
}
}
}
and the character moves and jumps, it just seems sloppy. I am moving the characters by taking their current position and either incrementing or decrementing it.
My second question is gravity. It works right now well, but I cannot get it to move diagonally. If I am moving and I hit the jump button, the character stops moving to jump and doesn't continue to move even though the key is held down. Any help or guidance is much appreciated.
Sorry, I only posted the section I thought was relevant. Here is the entire main class.
//
// game.cpp
// Game
//
// Created by Bryan Perino on 2/26/13.
// Copyright (c) 2013 Bryan Perino. All rights reserved.
//
#include "game.h"
#include "splashscreen.h"
#include "mainmenu.h"
#include "level.h"
#include <cmath>
#include <algorithm>
#include "Player.h"
//Starts the game, displays the splash and menu screen.
static level* level_One;
float cameraSpeed = 20.0f;
float playerSpeed = .005f;
float gravity = .5;
bool jumping = false;
Player* player1;
sf::Clock* myClock;
sf::Vector2<float> playerPosition;
sf::Vector2<float> cameraV(1.0f,1.0f);
sf::View mainView; //Used for the menu
sf::View camera; //Used for the scrolling camera.
bool endRight = false;
sf::Vector2<float> cameraCenter;
int mapWidth;
int mapHeight;
Game::Bounds boundaryX; //Boundary for width
void Game::Start()
{
if(_gameState != Uninitialized)
return;
_mainWindow.create(sf::VideoMode(1024,768), "Save The Plumber");
mainView = _mainWindow.getDefaultView(); //Used for the menu.
_mainWindow.setFramerateLimit(60);
camera = _mainWindow.getDefaultView(); //Starts game with camera centered.
cameraCenter = camera.getCenter(); //Gets the default center of the camera.
_gameState = Game::ShowingSplash;
myClock = new sf::Clock();
level_One = new level();
mapWidth = level_One->getmapWidth();
mapHeight = level_One->getmapHeight();
boundaryX.max = mapWidth;
boundaryX.min = 0;
player1 = new Player();
int playerStartY = (level_One->getmapHeight() - level_One->getWorldHeight()) - (player1->playerHeight/2);
player1->playerSprite.setOrigin(player1->playerSprite.getTexture()->getSize().x/2, player1->playerSprite.getTexture()->getSize().y/2);
playerPosition.x = (player1->playerWidth)/2;
playerPosition.y = playerStartY;
player1->setPosition(playerPosition);
while(!isExiting())
{
gameLoop();
}
_mainWindow.close();
}
bool Game::isExiting()
{
if(_gameState == Game::Exiting)
return true;
else
return false;
}
void Game::gameLoop()
{
sf::Event currentEvent;
while(_gameState != Game::Exiting)
{
_mainWindow.setView(camera);
playerMove(playerPosition);
applyGravity(playerPosition);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
playerPosition.x -= playerSpeed;
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
playerPosition.x += playerSpeed;
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space)){
jumping = true;
jumpPlayer(playerPosition);
jumping = false;
}
while(_mainWindow.pollEvent(currentEvent)){
switch(_gameState)
{
case Game::ShowingSplash:
{
showSplashScreen();
break;
}
case Game::ShowingMenu:
{
_mainWindow.setView(mainView);
showMenu();
break;
}
case Game::Playing:
{
Game::Play();
if(currentEvent.type == sf::Event::Closed)
{
_gameState = Game::Exiting;
}
if(currentEvent.type == sf::Event::KeyPressed)
{
if(currentEvent.key.code == sf::Keyboard::Escape)
{
_gameState = Game::ShowingMenu;
//showMenu();
}
}
break;
}
}
}
}
}
void Game::showSplashScreen()
{
SplashScreen splashcreen;
splashcreen.Show(_mainWindow);
_gameState = Game::ShowingMenu;
}
void Game::showMenu()
{
MainMenu mainMenu;
MainMenu::MenuResult result = mainMenu.Show(_mainWindow);
switch(result)
{
case MainMenu::Exit:
_gameState = Game::Exiting;
break;
case MainMenu::Play:
_gameState = Game::Playing;
break;
}
}
void Game::Play(){
_mainWindow.clear();
level_One->drawMap(_mainWindow, player1->playerSprite);
}
//Updates View position
void Game::updatePost(sf::Vector2<float>& cameraV){
camera.move(cameraV.x * cameraSpeed, cameraV.y * cameraSpeed);
float viewLeft = camera.getCenter().x - camera.getSize().x/2;
float viewRight = camera.getCenter().x + camera.getSize().x/2;
if(viewLeft < boundaryX.min){
camera.setCenter(cameraCenter);
}
else if(viewRight >= boundaryX.max){
camera.setCenter(mapWidth - (camera.getSize().x/2),camera.getCenter().y);
endRight = true;
}
if(viewRight <= boundaryX.max){
endRight = false;
}
else if(viewLeft >= boundaryX.min){
//When there is nothing left to scroll left.
}
}
//Updates player position.
void Game::playerMove(sf::Vector2<float> &position){
float playerWidth = player1->playerWidth;
cameraV.x = 0;
cameraV.y = 0;
//Player is nearing the edge of the camera.
if(position.x + playerWidth/2 > camera.getCenter().x + (camera.getSize().x/2) - 100 && !endRight){
//Can't Scroll anymore right
player1->updatePlayer(position);
cameraV.x = 1;
updatePost(cameraV);
}
//Stop the player from moving beyond map border.
else if(position.x - playerWidth/2 < 0){
position.x = (player1->playerWidth)/2;
player1->updatePlayer(position);
}
//Stop player from moving back.
else if(position.x - playerWidth/2 < camera.getCenter().x - (camera.getSize().x/2)){
position.x = camera.getCenter().x - (camera.getSize().x/2) + playerWidth/2;
player1->updatePlayer(position);
}
//Stop player from moving out of map right.
else if(position.x + playerWidth/2 > mapWidth && endRight){
position.x = mapWidth - (playerWidth/2);
player1->updatePlayer(position);
}
else{
//Move the player normally;
player1->updatePlayer(position);
}
//std::cout << "Player position: " << player1->getPosition().x << std::endl;
}
void Game::applyGravity(sf::Vector2<float> &position){
float playerYBound = position.y + (player1->playerHeight/2);
if(playerYBound < level_One->getworldFloor()){
position.y += 5;
playerMove(position);
}
}
void Game::checkBounds(sf::Vector2<float> &position){
if(position.y - player1->playerHeight/2 <= level_One->getworldFloor()){
position.y = (level_One->getworldFloor()) + player1->playerHeight/2;
playerMove(position);
}
}
void Game::jumpPlayer(sf::Vector2<float> &position){
position.y -= 40.0f;
playerMove(position);
}
Game::GameState Game::_gameState = Uninitialized;
sf::RenderWindow Game::_mainWindow;
I moved the keyboard events outside of the event loop to check if the key is pressed down. I don't know if you wanted a more complete example. The project has a lot of files, but I could probably put something together to demonstrate what is going on.
I made a small program to represent what I am talking about. It follows the same logic that I am applying to my other game.
Main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
#include "main.h"
#include <iostream>
int main (){
Game* myGame = new Game();
myGame->Play();
return 0;
}
void Game::Play(){
//Moveable Circle Object
myCircle.setRadius(100);
myCircle.setOutlineColor(sf::Color::Red);
myCircle.setFillColor(sf::Color::Red);
sf::Vector2<float> position;
position.x = myCircle.getRadius()/2;
position.y = 20;
myCircle.setPosition(position.x, position.y);
myCircle.setOrigin(myCircle.getRadius()/2, myCircle.getRadius()/2);
//Rectangle representing floor
sf::RectangleShape myRect;
sf::Vector2<float> rectSize;
rectSize.x = 1025.0f;
rectSize.y = 400.0f;
myRect.setSize(rectSize);
myRect.setFillColor(sf::Color::Blue);
myRect.setPosition(0,400);
Game::_mainWindow.create(sf::VideoMode(1024,768), "Example");
while(Game::_mainWindow.isOpen()){
_mainWindow.clear();
applyGravity(position);
sf::Event event;
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
myCircle.setPosition(position.x += .5f, position.y);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
myCircle.setPosition(position.x -= .5f, position.y);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
myCircle.setPosition(position.x, position.y -= .5f);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space)){
jumpPlayer(position);
}
while (_mainWindow.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
_mainWindow.close();
}
_mainWindow.draw(myCircle);
_mainWindow.draw(myRect);
_mainWindow.display();
}
}
void Game::applyGravity(sf::Vector2<float> &position){
float playerYBound = position.y + (myCircle.getRadius()/2);
float mapFloor = (1024 - 728);
std::cout << "Ball Position" << position.y << std::endl;
if(playerYBound < mapFloor){
position.y += 5;
myCircle.setPosition(position);
}
}
void Game::jumpPlayer(sf::Vector2<float> &position){
position.y -= 10.0f;
myCircle.setPosition(position.x, position.y);
}
sf::CircleShape Game::myCircle;
sf::RenderWindow Game::_mainWindow;
main.h
class Game
{
public:
static void Play();
static void applyGravity(sf::Vector2<float> &position);
static sf::CircleShape myCircle;
static sf::RenderWindow _mainWindow;
static void jumpPlayer(sf::Vector2<float> &position);
};
This should create a ball that you can move around the screen. It should start in the origin (0,0) and move down to the floor of the map. The problem is that the motions seems to be jerky. I was able to fix some of the movement issues by moving the logic out of the game loop.
void Game::gameLoop()
{
sf::Event currentEvent;
while(_gameState != Game::Exiting)
{
//std::cout << "Is right being pressed " << sf::Keyboard::isKeyPressed(sf::Keyboard::Right) << std::endl;
_mainWindow.setView(camera);
playerMove(playerPosition);
applyGravity(playerPosition);
if(_gameState == Game::Playing){
Game::Play();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
playerPosition.x -= playerSpeed;
playerMove(playerPosition);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
playerPosition.x += playerSpeed;
playerMove(playerPosition);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space)){
jumping = true;
jumpPlayer(playerPosition);
jumping = false;
}
while(_mainWindow.pollEvent(currentEvent)){
switch(_gameState)
{
case Game::ShowingSplash:
{
showSplashScreen();
break;
}
case Game::ShowingMenu:
{
_mainWindow.setView(mainView);
showMenu();
break;
}
case Game::Playing:
{
//Game::Play();
if(currentEvent.type == sf::Event::Closed)
{
_gameState = Game::Exiting;
}
if(currentEvent.type == sf::Event::KeyPressed)
{
if(currentEvent.key.code == sf::Keyboard::Escape)
{
_gameState = Game::ShowingMenu;
//showMenu();
}
}
break;
}
}
}
}
}