The Corona Event Model explained

Posted by

Although events are probably one of the most important concepts in Corona, they are also one of the highest sources of confusion for new users. In this blog post, I’ll explore events in detail: what they are, common properties, best practices, and how to use them to leverage nearly all of Corona’s best features.

But first…

What Are Events?

The dictionary defines an event as being:

A thing that happens, esp. one of importance.

And even though we’re talking about a “Corona” event, that definition still applies very well. In Corona, an event is triggered when something happens, to include (but is not limited to):

  • The user touches something on the screen
  • A new “frame” in the app’s runtime has started (aka, “enterFrame”)
  • An in-app purchase item was requested
  • Something has finished happening (completion event)

Just about anything else that falls into the category of “something happening” can be considered an event in Corona.

How Events Are Detected

Just like in real life, events could be happening all the time, but you’ll never know about them unless you listen out for them. If you’ve done any reading of the Corona documentation, you’ve probably seen the word “listener” mentioned a few times–and you guessed it, this is exactly where they come in.

A listener is a function–which you created–that will “listen” for a specific type of event, and decide what to do from there (again, you’ll be the one to decide that).

For instance, if you assigned a “touch” event to an image you placed on the screen (aka “Display Object”), within your listener, you’d tell your app what to do at that point. It could be anything, such as, removing (deleting) the object, moving it to another location, doing something with a separate object, etc.

In all actuality, listeners are no different than any other function you define in Corona. It is simply called a “listener” because it is the function that will be automatically called whenever a specific event is detected.

Assigning Listeners to Events

In order for your listener function to work properly (e.g. get called at the appropriate time), you need to tell your app what event it’s supposed to “listen out” for. This is done using another function that’s built-in to all Corona display objects: addEventListener().

Let’s say you have an object called myImage and you want to assign a “touch” event listener to it. You’d do it with one simple line of code:

    myImage:addEventListener( "touch", touchListener )

In the example, myImage is the display object, addEventListener() is the function we are calling, “touch” is the type of event we are going to be listening for, and touchListener is the “listener” function that will be called every time myImage receives a touch event (e.g. when the user touches the object).

An important thing to note is that your listener function (in this case, touchListener) must be defined prior to referencing it within the call to addEventListener(). For an example of what I’m talking about, it would look something like this:

    -- Create a new display object
    local myImage = display.newImage( "myimage.png" )

    -- Define the touch listener
    local function touchListener( event )
        print( "Touched!" )
    end

    -- Assign a 'touch' event to the display object
    myImage:addEventListener( "touch", touchListener )

Notice how the function touchListener() is defined before (above/prior to) the call to addEventListener()? As I’ve witnessed through emails and forum threads, this has caused quite a bit of confusion for a lot of people.

The reason why you must define your listener function prior to calling addEventListener() is because the function addEventListener will not know your function “touchListener” exists unless it is defined prior to being referenced to.

Your code is called in a sequential manner, just like reading a book. And as with books, you don’t know what happened unless it is happening, or happens prior to where you are currently at. In other words, you can’t know about the character “Bob” unless the book has previously introduced you to him in some manner.

The same goes for referencing functions. You can’t pass a reference to touchListener in your call to addEventListener() unless you define “touchListener” first (e.g. prior to calling addEventListener).

Event Phases

If you put the example I just gave into a main.lua file and run it in the Corona Simulator, you’ll notice that every time you touch the myImage display object, the event listener is called several times for just a single touch!

That doesn’t seem quite right does it? Normal logic would have you believe that for ONE touch, only ONE call to your event listener should happen; however, many (but not all) events have what are called “phases”.

Whenever an event has multiple phases, your listener function is called for every single phase that occurs. In the case of a “touch” event, there are four different possible phases for each touch:

  • began – the touch begins (the moment a touch happens)
  • moved – the user moves/drags their finger (but does not lift it)
  • ended – when the user lifts their finger from the screen
  • cancelled – if the system stops tracking the touch before it officially ends (e.g. object is removed while still being touched, focus is put on a different object, etc)

Ideally, you’ll want things to happen during specific phases of an event (rather than every phase). You only need to tell your function what to do during the events you actually want something to happen, so let’s say you want the words “Touched!” to be written to the terminal as soon as the user touches the display object.

You’d rewrite the touchListener() function from the prior example to look like this:

    local function touchListener( event )
        if event.phase == "began" then
            print( "Touched!" )
        end
    end

In the example above, the words “Touched!” will only be printed once, every time the user touches the object (and as soon as they touch it).

Remember, all events are different, so phases will differ (or may not even exist) depending on the type of event you are listening for. Consult the event documentation for a complete listing of the different types of events in Corona, as well as their associated properties.

Common Properties for Touch Events

Although events have different properties depending on their type, I’ll take a moment to go over the ones that are associated with “touch” events, to give you an idea of how information is passed from the actual event to the listener function.

We already went over one, and that is the event.phase property. Here are a few others:

event.id

This is a unique identifier given to most events which can be used to identify similar events separately. The most common use for this property is detecting separate touches on the same object when multi-touch is enabled.

