[WIP] Archon II - Adept

Showcase your completed projects or get feedback about your work-in-progress

[WIP] Archon II - Adept

Postby hardwarebob » Wed May 16, 2012 1:57 am

I will be creating a clone of the old C64 game, Archon II.

Check out the wiki link here for info on this great little game: http://en.wikipedia.org/wiki/Archon_II:_Adept

Online repository for code: https://bitbucket.org/bhartwich/archon-adept/

This project gave me a chance to reuse the code previously used for the Magic Realm game for generating grids.

Magic realm used a hex based grid, but Archon uses squares, with the way it was written I was able to write a new class SquareGridCell instead of HexGridCell and it worked quite nicely - as stated before I still want to share this part on the forums soon, but need to clean it up a bit first.

I have created a mock up grid using different coloured gif images for each of the elements represented on the tiles (fire, air, water, earth). It looks like some kind of pac man game! I will worry about making it look pretty later and focus on the technical aspects first.
Image

I have zoomed the image out using the Agen code for this, but I noticed that when doing this, some of the grid lines don't redraw, as seen in the screen shot. Not something I will worry about for now as I won't be using zoom for the game.
Last edited by hardwarebob on Sun Jun 17, 2012 8:04 am, edited 1 time in total.
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Re: [WIP] Archon II - Adept

Postby Ivan » Fri May 18, 2012 11:52 am

Hey man, nice to see you got some progress going on. :)
Would love to see some of the code.
I've been playing around with some similar prototypes in Agen so I might be able to contribute a little bit.

The reason why certain lines might disappear is because lines are affected by the zoom as well.
To counteract this, your lines need to have a thickness of: 1/zoom
You can specify the thickness of lines using "canvas:set_line_style ( thickness, color, alpha )"
Ivan
Site Admin
 
Posts: 414
Joined: Thu Dec 27, 2007 9:21 pm

Re: [WIP] Archon II - Adept

Postby hardwarebob » Sun Jun 17, 2012 8:03 am

I am in the progress of debugging object movement.

In particular, I am trying to select a gamePiece and then move it to a new location on the board.

The first gamepiece move is OK, with the sprite moved to the new location, but when trying to move the object from the updated location, it selects the tile object, not the gamePiece.

I understand that this must be something to do with the container within the gameboard not knowing that I have moved the gamepiece on top of the tile? But since the image of the gamePiece is actually displaying, this doesn't make as much sense.

Any assistance much appreciated, latest code updated to bitbucket.
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Re: [WIP] Archon II - Adept

Postby Ivan » Sun Jun 17, 2012 2:47 pm

Hi, I looked at the code and I think I pinpointed the bug.
When you click on a container, the GUI system returns the first object which it finds instead of the top-most object.
Depth ordering is a bit of a tricky area (since in 1.3.7 sprites are sorted globally, that is across layers) and we have plans of changing this in 1.4.
Please replace the following function in "Extensions/lua/container.lua":
Code: Select all
ContainerG.query_local = function(self, x, y)
  local q = {}
  for i, v in ipairs(self.objects) do
    -- convert to local coords
    local lx, ly = v:to_local(x, y)
    local q2 = v:query(lx, ly)
    if q2 then
      table.insert(q, q2)
    end
  end
  local top, depth
  for i, v in ipairs(q) do
    local d = v.node.depth
    if depth == nil or depth > d then
      top, depth = v, d
    end
  end
  return top
end

This should prevent tiles from being selected prior to sprites.
Also I think the following might be a bug:
Code: Select all
GamePieceG.toggleSelection = function ( self )
   if self.selected then      
      self.node.depth = self.node.depth - self.selectedDepthOffset      
   else
      self.selected = not self.selected
      self.node.depth = self.node.depth + self.selectedDepthOffset
   end
end

Shouldn't it be:
Code: Select all
GamePieceG.toggleSelection = function ( self )
   self.selected = not self.selected
   if self.selected then      
      self.node.depth = self.node.depth - self.selectedDepthOffset      
   else
      self.node.depth = self.node.depth + self.selectedDepthOffset
   end
end

