1. 程式人生 > >Matter-JS constraint 約束

Matter-JS constraint 約束

目錄

constraint 約束

Constraint.create


constraint 約束

1、多個物體可以使用一根有彈性的繩子連線起來,這個繩子就相當於 constraint (約束),被約束的多個剛體連線在一起之後,移動就相互受到了牽制。

2、Matter.Constraint 模組包含了用於建立和處理約束的方法,如約束的長度、約束的強度等。

3、官網 API:http://brm.io/matter-js/docs/classes/Constraint.html

4、Matter.Constraint.create(options)→ Constraint 用於建立約束,具體使用在程式碼的註釋中已經詳細說明。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>Matter-JS</title>
    <!--matter-js cdnjs地址-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.js"></script>
    <script type="text/javascript">
        var stageWidth = 800;//舞臺寬度
        var stageHeight = 500;//舞臺高度
        window.onload = function () {
            var Engine = Matter.Engine;//引擎
            var Render = Matter.Render;//渲染器
            var World = Matter.World;//世界
            var Constraint = Matter.Constraint;//約束
            var MouseConstraint = Matter.MouseConstraint;//滑鼠控制
            var Composites = Matter.Composites;//複合材料
            var Bodies = Matter.Bodies;//物體

            var engine = Engine.create();//建立引擎
            var render = Render.create({//建立渲染器
                engine: engine,//渲染建立好的引擎
                element: document.body,//渲染頁面的body元素
                options: {
                    width: stageWidth,
                    height: stageHeight,
                    wireframes: false,//是否顯示邊線框(顯示邊線方便除錯)
                    showVelocity: true//是否顯示速度
                }
            });

            Engine.run(engine);//執行引擎
            Render.run(render);//執行渲染器

            var crossArr = createCross(Bodies);
            /**
             * Constraint.create():建立約束,其引數是一個json物件,主要引數有:bodyA,pointA,bodyB,pointB,length,stiffness等
             * 本例中:bodyA 和 bodyB 分別為蹺蹺板橫豎兩個矩形;
             * pointA 和 pointB 表示約束點的位置,其值為向量,預設為0,在物體中心位置。以物體中心為原點進行量相減(三角形法則)
             * 為兩個向量,預設為0向量,即物體的約束點預設在物體中心位置,否則,物體的對應的約束點為中心位置加上所賦的向量值。
             * crossArr[0]是蹺蹺板豎杆,高150,以中心為起點,y方向-75,則約束點剛好在矩形頂點中間位置
             * crossArr[1]是蹺蹺板橫杆,為設定 pointB 時,其約束點預設在物體中心{x:0,y:0}
             */
            var rotate = Constraint.create({
                bodyA: crossArr[0],
                pointA: {x: 0, y: -75},
                bodyB: crossArr[1],
                length: 0,//約束點的長度
                stiffness: 0.9//剛度值(0,1],值越大,物體剛度越強,越不容易拉伸
            });

            /**建立一個堆疊混合體——堆疊矩形,3行4列
             * stack(xx, yy, columns, rows, columnGap, rowGap, callback)*/
            var stack_rect = Composites.stack(stageWidth / 4, 20, 4, 3, 0, 0, function (x, y) {
                return Bodies.rectangle(x, y, 100, 40);//堆疊的每個矩形寬100,高40
            });

            /**新增滑鼠控制*/
            var mouseConstraint = MouseConstraint.create(engine, {
                element: document.body
            });

            World.add(engine.world, crossArr);//新增十字架剛體
            World.add(engine.world, [stack_rect, mouseConstraint, rotate]);//新增複合體和滑鼠控制以及約束
            World.add(engine.world, create4Wall(Bodies));//新增4面牆到世界中
        }

        /**
         * 建立一個蹺蹺板(橫豎兩個矩形組成)
         */
        function createCross(Bodies) {
            /**豎杆(矩形),這裡有兩個屬性需要注意
             * render:{visible:true,opacity:1,sprite:{xScale:1,yScale:1,xOffset:0,yOffset:0},lineWidth:0}
             * collisionFilter:{category:0x0001,mask:0xFFFFFFFF,group:0}:碰撞過濾器
             * group相等的物體,當group>0時,則它們始終會碰撞
             * group相等的物體,當group<0時,則它們始終不碰撞
             */
            var rectA = Bodies.rectangle(stageWidth / 2, stageHeight - 75, 40, 150, {
                isStatic: true,//靜止豎杆運動
                render: {
                    fillStyle: "#0ff"//設定物體背景顏色
                },
                collisionFilter: {
                    group: -1//十字架的橫杆與豎杆設定為始終不會碰撞,兩者的group相等且小於0
                }
            });
            var rectB = Bodies.rectangle(stageWidth / 2, 350, 500, 40, {//建立十字架橫杆(矩形)
                render: {
                    fillStyle: "#f00"
                },
                collisionFilter: {
                    group: -1
                }
            });
            return [rectA, rectB];
        }

        /**
         *建立4面牆-強制物體在牆內運動
         */
        function create4Wall(Bodies) {
            var ground_top = Bodies.rectangle(stageWidth / 2, 0, stageWidth, 40, {isStatic: true});
            var ground_right = Bodies.rectangle(stageWidth, stageHeight / 2, 40, stageHeight, {isStatic: true});
            var ground_bottom = Bodies.rectangle(stageWidth / 2, stageHeight, stageWidth, 40, {isStatic: true});
            var ground_left = Bodies.rectangle(0, stageHeight / 2, 40, stageHeight, {isStatic: true});
            return [ground_top, ground_right, ground_bottom, ground_left];
        }
    </script>
