Home > MHEG > MHEG+ Game Development Tutorial, Part #2 — Animation

MHEG+ Game Development Tutorial, Part #2 — Animation

December 14, 2008 Leave a comment Go to comments

In part #1 of this series we built a basic MHEG+ scene which was capable of firing timer events at a consistent rate.  In this part we will look to extend the code by adding an animating graphic to the scene.

A further aim of this part is to explain some more of the language features exclusive to MHEG+, including foreach loops, sequential blocks and ifs.

Adding our graphics to the scene

I have hand crafted the following 5 png graphics to use for this tutorial:

pacs

Using an MHEG+ foreach loop, we can add all these graphics to our scene in one fell swoop:

  foreach num (0..4) {
    {:Bitmap bmpPac<num> = [constPacObjectIdOffset + <num>]
     :OrigPosition          [(720 - 32) / 2] [(576 - 32) / 2]
     :OrigBoxSize           32 32
     :OrigContent           :ContentRef ('/tut/<num>.png')
     :InitiallyActive       false
    }
  } endfor

There a number of subtleties of this loop which are probably worth discussing:-

  • foreach loops are preprocessor instructions, not run-time loops.  Because of this we can use foreach loops around object declarations, or in fact any code section at all.
  • In this case, the contents of the foreach loop will be duplicated 5 times, this is controlled by the (0..4) range on the first line.  This range need not be numeric, text may also be used, for example: foreach key (red, green, yellow, blue) is allowed.
  • The Bitmaps we declare will be named bmpPac0 through bmpPac4, <num> is substituted with each value in the range.
  • The object identifiers for the Bitmaps will be 100 through 104, assuming constPacObjectIdOffset has the value 100.  We use the MHEG+ notation: objectname = objectid to declare both names and object identifiers for our Bitmaps.
  • <num> is also substituted in the png filename

You may also have noticed that our 5 Bitmaps have :InitiallyActive set to false.  We will later call :Run and :Stop on our Bitmaps to control which frame of the animation is visible, so they do not need to be running at startup.  However we do still want to pre-load our Bitmaps when the scene loads in order to have them prepared in memory and ready to use, so we add the following to lnkStartup:

  // preload graphics
  foreach num (0..4) {
    bmpPac<num>.Preload()
  } endfor

Here we are using a foreach loop not for ingredient declaration but to duplicate a line of code in a link effect.  Again, <num> is substituted appropriately.

Drawing each frame

As in part #1 of this series, we will use the Link lnkGameTick to calculate and render each frame of our game.  The first thing we did in lnkGameTick is set the next timer event, this is to ensure our game runs at a fairly consistent pace.

We now extend lnkGameTick to call out to two sequentials.  The first sequential “calculates” the next game tick, the second renders it to the screen.  We therefore add the following to lnkGameTick:

  tgPac.CallActionSlot(calculateNextFrame)
  :LockScreen()
  tgPac.CallActionSlot(updateScreen)
  :UnlockScreen()

(Note: Games often separate the concerns of calculating a frame from rendering it.  In an environment other than MHEG this could allow us to choose whether to render the current frame (which is normally an expensive operation) to keep the game tick rate consistent.  MHEG however without precision timing does not allow us this freedom, but separating along these lines makes our code more maintainable.)

Note that we call :LockScreen and :UnlockScreen around the call to updateScreen.  This effectively tells the MHEG engine to render our new frame off-screen and display the changes only when we call unlock.  This is akin to Double Buffering and helps reduce screen flicker.

We still need to implement the sequentials calculateNextFrame and updateScreen, which I’ll describe in the next two sections.

Calculating what to display

For each game tick we need to determine what should be visible on the screen.  In the case of our animation of the ‘Pac’ character, this means determining which of our 5 Bitmap ingredients should be visible.

So the effect of the calculateNextFrame macro should be to modify an integer variable whose value will later be used to identify the Bitmap to display.  We’ll call this integer intNextPacObjectId.

Here is the necessary code:

  defSequential calculateNextFrame :Namespace tgPac {
  (
    // increment counter
    intCount.Add(1)
    intCount.Modulo(constNumFrames)

    // calculate object Id of Pac Bitmap to display this frame
    intNextPacObjectId.SetVariable(:IndirectRef intCount)
    :If (intNextPacObjectId >= constNumBitmaps) {
      intNextPacObjectId.SetVariable(constNumFrames)
      intNextPacObjectId.Subtract(:IndirectRef intCount)
    }
    intNextPacObjectId.Add(constPacObjectIdOffset)
  )
  } endSequential

Of note in this code section:-

  • The sequential declaration states which namespace it belongs to.  When we call a sequential we use the notation: namespace dot CallActionSlot( sequential ), as we do in lnkGameTick.
    In MHEG+ we also must declare the tgPac token group and call :InsertSequentials, see the complete code listing below for example code.  An MHEG+ sequential is a façade to an MHEG ActionSlot.  Sequentials though can be declared anywhere in the source file and have cleaner syntax.
  • Did you notice the :If?  MHEG+ gives us the syntatic sugar of being able to declare inline If blocks.  In classic MHEG our only way of implementing conditional code sections was to use the :TestVariable action and write separate :Links to perform actions based on the evaluation of the test.  In fact this is what MHEG+ does behind the scenes, but the abstraction is very handy.  We can also write :Else and :ElseIf blocks.

Updating the screen

Now that we have calculated the object Id of the Bitmap to display and stored this in an integer, we need to write a sequential to hide the current Bitmap that is displayed and show the new one.

