Pokemon style movement

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Pokemon style movement

Post by ATS »

Hello, I'm developing a game (novelty '__' ) and in this game, I'm trying to recreate the player's movement in the style of pokemon games, but I'm having a lot of difficulty writing it, I wanted to know how you would or did this mechanics in your game.

Thank you for helping me 'v'

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: Pokemon style movement

Post by darkfrei »

ATS wrote: Sat Apr 09, 2022 9:00 pm Hello, I'm developing a game (novelty '__' ) and in this game, I'm trying to recreate the player's movement in the style of pokemon games, but I'm having a lot of difficulty writing it, I wanted to know how you would or did this mechanics in your game.

Thank you for helping me 'v'
Can you please explain the pokemon player movement system for people that have not played the pokemon?
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
Hugues Ross
Party member
Posts: 110
Joined: Fri Oct 22, 2021 9:18 pm
Location: Quebec
Contact:

Re: Pokemon style movement

Post by Hugues Ross »

I'm assuming they mean the older tile-based games, as they had a somewhat distinct system. I can recall 2 main attributes:
- 4-directional movement locked to the tile grid (ie. once you start moving between tiles, you will continue until you reach the tile even if you drop the input)
- Tapping one of the directional inputs turns your character to face in a direction instead of immediately moving

Unless I'm forgetting something, both of these should be reasonably doable. For the former, I'd have two sets of coordinates per-object. The tile grid coordinate would control the actual movement and collision checks, while a second "world-space" coordinate controls the player's rendered position. When you get a directional input, start a very short timer and start moving when it elapses (if the button is held the whole time, you can do this by resetting the timer when the input is dropped). You could also have a check to skip the timer if the input is in the same direction as the player's facing, for quality-of-life.

Once movement starts, the player should instantly warp to the target position on the tile grid (if no collision, ofc). This prevents cases where two entities try to walk into the same square around the same time. Then, you just need to interpolate the player's world-space position to the new spot and repeat. For the facing, easiest option is probably assigning each direction to a key and converting to offset coordinates from that. You can re-use that same key with a second lookup to find the correct heading in a spritesheet too!
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Re: Pokemon style movement

Post by ATS »

Hugues Ross wrote: Sat Apr 09, 2022 11:14 pm I'm assuming they mean the older tile-based games, as they had a somewhat distinct system. I can recall 2 main attributes:
- 4-directional movement locked to the tile grid (ie. once you start moving between tiles, you will continue until you reach the tile even if you drop the input)
- Tapping one of the directional inputs turns your character to face in a direction instead of immediately moving

Unless I'm forgetting something, both of these should be reasonably doable. For the former, I'd have two sets of coordinates per-object. The tile grid coordinate would control the actual movement and collision checks, while a second "world-space" coordinate controls the player's rendered position. When you get a directional input, start a very short timer and start moving when it elapses (if the button is held the whole time, you can do this by resetting the timer when the input is dropped). You could also have a check to skip the timer if the input is in the same direction as the player's facing, for quality-of-life.

Once movement starts, the player should instantly warp to the target position on the tile grid (if no collision, ofc). This prevents cases where two entities try to walk into the same square around the same time. Then, you just need to interpolate the player's world-space position to the new spot and repeat. For the facing, easiest option is probably assigning each direction to a key and converting to offset coordinates from that. You can re-use that same key with a second lookup to find the correct heading in a spritesheet too!
You described it perfectly! If it's not too much trouble, could you show me some code with this mechanic in place? Because I wanted to have an idea or even understand how to make it work =)

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Re: Pokemon style movement

Post by ATS »

darkfrei wrote: Sat Apr 09, 2022 10:44 pm
ATS wrote: Sat Apr 09, 2022 9:00 pm Hello, I'm developing a game (novelty '__' ) and in this game, I'm trying to recreate the player's movement in the style of pokemon games, but I'm having a lot of difficulty writing it, I wanted to know how you would or did this mechanics in your game.

