PAYATHON 2026

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() 已经定义即可。

备注:内容仅供参考。