Page 3 of 3

Re: How to avoid drawing map every single tick

Posted: Thu Aug 23, 2012 8:47 pm
by Kadoba
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.

Re: How to avoid drawing map every single tick

Posted: Thu Aug 23, 2012 10:00 pm
by Roland_Yonaba
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.

Re: How to avoid drawing map every single tick

Posted: Fri Aug 24, 2012 2:39 pm
by munchor
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.

Re: How to avoid drawing map every single tick

Posted: Fri Aug 24, 2012 3:32 pm
by Kadoba
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

Re: How to avoid drawing map every single tick

Posted: Fri Aug 24, 2012 4:46 pm
by munchor
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 :)

Re: How to avoid drawing map every single tick

Posted: Fri Aug 24, 2012 6:14 pm
by Kadoba
munchor wrote:Is blockedTiles the same thing as solidTiles?
Opps. Yes they're the same. I fixed my post.

Re: How to avoid drawing map every single tick

Posted: Fri Sep 07, 2012 9:36 pm
by Adamantos
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 ?!

Re: How to avoid drawing map every single tick

Posted: Sun Sep 09, 2012 10:47 am
by Ragzouken
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.

Re: How to avoid drawing map every single tick

Posted: Sun Sep 09, 2012 11:46 am
by Boolsheet
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? ;)

Re: How to avoid drawing map every single tick

Posted: Sun Sep 09, 2012 12:28 pm
by Adamantos
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.