Moving pieces seems to work fine after these two changes.
Code: Select all
   local statusMsg = Label ( 0, 100, "                                                                                                                      ")

The label constructor accepts optional width/height parameters :)
Code: Select all
   local statusMsg = Label ( 0, 100, "", 300) -- where 300 is the desired width

Notice that labels are aligned to the "center" by default (in cases where their width is greater than the caption).
You can change alignment with:
Code: Select all
statusMsg:set_style("align", "left")

or
Code: Select all
gui.set_style("align", "left")
statusMsg = Label ( 0, 100, "", 300 )
Ivan
Site Admin
 
Posts: 414
Joined: Thu Dec 27, 2007 9:21 pm

Re: [WIP] Archon II - Adept

Postby hardwarebob » Mon Jun 18, 2012 1:10 pm

Also I think the following might be a bug:

You are correct, this has been changed.

Moving pieces seems to work fine after these two changes.

Yep :D , thanks for the assistance!

The label constructor accepts optional width/height parameters

Yes, I should have checked that! That works great now.

Notice that labels are aligned to the "center" by default (in cases where their width is greater than the caption).

align center is quite good for now, as it allows me to set it to the width of the gameBoard and have it centered.

I have noticed that the text in the label seems to get stretched when I display both the first and second tile selection coords. Is this a bug with the label or something I am doing? I also noticed that my code here is assigning the coords from the destination object, and not the source and dest. I have left this part be for now and will visit it again, but I couldn't figure out where the bug in my code was.

I have now created an object, BattleBoard, which is where the two sides battle it out when they attack each other. I have created a basic 'test' movement system for each player and each piece is moving around nicely, w,a,s,d for the 'order' and the arrow keys for chaos. I have just hard coded where the 'element' tiles are placed for now, but they will need to be randomly placed when fully implemented. I am happy with how quickly and easy this additional class has been so far with your GUI, great work!

I have a question around the testing I am doing for the gamePiece movement.

I have the 'order' piece moving with the w,s,a,d keys by one pixel per keystroke. How do I increase the speed that this happens? In the game some pieces move faster than others and I am wondering how I recreate this? An issue at the moment with the way I have implemented it, is that it seems the speed might be different depending on the speed of the host system, which is not what I want. Is there some way to control this aspect?
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Re: [WIP] Archon II - Adept

Postby hardwarebob » Tue Jun 19, 2012 3:28 am

to quote myself:
I have the 'order' piece moving with the w,s,a,d keys by one pixel per keystroke. How do I increase the speed that this happens?


I have done a tiny bit of research on this and it seems that I might achieve this with a combination of the timer object for consistency between processor speeds and moving the objects by more than one pixel at a time to increase the speed?

Ivan, please let me know if this is the right track to follow.
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Re: [WIP] Archon II - Adept

Postby Ivan » Tue Jun 19, 2012 7:12 am

Hi. There are different ways you can implement piece movement.
The approach I use most often involves vector math:
Code: Select all
-- set target cell
GamePieceG.setTargetCell = function(self, i, j, speed)
  self.targI = i
  self.targJ = j
  self.targSpeed = speed or 10 -- pixels per second
end

-- continually update piece position
GamePieceG.moveToTarget = function(self, delta)
  -- target cell index
  local tI, tJ = self.targI, self.targJ
  -- moving piece?
  if tI == nil or tJ == nil then
    return
  end
  -- convert cell index to screen position
  local w, h = self.cell.WIDTH, self.cell.HEIGHT
  -- target position
  local tx, ty = (tI - 1)*w, (tJ - 1)*-h

  -- initial position
  local x, y = self:get_position()
  -- vector to target
  local dx, dy = tx - x, ty - y
  -- distance to target
  local dist = base.math.sqrt(dx*dx + dy*dy)
  -- travel distance for this redraw step
  local step = self.targSpeed*delta
  -- are we there yet? :)
  if dist > step then
    -- normalize 'distance to target' vector
    local nx, ny = dx/dist, dy/dist
    -- calculate movement vector for this step
    local mx, my = nx*step, ny*step
    -- keep moving
    self:set_position(x + mx, y + my)
  else
    -- arrived!
    self:updatePosition(tI, tJ)
    self.targI = nil
    self.targJ = nil
  end
