Main content
Computer programming - JavaScript and the web
Interactive scenes
Now that we know how to make sweet animated scenes, let's make sure we can handle the other sort of non-static scene: scenes that respond to user interaction. For example, we want to draw a scene where Winston has babies (after his rock star phase, of course) -- but we also want to let the user click to give Winston MORE babies. Because we can always use more little Winstonitos in the world, right?
Here's what that scene would look like as a standalone program. The program draws the static part of the scene, and then in
mouseClicked
, it draws Winston baby images at the clicked mouse location, layering them on top of whatever was already drawn.How would we integrate that into our multi-scene program? Well, we'd start by just wrapping all the static drawing code into a scene drawing function
drawScene5()
and adding scene switching logic to mouseClicked
:var drawScene5 = function() {
currentScene = 5;
background(173, 239, 255);
fill(7, 14, 145);
textSize(39);
text("Winston has babies!", 10, 47);
...
};
mouseClicked = function() {
if (currentScene === 1) {
drawScene2();
} else if (currentScene === 2) {
drawScene3();
} else if (currentScene === 3) {
drawScene4();
} else if (currentScene === 4) {
drawScene5();
} else if (currentScene === 5) {
drawScene1();
}
};
Here's what that looks like:
But how do we integrate the
mouseClicked
functionality? We've already defined a mouseClicked
in our code, and we can't define it twice. In JavaScript, the last function definition "wins", it overrides any previous definitions. That means that we need to find a good place to put that baby drawing line inside our existing mouseClicked
. Let's talk about a few options:1. We could put the line at the top of the function:
mouseClicked = function() {
image(getImage("creatures/BabyWinston"), mouseX-20, mouseY-20);
...
};
Then it will get called EVERY time the user clicks the mouse, even if it's not the baby making scene (and it'd be weird if baby Winston had a baby). No good.
2. We could put the line inside the
currentScene === 4
if block:mouseClicked = function() {
if (currentScene === 1) {
drawScene2();
} else if (currentScene === 2) {
drawScene3();
} else if (currentScene === 3) {
drawScene4();
} else if (currentScene === 4) {
drawScene5();
image(getImage("creatures/BabyWinston"), mouseX-20, mouseY-20);
} else if (currentScene === 5) {
drawScene1();
}
};
After all, that's where we call
drawScene5()
, and the babies should be added to scene 5. But think about it: that'd mean that we'd always draw an extra baby, every time we drew the scene. It'd also mean that we'd never draw any more babies, because currentScene
would get set to 5, and the code in that if block wouldn't get executed any more.3. We could put the line inside the
currentScene === 5
if block:mouseClicked = function() {
if (currentScene === 1) {
drawScene2();
} else if (currentScene === 2) {
drawScene3();
} else if (currentScene === 3) {
drawScene4();
} else if (currentScene === 4) {
drawScene5();
} else if (currentScene === 5) {
image(getImage("creatures/BabyWinston"), mouseX-20, mouseY-20);
drawScene1();
}
};
That would mean we wouldn't draw babies until the first click after the initial drawing of the scene. But, as you can see from the line after it, the baby would be instantly replaced with scene 1.
This is where we realize a fatal flaw in our idea of integrating this baby-clicking scene into our scenes: we're using the exact same interaction--a mouse click anywhere on the screen--to change scenes as well as to do within-scene interaction. Now we really have a conundrum on our hands, and we need to consider more radical options for integrating the scene.
4. We could stop re-drawing scene 1 at the end, and tell the user to restart in that case. Sure, that'd work, but that relies on the fact that our click-controlled screen happens to be in the last one. What if we wanted an earlier click-controlled scene? That solution would fail.
5. We could use a different sort of interaction - like
mouseDragged.
That will work, because dragging doesn't also cause a click event. We still need to check that currentScene === 5
, to make sure dragging doesn't draw babies in any other scene:mouseDragged = function() {
if (currentScene === 5) {
image(getImage("creatures/BabyWinston"), mouseX-20, mouseY-20);
}
};
Try it out below, making sure you drag on the final scene:
So, that kind of works, although I'm a bit worried about how many babies Winston might end up with. Generally, this is not an optimal solution, since it means we have to limit ourselves to designing scenes that don't respond to mouse clicks. We don't want to have to have that constraint, there must be a better way.
What if we instead differentiated mouse clicks by location, so that a click in one location would mean changing scenes, and clicks elsewhere could be used for in-scene interaction? You know, like a button! In fact, that's how most multi-scene programs approach this problem, and we'll talk through that next.
Want to join the conversation?
- instead of dragging to make babies, cant you set it to a key press?(85 votes)
- Yeah, there are many ways you could do it besides how she shows you. You will find that in programming there are either millions of ways to do something or a singular way.(103 votes)
- Why is all of Advanced Js Games and Visualization in scripts not talk-throughs?(29 votes)
- Because the more advanced you get the less you will be able to learn using things like talk throughs and videos. Eventually to learn new things you'll have to read books and web pages, and reference documentation. At some point there will be no documentation and to learn more stuff you have to read code.(64 votes)
- what if we used mouseButton function instead(12 votes)
mouseButton
determines which button on the mouse is used, not whether or not the mouse is being pressed.(22 votes)
- What's the difference between mouseClicked, and mouseIsPressed?(3 votes)
mouseClicked
detects whether or not the mouse has been clicked (pressed and then released), whereasmouseIsPressed
only checks if the mouse is being pressed (it doesn't have to have been released). Hope this helps!(22 votes)
- I was curious. Is KA JS more like the new p5.js or is it based on processing.js? Because these seem more like p5.js.
Thanks
.(7 votes)- Khan Academy made their own rendition of Processing.js. Thus it is much closer to Processing.js than P5.js. In my limited experience and opinion, P5.js suffers from the second system syndrome.(13 votes)
- Is the "else" if really needed? Can't it just be:
mouseClicked = function() {
if (currentScene === 1) {currentScene = 2}
if (currentScene === 2) {currentScene = 3}
... // And so on
};(1 vote)- That would be inefficient, and in some situations you need to know how to use the else/if ladder rather than a series of if statements.(15 votes)
- Could you use something like cases? I am not entirely familiar with the topic, so I was wondering if that could possibly be equally or more efficient then using an If-Else statement "ladder".(3 votes)
- Yes using something like cases would work a lot better, and can be none with the
switch
function. Here is example:var number = 1;
switch(number) {
// Works well with number values.
case 1:
println("The current value of \"number\" is 1.");
break;
case 2:
println("The current value of \"number\" is 2.");
break;
// Also works great with strings.
case "randomNumber":
println("The current value of \"number\" is \"randomNumder\"");
break;
}(8 votes)
- Can you use images of LEGO mini figures, or is it just Winston?(4 votes)
- You cannot easily use external images on Khan Academy, but you can make your own by drawing with code.(6 votes)
- What's the difference between mouseClicked, and mouseIsPressed?(3 votes)
- mouseIsPressed checks whether the mouse is currently pressed down. It's a boolean (True/False).
mousePressed = function(){ ... }; is a built in function call that will call a function whenever you press the mouse.(7 votes)
- I just want to ask about why we multiply in one here :
cos(millis()*1); ?!
the program will work without this step ! So, I think the code should be like that :
cos(millis());(4 votes)- You are correct.(3 votes)