How to avoid drawing map every single tick

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.
User avatar
Kadoba
Party member
Posts: 399
Joined: Mon Jan 10, 2011 8:25 am
Location: Oklahoma

Re: How to avoid drawing map every single tick

Post by Kadoba » Thu Aug 23, 2012 8:47 pm

ATL has spritebatch support built in. You just need to change map.useSpriteBatch to true and it should do the rest automatically. Canvas support is something I'm hoping to put into next version but I'm waiting for Tiled 0.9.0 to come out before I start breaking things since ATL needs another major overhaul.

But I don't think ATL is the problem in this situation. I get 33 FPS normally but it jumps up to about 170 FPS if I comment out your love.update. It jumps up to 320 FPS if I set the map to use sprite batches.

User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: How to avoid drawing map every single tick

Post by Roland_Yonaba » Thu Aug 23, 2012 10:00 pm

Kadoba wrote:But I don't think ATL is the problem in this situation...
Definitely not

@munchor: I guess you have collision detection problems mostly because of how the way you perform it, actually.
When a rectangle shape is moving right, for instance, collision checks need to be performed on the right edge.
Generally, people just checks for collision on the upper-right AND down-right corners.
I made a picture, to explain. Yeah, it's a crap, but I hope you'll get my point.

Also, maybe you might want to reconsider the way you get solid tiles.
Actually, what you are doing is looping through all tiles, to build a list of those who are collidable before entering the game loop.
Then each time the player moves, you basically tests if the tiles where the player is trying to is listed inside these collidables tiles or not.

Code: Select all

function getSolidTiles(map)
  local collidableTiles = {}
  local layer = map.tl["map"]
  for tileX, tileY, tile in layer.tileData:iterate() do
    if tile and tile.properties.type == "block" then
      local tile = {}
      tile.x = tileX
      tile.y = tileY
      table.insert(collidableTiles, tile)
    end
  end
  return collidableTiles
end

  currentMap.isSolid = function(x, y)
    for i = 1, #currentMap.solidTiles do
      if x == currentMap.solidTiles[i].x and y == currentMap.solidTiles[i].y then
        return true
      end
    end

currentMap.solidTiles = getSolidTiles(currentMap)
Well, it might work, but this is pretty costly. Fact is, it might be the other reason to the performance drop you are running to.
I suggest you check ATL's API. I am pretty sure there is a method to check if tile is walkable or not, and very simply.
Attachments
test.png
test.png (22.47 KiB) Viewed 1911 times

munchor
Prole
Posts: 39
Joined: Sun Jun 10, 2012 4:28 pm

Re: How to avoid drawing map every single tick

Post by munchor » Fri Aug 24, 2012 2:39 pm

Roland_Yonaba wrote:
Kadoba wrote:But I don't think ATL is the problem in this situation...
Also, maybe you might want to reconsider the way you get solid tiles.
Actually, what you are doing is looping through all tiles, to build a list of those who are collidable before entering the game loop.
Then each time the player moves, you basically tests if the tiles where the player is trying to is listed inside these collidables tiles or not.
And what is wrong with that? I think it's fine :S

Regarding the collision, I tried to do both edges, and the collision errors are exactlyy the same:

Code: Select all

  if player.vx > 0 then
    for i = 0, player.vx - 1 do
      if currentMap.isSolid(player.getTile(player.x + 16, player.y, 16, 16)) and currentMap.isSolid(player.getTile(player.x + 16, player.y + 16, 16, 16)) then
        player.vx = 0
      else
        player.x = player.x + 1
        player.translated = player.translated + 1
      end
    end
  else
    for i = 0, math.abs(player.vx) - 1 do
      if currentMap.isSolid(player.getTile(player.x - 1, player.y, 16, 16)) and currentMap.isSolid(player.getTile(player.x - 1, player.y + 16, 16, 16)) then
        player.vx = 0
      else
        player.x = player.x - 1
        player.translated = player.translated - 1
      end
    end
  end

  if player.vy > 0 then
    for i = 0, player.vy - 1 do
      if currentMap.isSolid(player.getTile(player.x, player.y + 16, 16, 16)) and currentMap.isSolid(player.getTile(player.x + 16, player.y + 16, 16, 16)) then
        player.vy = 0
      else
        player.y = player.y + 1
      end
    end
  else
    for i = 0, math.abs(player.vy) - 1 do
      if currentMap.isSolid(player.getTile(player.x, player.y - 1, 16, 16)) and currentMap.isSolid(player.getTile(player.x + 16, player.y - 1, 16, 16)) then
        player.vy = 0
      else
        player.y = player.y - 1
      end
    end
  end
Edit
So, instead of checking first and last edge, I check all edge pixels, and it works just fine :)

Code: Select all

  if player.vx > 0 then
    for i = 0, player.vx - 1 do
      if player.collidingRight(player.currentMap) then
        player.vx = 0
      else
        player.x = player.x + 1
        player.translated = player.translated + 1
      end
    end
  else
    for i = 0, math.abs(player.vx) - 1 do
      if player.collidingLeft(player.currentMap) then
        player.vx = 0
      else
        player.x = player.x - 1
        player.translated = player.translated - 1
      end
    end
  end

  if player.vy > 0 then
    for i = 0, player.vy - 1 do
      if player.collidingBelow(player.currentMap) then
        player.vy = 0
      else
        player.y = player.y + 1
      end
    end
  else
    for i = 0, math.abs(player.vy) - 1 do
      if player.collidingAbove(currentMap) then
        player.vy = 0
      else
        player.y = player.y - 1
      end
    end
  end
