我已经在WebGL(html&javascript)中创建了一个简单的动画,其中在画布上对2D形状进行了动画处理和操纵。默认动画是形状以设定的速度向右移动,然后使用“ WASD”更改其方向。即使从画布上移出并从剪辑空间中移出,形状也会无限地沿给定方向移动。我想将形状包裹在画布上,而不是即使看不见也要继续。例如,如果形状向右移动并完全移出画布,我希望它出现在左侧,但仍向右移动并继续循环。它向左或向上或向下移动也是如此。

关于如何实施此建议?

最佳答案

您必须将其绘制2至4次,具体取决于是否要同时包裹左/右和上/下

假设我们只想水平环绕。如果播放器位于左边缘附近,我们还需要向右侧绘制播放器1的屏幕宽度。如果玩家在右边缘附近,我们需要在左侧向左拖动一个玩家。上下相同

这是一个使用canvas 2D的示例,但是WebGL的唯一区别是您将使用WebGL进行绘制。概念是相同的。

例:



var x = 150;
var y = 100;
var vx = 0;
var vy = 0;
const maxSpeed = 250;
const acceleration = 1000;
const ctx = document.querySelector("canvas").getContext("2d");
const keys = {};
const LEFT = 65;
const RIGHT = 68;
const DOWN = 83;
const UP = 87;
const width = ctx.canvas.width;
const height = ctx.canvas.height;

var then = 0;
function render(now) {
  now *= 0.001;  // seconds
  const deltaTime = now - then;
  then = now;

  ctx.clearRect(0, 0, width, height);

  if (keys[UP])    { vy -= acceleration * deltaTime; }
  if (keys[DOWN])  { vy += acceleration * deltaTime; }
  if (keys[LEFT])  { vx -= acceleration * deltaTime; }
  if (keys[RIGHT]) { vx += acceleration * deltaTime; }

  // keep speed under max
  vx = absmin(vx, maxSpeed);
  vy = absmin(vy, maxSpeed);

  // move based on velociy
  x += vx * deltaTime;
  y += vy * deltaTime;

  // wrap
  x = euclideanModulo(x, width);
  y = euclideanModulo(y, height);

  // draw player 4 times
  const xoff = x < width / 2 ? width : -width;
  const yoff = y < height / 2 ? height : -height;
  drawPlayer(x, y);
  drawPlayer(x + xoff, y);
  drawPlayer(x, y + yoff);
  drawPlayer(x + xoff, y + yoff);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);

