Randomizing in Captivate

Intro

Randomizing exists in some limited situations in Captivate:

  • Question pools allow to add randomized quiz slides to a project.
  • Shuffle feature for several question types can be seen as randomizing.

However in all other situations you need to use JavaScript to get a randomized number (or text). This short blog is meant as an answer to a user request in the eLearning community, to be found under this link. In my blog you will find multiple examples of randomizing for games. This is a very simple example since the user only wants to have a random card chosen from a deck on clicking the deck. A second click on the deck needs to flip back to the cover of the cards.

Example file

There is only one slide in this project besides the Title slide. The three (tarot) decks are identical, have 14 cards. Try it out. You may get some ideas how to use this workflow for games. In the future I will post more examples of this workflow in a bigger tutorial project. 

This example can be watched below (fixed size) or you can click this link for a rescalable version.


Step-by-step workflow

The timeline of the tarot slide shows the three decks. Each deck has a Click box on top of the deck, which will trigger an advanced action with embedded JavaScript. I hear you exclamations! Why not use the deck itself (PNG image) as interactive object. It is impossible because JS is used to change the state of the object, and it is impossible in that case to use the image itself as button. Of course that is annoying, because a responsive project with Fluid Boxes will not allow stacking of the click box with the multistate object in the same location. You would need a button in another location. For a non-responsive project (like this example) it is not a problem.

Multistate object (deck)

Decks are multistate objects with 15 states. The Normal state shows the cover. Due to the script the labeling is important for the other custom states. They are all labeled Cardx  where x is a number corresponding with the rank of the card. The three decks in the example are identical, but you can have decks with a different number of cards, just use the same logic for the numbering. Look at the Object state panel for Deck1:

Variables

In Captivate I need only one variable for each deck to follow up the status: is it showing the cover, or a random card? Two possibilities means that I can use a Boolean variable. When the cover is visible, the variable has the value 0, for a random card it has the value 1.

Advanced Actions

Click boxes trigger a conditional advanced action. Here is the action for the first click box (CB_1) over Deck1:

The Boolean variable v_1 is checked. If it has the value 0, a random card needs to be shown which is done by a JS script (see below), and the variable is toggled to 1. If it has the value 1, the cover (which is the Normal state) is shown. 

For the second and third deck, the variable and the name of the deck need to be edited.

Javascript

The used trick is to create the name of the state by concatenation of two strings:

  • First string is always 'Deck1'.
  • Second string is a random number between 1 and 14, converted to string.

I have explained in depth the use of Math.floor(Math.random()*(max-min))+min) in an older blog post:

Playing-with-numbers-part-1

That random number is converted to a string with a JS method. Result of the concatenation is the name of one of the states in the deck multistate object. 

For the second and third deck, the deck name need to be changed in this script. If you do have more or less cards in those decks you need to edit the maximum number (here set at 15).

More ideas?

This basic example may have ignited your creativeness? What about creation of a jackpot game? A funny mathematics exercise for your kids? A board game where you use a dice? 

For this use case it is impossible to create a shared action. One of my long-standing feature requests for actions is the possibility to generate a command using concatenation. A second one: being able to change a state based on a variable.






Display Time (CP2019)

Intro

Almost 10 years ago I wrote a blog post to answer user question  quote: “…. if there was anyway to insert running time”.
The user wanted a display on all slides of the time information you can find  in the TOC (total duration of the project and elapsed duration).  Total duration can be found also in the panel ‘Project Info’ in Captivate, which shows global project information (number of slides/frames). In this panel you can also check the expected size of the output file.
Total duration is calculated as the sum of slide durations. Similar the elapsed duration:is calculated as the sum of the duration of the slides before the currently viewed slide. I would call that type of duration ‘developer time’. 

Developer time can be very different from the real time spent by the user viewing slides in that project.  You’ll see in this refurbished article how to show both times, developer and real ones. It is a nice use case for System variables.  BTW, if you didn’t get my free table with System variables, maybe it is the right moment .

Contrary to the old post, which was meant for SWF output, I used JS this time instead of advanced actions with the Expression command.  Reason is that for SWF output the formatting of the variable values could be controlled by the number of characters to display when inserting the variable in a text container. That is not working for HTML5 output. Formatting of numbers is a typical use case (like random data) where I tend to use JavaScript.

Example output

