使用 p5.js 从一个场景过渡到下一个场景
Transitioning from one scene to the next with p5.js
我目前正在使用 p5.js 创建一个互动故事。我的主要问题是我似乎无法弄清楚如何编写从第二个场景过渡到第三个场景的逻辑。
我使用了 p5.js 的切换场景示例 here 作为参考指南。我还没有真正完全掌握 mousePressed()
函数背后的逻辑,以便利用它来发挥我的优势。
function mousePressed() {
if (scene1 == true) {
if (mouseX < width / 2) {
//do something
scene2 = true;
} else if (mouseX > width / 2) {
//do something else
scene3 = true;
}
// turn scene 1 off
scene1 = false;
}
}
函数将canvas分成两个位置,供mousePressed()
函数执行和过渡到另一个场景。
我只想按 canvas 上的任意位置作为从第一个场景过渡到第二个场景的起点,但后来我想按一个特定的对象以便从第二个场景过渡到第三个场景。
当前代码片段:
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
easycam = new Dw.EasyCam(this._renderer, {
distance: lunarDistance * 1.3
});
}
function draw() {
if (scene1 == true) {
drawScene1();
} else if (scene2 == true) {
drawScene2();
} else if(scene3 == true){
drawScene3();
}
}
function drawScene1() {
...
}
function drawScene2() {
...
}
function drawScene3() {
...
}
function mousePressed() {
if (scene1 == true) {
if (mouseX == width) {
scene2 = true;
if (mouseX == width) {
scene3 = true;
}
}
scene1 = false;
}
}
不幸的是,这似乎不起作用。我尝试通过删除 mousePressed():
进一步修改它
function draw(){
if (scene1 == true) {
drawScene1();
if(mouseIsPressed){
scene1 = false;
scene2 = true;
drawScene2();
}
}
}
这似乎有效,但它禁用了我的动画并完全搞砸了。
我该怎么做?
MousePressed 事件:P5 参考
语言参考应该是您应该首先搜索的地方。 p5 reference
中描述了鼠标按下功能
这是引用中的事件描述片段。
The mousePressed() function is called once after every time a mouse button is pressed. The mouseButton variable (see the related reference entry) can be used to determine which button has been pressed. If no mousePressed() function is defined, the touchStarted() function will be called instead if it is defined.
否定
想想如何应用否定来管理您的活动。
scene1 = !scene1; // This would turn your scene on or off
鼠标按下事件
此处的代码片段会中断,因为它只会绘制 1 帧,并且由于场景 1 设置为 false,在第一帧之后将无法访问其他条件。
function draw(){
if (scene1 == true) {
drawScene1();
if(mouseIsPressed){
scene1 = false;
scene2 = true;
drawScene2();
}
}}
您要做的是首先检查鼠标点击的位置。基于鼠标点击改变你的场景。有很多方法可以做到这一点。跟踪布尔值和变量太乏味且容易出错。那么,您如何解决这个问题?
有什么合集可以帮到你吗?是的!
You can use lists or any other flavor of collections to help track scenes easily. To do this, keep track of the current scene using a pointer and only draw that scene if the pointer is on that scene.
即:if (pointer==1){drawScene1();}
我相信你可以解决剩下的问题。
在 draw()
中使用 if
s 来确定要渲染哪个场景的方法对于小草图可能很好,但不是特别可扩展,涉及布尔值,hard-to-reason-about 条件,共享状态 and/or 程序必须知道 magic/hardcoded 变量名。
每当你发现自己在处理 thing1
、thing2
、thing3
、...、thingN
时,前进的道路几乎总是对象或数组.对于长 if
-else
或 switch
链,常见的重构是使用数组或函数对象。
场景本质上是 state machines, which are a perfect use-case for keyable/indexable objects or arrays of functions. If you're proceeding through your scenes in a stepwise manner, arrays are probably best since they're ordered and can be stepped through sequentially with an index or shift()
. See 的方法。
如果您的场景不是那么线性,给它们命名而不是数字可能是浏览它们的更好方法。这是一个例子:
const scenes = {
loading: () => {
let ticks = 0;
frameRate(3);
mousePressed = () => {};
draw = () => {
clear();
text(`loading sceen, please wait${".".repeat(ticks % 4)}`, 50, 50);
if (++ticks > 10) {
frameRate(60);
scenes.menu();
}
};
},
menu: () => {
mousePressed = () => {
scenes.gamePlay();
};
draw = () => {
clear();
text("menu scene. click to play", 50, 50);
};
},
gamePlay: () => {
mousePressed = () => {
fill(0);
scenes.gameOver();
};
let x = 0;
fill(50, 50, 160);
draw = () => {
clear();
text("gameplay scene. click to go to game over", cos(x) * 20 + 50, 50);
x += 0.1;
};
},
gameOver: () => {
mousePressed = () => {
scenes.menu();
};
draw = () => {
clear();
text("game over scene. click to go to menu", 50, 50);
};
},
// ... more scenes ...
};
function setup() {
createCanvas(500, 100);
textSize(20);
}
function draw() {
scenes.loading();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
当然,切换场景的条件会更复杂,并且在很多情况下您可能不会使用鼠标进行切换(游戏状态或按钮很可能),但这个例子说明了每个场景都将以 event-driven 的方式用自己的实现替换 p5 的 window 库函数,避免任何 if
并保持代码的可读性和模块化。您可以很容易地将场景分成单独的文件。
每个场景的“设置”逻辑都包含在 top-level scenes.yourScene
函数中,它为应该在 draw
调用之间持续存在而不会污染其他场景的状态提供了一个很好的闭包.您可以根据需要将变量移出全局或共享范围,以在场景之间保留数据,例如总体最高分。
虽然上面的例子中场景之间的关系很简单,但是场景可以测试不同的条件来过渡到任何场景,比如单独的win/loss场景。触发转换的条件块表示退出状态的 cleanup/teardown 逻辑。对于非常复杂的游戏和动画,嵌套场景以及分别使用数组和对象的组合进行逐步和状态机转换应该是非常可行的。
画一个图表来显示应用中的场景以及触发转换的条件通常很有用。或者,写下应该采取哪些操作来设置和拆除每个场景:
例如,上述应用程序的场景可以这样可视化:
.---------.
| loading |
`---------`
|
loaded
|
v
.------. .-----------.
| menu |--click-->| game play |
`------` `-----------`
^ |
| click
| |
click v
| .-----------.
+--------------| game over |
`-----------`
最后,如果覆盖 p5 知道的 draw
函数让您感到困扰,您总是可以添加一个间接层,在几个不同的选项之间翻转一个局部函数,然后从 draw
以一种对 p5 侵入性较小的方式:
const menuScene = () => {
if (someCondition) {
renderScene = gameScene;
}
// update and draw menu stuff
};
const gameScene = () => {
if (someCondition) {
renderScene = menuScene;
}
// update and draw game stuff
};
let renderScene = menuScene;
function draw() {
renderScene();
}
如果没有闭包,我们将丢失 scene-specific 设置代码和局部变量,但这很容易使用类似于第一个示例中显示的模式重新引入。
相同的策略适用于为每个场景设置鼠标和键盘等处理程序。
我目前正在使用 p5.js 创建一个互动故事。我的主要问题是我似乎无法弄清楚如何编写从第二个场景过渡到第三个场景的逻辑。
我使用了 p5.js 的切换场景示例 here 作为参考指南。我还没有真正完全掌握 mousePressed()
函数背后的逻辑,以便利用它来发挥我的优势。
function mousePressed() {
if (scene1 == true) {
if (mouseX < width / 2) {
//do something
scene2 = true;
} else if (mouseX > width / 2) {
//do something else
scene3 = true;
}
// turn scene 1 off
scene1 = false;
}
}
函数将canvas分成两个位置,供mousePressed()
函数执行和过渡到另一个场景。
我只想按 canvas 上的任意位置作为从第一个场景过渡到第二个场景的起点,但后来我想按一个特定的对象以便从第二个场景过渡到第三个场景。
当前代码片段:
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
easycam = new Dw.EasyCam(this._renderer, {
distance: lunarDistance * 1.3
});
}
function draw() {
if (scene1 == true) {
drawScene1();
} else if (scene2 == true) {
drawScene2();
} else if(scene3 == true){
drawScene3();
}
}
function drawScene1() {
...
}
function drawScene2() {
...
}
function drawScene3() {
...
}
function mousePressed() {
if (scene1 == true) {
if (mouseX == width) {
scene2 = true;
if (mouseX == width) {
scene3 = true;
}
}
scene1 = false;
}
}
不幸的是,这似乎不起作用。我尝试通过删除 mousePressed():
进一步修改它function draw(){
if (scene1 == true) {
drawScene1();
if(mouseIsPressed){
scene1 = false;
scene2 = true;
drawScene2();
}
}
}
这似乎有效,但它禁用了我的动画并完全搞砸了。
我该怎么做?
MousePressed 事件:P5 参考
语言参考应该是您应该首先搜索的地方。 p5 reference
中描述了鼠标按下功能这是引用中的事件描述片段。
The mousePressed() function is called once after every time a mouse button is pressed. The mouseButton variable (see the related reference entry) can be used to determine which button has been pressed. If no mousePressed() function is defined, the touchStarted() function will be called instead if it is defined.
否定
想想如何应用否定来管理您的活动。
scene1 = !scene1; // This would turn your scene on or off
鼠标按下事件
此处的代码片段会中断,因为它只会绘制 1 帧,并且由于场景 1 设置为 false,在第一帧之后将无法访问其他条件。
function draw(){
if (scene1 == true) {
drawScene1();
if(mouseIsPressed){
scene1 = false;
scene2 = true;
drawScene2();
}
}}
您要做的是首先检查鼠标点击的位置。基于鼠标点击改变你的场景。有很多方法可以做到这一点。跟踪布尔值和变量太乏味且容易出错。那么,您如何解决这个问题? 有什么合集可以帮到你吗?是的!
You can use lists or any other flavor of collections to help track scenes easily. To do this, keep track of the current scene using a pointer and only draw that scene if the pointer is on that scene.
即:if (pointer==1){drawScene1();}
我相信你可以解决剩下的问题。
在 draw()
中使用 if
s 来确定要渲染哪个场景的方法对于小草图可能很好,但不是特别可扩展,涉及布尔值,hard-to-reason-about 条件,共享状态 and/or 程序必须知道 magic/hardcoded 变量名。
每当你发现自己在处理 thing1
、thing2
、thing3
、...、thingN
时,前进的道路几乎总是对象或数组.对于长 if
-else
或 switch
链,常见的重构是使用数组或函数对象。
场景本质上是 state machines, which are a perfect use-case for keyable/indexable objects or arrays of functions. If you're proceeding through your scenes in a stepwise manner, arrays are probably best since they're ordered and can be stepped through sequentially with an index or shift()
. See
如果您的场景不是那么线性,给它们命名而不是数字可能是浏览它们的更好方法。这是一个例子:
const scenes = {
loading: () => {
let ticks = 0;
frameRate(3);
mousePressed = () => {};
draw = () => {
clear();
text(`loading sceen, please wait${".".repeat(ticks % 4)}`, 50, 50);
if (++ticks > 10) {
frameRate(60);
scenes.menu();
}
};
},
menu: () => {
mousePressed = () => {
scenes.gamePlay();
};
draw = () => {
clear();
text("menu scene. click to play", 50, 50);
};
},
gamePlay: () => {
mousePressed = () => {
fill(0);
scenes.gameOver();
};
let x = 0;
fill(50, 50, 160);
draw = () => {
clear();
text("gameplay scene. click to go to game over", cos(x) * 20 + 50, 50);
x += 0.1;
};
},
gameOver: () => {
mousePressed = () => {
scenes.menu();
};
draw = () => {
clear();
text("game over scene. click to go to menu", 50, 50);
};
},
// ... more scenes ...
};
function setup() {
createCanvas(500, 100);
textSize(20);
}
function draw() {
scenes.loading();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
当然,切换场景的条件会更复杂,并且在很多情况下您可能不会使用鼠标进行切换(游戏状态或按钮很可能),但这个例子说明了每个场景都将以 event-driven 的方式用自己的实现替换 p5 的 window 库函数,避免任何 if
并保持代码的可读性和模块化。您可以很容易地将场景分成单独的文件。
每个场景的“设置”逻辑都包含在 top-level scenes.yourScene
函数中,它为应该在 draw
调用之间持续存在而不会污染其他场景的状态提供了一个很好的闭包.您可以根据需要将变量移出全局或共享范围,以在场景之间保留数据,例如总体最高分。
虽然上面的例子中场景之间的关系很简单,但是场景可以测试不同的条件来过渡到任何场景,比如单独的win/loss场景。触发转换的条件块表示退出状态的 cleanup/teardown 逻辑。对于非常复杂的游戏和动画,嵌套场景以及分别使用数组和对象的组合进行逐步和状态机转换应该是非常可行的。
画一个图表来显示应用中的场景以及触发转换的条件通常很有用。或者,写下应该采取哪些操作来设置和拆除每个场景:
例如,上述应用程序的场景可以这样可视化:
.---------.
| loading |
`---------`
|
loaded
|
v
.------. .-----------.
| menu |--click-->| game play |
`------` `-----------`
^ |
| click
| |
click v
| .-----------.
+--------------| game over |
`-----------`
最后,如果覆盖 p5 知道的 draw
函数让您感到困扰,您总是可以添加一个间接层,在几个不同的选项之间翻转一个局部函数,然后从 draw
以一种对 p5 侵入性较小的方式:
const menuScene = () => {
if (someCondition) {
renderScene = gameScene;
}
// update and draw menu stuff
};
const gameScene = () => {
if (someCondition) {
renderScene = menuScene;
}
// update and draw game stuff
};
let renderScene = menuScene;
function draw() {
renderScene();
}
如果没有闭包,我们将丢失 scene-specific 设置代码和局部变量,但这很容易使用类似于第一个示例中显示的模式重新引入。
相同的策略适用于为每个场景设置鼠标和键盘等处理程序。