对象必须多快才能到达特定峰值

How quick must an Object be in is y component to reach a specific peak

我想为达到特定峰值(点(x,y))的播放器(左下角)添加一个提升。玩家的水平速度 (v_x) 是恒定的,但其垂直速度可以变化。我想计算到达点 (x,y)(这必须是峰值)所需的结束速度 v_y,这也是给定的。我想也许我可以得到 Vector player 和 Vector point 之间的角度并用运动学公式计算但它没有给我正确的结果。

希望我正确理解你的问题。首先你要计算你需要多少时间才能爬到那个山峰。

// distance left to travel horizontally
delta.x = peak.x - player.x 

// time left to reach peak
t = delta.x / speed.x

假设 delta.x 是 200px,玩家的速度(水平)是每秒 40px。这给了我们 5 秒的时间从 player.y 爬升到 peak.y.

现在我们计算需要垂直移动多远。

// We have 5 seconds to move vertically by this much
delta.y = peak.y - player.y

// How fast to move per second to reach peak in 5 seconds.
speed.y = delta.y / t

关于重力的注意事项:

如果重力为 3m/s(为便于数学计算而简化),则重力在持续施加的每一秒都会变大(因为它在加速)。

second - m/s
1 - 3
2 - 6
3 - 9
4 - 12
5 - 15

这并不意味着 5 秒后重力将您的物体拉动 15 米。这是序列中每一秒的力,所以你实际上把它们加起来就是总数,就是 45。所以你要在上面计算的总距离上加上 45。

跳?有多高?

重力是恒定的,但随着我们接近障碍物的距离会发生变化。

在某个点,距离障碍物有一定距离,跳跃速度是清除障碍物所需的最小速度。

越往后需要更大的速度才能在空中停留足够长的时间以清除它,或者越靠近则速度必须越大才能在扩孔时间内清除。

不清楚你是否想要最佳跳跃速度来清除物体,这也将定义何时跳跃。

所以我们将给定重力作为加速度和水平速度,跳过高度和距离的障碍物需要多少垂直速度。

获取跳跃速度

为了简单起见,我们首先将水平速度归一化

使用的术语

  • v为跳跃速度,或初速度(正为向上)
  • t是时间
  • a是重力加速度(负数向下)
  • h为障碍物高度。积极向上
  • d是到障碍物的距离。相当于归一化水平速度
  • s是水平速度。假设正向障碍物。

用于求解的步骤

下落(自由落体)时的速度方程为v + at

方程 f'(t) = v + at 方程的反导数 f(t) = vt + (1/2)at^2 告诉我们在任何时候我们有多高 t

因为我们已经归一化水平速度s到障碍物的距离等于到障碍物的时间t或到障碍物的距离d