Only by a call to a resident program can we translate an integer value into a reference to an actual object, the resident program to do this is called CastToObjectReference, or CTO.

Here is our implementation of this sequential:

  defSequential updateScreen :Namespace tgPac {
  (
    // stop the bitmap of previous frame
    :Stop(:IndirectRef objPac)
    prgCastToObjectRef.Call(boolTemp
        "/tut/tut2.mhg" :IndirectRef intNextPacObjectId
        :IndirectRef objPac)
    // run the bitmap of this frame
    :Run(:IndirectRef objPac)
  )
  } endSequential
  • We call the prgCastToObjectRef resident program in order to retrieve a handle to an object given its object Id. We pass an integer (which will be in the range 100 to 104) and dynamically gain access to the object with this identifier. This is why we explicitly stated the our Bitmaps’ object Ids when we declared them.
  • Note that we can’t use object dot action notation when dealing with object references (objPac is an object reference variable), this is a limitation of MHEG+.  This we must write :Run(:IndirectRef objPac) and not objPac.Run().

Putting it all together

Hopefully it should be fairly clear by now what the scene does.  At the middle of the screen we see the following animation:

pacanim

Because we have used :LockScreens and tried to keep the work done whilst the screen is locked to a minimum, the animation will be fast and practically flicker free.

Looking at the complete listing (see Resources section below) it would be true to say that MHEG+ is not the cleanest of programming languages. However it is a lot more readable and maintainable than traditional MHEG.

The source listing really boils down to two key sequentials: calculateNextFrame and updateScreen.  We would be wise to move each of these sequentials into separate files and develop them somewhat independently of eachother and the rest of the scene.  MHEG+ allows us to use import declarations in order to better manage our source code.

Where to from here?

This is a good starting point for a game.  Of course there is still plenty to do, including:-

  • Moving the sprite around the screen
  • Rotating the sprite when it changes direction
  • Adding a maze
  • Adding other objects to the maze

There are also a number of  MHEG+ features we have yet to use including macros (both parameterised and not), state machines and imports.

In later posts I hope to explore these themes and also discuss the tools that make up the MHEG+ SDK including the MHEG Player (emulator), MHEG+ compiler and the Eclipse editor plug-in.

Resources

Complete source listing as a PDF: tut2.pdf.

Animation graphics (0.png through 4.png):

0.png

1.png2.png3.png4.png

Other posts in this series:
Part #1 — Timing

About these ads
  1. Derek
    January 2, 2009 at 9:40 am | #1

    Hi
    I’m not that sure about the function “prgCastToObjectRef”,
    I try to rewrite that using the old way of “CTO”

    …..
    {:Link 1014
    :EventSource 0
    :EventType TimerFired
    :EventData 1 // timer id = 1
    :LinkEffect (
    :Activate(1030)
    )
    }

    {:Link 1030
    :InitiallyActive False
    :EventSource 1030
    :EventType IsRunning
    :LinkEffect (
    :Call ( 404 5009
    :GOctetString :IndirectRef 112
    :GInteger 0
    :GObjectRef 106)
    //…. set share value and draw it again
    :TransitionTo(:IndirectRef)

    )
    }

    {:BooleanVar 5009
    :Shared True
    :OrigValue False
    }

    {:OStringVar 112
    :Shared True
    :OrigValue “/tut/tut2.mhg”
    }

    {:ObjectRefVar 106
    :Shared True
    :OrigValue :ObjectRef 106
    }

    {:ResidentPrg 404
    :Shared true
    :Name ‘CTO’
    }

    …..

    I have problem with “Transition”, it seems the mheg engine complain about “TransitionTo: unable to transition to an internal reference”
    Any idea?

    Thanks again, your tutorial is great

  2. Deano
    January 5, 2009 at 3:31 am | #2

    Hey, can you point me towards Mheg-5 language refrences?..

    Tho, i’m not sure if such documentation exist’s, apart from Ian’s(digvid.info) Hello World script, but that’s like learning Spanish – and the only thing you get tought is how to say hello. Lol.

    Thanks.

  3. purple floyd
    January 5, 2009 at 5:51 pm | #3

    @Derek – prgCastToObjectRef is a reference to the CTO program but MHEG+ allows us to give it a meaningful name. In your MHEG you have similarly declared 404 to be a reference to CTO. If you look at the complete source listing (linked to from the article as a PDF), prgCastToObjectRef is declared near the top.

    With regards to the error you are getting, I’m guessing your TransitionTo statement should be:

    :TransitionTo(:IndirectRef 106)

    Correct me if I’m wrong, but I believe you are getting the error because you are trying to transition to the scene you are already in, this is why the engine is reporting it to be an internal reference?

    Have you tried not using CTO and just using a static reference instead? Like:

    :TransitionTo((“/tut/tut2.mhg” 0))

    @Deano – I’m not aware of a language reference being publicly available I’m afraid. Is anyone else?

  4. Luigi
    April 10, 2009 at 2:22 pm | #4

    The MHEG language is defined into the standard document “ISO 13522-5 Information Technology – Coding of Multimedia and Hypermedia Information – Part 5: Support for Base-Level Interactive Applications”

    Is there any news about the availability of the MHEG+ development toolkit? I would like to know something more and, if possibile, try it.

    Ciao
    Luigi

  5. Rajat
    August 17, 2010 at 12:42 pm | #5

    Sir,

    I am new on MHEG,so i need your help.Please tell me how to use for,if and other loops in MHEG.and also tell me how to run video and audio via programming.

    If you have any document related to this please refer me.

    Thanks
    Rajat

  1. December 15, 2008 at 1:31 pm | #1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: