Scoreboards and Video Routing in Python

Time and Possession

The clock is a unique case because it is the only game variable that updates independently. Everything else is a response to something that happens on the court.

In the clock function (lines 314-331), getClock returns a string that represents the current game time, called by a recurring interval in a JavaScript function on the operator's web page to update the operator's clock. A timer event in the graphics thread both updates the internal clock variable and redraws the clock on the scoreboard display.

updateClock is only found in the JavaScript and does the job of keeping the control page clock up to date. It calls getClock by jQuery to get the current clock time then updates the HTML div with the current value:

function updateClock()
{{
   $.post ( "getClock" , function ( data ) {{
      $ ( "#clock" ).html ( data );
   }} );
}}

Both the clockRun and clockStop functions change the state of self.scoreboard.clk.running. If that variable is True, the clock is updated. If it's False, the clock is not updated or is, for all practical applications, paused. clockStop also calls self.gameEvent to add an entry that the clock was stopped. Because the clock is not team-specific, the second argument is  . The JavaScript version of these functions (lines 80-92) are not shown. Once the Python function is called by jQuery, a JavaScript interval is either set or cleared to update the control page.

For the clockSet function, the variable self.scoreboard.clk.seconds keeps track of the game time remaining in seconds, which is set to one second more than requested. Next, self.scoreboard.clk.tick automatically subtracts one second. Passing manual = True forces a redraw of the clock even though it is not running. Finally, self.scoreboard.clk.clockString, which is a text representation of the time remaining in minutes and seconds, returns.

The JavaScript version can be found on lines 101-116 (not shown). It asks the operator what the clock should be set to and will contain either the requested time or null if the dialog box was canceled.

If the value is valid, the request is split into minutes and seconds and then converted to seconds. After a new object is created and a seconds parameter added, it is posted by jQuery back to the Python function. The control screen then updates with the new clock value and the update interval clears so clock requests are not made until it starts running again.

The posession [sic] function (333-339) calls the toggle method of self.scoreboard.posession, which flips which team currently has the ball (indicated by the < and > symbols as arrows).

Lines 336 and 337 check to see which team has possession and add a gameEvent to reflect the change. Finally, two strings are returned separated by a colon; each string is either a space or one of the < or > symbols. The JavaScript version is on lines 71-78 (not shown). After posting a possession change by jQuery the return string is split into two parts, and the left and right divs are updated on the control screen.

The Clock Class

The clock class (lines 341-371) manages the game clock for the main graphics (scoreboard) display. The __init__ function sets up a few things: self.screen is a reference to the screen object passed when creating the class; self.width stores the screen width by calling get_width() on the screen surface; self.height is set explicitly to define how tall you want the clock to be displayed; and self.seconds is the time on the clock, initialized to 6 * 60 seconds to reflect six-minute periods. Line 347 creates a Pygame surface onto which the clock is drawn, and line 348 sets up the font. Finally self.running tracks whether the clock is running or paused.

Lines 351-354 calculate the clock values and convert to int; seconds is the mod (remainder) of self.seconds / 60. Finally, self.clockString is assembled from the calculated values.

The main loop calls the tick function (lines 356-363) once a second. If time is left on the clock and either self.running or manual is set to True, self.seconds decrements by one, and the same calculations as before are repeated to update self.clockString. The manual parameter used when setting the clock forces the clock to be regenerated immediately rather than waiting for the next automatic call to tick.

The render function (lines 365-371) draws the clock. self.clockFont.render takes self.clockString and a color tuple as arguments. The True argument says to anti-alias the text as it is drawn. The result is timeSurf, a Pygame surface with the current game time drawn onto it.

After the center point of timeSurf is calculated, self.clockSurf is cleared by filling it with black and blitting (copying) timeSurf onto it. Then the surface is returned. This copying might seem like an extra step, but here's what's happening: When self.clockFont.render generates the text, the surface is the exact size of the text it generated. For it to fill the space at the dimensions specified in __init__, you have to copy it onto the larger surface.

The Posession Class

The posession class (lines 373-406) indicates who has the ball at any given time. __init__ ( self ) sets up the graphical display, with the initial state specified by self.direction (who currently has the ball). A surface is created and initialized with self.leftString and self.rightString.

The convenience function makeSurfaces creates self.leftSurf and self.rightSurf possession arrows rendered with < and > symbols.

When the toggle function (lines 388-396) is called, possession has changed from one team to the other. The if checks to see which team currently has possession and then updates all of the variables to their opposite states. Note that self.leftString and self.rightString either have an arrow or an empty string.

When everything is ready to be drawn in the render function, self.posSurf is cleared by filling it with black (line 399) before generating the label text, calculating the center point, and blitting it onto the final surface.

Lines 404 and 405 make sure the arrow is blitted on the appropriate side of the surface. If self.direction is <, the arrow is drawn on the left side of the surface; otherwise, it's drawn on the right before the final surface is returned.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Nerf Target Game

    A cool Nerf gun game for a neighborhood party provides a lesson in Python coding with multiple processors.

  • Gesture-Controlled Book

    Have you found yourself following instructions on a device for repairing equipment or been half-way through a recipe, up to your elbows in grime or ingredients, then needed to turn or scroll down a page? Wouldn't you rather your Raspberry Pi do the honors?

  • ReportLab and Panda3D

    A game of bingo illustrates how to use the ReportLab toolkit and Panda3D real-time 3D engine.

  • Panda3D

    Several free game engines are available for Linux users, but programming with them is often less than intuitive. Panda3D is an easy-to-use engine that is accessible enough for newcomers but still powerful enough for the pros at Disney Studios.

  • Perl: Skydiving Simulation

    Computer game programmers apply physical formulas and special tricks to create realistic animations. Simple DirectMedia Layer (SDL), which is available as a Perl wrapper, provides a powerful framework for creating simple 2D worlds with just a couple of lines of code.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News