Smooth Scrolling on a Gridlocked Game

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Smooth Scrolling on a Gridlocked Game

Post by leNoah »

Hi,
Currently I have a player who can move across a map like this:

Code: Select all

function love.keypressed(key)
  if key == "up" then
      yPos = yPos + 1
  end
  if key == "left" then
    xPos = xPos + 1
  end
  if key == "down" then
    yPos = yPos - 1
  end
  if key == "right" then
    xPos = xPos - 1
  end
end
When I move, the map is written to a spritebatch and drawn to the screen.
How would I make it so that the map smoothly scrolls across to the next tile position? I'd have to add an extra tile in every direction into the spritebatch, and then move it across, but I'm not sure how to do this. Any ideas?

Thanks in advance, Noah
--Home is where the heart is. Home is the ribcage.--
drunken_munki
Party member
Posts: 134
Joined: Tue Mar 29, 2011 11:05 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by drunken_munki »

Hi,

have you read this tutorial on the wiki that covers the same topic: https://love2d.org/wiki/Tutorial:Effici ... _Scrolling

Scroll down to the section titles 'Continuous Movement' which describes sub-pixel movement, this might give you some ideas.
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by leNoah »

drunken_munki wrote: Thu Oct 05, 2017 10:37 am have you read this tutorial on the wiki that covers the same topic: https://love2d.org/wiki/Tutorial:Effici ... _Scrolling
I did read that, but it covers how to use a spritebatch to do scrolling that doesn't move a tile at a time. I'm looking for an effect similar to that of the old pokemon games, where movement looks smooth between tiles but you can only ever stand on a single tile. I'm making a roguelike type game, if that helps to make any more sense to you.

So as far as I can see, I'd still need to use love.keypressed, not isDown(), and have some kind of variable that scrolls it before you can move again, but I'm not sure the best way to implement this.

Thanks for your help :)
--Home is where the heart is. Home is the ribcage.--
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by grump »

You need to decouple the display position from the logical position and allow the display position to be non-integer values. Do not set the position directly whenever a key is pressed; set a variable that says "move in this direction", and do the actual movement in love.update. Instead of moving a whole tile at a time, move only a fraction of a tile each frame, until the target position has been reached.
Terff
Prole
Posts: 5
Joined: Tue Sep 26, 2017 12:42 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by Terff »

All you have to do is offset the x and y of the draw method from the actual position of the character
heres some math, focus is the "center" of the screen, it doesn't have to be the actual center, but wherever you want the focus to be (wherever you're drawing your character, I'm guessing in your case)
-character.position (in xcoordinates and ycoordinates, not in tiles) + focus

This is assuming you already have "smooth" movement for the character, which it sounds like it is not the case.

Lets make that then shall we... I'll capitalize variables. You'll need a separate variable POSITION (which contains the x and y coordinates for your character, not in tiles but individual pixels) Lets say you want the character to move 1/10th of the tile per frame. Lets make another variable called MOVEQUEUE, this could be set up multiple ways, but lets make it a table set up like this

Code: Select all

MOVEQUEUE = {direction = {x = 0, y = 0},amount = 0}
Once you input the direction, lets use the code you have currently with a few changes

Code: Select all

function love.keypressed(key)
--this if statement holding the others makes sure you're not already moving, that would make some messy business otherwise
  if MOVEQUEUE.amount == 0 then
    if key == "up" then
    	MOVEQUEUE.direction = {x= 0,y = -1}
        MOVEQUEUE.amount = 10
    end
    if key == "left" then
        MOVEQUEUE.direction = {x= -1,y = 0}
        MOVEQUEUE.amount = 10
    end
    if key == "down" then
      	MOVEQUEUE.direction = {x= 0,y = 1}
        MOVEQUEUE.amount = 10
    end
    if key == "right" then
      	MOVEQUEUE.direction = {x= 1,y = 0}
        MOVEQUEUE.amount = 10
    end
  end
end
Another thing that might help your code be more consistent is that the point {0,0} is the upper left hand corner of your screen, adding 1 to y is going down and adding 1 to x is going right. This code will make your character go "backwards" you might need some readjustment to your tile positioning for this to work

Go into the update() method for your character, or if your character is not a separate object then just use your normal update method

Code: Select all

function CHARACTER.update()
	if MOVEQUEUE.amount > 0 then
		--This will check if the character will be moving in the bounds of your game, you could also check for tile collision here
		if xTile + MOVEQUEUE.direction.x > 0 and yTile + MOVEQUEUE.direction.y > 0 and xTile + MOVEQUEUE.direction.x < MAXTILES.x and yTile + MOVEQUEUE.direction.y < MAXTILES.y then
			MOVEQUEUE.amount = MOVEQUEUE.amount - 1
			POSITION.x = MOVEQUEUE.direction.x + TILESIZE/10
			POSITION.y = MOVEQUEUE.direction.y + TILESIZE/10
		end
	else
		if MOVEQUEUE.direction.x ~= 0 or MOVEQUEUE.direction.y ~= 0 then
			xTile = xTile + MOVEQUEUE.direction.x
			yTile = yTile + MOVEQUEUE.direction.y
			MOVEQUEUE.direction = {x = 0, y = 0}
		end
	end
