【问题标题】:Simulation of the rotation angular speed旋转角速度的模拟
【发布时间】:2021-03-20 10:02:11
【问题描述】:

我希望能够在物理方面模拟“旋转木马”上的身体运动。 (向心力、离心力、角速度)。下面是一些示例代码。

<!DOCTYPE html>
<html>
    <head>
        <script>
            
            var rotate = Math.PI / 180;
            var ballRotation = 1;
        
            function drawthis() {
    
                var friction = 0.5;

                context.setTransform(1, 0, 0, 1, 0, 0);
                context.clearRect(0, 0, cvs.width, cvs.height);
                context.translate(350, 350);
                context.rotate(rotate);
                context.beginPath();
                context.arc(1, 1, 12, 0, 2 * Math.PI, false);
                context.fill();
                context.beginPath();
                context.arc(0, 0, 150, 0, Math.PI * 2, false);
                context.lineWidth = 6;
                context.stroke();
                
                motion = ballRotation - friction;
                rotate += motion;
                requestAnimationFrame(drawthis);
}
            function init() {
                cvs = document.getElementById("canvas");
                context = cvs.getContext("2d");
                context.clearRect(0, 0, context.width, context.height);
                context.fillStyle = "#ff0000";
                requestAnimationFrame(drawthis);
}
        </script>
    </head>
    <body onload="init()">
            <canvas id="canvas" width="800" height="800"></canvas>
    </body>
</html>

I mean something like this

