p5.js 中 keyPressed() 无法正确更改 y 坐标
结论
问题出在 move() 的条件判断:
if (key === 'a' || 'd')
JavaScript 会把这段代码解析为:
if ((key === 'a') || 'd')
非空字符串 'd' 会被转换为 true,所以条件始终成立,后面的 else if 没有机会执行。按下 W 或 S 时,传入的 -2 或 2 依然会加到 x1、x2、x3 上,飞船因此会左右移动 2 像素。
变量作用域和三角形的坐标定义都没有造成这个问题。
修正条件判断
每次比较都要写出变量和比较运算符:
this.move = function(dir) {
if (key === 'a' || key === 'd') {
if (this.x1 > 0 && this.x3 < width) {
this.x1 += dir;
this.x2 += dir;
this.x3 += dir;
}
} else if (key === 'w' || key === 's') {
if (this.y1 < height && this.y2 > 0) {
this.y1 += dir;
this.y2 += dir;
this.y3 += dir;
}
}
};
keyPressed() 可以继续使用原来的分支结构:
function keyPressed() {
const left = -5;
const right = 5;
const up = -2;
const down = 2;
if (key === 'a') {
ship.move(left);
} else if (key === 'd') {
ship.move(right);
} else if (key === 'w') {
ship.move(up);
} else if (key === 's') {
ship.move(down);
}
}
修改后,按 A 或 D 会进入第一个分支,更新三个顶点的 x 坐标;按 W 或 S 会进入第二个分支,更新三个顶点的 y 坐标。dir 为负数时向左或向上移动,为正数时向右或向下移动。
p5.js 的坐标原点在画布左上角,x 向右增大,y 向下增大。向上移动需要减小 y,向下移动则要增大 y。
直接传入 x、y 位移
目前的 move() 会再次读取全局变量 key,按键判断分散在两个函数中,维护起来不太方便。可以由 keyPressed() 判断按键,让 move() 专门负责更新坐标:
// constructors.js
this.move = function(dx, dy) {
this.x1 += dx;
this.x2 += dx;
this.x3 += dx;
this.y1 += dy;
this.y2 += dy;
this.y3 += dy;
};
// sketch.js
function keyPressed() {
if (key === 'a') {
ship.move(-5, 0);
} else if (key === 'd') {
ship.move(5, 0);
} else if (key === 'w') {
ship.move(0, -2);
} else if (key === 's') {
ship.move(0, 2);
}
}
参数 dx 表示 x 方向的变化量,dy 表示 y 方向的变化量。例如,ship.move(-5, 0) 只改变 x,ship.move(0, -2) 只改变 y。这样写也去掉了 move() 对全局变量 key 的依赖。
改进边界检查
原来的边界判断检查的是移动前的位置。假设飞船距离右边界只剩 1 像素,它仍然可能向右移动 5 像素,最终越过边界。可以先算出移动后的坐标,再判断是否允许移动:
this.move = function(dx, dy) {
const nextX1 = this.x1 + dx;
const nextX2 = this.x2 + dx;
const nextX3 = this.x3 + dx;
const nextY1 = this.y1 + dy;
const nextY2 = this.y2 + dy;
const nextY3 = this.y3 + dy;
const insideCanvas =
nextX1 >= 0 &&
nextX2 >= 0 &&
nextX3 <= width &&
nextY1 <= height &&
nextY2 >= 0 &&
nextY3 <= height;
if (insideCanvas) {
this.x1 = nextX1;
this.x2 = nextX2;
this.x3 = nextX3;
this.y1 = nextY1;
this.y2 = nextY2;
this.y3 = nextY3;
}
};
对于当前这个朝上的三角形,x1 是最左侧坐标,x3 是最右侧坐标,y2 是顶部坐标,y1 和 y3 位于底部。根据这些特点,边界判断还可以简化为:
this.move = function(dx, dy) {
if (
this.x1 + dx >= 0 &&
this.x3 + dx <= width &&
this.y2 + dy >= 0 &&
this.y1 + dy <= height
) {
this.x1 += dx;
this.x2 += dx;
this.x3 += dx;
this.y1 += dy;
this.y2 += dy;
this.y3 += dy;
}
};
其他注意事项
如果还要支持大写按键,可以先把 key 转成小写:
function keyPressed() {
const pressedKey = key.toLowerCase();
if (pressedKey === 'a') {
ship.move(-5, 0);
} else if (pressedKey === 'd') {
ship.move(5, 0);
} else if (pressedKey === 'w') {
ship.move(0, -2);
} else if (pressedKey === 's') {
ship.move(0, 2);
}
}
飞船应在 createCanvas() 之后创建,因为 Ship() 初始化坐标时会用到 width 和 height:
function setup() {
createCanvas(400, 400);
ship = new Ship();
}
constructors.js 和 sketch.js 分开存放不会引起这个移动错误。只要两个文件都已正确加载,并且创建 Ship 实例时 Ship() 已经定义即可。
备注:内容仅供参考。