Thank you for helping me 'v'
Can you please explain the pokemon player movement system for people that have not played the pokemon?
Read Hugues Ross' answer, he explained it perfectly. :ultrahappy:

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
User avatar
Hugues Ross
Party member
Posts: 110
Joined: Fri Oct 22, 2021 9:18 pm
Location: Quebec
Contact:

Re: Pokemon style movement

Post by Hugues Ross »

ATS wrote: Sun Apr 10, 2022 2:32 am If it's not too much trouble, could you show me some code with this mechanic in place?
Sure! It isn't the cleanest implementation, but this demo I just threw together should help give you some ideas. It's a completely playable, so you can throw this into LÖVE and try it if you'd like.

Code: Select all

local tile_x,tile_y = 10,7    -- Player's tile position
local w,h = 32,32             -- Tile width/height in pixels
local x,y = tile_x*w,tile_y*h -- Player's pixel position
local heading = 1             -- The player's in-game heading direction
local key_heading = 0         -- The heading direction the player is holding
local turn_timer = 0          -- Timer for waiting after the player turns before moving
local turn_wait = 0.1         -- How long to wait after the player turns

-- Lookup to get the heading value from a keycode
local keys = {
    left = 1,
    right = 2,
    up = 3,
    down = 4,
}

-- Lookup to get a directional vector from a heading value
local heading_vectors = {
    { -1,  0 },
    {  1,  0 },
    {  0, -1 },
    {  0,  1 },
}

function love.keypressed(key, _, isrepeat)
    if not isrepeat then
        -- When the player presses a new directional key, change the heading
        local dir = keys[key]
        if dir and dir ~= key_heading then
            key_heading = dir
        end
    end
end

function love.keyreleased(key, _)
    -- When the player releases the current heading key, reset
    local dir = keys[key]
    if dir and dir == key_heading then
        key_heading = 0
    end
end

function love.update(dt)
    if x / w ~= tile_x or x % w ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_x * w
        local sign = (dest - x) / math.abs(dest - x)
        x = x + (sign * 2)
    elseif y / h ~= tile_y or y % h ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_y * h
        local sign = (dest - y) / math.abs(dest - y)
        y = y + (sign * 2)
    else -- No need to move, handle input
        if key_heading ~= 0 then -- Directional key is held down

            -- If we need to turn, turn and start the timer
            if key_heading ~= heading then
                heading = key_heading
                turn_timer = turn_wait
            end

            -- Decrement the turn timer
            turn_timer = turn_timer - dt

            -- If we're done waiting, warp the player to the next tile position
            if turn_timer <= 0 then
                turn_timer = 0

                local vec = heading_vectors[heading]
                tile_x = tile_x + vec[1]
                tile_y = tile_y + vec[2]
            end
        end
    end
end

function love.draw()
    -- Checkerboard tiles
    local window_w,window_h = love.graphics.getDimensions()
    for i=0,math.ceil(window_w/w) do
        for j=0,math.ceil(window_h/h) do
            if (i + j) % 2 == 0 then
                love.graphics.setColor(0.1,0.1,0.15)
                love.graphics.rectangle("fill", i * w, j * h, w, h)
            end
        end
    end

    -- Draw the player
    love.graphics.setColor(1,1,1)
    love.graphics.rectangle("fill", x, y, w, h)

    -- Draw the player's heading
    local vec = heading_vectors[heading]
    local offset_x = vec[1] * (w * 0.5) + 4
    local offset_y = vec[2] * (h * 0.5) + 4
    love.graphics.setColor(1,0,0)
    love.graphics.rectangle("fill", x + (w * 0.5) + offset_x - 8, y + (h * 0.5) + offset_y - 8, 8, 8)
end
EDIT: Should also note that there's no collision-checking... it's easy to do though, you just compare two sets of tile coordinates and it's a hit if they're the same. For objects larger than 1 tile, you can probably do a point to AABB check using the tile coordinates & tile dimensions of the large object.
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Re: Pokemon style movement

Post by ATS »

Hugues Ross wrote: Sun Apr 10, 2022 12:33 pm
ATS wrote: Sun Apr 10, 2022 2:32 am If it's not too much trouble, could you show me some code with this mechanic in place?
Sure! It isn't the cleanest implementation, but this demo I just threw together should help give you some ideas. It's a completely playable, so you can throw this into LÖVE and try it if you'd like.

