我正在开展一个单独的项目,我正试图从炮塔到敌人画一条线。但我希望这条线保持在炮塔的边界内,而不是一直延伸到敌人。我在下面的代码片段中找到了一个较小的示例。
请彻底解答,因为我绝对不是专家。非常感谢!感谢。
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var turret = {x:20, y:20, w:20, h:20};
var randomBox = {x:30+Math.random() * 300, y:30+Math.random() * 300, w:40, h:40};
c.fillStyle = "red";
c.fillRect(turret.x, turret.y, turret.w, turret.h);
c.fillStyle = "yellow";
c.fillRect(randomBox.x, randomBox.y, randomBox.w, randomBox.h);
c.beginPath();
c.moveTo(turret.x + (turret.w/2), turret.y + (turret.h/2));
c.lineTo(randomBox.x, randomBox.y);
c.stroke();
#canvas {
background-color: #eee;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<canvas id="canvas" width=500 height=500></canvas>
</body>
</html>
答案 0 :(得分:1)
一条线可以被认为是一个坐标和一个矢量,该矢量描述了从起始坐标到终点的方向和距离。当您通过其端点描述一条线时,您使用的是2个坐标。通常我们将一个以开头和结尾为线的线称为线段,而不是实际上无限长的线。
线段为坐标
var x1 = 100;
var y1 = 100;
var x2 = 300;
var y2 = 400;
要获取直线的矢量分量,只需从终点减去起点即可。向量是各种类型的线段,它们的起始点始终位于坐标0,0。
var vx = x2 - x1; // v for vector
var vy = y2 - y1;
所有向量的大小(或长度)都是由于计算长度而总是正的。矢量的x和y分量构成三角形的两个短边,沿矢量的线是最长的线,即斜边。
我们使用毕达哥拉斯来计算向量幅度。
var mag = Math.sqrt(vx * vx + vy * vy); // mag for magnitude
在许多关于线条的情况下(比如在这种情况下),我们对线条的长度并不是那么感兴趣,而只是方向。为了简化计算,我们喜欢使用一种称为单位向量的特殊形式的向量。这是一个正好长1个单位的向量(在我们的例子中,1是单位是一个像素)
单位矢量具有许多在执行几何时非常有用的属性。
创建单位向量的过程称为规范化。它只是通过将矢量的x,y部分除以其总长度(如上所计算)来完成的
// normalise vector
var nx = vx / mag; // n for normal
var ny = vy / mag;
现在你有了表示直线方向的单位向量,很容易找到距离起点固定距离的点。就像在数字线上一样,如果你想要一个点10个单位,比如5,你可以添加10到5.但它也与10“单位”到5或10 * 1 + 5
相同。在几何中,单位是单位矢量。
因此,如果我们想要从开始点开始10个单位(像素),我们将单位向量的两个分量乘以10,并将它们添加到起始坐标。
var endX = x1 + nx * 10;
var endY = y1 + ny * 10;
现在是endX,endY距离线路起点10个像素。
正如您所看到的那样,使用单位矢量可以轻松地获得距离起点任何距离的任何点。
所有矢量库都包括矢量和坐标的上述操作。我们将一组两个坐标(x,y)称为2D矢量vec
,我们有add()
,sub()
,multiply()
,divide()
,{ {1}},magnitude()
以及一些同样方便的操作,例如normalise()
和cross()
,但目前这个问题超出了这个范围。
如果您对编写游戏感兴趣,我强烈建议您编写自己的小型2D矢量库,创建时间不应超过一小时(如果您不熟悉编程,可能需要两个小时),并且您将学习一些必要的数学几乎任何类型的游戏所需的技能(BTW 3D矢量数学几乎与2D相同,只有额外的组件称为z)。
我复制了您的代码并进行了一些更改。我还在上面描述的数学中做了几个捷径。与任何一组公式一样,如果将它们放在一起,您可以简化它们以获得更有效和易于理解的公式。在游戏中,这是一件特别容易的事情,你可能每秒进行数百万到数百万次的计算。
函数dot()
使用线段x1,y1,x2,y2和值maxLen绘制一条长度不超过drawLineLength
像素的线。
如果您有疑问,请在评论中提问。
maxLen
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
c.lineWidth = 4;
c.lineCap = "round";
var turret = {x:20, y:20, w:20, h:20};
const maxLineLength = 50; // max length of line in pixels
var rBox = { // FKA randomBox
x : 0,
y : 0,
w : 40,
h : 40
};
// drawLineLength draws a line with maximum length
// x1,y1 from
// x2,y2 line to
// maxLen the maximum length of the line. If line is shorter it will not be changed
function drawLineLength (x1, y1, x2, y2, maxLen) {
var vx = x2 - x1; // get dist between start and end of line
var vy = y2 - y1; // for x and y
// use pythagoras to get line total length
var mag = Math.sqrt(vx * vx + vy * vy);
if (mag > maxLen) { // is the line longer than needed?
// calculate how much to scale the line to get the correct distance
mag = maxLen / mag;
vx *= mag;
vy *= mag;
}
c.beginPath();
c.moveTo(x1, y1);
c.lineTo(x1 + vx, y1 + vy);
c.stroke();
}
// Test function is on a timer and run every 500ms (1/2 second)
function doTest () {
// clear the canvas of last render
c.clearRect(0, 0, canvas.width, canvas.height);
// find a random position for the box inside the canvas and 10 pixels from any edge
rBox.x = 10 + Math.random() * (canvas.width - rBox.w - 20);
rBox.y = 10 + Math.random() * (canvas.height - rBox.h - 20);
// draw both objects
c.fillStyle = "red";
c.fillRect(turret.x, turret.y, turret.w, turret.h);
c.fillStyle = "yellow";
c.fillRect(rBox.x, rBox.y, rBox.w, rBox.h);
// call function to draw line that will have a maximum length
drawLineLength(
turret.x + turret.w / 2,
turret.y + turret.h / 2,
rBox.x + rBox.w / 2,
rBox.y + rBox.h / 2,
maxLineLength
);
setTimeout(doTest,500);
}
doTest();
#canvas { background-color: #eee; }