Difference between revisions of "AN1001 - Animation in SHIP"

From Serious Documentation
Jump to: navigation, search
(Creating the Visual Container)
({{Node|image}} Types and Construction)
 
(27 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= Animation in SHIP =
+
__TOC__
 +
== Animation in SHIP ==
 
Animation is a common element of many GUIs.  Whether a rotating fan, slowly filling indicator, or a glowing element, subtle animations can enhance a GUI and make it more professional and modern.
 
Animation is a common element of many GUIs.  Whether a rotating fan, slowly filling indicator, or a glowing element, subtle animations can enhance a GUI and make it more professional and modern.
  
Line 12: Line 13:
  
  
There are many ways to implement animation in [[SHIP]]; the attached project uses the concept of "grouped images" and demonstrates 4 different [[SHIP:Node:Timer|timer]] modes.
+
There are many ways to implement animation in [[SHIP]]; the attached project uses the concept of "grouped images" and demonstrates 4 different {{Node|timer}} modes.
  
 
== Animation Images ==
 
== Animation Images ==
Line 72: Line 73:
 
== Creating the Visual Container ==
 
== Creating the Visual Container ==
  
To display an 'image', one simply needs to add a 'box' to the layout and set the 'object property' to an 'image resource'. After doing this, you can easily adjust the image's 'position', 'opacity', 'alignment', and more by manipulating the corresponding properties on the 'box'.
+
To display an {{Node|image}}, one simply needs to add a {{Node|box}} to the {{Node|layout}} and set the {{Prop|box|object}} property to point to the {{Node|image}}. After doing this, you can easily adjust the {{Node|image}}'s position and other characteristics by manipulating the corresponding properties on the {{Node|box}}.
  
Here is an enclosing animation image [[SHIP:Node:box|box]] from the example project:
+
Here is an enclosing animation {{Node|box}} from the example project:
  
[[File:AN1001D.png|border|center|AN1001 - Enclosing Image Box]]
+
[[File:AN1001D.png|border|center|690px|AN1001 - Enclosing Image Box]]
  
Notice it is simply a horizontally and vertically centered box within it's parent box, with the first of the three images attached to its {{Prop:box:object|object}} property.  This sets up the box's width/height/position at build time (vs. run time) to match the images we're animating.   
+
Notice it is simply a horizontally and vertically centered box within it's parent box, with the first of the three images attached to its {{Prop|box|object}} property.  This sets up the box's width/height/position at build time (vs. run time) to match the images we're animating.   
  
When the GUI runs, this first image will be displayed statically in the page.  If you do not want this image visible at all until some signal or condition, just hide the box by setting its {{Prop:box:visible|visible}} property to <code>false</code> in SHIPTide, then set it to <code>true</code> in a SAIL script when you want the image displayed.
+
When the GUI runs, this first image will be displayed statically in the page.  If you do not want this image visible at all until some signal or condition, just hide the box by setting its {{Prop|displayable|visible}} property to <code>false</code> in SHIPTide, then set it to <code>true</code> in a SAIL script when you want the image displayed.
  
== Creating the Animation Timer ==
+
== The Animation "Engine"  ==
  
== Animating the Image ==
+
To animate the image, we need to rotate the {{Prop|box|object}} property of the {{Node|box}} container through the various {{Node|image}}s in the {{Node|group}}. 
  
There are various ways to create the effect of an animated image in SHIPTide, but this section will discuss the preferred method.
+
This requires only a four basic elements:
 +
* a {{Node|variable}} that continuously counts up so we can index, modulus the number of images, what image we want to display in the sequence
 +
* a {{Node|timer}} that runs the animation frame rate
 +
* a {{Node|listener}} that watches for the timer to alarm,
 +
* a {{Node|script}} that increments the {{Node|variable}}, switches the {{Node|image}}, and restarts the {{Node|timer}}
  
= Description =
+
Here are those 4 elements from the example project:
We are going to group our image resources in such a way that we will be able to use a single variable to display the correct image from the animation sequence at a specific interval.
 
  
We will be using a 'group' to hold a set of 'images', a 'variable' to keep track of the next image, and a 'timer' to trigger a 'listener' that will run a 'script' to change the 'object property' on the 'box' that is holding our image.
+
[[File:AN1001E.png|border|690px|AN1001 - The Animation Engine]]
  
Inside the 'script', we will use the [[SHIP:Sail:getChildCount|getChildCount()]] and [[SHIP:Sail:getChild|getChild()]] functions.
+
In this example, the '''fanValue''' {{Node|variable}} has data type {{DataType|Integer}}.  Every time the {{Node|timer}}'s {{Prop|timer|alarm}} goes high (indicating the {{Node|timer}} has expired), the {{Node|listener}} wakes up.  Notice how the {{Node|listener}}'s {{Prop|listener|condition}} is set to filter the event only when the {{Prop|timer|alarm}} is high.
  
= Step 1 =
+
The {{Node|script}}, well documented right in the code in this example, increments the '''fanValue''' {{Node|variable}} every time the {{Node|script}} is executed.
Create a 'group' to refer to the set of images that make up your animation.
 
  
= Step 2 =
+
At the heart of this {{Node|script}} is this expression:
Add the 'image' resources that make up your animation to the group.
 
  
NOTE: It is very important that the images are sorted in the correct sequential order after being added to the group.
+
box.object = [[SHIP:Sail:getChild|getChild]](fanGroup, fanValue % [[SHIP:Sail:getChildCount|getChildCount]](fanGroup));
  
= Step 3 =
+
This fetches the image node from the '''fanGroup''' {{Node|group}} based on the index in the second parameter.  In this case, the script takes the current value of '''fanValue''' modulus the number of images.
 +
 
 +
If you examine the [[SHIP:Sail:getChild|getChild()]] function parameter reference, the function automatically does this modulus.  So the script can be written simply as:
 +
 
 +
box.object = [[SHIP:Sail:getChild|getChild]](fanGroup, fanValue);
 +
 
 +
The result of [[SHIP:Sail:getChild|getChild()]] is assigned to the {{Node|box}}'s {{Prop|box|object}} property.  The LCD display will automatically be refreshed if the image is a different one than prior assigned.
 +
 
 +
Finally, the {{Node|script}} restarts the animation {{Node|timer}}. See the {{Node|timer}} reference for information on the 4 different {{Node|timer}} operating modes and how to restart the {{Node|timer}} in each mode.  The example project not only shows how to animate an image, but also demonstrates all 4 {{Node|timer}} modes.
 +
 
 +
== Starting and Stopping the Animation ==
 +
 
 +
You can start and stop the animation simply by starting and stopping the animation timer. In the example project, touching the touchscreen in the containing {{Node|box}} enables the {{Node|timer}}:
 +
 
 +
[[File:AN1001F.png|border|690px|AN1001 - Starting/Stopping the Animation with a Touch]]
 +
 
 +
In this case, the {{Node|script}} just inverts the {{Node|timer}}'s {{Prop|layoutarea|enabled}} property, pausing/resuming the {{Node|timer}}.
 +
 
 +
== Optimization and Performance ==
 +
 
 +
There are several factors to consider to ensure the animation is as light-weight as possible.
 +
 
 +
=== Enable {{Node|timer}}s and {{Node|listener}}s only when Needed ===
 +
Ensure your {{Node|timer}}s and {{Node|listener}}s are disabled when the animation is not required.  Setting the enclosing {{Node|box}}'s {{Prop|layoutarea|enabled}} property to <code>false</code> will turn off all {{Node|listener}}s inside that container.  However, the {{Node|timer}} will still run, so make sure the {{Node|timer}}'s {{Prop|layoutarea|enabled}} property is also set  <code>false</code>.
 +
 
 +
=== Use a Global Animation {{Node|timer}} and Counter {{Node|variable}} ===
 +
To minimize resources, declare a global {{Node|variable}}/{{Node|timer}}/{{Node|listener}}/{{Node|script}} set that can drive all animations.  For example, the following structure:
 +
 
 +
[[File:AN1001G.png|border|690px|AN1001 - Setting up a Global Animation Engine]]
 +
 
 +
is a single global structure that could drive the animations for the whole GUI.
 +
 
 +
Each animation, then, would only have a single {{Node|listener}}/{{Node|script}} pair {{Prop|listener|listeningto}} the global {{Node|variable}} '''animEngine'''.
 +
 
 +
For example, if we modified one of our fans in the demo to use this global counter, the {{Node|listener}}/{{Node|script}} pair would look like this:
 +
 
 +
[[File:AN1001H.png|border|690px|AN1001 - Using a Global Animation Engine]]
 +
 
 +
Notice how much simpler the structure is.  Also, enabling/disabling the animation only involves turning on and off the {{Node|variable}} {{Node|listener}}. 
 +
 
 +
=== {{Node|image}} Types and Construction ===
 +
 
 +
Image rendering speed factors in to the overall performance of the animation and the GUI, especially on larger images or more limited hardware platforms without hardware graphics controllers.
 +
 
 +
Pixels are rendered on these software-rendered platforms one by one.  In order of overhead/complexity:
 +
* fully transparent pixels are very fast -- they do nothing!
 +
* fully opaque pixels are fast -- they are just written to the frame buffer
 +
* alpha blended pixels (partly opaque) are the slowest -- the current frame buffer value needs to be read, blended with the new color value, then written back
 +
 
 +
Therefore the fastest animations will come from images that are (a) fully opaque, or, (b) have pixels that are either fully opaque or fully transparent.
 +
 
 +
[[Category:Getting Started with SHIP]]

Latest revision as of 14:19, 2 November 2016

Animation in SHIP

Animation is a common element of many GUIs. Whether a rotating fan, slowly filling indicator, or a glowing element, subtle animations can enhance a GUI and make it more professional and modern.

At the core of every animation is a series of images that, when rotated through in-place one after another, form the impression of an animated object.

Animation in SHIP is the process of cycling through a series of images at a certain rate, making each one visible in sequence with the others invisible.

SHIPTide Project Files for AN1001

Assuming you've downloaded and installed the SHIPTide Rapid GUI Development Environment, download the following zip file. Unzip the projects to a directory of your choice and open up the project corresponding to your target platform in SHIPTide.


There are many ways to implement animation in SHIP; the attached project uses the concept of "grouped images" and demonstrates 4 different timer modes.

Animation Images

Just like the cartoon drawings you might have drawn as a child on a corner of a book and "flipped" through to animate, these images will be sequentially shown within the runtime SHIPEngine environment. Therefore, as you create the images in Photoshop or your graphics design program of choice, ensure that each image transitions to the next in exactly the right location within the borders of the image boundary.

Creating a set of Animation Images

Each animation image should be exactly the same size -- in this example project the fan images are all exactly 75 pixels square. Here they are, all in fully transparent alpha-blended .png format:

Example Rotating Fan Image, Part 1/3Example Rotating Fan Image, Part 2/3Example Rotating Fan Image, Part 3/3

In this example the three fan images are not only the same size (75 x 75 pixels), but the actual drawings within the image boundaries are in exactly the same location just rotated. If one of the fan images were shifted to the right (for example) you would see horizontal jitter in the animation.

This may be a desired effect, for example if you wish to animate an arrow moving from left to right towards a target. But for many animations you will want to pick a drawing center-point and ensure the various versions of the image are centered on that point within the image boundaries.

Image Formats and Optimizations

In SHIPTide, look in the Project Resources pane and find the three fan images. Click on one of them and examine the properties of the image in the Properties pane:

AN1001 - Examining an Image

All images in SHIPTide need to be pre-scaled to the desired size for the GUI. SHIPTide does not scale images because on small LCD screens generic auto-scaling algorithms rarely produce attractive results. Edges often get very jagged and distorted.

SHIPTide does not attempt to replace high-end graphics design tools -- you will want to use tools like Adobe Photoshop, Fireworks, or Illustrator to develop your icons, backdrops, and images. You can even use Microsoft PowerPoint to generate some images -- beveled buttons are surprisingly easy to create and export in PowerPoint! Illustrator is particularly powerful as you can create your source images in vector format, scale them well, export them to a fixed size and then use an image editing tool like Photoshop to clean up any residual issues.

Small image and icon creation is an art. Sites like Icojam, IconFinder, DryIcons, and many, many more (browser-search for "icons" and you will see!) feature innumerable talented small-image artists -- many can be contracted to develop your images and ensure you have sufficient legal rights to use them.

You will typically use .png or .gif images with transparency so that the animation can alpha-blend on top of any background. While the example project uses a fixed color background, you can change this background color or even layer a background image underneath the fan animation and the fan will rotate and "float" over the background with the background showing through the non-blade part of the fan images. Here is the same fan image over top of an image where you can see the underlying image coming through:

AN1001 - A PNG Image with Transparency on a Background

In almost all graphic design tools images can be saved with an 8- or 32-bit color depth, with or without alpha-transparency. Here is a brief chart of the various image formats and their capabilities:

Format Color Depth (Bits) Alpha Transparency
BMP 8,32 No
JPG 24 No
GIF 8 Yes
PNG 8,32 Yes

SHIPTide can read and use all these formats. At Serious we generally prefer 8- or 32-bit PNGs -- PNGs are a superset of the other formats.

Try creating your images with 8-bit color depth (with alpha transparency) and see if there is sufficient color depth to create a viable result -- the memory requirements and access times of these smaller 8-bit images are generally advantageous in your runtime GUI environment. For even more speed-sensitive applications, you can pre-blend the image(s) onto the single desired background and save the image as a non-transparent png/gif/jpg/bmp. These image, while not as flexible since they cannot be dropped on top of various backgrounds, will have no transparency channel and will not require run-time alpha blending -- a faster and simpler operation.

Grouping the Images

The three fan images, instead of being placed directly in the resources/images area, are placed in a named group. Exploring the project in SHIPTide the group looks like this:

AN1001 - Grouped Images

We'll explain why this is important when we demonstrate how the images are assigned to a container visually. A group of images can function as a basic indexed array and there are functions that can extract and assign these to visual containers.

Creating the Visual Container

To display an image, one simply needs to add a box to the layout and set the object property to point to the image. After doing this, you can easily adjust the image's position and other characteristics by manipulating the corresponding properties on the box.

Here is an enclosing animation box from the example project:

AN1001 - Enclosing Image Box

Notice it is simply a horizontally and vertically centered box within it's parent box, with the first of the three images attached to its object property. This sets up the box's width/height/position at build time (vs. run time) to match the images we're animating.

When the GUI runs, this first image will be displayed statically in the page. If you do not want this image visible at all until some signal or condition, just hide the box by setting its visible property to false in SHIPTide, then set it to true in a SAIL script when you want the image displayed.

The Animation "Engine"

To animate the image, we need to rotate the object property of the box container through the various images in the group.

This requires only a four basic elements:

  • a variable that continuously counts up so we can index, modulus the number of images, what image we want to display in the sequence
  • a timer that runs the animation frame rate
  • a listener that watches for the timer to alarm,
  • a script that increments the variable, switches the image, and restarts the timer

Here are those 4 elements from the example project:

AN1001 - The Animation Engine

In this example, the fanValue variable has data type Integer. Every time the timer's alarm goes high (indicating the timer has expired), the listener wakes up. Notice how the listener's condition is set to filter the event only when the alarm is high.

The script, well documented right in the code in this example, increments the fanValue variable every time the script is executed.

At the heart of this script is this expression:

box.object = getChild(fanGroup, fanValue % getChildCount(fanGroup));

This fetches the image node from the fanGroup group based on the index in the second parameter. In this case, the script takes the current value of fanValue modulus the number of images.

If you examine the getChild() function parameter reference, the function automatically does this modulus. So the script can be written simply as:

box.object = getChild(fanGroup, fanValue);

The result of getChild() is assigned to the box's object property. The LCD display will automatically be refreshed if the image is a different one than prior assigned.

Finally, the script restarts the animation timer. See the timer reference for information on the 4 different timer operating modes and how to restart the timer in each mode. The example project not only shows how to animate an image, but also demonstrates all 4 timer modes.

Starting and Stopping the Animation

You can start and stop the animation simply by starting and stopping the animation timer. In the example project, touching the touchscreen in the containing box enables the timer:

AN1001 - Starting/Stopping the Animation with a Touch

In this case, the script just inverts the timer's enabled property, pausing/resuming the timer.

Optimization and Performance

There are several factors to consider to ensure the animation is as light-weight as possible.

Enable timers and listeners only when Needed

Ensure your timers and listeners are disabled when the animation is not required. Setting the enclosing box's enabled property to false will turn off all listeners inside that container. However, the timer will still run, so make sure the timer's enabled property is also set false.

Use a Global Animation timer and Counter variable

To minimize resources, declare a global variable/timer/listener/script set that can drive all animations. For example, the following structure:

AN1001 - Setting up a Global Animation Engine

is a single global structure that could drive the animations for the whole GUI.

Each animation, then, would only have a single listener/script pair listeningto the global variable animEngine.

For example, if we modified one of our fans in the demo to use this global counter, the listener/script pair would look like this:

AN1001 - Using a Global Animation Engine

Notice how much simpler the structure is. Also, enabling/disabling the animation only involves turning on and off the variable listener.

image Types and Construction

Image rendering speed factors in to the overall performance of the animation and the GUI, especially on larger images or more limited hardware platforms without hardware graphics controllers.

Pixels are rendered on these software-rendered platforms one by one. In order of overhead/complexity:

  • fully transparent pixels are very fast -- they do nothing!
  • fully opaque pixels are fast -- they are just written to the frame buffer
  • alpha blended pixels (partly opaque) are the slowest -- the current frame buffer value needs to be read, blended with the new color value, then written back

Therefore the fastest animations will come from images that are (a) fully opaque, or, (b) have pixels that are either fully opaque or fully transparent.