Code: Select all

local tile_x,tile_y = 10,7    -- Player's tile position
local w,h = 32,32             -- Tile width/height in pixels
local x,y = tile_x*w,tile_y*h -- Player's pixel position
local heading = 1             -- The player's in-game heading direction
local key_heading = 0         -- The heading direction the player is holding
local turn_timer = 0          -- Timer for waiting after the player turns before moving
local turn_wait = 0.1         -- How long to wait after the player turns

-- Lookup to get the heading value from a keycode
local keys = {
    left = 1,
    right = 2,
    up = 3,
    down = 4,
}

-- Lookup to get a directional vector from a heading value
local heading_vectors = {
    { -1,  0 },
    {  1,  0 },
    {  0, -1 },
    {  0,  1 },
}

function love.keypressed(key, _, isrepeat)
    if not isrepeat then
        -- When the player presses a new directional key, change the heading
        local dir = keys[key]
        if dir and dir ~= key_heading then
            key_heading = dir
        end
    end
end

function love.keyreleased(key, _)
    -- When the player releases the current heading key, reset
    local dir = keys[key]
    if dir and dir == key_heading then
        key_heading = 0
    end
end

function love.update(dt)
    if x / w ~= tile_x or x % w ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_x * w
        local sign = (dest - x) / math.abs(dest - x)
        x = x + (sign * 2)
    elseif y / h ~= tile_y or y % h ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_y * h
        local sign = (dest - y) / math.abs(dest - y)
        y = y + (sign * 2)
    else -- No need to move, handle input
        if key_heading ~= 0 then -- Directional key is held down

            -- If we need to turn, turn and start the timer
            if key_heading ~= heading then
                heading = key_heading
                turn_timer = turn_wait
            end

            -- Decrement the turn timer
            turn_timer = turn_timer - dt

            -- If we're done waiting, warp the player to the next tile position
            if turn_timer <= 0 then
                turn_timer = 0

                local vec = heading_vectors[heading]
                tile_x = tile_x + vec[1]
                tile_y = tile_y + vec[2]
            end
        end
    end
end

function love.draw()
    -- Checkerboard tiles
    local window_w,window_h = love.graphics.getDimensions()
    for i=0,math.ceil(window_w/w) do
        for j=0,math.ceil(window_h/h) do
            if (i + j) % 2 == 0 then
                love.graphics.setColor(0.1,0.1,0.15)
                love.graphics.rectangle("fill", i * w, j * h, w, h)
            end
        end
    end

    -- Draw the player
    love.graphics.setColor(1,1,1)
    love.graphics.rectangle("fill", x, y, w, h)

    -- Draw the player's heading
    local vec = heading_vectors[heading]
    local offset_x = vec[1] * (w * 0.5) + 4
    local offset_y = vec[2] * (h * 0.5) + 4
    love.graphics.setColor(1,0,0)
    love.graphics.rectangle("fill", x + (w * 0.5) + offset_x - 8, y + (h * 0.5) + offset_y - 8, 8, 8)
end
EDIT: Should also note that there's no collision-checking... it's easy to do though, you just compare two sets of tile coordinates and it's a hit if they're the same. For objects larger than 1 tile, you can probably do a point to AABB check using the tile coordinates & tile dimensions of the large object.
THAT'S RIGHT!!!! Thank you very much, you helped me a lot :ultrahappy: :ultrahappy: :ultrahappy: :ultrahappy: :ultrahappy:

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Re: Pokemon style movement

Post by ATS »

Hugues Ross wrote: Sun Apr 10, 2022 12:33 pm
ATS wrote: Sun Apr 10, 2022 2:32 am If it's not too much trouble, could you show me some code with this mechanic in place?
Sure! It isn't the cleanest implementation, but this demo I just threw together should help give you some ideas. It's a completely playable, so you can throw this into LÖVE and try it if you'd like.

Code: Select all

