沿着直线以恒定速度从点A移动物体到点B

7

我知道之前已经有人问过这个问题,但是没有找到真正有效的答案。有一个类似的问题,但是速度会根据移动距离而异。

所以我遇到的问题是,我想让一个物体(在这种情况下是一个玩家)以恒定的速度沿着一条直线从点A到点B移动。这是通过点击玩家并将其拖动到我想要他走路的位置来完成的,因此可以在任何方向和距离上进行移动。

我有一些代码几乎可以解决问题,但是玩家总是稍微偏离航线,尤其是他行驶的距离越长。以下是那段代码:

window.addEventListener('mouseup', function(e) {
    selectedPlayer.moveX = e.pageX;
    selectedPlayer.moveY = e.pageY;
    movePlayer(selectedPlayer);
});

function movePlayer(player) {

    var xDistance = player.moveX - player.x;
    var yDistance = player.moveY - player.y;
    var travelDistance = Math.sqrt((xDistance * xDistance) + (yDistance * yDistance));
    var timeToTravel = travelDistance; //This may seem pointless, but I will add a speed variable later
    var playerAngle = Math.atan2(yDistance, xDistance) * (180 / Math.PI);
    var xRatio = Math.atan2(xDistance, travelDistance);
    var yRatio = Math.atan2(yDistance, travelDistance);

    //This function is called in another part of code that repeats it 60 times a second
    walkPlayer = function() {

        setTimeout(function(){
            player.x = player.moveX;
            player.y = player.moveY;
            selectedPlayer = undefined;
            walkPlayer = undefined;
        }, timeToTravel * 20)

        player.angle = playerAngle;
        player.x += xRatio;
        player.y += yRatio;
    };
}

我希望这有意义,我只包含相关代码的部分。我认为我的问题可能在于xRatio和yRatio部分,但我无法弄清楚;我完全被卡住了。
编辑:我想补充说明,playerAngle使玩家面向拖动的方向,这一部分运行正常。

x和y是整数吗?因为移动可能需要浮点数来保持稳定。(我不是特别懂JavaScript,也许var是正确的东西?) - ninMonkey
setTimeout 应该做什么?尤其是每秒钟调用60次的情况下? - Bergi
@Bergi 这是一个(不好的)尝试,旨在让玩家停在正确的位置。它能用,但相当有 bug 和低效率。我还没有时间检查那部分,因为我一直在试图解决主要问题。 - user1898720
2个回答

11

实时演示

以下是使您所需功能正常运行所需的基本要求:

var tx = targetX - x,
    ty = targetY - y,
    dist = Math.sqrt(tx*tx+ty*ty),
    rad = Math.atan2(ty,tx),
    angle = rad/Math.PI * 180;;

    velX = (tx/dist)*thrust;
    velY = (ty/dist)*thrust;

player.x += velX
player.y += velY

这是我之前做的一个演示,听起来就像是你所需要的,我增加了点击功能,可以根据你的问题更改目标。

window.addEventListener('mouseup', function(e) {
    targetX  = e.pageX;
    targetY = e.pageY;
});

var ctx = document.getElementById("canvas").getContext("2d"),
    x = 300,
    y = 0,
    targetX = Math.random()*300,
    targetY = Math.random()*300,
    velX = 0,
    velY = 0,
    thrust = 5;


function draw(){   
    var tx = targetX - x,
        ty = targetY - y,
        dist = Math.sqrt(tx*tx+ty*ty),
        rad = Math.atan2(ty,tx),
        angle = rad/Math.PI * 180;;

    velX = (tx/dist)*thrust;
    velY = (ty/dist)*thrust;

    // stop the box if its too close so it doesn't just rotate and bounce
    if(dist > 1){
      x += velX;
      y += velY;
    }

    ctx.fillStyle = "#fff";
    ctx.clearRect(0,0,400,400);
    ctx.beginPath();
    ctx.rect(x, y, 10, 10);
    ctx.closePath();
    ctx.fill();

    ctx.fillStyle = "#ff0";
    ctx.beginPath();
    ctx.rect(targetX, targetY, 10, 10);
    ctx.closePath();
    ctx.fill();

    setTimeout(function(){draw()}, 30);   
}

draw();

看起来非常像我正在寻找的,谢谢!我会尝试将其适应我的代码并查看它是否有效,并在此后与您联系。 - user1898720
@JamieRushworth 是的,我有点匆忙地发布了它,我在上面写了一些解释,并修改了我的代码示例,以基于用户点击的位置为目标。 - Loktar
这个效果好多了,谢谢!感谢你更新鼠标点击的部分,不过在你的演示中它似乎会在目标周围弹跳,这有点奇怪。我想这是因为一旦绘制函数到达目的地就没有实际停止的东西? - user1898720
@Loktar:当白点到达目标时,它会闪烁。 - Bergi
1
它仍然有一半的时间闪烁,但我已经使用了@Bergi的time方法来解决这个问题。在我获得15个声望之前,我无法为您的帖子点赞,但我会确保在我能够时这样做! - user1898720
显示剩余2条评论

4

您的问题似乎是 xRatioyRatio 是角度,而不是向量坐标。以下代码应该能解决您的问题:

document.addEventListener('mouseup', function(e) {
    movePlayer(selectedPlayer, {x:e.pageX, y:e.pageY});
});

function movePlayer(player, target) {
    var start = {
            x: player.x,
            y: player.y,
            t: Date.now()
        },
        distance = Math.sqrt(distance.x*distance.x + distance.y*distance.y),
        time = distance; //This may seem pointless, but I will add a speed variable later
        difference = {
            x: target.x - player.x,
            y: target.y - player.y,
            t: time
        };

    player.angle = Math.atan2(distance.y, distance.x) * (180 / Math.PI);

    //This function is called in another part of code that repeats it 60 times a second
    walkPlayer = function(curTime) { // we need timing information here: Date.now()
        var elapsed = curTime - start.t,
            ratio = elapsed / difference.t;
        player.x = start.x + difference.x * ratio;
        player.y = start.y + difference.y * ratio;
        if (ratio >= 1) {
            player.x = target.x;
            player.y = target.y;
            // end calling of walkPlayer
        }
    };
}

非常感谢,我已经使用了您的时间部分,它运行得很好。在我获得15个声望之前,我无法为您的帖子点赞,但我一定会在可以的时候这样做! - user1898720

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接