I still feel like it could be optimized, but I'm not sure.

User avatar
Kadoba
Party member
Posts: 399
Joined: Mon Jan 10, 2011 8:25 am
Location: Oklahoma

Re: How to avoid drawing map every single tick

Post by Kadoba » Fri Aug 24, 2012 3:32 pm

It can be done much easier if you just crate a 2d array for blocked tiles. Iterating over and checking every blocked tile is much less efficient.

Code: Select all

local solidTiles

local function getSolidTiles(map)
  solidTiles = setmetatable({},  {__call = function(self, x, y) return self[x] and self[x][y] end})
  for x, y, tile in map.tl["map"]:iterate() do
    if tile.properties.type == "block" then
      if not solidTiles[x] then solidTiles[x] = {} end
      solidTiles[x][y] = true
    end
  end
end

local function movePlayer(x,y)
   if not solidTiles(x, y) then
       player.x = x
       player.y = y
   end
end
Last edited by Kadoba on Fri Aug 24, 2012 6:11 pm, edited 1 time in total.

munchor
Prole
Posts: 39
Joined: Sun Jun 10, 2012 4:28 pm

Re: How to avoid drawing map every single tick

Post by munchor » Fri Aug 24, 2012 4:46 pm

Kadoba wrote:It can be done much easier if you just crate a 2d array for blocked tiles. Iterating over and checking every blocked tile is much less efficient.

Code: Select all

local solidTiles

local function getSolidTiles(map)
  solidTiles = setmetatable({},  {__call = function(self, x, y) return self[x] and self[x][y] end})
  for x, y, tile in map.tl["map"]:iterate() do
    if tile.properties.type == "block" then
      if not blockedTiles[x] then blockedTiles[x] = {} end
      blockedTiles[x][y] = true
    end
  end
end

local function movePlayer(x,y)
   if not solidTiles(x, y) then
       player.x = x
       player.y = y
   end
end
Is blockedTiles the same thing as solidTiles? Thanks a lot, that's a MFS (memory for speed) optimization, I feel like it will improve game performance a lot :)

User avatar
Kadoba
Party member
Posts: 399
Joined: Mon Jan 10, 2011 8:25 am
Location: Oklahoma

Re: How to avoid drawing map every single tick

Post by Kadoba » Fri Aug 24, 2012 6:14 pm

munchor wrote:Is blockedTiles the same thing as solidTiles?
Opps. Yes they're the same. I fixed my post.

User avatar
Adamantos
Prole
Posts: 27
Joined: Sun May 16, 2010 10:47 pm

Re: How to avoid drawing map every single tick

Post by Adamantos » Fri Sep 07, 2012 9:36 pm

Hello there,

just stumbled across this thread... I think the initial question wasn't answered !
Isn't OpenGL handling the frame buffering (double/tripple buffer) ?!

So I don't really have to keep another copy of the screen in a canvas. Updating only
the changed part of the screen should do the job, or am I wrong ?!

User avatar
Ragzouken
Citizen
Posts: 84
Joined: Fri Aug 10, 2012 7:59 am
Contact:

Re: How to avoid drawing map every single tick

Post by Ragzouken » Sun Sep 09, 2012 10:47 am

Adamantos wrote:So I don't really have to keep another copy of the screen in a canvas. Updating only
the changed part of the screen should do the job, or am I wrong ?!
My understanding is that at the beginning of each love.draw call, the screen is clear again and so you will have to manually redraw something each frame, even if it is only a static canvas you've been keeping.

User avatar
Boolsheet
Inner party member
Posts: 780
Joined: Wed Dec 29, 2010 4:57 am
Location: Switzerland

Re: How to avoid drawing map every single tick

Post by Boolsheet » Sun Sep 09, 2012 11:46 am

Adamantos wrote:Isn't OpenGL handling the frame buffering (double/tripple buffer) ?!
Yes and no. This is part of the OS and driver implementation and is not specified in the standard. Being implementation defined, the behaviour is different for everyone.
Adamantos wrote:So I don't really have to keep another copy of the screen in a canvas. Updating only the changed part of the screen should do the job, or am I wrong ?!
Like Ragzouken said, the first thing you'll notice is that love.graphics.clear is called immediately before love.draw. This usually just clears the screen buffer (sets the entire buffer to the clear color set with love.graphics.setBackgroundColor), but can also clear the Canvas isntead if one is still set. This may be considered an issue or bug and could be changed in the future. Always remember to unset your Canvas. :P

The love.graphics.clear before love.draw is easy to remove, but now you'll see the effects of double buffering (if it's active). You would have to draw changes to both buffers to prevent it from flickering. Now imagine doing this with triple buffering where the swap behaviour may be unpredictable. It would not be possible to tell which buffer you're drawing to.

Speaking of swap behaviour, some implementations define the content of the swapped buffer as undefined. It may contain garbage and not the previous frame.

Wait, I have one more! OpenGL has a pixel ownership test. Implementations can choose to not render pixels of the OpenGL window that are obscured by other windows making it necessary to redraw everything just to be sure that all is drawn.

Does that answer it? ;)
Shallow indentations.

User avatar
Adamantos
Prole
Posts: 27
Joined: Sun May 16, 2010 10:47 pm

Re: How to avoid drawing map every single tick

Post by Adamantos » Sun Sep 09, 2012 12:28 pm

Okay.... thank you for your answer !

I will give it a try and remove the love.graphics.clear() from the love.run() function.
And to prevent flickering I have to do every draw operation atleast for two/three consecutive frames.

Post Reply

Who is online

Users browsing this forum: Google [Bot] and 38 guests