Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
How to handle communication between objects? (Send messages, trigger actions) — Gideros Forum

How to handle communication between objects? (Send messages, trigger actions)

MellsMells Guru
edited May 2012 in General questions
Hi community,

I would like to get a good understanding of the basics -> communication between objects, trigger actions.
If I understand well this would have to do with event listeners, but I can't see how to structure the logic with efficiency.

Here is the situation :
Let's say I have a moving object that contains a property (ex : "enemy").
When enemy gets closer to a friend (object with "friend" property), I want to send a message to other "friends" objects to notify them.
Then a message is sent to the HUD to display a "Under fire" mode (let's say, a red light sign showing up in the corner).

I am not asking for precise code, but how would you handle that scenario? How would you structure the listeners/triggers?
Are there some things to know?

I am not asking for the community to do the work for me, but I don't want to reinvent the wheel if there are best practices that "every developer" should know about :)

Thank you!
twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps

Comments

  • MichalMichal Member
    edited May 2012
    I guess there is many ways of doing it, and it depends on your code.

    For example:
    function FriendsContainer:init()
      self.FriendsTable={} -- all friend objects go here
    end
     
    function FriendsContainer:onEnemyWarning(enemy)
      for n, friend in self.FriendsTable() do
        friend:onEnemyWarning(enemy)
      end
    end
     
    if isClose(enemy, friend) then
      FriendsContainer:onEnemyWarning(enemy)
      HUD:onUnderFire()
    end
  • denizdeniz Maintainer
    edited May 2012
    Hello Mells,

    The best way to do this is by events and listeners. Whenever you want to notify some class about a change or condition, (in this example enemy gets closer), you create a specific Event about it and dispatch that event. You also add this events listener to the objects that you want to notify about this event. So whenever the events is dispatch, the listeners know it happened and do the action.

    Say you have a MovingObject class:
    function MovingObject:enemyGetsCloser()
        -- you need to create a event here. The event is fired and the all the classes that 
        -- listens to the event, catches it.
        closerEvent = Event.new("enemyIsClose")   --with the specific keyword for the event.
        self:dispatchEvent(closerEvent)
    end
    For the objects that want to hear about this event...
    friend= FriendClass.new()
    movingObject:addEventListener("enemyIsClose", friend.enemyAlertFunction, friend)
     
    function FriendClass:enemyAlertFunction(event)
        print("Enemy is close!")
    end
    You need to add this event listener to every friend object and your HUD class.

    I hope this helps.
    Cheers,


  • denizdeniz Maintainer
    I think there is a better way:

    If you have a Game class, which handles enemies and friends, you can let it catch the event. It knows who are friends and notifies them.
  • alexzhengalexzheng Guru
    edited May 2012
    also consider if you have two or more moving objects, it will become complicated for adding event listeners, I think broadcast events will make our life easier.
  • @Michal, @deniz
    I have built some tools that help me achieve what I had in mind in a very convenient way, thanks to your help :)

    @alexzheng
    I am not sure I understand what you mean by broadcast events?
    I have created a class (ex: Game like suggested by Deniz) that handles the attribution of triggers, listeners and actions to groups of objects. When a trigger sends an event, the listeners get automatically activated and an action is performed.
    The Game class stores and pairs groups of objects.

    Ex : when a moving lamp with a "light" property sends an event ("close"), a wall that has the same "light" property gets the message and its color changes.
    Those triggers and listeners are grouped by "light" property and this allows me to loop and check distance between all objects contained.

    Is it much different than what you are suggesting, Alex?


    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Also I have another question, I have tried some variations to test, but now I can't get the basics to work.
    Can you tell me why isn't the following displaying "Enemy is close"?
    -- main.lua
    game = Game.new()
    game:testListener()
    game:testEvent()
    -- game.lua
    Game = Core.class(Sprite)
     
    function Game:testListener()
    	local function displayAlert(event)
    		print("Enemy is close!")
    	end	
    	local image = Sprite.new()		
    	image:addEventListener("enemyIsClose", displayAlert, image)
    end
     
    function Game:testEvent()
    	local image = Sprite.new()	
    	closerEvent = Event.new("enemyIsClose")
            image:dispatchEvent(closerEvent)
    end
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • The problem is that you're adding the event listener to a different image than the one you're dispatching the event on. Here's an example that works for me -- it's your example modded to use one image:
    -- game.lua
    Game = Core.class(Sprite)
    function Game:init()
    	self.image = Sprite.new()
    end
     
    function Game:testListener()
    	local function displayAlert(event)
    		print("Enemy is close!")
    	end	
    	self.image:addEventListener("enemyIsClose", displayAlert, image)
    end
     
    function Game:testEvent()
    	closerEvent = Event.new("enemyIsClose")
        self.image:dispatchEvent(closerEvent)
    end
     
    game = Game.new()
    game:testListener()
    game:testEvent()
  • @ndoss thank you,
    If I understand well in your example self.image is telling to itself "enemy is close" right?

    But how would "enemy" send a message to "friend" -> "I'm close, fear me!"
    And "friend" would be listening, get the message, and react -> "We are under attack!"

    I would like the sender and the listener to be different objects.

    In fact I'm working on some tests with moving lights.
    When the light is close to the wall object, "light" tells "wall" to light up. Wall changes color.
    When the light is going away, the light on the wall reverts to normal.

    My goal is to have a lot of different lights and walls.

    Any idea how I should tackle this? I have tried without success..
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Any idea how I should tackle this? I have tried without success..
    I don't have a good answer :(
  • @Mr. Mells,
    1. How do you determine proximity? if you want some pointers on that, read up on Levenshtein Distance.
    2. Loop through a list of all the objects and determine the Levenshtein Distance of each of the objects in reference to the wall.
    3. Check if the object that does have the allowable distance (proximity) is a friend or an enemy, and based on that act accordingly.

    Hope that helps you to proceed. In case you have different walls, run this for each wall to determine the distance and the lights that fit the criteria.
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • MellsMells Guru
    edited May 2012
    hey @OZApps thanks

    1. How do you determine proximity?

    Like this, it seems to work :
    local function checkDistance (source, target)
       local srcX = source:getX()
       local srcY = source:getY()
       local targetX = source:getX()
       local targetY = target:getY()
     
       local xdiff = srcX - targetX
       local ydiff = srcY - targetY
       local dist = (xdiff*xdiff+ydiff*ydiff)^0.5		
    return dist
    end
    2. and 3.
    This is what I have and I am able to loop, check the allowable distance, check if friend/enemy ("light" and "walls" in the following example), but the last part (act accordingly) is not working.
    game = Game.new()
     
    -- Game:addTriggerProp(triggerProperty, objArray, eventMessage, defaultProperties to add to the object)
    -- I add the trigger property light to light1 and light2. The event they dispatch is lightEvent. Also, I add extra default properties
    game:addTriggerProp("light", {light1, light2}, "lightEvent", {"distance", "power"})
     
    -- Game:addListenerProp(listenerProperty, objArray, actionFunction, defaultProperties)
    -- I add the trigger property light to wall1, and wall2. The function to run when the listener gets the message is lightUp. Also I add extra default properties.
    game:addListenerProp("light", {wall1, wall2}, lightUp, {"distance", "power"})
    For each light I'm able to print "I'm lightx and I'm close to wallx : distance is xxx", only if within allowable distance and "too far" when leaving that perimeter.
    So I believe the distance check is ok.

    But I can't send the message to wallx so wallx runs lightUp.
    The part that is not working is "send message -> get message, run function".

    So that's why I was asking for a very simple example of working send/listen message between two different objects so I can adapt it to my own code (that would be two long to post here).
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
Sign In or Register to comment.