我们还可以使用反导数求解给定距离的高度h = vd+(1/2)ad^2(将t替换为d

现在我们在一个等式中有了我们想要的所有项。 h = vd+(1/2)ad^2

我们想要跳跃速度所以根据 v 重新排列 h = vd+(1/2)ad^2 我们得到 v = -ad/2+h/d

最后一件事。水平速度被归一化,因此始终为 1。我们需要使用任何速度。我们可以通过将到障碍物的距离 d 除以水平速度(速度)s 来做到这一点。换句话说,以 s

的速度完成距离 d 需要多长时间(以秒为单位)

最后的结果是v = -a(d/s)/2+h/(d/s)

作为代码

让我们将其写入代码 (JavaScript)

函数jumpObstacle(jumper, obstacle)将设置物体的跳跃速度jumper只有当跳跃者正在向障碍物移动时才能清除物体obstacle

跳跃者有一个 jumpClear 来提供间隙和最大跳跃速度 maxJumpVel。这意味着跳跃可能无法越过障碍物。

函数returns跳跃高度,如果没有跳跃则为0。

// Assuming +y is up
const gravity = 9.0;
const jumper = {
    vx: 1,
    vy: 0,
    x: 0,
    y: 0,
    maxJumpVel: 3,  // max jump velocity y
    jumpClear: 0.5, // clearance when jumping. May not if max jump to high
};
const wall = {
    x: 4,
    y: 0,   // base
    h: 4,  // height
};
function jumpObstacle(jumper, obstacle) {
    const dist = obstacle.x - jumper.x;
    if (jumper.vx && Math.sign(jumper.vx) === Math.sign(dist)) {
        const nd = dist / jumper.vx;                    // normalize dist (time)
        const h = (obstacle.h + obstacle.y + jumper.jumpClear) - jumper.y; // height to clear
        const vy = -(gravity * nd) / 2 + h / nd;
        jumper.vy = Math.min(vy, jumper.maxJumpVel);
        return jumper.y + vy * nd + gravity * nd * nd * 0.5;
    }
    return 0;
}

演示

我不确定答案是否清晰。该演示将上述内容实现为交互式演示。

当盒子在墙的范围内时点击或点击动画。 jumpObstacle 函数将计算越过墙壁所需的跳跃速度。

我还添加了盒子的宽度,以及x跳跃间隙以确保跳跃在视觉上可以清除墙壁。

如果跳线不能清除墙壁,它会闪烁红色。下次再试。

地面下的红色框表示跳线能够越过墙壁的大约距离。

注意坐标是Y+向下

注意我的最低跳跃速度是每秒271px

// Note y is + down the page
requestAnimationFrame(update);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
const h = 120, w = 200;
const ground = h - 10;
canvas.addEventListener("click", () => {
    jumper.onGround && (jumper.jumpNextFrame = true); 
});

const gravity = 900.0;
const jumper = {
    vx: 60,
    vy: 0,
    x: 0,
    y: ground,
    h: 24,
    w: 8,
    jumpTime: 0,    
    jumpNextFrame: false,
    get onGround() { return this.y >= ground },
    maxJumpVel: -490, 
    jumpClear: -5,
    jumpClearX: 5,
    jumpFail: 0,
    draw(ctx) { 
        this.jumpFail-- > 0 && ctx.fillRect(this.x - this.w / 2, this.y - this.h, this.w, this.h);
        ctx.strokeRect(this.x - this.w / 2, this.y - this.h, this.w, this.h);
    },
    jump(time, what) {
        this.jumpNextFrame = false;
        this.jumpTime = time;
        const h = jumpObstacle(this, what);
        if (h < 0 || h > what.h) { 
            this.jumpTime = 0;
            this.jumpFail = 10;
            this.vy = 0;
        } else {
            console.clear();
            console.log("Jump vel: " + (-this.vy).toFixed(0) + "px per second");
        }
    },
    update(time) {
        this.x = this.vx * time;
        if (this.x > w ) { this.x = this.x % w }  
        if (this.jumpTime > 0) {
            const t = time - this.jumpTime;
            this.y = ground + this.vy * t + gravity * t * t * 0.5;
            if (this.y > ground) {
                this.vy = 0;
                this.jumpTime = 0;
                this.y = ground;
            }            
        }
    }
};
const wall = {
    x: 140,
    y: ground,   
    h: 34,  
    draw(ctx) { ctx.strokeRect(this.x - 1, this.y - this.h, 2, this.h) },
};
function jumpObstacle(jumper, obstacle) {
    const dist = (obstacle.x - (jumper.x - jumper.w)) + jumper.jumpClearX;
    if (jumper.vx && Math.sign(jumper.vx) === Math.sign(dist)) {
        const nd = (dist / jumper.vx) ;                    
        const h = -((obstacle.h + obstacle.y + jumper.jumpClear) - jumper.y); 
        const vy = -(gravity * nd) / 2 + h / nd;
        jumper.vy = vy < jumper.maxJumpVel ? jumper.maxJumpVel : vy;
        return (jumper.vy * nd + gravity * nd * nd * 0.5) - h;
    }
    return 0;
}

function update(time) {
    time /= 1000;
    ctx.clearRect(0, 0, w, h);
    ctx.strokeRect(-1, ground, w + 2, h - ground + 2); // draw ground
    ctx.fillRect(wall.x - 50, ground + 2, 30, 2);
    if (jumper.jumpNextFrame) { jumper.jump(time , wall) }
    jumper.update(time);
    jumper.draw(ctx);
    wall.draw(ctx);
    requestAnimationFrame(update);
}
  console.log("Click animation to jump. Will flash red is not able to clear wall");
canvas { border: 1px solid black; }
<canvas id="canvas" width ="200" height="120"></canvas>