Watch this published movie. The described workflow (from the original post) is working only for linear projects. Sorry for the use of TTS, those slides are based on a longe project….in Dutch.



 
The ‘developer time’ is shown in the top left corner: total project time (secs), time of the previously viewed slides (secs) and percentage viewed.

Real time workflow

The real time is visible at the bottom, next to my name. I used the Timer widget for the real time.  You have to be careful: set the maximum time to a high number, to avoid triggering the end of time action. Getting rid of the background is easy, have a look at my setup.:
 
 

Developer's time workflow

As I announced at the start, for several reasons I preferred to use JS instead of Expressions in an Advanced action as was the case for the older blog post. I propose two scripts. The first one is close to the older blog post, because I use the same system variables, and two methods of the JS interface for Captivate. Details can be found in this document.

In the second script I skipped using the known system variables, replaced them by some more methods of the same document.

Script 1

Variables

The system variables used in the first script are:

  • cpInfoFrameCount: its value is the total number of frames in the Project Info panel (see first screenshot in this article)
  • cpInfoFPS: each movie is played at a certaing speed, which defines its qualtiy. It is indicated as Frames per Second. The higher that rate, the higher the quality
  • cpInfoCurrentFrame: indicates the present frame number. Frames are numbered, starting with 0, not per slide but for the whole project. More details can be found in this blog post about Micro-navigation

In Captivate I created these user variables to display the results in the project:

  • v_total: total duration of the project in seconds
  • v_done: sum of the duration of the previous slides in seconds
  • v_perc: percentage of duration viewed, in %

To avoid confusion, variables in JavaScript used these names:

  • frames: will start with the value of cpInfoFrameCount and end after calculation return the total project duration (secs) 
  • start: will start with a frame number (frist frame of the present slide) and after calculation return the alreadu viewed duration (secs)
  • speed: will store the value of cpInfoFPS
  • percent: will return the percentage viewed after calculation

Javascript

This script has to be triggered On Enter for each slide. That can be directly with the command 'Execute JavaScript' as simple action if you don't need any other commands to be done on entering the slide. If no slide nneeds any extra commands, you can select all slides in the Filmstrip and attach the script to all slides at once. If you need more for some slides, it could be a good idea to use the script in an advanced action, where you can add more commands. Even a shared action is possible, but in this example had no real sense.

Have a look at the script:

I am using two methods from the Common JS Interface:

  • getVariableValue to store the values of Captivate's system variables in the JS variables frames, speed, start. (lines 1-3)
  • setVariableValue to return the results to the Captivate variables v_total, v_done and v_perc (lines 6-7,9)

The calculations in lines 4-5 and 8 are straightforward. To control the formatting of the numbers I used the method 'Number.toFixed(x)'. I didn't want to display two decimals for a simple reason: the first frame will be detected on each slide, and that would have led to a duration of 0,03 seconds on the first slide (30 seconds in a frame). It would be possible to split up the results in minutes and seconds as well. I wanted this example to be a simple introduction to JS newbies. I planned to use the formatting in a third article about numbers. The first two were 'Playing with numbers - part 1' and 'Playng with numbers - part 2'. Both were meant as simple intro to JS for newbies. However, since they didn't get a lot of viewers, I gave up spending more time on it.

Script 2

No system variables here, the user variables and variables in JS are the same as in the first script: v_total, v_done, v_perc, frames, speed, start, percent.. Have a look at the script:

I didn't need the method getVariableValue in this case but used three more methods on top of setVariableValue to return the results to Captivate:

  • getPlaySpeed: returns the same value as the system variable cpInfoFPS (line 1)
  • getCurrentFrame: returns the same value as the system variable cpInfoCurrentFrame (line 2)
  • getDurationInSeconds: spares me one calculation, this value doesn't exist as system variable. This time I don't have to divide the total number of frames (exists as getDurationInFrames) by the FPS value. (line 3)

The other lines in the script are similar to those explained in the first script.



Playing with Numbers - part 2

Intro

In the previous blog post I introduced you to the common JS API, and you used the method 'setVariableValue' to populate a variable defined in the Captivate project. That value was a random number generated in the JS window, using the Math.random() and the Math.floor methods. The values were integers, between fixed numbers 10 (included) and 100 (not included). In this second article you'll learn to know a new method 'getVariableValue' from the JS API, which lets you transfer a value to JS. We started with the mathematical operation 'Sum', now we'll switch to 'Subtractions' needing again the command 'Expression'. Because CP cannot handle well negative values, we'll have to take care of that as well.

The progress bar introduced with the Sum exercises, will be continued. You'll see that it is possible to change the state of an object, displayed for the rest of the project, from a slide where that object is not inserted. 

Scenario Subtract Slide

Subtraction will be with two terms, always integers. Whereas in the Sum slide I decided that all terms would have to be between 10 and 100, on this slide the user will be able to choose a Maximum number, and if he wants a Minimum number. The minimum number has already a default value of 10, which can be changed by the user, the maximum number is empty. The Max value needs to be bigger than the Min value, if that is not the case, the Max value will automatically be set to 'Min + 200'.
The same sticky figure (with states) and OK button (with state New) are used as described in the previous post for the Sum slide.
This Subtract slide can only be visited when 5 correct Sum exercises have been done, hence the 5 stars on the progress bar are in place on entering this slide. The progress bar is set to display for the rest of the project, and you can change the state (to 6 or more stars) from this Subtract slide, although the progress bar is not visible in the Timeline. New stars can be added after a correct subtract exercise, and after 10 stars, the Next scenario is executed (same as on Sum slide).
Here is a slide preview, after answering the first correct subtract question:

Used objects

The same multistate objects are used as on the Sum slide (see state panels on previous post):

  • Sticky figure with its states and a motion effect for the minus sign
  • OK button, which changes to a New button after the first exercise
  • Progress bar, which has been displayed for the rest of the project.

In this slide I only used TEB's, which I can control due to the presence of the CpExtra widget (InfoSemantics). The consequence is that this description is only valid for HTML output. For SWF output the TEB can be replaced by a Scrolling Text Interaction:

  • TEB_max accepts only numbers and is empty when starting - associated variable is v_max
  • TEB_min accepts only numbers and has a default value of 10 (changeable)  - associated variable is v_min
  • TEB_result accepts only numbers - associated variable is v_result (reused from sum slide)

Two shape buttons, that are alternatively enabled/disabled:

  • SB_Number is the OK/New button, triggers the advanced conditional action SubtractTerms
  • SB_CheckSubtract is the Check button, triggers the advanced conditional action CheckSubtract
To make the next state of the sticky figure interactive, it is covered by a Click box CB_Next2.

This is the Timeline of the slide:

Variables

Some variables created for the Sum slide are reused:

  • v_one, v_two, v_three, v_four: are reused for the values of the two terms, which will end up in v_one (first term) and v_two; those variables will be reset by the On Enter action of the Subtract slide
  • v_result: will store the result value typed in by the user in TEB_result, has to be reset as well.
  • v_check: will be calculated and used to validate the result given by the learner. 
  • v_progress: is a counter for the correct answers, will be used to change the state of the progress bar; this variable is not reset but starts with the value 5 (end value after the Sum slide)
  • v_null: empty variable used to clear the content of the displayed result for a new sum (see Where is Null?)
  • xprefTEBUpdateFromVariable:  for HTML output, one of the CpExtra variables. When assigning the value 1 to this variable (with CpExtra widget loaded either in the file, or headless) it is possible to change the associated variable of a TEB by an action, and it will be reflected immediately in the TEB. I used this to clear TEB_Result.

New variables are:

  • v_max associated with TEB_max will store the maximum value for the terms

  • v_min associated with TEB_min will store the minimum value for the terms

Advanced Actions

Shared actions were not possible. One Standard advanced action was needed (to reset vars) and two Conditional actions.

EnterSubtract

This very simple standard action, triggered by the On Enter event of the slide, clears several variables that are reused from the Sum slide:

SubtractTerms

