0 votes
by (230 points)

Hi,

I would like to display a canvas in a passage that interact with my story.

For this I planed to use p5.js and p5.dom libraries.

What I wanted is to place a specific div in some passage and ask to the p5 script to display itself in it. Then I would ask to the script to create html content, like a link to another passage.

Sadly I can't manage to make it work with sugarcube. What should I do ?

Here is the example in html without using twine you can try it here

<html>
  <head>
    <script src="p5.min.js"></script>
    <script src="p5.dom.min.js"></script>
    <script src="p5.sound.min.js"></script>
    <script src="sketch.js"></script>
  </head>
  <body>
    <div id="myContainer"></div>
    <div id="link"></div>
  </body>
</html>
function setup() {
  var myCanvas = createCanvas(600, 400);
  myCanvas.parent('myContainer');
}

function draw() {
  background(100+sin(millis()*0.005)*60);

  if(mouseIsPressed && !link){
    txt = createDiv('[[next passage]]');
  	txt.parent('link');
    link=true;
  }
}

In twine I loaded the scripts in a storyInit passage, and placed a container div in the first passage, but nothing happened.

I would be grateful I anyone can help !

1 Answer

+1 vote
by (63.1k points)

A passage isn't a web page, your passages are rendered into an already existing webpage, which already has its own <html>, <head>, and <body>. Loading scripts in a passage will not work. 

Fortunately, there's a few fairly simple options. You can edit the <head> element of the built app using tweego's head option. You could also do this manually if you don't want to use tweego, but that's labor intensive, so don't. 

You could use the importScripts() function. Read about it here: http://www.motoslave.net/sugarcube/2/docs/functions.html#importscripts 

You can also just include the minified source code directly in your story JavaScript area or equivalent by using a wrapper like this to give it the context it expects: 

(function (exports, module, define) {

    // library code goes here 

}).call(window);

This will include the code in your story, which can be good or bad depending on how you feel about that. It'll slow down initial loads since its synchronous, but may actually load the library faster because it isn't fetching it over the network, so if your game needs the library to load before anything else happens, this might be more efficient. 

by (68.6k points)

Additionally, you'll may need to register your functions with P5 somehow.  I don't recall and I'm on my phone at the moment, so I cannot check.

Also.  Looking at this P5 thread reply from the old forums might be helpful.

by (230 points)

Thanks for your help !

I added all of the p5 librairie in the JavaScript Area this way. But where do I put my p5 sketches, and how do I call them in a passage ? (I also learned how to use instance mode in p5)

(function () {

/* all of the ps5.min.js script */

}.call(window));

 

by (68.6k points)

In basic, the p5() instance mode constructor looks something like the following:

new p5( sketch , node );
/*
	sketch  → Function
	node    → Element Node | Element ID
*/

NOTE: There's a bit more to it which I'm not covering here.

 

So, to make a basic one-off sketch, you could do something like the following within a passage:

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

		sketch.setup = function () {
			sketch.createCanvas(640, 480);
			sketch.background(128);
		};

		sketch.draw = function () {
			if (sketch.mouseIsPressed) {
				sketch.fill(0);
			}
			else {
				sketch.fill(255);
			}
			sketch.ellipse(sketch.mouseX, sketch.mouseY, 80, 80);
		};

	}, 'p5sketch');
});
<</script>>

 

To make a sketch function which you can reuse, I'd probably suggest storing it on SugarCube's setup object, so you can refer to it whenever you'd like.  For example:

setup.mySketch = function (sketch) {

	sketch.setup = function () {
		sketch.createCanvas(640, 480);
		sketch.background(128);
	};

	sketch.draw = function () {
		if (sketch.mouseIsPressed) {
			sketch.fill(0);
		}
		else {
			sketch.fill(255);
		}
		sketch.ellipse(sketch.mouseX, sketch.mouseY, 80, 80);
	};

};

