Hey, there are many ways to make this, but since im a newb myself and have never done it before, i will take up the challenge 8)
There are better ways to do this, but i think this is a pretty good start ;D
If you put the speed too high up, then its kinda gonna lag a little between each set of tile movement.
You can of course fix it by making some extra booleans about when you are able to walk again, but i wont show you since its gonna be too detailed then.
Exploiters description is a much more efficient way to make it, but its kinda hard to understand if u dont even know how to make a simple tilewalking code to work
#include <SFML/Graphics.hpp>
sf::RenderWindow mywindow(sf::VideoMode(800,600,32),"title");
sf::Event ev;
int tilesize = 50;
class character
{
public:
character()
{
x = 0;
y = 0;
// in this case, every loop, it will walk 2 pixels.
//if u put 50 as movespeed, it will walk 1 pixel each loop
movespeed = 100.0 / tilesize;
for(int i = 0; i < 4; ++i) //initialize the all move booleans to false
move[i] = false;
walking = false;
myrect.setSize(sf::Vector2f(50,50)); //size 50 by 50, same as every tile
}
void keymove(); //keypress detection
void moving(); //moving if "walking" boolean is true
float x;
float y;
float movespeed; //sets the movespeed
enum MOVE {UP,DOWN,LEFT,RIGHT}; //enums instead of remember numbers
bool move[4]; //deciding if u move up/down/left/right
bool walking;
int nextspot; //the next tilespot of the map
sf::RectangleShape myrect;
};
void character::keymove()
{
/*keymove() and moving() functions are working together*/
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
if(walking == false)
{
/*if you click up, the the nextspot will of course be 50
pixels above yourself, so thats why nextspot = y - tilsize*/
nextspot = y - tilesize;
/*this is gonna make sure you cant have move[UP] and move[LEFT]
true on the same time so u wont walk 45 degrees*/
move[UP] = true;
walking = true;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
if(walking == false)
{
nextspot = y + tilesize;
move[DOWN] = true;
walking = true;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
if(walking == false)
{
nextspot = x - tilesize;
move[LEFT] = true;
walking = true;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
if(walking == false)
{
nextspot = x + tilesize;
move[RIGHT] = true;
walking = true;
}
}
}
void character::moving()
{
if(walking == true)
{
if(move[UP] == true)
{
y -= movespeed;
/* i do <= and not just == because maybe your movespeed has a
decimalpoint and then it wont become the same number as nextspot*/
if(y <= nextspot)
{
y = nextspot;
walking = false;
move[UP] = false;
}
}
if(move[DOWN] == true)
{
y += movespeed;
if(y >= nextspot)
{
y = nextspot;
walking = false;
move[DOWN] = false;
}
}
if(move[LEFT] == true)
{
x -= movespeed;
if(x <= nextspot)
{
x = nextspot;
walking = false;
move[LEFT] = false;
}
}
if(move[RIGHT] == true)
{
x += movespeed;
if(x >= nextspot)
{
x = nextspot;
walking = false;
move[RIGHT] = false;
}
}
}
}
int main()
{
mywindow.setVerticalSyncEnabled(true); // 60 fps
character pacman; // a squared pacman
pacman.myrect.setFillColor(sf::Color(255,255,0));
while(mywindow.isOpen())
{
while(mywindow.pollEvent(ev))
{
if(ev.type == sf::Event::Closed) mywindow.close();
}
pacman.keymove();
pacman.moving();
pacman.myrect.setPosition(pacman.x,pacman.y);
mywindow.clear(sf::Color(0,200,0));
mywindow.draw(pacman.myrect);
mywindow.display();
}
}
Hope you like it :)
I'm a newbie like you, but I think using a thread is something of an 'overkill'.
I believe most games don't use threads, specially older games.
The thing is computers are so fast that there is an illusion of things happening simultaneously, when in reality they happen in an order. You should use this fact to your advantage.
Anyway, here's another version of a tile-based movement system.
const int TILE_WIDTH = 16; //we assume tile width and height are the same
struct Dir {enum Type {Up, Right, Down, Left};};
struct Actor
{
float x, y;
int gridX, gridY;
float speed;
Actor() : speed(1.0f) {} //change speed!
bool isMoving() const
{
return !(x == gridX * TILE_WIDTH && y == gridY * TILE_WIDTH);
}
void warp(int newGridX, int newGridY)
{
gridX = newGridX;
gridY = newGridY;
x = newGridX * TILE_WIDTH;
y = newGridY * TILE_WIDTH;
}
void move(const Dir::Type dir)
{
/* if actor is already moving from one tile to the other,
don't make new move */
if (isMoving())
return;
if (dir == Dir::Up)
gridY -= 1;
else if (dir == Dir::Down)
gridY += 1;
if (dir == Dir::Left)
gridX -= 1;
else if (dir == Dir::Right)
gridX += 1;
}
void update()
{
if (x < gridX * TILE_WIDTH) //moving right
x = std::min(x + speed, float(gridX * TILE_WIDTH));
else if (x > gridX * TILE_WIDTH) //moving left
x = std::max(x - speed, float(gridX * TILE_WIDTH));
if (y < gridY * TILE_WIDTH) //moving down
y = std::min(y + speed, float(gridY * TILE_WIDTH));
else if (y > gridY * TILE_WIDTH) //moving up
y = std::max(y - speed, float(gridY * TILE_WIDTH));
}
sf::Vector2f getCoors() const {return sf::Vector2f(x,y); }
};
You can use it like so:
Actor actor;
actor.warp(4, 4); //initial tile
In the loop:
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
actor.move(Dir::Right);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)){
actor.move(Dir::Left);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)){
actor.move(Dir::Up);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)){
actor.move(Dir::Down);
}
actor.update();
sprite.setPosition(actor.getCoors()); //your sf::Sprite
//do draw, etc...