function drawPlayer(x, y) {
  ctx.fillStyle = "blue";
  ctx.strokeStyle = "red";
  ctx.lineWidth = 4;
  ctx.beginPath();
  ctx.arc(x, y, 20, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.stroke();
}

function absmin(v, max) {
  return Math.min(Math.abs(v), max) * Math.sign(v);
}

function euclideanModulo(n, m) {
	return ((n % m) + m) % m;
}

window.addEventListener('keydown', e => {
  keys[e.keyCode] = true;
});

window.addEventListener('keyup', e => {
  keys[e.keyCode] = false;
});

canvas {
  display: block;
  border: 1px solid black;
}

<canvas></canvas>
<p><span style="color:red;">click here</span> then use ASWD to move</p>





WebGL版本不更改与包装相关的代码。



var x = 150;
var y = 100;
var vx = 0;
var vy = 0;
const maxSpeed = 250;
const acceleration = 1000;
const gl = document.querySelector("canvas").getContext("webgl");
const keys = {};
const LEFT = 65;
const RIGHT = 68;
const DOWN = 83;
const UP = 87;
const width = gl.canvas.width;
const height = gl.canvas.height;

var program = setupWebGL();
var positionLoc = gl.getAttribLocation(program, "position");

var then = 0;
function render(now) {
  now *= 0.001;  // seconds
  const deltaTime = now - then;
  then = now;

  if (keys[UP])    { vy -= acceleration * deltaTime; }
  if (keys[DOWN])  { vy += acceleration * deltaTime; }
  if (keys[LEFT])  { vx -= acceleration * deltaTime; }
  if (keys[RIGHT]) { vx += acceleration * deltaTime; }

  // keep speed under max
  vx = absmin(vx, maxSpeed);
  vy = absmin(vy, maxSpeed);

  // move based on velociy
  x += vx * deltaTime;
  y += vy * deltaTime;

  // wrap
  x = euclideanModulo(x, width);
  y = euclideanModulo(y, height);

  // draw player 4 times
  const xoff = x < width / 2 ? width : -width;
  const yoff = y < height / 2 ? height : -height;
  drawPlayer(x, y);
  drawPlayer(x + xoff, y);
  drawPlayer(x, y + yoff);
  drawPlayer(x + xoff, y + yoff);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);

function drawPlayer(x, y) {
  gl.useProgram(program);
  // only drawing a single point so no need to use a buffer
  gl.vertexAttrib2f(
     positionLoc,
     x / width * 2 - 1,
     y / height * -2 + 1);
  gl.drawArrays(gl.POINTS, 0, 1);
}

function absmin(v, max) {
  return Math.min(Math.abs(v), max) * Math.sign(v);
}

function euclideanModulo(n, m) {
	return ((n % m) + m) % m;
}

window.addEventListener('keydown', e => {
  keys[e.keyCode] = true;
});

window.addEventListener('keyup', e => {
  keys[e.keyCode] = false;
});

function setupWebGL() {
  const vs = `
  attribute vec4 position;
  void main() {
    gl_Position = position;
    gl_PointSize = 40.;
  }
  `;
  const fs = `
  void main() {
    gl_FragColor = vec4(1,0,1,1);
  }
  `;
  // compiles and links shaders and assigns position to location 0
  return twgl.createProgramFromSources(gl, [vs, fs]);
}

canvas {
  display: block;
  border: 1px solid black;
}

<canvas></canvas>
<p><span style="color:red;">click here</span> then use ASWD to move</p>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>





如果您不希望播放器出现在两侧,那么您的问题与图形无关,您只需等到播放器的x位置至少为screenWidth + haflPlayerWidth,这意味着播放器完全位于右侧,然后进行设置他们在-halfPlayerWidth的x位置,这会使他们偏离左侧,反之亦然



var x = 150;
var y = 100;
var vx = 0;
var vy = 0;
const maxSpeed = 250;
const acceleration = 1000;
const ctx = document.querySelector("canvas").getContext("2d");
const keys = {};
const LEFT = 65;
const RIGHT = 68;
const DOWN = 83;
const UP = 87;
const width = ctx.canvas.width;
const height = ctx.canvas.height;
const playerSize = 40;
const halfPlayerSize = playerSize / 2;

var then = 0;
function render(now) {
  now *= 0.001;  // seconds
  const deltaTime = now - then;
  then = now;

  ctx.clearRect(0, 0, width, height);

  if (keys[UP])    { vy -= acceleration * deltaTime; }
  if (keys[DOWN])  { vy += acceleration * deltaTime; }
  if (keys[LEFT])  { vx -= acceleration * deltaTime; }
  if (keys[RIGHT]) { vx += acceleration * deltaTime; }

  // keep speed under max
  vx = absmin(vx, maxSpeed);
  vy = absmin(vy, maxSpeed);

  // move based on velociy
  x += vx * deltaTime;
  y += vy * deltaTime;

  // wrap
  x = euclideanModulo(x + halfPlayerSize, width + playerSize) - halfPlayerSize;
  y = euclideanModulo(y + halfPlayerSize, height + playerSize) - halfPlayerSize;

  // draw player
  drawPlayer(x, y);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);

function drawPlayer(x, y) {
  ctx.fillStyle = "blue";
  ctx.strokeStyle = "red";
  ctx.lineWidth = 4;
  ctx.beginPath();
  ctx.arc(x, y, halfPlayerSize, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.stroke();
}

function absmin(v, max) {
  return Math.min(Math.abs(v), max) * Math.sign(v);
}

function euclideanModulo(n, m) {
	return ((n % m) + m) % m;
}

window.addEventListener('keydown', e => {
  keys[e.keyCode] = true;
});

window.addEventListener('keyup', e => {
  keys[e.keyCode] = false;
});

canvas {
  display: block;
  border: 1px solid black;
}

<canvas></canvas>
<p><span style="color:red;">click here</span> then use ASWD to move</p>





该代码可能需要解释

x = euclideanModulo(x + haflPlayerSize, width + playerSize) - haflPlayerSize;
y = euclideanModulo(y + haflPlayerSize, height + playerSize) - haflPlayerSize;


首先,euclideanModulo就像普通的%模运算符一样,除法后,即使对负数,欧几里德模仍保持相同的余数,它返回除法运算后的余数。换一种说法

  3 % 5 = 3
  8 % 5 = 3
 13 % 5 = 3
 -2 % 5 = -2
 -7 % 5 = -2
-12 % 5 = -2




  3 euclideanMod 5 = 3
  8 euclideanMod 5 = 3
 13 euclideanMod 5 = 3
 -2 euclideanMod 5 = 3
 -7 euclideanMod 5 = 3
-12 euclideanMod 5 = 3


因此,这是包装物品的超级简单方法。

 x = euclideanModulo(x, screenWidth)


类似于

 if (x < 0)            x += screenWidth;
 if (x >= screenWidth) x -= screenWidth;


除非如果x > screenWidth * 2会失败,例如使用euclideanModulo的那个不会失败。

所以,回到

x = euclideanModulo(x + haflPlayerSize, width + playerSize) - haflPlayerSize;
y = euclideanModulo(y + haflPlayerSize, height + playerSize) - haflPlayerSize;


我们知道玩家(在本例中为圆圈)在其中心位置。因此,我们知道当其中心位于屏幕左右两侧的playerSize的一半时,它完全不在屏幕上,因此我们希望将其移到另一侧。这意味着我们可以想象屏幕确实是width + halfPlayerSize + halfPlayerSize宽的。第一个halfPlayerSize用于下移左侧,第二个halfPlayerSize用于下移右侧。换句话说,它只有width + playerSize宽。然后,我们希望玩家在x < -halfPlayerSize时从左到右环绕。因此,我们将halfPlayerSize添加到玩家的位置,然后执行euclideanModulo来包装位置,然后减去halfPlayerSize。

08-06 03:21