end
Now you can change the draw method to draw your spritebatch

Code: Select all

	--focus is wherever you draw your character
	love.graphics.draw(SPRITEBATCH,-POSITION.x+FOCUS.x,-POSITION.y + FOCUS.y)
Hope this helped, might not be 100% correct and like I said might be off for your game due to your inverted tile coordinates, a temporary fix could be in the "MOVEQUEUE" section to change this chunk of code

Code: Select all

		if MOVEQUEUE.direction.x ~= 0 or MOVEQUEUE.direction.y ~= 0 then
			xTile = xTile + MOVEQUEUE.direction.x
			yTile = yTile + MOVEQUEUE.direction.y
			MOVEQUEUE.direction = {x = 0, y = 0}
		end
to this (changing the sign on the xTile = xTile + MOVEQUEUE.direction.x and the yTile = yTile + MOVEQUEUE.direction.y) but I'd highly recommend changing how you work with your tiles

Code: Select all

		if MOVEQUEUE.direction.x ~= 0 or MOVEQUEUE.direction.y ~= 0 then
			xTile = xTile - MOVEQUEUE.direction.x
			yTile = yTile - MOVEQUEUE.direction.y
			MOVEQUEUE.direction = {x = 0, y = 0}
		end
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by leNoah »

Thanks so much, this is exactly what I meant :)

About the tiles, I actually have changed how they work now, it was a mistake that I didn't pick up on for a while (partly due to the fact that movement wasn't smooth, so I couldn't tell what direction they were moving in :P)

I'll have a look through all of that code in more detail. Thanks again for taking the time to reply with so much detail.
--Home is where the heart is. Home is the ribcage.--
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by leNoah »

So here's the solution that I used. I declared my player like this:

Code: Select all

player = {
    x = 0,
    y = 0,
    speed = 8,
    move = function(xDif, yDif)
      player.moveQueue = {x = xDif, y = yDif}
      mapMoveStep = player.speed
    end
  }
And also created some map variables:

Code: Select all

mapOffsetX = 0
  mapOffsetY = 0
  mapMoveStep = 0
In my love.update() function, I call the playe.move function if a key is pressed (I have a table KEYS which holds a string for each key):

Code: Select all

if mapMoveStep == 0 then
    if love.keyboard.isDown(KEYS.UP) and not love.keyboard.isDown(KEYS.DOWN, KEYS.LEFT, KEYS.RIGHT) then
        player.move(0, 1)
    end
    if love.keyboard.isDown(KEYS.LEFT) and not love.keyboard.isDown(KEYS.DOWN, KEYS.UP, KEYS.RIGHT) then
      player.move(1, 0)
    end
    if love.keyboard.isDown(KEYS.DOWN) and not love.keyboard.isDown(KEYS.UP, KEYS.LEFT, KEYS.RIGHT) then
      player.move(0, -1)
    end
    if love.keyboard.isDown(KEYS.RIGHT) and not love.keyboard.isDown(KEYS.DOWN, KEYS.LEFT, KEYS.UP) then
      player.move(-1, 0)
    end
  end
And also in my love.update(), I have the map moving code, which works well. I can also change player.speed in love.load() or at any other point in order to change the movement speed:

Code: Select all

if mapMoveStep > 0 then
    mapMoveStep = mapMoveStep - 1
    mapOffsetX = mapOffsetX + (player.moveQueue.x * tileWidth / player.speed)
    mapOffsetY = mapOffsetY + (player.moveQueue.y * tileHeight / player.speed)
    
    if mapMoveStep == 0 then
      mapOffsetX, mapOffsetY = 0, 0
      player.x = player.x + -player.moveQueue.x
      player.y = player.y + -player.moveQueue.y
      
      updateTileBatch()
    end
  end
My only gripe is possible that making the speed lower makes the transition faster, but that's fine and I think I actually prefer it that way. Thanks everyone for your help :)

-Noah
--Home is where the heart is. Home is the ribcage.--
Terff
Prole
Posts: 5
Joined: Tue Sep 26, 2017 12:42 pm

Re: Smooth Scrolling on a Gridlocked Game

Post by Terff »

Glad to hear everything worked out, good luck on your game!
Post Reply

Who is online

Users browsing this forum: No registered users and 48 guests