Page 1 of 2
Pokemon style movement
Posted: Sat Apr 09, 2022 9:00 pm
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'
Re: Pokemon style movement
Posted: Sat Apr 09, 2022 10:44 pm
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?
Re: Pokemon style movement
Posted: Sat Apr 09, 2022 11:14 pm
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!
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 2:32 am
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 =)
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 2:34 am
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.
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 12:33 pm
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.
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 2:52 pm
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
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 4:13 pm
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?
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 4:59 pm
by Hugues Ross
Unfortunately, I've never used that library. You'll have to check with someone else!
Re: Pokemon style movement
Posted: Sun Apr 10, 2022 5:20 pm
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