Design Talks - Custom Runtime Events

@Dave
Now that async example makes sense and is definitivly something i will use in my current project cause not being able to just return the value on answer received can be quite a troublemaker when the app uses alot of them
Thanks!

Hi,

This is a topic close to my heart. Almost all my projects are event driven (a throwback from my Flash days). I don’t use Runtime events though, I build event dispatching directly into my modules using this technique:

Module:

--############################################################################# --# Evented Module --############################################################################# local \_M = {} function \_M:doSomething() self:dispatchEvent({name="SomeEvent", value=20, target=self}) end function \_M:somethingElse() print("Hi!") end return setmetatable(\_M, { \_\_index = system.newEventDispatcher() })

Usage:

--############################################################################# --# Contrived Evented Module Test --############################################################################# local eventedMod = require("eventedMod") --add listener local function onEvent(evt) print(evt.value) evt.target:somethingElse() end eventedMod:addEventListener("SomeEvent", onEvent) --test call eventedMod:doSomething() --remove listener eventedMod:removeEventListener("SomeEvent", onEvent)

Another technique is using a “master” dispatcher:

--############################################################################# --# Main Controller --############################################################################# local events = system.newEventDispatcher() local score = require("score") local entity = require("entity") --pass master dispatcher to modules (internals not shown) score:init(events) entity:init(events) --something dispatched from entity events:addEventListener("EntityScored", function(evt) --dispatch to anyone listening for the event events:dispatchEvent({name=evt.name, value=evt.value}) end) -- ...

Using this technique it’s simple to listen for/dispatch events from any module, anywhere, without them needing to know about each other intimately. This is of course a pseudo example.

Reference: https://docs.coronalabs.com/api/library/system/newEventDispatcher.html

-dev

Hi dev,

Looks great and I just wish I understood it better.

Metatables and using _ before variables isnt something I have grasped yet.

Also object oriented programming using 30log, which I know you favor, is on my to do (and try to understand) list  :wink:

@dev - I’m out of ‘likes’ for the day, so…

LIKE :slight_smile:

You can have one of mine RG  :slight_smile:

I think having these ‘design’ talks are great.  The bottom line is - as it always has been - there are a dozen ways to do this or that.  There are always tradeoffs - efficiency versus time - repeatability of code - code size - etc.

Like all tools; use the correct tool where needed…  don’t use a hammer to screw in a screw - unless of course it works. 

Lets try a ‘very simple’ example, and see what approach some may have to this :

1 a generic tower defense game:  10 towers and 50 enemies.

  1. each enemy has a life, image, speed, and position it is on the screen, along a set of waypoints (the path).

  2. each tower has an image, aim direction and firing state

  3. bullets

Assume 3 modules (lua files)  (main.lua, tower.lua,  enemy.lua)

Which approach might you take:

  1. use a game loop in main.lua checking each tower(object) to see if each enemy(object) is in range and each enemy to see if it is spawned, dead or at end of path. 

  2. use game loop in main.lua but to manage a few limited things, and create enterFrame events in the tower and the enemy modules for each of the towers or enemies created. Let the objects manage themselves, at least for most things… 

If doing it this way, what method would you use to have those objects communicate back to main.lua that is managing the overall gameplay.

  1. use only one module create all objects (towers and enemies) via tables in the main.lua, avoiding any communication issues between modules. I think this is fine, when it is a very small super simple game, but I tend to lean toward modular (pseudo oop) when a game is even a little complex.

  2. some other approach?

note: I believe any and all the methods and ideas mentioned above, by way-more-expereinced developers then I, likely will all work fairly at the same efficiency; at least for such a simple game. But I am curios which approach (regarding custom events) some of you may think better in this type of game.

In my tower defense game I did years ago, I did not know about custom events, and I had lots of towers and enemies on screen at one time, so I had to do a fair number hacks and work arounds to keep the gameLoop manageable. I think in my case custom-events could have been helpful. I am going to re-do my tower defense game and look at using custom events.

Thanks RoamingGamer and to all for this helpful discussion.

Bob

Hi,

I generally find events useful when I know that I have multiple subsystems that need to be notified. I think every programmer has their own best methods, but I am an abstraction junkie, so events are a good fit for me.

I don’t generally use events to perform an action, rather respond to one.

-dev

Playing devil’s advocate here…  I personally think

coins:addCoins(20)

Is far better than

Runtime:dispatchEvent( { name = "onCoins", coins = 20 } )

If you are performing “an action” on something I feel a function is more standard, readable and IMHO preferable.  (it is also a lot less typing).

With regards to IAP, I simply pass in a callback which I want called when the purchase has completed successfully.  For example in my IAP module I have

IAP:doPurchase(whichOne, callbackFunction)

I then call _callbackFunction _when _whichOne _has been purchased.  If I had multiple listeners for IAP then potentially they could all respond to a broadcast message.  callbackFunction is passed a boolean to indicate success or failure so it know whether to action the purchase or not.

At the end of the day it is personal choice, but I prefer a clear and functional interface to my modules rather than using ad hoc events.

In reference to your OP regarding sounds.  I have a sound effects module that I can simply call as follows

SFX:play("collect")

or if I want a 500ms delay

SFX:play("collect", 500)

Again, just my personal preference (and still less typing).

A huge LIKE goes out to @roamimggamer!

I joined the Corona community 5 years ago after a 4 year programming hiatus as I mourned the “death” of Flash.  Stimulating topics and discussions like this have really enhanced my coding chops.  Like a lot of us, I often stick to what I know and techniques I’m fast and confident with but then I’m inspired by discussions like this to work on a few new concepts here and there, slowly expanding my repertoire of programming tricks.

Just knowing that these solutions exist, help when a problem arises for them to fix  :wink:

I share with @sporkfin.

Also I joined about 5 years ago and now I’m 21 so I guess you started very early. I learned a lot of things on my own and seeing other reasoning is useful here.

@SGS

I also think that:

coins:addCoins(20) 

it’s clearer too.

But in some cases “coins” it may not be reachable from a module. Or however require many steps of functions.

so to do first and simplify the code, why not?

@everyone - You guys are killing me.  :slight_smile:

I go with a dumb example and you guys won’t stop telling how inappropriate events are for it.  

I used it because the concept of coins and a counter were familiar and easy to understand.  Just so we are clear I don’t actually code my HUDs this way. 

Again big smile and a small head shake.  :)   

Lol @Ed… It’s just harmless debate :slight_smile:

Haha, nice gif @Ed :) 

It is tough to put up some “theoretical” code and then the code becomes the topic on conversation instead of the theory.

Hi!

Very cool stuff. I tried to implement something similar some time ago, but I actually gave up for two reasons:

  1. Your functionality is limited to displayObjects - other “objects” can’t receive custom events. Unless I’m mistaken here. Not 100% sure.

  2. This might sound silly, and is very personal, but I somehow came to a point where I found a need for this kind of events to be a result of sloppy code architecture on my part. Please allow me to make very clear that I don’t want to accuse you of sloppy coding - it was just a very personal decision where my game’s modular architecture made more sense to myself if all objects where hierarchically structured. That way each single module is accessible in a logical way, either as a child or parent - or part of a properyList (which is basically also a child/parent relationship).

Especially when it comes to removing objects, cleaning up and freeing up memory, I found the fact of not being able to clearly access all modules and object to be a serious drawback.

Hi @thomas6 ,

  1. It’s work for everything not only gameObject.

2)It can be really useful to dispatch data for example, to set the game in pause. For example when the game go in pause you want to save the game, pause some enter frame, do some calculation. You can win a lot of code line. You can avoid a function to dispatch the information

function dispatchPause() function1() function2() for a lot of object do object:eventPause() end ... end --- where you set pause to the app--- dispatchPause()

I think it do not slow the app because when you build, it’s create a kind of function to call all functions with a listener on the event. The created function can’t be slower or heavier than if you write it by yourself

Hi Remi,

Are you sure about that? The Corona documentation only mentions displayObjects and the global runtime. Have you tried and tested creating a custom code object (no displayObject) and having it respond to a custom event?

Of course, even if it only works with displayObjects (and the runtime), I guess you could have a displayObject as part of the code object, and then have the displayObject propagate the action to the encompassing object.

Don’t get me wrong, I see the appeal of custom events. It just felt too patched up for my personal style of coding.

@thomas6 - From The Corona docs: “This method is available for any DisplayObject or the global Runtime object, if it has a listener registered to receive name events.”

You can either send and listen to event on a display object or the runtime, and what everyone seems to be talking about here is the runtime.

Personally I think Corona is by default an event processing system (you “have” to listen to specific event and respond to them in some cases like finger movement, collisions, etc…) so listening and sending events kind of fits in to that model. When people bypassed that and did things game loop way (in examples and such) stuck out more to me because of this. But considering Corona is single threaded then whether you send an event or do your own game loop primary turns out to be a developer preference than a technical decision.

(PS @roaminggamer I’m loving these design discussions - as a beginner in the game dev space it’s very informative to see how people are doing things and how things might be different from other types of programming - maybe you should have a place where people can give suggestions on design discussions to have - I’d personally love one on different strategies and types of handling movement/touch/swiping/etc… in games)

@thomas6,

Thanks for chiming in on the discussion.

1.  This has been said, but I’ll repeat it.  Any table, display object, or function can ‘catch’ a Runtime event.  So this works for all possible ‘object’ types.

  1. That’s fine.  If this style of coding doesn’t work for you and  another does, super.

I agree, that it can lead to poor programming styles.  As others pointed out, one must be judicious in the use of this technique.  It has some more valid uses and some less valid uses.

One thing is for sure.  It is not a remedy for bad architecture.  It will only make that worse.

Thanks again for the feedback and thoughts.

PS - Re: #1 You might have been thinking of this (which I don’t use): https://docs.coronalabs.com/api/type/EventDispatcher/dispatchEvent.html

PPS - Assuming you have a game with multiple modules and scenes, used correctly, custom events can massively increase the speed of your development while minimizing cross-dependencies.  They can also render (make) some difficult coding challenges simple.

PPPS - Custom event coding like this is not at all useful in loop based approaches and monolithic designs.

I’ll add another wrinkle… You can also “fake” events by using Runtime:dispatchEvent(event). If you look at my joykey library, it listens for “axis” events and then creates “key” events to allow for joysticks to emulate keyboard keys…

https://github.com/ponywolf/joykey/blob/master/com/ponywolf/joykey.lua

You can emulate almost any event by setting the event.name property to the corresponding event type.

@ponywolf, that is one sweet little module!  Have you ever used Runtime:dispatchEvent(event) to fake onscreen touches or cursors?