Quick Links: Gideros Home | Download Gideros | Developer Guide
Dig Dug tunnel logic.
  • AstirianAstirian +1 -1
    Member
    Hi guys!

    So, I'm trying to get my head around doing a Dig Dug type game. I decided to try with drawing rectangles. I've got something that looks promising but doesn't behave very well. :)
        --local tunnelHalfHeight = PLAYER.tunnel:getHeight()/2
    local tunnelY = PLAYER.tunnel:getY()
     
    --local playerHalfHeight = PLAYER:getHeight()/2
    local playerY = PLAYER:getY()
     
    local distance = playerY - tunnelY
    print("Tunnel distance is: "..distance)
     
    if distance > 1 then
    --PLAYER.tunnel:setY(PLAYER:getY())
    PLAYER.tunnel:setSize(PLAYER.tunnel:getWidth(), distance)
    end


    The rectangle expands but the Y stays static. If I uncomment setY(), it never resizes as it moves with the player. (I'm using http://giderosmobile.com/forum/discussion/100/any-chance-of-a-setwidth-and-setheight-for-bitmaps/p1)

    I thought of just creating a new rectangle with every +1 playerY but that sounds like it could turn into a performance nightmare.

    Perhaps this is entirely the wrong approach though and I would be better off drawing straight onto canvas somehow. Assuming I can then limit monster movement to stay inside what is drawn, i.e. the tunnels (I was originally going to do rectangle collision checks)...

    I'm guessing the way they used to do it was to calculate every line with every frame (or something?).
  • hgy29hgy29 +1 -1 (+1 / -0 )
    Maintainer Accepted Answer
    I would have done it with a RenderTarget acting as a mask: paint dug zones on that render target and use it both to display tunnel shape and to check for collisions by using getPixel()

    Likes: Astirian

  • AstirianAstirian +1 -1
    Member
    This sounds like exactly what I need, thanks! :D
  • AstirianAstirian +1 -1
    Member
    OK, so I had a look at this, looks way beyond my pay-grade haha!

    But seriously, I saw the example here: http://docs.giderosmobile.com/reference/gideros/RenderTarget

    Which is great, looks like we can basically manipulate render targets directly (had to Google render targets ;)))

    I guess I would add a transparent texture over diggable ground as a mask (this would be my target), then apply color transforms to certain areas of this mask depending on player direction, let's say make them black. Then I suppose I use getPixel like: if the color data is black I know it's a tunnel?

  • hgy29hgy29 +1 -1 (+1 / -0 )
    Maintainer
    Yes, thats exactly what I was suggesting!

    Likes: Astirian

  • antixantix +1 -1
    Member
    Any reason you aren't using a tilemap? It seems the logical choice for this kind of game since it's based on a grid right?
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1
    Member
    I haven't gotten to that part of my career yet :). Actually, I want to do a platformer next. I thought about a grid for this game, but I couldn't think of doing it without the grids sort of "popping out". I'm after that kind of continous "drawing" feel as the tunnel is dug.

    But I'm struggling a little bit with renderTarget (I'm still a baby programmer :))).

    I can't get a hold of the individual pixels in my buffer and I hope I don't have to write a shader because that's still way too advanced for me.

    Basically, I'm trying something like this on my RT (background):
     
    buffer = self.rt:getPixels(PLAYER:getX(), PLAYER:getY(), PLAYER:getWidth(), PLAYER:getHeight())
    --buffer:setAlpha(0)
     
    for i=1, #buffer do
     
    local pixel = buffer[i]
    print(pixel:getAlpha()) -- returns nil, I don't know how to grab the pixel in the array...
    -- pixel:setAlpha(0)
    end


    I feel like I'm sort of close. If this works, then I could probably do collision checks for enemies against 0 alphas as hgy29 suggested. :)

    But maybe there's an easier way to paint on a render target..
  • antixantix +1 -1
    Member
    After watching a youtube video on Dig Dug gameplay maybe you need a finer grained grid.

    I haven;t played with RenderTarget and its methods of getting pixels. I think that needs better documentation (ie; there is no documentation to my knowledge).

    In theory you could use a Rendertarget and at the start of the level fill it with a color (0xffffff for this example). Whenever the player moves further in the map just clear the immediate area of the RenderTarget (clear(x, y, w, h) and then for collision you can check if the pixels in the direction of travel are not 0xffffff. If they are not then you should be able to move in that direction.

    I think I need to find a Dig Dug emulator and play it to see how the controls actually work.
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • antixantix +1 -1
    Member
    So Dig Dug is one strange game. Even though you can move in fine pixel increments, you can only change direction (l, r, u, d) on a grid alignment. Even stranger is that between each grid column and row there is an extra one for collision purposes with regards to the monsters movements.
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • hgy29hgy29 +1 -1
    Maintainer
    @Astirian,

    To modify the rendertarget's pixels, just draw some sprite on it. There is no setPixel() method. For getPixels(), you were nearly there: the object returned is a string containing the R,G,B and A components of all pixels of the selected region (left to right, top to bottom), each char of the string being a component. I hope this make sense...
  • antixantix +1 -1
    Member
    @Astirian I'm not sure how you intend to make this work but I'd be eager to find out. Im my mind it would be far easier to use a traditional grid of ones and zeros for collision.

    You could still maintain the "digging" feel with a grid, you would just use more grids, so one grid for the actual collision data and another for storing the digging information.

    Does that make sense?
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1
    Member
    @hgy29 @antix I got the first part working, basically doing:

    self.rt:draw(myShape)

    on onEnterFrame().

    So I guess I'm drawing the shape onto the render target each frame, seems to work, dunno if I'll have to add a line to remove the shape for performance. Actually, I know! I'll just create the shape outside onEnterFrame and just update the coordinates on onEnterFrame.

    As for the grid system I'm sure that would work as well, it never occurred to me to just use pixel sized grids! I had 32x32 stuck in my head for some reason.

    Collisions are up next.

    8-X
  • antixantix +1 -1
    Member
    You probably don't need to use a pixel sized grid for collision, I think that would be a very large table :D

    One of the cool things about Dig Dug is that there are thin walls between rows and columns which not many other games have. Usually those other games have walls that are one tile wide and high.

    I got to thinking that you could just use a grid of cells and each cell would store connectivity data to specify if any direction (Left, Right, Up, Down ) was blocked or open. So a cell in the grid would be like so..
    {t = 0, l = true, r = false, u = true, d = false}
    In this example the cell in question has a graphical tile (t) of 0, and any game agent can travel left and up from it. Using this method, a wall can be any size you like, but in Dig Dug's case it would be quite thin.

    Just a side note that this type of grid is commonly used to generate mazes :)

    So I made a little example to show how it all works. Sorry it's a bit messy and there are a bunch of hardcoded things in there but you should be able to decipher it without too much problem.

    You will see in the example that the walls of the tunnels are very thin (like Dig Dug) and the simple collision checking implemented makes the agent only able to move in the tunnels.
    DDCollision.png
    512 x 512 - 74K
    DDCollision.zip
    128K
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1 (+1 / -0 )
    Member
    @antix Thanks that's awesome! Haven't touched my game in a while but will download this tonight after work and have a play around.

    On a side note, would adding enemies and doing collisions against them be object based? At the moment I'd probably do something like add each enemy on creation to an enemies array then iterate through the array in onEnterFrame to check for collisions using bump.lua, dunno if that's silly or not.

    I'm gonna have a fun time with AI and pathing too, I can tell. :P Great fun though. Especially when it all comes together on the screen! :D

    Likes: antix

  • antixantix +1 -1
    Member
    @Astirian, good call on your objects. I would recommend that when using bump.lua you use bump:update() to move your enemies (without detection) and then bump:move() to move and detect collisions on your player.

    With a cell based system like the one in my example I would probably also run a 2d grid array alongside it. This grid would be used for path finding using one of the many a* libraries out there. Dig Dug does also have the whole "ghosty drifting" thing but that shouldn't be too hard to get working.
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • SinisterSoftSinisterSoft +1 -1
    Maintainer
    This type of map can get complicated though if you have doors and 8 way movement.

    Sometimes it's easier to look in the direction of the move for a map cell that is empty or not - then set it as being occupied and move in that direction (over x frames), at the same time as setting the current cell to empty.
  • antixantix +1 -1
    Member
    @SinisterSoft true, but Dig Dug only has 4-way movement :)
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1
    Member
    Hi guys,

    Bump.lua is working well so far, I've added rocks and it all seems to work (well, anchor points seem to need some tweaking, hopefully that doesn't bite me in the bum later!)

    So I'm working on procedural generation of elements when the level loads up. The idea is that I'll move the rock if it collides against an enemy, only problem is, I'm stuck in an infinite loop (unless I remove that last IF on the collisionsResolved boolean):

    		-- If the rock is on top of an enemy, move it till it... isn't.
    i = 1
     
    if world:check(rock) then
    print("Ouch!")
     
    actualX, actualY, cols, len = world:check(rock, randomX, randomY)
    print("Collision Results: "..actualX.." | "..actualY.." | "..len)
    end
     
    if len == 1 then
    collisionsResolved = false
    end
     
    --while collisionsResolved == false do
    repeat
    print("Attempt "..i.." to resolve collision.")
     
    if (self.quadrantOneStatus ~= "full") then
    print("Noo PLACEZ! 1")
    randomX = math.random(self.quadrantOneOrigin.x, self.quadrantOneEndpoint.x)
    randomY = math.random(self.quadrantOneOrigin.y, self.quadrantOneEndpoint.y)
    end
     
    if (self.quadrantOneStatus == "full") then
    print("Noo PLACEZ! 2")
    randomX = math.random(self.quadrantTwoOrigin.x, self.quadrantTwoEndpoint.x)
    randomY = math.random(self.quadrantTwoOrigin.y, self.quadrantTwoEndpoint.y)
    end
     
    if (self.quadrantTwoStatus == "full") then
    print("Noo PLACEZ! 3")
    randomX = math.random(self.quadrantThreeOrigin.x, self.quadrantThreeEndpoint.x)
    randomY = math.random(self.quadrantThreeOrigin.y, self.quadrantThreeEndpoint.y)
    end
     
    if (self.quadrantThreeStatus == "full") then
    print("Noo PLACEZ! 4")
    randomX = math.random(self.quadrantFourOrigin.x, self.quadrantFourEndpoint.x)
    randomY = math.random(self.quadrantFourOrigin.y, self.quadrantFourEndpoint.y)
    end
     
    if (self.quadrantFourStatus == "full") then
    print("Noo PLACEZ! 5")
    randomX = math.random(self.quadrantFourOrigin.x, self.quadrantFourEndpoint.x)
    randomY = math.random(self.quadrantFourOrigin.y, self.quadrantFourEndpoint.y)
    end
     
    rock:setPosition(randomX, randomY)
     
    actualX, actualY, cols, len = world:check(rock, randomX, randomY)
     
    print("SIGH: "..len)
     
    if len == 0 then
    collisionsResolved = true
    --break
    end
    i = i+1
    until collisionsResolved == true
    --end
  • antixantix +1 -1 (+1 / -0 )
    Member
    @Astirian, something like this should work...
        local random = math.random
     
    local done = false
     
    repeat
    local x, y, c, l = world:check(rock, randomX, randomY)
     
    if l == 0 then
    done = true -- no collision detected so exit loop
    else
     
    if (self.quadrantOneStatus == "full") then
    print("Noo PLACEZ! Quad 1")
    randomX = random(self.quadrantTwoOrigin.x, self.quadrantTwoEndpoint.x)
    randomY = random(self.quadrantTwoOrigin.y, self.quadrantTwoEndpoint.y)
    end
     
    if (self.quadrantTwoStatus == "full") then
    print("Noo PLACEZ! Quad 2")
    randomX = random(self.quadrantThreeOrigin.x, self.quadrantThreeEndpoint.x)
    randomY = random(self.quadrantThreeOrigin.y, self.quadrantThreeEndpoint.y)
    end
     
    if (self.quadrantThreeStatus == "full") then
    print("Noo PLACEZ! Quad 3")
    randomX = random(self.quadrantFourOrigin.x, self.quadrantFourEndpoint.x)
    randomY = random(self.quadrantFourOrigin.y, self.quadrantFourEndpoint.y)
    end
     
    if (self.quadrantFourStatus == "full") then
    print("Noo PLACEZ! Quad 4")
    randomX = random(self.quadrantFourOrigin.x, self.quadrantFourEndpoint.x)
    randomY = random(self.quadrantFourOrigin.y, self.quadrantFourEndpoint.y)
    end
     
    end
    until done
     
    rock:setPosition(randomX, randomY)
    Of course whenever you use one of these kinds of loops there is always the possibility that the program could get caught in an infinite loop, however tiny the chance may be.

    Note that also even though it might not get caught forever.. it might get caught up for many seconds.

    Likes: Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • antixantix +1 -1 (+1 / -0 )
    Member
    Thinking about this more, I am firmly of the opinion that everything should initially be placed into a game world based on a 2d grid.

    So I made this little example which populates the game world with game objects using a grid system. There is more code and maybe it is a bit messier but it will always work and never get caught in any strange loops.

    Looking at the screen dump you can see the world is divided up into 4 quadrants and each quadrant is inhabited by 4 enemies and 2 rocks.

    This example can be used for any game where you have quadrants that need objects inserted into them without those objects overlapping any other objects in the quadrant.

    Just ask if you have any questions about it :)
    digdugproc.png
    336 x 540 - 12K
    DigDugProc.zip
    15K

    Likes: Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • antixantix +1 -1 (+2 / -0 )
    Member
    While the example above works, it is not very generic. I have made another example which now has a Plane class, which contains 4 quadrants...

    1 Upper left quadrant
    2 Upper right quadrant
    3 Lower left quadrant
    4 Lower right quadrant

    Create a plane with each quadrant having these dimensions.
    myPlane = Plane.new(width, height)


    Resize the plane with these new quadrant dimensions.
    myPlane:resize(width, height)


    Clear all quadrants in the plane.
    myPlane:reset()


    Get the position of a random empty cell in quad. data is optional and will be used to fill the empty cell, otherwise the cell will be filled with 1.
    local x, y = myPlane:getPosition(quad, data)


    Get the specified quad.
    local quad = myPlane:getQuad(quad)


    Get a plane which is a grid comprised of all 4 quadrants.
    local plane = myPlane:getPlane()


    An example use (minus drawing code).
    QUAD_WIDTH = 5
    QUAD_HEIGHT = 5
     
    ENEMIES_PER_QUAD = 5
    ROCKS_PER_QUAD = 2
     
    local myPlane = Plane.new(QUAD_WIDTH, QUAD_HEIGHT) -- our plane
     
    -- generate enemies
    for q = 1, 4 do
    for i = 1, ENEMIES_PER_QUAD do
    local x, y = myPlane:getPosition(q, 1)
    print("enemy created at " .. x .. ", " .. y)
    end
    end
     
    -- generate rocks
    for q = 1, 4 do
    for i = 1, ROCKS_PER_QUAD do
    local x, y = myPlane:getPosition(q, 2)
    print("rock created at " .. x .. ", " .. y)
    end
    end


    The attached example has drawing code as well and is a lot simpler because it uses getPlane() and uses that when drawing everything.
    digdugproc.png
    336 x 540 - 13K
    DigDugProc2.zip
    18K

    Likes: pie, Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1 (+1 / -0 )
    Member
    Oh wow! Thanks @antix, this is well and truly above and beyond the call of duty! I was expecting a one liner along the lines of "your boolean declaration's out of scope" or something.

    You are a gentleman and a scholar! Thank you very much. :)

    Likes: antix

  • antixantix +1 -1 (+1 / -0 )
    Member
    @Astirian, you are welcome :)

    Another idea I had for a bump based solution would be to just move objects slowly away from each other until they didn't collide. This would work pretty well and the only case where you would need to choose a new random location would be when one of the moving objects overlapped with two or more other objects.

    I do feel however that the grid based system is far superior. I have been thinking about how to make the class better too so you could have sectors (name change of quad) and you could have a 3x3 matrix, or whatever sized matrix you liked, as opposed to the 2x2 matrix it currently has. This would be beneficial in games with big maps I think.

    Likes: Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1
    Member
    @antix Yeah, I think you're right. I guess there's a reason grid based symptoms are so popular. I was going to try and figure something out for a proper platformer but I think you've solved that for me too! :D

    I'm sure it'd be great for stuff like; "this sector is water" etc... Then you could just move the world/stage around when the player got close to the sides of the screen to mimic a camera moving with the player I guess.

    Of course I wouldn't be surprised if there was already a Lua library for this as platformers are so ubiquitous. I had read briefly about the Tiled editor a while back and was going to circle around back to it.
  • antixantix +1 -1 (+1 / -0 )
    Member
    @Astirian well Gideros has built in support for tile maps that are created in Tiled. Add to that Bump and you can pretty much have a platform game up and running very quickly. Somewhere on the forums here I posted some code a while ago that can create bump collision rects from a tiled map.

    For zones I found it was quite easy to just use the characters midpoint for that. in your tilemap you can decide which blocks are which types of zone for example blocks 1 might be water and 2 might be lava, etc. I used an extra layer in my tiled map to edit them.

    Check which tile in the map the player is on. If it's in the water zone and the player is not set to be in water then set them in water, and do enterNewZone effects. If it's no zone (0 - 109) and they are not in no zone then set them in no zone and do exitLastZone effects.

    I chose this method because in a platformer you are always checking the players midpoint so it was natural to use it for zone entry/exit as well.

    Likes: Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • AstirianAstirian +1 -1
    Member
    @antix Hiya! So I threw out everything and started with the grid based approach, works great but collision broke; I'm having issues adding "bump bodies" to the brushes. I think it treats the last type in the quad as the only brush or something...

    I tried replacing drawing brushes onto the RT with self:addChild and they're always just the last two in the last two quads for some reason.

    I'm probably missing something obvious though?

    --world:add(brush, x, y, 40, 46) -- x,y,w,h //Duplicate error.
     
    --if (c ~= 3 and c ~= 4 and r ~= 5 and r ~= 6) then
    --if brush ~= nil then
    brush:setPosition(c * TILESIZE, r * TILESIZE)
    --canvas:draw(brush)
    self:addChild(brush) -- Only adds last of each?
    --end
    --end


  • antixantix +1 -1 (+1 / -0 )
    Member
    @Astirian, okay I can see what is happening here now (took me a while)

    The brush is a SINGLE object but bump expects every object you add to its world to be UNIQUE (so you are getting the error because you are trying to add the same object repeatedly).

    The solution is to create a new collision rectangle for each tile that is solid and then add that to the bump world..
    local rect = {isWall = true}
    world:add(rect, c * TILESIZE, y * TILESIZE, 40, 46)

    Between levels I would suggest removing all items from the bump world..
    local items, len = world:getItems()
    if len > 0 then
    for i = 1, len do
    world:remove(items[i])
    end
    end

    Then at the start of each level add the new ones back in.

    Likes: Astirian

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Login with Facebook Sign In with Google Sign In with OpenID

In this Discussion

Top Posters