当前位置: 首页 > 工具软件 > Box2D > 使用案例 >

Box2d interact test

邢永安
2023-12-01
<html>
    <head>
        <title>interact Box2D demo</title>
        <script type="text/javascript" src="../../build/Box2D_v2.2.1_min.js"></script>
        <script type="text/javascript" src="../../helpers/MathUtil.js"></script>
        <script type="text/javascript" src="../../helpers/embox2d-helpers.js"></script>
        <script type="text/javascript" src="../../helpers/embox2d-html5canvas-debugDraw.js"></script>

        <script type="text/javascript">  
        var e_shapeBit = 0x0001;
        var e_jointBit = 0x0002;
        var e_aabbBit = 0x0004;
        var e_pairBit = 0x0008;
        var e_centerOfMassBit = 0x0010;
        var myDebugDraw;
        var mouseJoint = null;        
        var run = true;
        var PTM = 32;
        var world = null;
        var myQueryCallback;
        var mouseDown = false;
        var mouseJointGroundBody = null;

            var Box2D;
            var canvas;
            var context;
            var canvasOffset = {x: 0, y: 0};
            var mousePosPixel = {x: 0, y: 0};
            var mousePosWorld = {x: 0, y: 0};
            var creating = true;
            var slicing = false;

            if (!Box2D) Box2D = (typeof Box2D !== 'undefined' ? Box2D : null) || Module;
            window.onload = function() {
                using(Box2D, "b2.+")
                init();
                // changeTest();
                animate();
            };

            function init() {
                myDebugDraw = getCanvasDebugDraw();            
                myDebugDraw.SetFlags(e_shapeBit);
                world = new Box2D.b2World( new Box2D.b2Vec2(0.0, -10.0) );
                world.SetDebugDraw(myDebugDraw);
                mouseJointGroundBody = world.CreateBody( new Box2D.b2BodyDef() );
                setup();

                canvas = document.getElementById("canvas");
                context = canvas.getContext( '2d' );

                canvasOffset.x = canvas.width/2;
                canvasOffset.y = canvas.height/2;

                // canvas.addEventListener('click', function(evt) {
                //     onMouseClick(canvas,evt);
                // }, false);

                canvas.addEventListener('mousemove', function(evt) {
                    onMouseMove(canvas,evt);
                }, false);

                canvas.addEventListener('mousedown', function(evt) {
                    onMouseDown(canvas,evt);
                }, false);

                canvas.addEventListener('mouseup', function(evt) {
                    onMouseUp(canvas,evt);
                }, false);

                myDebugDraw = getCanvasDebugDraw();            
                myDebugDraw.SetFlags(e_shapeBit);

                myQueryCallback = new Box2D.JSQueryCallback();

                myQueryCallback.ReportFixture = function(fixturePtr) {
                    var fixture = Box2D.wrapPointer( fixturePtr, b2Fixture );
                    if ( fixture.GetBody().GetType() != Box2D.b2_dynamicBody ) //mouse cannot drag static bodies around
                        return true;
                    if ( ! fixture.TestPoint( this.m_point ) )
                        return true;
                    this.m_fixture = fixture;
                    return false;
                };
            }

            function onMouseClick(canvas, evt) {
                mouseDown = false;
                if ( mouseJoint != null ) {
                    world.DestroyJoint(mouseJoint);
                    mouseJoint = null;
                    return;
                }
                updateMousePos(canvas, evt);
                // console.log(mousePosWorld.x + "," + mousePosWorld.y);

                {
                    var cshape = new b2CircleShape();
                    cshape.set_m_radius(0.5);

                    //falling shapes
                    var ZERO = new b2Vec2(0, 0);
                    var temp = new b2Vec2(mousePosWorld.x, mousePosWorld.y);
                    var bd = new b2BodyDef();
                    // bd.set_type(b2_dynamicBody);
                    bd.set_type(Module.b2_dynamicBody);
                    bd.set_position(temp);
                    var body = world.CreateBody(bd);
                    var randomValue = Math.random();
                    if ( randomValue < 0.2 )
                        body.CreateFixture(cshape, 1.0);
                    else
                        body.CreateFixture(createRandomPolygonShape(0.5), 1.0);
                    // temp.Set(16*(Math.random()-0.5), 4.0 + 2.5*i);
                    // body.SetTransform(temp, 0.0);
                    body.SetLinearVelocity(ZERO);
                    body.SetAwake(1);
                    body.SetActive(1);
                }
            }

            function startMouseJoint() {

                if ( mouseJoint != null )
                    return;

                // Make a small box.
                var aabb = new Box2D.b2AABB();
                var d = 0.001;            
                aabb.set_lowerBound(new b2Vec2(mousePosWorld.x - d, mousePosWorld.y - d));
                aabb.set_upperBound(new b2Vec2(mousePosWorld.x + d, mousePosWorld.y + d));

                // Query the world for overlapping shapes.            
                myQueryCallback.m_fixture = null;
                myQueryCallback.m_point = new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y);
                world.QueryAABB(myQueryCallback, aabb);

                if (myQueryCallback.m_fixture)
                {
                    var body = myQueryCallback.m_fixture.GetBody();
                    var md = new Box2D.b2MouseJointDef();
                    md.set_bodyA(mouseJointGroundBody);
                    md.set_bodyB(body);
                    md.set_target( new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y) );
                    md.set_maxForce( 1000 * body.GetMass() );
                    md.set_collideConnected(true);

                    mouseJoint = Box2D.castObject( world.CreateJoint(md), Box2D.b2MouseJoint );
                    body.SetAwake(true);
                }
            }

            function verticesToBox2D(vertices) {
                var ret = [];
                for (var i = 0; i < vertices.length; i++) {
                    var p = vertices[i];
                    ret.push( new b2Vec2(p.x, p.y) );
                }
                return ret;
            }

            var mouseDownLst = [];
            var curObj = null;
            var hoveringObj = null;
            var hoverIndex;
            var laserSegment;
            var laserLocked = false;
            var slicingPoints = [];
            var laserColor = "#aa00aa";
            var affectedByLaser = [];
            var entryPoints = [];
            var deletingBodies = [];

            function onMouseDown(canvas, evt) {
                updateMousePos(canvas, evt);
                if ( !mouseDown )
                    startMouseJoint();
                mouseDown = true;
                if ( mouseJoint != null )
                    return;

                if (slicing) {
                    if (laserLocked) {
                        laserSegment = null;
                        slicingPoints = [];
                        debugIds = [];
                        // affectedByLaser = [];
                        entryPoints = [];
                    }
                    if (laserSegment == null) {
                        laserLocked = false; 
                        laserSegment = new Box2D.b2EdgeShape();
                        laserSegment.p1=new Vertex(mousePosWorld.x, mousePosWorld.y);
                    } else {
                        laserSegment.p2=new Vertex(mousePosWorld.x, mousePosWorld.y);
                        laserSegment.Set(new b2Vec2(laserSegment.p1.x, laserSegment.p1.y), new b2Vec2(laserSegment.p2.x, laserSegment.p2.y));
                        laserLocked = true;

                        var laserFired = new Box2D.JSRayCastCallback();
                        var idd = 0;
                        laserFired.ReportFixture = function(fixture, point, normal, fraction) {
                            var ptr = Box2D.wrapPointer( point, Box2D.b2Vec2 );
                            var fixturePtr = Box2D.wrapPointer(fixture, Box2D.b2Fixture);
                            // console.log(ptr.get_x() + ", " + ptr.get_y() );
                            slicingPoints.push(copyVec2(ptr));

                            var p = new Vertex(ptr.get_x(), ptr.get_y());
                            p.id = idd;
                            idd++;
                            var affectedBody = fixturePtr.GetBody();
                            if (affectedBody.GetType() != Module.b2_dynamicBody) {
                                return;
                            }
                            console.log("laserFired.ReportFixture");
                            var bodyIndex = deletingBodies.indexOf(affectedBody);
                            if (bodyIndex < 0) {
                                deletingBodies.push(affectedBody);
                            }
                            var fixtureIndex = affectedByLaser.indexOf(fixturePtr);
                            if (fixtureIndex < 0) {
                                affectedByLaser.push(fixturePtr);
                                entryPoints.push( p );
                            } else {
                                var points = getPointsByPolygonShape(fixturePtr);
                                var p1 = p;
                                var p2 = entryPoints[fixtureIndex];
                                if (affectedBody.sliceDots == null) {
                                    affectedBody.sliceDots = [];
                                }
                                affectedBody.sliceDots.push(p1, p2);
                                // if (affectedBody.sliceDots.indexOf(p1) < 0) {
                                //     affectedBody.sliceDots.push(p1);
                                // }
                                // if (affectedBody.sliceDots.indexOf(p2) < 0) {
                                //     affectedBody.sliceDots.push(p2);
                                // }
                            }
                            return 1;
                        }
                        world.RayCast(laserFired, laserSegment.get_m_vertex1(), laserSegment.get_m_vertex2());
                        world.RayCast(laserFired, laserSegment.get_m_vertex2(), laserSegment.get_m_vertex1());
                    }
                } else if (creating) {
                    if (hoveringObj != null) {
                        var verts = [];
                        for (var i = hoverIndex; i < mouseDownLst.length; i++) {
                            verts.push( new b2Vec2( mouseDownLst[i].x, mouseDownLst[i].y ) );
                        }
                        createPolygonBody(verts);
                        mouseDownLst = [];
                        hoveringObj = null;
                    } else {
                        if (curObj != null) {
                            mouseDownLst.push(curObj);
                            curObj = null;  
                        }
                    }
                }
            }

            var debugIds = [];
            function toIdString(arr) {
                var lst = [];
                for (var i = 0; i < arr.length; i++) {
                    lst.push(arr[i].id);
                }
                return lst.toString();
            }

            function createPolygonBody(verts) {
                var first = copyVec2(verts[0]);
                var arr2 = [];
                for (var i = 0; i < verts.length; i++) {
                    verts[i]= new b2Vec2( verts[i].get_x() - first.get_x(), verts[i].get_y() - first.get_y() );
                    arr2.push( new Vertex( verts[i].get_x(), verts[i].get_y() ) );
                }

                var bd = new b2BodyDef();
                bd.set_type(Module.b2_dynamicBody);
                bd.set_position(first);
                var body = world.CreateBody(bd);

                var ploygons = [];
                var arr3 = copyArr(arr2);
                concavToConvex(arr2, ploygons);
                for ( var i = 0; i < ploygons.length; i++) {
                    var ploygon = ploygons[i];
                    var isClockwise = isClockwiseSquence(ploygon);
                    //because we flipped coordinate for one time before, context.scale(1,-1); 
                    isClockwise = !isClockwise;
                    if (isClockwise) {
                        ploygon.reverse();
                    }
                    var shape = createPolygonShape(verticesToBox2D(ploygon) );
                    body.CreateFixture(shape, 1.0);
                }
                body.userData = arr3;
            }

            function createSlicingBody(verts) {
                var first = copyVec2(verts[0]);
                for (var i = 0; i < verts.length; i++) {
                    verts[i]= new b2Vec2( verts[i].get_x() - first.get_x(), verts[i].get_y() - first.get_y() );
                }
                var shape = createPolygonShape( verts );
                var bd = new b2BodyDef();
                bd.set_type(Module.b2_dynamicBody);
                bd.set_position(first);
                var body = world.CreateBody(bd);
                body.CreateFixture(shape, 1.0);
                return body;
            }

            function onMouseMove(canvas, evt) {
                if (mouseJoint != null) {
                    prevMousePosPixel = mousePosPixel;
                    updateMousePos(canvas, evt);
                    if ( mouseDown ) {
                        mouseJoint.SetTarget( new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y) );
                    }
                    laserSegment = null;
                    slicingPoints = [];
                    mouseDownLst = [];
                } else {
                    updateMousePos(canvas, evt);
                    if (slicing && !laserLocked && laserSegment != null) {
                        laserSegment.p2=new Vertex(mousePosWorld.x, mousePosWorld.y);
                    } else if (creating) {
                        curObj = {};
                        curObj.x = mousePosWorld.x;
                        curObj.y = mousePosWorld.y;
                        hoveringObj = null;
                        if (mouseDownLst.length < 3) return;
                        for (var i = 0; i < mouseDownLst.length - 2; i++) {
                            var p = mouseDownLst[i];
                            var dx = p.x - curObj.x;
                            var dy = p.y - curObj.y;
                            if (dx * dx + dy * dy <= 100 / (PTM*PTM)) {
                                hoveringObj = p;
                                hoverIndex = i;
                                break;
                            }
                        } 
                    }
                }
            }

            function onMouseUp(canvas, evt) {
                mouseDown = false;
                if ( mouseJoint != null ) {
                    world.DestroyJoint(mouseJoint);
                    mouseJoint = null;
                    return;
                }
            }

            function updateMousePos(canvas, evt) {
                var rect = canvas.getBoundingClientRect();
                mousePosPixel = {
                    x: evt.clientX - rect.left,
                    y: canvas.height - (evt.clientY - rect.top)
                };
                mousePosWorld = getWorldPointFromPixelPoint(mousePosPixel);
            }

            function getWorldPointFromPixelPoint(pixelPoint) {
                return {                
                    x: (pixelPoint.x - canvasOffset.x)/PTM,
                    y: (pixelPoint.y - (canvas.height - canvasOffset.y))/PTM
                };
            }

            function setup() {
                var bd_ground = new b2BodyDef();
                var groundBody = world.CreateBody(bd_ground);
                //ground edges
                var shape0 = new b2EdgeShape();
                shape0.Set(new b2Vec2(-40.0, -6.0), new b2Vec2(40.0, -6.0));
                groundBody.CreateFixture(shape0, 0.0);
                shape0.Set(new b2Vec2(-9.0, -6.0), new b2Vec2(-9.0, -4.0));
                groundBody.CreateFixture(shape0, 0.0);
                shape0.Set(new b2Vec2(9.0, -6.0), new b2Vec2(9.0, -4.0));
                groundBody.CreateFixture(shape0, 0.0);
            }

            function step(timestamp) {
                world.Step(1/60, 3, 2);
                draw();
            }

            function draw() {
                //black background
                context.fillStyle = 'rgb(0,0,0)';
                context.fillRect( 0, 0, canvas.width, canvas.height );

                context.save();            
                    context.translate(canvasOffset.x, canvasOffset.y);
                    context.scale(1,-1);                
                    context.scale(PTM,PTM);
                    context.lineWidth /= PTM;

                    drawAxes(context);

                    context.fillStyle = 'rgb(255,255,0)';
                    world.DrawDebugData();
                    if (slicing) {
                        if (laserSegment != null && laserSegment.p2 != null) {
                            context.moveTo(laserSegment.p1.x, laserSegment.p1.y);
                            context.lineTo(laserSegment.p2.x, laserSegment.p2.y);
                            context.strokeStyle = laserColor;
                            context.stroke();
                        }
                        for (var i = 0; i < slicingPoints.length; i++) {
                            var p = slicingPoints[i];
                            context.beginPath();
                            context.arc(p.get_x() , p.get_y() , 5/PTM, 0, Math.PI * 2, true);
                            context.strokeStyle = "#00aa00";
                            context.stroke();
                            context.closePath();
                        }
                        var addBodies = [];
                        for (var i = 0; i < deletingBodies.length; i++) {
                            var body = deletingBodies[i];
                            var vertices = getPointsByPolygonBody(body);
                            // var a = 1;
                            // debugIds = body.sliceDots;
                            if (vertices != null && vertices.length > 2) {
                                var slicingDots = filterOutInnerPoints(vertices, body.sliceDots);
                                sliceConcavPolygon(vertices, slicingDots, 0, addBodies);
                            }
                            world.DestroyBody(body);
                        }
                        for (var i = 0; i < addBodies.length; i++) {
                            // createSlicingBody(verticesToBox2D(addBodies[i]));
                            createPolygonBody(verticesToBox2D(addBodies[i]));
                        }
                        affectedByLaser = [];
                        deletingBodies = [];

                        context.scale(1,-1);                
                        context.scale(1/PTM,1/PTM);
                        for (var i = 0; i < debugIds.length; i++) {
                            var vertex = debugIds[i];
                            context.font = "12px Courier New";
                            context.fillStyle = "white";
                            context.fillText(vertex.id, vertex.x*PTM, -vertex.y*PTM);
                        }
                        context.scale(1,-1);                
                        context.scale(PTM,PTM);

                    } else if (creating) {
                        if (mouseDownLst.length > 0) {
                            var arr2 = mouseDownLst;
                            if (curObj != null) {
                                arr2 = mouseDownLst.concat([curObj]);
                            }
                            for (var i = 0; i < arr2.length; i++) {
                                var p1 = arr2[i];
                                var p2 = i < arr2.length - 1 ? arr2[i+1] : null;
                                if (p2 != null) {
                                    context.beginPath();
                                    context.arc(p1.x, p1.y, 5/PTM, 0, Math.PI * 2, true);
                                    context.closePath();
                                    context.strokeStyle = "#00aa00";
                                    context.stroke();

                                    context.beginPath();
                                    context.moveTo(p1.x,p1.y);
                                    context.lineTo(p2.x,p2.y);
                                    context.stroke();
                                }
                             }
                             if (hoveringObj != null) {
                                context.beginPath();
                                context.arc(hoveringObj.x, hoveringObj.y, 10/PTM, 0, Math.PI * 2, true);
                                context.closePath();
                                context.strokeStyle = "orange";
                                context.stroke();
                             }
                        }
                    }

                    if ( mouseJoint != null ) {
                        //mouse joint is not drawn with regular joints in debug draw
                        var p1 = mouseJoint.GetAnchorB();
                        var p2 = mouseJoint.GetTarget();
                        context.strokeStyle = 'rgb(204,204,204)';
                        context.beginPath();
                        context.moveTo(p1.get_x(),p1.get_y());
                        context.lineTo(p2.get_x(),p2.get_y());
                        context.stroke();
                    }
                context.restore();
            }

            window.requestAnimFrame = (function(){
                return  window.requestAnimationFrame       || 
                        window.webkitRequestAnimationFrame || 
                        window.mozRequestAnimationFrame    || 
                        window.oRequestAnimationFrame      || 
                        window.msRequestAnimationFrame     || 
                        function( callback ){
                          window.setTimeout(callback, 1000 / 60);
                        };
            })();

            function animate() {
                if ( run )
                    requestAnimFrame( animate );
                step();
            }

            function pause() {
                run = !run;
                if (run)
                    animate();
            }

            function checkCreating() {
                creating = document.getElementById('creatingCheck').checked;
                if (creating) {
                    slicing = false;
                    laserSegment = null;
                    slicingPoints = [];
                    document.getElementById('slicingCheck').checked = false;
                } else {
                    mouseDownLst = [];
                }
            }

            function checkSlicing() {
                slicing = document.getElementById('slicingCheck').checked;
                if (slicing) {
                    creating = false;
                    mouseDownLst = [];
                    document.getElementById('creatingCheck').checked = false;
                } else {
                    laserSegment = null;
                    slicingPoints = [];
                }
            }

        </script>

    </head>
    <body>
        <div style="text-align:center">
            <h2>Jerome.huang Box2D demo</h2>
            This demo uses JavaScript generated from the Box2D C++ source code by the <a href="https://github.com/kripken/emscripten/wiki">emscripten</a> compiler.<br>
            See <a href="https://github.com/kripken/box2d.js">https://github.com/kripken/box2d.js</a> for more details.<br>
            <br>            
            <!--
                If you change the size of the canvas, you'll also need to change 
                the value of 'viewCenterPixel' in embox2d-html5canvas-testbed.js
            -->
            <div style="margin:auto;width:640px;padding:2px;border:1px solid #888;text-align:left">            

                <!--<canvas id="canvas" width="480" height="320" tabindex='1'></canvas>-->
                <canvas id="canvas" width="640" height="480" tabindex='1'></canvas>
                <br>

                Operation flags:<br>
                <input id="creatingCheck" type="checkbox" onclick="checkCreating();" checked="true">creating<br>
                <input id="slicingCheck" type="checkbox" onclick="checkSlicing();">slicing laser<br>
                <br>
                </div>
            </div>        
        </div>
    </body>
</html>

 类似资料:

相关阅读

相关文章

相关问答