首页 > 解决方案 > Matter.JS 约束在对象旋转后就像弹弓一样

问题描述

我在使用 MatterJS 库进行简单测试时遇到了问题。

我想构建一种通过设置其第一个元素的速度和角度来控制的火车(链,蛇)(请参阅下面的代码)。我希望有一些刚度和阻尼,但是在应用一些旋转约束之后开始像弹弓一样,扩展然后将两个对象移动到随机方向。

这是某种浮点问题吗?我在当前的引擎设置等方面没有发现任何问题。可以以某种方式处理吗?

测试片段(包含主游戏类和实用键盘功能):

function keyboard(value) {
    let key = {};
    key.value = value;
    key.isDown = false;
    key.isUp = true;
    key.press = undefined;
    key.release = undefined;

    key.downHandler = event => {
        if (event.key === key.value) {
            if (key.isUp && key.press) key.press();
            key.isDown = true;
            key.isUp = false;
            event.preventDefault();
        }
    };

    key.upHandler = event => {
        if (event.key === key.value) {
            if (key.isDown && key.release) key.release();
            key.isDown = false;
            key.isUp = true;
            event.preventDefault();
        }
    };

    //Attach event listeners
    const downListener = key.downHandler.bind(key);
    const upListener = key.upHandler.bind(key);
    
    window.addEventListener(
        "keydown", downListener, false
    );
    window.addEventListener(
        "keyup", upListener, false
    );
    
    // Detach event listeners
    key.unsubscribe = () => {
        window.removeEventListener("keydown", downListener);
        window.removeEventListener("keyup", upListener);
    };
    
    return key;
}

const Engine = Matter.Engine,
    Render = Matter.Render,
    World = Matter.World,
    Body = Matter.Body,
    Bodies = Matter.Bodies,
    Mouse = Matter.Mouse,
    Runner = Matter.Runner,
    Composite = Matter.Composite;

class Game {
    constructor(document) {
        this.engine = Engine.create({
            gravity: {
                y: 0,
            }
        });

        let position = {x: 100, y: 100};
        this.createLeader(position);

        position.x -= 70;
        this.createFollower(position);
        this.attachFollower();

        this.setupControls();
    
        Runner.run(this.engine);

        this.setupRender();    
    }

    createLeader = (position) => {
        this.leader = Bodies.rectangle(
            0,
            0,
            64,
            64,
            {
                //restitution: 0.8,
                position,
                density: 0.1
            }
        );
        World.addBody(this.engine.world, this.leader);
    }

    createFollower = (position) => {
        this.follower = Bodies.rectangle(
            0,
            0,
            45,
            45,
            {
                //restitution: 0.8,
                position,
                density: 0.01,
            } 
        );
        World.addBody(this.engine.world, this.follower);
    }

    attachFollower = () => {
        let attachTo = [{x: -30, y: -10}, {x: -30, y: 10}];
        let attachableAt = [{x: 20, y: -10}, {x: 20, y: 10}];

        this.constraints = attachableAt.map((point, index) => {
            return Matter.Constraint.create({
                bodyA: this.leader,
                pointA: attachTo[index],
                bodyB: this.follower,
                pointB: point,
                //length: 20,
                stiffness: 0.01,
                damping: 0.1
            });
        });
        Composite.add(this.engine.world, [this.leader, this.follower, ...this.constraints]);
    }

    setupControls = () => {
        this.right = keyboard('d');

        requestAnimationFrame(this.update);
    }

    setupRender = () => {
        var render = Render.create({
            element: document.body,
            engine: this.engine,
            options: {
                width: window.innerWidth, 
                height: window.innerHeight,
                showAngleIndicator: true,
                wireframes: true,
                wireframeBackground: '#000000',
                showBounds: true,
            }
        });

        Render.run(render);
    } 

    update = () => {
        if(this.right.isDown) {
            this.leader.angle += 0.001;
        }

        requestAnimationFrame(this.update);
    }
}

function onLoad() {
    game = new Game(document);
}
onLoad();
* {
  padding: 0;
  margin: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

重现问题的步骤:

  1. 运行代码片段
  2. 按住“D”键可旋转对象约 10 分钟。300度。
  3. 释放“D”键

结果:两个物体都飞走了

标签: javascriptconstraintsphysics-enginematter.js

解决方案


推荐阅读