event.target

This is probably the most common event property, and is used to reference the object that actually triggered the event. In the “touch” event examples I have provided so far, event.target would be a direct reference to the myImage display object.

event.x and event.y

If your event corresponds to specific locations on the screen, such as the location of touches during a “touch” event, the coordinates can be accessed via the event.x and event.y properties. Some others include event.xStart and event.yStart (which are the coordinates for where the touches began).

Defining Event Listeners

You’ve already seen how a touch listener function is defined in the examples I gave, and you probably noticed that it is nearly identical to defining any other function in Corona. There different ways of defining and assigning event listeners, however, so I’ll quickly go over the most common things you’ll see “out in the wild” to alleviate any potential confusion later on.

(Once again, we’ll focus on “touch” events since they are among the most common events you’ll have to deal with.)

    -- METHOD 1: (already covered)

    local function touchListener( event )
        ...
    end

    myImage:addEventListener( "touch", touchListener )

    ----------------------------------------------------------------------

    -- METHOD 2:

    local function touchListener( self, event )

        -- In this example, "self" is the same as event.target
        ...
    end

    -- Notice the difference in assigning the event:
    myImage.touch = touchListener
    myImage:addEventListener( "touch", myImage )

    ----------------------------------------------------------------------

    -- METHOD 3:

    function myImage:touch( event )

        -- Same as assigning a function to the .touch property of myImage
        -- This time, "self" exists, but does not need to be defined in
        -- the function's parameters (it's a Lua shortcut).
        ...
    end

    myImage:addEventListener( "touch", myImage )

All three “methods” of defining your listener functions and assigning them to an event are different, yet they are identical. In other words, they are multiple paths to the same outcome. The way you define them could potentially depend on the needs of your app, but it’s mostly a matter of personal preference.

Removing Event Listeners

Most listeners, with an exception of “Runtime events” (such as enterFrame events), will be removed automatically when the associated object is removed (e.g. when you call removeSelf or display.remove), however, there will be times you need to simply remove the event listener without removing the object.

Removing an event listener is done using another simple built-in Corona function: removeEventListener().

This function is called in the same exact manner as addEventListener. With an exception of the word “add” and “remove” being different, everything else stays the same. For example, let’s say you want to stop listening to touch events on the myImage object from our first example:

    myImage:removeEventListener( "touch", touchListener )

You see, it’s called in the exact same manner as addEventListener. You may be wondering why you have to specify the listener function again when you remove the event listener. This is because you can actually assign multiple same-type listeners to a single object.

For instance, you could have three (or more) different listener functions assigned to the touch event of the myImage display object. Therefore, when calling removeEventListener(), you need to once again specify the type of event you want to STOP listening for, as well as the listener function that was previously assigned to that event.

Runtime Events

There are specific events that correspond to your app as a whole, and not specific display objects–these belong to the global “Runtime” object. You can read the Runtime events documentation for a complete listing of these events, so I won’t go over those here, but there is an important thing to note regarding Runtime events and their corresponding listeners (Hint: I touched on it briefly in the previous section):

 

Runtime events will not be removed automatically — EVER!

If you assigned an event listener to a specific object, when you call removeSelf or display.remove on that object (aka, “deleting” the object), the associated listener is also removed from the object (although, it is a recommended best practice to always remove listeners manually).

Since you cannot remove the Runtime object, events that are added to it will never be removed automatically. Failure to remove events that you no longer intend to be there can and will result in unexpected app behavior and crashes.

Conclusion

To recap on everything that was discussed, you learned:

  • What events and event listeners are.
  • When and how they are defined.
  • Assigning event listeners to specific objects.
  • Removing event listeners from display objects.
  • Why you must manually manage your Runtime event listeners.

And that about covers the main concept behind Corona events! To further solidify your knowledge and understanding, please take some time to go over the events documentation, and take a look at the various Corona Sample Apps (which use many different types of events).

Most of all, practice, practice, practice! The best way to increase your knowledge and understanding of any Corona concept is to create an app from start to finish–it’s the absolute best learning technique, hands down.

Ready to get started?

Create amazing games and apps for iOS & Android

16 Comments

Maicol R.June 16th, 2011 at 2:34 pm

Very good, it will be very useful for many users. tks

PiyushJune 16th, 2011 at 3:27 pm

Great Explanation,

If Possible Please Explain :

—- Tables

BTLJJune 16th, 2011 at 7:03 pm

Thanks for the method 2 explanation, I had been meaning to post on the forum to ask how “self” technically got passed to the function (“event.target” is the key) !

For method 3, is it the case that this method requires a function for each myImage (i.e. if there is myImage1, myImage2, etc), whereas method 2 provides for the option to share a single function for multiple myImage1, myImage2, etc?

Jonathan BeebeJune 16th, 2011 at 9:17 pm

@BTLJ: Yup, you’re exactly correct. Method 3 is usually used in conjunction with specific objects, or at least, specific types of objects created with a specific function. It is useful if you have a single object that you simply want to monitor touch events (or any other kind of event for that matter).

Glad the article was of some help :-)

Stay tuned… more to come!

MarkJune 16th, 2011 at 9:49 pm

At last! A blog post that talks about something technical… It’s been a long time!!

BTLJJune 16th, 2011 at 10:02 pm

Jonathan, thanks for the explanation. This article should be included in the official Corona documentation. Also enjoying your other posts from http://jonbeebe.net/

NeilJune 17th, 2011 at 6:18 am

Jonathan – great post for us beginners put in simple, understandable language. I agree with Piyush – a similar expanation for Tables would be helpful as well. Also, although it may be outside the scope of Corona, maybe something about preparing digital assets for your app.

Thanks.

BTLJJune 17th, 2011 at 10:51 pm

Just to make things absolutely crystal clear, in method 2, where: myImage.touch = touchListener, is it the case that:
1) myImage.touch means the field “touch” of object “myImage”;
2) “.touch” is reserved for tables to receive touch events from a listener?

Hence, we are first defining the field myImage.touch = touchListener (a function within the table myImage), and then subsequently when we call the event listener it knows to refer to myImage.touch

And as touchListener is a object/table entry within myImage, “self” therefore refers to myImage?

Sorry for the complexity but I want to nail down conceptually whats happening step-by-step (I understand this all works but just want to fully confirm how technically it works)

Thanks

Jonathan BeebeJune 18th, 2011 at 12:46 pm

@BTLJ –

1) myImage.touch should be defined as a function, or a reference to a separate function (which will act as the touch listener), however, this is only if you call addEventListener(). If you don’t call addEventListener, then myImage.touch becomes just like any other property you set.

2) .touch is not necessarily reserved, it’s just that when you call addEventListener, if you are setting up a “touch” listener, if you specify the object itself as the listener, then it will search for a .touch property (because it is the name of the event).

It’s not reserved though, you can put whatever you want in the .touch property of a variable and NOT assign a touch listener to it if you don’t want.

This statement you made:

“Hence, we are first defining the field myImage.touch = touchListener (a function within the table myImage), and then subsequently when we call the event listener it knows to refer to myImage.touch”

… is correct, however, when you call addEventListener, it’ll only refer to myImage.touch IF you specify the object itself as the listener. If you specify a separate function (and not the object), it’ll use whatever function you specified instead as the listener.

BTLJJune 18th, 2011 at 7:57 pm

Jonathan, thanks, I think your explanation is the first time these specifics have been covered to this level of detail – v helpful !

PeterJune 22nd, 2011 at 11:22 am

Just wondering If I can create ANY event, not just “touch” events.
Ie, I create an event listener for event X and I send event X, so only the objects that have the X listener will listen

Jonathan BeebeJune 22nd, 2011 at 12:11 pm

@Peter: Yes, I only used “touch” events in the article because they are very common … but events cover a wide range of things. You can even create custom events.

See: http://developer.anscamobile.com/reference/index/events/

Kang Ghee KeongJune 22nd, 2011 at 6:14 pm

Very clear explanation. Also touches on key aspects. Well done and thanks for the effort!

MoNovember 7th, 2011 at 3:45 pm

Hi Jon.

Great stuff as usual!

I just decided to switch from a virtual joystick to a touch control in my game and can see I have a lot to learn still! Anyway my question has to do with what if you have multiple sprites moving around on the screen and you want to tap them to kill them?

I will assume you will have a table of sprites and for each sprite ( inside a for loop for instance) you will need to add a touch event listener. To that point I go it BUT how do you remove a sprite when touched? You need to use removeself I suppose. Would that remove the specific sprite or do you need to remove it manually from the orginal sprites table as well? ( ie sprites[i] = nil and so on) and what happened if you have a move sprites function that move all sprites in sequence ( again a for loop)? Would that loop would know to skip the sprites (i) element that you just destroyed by touching it?

I am sure my questions are probably stupid but I cannot seems to figure out…. I probably should just go for a walk and think ah ah!

Once again I just LOVE your blog posts and the way you explain things!

Thanks Jon.

Mo

AlexMarch 26th, 2012 at 9:52 am

Hi
I am trying to run the following code but it gives me an error, what am I doing wrong?

local function mapAddressHandler( event )
— handle mapAddress event here
print( “The specified location is in: ” .. event.city .. “, ” .. event.country )
end

myMap:nearestAddress( 38.898748, -77.037684 )

Runtime:addEventListener( “mapAddress”, mapAddressHandler )

The error is “myMap” first which is the object but when I substitute it with the name of a button then it says: “attempt to call a method ‘nearestAddress’ (a nil value)”

Lynne BolducApril 10th, 2012 at 7:23 am

Hi Jonathan,

I’m not a programmer by trade. This is brilliantly clear and extremely helpful. Thanks so much.

Can you confirm/explain how return true can be used to stop propagation of the touch event for stacked elements, so that only the first-touched element receives the touch (not the bottom one)?

Thank you,

Lynne

Leave a comment

Your comment