This conditional action is triggered by the OK/New button SB_Number. It has 3 decisions:

  1. "CheckCorrect" will compare the values stored in v_max en v_min. If the maximum value is not exceeding the minimum value, the maximum value will be calculated by adding 200 to the minimum value.

  2. "RandomNum" will generate two random numbers and store them in the variables v_three and v_four. Reason is that the last decision will have to put the largest number into v_one and  the smallest in v_two. This could have been checked in the JS script window as well, but I wanted to keep the JS as simple as possible and checked with the last decision. 

  3. "CheckMax" compares the values in v_three and v_four. The largest value will be assigned to v_one, the other to v_two. This decision also calculates the result of the subtraction to be stored in the variable v_check (will be used to check the user's entry).

CheckSubtract

This conditional action is triggered by the check shape button SB_CheckSubtract and has 6 decisions

The first decision 'Checker', will check the entered value (v_result) with the correct value (v_check). It is the only decision with a Then and Else part. For correct answer, the state of the sticky character is changed, the counter (v_progress) is incremented,  the state of the button SB_Number is changed to another state and enabled again, while the button SB_CheckSubtract is disabled. For an incorrect answer, the counter is not incremented, and the sticky character is changed to another state.

The 5 other decisions 'ProgressXxxx' check the value of v_counter and show the appropriate state for the progress bar. The last decision 'ProgressTen' will also change the sticky Character to its state GoNext and enable the click box to proceed to the next slide (subject of the next blog post, with Multiply exercises).


Javascript 

In the previous post you learned to use 'window.cpAPIInterfaceSetVariable (x,y)' method to populate a variable x (to be entered as a string between quotes) with a value y. You calculated y by using a combination of two Math methods in JS: 

  • Math.random() which generates a random number between 0 and 1 (not included)
  • Math.floor() which will convert a decimal number to an integer by rounding it down (cutting off the decimals).

For the sum slide we used a combination of those two methods, to end up with a random number between 10 and 100 (not included):

Math.floor(Math.random()*(100-10)+10))

For the Subtraction, the maximum and minimum values are not fixed, but stored in Captivate variables v_max and v_min. Due to the first decision in SubtractTerms action, we are sure that v_max > v_min. Those value will be transferred to JS variables with the method 'window.cpAPIInterfaceGetVariable(x)' where 'x' is the name of the Captivate variable, entered as a string (between quotes). Example:

var max = window.cpAPIInterfaceGetVariable("v_max");       stores value of v_max in JS variable max

Instead of the fixed values 100 and 10, used for the sum slide, you use this time min and max to generate a random number between min and max. The two random numbers are stored in variables v_first and v_second. Since they are random, we do not know yet which one has the largest value:

var first = Math.floor(Math.random()*(max-min)+min);

var second= Math.floor(Math.random()*(max-min)+min);

You are already familiar with SetVariableValue to store the result of first in v_three - Captivate variable, and second in v_four. 

window.cpAPIInterface.setVariableValue("v_three",first);
window.cpAPIInterface.setVariableValue("v_four",second);


Conclusion

You earned a second star, soon a third part will be ready, up to multiplications and bit more of randomness.

Playing with Numbers - part 1

Intro

How to use Adobe Captivate to create a course for simple calculations, has been popping up on the forums several times. You can use Text Entry Boxes, MCQ's to reach that goal, but that is pretty limiting. Some examples can be found in this older blog post where the real goal was to have the score being linked to the attempts.

I started using JavaScript in Captivate projects since the release of the common API for JS (version 8) whenever the advanced actions did lack the wanted functionality. For those who want to start using JS, I wanted to explain some very simple use cases in a sequence of 4 articles that will focus on the 4 basic operators that are available in the Expression command: sum, subtract, multiply and divide. Each of the posts will introduce a JS example. The Expression command is not available as a simple command from the dropdown list in the Actions tab, it is only available in advanced/shared actions. 

This article will focus on the Sum operator, and introduce JS to make it possible to generate random numbers. You'll also see how to assign that generated number to a Captivate variable with the function setVariableValue from the API. That will allow to use the same slide for several sum questions. I will use the Multi-state object feature of Captivate 9 several times as well. In the next parts you'll also learn about retrieving a Captivate variable value with getVariableValue, how to format a number to specific number of decimals, how to avoid dividing by 0 (zero).

Scenario Summing slide

User will be allowed to choose for sums consisting of 2 up to 4 terms. The number of terms can be changed after completion of one exercise.

For a correct answer, the stick figure will change to another state, and a star will be added to the progress bar (also a multi-state object). For an incorrect answer the stick figure also changes to a state but no star will be ended to the progress bar.

After completion of 5 correct sums, the stick figure will change to a Next button. The user could still add more questions, but no more 'stars' will be added to the progress bar. 

In this screenshot, from HTML output, you'll see the situation after one successful answer, second sum has been created with 4 terms:

Used objects

Multi-state objects

Stick Character: see image Object States in Library below

OK/New shape button: see image Object States in Library below

Progress bar: see image Object States in Library below 

Special objects

Radio buttons - works perfectly for SWF output but formatting is not preserved for HTML output; labeled "Wd_TermNumber"

Scrolling Text interaction for SWF output, labeled "Wd_SumResult". It is possible to change the shown content in this interaction by changing the associated variable (v_result). However that functionality only works for SWF, not for HTML output. For that reason I needed an alternative which is a  

