How would I go about approaching this?

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.
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

How would I go about approaching this?

Post by KayleMaster »

Yeah, I know, the title kinda breaks the rules but I didn't know how to name this topic.

So I'm making an isometric RTS and so far, I've got the terrain set up only.
Image
I'm using spriteBatch for the tiles and I update the spriteBatch only when I update the terrain (The white and blue are placed via mouse buttons) so the terrain is editable. I'm pretty happy with it actually. The white border is the chunk. Currently this chunk is surrounded by 8 more in every isometric direction. I would later expands this so the map is something like 256x256 chunks but only draw 9 at a time (or more if the user has a bigger resolution). So there's 4096 tiles in one chunk and total of 36864 drawn on screen. Now, I want to make 2 more tables.
So terrain information is stored in terrain_chunk (a 2D array or table). Now I would make object_chunk and entity_chunk.
Object_chunk will hold the buildings and environment (trees, bushes etc) but it isn't as simple as terrain chunk.
I'd need to hold several things, location in the grid (localX, localY), the image of the object and the depth of it.
Then, in the love.draw (I guess?) I would sort the table by depth and draw them in that order.
Would all of this fit in one table? Or would I have several?
It all sounds good, but in practice I have no idea where to start from.
I have found a way to sort the table by value or keys on stackoverflow:

Code: Select all

function spairs(t, order)
    -- collect the keys
    local keys = {}
    for k in pairs(t) do keys[#keys+1] = k end

    -- if order function given, sort by it by passing the table and keys a, b,
    -- otherwise just sort the keys 
    if order then
        table.sort(keys, function(a,b) return order(t, a, b) end)
    else
        table.sort(keys)
    end

    -- return the iterator function
    local i = 0
    return function()
        i = i + 1
        if keys[i] then
            return keys[i], t[keys[i]]
        end
    end
end
Usage:

Code: Select all

HighScore = { Robin = 8, Jon = 10, Max = 11 }

-- basic usage, just sort by the keys
for k,v in spairs(HighScore) do
    print(k,v)
end
--> Jon     10
--> Max     11
--> Robin   8
-- this uses an custom sorting function ordering by score descending
for k,v in spairs(HighScore, function(t,a,b) return t[b] < t[a] end) do
    print(k,v)
end
--> Max     11
--> Jon     10
--> Robin   8
So k returns the entry or whatever you call it (in this case Max) and v returns the value (11)
And I'm wondering if I could change the entries of the table to the image name. Something like this:

Code: Select all

image_name1 = love.graphics.newImage( filename, flags )
image_name2 = love.graphics.newImage( filename1, flags )

ObjectData = {image_name1 = 1, image_name2 = 3, image_name = depth}
However this won't work if there's two of the same images as it will just overwrite the previous value, yes?
Any ideas how would I approach this?
EDIT: I think I can store all the info I need in a 3D table, but should I? Or should I just store it in several tables?
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: How would I go about approaching this?

Post by raidho36 »

In Lua you can use "duck typing", so for sorting function to figure out which object goes where, they only have to have common variables (it looks like a duck, and quacks like a duck, so it's the same as a duck). If all of your objects have X and Y variables, then you can put them all in the same table and sorting function will sort them all properly.

Simply using table.sort with appropriate sort function will get it sorted. Note that only integer keys get sorted. The example you provided is a bit more convoluted than that in order to sort non-integer-keyed values, if you don't need that you can avoid all of it and simply use table.sort.

I don't exactly understand where you're going with the last bit, and why, but yes that won't work, if you write to a key that already exists, you overwrite. You can however create a table for that key and append new entries to that table. That does makes interaction with it more complicated, as now you get table of values rather than just value.
Germanunkol
Party member
Posts: 712
Joined: Fri Jun 22, 2012 4:54 pm
Contact:

Re: How would I go about approaching this?

Post by Germanunkol »

I'm not sure what your game does exactly, and what kind of objects will be present on the map, but I expect that most of them will not often change in height (trees, for exampled, probably won't change at all, except maybe when they're chopped down). Since sorting is expensive, I would not call it before every draw call if I were you. Instead, change the order in the table only for objects which actually move, and keep the table always sorted.
For example, when the player jumps from a box, take the player out of the table (or list, which I think is a better word in this case) and search through the list (starting at the list entry where the player was previously) going downwards until you reach a tile which is lower than the new player's height, and place the player there.

This also has other advantages. For example, if you ever need the player to react to anything, you can check what position the player has in the list and only interact with the objects on the same or similar height, without needing to sort the list first.

Tl;dr: My suggestion is to keep the list sorted at all times, instead of sorting it before drawing.
trAInsported - Write AI to control your trains
Bandana (Dev blog) - Platformer featuring an awesome little ninja by Micha and me
GridCars - Our jam entry for LD31
Germanunkol.de
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: How would I go about approaching this?

Post by KayleMaster »

Yeah, I will only sort when a new object is added to the table.
I'll just loop through the sorted using a for loop or something to draw it.
How can I get the size of the table? Or should I make a variable to keep track of it? I tried table.setn table.getn which is in the lua manual but doesn't seem to do anything in love2d.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: How would I go about approaching this?

Post by raidho36 »

There is a table "length" operator, simply prefix table name with a hash symbol.

Code: Select all

len = #table
Note that this only valid for tables that you created by "growing" starting at index 1, since (for what I can tell) this operator only returns length of array part of the table, but not hash part of the table. If you try to insert an index that's outside of allocated array space and engine decides it's not worth to resize array to fit new key, it's gonna put it to hash part. Since that key is not part of array, array length operator will not include it, and ipairs won't iterate over it either.

You can perform basic insertion and removal of one element at a time using table.insert and table.remove respectively, which will handle all underlying related activity.
User avatar
mr_happy
Citizen
Posts: 84
Joined: Fri Mar 18, 2016 8:57 pm

Re: How would I go about approaching this?

Post by mr_happy »

I have a suggestion which is not so much a means of implementation but an alternative approach which avoids depth sorting altogether, a kind of modified 'painter's algorithm'. I used the method in an isometric game (unfinished) many years ago and it worked well. It's a little hard to describe but here goes:

For simplicity, assume you are dealing with just two layers, the ground layer (layer1) and another containing the player and some buildings (layer2).

1 Into a buffer (buffer1, which will be flipped to the screen) draw the ground layer then everything on the layer above in its proper place as normal, ignoring the player.

2 Grab the area from the buffer (say four tiles) in which the player sprite might lie, call this buffer2.

3 Blit the player sprite onto buffer2 then blit any layer 2 items that lie in front of the player also into buffer2

4 Copy buffer2 onto buffer1 then flip buffer1 to the screen

It sounds like a lot of work but blitting a small number of small sprites is typically pretty cheap compared to convoluted depth sorting, and is much easier to debug!
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: How would I go about approaching this?

Post by KayleMaster »

Not sure what "blit" means, I'm not native english and google translate says nothing about it.
Anyways, that really does sound like a lot of work, and I already implemented a depth sorting. It uses a skip list and it's O(log(n)) insertions, deletions and search, and O(n*log(n)) space usage and I'm fine with that. It only sorts when an element is inserted.
Also, I do plan to have elevated terrain but not now. Thanks for the suggestion anyway.
In action:
Image
If my math's right, then that's 1024 objects(trees). If I use a spriteBatch (which I'm not currently) I could achieve even more fps. The actual fps is 300~, the recorder drops that by 230 :D
Zireael
Party member
Posts: 139
Joined: Fri Sep 02, 2016 10:52 am

Re: How would I go about approaching this?

Post by Zireael »

I wish there was a nice tutorial on layered tiles drawing. I've had the same problem except my game isn't isometric. Currently I have a rather naive implementation with a Cell class, which contains the cell's contents (currently terrain or actors only).

My Map does:

Code: Select all

 -- Initialize the array of cells
    for x = 0, self.bounds.Width-1 do
        self.cells[x] = {}
        for y = 0, self.bounds.Height-1 do
            self.cells[x][y] = Cell:new()
        end
    end
Then my map can do lookups using self.cells[x][y]:whatever() and I'm using it to call to love.graphics.draw a second time if it finds any cells containing actors. I am aware it could be made better (caching, maybe a more performant way to find actors/objects/whatever other optional layers may be).
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: How would I go about approaching this?

Post by KayleMaster »

That's what mine does too, kinda. And some extra transformation to make isometric work.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: How would I go about approaching this?

Post by raidho36 »

300 fps is not a compliment if it runs on gtx 1080. As the saying goes, if you want to make fast software use slow computer.
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests