The Corona Labs Blog
Posted on . Written by

Custom Events in Corona SDKA little over a year ago (in June of 2011), I went over the Corona Event Model, and explained exactly what events are in Corona, when they occur, and how you can “hook” into them to take advantage of some of Corona’s best features.

However, in the previous article, I kept the subject-matter focused on the built-in events associated with specific API’s that are provided out-of-the-box in Corona. What I didn’t mention is that you can define your own custom events, and have objects listen for when those events are dispatched (which you have complete control over as well).

This tutorial will walk you through defining and dispatching your very own custom events, so you can then apply the knowledge to your own games and apps. If you’re unfamiliar with the Corona event model, I recommend first reading The Corona Event Model Explained before continuing.

The Scenario

For the sake of this tutorial, the scenario we’ll be working with will involve a single object, player, that will dispatch various events related to its health state. We will call it a “health” event (it can be named anything you want, as long as it does not conflict with any of the pre-defined Corona events) and will have three types: full, low, and empty.

Dispatching an Event

Let’s say at the start of a game, the player’s health will be at 75%. He picks up a “med pack” and that brings his health to 100%. At this time, you’d want to dispatch a “health” event with an event.type set to “full”. So the first thing you need to do is create an event table that will have all the data you want sent to the listener function (more on that later). Once you have an event table set up, you can call the object:dispatchEvent() method.


local event = {
name = "health",
type = "full",
healthPercent = 100
}
player:dispatchEvent( event )

The name key in the event table that is passed to the object:dispatchEvent() method is the only one that is requried—the rest are completely custom, as well as optional.

And for the sake of being thorough, here’s how you would dispatch a “health” event when the user’s health level is “low” and “empty”:


local event = {
name = "health",
type = "low",
healthAmount = 10
}
player:dispatchEvent( event )


local event = {
name = "health",
type = "empty",
healthAmount = 0
}
player:dispatchEvent( event )

Listening to Events

Having objects listen to custom events is exactly the same as having them listen for any of Corona’s predefined events. You simply specify the event name (the same one specified when you dispatched the event), and the function to be called when that type of event is received.

The following examples show how you would have the player object listen to any “health” events that are dispatched. The first example shows how to do so using a function listener:


local function health_listener( event )
if event.type == "full" then

print( event.healthAmount ) -- 100
turn_health_bar_green()

elseif event.type == "low" then

print( event.healthAmount ) -- 10
warn_player()
turn_health_bar_red()

elseif event.type == "empty" then

print( event.healthAmount ) -- 0
initiate_game_over()
end
end
player:addEventListener( "health", health_listener )

By using a table listener—as shown in the following example—you can have a reference to the object with the self object.


local function health_listener( self, event )
if event.type == "full" then

print( event.healthAmount ) -- 100
turn_health_bar_green()

elseif event.type == "low" then

print( event.healthAmount ) -- 10
warn_player()
turn_health_bar_red()

elseif event.type == "empty" then

print( event.healthAmount ) -- 0
initiate_game_over()
end
end
player.health = health_listener
player:addEventListener( "health", player )

Notice how the healthAmount variable is accessible from the listener function (the event table is the same one that was passed when you dispatched the event). This is what really makes Corona’s event model worthwhile.

So to re-cap, at any point you want an object to respond to a specific type of event (in our case, “health” events), you do so by using the object:addEventListener() method. Conversely, you can have an object stop listening for events using the object:removeEventListener() method.

Things to Keep in Mind

Believe it or not, that just about covers dispatching and listening to custom events! As you go along, here are some things to keep in mind with custom events:

  • In contrast with the built-in predefined Corona events, YOU must control when events are dispatched.
  • As with the built-in predefined Corona events, nothing will happen unless you have objects “listening” to the event-to-be-dispatched.
  • The event table passed to the object:dispatchEvent() method requires a ‘name’ key to be specified—the rest is completely optional, and accessible via your event listener function.

Of course, you could just directly call a function instead of dispatching an event. The primary benefit to using events is that you can simply have certain objects stop listening to events whenever you want (which is easier than setting up—and managing—a bunch of Boolean variables and checking them every time you call the function).

Using events also arguably promotes cleaner coding practices, which ultimately makes your code more maintainable, which is obviously a good thing.

What kind of creative, custom ways have you utilized Corona’s event model?

10 Responses to “How to Use Custom Events in Corona”

  1. Angelo Yazar

    Dispatching an event to a single object is cool, like we could get the object in the grid cell next to us and send it an ‘interact’ event. If it’s a dog object it might bark, and if it’s a wall then it’s not even listening to you. ~ but I think the ability to broadcast an event would be more useful.
    Why not setup the custom events like this:

    flower.season = function(self, event)
    if event.phase == “spring” then
    self:blossom()
    end
    end
    flower:addEventListener(“season”, flower)

    Then when you detect that some event like ‘the season has changed to spring’ has happend, you can do this:
    broadcastEvent(“season”, {phase=”spring”})

    And the flowers will blossom, but you could also have the snow melt and have the town setup a spring festival with that same call, since all of those objects could respond differently to a common event in your game world.

    It’s not that hard to set something like this up, but it might be nice as an official API. (If it’s not there already and I just missed it)

    Reply
  2. russell

    This seems very similar to the property callback tutorial with ‘proxy’. Is this a replacement for that programming method or are their differences?

    Reply
  3. Michael Lysons

    Nice article.

    This feature of Corona is fantastic. I’ve recently been creating some code for timing, and I use custom events to dispatch things like “tick” events, e.g. the event is dispatched every time a second passes in the timer.

    I’ve also found them useful for handling scoring. For example, an object could dispatch an event when it is hit and the listener can use that to know that it should update the score.

    Reply
  4. nest

    What if an event should be listened by different “types” of objects
    player:dispatchEvent( event ), only the player object will receive the event. How can I make other objects to receive it also.

    Look at the following code for better understanding:

    local object1=display.newGroup()
    local object2=display.newGroup()
    local object3=display.newGroup()

    randomeventListenter=function()
    print("randomevent")
    end

    object2.randomevent=randomeventListenter
    object2:addEventListener("randomevent")
    object3.randomevent=randomeventListenter
    object3:addEventListener("randomevent")

    object1:dispatchEvent {name="randomevent"}

    What am I doing wrong?

    Thanks!

    Reply
  5. Angelo

    @nest that’s exactly what I’m talking about, there should be a way to broadcast ‘randomevent’ to all of the objects listening for that event. There isn’t a built in way to do it that I know of, but it’s not hard to setup something that lets you do that.

    Right now the way you have it you have only dispatched the event to object1, and that has nothing to do with the other objects.

    -Angelo

    Reply
  6. nest

    The solution I’m using is dispatch and register events with Runtime. But I don’t like it. It doesn’t seem a very clean solution

    Reply
  7. Angelo Yazar

    Oh, that works to broadcast? How did you set it up?

    Woah crazy, you’re right ~ this seems to work:

    object1 = display.newCircle(10,0,12)
    object2 = display.newCircle(20,0,12)

    function randomeventListenter(self, event)
    print( self.x, event )
    end

    object1.randomevent=randomeventListenter
    Runtime:addEventListener( "randomevent", object1 )

    object2.randomevent=randomeventListenter
    Runtime:addEventListener( "randomevent", object2 )

    Runtime:dispatchEvent{ name="randomevent" } --'Broadcast'

    But yeah it would still be nice to have a way to broadcast. It does seem like this would work with any display object though, not just Runtime. Like:


    Broadcaster = display.newGroup()
    object1 = display.newCircle(10,0,12)
    object2 = display.newCircle(20,0,12)

    function randomeventListenter(self, event)
    print( self.x, event )
    end

    object1.randomevent=randomeventListenter
    Broadcaster:addEventListener( "randomevent", object1 )

    object2.randomevent=randomeventListenter
    Broadcaster:addEventListener( "randomevent", object2 )

    Broadcaster:dispatchEvent{ name="randomevent" } --'Broadcast'

    It just seems to be a side effect of table listeners to have the ‘self’ be set to the table, not the display object that is listening for the event.

    You could setup a simple dispatcher like this:

    --------------------------------------------------------
    -- Event Dispatcher
    --------------------------------------------------------
    local Dispatcher = display.newGroup()

    local function addEventListener(object, event, listener)
    if listener then
    object[event] = listener
    end
    Dispatcher:addEventListener(event, object)
    end

    local function dispatchEvent( event )
    Dispatcher:dispatchEvent( event )
    end

    --------------------------------------------------------
    -- Example Usage
    --------------------------------------------------------
    object1 = display.newCircle(10,0,12)
    object2 = display.newCircle(20,0,12)

    local function randomeventListenter(self, event)
    print( self.x, event )
    end

    object2.randomevent = function(self, event)
    print( self.y, event )
    end

    addEventListener(object1, "randomevent", randomeventListenter)
    addEventListener(object2, "randomevent")

    dispatchEvent{ name="randomevent" } -- Dispatch the event to all objects listening for it.

    Using a Dispatcher display object would make it a bit easier to remove all the event listeners at once. Otherwise it’s pretty much the same as using Runtime as far as I can tell.

    -Angelo

    Reply
  8. mitchell

    Great post, this is very helpful. I found the example a little odd in that player is dispatching event to itself. Seems a more likely scenario would be another object would register with player to receive messages.

    Reply

Leave a Reply

  • (Will Not Be Published)