Text Entry Box, labeled "TEB_result" combined with the CpExtra widget and a specific command variable for HTML output. Since there is a bug in Captivate 9, which prevents using a TEB multiple times on a slide, I used a workaround. The default Submit button was deleted. It was replaced by a custom shape button (SB_CheckSum in the timeline) to trigger an advanced action. In this case an easy workaround, since I used that same shape button for the Scrolling Text Interaction for SWF output. The associated variable for the TEB is the same as for the Scrolling Text Interaction (v_result). Nothing had to be changed to the advanced action 'CheckSum' when replacing the Scrolling Text Interaction by a TEB.

Variables

v_one, v_two, v_three, v_four: will get the values for the (possible) 4 terms of the sum which are generated by the advanced action 'SumTerms'

v_result: will store the result value typed in by the user in the Scrolling Text interaction (SWF) or the TEB (HTML)

v_check: will be calculated and used to validate the result given by the learner in the advanced action 'CheckSum'

v_progress: is a counter for the correct answers, will be used to change the state of the progress bar

v_null: empty variable used to clear the content of the displayed result for a new sum (see Where is Null?)

xprefTEBUpdateFromVariable: only for HTML output, one of the CpExtra variables. When assigning the value 1 to this variable (with CpExtra widget loaded either in the file, or headless) it is possible to change the associated variable of a TEB by an action, and it will be reflected immediately in the TEB. I used this to clear the result when defining a new sum.

Advanced Actions

In this particular case it was not possible to use shared actions. Two advanced actions, both conditional, were needed:

SumTerms, triggered by the OK button (SB_Terms) after choosing the number of terms

This conditional action has 4 decisions, the first 'Always' is a mimicked standard action (will always be executed), prepares for a new sum by clearing the result and the terms. It also resets the state of the sticky character.
The three other decisions will show the correct number of terms, based on the choice made by the user. In these decisions, a Javascript command is used to generate random numbers (see later).

CheckSum, triggered by the Check button (SB_CheckSum) to validate the answer

This conditional action has 7 decisions.

The first decision 'Always' will calculate the sum. It doesn't matter that it always sums four terms, since the lacking terms are empty.

The second decision 'Checker', will check the entered value (v_result) with the correct value (v_check). It is the only decision with a Then and Else part. For correct answer, the state of the sticky character is changed, the counter (v_progress) is incremented,  the state of the button SB_Terms is changed to another state and enabled again, while the button SB_CheckSum is disabled. For an incorrect answer, the counter is not incremented, and the sticky character is changed to another state.

The 5 other decisions 'ProgressXxxx' check the value of v_progress and show the appropriate state for the progress bar. The last decision 'ProgressFive' will also change the sticky Character to its state GoNext and enable the click box to proceed to the next slide (subject of the next blog post, with Subtract exercises).

JavaScript for random numbers

In the documentation provide by the Captivate team about using the common JS interface, you'll find a description of creating random numbers:

Common JS Interface

In this example I wanted to generate a random number between 10 included) and 100 (not included). The script window for the decision 'TwoTerms' of the action SumTerms (see above) had to generate two random numbers, to be stored in v_one and v_two; for the decision 'FourTerms' 4 variables had to get a random number:

Short explanation of this code:

The JS method Math.random() generates a decimal number between 0 (included) and 1 (not included).

The multiplication Math.random()*(100-10) will result in a random (decimal) number between 0 (included) and 90 (not included).

The sum Math.random()*(100-10) + 10 will result in a random (decimal) number between 10 (included) and 100 (not included).

The JS method Math.floor(e) will cut off the decimals of the argument e, round iit down to the nearest integer. As a result the full expression used in the JS window  Math.floor(Math.random()*(100-10) + 10) will result in an integer between 10 (included) and 99 (included). 

The method from the common API setVariableValue(x,y) allows to assign the value from the second argument to the variable indicated in the first argument. Beware: the variable name has to be identified as a string (text) by putting it between single or double quotes. For the value, which is a number, you can use the expression explained above. It is a method of the cpAPIInterface which is in the window object. That explains the total line of code, which in JS always ends with a semicolon:
window.cpAPIInterface.setVariableValue("v_one", Math.floor(Math.random()*(100-10) + 10));

Conclusion

You will be able to download the published files - both for SWF and for HTML, when the 4 articles are ready. If you succeeded reaching  this conclusion, you have won your first star!  Three more to go :)