One of the most visited blogs on my site is an example from years ago. It showed how to build two-way communication between Storyline and an embedded webobject. This is a newer example as part of the Articulate Heroes eLearning Challenge.
Never played Connect 4
I have to admit, as a child, I never played Connect 4 but my children did, so I was familiar with the rules. You play against an opponent by taking turns to drop a coin in one of the seven slots. Whoever connects four of the same color wins the game.
Turns out, there are multitude of implementations of this game online in various programming languages. First thing I learned is that it’s much easier to SEE when four coins connected than creating an algorithm to check for that. Anyway, this example shows how Storyline communicates with a webobject, and how the webobject communicates with Storyline. In this case, the webobject is a game I created in Construct 3 to model the behavior (but it could be any HTML5 site).
Play test it first!
Play around with my version of Connect 4 before reading the explanation below. I will also post a strip-down version on JavaScript Pop(99) site to download.
Gameplay
The Story starts in Storyline. The page just serves as an entry point with a little storytelling about the Green Fourest where monsters rule. It’s a call to action to beat them in Connect 4.
This slide leads into the selection page. There are four monsters to play against. Each has a level. In the game engine you’ll play against a player. The player is controlled by an algorithm. Original credit goes to https://github.com/lgfischer/connect-four-js for the AI implementation of the minmax algorithm.
I modified the algorithm a little so we can have players with different levels. According to the story, you must beat all of them to unite the Forest.
The point of creating four levels is also to show how the embedded game engine in the webobject can read the selected level from Storyline and adjust the gameplay.
Once you select a level Storyline goes to the game slide. This is a slide that has Storyline as a framework and inside the game engine with the actual connect 4 game. The big spider, the scary button are still in Storyline but the gameboard and the two players are not.
Reading Storyline variables
It takes time for the game engine to load. Therefore, we put a black rectangle on Storyline with some info like “Loading level x…”. This way, the user can see that something is happening. Now, the question is when to hide this black rectangle. You could time it in Storyline and add a fixed duration like 4-5 seconds. The problem is you don’t know how long it takes to load. It would be more practical for the game engine to let Storyline know when it’s ready.
When the game engine loads and ready to play, it communicates this status to Storyline by changing a Storyline variable. Storyline variables are not directly accessible from outside Storyline but there’s a way to do that. In Storyline, all we need to set up is a trigger to watch this variable to change. And when it changes, hide the rectangle and reveal the game.
parent.GetPlayer().SetVar('Hide',1);
This is done by JavaScript. We use the “parent” because the webobject is always embedded inside the Storyline as an iframe. This is a child-parent relationship. Therefore, to call something outside of the iframe, we use the “parent” notation.
The rest is standard JavaScript for Storyline. We grab the GetPlayer() object that Storyline provides, and use the SetVar function to set the Storyline variable, “Hide” to 1. That triggers Storyline to hide the rectangle.
Next, we need to learn about the selected level.
parent.GetPlayer().GetVar('Level');
This code (similar to the previous line) gets the Storyline variable “Level” and you can assign this to a variable inside the iframe. Construct 3 game engine provides an interface for this but you could do this with plain JavaScript inside:
var gameLevel = parent.GetPlayer().GetVar('Level');
And based on the value, you can adjust the gameplay inside the game.
Winning the game
The rest of the gameplay happens inside the game engine. It’s using a modified minmax algorithm to figure out the best move against you. When the game is over, we need to let Storyline know who won. If the user wins, Storyline should set the selected level to “beaten” state. If the computer wins, Storyline should wipe out all previous wins according to the rules.
In Construct 3 this is what the code looks like:
parent.GetPlayer().SetVar('Level"&SLStatus&"Status',"&Winner&")
The game engine has its own variable called SLStatus. SLStatus is the level set in Storyline. We need that information to set one of the Storyline variables (Level1Status, Level2Status, Level3Status, Level4Status) to indicate who won and at what level. Winner is another game engine variable that holds either -1 (tie), 1 (user won), or 2 (computer won).
If you’re not using a game engine with its own variables and syntax, the same code would look something like this in pure JavaScript.
var SLStatus = parent.GetPlayer().GetVar('Level'); // getting the level from SL
var Winner = 0; // Winner will show who won the game
… game play here. Let’s assume we won the game, so somewhere Winner is set to 1;
parent.GetPlayer().SetVar('Level'+SLStatus+'Status',Winner)
Explanation: JavaScript allows you to to create a string by adding parts of text and numbers together. If SLStatus is 1, then the variable we’re setting in Storyline is “Level1Status” because we added “Level” + SLStatus + “Status” together. When Storyline receives this information it will see the text Level1Status. And since Winner is 1, Storyline will see the following code:
GetPlayer().SetVar('Level1Status',1)
Note that it doesn’t matter what level we’re playing (the SLStatus can be 1,2,3,4), the code remains the same. You could hard code this by writing four times more lines for each variable (Level1Status, Level2Status, Level3Status, Level4Status) but it is much more efficient this way.
Turning the music and off
If you noticed the communication so far was driven by the game engine. When it loads, it reads the level. When it ends it sets the winner. That means so far, only the iframe has communicated to the Storyline.
What about the turning off and on the music button?
This button is in Storyline. When the user clicks on the button it should let the game engine know to toggle between music on and off.
In the game engine there’s a function that turns the music on and off. We can call that function from outside of the iframe, from Storyline. Note that both Storyline and the iframe content (webobject) are on the same server, same domain. That’s why this communication can work easily. If the webobject content is stored on a different domain this is a much more complicated issue due to cross-browser security.
How do we let the game engine know to turn the music on or off? There are two things going on here.
First, we need to be able to toggle the button on and off in Storyline and changing the words “ON” to “OFF” and vice versa. Storyline does not have an IF … ELSE … structure. You can use a trigger to switch the ON to OFF and a trigger to OFF to ON. However, since they both execute after each other (there’s no ELSE here), it is more complicated than you think to make a toggle work.
I’m sure you can do it but I just quickly made this happen in JavaScript and added this to a trigger in Storyline.
var player = GetPlayer();
var music = player.GetVar("MSTAT");
if (music == "ON")
{
player.SetVar("MSTAT","OFF");
}
else
{
player.SetVar("MSTAT","ON");
}
The second part is letting the iframe (game engine) know about the change.
$('iframe')[0].contentWindow.c3_callFunction("setMusic",[""+GetPlayer().GetVar("MSTAT")+""]);
This code works only with Construct 3 but the structure is the same. Assuming there’s only one iframe (meaning you don’t have more than webobjects inside the page), you can access the content of the window using jQuery (comes with Storyline out of the box):
$('iframe')[0].contentWindow
Once accessing the contentWindow, Construct 3 offers a way to call a function inside. In this case the function is called setMusic and it’s expecting one argument: ON or OFF:
Depending on whether the Storyline variable MSTAT is “ON” or “OFF” this function pauses or resumes the music. It also sets the MusicOn global variable to “ON” or “OFF” so other parts of the program can use that.
If you’re not using the game engine but plain JavaScript inside the iframe, the syntax is different but the structure would be the same:
$('iframe')[0].contentWindow.setMusic(GetPlayer().GetVar("MSTAT"));
Assuming you have a function in your JavaScript code inside the webobject iframe.
Noticed how we don’t use the “parent” here in the code. It is because this code sits inside a Storyline Execute JavaScript trigger and not in an iframe. Therefore, the GetPlayer() is available to access.
Conclusion
You can access Storyline variables from JavaScript this way:
var someJSVariable = GetPlayer().GetVar(“SLvariablename”);
You can set Storyline variables from JavaScript this way:
GetPlayer().SetVar(“SLvariablename”,1);
You can access Storyline variables from an embedded webobject this way:
var someJSVariable = parent.GetPlayer().GetVar(“SLvariablename”);
You can set Storyline variables from an embedded webobject this way:
parent.GetPlayer().SetVar(“SLvariablename”,1);
To access a function inside the embedded webobject from Storyline:
$(‘iframe’)[0].contentWindow.fooFunction(); // where fooFunction is a function inside the iframe. For example: function fooFunction() { alert(‘Called!’); }
Leave a Comment