Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Grid-based sprite movement  (Read 16724 times)

0 Members and 1 Guest are viewing this topic.

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Grid-based sprite movement
« on: November 07, 2012, 04:56:35 pm »
Hello all,

Well, I think it's not specifically related to SFML, but I'm sure that some other beginners might run into this problem as well, so at least it's not completely off-topic...

Since my game world is constructed from square tiles, I want all the actor sprites to move tile by tile, not pixel by pixel. Well, just moving them by jumping from one tile to another is easy, but I don't know how to translate them smoothly, just like in basically all 2D RPGs. So even if a movement key is pressed and released immediately, the sprite would smoothly "walk" to the next tile, not just move by a few pixels and stop immediately when the key has been released.

Does anybody know a tutorial about this, or have some code to share? The only tutorial I've found was implemented in XNA, and didn't even work on my machine... Thank you in advance!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Grid-based sprite movement
« Reply #1 on: November 07, 2012, 05:48:23 pm »
Well there are hundred different ways to achieve what you want. ;)

The basic idea behind is that you don't really make the movement depended on the keyboard input, but just 'trigger' the movement mechanism once a key is pressed. The movement then needs to be calculated framerate independently until the entity is at the new tile position. The calculation is a simple movement formula, e.g. sprite.move(direction.x * speed.x * dt, direction.y * speed.y * dt), where direction is -1, 0 or 1 depended on the key input, speed is a fixed or variable value and dt is the delta time. Then you simply check if the entity has moved far enough with some tolerance and if so you hard set it on the tile position.

I hope this gives you a bit of an idea. :)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Assassinbeast

  • Jr. Member
  • **
  • Posts: 62
    • View Profile
    • Email
Re: Grid-based sprite movement
« Reply #2 on: November 07, 2012, 07:21:02 pm »
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  :)
« Last Edit: November 07, 2012, 07:55:42 pm by Assassinbeast »

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Grid-based sprite movement
« Reply #3 on: November 07, 2012, 08:05:22 pm »
Thank you all, I'll try to implement something based on your suggestions as soon as I have some time again.  ;)

didii

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: Grid-based sprite movement
« Reply #4 on: November 08, 2012, 03:38:01 am »
An extra question about this. I'll 'soon' try to make a tile based game too, but don't want to hang the complete game when trying to move your character. Is it a good idea to trigger a thread dedicated to move the player? So that the scenery or such can still be displayed correctly? I never worked with threads, and don't know the ups and downs for it.

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Grid-based sprite movement
« Reply #5 on: November 08, 2012, 07:29:53 am »
It could work, but it brings complexity you don't need.

You can do two things without a thread. Just do a little bit of each task on each frame, like in the code AssassinBeat posted. Moving a guy won't freeze anything, and you can still add stuff to update other things, all without needing another thread.

Now, threads aren't always bad ; but for something simple like this, they'll probably add synchronization issues that aren't worth the benefits.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Grid-based sprite movement
« Reply #6 on: November 08, 2012, 07:39:28 am »
I'll 'soon' try to make a tile based game too, but don't want to hang the complete game when trying to move your character. Is it a good idea to trigger a thread dedicated to move the player?
Why would you believe that the the game will hang? ;)
Moving one object is a very cheap operation, so you won't run into performance issues. And after ou've moved the player, simply adjust the sf::View to move the world with the player.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

didii

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: Grid-based sprite movement
« Reply #7 on: November 08, 2012, 02:11:57 pm »
Why would you believe that the the game will hang? ;)
Moving one object is a very cheap operation, so you won't run into performance issues. And after ou've moved the player, simply adjust the sf::View to move the world with the player.
Oh, I meant moving the player with an animation for let's say a second. Thus if I had a function Update() that updates the animations of all scenery. And a function Player.Move() (which takes a second to go from tile A to tile B) that executes when a specific button is pressed. Both in the main loop of course. This means that if the specific button is pressed the game gets inside the Move()-function and thus won't execute Update() for a whole second, thus freezing the game apart from the player.
Or would I need to handle it completely different?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Grid-based sprite movement
« Reply #8 on: November 08, 2012, 02:37:23 pm »
Or would I need to handle it completely different?
Yes ;D

Of course you can just do one thing at a time, but like you noticed you'd only be able to do one thing at a time... ;)
You should learn a bit more about how to handle things, e.g. start by looking at the pong example that comes with SFML.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

didii

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: Grid-based sprite movement
« Reply #9 on: November 08, 2012, 03:51:55 pm »
Of course you can just do one thing at a time, but like you noticed you'd only be able to do one thing at a time... ;)
You should learn a bit more about how to handle things, e.g. start by looking at the pong example that comes with SFML.
So if I understand it correctly: I'll need to use basic physics like speed and acceleration. I should be good at that :) It seems however more logical to launch a separate thread and if I want to do something new to 1) wait for it to finish or 2) jump to the location and terminate the thread.
Mind that I'm thinking for the moment solely on a turn- and grid-based game. In real-time games/applications I can see the negative side of threads (like indeed timing and ehm.. what else?) but for this kind of game I don't see why not.

You can do two things without a thread.
Then what are the advantages/disadvantages of using threads apart from the synchronization problem?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Grid-based sprite movement
« Reply #10 on: November 08, 2012, 04:13:43 pm »
It seems however more logical to launch a separate thread
Not if you have worked a bit with threads...

If I want to do something new to 1) wait for it to finish or 2) jump to the location and terminate the thread.
No never force-terminate a thread. You should always let the thread finish his work first, otherwise you might run into undefined behavior and other evil stuff.

Then what are the advantages/disadvantages of using threads apart from the synchronization problem?
You make it sound like so easy, but you probably have no idea what it's all about. ;)
Parallel Programming is a lecture on its own at my university, so as you see the topic is not trivial, but everything highly depends on what you do.

Mind that I'm thinking for the moment solely on a turn- and grid-based game. In real-time games/applications I can see the negative side of threads (like indeed timing and ehm.. what else?) but for this kind of game I don't see why not.
Well the overhead is not only important in time critical applications, e.g. the starting of a thread will have a slight delay of up to a few hundred ms, which is not just nothing regardless of the game.
Like I said, it seems you've not much of an idea what you're getting yourself into. Especially for low performance games there's no reason at all to use many different threads, there just aren't any benefits doing so. ;)

But I'm not stopping you from doing whatever you want, so go ahead implement it and you'll probably start to understand... :)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

kaB00M

  • Full Member
  • ***
  • Posts: 101
    • View Profile
    • Caffeware
    • Email
Re: Grid-based sprite movement
« Reply #11 on: November 09, 2012, 01:22:22 am »
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...
 
« Last Edit: November 13, 2012, 09:13:34 pm by kaB00M »



didii

  • Full Member
  • ***
  • Posts: 122
    • View Profile
Re: Grid-based sprite movement
« Reply #12 on: November 10, 2012, 02:38:11 pm »
But I'm not stopping you from doing whatever you want, so go ahead implement it and you'll probably start to understand... :)
Will do :) thanks for the explanations!