To use the stored setup.mySketch function within 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>>

 

by (230 points)

Thanks TheMadExile !  That's exactly what I'm looking for !

Unfortunately I can't make it work both way. What am I missing ?

Here is what I did (My story is in SugarCube 2.14.0) :

  • I put all the p5.min.js content in the javaScript area like the following :
(function (exports, module, define) {

/* p5.min.js content here */

}.call(window));
  • I put these in a passage called setup :
setup.mySketch = function (sketch) {

	sketch.setup = function () {
		sketch.createCanvas(640, 480);
		sketch.background(128);
	};

	sketch.draw = function () {
		if (sketch.mouseIsPressed) {
			sketch.fill(0);
		}
		else {
			sketch.fill(255);
		}
		sketch.ellipse(sketch.mouseX, sketch.mouseY, 80, 80);
	};

};
  • And I put this in the starting passage :
!!Below you should see a basic p5js sketch.
<div id="p5sketch"></div>
<<script>>
$(document).on(':passageend', function () {
	new p5(setup.mySketch, 'p5sketch');
});
<</script>>

 

by (68.6k points)

You have two issues:

  1. The setup.mySketch code goes into a script section (Twine 2: Story JavaScript; Twine 1/Twee: script-tagged passage), not a passage named setup.
  2. You tagged the opening post with sugarcube2-21 (SugarCube v2.21.0), yet in your latest reply state you're using SugarCube v2.14.0.  Which is it?  The version is important to know, correctly, for various reasons (notably, because features added in later versions are not available in earlier ones).

The former is an easy fix.  Just move the code to a script section.

The latter depends on the version of SugarCube you're actually using.  If you're using v2.21.x as you tagged the optioning post, then you don't need to do anything.  If you're using v2.14.0 as you most recently claimed, then you'll need to change the sketch initialization code to something like the following:

!!Below you should see a basic p5js sketch.
<div id="p5sketch"></div>
<<script>>
postdisplay["initialize p5 sketch"] = function () {
	new p5(setup.mySketch, 'p5sketch');
};
<</script>>

The reason for the change is because the :passageend passage navigation event, which the original example used, was added in v2.20.0.  If you're using v.2.14.0, then you'll need to use the older task system (specifically a postdisplay task).

by (230 points)
edited by

Thanks a lot, It's finally working ! that give me so many possibilities ! I plan to create a topic in the processing forums that learn how to implement p5 in twine games, but before that I have two more questions.

- Is it possible to call my p5 sketch in different passages with different parameters ?

- How do I make a p5 sketch lead to another passage ? I tried to create a div with a link in it but it only appears as a string.

here is the p5 skecth

setup.mySketch = function (sketch) {

  var link=false; 

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

  sketch.draw = function() {
    sketch.background(100+sketch.sin(sketch.millis()*0.005)*60);
    if(sketch.mouseIsPressed && !sketch.link){
    sketch.txt = sketch.createDiv('[[next|next]]');
  	sketch.txt.parent('link');
    sketch.link=true;
  }
  };
};

Thanks for all your answers !

by (230 points)
Just realized you were the creator of sugarCube. Thank you so much for creating this, it is really an amazing tool !
by (100 points)
I found the steps above very helpful.

Using the import scripts method and Sugar Cube 2.21.0, pasting the sketch-code into a given passage as outlined above,  I was able to display a sketch in a given passage.

However, I experience a glitch where the sketch /p5 canvsas remains displayed at the bottom of other passages when I navigate to them subsequent ot visiting the page with the sketch.

I'm still trying to understand "instance mode"; so if that's the problem, I can do that homework.
by (230 points)

Hi,

The only way I found for that is to delete your canvas.

Please, tell me if you find another way !

setup.mySketch = function (sketch) {

	/*place your sketch here*/

	/*here is an example*/
 	sketch.setup = function() {
		sketch.createCanvas(200, 200);
	};

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

};

 

...