end

There's no need to create a new timer object, you can simply use the "delta" value (in seconds) provided by the gui system.
All you have to do basically, is call the above function during "redraw":
Code: Select all
GamePieceG.redraw = function ( self, delta )
   local c = self.node.canvas
   c:clear ( )
   -- draws texture so that its upper left corner is at the origin of TileG
   local cell = self.cell
   c:set_source_image ( self.img, self.offsetX, -self.offsetY );
   c:paint()
   
   if self.selected then      
      self:drawSelectedItem( )      
   end
   self:moveToTarget(delta) -- move piece!
end

That's all, now your pieces will move at the same speed (in pixels per second) regardless of the user's system.
Naturally, you would also have to update "BattleBoardG", something like:
Code: Select all
BattleBoardG.key_command = function( self, key, ctrl, shift )
   local piece = nil
   local cell = nil
   local i, j = nil, nil
   
   -- check of 'order' player movement keys
   if key == KEY_W or key == KEY_A or key == KEY_S or key == KEY_D then
      -- testing movement by pixels
      piece = self.gamePieceOrder
      cell = piece.cell
      i, j = cell:getIndexI(), cell:getIndexJ()

      if key == KEY_W then
         piece:setTargetCell(i, j - 1)
      elseif key == KEY_A then
         piece:setTargetCell(i - 1, j)
      elseif key == KEY_S then
         piece:setTargetCell(i, j + 1)
      elseif key == KEY_D then
         piece:setTargetCell(i + 1, j)
      end

One part of the code above I don't like is the following:
Code: Select all
  -- convert cell index to screen position
  local w, h = self.cell.WIDTH, self.cell.HEIGHT
  local tx, ty = (tI - 1)*w, (tJ - 1)*-h

This sort of math transformation doesn't really belong to the piece class in my opinion.
These two lines I think can be generalized like so:
Code: Select all
-- return screen position from cell index
BattleBoardG.cellToPosition = function(self, cx, cy)
  -- todo: assert cx and cy are within map bounds
  local cw = self.cellWidth
  local x = (tI - 1)*cw
  local y = (tJ - 1)*-cw
  return x, y
end


For this sort of game, you may also have to prevent the user from moving several pieces at the same time (or prevent the user form setting a new target to a piece while it's still moving). One easy solution might be to store a reference in BattleBoard of the currently moving piece.

A minor thing I noticed is that the board is not in focus when you run the application so the keyboard keys don't work until you click on the window (gui_demo.lua):
Code: Select all
local battleBoard = gui.BattleBoard( x, y, boardWidth, boardHeight, cellWidth, gameTiles, gamePieces[1], gamePieces[6] )
battleBoard:setup()
gui.container:add_child( battleBoard )
gui.container:set_focus( battleBoard ) -- let's set the focus right away :)


I have noticed that the text in the label seems to get stretched when I display both the first and second tile selection coords. Is this a bug with the label or something I am doing?

That's odd, doesn't look stretched on my computer. Any idea on how to reproduce this?

I also noticed that my code here is assigning the coords from the destination object, and not the source and dest. I have left this part be for now and will visit it again, but I couldn't figure out where the bug in my code was.

I think this is happening because you are calling "piece:updatePosition" during "setSelectedObj". Therefore, after the call to "setSelectedObj" the piece is already moved to the destination position. One solution to this would be:
Code: Select all
GameBoardG.mouse_event = function(self, event, button, x, y)   
   -- pass down the event to the object in focus
   local focus = self.focus
   if focus then
      --logmsg("GameBoardG.mouse_event: " .. event )
      
      if event == "mouse_press" then      
         local cell = focus.cell
         --local text = "Selected " .. cell:getIndexI() .. "," .. cell:getIndexJ()            
                  
         self:setSelectedObj( focus ) -- don't move piece yet :)
         local text = self:getSelectionBufferCoords()
         self:setMessage( text )
         self:syncSelectionBuffer( ) -- move it now!
      end
      
      local lx, ly = focus:to_local(x, y)
      focus[event](focus, button, lx, ly)
   end
end

--
--   Sets either the first or second object selected in a selection pair
--  This will be used to test moving pieces from one selected position to another
--
GameBoardG.setSelectedObj = function ( self, object )
   local sBuffer = self.selectionBuffer
   -- is this the second selection for the pair?
   if #sBuffer == 1 then
      sBuffer[2] = object
      logmsg("GameBoardG.setSelectedObj, second selection: " .. object.cell:getIndexI() .. "," .. object.cell:getIndexJ() )
      -- just update the buffer, don't move piece yet :)
   else -- the first selection for the pair
      logmsg("GameBoardG.setSelectedObj, first selection: " .. object.cell:getIndexI() .. "," .. object.cell:getIndexJ() )   
      sBuffer[1] = object
      sBuffer[2] = nil
   end
end

GameBoardG.syncSelectionBuffer = function ( self )
   local sBuffer = self.selectionBuffer
   -- is this the second selection for the pair?
   if #sBuffer == 2 then
      local source, dest = sBuffer[1], sBuffer[2]
      -- deselect dest cell
      dest:toggleSelection( )
   
      -- todo:check if the sBuffer[1] is a piece and not a tile
   
      -- move the object to the selected cell and deselect
      source:updatePosition( dest.cell:getIndexI(), dest.cell:getIndexJ() )
      source:toggleSelection( )
   else
   end
end

Another solution might be to change the selection buffer so that it stores map coords instead of references to objects.

That's all I can think of for now. A lot of these examples are based on the approach I used in "Touch Chess". I think you can probably improve/simplify a lot of the code above, but hopefully you might find it useful. :)
Ivan
Site Admin
 
Posts: 414
Joined: Thu Dec 27, 2007 9:21 pm

Re: [WIP] Archon II - Adept

Postby hardwarebob » Tue Jun 19, 2012 2:49 pm

Wow, your advice is truly amazing and quite quick! Kudos to you, I really appreciate it :D

Hi. There are different ways you can implement piece movement.
The approach I use most often involves vector math:

Yes, I quite like your approach and have used this, although I had to modify it as it was not quite what the game requires (a good piece of homework for me!).

Movement in the battle board is 'free movement' and controlled by each players movement keys/controller, if a direction is pressed, then the piece moves.
Basically, I changed it so that the destination was only 1 pixel in the direction of movement and it continued to move until any other key is pressed.

At the moment, the piece now keeps moving after the key is pressed just once. I actually want to only move on the key down event and stop on the key up event. To do this I understand that I would need to wire up the on_press ( keyboard, key ) and on_release ( keyboard, key ) events.
I don't see these in your GUI objects, so assume that these are not implemented yet? I was trying to figure out how your mouse_press, mouse_release, etc events are wired up to their equivalent on_press ( mouse, button ), on_release ( mouse, button ), etc events, but couldn't see anything. Are these also linked to some dll's for Agen?

I have also come across an issue with the two separate images moving a very different speeds, even while set to the same initial speed. The 'order' piece on the left moves much slower than the chaos piece. It doesn't matter which one I start to move first, it's always the same result.

A minor thing I noticed is that the board is not in focus when you run the application so the keyboard keys don't work until you click on the window (gui_demo.lua):
Yes, this was annoying, needed to get around to that, fixed now, so easy with your GUI!

I think this is happening because you are calling "piece:updatePosition" during "setSelectedObj". Therefore, after the call to "setSelectedObj" the piece is already moved to the destination position. One solution to this would be:

You are correct, fixed using your advice.

That's odd, doesn't look stretched on my computer. Any idea on how to reproduce this?

See this first image, that the text is displaying nice and crisp at the top
Image

Now we select the second tile, it's like the text is zoomed in and not redrawn correctly
Image

Another solution might be to change the selection buffer so that it stores map coords instead of references to objects.

Yes, I might look into this at a later stage.
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Re: [WIP] Archon II - Adept

Postby Ivan » Wed Jun 20, 2012 7:58 am

Yes, I quite like your approach and have used this, although I had to modify it as it was not quite what the game requires (a good piece of homework for me!).

Hey, just a small note, you have to be careful with the above algorithm so that "dist" is greater than zero or you'll get a division by 0:
Code: Select all
   -- normalize 'distance to target' vector
   assert(dist > 0, "Division by 0!") -- already on top of the target
   local nx, ny = dx/dist, dy/dist

At the moment, the piece now keeps moving after the key is pressed just once. I actually want to only move on the key down event and stop on the key up event

The easiest solution I think would be to check if "keyboard:is_down(key)" during the "redraw" function. This is especially convenient since at that point, you'll also have access to "delta".

Alternatively you can modify the GUI to report "key_press" and "key_release" in "gui.lua":
Code: Select all
function keyboard_on_release(keyboard, key)
  if container == nil then
    return
  end
  container:key_release(key) -- add this line to pass down event to default container
  if old_keyboard_on_release then
    old_keyboard_on_release(keyboard, key)
  end
end

You'll also have to add the "key_release" handler in ContainerG and ObjectG (for the latter, the handler would be blank):
Code: Select all
ContainerG.key_release = function(self, key)
  if self.focus then
    self.focus:key_release(key)
  end
end

I don't see these in your GUI objects, so assume that these are not implemented yet? I was trying to figure out how your mouse_press, mouse_release, etc events are wired up to their equivalent on_press ( mouse, button ), on_release ( mouse, button ), etc events, but couldn't see anything. Are these also linked to some dll's for Agen?

The GUI is entirely Lua scripted.
At the moment, there is: "key_command" which is pretty much the same as "key_press".
The reason why I left out the "key_release" event is because the GUI tries to implement keystroke repeat for keys that are pressed and held down for a period of time.
I have to look into this but I do admit that the current implementation is not that great.
In fact, you'll probably have to disable the repeat delay so that you're not getting repeated key_release/press events:
Code: Select all
gui.set_style("repeat_delay", math.huge) -- wait period until repeat is on (let's make it huge!)
--gui.set_style("repeat_rate", 0.05) -- keystroke repeat rate (default is 0.05 secs)


You can also do away with GUI module altogether and use the default callbacks:
Code: Select all
keyboard.on_press = function(keyboard, key)

These callbacks are always available (even if you continue using the GUI module).

Now we select the second tile, it's like the text is zoomed in and not redrawn correctly

Looks like the game window (including its borders and decorations) might be slightly wider than your desktop resolution.
Therefore, the display buffer might be getting "stretched" down so that the entire window can fit on the screen. Try creating a slightly smaller game window and see if that works.
Ivan
Site Admin
 
Posts: 414
Joined: Thu Dec 27, 2007 9:21 pm

Re: [WIP] Archon II - Adept

Postby hardwarebob » Wed Jun 20, 2012 2:21 pm

Hey, just a small note, you have to be careful with the above algorithm so that "dist" is greater than zero or you'll get a division by 0:

change made, thanks

The easiest solution I think would be to check if "keyboard:is_down(key)" during the "redraw" function.

Yep, quite simple indeed, I was originally trying to use this sort of approach, but couldn't get it right, now it's sorted!

Thanks for the extra info on the GUI about the other key events, I might investigate this another time if required.

Looks like I should also be able to use this same code to create the projectiles that some characters fire for their weapons.

Code: Select all
Looks like the game window (including its borders and decorations) might be slightly wider than your desktop resolution.
Therefore, the display buffer might be getting "stretched" down so that the entire window can fit on the screen. Try creating a slightly smaller game window and see if that works.

No, this didn't work. You probably thought my window was wider than the res due to my screenshot and how it is clipped.

After playing around with moving tiles some more, I did notice that certain text would actually display correctly with both selections, but most don't.

I think you missed a comment of mine, which is puzzling me:
I have also come across an issue with the two separate images moving a very different speeds, even while set to the same initial speed. The 'order' piece on the left moves much slower than the chaos piece. It doesn't matter which one I start to move first, it's always the same result.

Does it do this on your PC? Any ideas as to what is causing this?
hardwarebob
 
Posts: 44
Joined: Sun Jun 12, 2011 3:41 am

Next

Return to Showcase

Who is online

Users browsing this forum: No registered users and 1 guest

cron