Angry Rationals! A Lesson in Dynamic Interactivity and Iterative Development. Part 2

In the first installment (Angry Rationals Part I) we set out the structure for a multi-level game inspired by Angry Birds. Today we will flesh things out a bit by adding some more compelling game play to our first level.

Level 1 of our game challenges  the player to set the initial position of a rational projectile (1/2) so its path along a parabola causes it to crash into the game’s first irrational villan (π). The level will have several effects:

  1. The projectile should leave a trace of its path as it flies across the screen.
  2. If hit, the villan should change color to provide feedback to the player.
  3. If hit, the villan should tip over to provide a degree of satisfaction to the player (and the rational).

Additional effects, such as sound would be nice but we will save those for a future installment.

The first effect can be achieved using Line and Table to generate the points. The points come from the trigger variable t, the single user set parameter p1 and the function for a parabola f[t_] := 1 – t^2. Despite its name, Line can generate smooth curves provided the collection of points are finely spaced.

f[t_] := 1 - t^2;
path = Table[{t1 + p1, f[t1]}, {t1, TSTART + p1, t + p1, PathIncr}];

The second effect requires a means to detect collisions. EuclideanDistance does the trick as long as we define a collision as an intersection of two points within a reasonable delta.

collision[p1_, p2_] := EuclideanDistance[p1, p2] < COLLIDETHRS;

The game and sprites are implemented in terms of Mathematica’s 2D Graphics primitives. I already mentioned Line and I also use Text and Style. The third effect, tipping over the Pi is done using Rotate.

The remainder of the code is game state logic. In particular, when using Manipulate, remember that the body of the Manipulate is continuously being evaluated. This means that you must arrange for state variables to always represent the current state of play. For example, we have a state variable called success that is updated using the following logic.

success = success || collision[{t + p1, f[t]}, {PiX, PiY}];
success = success && t > TSTART;

This keeps the value of success False until a collision is detected and then keeps the value of success True until the trigger variable t is reset to TSTART.

The complete code for level 1 follows.

DynamicModule[{success = False},
 With[{LMARGIN = 50, RMARGIN = 50, BMARGIN = 50, TMARGIN = 50,
   TSTART = -1, TEND = 1, COLLIDETHRS = 0.05, PiX = 0.85, PiY = 0.35,
   PiSZ = 48, ratSZ = 28, PathIncr = 0.05, PlRngPad = 0.25,
   PlotRng = {{-1, 1}, {0, 1}}},
   DynamicModule[{f, pi, collision, path},
    f[t_] := 1 - t^2;
    path =
     Table[{t1 + p1, f[t1]}, {t1, TSTART + p1, t + p1, PathIncr}];
    collision[p1_, p2_] := EuclideanDistance[p1, p2] < COLLIDETHRS;     success = success || collision[{t + p1, f[t]}, {PiX, PiY}];     success = success && t > TSTART;
    pi = If[success,
        Style[\[Pi], Bold, Pink, PiSZ], {PiX, PiY}], -30 Degree],
      Text[Style[\[Pi], Bold, Green, PiSZ], {PiX, PiY}]];

      Text[Style[1/2, Bold, Red, ratSZ], {t + p1, f[t]}],
     PlotRange -> PlotRng,
     PlotRangePadding -> PlRngPad,
     ImagePadding -> None]
   {p1, -0.1, 0.1},
   {t, TSTART, TEND, Trigger},

Here is level 1 to try out by itself as a CDF. I needed to alter the structure because it seems Wolfram does not allow CDF’s containing DynamaicModule or With, which is a shame. This explains why Part 1 would not work.

Lou can download the entire game, integrated into the framework from Part 1. In the next installment I’ll add more levels, sound and make the whole game it a bit more polished.


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>