0 votes
by (230 points)

Hi, I tried to use the importScripts() function of sugarCube but I can't make it work.
I need some external libraries to make my p5 sketch work, but for now it only works when I copy past the full content of the lib in the story's javascript which isn't very convenient.
What should I do ?
Here is my story javascript content :

importScripts(
  "https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.sound.min.js"
);

setup.mySketch = function (sketch) {
  sketch.setup = function() {
    sketch.createCanvas(200, 200);
  };

  sketch.draw = function() {
    sketch.background(100+sketch.sin(sketch.millis()*0.005)*60);
    if(sketch.mouseIsPressed){
      Engine.play("entrance");
    }
  };
};

Here is a passage :

!!Below you should see a basic p5js sketch.
<div id="p5sketch"></div>
<<script>>
$(document).on(':passageend', function () {
	new p5(setup.mySketch, 'p5sketch');
});
<</script>>
<div id="link"></div>

Thanks for your help !

 

1 Answer

+1 vote
by (156k points)

There are a couple of issues with your example.

1. You're trying to load the P5 related libraries concurrently instead of sequentially.

Because you're loading the libraries concurrently it is possible for the code of the 2nd and 3rd libraries to be executed before the 1st, and that situation will cause JavaScript errors to be thrown which you can see if you look in the Console of your web-browser.

importScripts([
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js",
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js",
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.sound.min.js"

]);


2. The :passageend event can occur before the P5 related libraries have been successfully executed.

It takes time for the remote libraries to be downloaded and executed, which means that if your example TwineScript is within your launch (Start) passage then it's :passageend event can occur before that execution has finished.

If you plan to use the P5 library at startup then you will need to monitor the Promise returned by the importScripts() function to determine when the loading and execution has finished. There are a number of ways you can do this. the following example uses a variable on the setup special object to do this,

setup.p5promise = importScripts([
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js",
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js",
	"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.sound.min.js"

]);

You can now use that setup.p5promise variable within your startup related code to delay your p5sketch related initialisation code like so.

<div id="p5sketch"></div>
<div id="link"></div>
<<script>>
	$(document).on(':passageend', function () {
		setup.p5promise.then(function () {
			new p5(setup.mySketch, 'p5sketch');
		});
	});
<</script>>

 

by (230 points)

Without seeing your modified code it is difficult to know what is actually happening.

Sorry about that

Here is my JS :

setup.mySketch = function (sketch) {

  sketch.setup = function() {
    sketch.createCanvas(200, 200);
  };

  sketch.draw = function() {
    sketch.background(100+sketch.sin(sketch.millis()*0.005)*60);
    if(sketch.mouseIsPressed){
      Engine.play("entrance");
    }
  };
};

setup.p5promise = importScripts([
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.sound.min.js"
]);

And here is my first passage, the second one is empty

<div id="p5sketch"></div>
<div id="link"></div>
<<script>>
	$(document).on(':passageend', function () {
		setup.p5promise.then(function () {
			new p5(setup.mySketch, 'p5sketch');
		});
	});
<</script>>

Thanks for your help !

by (65.4k points)

You should switch from using <jQuery>.on() to <jQuery>.one() when setting up the event handler.  Doing so will make the handler single-use.

 

FIND:

	$(document).on(':passageend', function () {

REPLACE WITH:

	$(document).one(':passageend', function () {

 

by (230 points)

Thanks ! The sketch is no longer displaying in the second passage but it still seems to read the line "Engine.play("entrance");" and refresh the page each time the user is clicking.

setup.mySketch = function (sketch) {

  sketch.setup = function() {
    sketch.createCanvas(200, 200);
  };

  sketch.draw = function() {
    sketch.background(100+sketch.sin(sketch.millis()*0.005)*60);
    if(sketch.mouseIsPressed){
      Engine.play("entrance");
    }
  };
};

setup.p5promise = importScripts([
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.sound.min.js"
]);
<div id="p5sketch"></div>
<div id="link"></div>
<<script>>
	$(document).one(':passageend', function () {
		setup.p5promise.then(function () {
			new p5(setup.mySketch, 'p5sketch');
		});
	});
<</script>>

 

by (156k points)

If you add temporarily change your sketch.draw() function to the following.. 

	sketch.draw = function() {
		console.log('draw: mouseIsPressed: ' + sketch.mouseIsPressed);
		sketch.background(100 + sketch.sin(sketch.millis() * 0.005) * 60);
		if(sketch.mouseIsPressed){
			Engine.play("entrance");
		}
	};

and then view your web-browser's Console while running your HTML file you will notice two things:
1. The sketch's draw() function keeps getting called even when the sketch is no long visible.
2. The P5 object is monitoring ALL click events on the page, including ones on the left side-bar.

To fix the first issue you need to call the P5 remove() function which will stop and clear up that P5 object. The following example demonstrates calling the function and also defines a mousePressed() function instead of checking the mouseIsPressed state.

setup.mySketch = function (sketch) {

	sketch.setup = function() {
		sketch.createCanvas(200, 200);
	};

	sketch.draw = function() {
		sketch.background(100 + sketch.sin(sketch.millis() * 0.005) * 60);
	};

	sketch.mousePressed = function () {
		sketch.remove();
		Engine.play("entrance");
	}
};


If you want to limit the P5 object to monitoring only the click events on the sketch itself then you need to associate the above mousePressed() function code to the canvas itself. The following example demonstrates how to do this.

setup.mySketch = function (sketch) {

	sketch.setup = function() {
		var canvas = sketch.createCanvas(200, 200);
		canvas.mousePressed(onClick);
	};

	sketch.draw = function() {
		sketch.background(100 + sketch.sin(sketch.millis() * 0.005) * 60);
	};

	function onClick() {
		sketch.remove();
		Engine.play("entrance");
	};
};

 

by (230 points)
Thank you so much greygef ! It was so simple I didn't think of that, simply deleting the sketch via p5. Now I think I'm ready to make games that combine twine and p5, so many possibilities !

I have a last question if you don't mind. Let's say I have a mini game made in p5 in my story, and I want it to appear in different passages but with few changes. What could be the easiest way to do it ? Is there a way to load the same script with some specific parameters ?
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...