Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
MovieClip loop query. — Gideros Forum

MovieClip loop query.

AstirianAstirian Member
edited August 2017 in General questions
Hi guys,

I'm trying to get a path animation going for my enemies in my game. Pathfinding is good but when I try to iterate over the path points with a MovieClip, it's producing strange results. It is as if my loop isn't waiting for the animation to finish before moving on to the next i.

The other thing is the bump implementation (which I have commented out). Am I better off with just calculating every coord between path points and using world:move on those?

Here's muh hacky code:
function GameScene:moveToPlayer(pursuer)
 
	pursuer.pursuitState = "following"
 
	if pursuer.path ~= nil then
		for i = 1, table.getn(pursuer.path) do
 
			local newX = pursuer.path[i].x -1-- Secret Sauce. Shh!
			local newY = pursuer.path[i].y -1
 
			local goalX, goalY = newX * boardRectSize , newY * boardRectSize
			-- local actualX, actualY, cols, len = world:move(pursuer, goalX, goalY)
 
			local mc1 = MovieClip.new{
				{1, 100, b, {x = {pursuer:getX(), goalX, "linear"}, y = {pursuer:getY(), goalY, "linear"}}}
			}
 
			print("I want to move to "..goalX..", "..goalY..". My actuals are "..actualX..", "..actualY)
			--pursuer:setPosition(actualX , actualY)
 
--[[
			-- prints "Collision with A"
			for i=1,len do -- If more than one simultaneous collision, they are sorted out by proximity
				local col = cols[i]
				print("Collision with "..col.other.name)
			end
]]--
		end
	end
end

Comments

  • HubertRonaldHubertRonald Member
    edited August 2017
    Hi @Astirian
    Yes it's happened because you're the same local object "mc1" in your "for loop" so you get strange results 'cos refresh the same local object in each loop.

    So first you can defined local variable mc1={} outside of you "for loop" and after you can put the next (I haven't had test it yet):
    if next(pursuer.path) ~= nil then --verify if table isn't empty
        local mc1 = {}
        local newX, newY, goalX, goalY --it's better define local variables outside of the loop
        for i, v in pairs(pursuer.path) do
               local newX, newY = v.x -1, v.y -1
    	   local goalX, goalY = newX * boardRectSize , newY * boardRectSize
     
    	   mc1[#mc1+1] = MovieClip.new{
    		{startTime(i), finalTime(i), b, {x = {pursuer:getX(), goalX, "linear"}, y = {pursuer:getY(), goalY, "linear"}}}
    			} -- in this part I don't know how you define time in animation (*/)
     
       end
    end
    (* )/ But another consideration if the time is progressive in your animation, take be careful! for example, I use the next trick:
    local vel = 7 --frame speed
    for i=1,4 do
    	print(i, i+(i-1)*(vel-1), i*vel+i-1)
    		--or
    	print( i, (i-1)*vel+1, i*vel+(i-1) )
            mc1[#mc1+1] = MovieClip.new{ (i-1)*vel+1,  i*vel+(i-1) --... and so on
    end
    --[[
    print results:
    1	1	7
    2	8	15
    3	15	23
    4	22	31
    ]]
    But finally all depends on what you really want to do

  • Ah thank you @HubertRonald, I was just thinking this morning that I was mixing my peanut butter with my spaghetti bolognese if you'll excuse the expression. :)

    When I move my player, I move his bitmap and bump.lua body by 1 pixel in the same function on every frame. This is a combination of bump's world:move() and setPosition().

    I figured if I wanted to do the same with the enemies, I would need to get a line between each valid path point (i in pursuer.path), and get every co-ordinate along that line. I could then have a nested loop inside my path points loop. Which sounds a bit hairy to be honest.

    I think the thing I was missing was velocity! :) (and also of course, fixing my dodgy vars)

    Thanks! I will try implementing this tonight when I get home.

  • Hmm... Had a play with this again tonight, the enemies eventually make it to the destination but it is as if the loop does not wait for the MovieClip to finish.

    I can see this because the getX() on the start of every loop teleports the enemy back slightly to a previous point in the path.

    And also the path becomes warped as the enemy proceeds, almost as if he starts taking a shortcut because the goalX and goalY have changed mid tween...
  • antixantix Member
    edited August 2017
    @Astirian I have a couple of questions..
    1. Do your monsters only move in four directions, ie left, right, up, down?
    2. Do you want your monsters to move at the same speed when moving from point to point?

    edit: nevermind I got curious and made an example 8-} 8-} 8-}

    Likes: Astirian

    +1 -1 (+1 / -0 )Share on Facebook
  • antixantix Member
    edited August 2017 Accepted Answer
    As an exercise I made a little example of an agent that follows a path. The agent only moves in one of four directions (left, right, up, or down) at any time which works pretty well. When the agent reaches the end of the path it starts at the beginning again. Hopefully this will be enough that you can get your head around it.. or mash it into your own code :)

    I absolutely recommend http://www.gameaipro.com/ with regards to this kind of thing as well. The first book (it's FREE) contains a very good section on state based agents, which this example uses. It is totally worth a read, and the earlier section on random numbers is fascinating :D
    Agent = Core.class(Bitmap)
     
    function Agent:init(texture) -- this only needs to be called once
      self:reset()  
    end
     
    function Agent:reset() -- we call this to actually initialize the agent
      local x, y = 0, 0
     
      self.position = {x = x, y = y}
      self.velocity = {x = 0, y = 0}
     
      self.speed = 0.3 -- how fast this agent can move (in pixels per frame)
     
      self.state = "nothing" -- what the agent is currently doing
     
      self.direction = "right" -- which direction the agent is travelling
      self.path = {}
      self.pathPos = 1
      self.destination = {}
     
    --  if BUMP:hasItem(self) then
    --    BUMP:update(self, x, y) -- just update if it already exists in bump
    --  else
    --    BUMP:add(self, x, y, 16, 16) -- otherwise add it
    --  end
    end
     
    function Agent:setState(state) -- set what this agent will do from now on
      self.state = state
    end
     
    function Agent:setPath(path) -- set the path this agent will follow
      self.path = path
      self.pathPos = 1
      self.destination = path[1]
      self:chooseDirection() -- choose movement direction
    end
     
    function Agent:advancePath() -- set agent to move towards next waypoint in current path
      local path = self.path
      local pathPos = self.pathPos + 1
     
      if pathPos > #path then
     
        -- at this point the agent has completed the path so you could choose 
        -- a new state or choose and generate a new path. for this example we 
        -- are just resetting the path position to the start so the agent will 
        -- walk about in a loop.
     
        pathPos = 1
      end
     
      self.destination = path[pathPos] -- set new destination
      self.pathPos = pathPos
      self:chooseDirection() -- choose movement direction
    end
     
    function Agent:chooseDirection() -- choose movement direction to next waypoint
      local p = self.position
      local v = self.velocity
      local d = self.destination
     
      local x, y = p.x, p.y -- current world position
      local vx, vy = 0, 0 -- initial velocities
      local dx, dy = d.x, d.y -- destination position
     
      local speed = self.speed -- how fast this agent can move
     
      if dx ~= x then -- if x is different then we will move on x axis (left/right)
     
        if dx < x then -- if destination is to left we will move left
          self.direction = "left"
          vx = -speed
        else -- otherwise we will move right
          self.direction = "right"
          vx = speed
        end
     
      else -- otherwise we will move on y axis (up/down)
     
        if dy < y then -- if destination is to above we will move up
          self.direction = "up"
          vy = -speed
        else -- otherwise we will move down
          self.direction = "down"
          vy = speed
        end
     
      end
     
      v.x, v.y = vx, vy -- set velocities
    end
     
    function Agent:update(dt)
      local p = self.position
      local v = self.velocity
      local d = self.destination
     
      local x, y = p.x + v.x, p.y + v.y -- move
      local dx, dy = d.x, d.y -- we check against these
     
      local state = self.state
      if state == "patrol" then
        local advance = false
     
        local path = self.path
        local dir = self.direction
        if dir == "left" then
          if x <= dx then
            x = dx
            advance = true
          end
        elseif dir == "right" then
          if x >= dx then
            x = dx
            advance = true
          end
        elseif dir == "up" then
          if y <= dy then
            y = dy
            advance = true
          end
        else -- must be down then
          if y >= dy then
            y = dy
            advance = true
          end
        end
     
        p.x, p.y = x, y -- update positions
        self:setPosition(x, y)
    --    BUMP:update(self, x, y) -- agents only need to be updated.. not moved
     
        if advance then -- advance path if required
          self:advancePath()
        end
     
      elseif state == "" then
        -- some other state
      else
        -- no state
      end
     
      -- process stuff here that happens regardless of state, things like
      -- checking if the agent can see the player, animating the agent, etc
     
    end
    zip
    zip
    monster_paths.zip
    3K
    monster_paths.png
    496 x 700 - 7K
    +1 -1 (+3 / -0 )Share on Facebook
  • Thanks again @antix! At this stage you're like some sort of awesome Code Santa/Mentor. I will get to work. :)

    I need to stop trying to do everything in the scene and start making proper classes for things, I'm being a bit too procedural I think.

    Lots of homework to do, that book you mentioned looks fantastic! I'll definitely give it a read. On a side note I recently picked up Level Up by Scott Rogers, once I've got the basics down and a few more light games out the door I'm planning to start coming up with my own gameplay concepts. :D
  • @Astirian, you're welcome.

    You should definitely be creating small reusable classes. Having lots of classes makes it a lot easier to prototype possible new games. I mashed together a basic platformer in a day and a half recently and that would not have been possible if I didn't have all those little classes just waiting to be combined into something new.

    The Game AI Pro book is pretty big (there are like three books now) but you only need to read sections as required or when you feel like reading something technical :)

    I might grab that other book by Scott Rogers if the price ever drops ~:>

    Likes: Astirian

    +1 -1 (+1 / -0 )Share on Facebook
  • AstirianAstirian Member
    edited August 2017
    Ooooh, so close now. It's mostly there, the critters are just stopping after the second path point for some reason. Closing in on it though!

    Edit: Sorted it, I was calling setPath() on every update, d'oh!

    Likes: antix

    +1 -1 (+1 / -0 )Share on Facebook
  • Nice sample @antix ;)

    Likes: antix

    +1 -1 (+1 / -0 )Share on Facebook
Sign In or Register to comment.