【问题讨论】:

    标签: javascript simulation game-physics physics simulate


    【解决方案1】:

    转盘上的球

    您将在下面找到一个点在转轮上滑动的简单模拟。该点代表一个球的接触点。

    模拟忽略了球可以滚动或有质量的事实。

    球通过一个简单的摩擦模型滑动,其中摩擦是一个标量值,应用于球速度矢量与球下方点的车轮速度之间的差值。

    只涉及一种力量。它是与从球到轮心的矢量相切的力,减去球运动矢量,然后乘以摩擦系数。

    有关如何计算的详细信息,请参阅函数 ball.update() 中的 cmets

    注意事项

    • 如果球从车轮的死点开始,什么都不会发生。

    • 如果是你想要的球的路径或者只是球的模拟,我无法锻炼,所以我都添加了。

    • 球离开轮子后会重置。

    • 车轮标有文字和中心十字,因此可以看到它的旋转。

    const ROTATE = Math.PI / 50;
    const WHEEL_SIZE = 0.6;
    Math.rand = (min, max) => Math.random() * (max - min) + min;
    Math.randPow = (min, max, p) => Math.random() ** p * (max - min) + min;
    
    var friction = 0.35;
    const ctx = canvas.getContext("2d");
    requestAnimationFrame(mainLoop);
    ctx.font = "30px arial";
    ctx.textAlign = "center";
    scrollBy(0, canvas.height / 2 - canvas.height / 2 * WHEEL_SIZE);
    
    function mainLoop() {
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        wheel.update();
        ball.update(wheel, arrow);
        wheel.draw();
        path.draw();
        ball.draw();
        arrow.draw(ball);
        requestAnimationFrame(mainLoop);
    }
    
    const path = Object.assign([],{
        draw() {
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            ctx.strokeStyle = "#F00";
            ctx.lineWidth = 1;
            ctx.beginPath();
            for (const p of this) { ctx.lineTo(p.x, p.y) }
            ctx.stroke();
        },
        reset() { this.length = 0 },
        add(point) {
            this.push({x: point.x, y: point.y});
            if (this.length > 1000) {  // prevent long lines from slowing render
                this.shift()
            }
        }
    });  
    const arrow = {
        dx: 0,dy: 0,
        draw(ball) {
            if (this.dx || this.dy) {
                const dir = Math.atan2(this.dy, this.dx);
                
                // len is converted from frame 1/60th second to seconds
                const len = Math.hypot(this.dy, this.dx) * 60; 
                const aXx = Math.cos(dir);
                const aXy = Math.sin(dir);        
                ctx.setTransform(aXx, aXy, -aXy, aXx, ball.x, ball.y);
                ctx.beginPath();
                ctx.lineTo(0,0);
                ctx.lineTo(len, 0);
                ctx.moveTo(len - 4, -2);
                ctx.lineTo(len, 0);
                ctx.lineTo(len - 4, 2);
                ctx.strokeStyle = "#FFF";
                ctx.lineWidth = 2;     
                ctx.stroke();
            }
        }
    };
    const ball = {
        x: canvas.width / 2 + 4,
        y: canvas.height / 2,
        dx: 0,  // delta pos Movement vector
        dy: 0,
        update(wheel, arrow) {
            // get distance from center
            const dist = Math.hypot(wheel.x - this.x, wheel.y - this.y);
            
            // zero force arrow
            arrow.dx = 0;
            arrow.dy = 0;
             
            // check if on wheel
            if (dist < wheel.radius) {
                // get tangent vector direction
                const tangent = Math.atan2(this.y - wheel.y, this.x - wheel.x) + Math.PI * 0.5 * Math.sign(wheel.dr);
                // get tangent as vector
                // which is distance times wheel rotation in radians.
                const tx = Math.cos(tangent) * dist * wheel.dr;
                const ty = Math.sin(tangent) * dist * wheel.dr;
    
                // get difference between ball vector and tangent vector scaling by friction
                const fx = arrow.dx = (tx - this.dx) * friction;
                const fy = arrow.dy = (ty - this.dy) * friction;
    
                // Add the force vector 
                this.dx += fx;
                this.dy += fy;
            } else if (dist > wheel.radius * 1.7) { // reset ball
    
                // to ensure ball is off center use random polar coord
                const dir = Math.rand(0, Math.PI * 2);
                const dist = Math.randPow(1, 20, 2);  // add bias to be close to center
                this.x = canvas.width / 2 + Math.cos(dir) * dist;
                this.y = canvas.height / 2 + Math.sin(dir) * dist;
                this.dx = 0;  
                this.dy = 0;        
                path.reset();
            }
            // move the ball
            this.x += this.dx;
            this.y += this.dy;      
            path.add(ball);
        },    
        draw() {
            ctx.fillStyle = "#0004";
            ctx.setTransform(1, 0, 0, 1, this.x + 5, this.y + 5);
            ctx.beginPath();
            ctx.arc(0, 0, 10, 0, 2 * Math.PI);
            ctx.fill();    
            ctx.fillStyle = "#f00";
            ctx.setTransform(1, 0, 0, 1, this.x, this.y);
            ctx.beginPath();
            ctx.arc(0, 0, 12, 0, 2 * Math.PI);
            ctx.fill();    
            ctx.fillStyle = "#FFF8";
            ctx.setTransform(1, 0, 0, 1, this.x - 5, this.y - 5);
            ctx.beginPath();
            ctx.ellipse(0, 0, 2, 3, -Math.PI * 0.75, 0, 2 * Math.PI);
            ctx.fill();  
        },
    }
    const wheel = {
        x: canvas.width / 2, y: canvas.height / 2, r: 0,
        dr: ROTATE, // delta rotate
        radius: Math.min(canvas.height, canvas.width) / 2 * WHEEL_SIZE,
        text: "wheel",
        update() { this.r += this.dr },
        draw() {
            const aXx = Math.cos(this.r);
            const aXy = Math.sin(this.r);
            ctx.setTransform(aXx, aXy, -aXy, aXx, this.x, this.y);
            ctx.fillStyle = "#CCC";
            ctx.strokeStyle = "#000";
            ctx.lineWidth = 6;
            ctx.beginPath();
            ctx.arc(0, 0, this.radius, 0, 2 * Math.PI);
            ctx.stroke();
            ctx.fill();    
            ctx.strokeStyle = ctx.fillStyle = "#aaa";
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.lineTo(-20,0);
            ctx.lineTo(20,0);
            ctx.moveTo(0,-20);
            ctx.lineTo(0,20);
            ctx.stroke();
            ctx.fillText(this.text, 0, this.radius - 16);
        },    
    }
    &lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;

    向心力

    向心力是朝向转动轮中心的力。然而,由于球在滑动,计算的力不是向心力。

    您可以通过“向量到中心”点“力向量”的点积来缩放从球到中心的向量来计算向心力

    球上的力矢量显示为白色箭头。箭头大小是以每秒像素为单位的加速度。

    矢量指向中心,但永远不会直接指向轮子的中心。

    近似值

    这个模拟是一个近似值。您需要了解微积分和微分方程才能更接近现实。

    只有当摩擦力非常接近或为 1 时,使用更复杂的模拟才会引人注目,并且更容易将球固定到车轮上,通过摩擦系数的倒数从中心缩放位置。

    【讨论】:

    • 非常感谢你试图澄清这个话题,但你能再看一下吗?我曾多次尝试运行模拟,但我的屏幕一直是空白的。 @盲人67
    • @ttt 我看不到任何问题。该演示在我手头的各种设备上运行良好。
    • 我不能早点回信。它起作用了,我开始分析和思考它。我可以将该解决方案用于教学目的吗?我很感谢您抽出宝贵的时间,同时将答案标记为有帮助。 @盲人67
    • @ttt SO 上的所有内容都属于公共领域,可以以您希望的任何方式使用。重要的是要强调这个模拟中的球是滑动而不是滚动。滚动球的行为会有所不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 2013-03-24
    • 1970-01-01
    • 2019-09-29
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多