</head>
<body>
</body>
</html>

1、兩個剛體(矩形)通過約束組合成一個混合體(蹺蹺板),可以看到中間約束點的繩子,stiffness(剛度)越大,物體越不容易拉伸。

2、掉下來的屬於符合材料,可以參考《Matter-JS Composites 混合材料詳解》中的 "stack 堆疊" 部分。

3、Matter.Constraint  約束將會是很常見的操作,也可以參考《Matter-JS Composites 混合材料 · 上》中的 "chain 鏈" 部分。

Constraint.create

1、Constraint.create():建立約束,其引數是一個 json 物件, 主要引數有:bodyA,pointA,bodyB,pointB,length,stiffness 等。

2、bodyA 和 bodyB 分別為被約束的兩個物體。

3、pointA 和 pointB 分別表示 bodyA、bodyB 的約束點位置。pointA、pointB 的值為向量,預設為 {x:0,Y:)},在物體的中心位置。以物體中心為原點進行向量相減(三角形法則)

4、Constraint.create() 函式原始碼地址:http://brm.io/matter-js/docs/files/src_constraint_Constraint.js.html#l28

現在來實現上圖效果:

1、3個鎖鏈,左邊為矩形堆疊組成,中間為圓形堆疊組成,右邊為圓角矩形堆疊組成。

2、每條鎖鏈中的物體相互應該不發生碰撞,則設定 collisionFilter: {group: group} group 為負數且相等即可。

3、鎖鏈與鎖鏈相互發生碰撞,則 group 不相等即可。

4、每條鎖鏈頂部不是單純的設定 {isStatic: true} 進行固定,而是使用 Constraint.create 建立約束而成,因為頂部可以看到約束的繩子。

親測Matter.js 的0.14.2版本效果如上,老版本可能會有區別,實現程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>Matter-JS</title>
    <!--matter-js cdnjs地址-->
    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.js"></script>-->
    <script src="../js/matter_0.14.2.js"></script>
    <script type="text/javascript">
        var stageWidth = 800;//舞臺寬度
        var stageHeight = 500;//舞臺高度
        var Engine = Matter.Engine;//引擎
        var Render = Matter.Render;//渲染器
        var World = Matter.World;//世界
        var Constraint = Matter.Constraint;//約束
        var MouseConstraint = Matter.MouseConstraint;//滑鼠控制
        var Bodies = Matter.Bodies;//內建常見剛體
        var Composites = Matter.Composites;//符合材料
        var Composite = Matter.Composite;//混合體
        var Common = Matter.Common;//公用模組
        var Body = Matter.Body;//剛體

        window.onload = function () {
            matterJS();
        }

        /**Matter-JS*/
        function matterJS() {
            var engine = Engine.create();//建立引擎
            var render = Render.create({//建立渲染器
                engine: engine,//渲染建立好的引擎
                /**渲染頁面的body元素,即會在body標籤自動新建<canvas>畫布,同理如果element的值是某個div元素-
                 * 則會在div下自動新建canvas,canvas的尺寸是options中的width、height
                 * */
                element: document.body,
                options: {
                    width: stageWidth,//畫布的寬度
                    height: stageHeight,//畫布的高度
                    wireframes: true,//線框模式,預設false不使用線框模式
                    showAngleIndicator: true,//是否顯示角度,預設false
                    showVelocity: true,//是否顯示速度,預設false
                    showCollisions: true,//是否顯示碰撞點,預設false
                }
            });
            Engine.run(engine);//執行引擎
            Render.run(render);//執行渲染器
            /**設定滑鼠控制*/
            var mouseConstraint = MouseConstraint.create(engine, {});

            /**---------第------1-------條-------鏈(矩形)*/
            /**Body.nextGroup(isNonColliding):下一個組ip值,
             * isNonColliding:表示非碰撞,為true時,第一次呼叫 nextGroup 時返回 -1,每呼叫一次則遞減1
             * isNonColliding=false時,第一次呼叫 nextGroup 時返回 1,每呼叫一次則遞加1*/
            var group = Body.nextGroup(true);
            /**參加豎向的堆疊材料*/
            var stack_1 = Composites.stack(stageWidth / 4, 40, 1, 9, 0, 0, function (x, y) {
                /**使用矩形組成鏈條,鏈條中所有物體屬於同一個group,且為負數,這樣相互就不會發生碰撞*/
                return Bodies.rectangle(x, y, 20, 40, {collisionFilter: {group: group}});
            });
            /**使用複合材料鏈將堆疊體連結起來
             * 注意:堆疊體只是將多個物體堆疊在一起,但是相互仍然是隔離的,相互沒有約束的
             * 而chain鏈是進行constraint(約束)的,所以鏈條是一個有約束的整體
             * stiffness:設定鏈條的剛度,相當於設定鏈條的彈性;length:鏈條的長度*/
            Composites.chain(stack_1, 0, 0.5, 0, -0.5, {stiffness: 0.7, length: 2});
            /**將鎖鏈固定在舞臺上:
             * 思路一是將鏈條上的第一個物體設定 {isStatic: true} 屬性即可固定住整個鏈條
             * 思路二是將第一個物體建立約束Constraint.create。這裡對3個物體都是要這種方式,從而抽取出公共方法
             * */
            var fixed_1 = constraintChain(stack_1);
            /**為鏈條上的每個物體設定一個初始偏移值,這樣一開始就會動起來*/
            for (var i = 0; i < stack_1.bodies.length; i++) {
                Body.translate(stack_1.bodies[i], {x: -i * 20, y: -50});
            }

            /**---------第------2-------條-------鏈(圓形)*/
            /**true表示非碰撞體,鏈條內部不碰撞,但是鏈條與鏈條應該發生碰撞,所以重新取值*/
            group = Body.nextGroup(true);
            var stack_2 = Composites.stack(stageWidth / 2, 40, 1, 9, 0, 0, function (x, y) {
                return Bodies.circle(x, y, 20, {collisionFilter: {group: group}});
            });
            Composites.chain(stack_2, 0, 0.5, 0, -0.5, {stiffness: 0.9, length: 2});
            var fixed_2 = constraintChain(stack_2);
            for (var i = 0; i < stack_2.bodies.length; i++) {
                Body.translate(stack_2.bodies[i], {x: -i * 20, y: -80});
            }

            /**---------第------3-------條-------鏈(圓角矩形)*/
            group = Body.nextGroup(true);
            var stack_3 = Composites.stack(stageWidth * (3 / 4), 30, 1, 9, 0, 0, function (x, y) {
                return Bodies.rectangle(x, y, 20, 60, {collisionFilter: {group: group}, chamfer: 10});
            });
            /**length=0時,只能看到約束的點,卻看不到約束的繩子*/
            Composites.chain(stack_3, 0, 0.3, 0, -0.3, {stiffness: 0.9, length: 0});
            var fixed_3 = constraintChain(stack_3);

            /**將物體以及滑鼠控制新增到世界中*/
            World.add(engine.world, [mouseConstraint, stack_1, stack_2, stack_3]);
            /**將鏈條的約束新增到世界中*/
            World.add(engine.world, [fixed_1, fixed_2, fixed_3]);
            /**為世界4周新增4面牆*/
            World.add(engine.world, create4Wall(Bodies));
        }

        /**為鏈條新增約束*/
        function constraintChain(stack) {
            /**將stack(鏈條)約束(固定)在 pointA的位置*/
            var fixed = Constraint.create({
                /**stack.bodies返回的Body陣列,.position返回物體當前位置的向量Vector,Default: { x: 0, y: 0 }*/
                pointA: {x: stack.bodies[0].position.x, y: stack.bodies[0].position.y - 30},
                bodyB: stack.bodies[0],
                pointB: {x: 0, y: -20},
                length: 20,//約束點的長度
                stiffness: 0.3//剛度值( 0,1],值越大,物體剛度越強,越不容易拉伸
            });
            return fixed;
        }

        /**建立4面牆-強制物體在牆內運動*/
        function create4Wall(Bodies) {
            var ground_top = Bodies.rectangle(stageWidth / 2, 5, stageWidth, 40, {isStatic: true});
            var ground_right = Bodies.rectangle(stageWidth, stageHeight / 2, 40, stageHeight, {isStatic: true});
            var ground_bottom = Bodies.rectangle(stageWidth / 2, stageHeight - 5, stageWidth, 40, {isStatic: true});
            var ground_left = Bodies.rectangle(10, stageHeight / 2, 40, stageHeight, {isStatic: true});
            return [ground_top, ground_right, ground_bottom, ground_left];
        }
    </script>
</head>
<body>
</body>
</html>