gpt4 book ai didi

javascript - 如何使用 WebGL 让 2D 形状环绕 Canvas ?

转载 作者:行者123 更新时间:2023-12-03 04:59:19 25 4
gpt4 key购买 nike

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

关于如何实现这一点有什么建议吗?

最佳答案

你必须绘制它 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 即可。这意味着它们完全偏离右侧,然后您将它们的 x 位置设置为 -halfPlayerWidth这将使它们位于左侧,反之亦然

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;

我们知道玩家(在本例中是一个圆圈)的位置位于其中心。因此,我们知道当它的中心距离屏幕左侧或右侧玩家大小的一半时,它就完全离开屏幕,因此我们希望将其移动到另一侧。这意味着我们可以想象屏幕实际上是 width + halfPlayerSize + halfPlayerSize宽的。第一个halfPlayerSize是为了从左侧迈出,第二个halfPlayerSize是为了从右侧迈出。换句话说,它只是 width + playerSize宽的。然后,我们希望播放器在 x < -halfPlayerSize 时从左到右换行。 。所以我们添加halfPlayerSize到玩家的位置,然后执行 euclideanModulo 来包裹该位置,然后减去 halfPlayerSize。

关于javascript - 如何使用 WebGL 让 2D 形状环绕 Canvas ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42291249/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com