local tile_x,tile_y = 10,7    -- Player's tile position
local w,h = 32,32             -- Tile width/height in pixels
local x,y = tile_x*w,tile_y*h -- Player's pixel position
local heading = 1             -- The player's in-game heading direction
local key_heading = 0         -- The heading direction the player is holding
local turn_timer = 0          -- Timer for waiting after the player turns before moving
local turn_wait = 0.1         -- How long to wait after the player turns

-- Lookup to get the heading value from a keycode
local keys = {
    left = 1,
    right = 2,
    up = 3,
    down = 4,
}

-- Lookup to get a directional vector from a heading value
local heading_vectors = {
    { -1,  0 },
    {  1,  0 },
    {  0, -1 },
    {  0,  1 },
}

function love.keypressed(key, _, isrepeat)
    if not isrepeat then
        -- When the player presses a new directional key, change the heading
        local dir = keys[key]
        if dir and dir ~= key_heading then
            key_heading = dir
        end
    end
end

function love.keyreleased(key, _)
    -- When the player releases the current heading key, reset
    local dir = keys[key]
    if dir and dir == key_heading then
        key_heading = 0
    end
end

function love.update(dt)
    if x / w ~= tile_x or x % w ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_x * w
        local sign = (dest - x) / math.abs(dest - x)
        x = x + (sign * 2)
    elseif y / h ~= tile_y or y % h ~= 0 then
        -- If the player's pixel position hasn't reached the tile position, keep moving
        local dest = tile_y * h
        local sign = (dest - y) / math.abs(dest - y)
        y = y + (sign * 2)
    else -- No need to move, handle input
        if key_heading ~= 0 then -- Directional key is held down

            -- If we need to turn, turn and start the timer
            if key_heading ~= heading then
                heading = key_heading
                turn_timer = turn_wait
            end

            -- Decrement the turn timer
            turn_timer = turn_timer - dt

            -- If we're done waiting, warp the player to the next tile position
            if turn_timer <= 0 then
                turn_timer = 0

                local vec = heading_vectors[heading]
                tile_x = tile_x + vec[1]
                tile_y = tile_y + vec[2]
            end
        end
    end
end

function love.draw()
    -- Checkerboard tiles
    local window_w,window_h = love.graphics.getDimensions()
    for i=0,math.ceil(window_w/w) do
        for j=0,math.ceil(window_h/h) do
            if (i + j) % 2 == 0 then
                love.graphics.setColor(0.1,0.1,0.15)
                love.graphics.rectangle("fill", i * w, j * h, w, h)
            end
        end
    end

    -- Draw the player
    love.graphics.setColor(1,1,1)
    love.graphics.rectangle("fill", x, y, w, h)

    -- Draw the player's heading
    local vec = heading_vectors[heading]
    local offset_x = vec[1] * (w * 0.5) + 4
    local offset_y = vec[2] * (h * 0.5) + 4
    love.graphics.setColor(1,0,0)
    love.graphics.rectangle("fill", x + (w * 0.5) + offset_x - 8, y + (h * 0.5) + offset_y - 8, 8, 8)
end
EDIT: Should also note that there's no collision-checking... it's easy to do though, you just compare two sets of tile coordinates and it's a hit if they're the same. For objects larger than 1 tile, you can probably do a point to AABB check using the tile coordinates & tile dimensions of the large object.
One more thing, I'm using the Windfield physics library and I'm having trouble colliding with this script you wrote, how can I solve this?

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
User avatar
Hugues Ross
Party member
Posts: 110
Joined: Fri Oct 22, 2021 9:18 pm
Location: Quebec
Contact:

Re: Pokemon style movement

Post by Hugues Ross »

Unfortunately, I've never used that library. You'll have to check with someone else!
User avatar
ATS
Prole
Posts: 20
Joined: Mon Jun 28, 2021 3:25 am
Location: Brazil
Contact:

Re: Pokemon style movement

Post by ATS »

Hugues Ross wrote: Sun Apr 10, 2022 4:59 pm Unfortunately, I've never used that library. You'll have to check with someone else!
ok thank you so much for helping me ;)

Code: Select all

-------------------------------
-- i love programming in Lua --
-------------------------------
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests