12
$\begingroup$

In beginner programming environments, I have noticed an essential difference in style for handling user interaction. Scratch and Alice are event-driven, while Processing and Greenfoot, for lack of a better term, are poll-driven. In the event-driven case, when the user clicks the mouse on something or types a key, a method is called to handle the event. In the poll-driven case, in order to respond to user actions, the programmer puts if statements into an update method that is called every frame to poll the input.

I like to use these beginning programming environments in beginning classes, especially for game programming. I'm not sure which approach is better, though. Event-driven programming is easier for programming GUIs, but poll-driven programming is better for simulations. Neither one seems best for all types of games.

Which approach will work the best for programming games for beginners? If I wanted to design my own environment, which style should I use? Are there any other styles?

$\endgroup$
7
$\begingroup$

On the face of it, event-driven programming seems far easier to explain. "This code will run when the button is clicked" seems obvious, and similarly, visual environments that favour this technique seem relatively intuitive:

Scratch 'When this sprite clicked' block

It would seem obvious to explain that the code below that block runs when the sprite is clicked.

Similarly, in Node.js, an event-driven programming language:

app.get('/', function (req, res) {
  res.send('Hello World!')
})

With some basic knowledge of the framework used (Express), you can tell that when the / route is accessed, Hello World! will be sent.

So, without looking too deeply, event-driven programming is the obvious choice. But, this isn't the end of the story. If you're not used to event-driven programming, the fact that your code might not run in the order you expect is very confusing.

fs.readFile('foo', 'utf8', function (err,data) {
    console.log(data);
});
console.log('Hello!')

Two things will print here: the contents of foo and the word 'Hello!'—but which prints first? The answer is: you can't tell, despite the intuition that many students have that would suggest the order is file then 'Hello!'.

Event-driven programming starts to encounter a lot of these 'gotchas' because it's difficult to predict in which order things will happen. Ideally, each event wouldn't need to consider any other event happening, but in practice it is necessary.

Let's say you wanted to end the game if your spaceship was touching two others at once. With events, you could need to subscribe to 'Touching [spaceship1]?' and 'Touching [spaceship2]?' and synchronise them (not terribly straightforward). But polling like so would be far easier—for each update loop, just test if(touching(spaceship1) && touching(spaceship2)).

Getting to your original question, I would personally introduce event-driven programming first, since it's so straightforward for trivial use-cases. As the problems start to appear with events, then introducing polling in an update loop would be wise. Most of the major introductory programming environments seem to follow that (Scratch -> Greenfoot, etc). The same principle should apply to your own environment, I think—consider your audience and what they'll be using your own environment for, and you'll have an answer that suits you.

You might also find Polling vs event driven input on Game Development Stack Exchange interesting for reference on the problems faced by professional game designers regarding events vs polling.

The only alternative, perhaps, would be to batch events from inputs so that they can be handled in the next update loop—that's something to consider if you're looking for another way of handling the issue, but it is more complex than the others.

$\endgroup$
3
$\begingroup$

It almost entirely depends on the context of the function within the program and the program within its usage context.

Here's my general approach: If only one event can happen at one time, especially if events are infrequent, use polling. This reduces the amount of function references needed when they would be unnecessary and also cuts down on the instantaneous overhead when an event occurs. This makes polling ideal for programs like timers, which are only waiting for one event to happen. Of course, one could also write a timer that schedules the buzzer at the appropriate time or one that binds a function that checks whether the correct amount of CPU cycles has passed every tick, but this uses references and possibly pointers where they are simply not needed. Polling is also ideal for graphical update (but not necessarily input) methods, where the screen is updated at a regular interval whether anything happens or not - otherwise the GPU would be receiving too many instantaneous drawing calls, especially in applications such as graphics-intensive games, and this way the FPS stays fairly consistent. Additionally, polling should be used when the output of the controlling function or event cannot be predicted: if you are designing a website and are creating a "Latest Threads" module for it, unless you are going to use something fancy like Pusher or React to update the module for all users, you don't know if you should update the module's HTML until you know the list of threads: in this case, polling must be used.

Event handling, on the other hand, is better for largely event-driven programs where, for example, the computer is waiting for users to perform a task. Event handlers are especially useful when multiple events can occur at one time or in quick succession, as event handlers are generally fired asynchronously, meaning that even if one event handler takes a whole second to finish executing, other event handlers do not have to wait that one second before running. This is ideal for the input aspects of video games, where a player could press the forward and left buttons at the same time, while also falling down a hole and being shot at. Each of these is at least one event, and it would be pretty bad if the game had to wait for the user to stop pressing the forward button before processing the left button before processing that they fell down a hole: they could theoretically delay the entire game by pressing a button. Event handling avoids this problem entirely, but blocking events can still be implemented easily enough if the programmer so desires.

Furthermore, despite appearances, polling-driven programs are generally less efficient than event-driven programs. Imagine the graph of time vs. CPU consumption for a purely polling-based program versus a purely event-based program. A polling-driven program's graph would show small spikes at regular intervals: every so often, it checks if a certain value is set and, optionally, if certain other conditions are met. If so, it calls a function and runs it. This would add an even larger spike whenever the event is called.

Now imagine the graph for a purely event-based program: when only considering the event handler, the graph is flat: no functions related to the event are being called. Once a user activates an event, the event calls the handling function, creating a single spike.

Now, in practice, an event-driven program would probably be more CPU-intensive simply because it is inferrable that it uses more event handlers which likely execute more often - but it would nonetheless be considered better practice to use event handlers.

The bottom line: It depends on context. Whichever one is more logical to use should be used. A program can use a mix of polling and instantaneous event handling as well, but there is a trade-off when you use a high amount of polling-based functions, even when they execute in their own threads: this will only solve the problem of blocking, not CPU consumption. Generally speaking, event-driven programs are more efficient if they are a feasible option, but polling-based functions are simpler and almost every program has to use them to some degree. Even the Microsoft XNA Framework uses an Update() function which is called every tick to update a window, as opposed to updating a single element when it changes.

For beginning users, polling is certainly easier to understand, but practicality should be kept in mind: even if you don't teach your students the how of event-driven programming, make sure to teach them the whats and the whys.

$\endgroup$
3
$\begingroup$

This is a technical response, rather than a response of a teacher for beginner students.

One of the big "aha" moments for me was understanding that they are the same thing.

Under the covers, for a long time "event driven" systems were actually polling. There was a loop hidden in the framework that polled and dispatched the events. In the early days of Windows and the MacIntosh, you always had a main event loop that looked something like this;

while (!done) {
    event = fetchNextEvent ();
    dispatchEvent (event);
}

Hardware interrupts pushed data into a cyclical queue from multiple sources. the events identified what kind of event they were and carried ancillary data.

the fetchNextEvent function would fetch the next unread event from the queue.

The dispatchEvent function was essentially a big case statement (if ... then ... else ... to beginners) that routed the event to the correct object, which ultimately handled the event.

When macintosh and Windows 2 hit the market, as an experienced programmer I had a hard time trusting the event driven frameworks, until I had traced into the framework to the point that I saw the main event loop and what the event dispatcher was doing. After that, it was natural to write everything in an event driven model, even for console applications on systems like the Commodore 64, Atari 800, or IBM PC (yes, the original one) that you had to write your own event queues and dispatchers for.

The event driven model makes for leaner (measured in kilobytes of deployed code), cleaner code (eliminates lots of decision points) and is much more flexible and reliable.

$\endgroup$
  • $\begingroup$ Thank you for the explanation. When I make a game in Java, I find myself unwinding the event mechanism, so all my handlers end up setting boolean variables. Then the code looks like this: mousePressed { mouseDown = true;} mouseReleased {mouseDown = false;} and in the game loop I have an if statement that does something based on the variable. Am I doing it wrong? This is for a simulation-style game like a jumping game. $\endgroup$ – James Vanderhyde Jul 2 '17 at 20:08
  • $\begingroup$ As with so much in computer programming, it depends. A lot of simulations depend on a "heartbeat". It is perfectly fine for the event to set the stage for the next heartbeat. Another pattern you may see is when an event submits another event back to the queue - the mouse release event may get rebroadcast as a menu event, such as File Save. $\endgroup$ – pojo-guy Jul 3 '17 at 1:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.