RayCast——光线投射

演示

Any All Closest

源代码

window.onload = function () {
                            var canvas, ctx, w, h, world, boxBody, planeBody;
    var scaleX = 50, scaleY = -50;
    var start = [0,0];
    var end = [0,0];

    var result = new p2.RaycastResult();
    var hitPoint = p2.vec2.create();

    var rayClosest = new p2.Ray({
        mode: p2.Ray.CLOSEST
    });
    var rayAll = new p2.Ray({
        mode: p2.Ray.ALL,
        callback: function(result){
            drawRayResult(result, rayAll);
        }
    });
    var rayAny = new p2.Ray({
        mode: p2.Ray.ANY
    });
    var raycastOptions = {};

    init();
    requestAnimationFrame(animate);

    function init(){
        // 初始化画布
        canvas = document.getElementById("renderCanvas");
        w = canvas.width;
        h = canvas.height;
        ctx = canvas.getContext("2d");
        ctx.lineWidth = 0.02;
        ctx.fillStyle = 'white';

        // 初始化物理引擎
        world = new p2.World({
            gravity: [0, 0]
        });

        // 添加一个盒子
        boxShape = new p2.Box({ width: 2, height: 1 });
        boxBody = new p2.Body({
            mass:1,
            position:[0,2],
            angularVelocity:1,
            angularDamping: 0
        });
        boxBody.addShape(boxShape);
        world.addBody(boxBody);

        // 添加一个圆
        circleShape = new p2.Circle({ radius: 0.5 });
        circleBody = new p2.Body({
            mass:1,
            position:[0,-1],
            angularVelocity:1
        });
        circleBody.addShape(circleShape);
        world.addBody(circleBody);

        // 添加一个胶囊
        capsuleShape = new p2.Capsule({ length: 1, radius: 0.5 });
        capsuleBody = new p2.Body({
            mass:1,
            position:[-1.5,0],
            angularVelocity:1,
            angularDamping: 0
        });
        capsuleBody.addShape(capsuleShape);
        world.addBody(capsuleBody);

        // 添加一个平面
        planeShape = new p2.Plane();
        planeBody = new p2.Body({
            position: [3,0],
            angle: Math.PI / 3
        });
        planeBody.addShape(planeShape);
        world.addBody(planeBody);

        // 添加一个五边形
        var vertices = [];
        var size = 1;
        for(var i=0, N=5; i<N; i++){
            var a=2*Math.PI / N * i;
            var vertex = [size*0.5*Math.cos(a), size*0.5*Math.sin(a)]; // 注意:顶点要以逆时针顺序进行定义
            vertices.push(vertex);
        }
        convexShape = new p2.Convex({ vertices: vertices });
        convexBody = new p2.Body({
            mass: 1,
            position: [1,0],
            angle: Math.PI / 3,
            angularVelocity: 1
        });
        convexBody.addShape(convexShape);
        world.addBody(convexBody);
    }

    function drawbox(){
        ctx.beginPath();
        var x = boxBody.interpolatedPosition[0],
            y = boxBody.interpolatedPosition[1];
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(boxBody.interpolatedAngle); 
        ctx.rect(-boxShape.width/2, -boxShape.height/2, boxShape.width, boxShape.height);
        ctx.stroke();
        ctx.restore();
    }

    function drawPlane(){
        ctx.beginPath();
        var x = planeBody.interpolatedPosition[0],
            y = planeBody.interpolatedPosition[1];
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(planeBody.interpolatedAngle);
        ctx.moveTo(-100, 0);
        ctx.lineTo(100, 0);
        ctx.stroke();
        ctx.restore();
    }

    function drawCircle(){
        ctx.beginPath();
        var x = circleBody.interpolatedPosition[0],
            y = circleBody.interpolatedPosition[1];
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(circleBody.interpolatedAngle);
        ctx.arc(0,0,circleShape.radius,0,2*Math.PI);
        ctx.stroke();
        ctx.restore();
    }

    function drawConvex(){
        ctx.beginPath();
        var x = convexBody.interpolatedPosition[0],
            y = convexBody.interpolatedPosition[1];
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(convexBody.interpolatedAngle);
        ctx.moveTo(convexShape.vertices[0][0], convexShape.vertices[0][1]);
        for (var i = 1; i < convexShape.vertices.length+1; i++) {
            ctx.lineTo(convexShape.vertices[i%convexShape.vertices.length][0], 
                         convexShape.vertices[i%convexShape.vertices.length][1]);
        }
        ctx.stroke();
        ctx.restore();
    }

    function drawCapsule(){
        var x = capsuleBody.interpolatedPosition[0],
            y = capsuleBody.interpolatedPosition[1];
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(capsuleBody.interpolatedAngle);
        var radius = capsuleShape.radius;
        var len = capsuleShape.length;

        // 绘制胶囊两侧的圆
        ctx.beginPath();
        var c = Math.cos(capsuleBody.interpolatedAngle);
        var s = Math.sin(capsuleBody.interpolatedAngle);
        ctx.arc(-len/2, 0, capsuleShape.radius, 0, 2*Math.PI);
        ctx.arc( len/2, 0, capsuleShape.radius, -Math.PI, Math.PI);
        ctx.fill();
        ctx.stroke();

        // 绘制胶囊中部的矩形
        ctx.beginPath();
        ctx.moveTo(-len/2, -radius);
        ctx.lineTo( len/2, -radius);
        ctx.lineTo( len/2, radius);
        ctx.lineTo(-len/2, radius);
        ctx.fill();

        // 绘制连线
        ctx.beginPath();
        ctx.moveTo(-len/2, -radius);
        ctx.lineTo( len/2, -radius);
        ctx.stroke();
        ctx.beginPath();
        ctx.lineTo( len/2, radius);
        ctx.lineTo(-len/2, radius);
        ctx.stroke();
        ctx.restore();
    }

    function drawRayResult(result, ray){
        result.getHitPoint(hitPoint, ray);

        // 绘制碰撞点
        if(result.hasHit()){
            ctx.beginPath();
            ctx.arc(hitPoint[0],hitPoint[1],0.1,0,2*Math.PI);
            ctx.stroke();
        }

        // 绘制碰撞法线
        ctx.beginPath();
        ctx.moveTo(hitPoint[0], hitPoint[1]);
        ctx.lineTo(
          hitPoint[0] + result.normal[0],
          hitPoint[1] + result.normal[1]
        );
        ctx.stroke();
    };

    function drawRay(start, end){
        // 绘制射线
        ctx.beginPath();
        ctx.moveTo(start[0], start[1]);
        ctx.lineTo(end[0], end[1]);
        ctx.stroke();
    }

    function drawRays(time){
        start[0] = -3;
        start[1] = Math.sin(time / 2000) * 4;
        end[0] = 5;
        end[1] = Math.sin(time / 2000);

        // Closest
        p2.vec2.copy(rayClosest.from, start);
        p2.vec2.copy(rayClosest.to, end);
        rayClosest.update();
        ctx.strokeStyle = 'blue';
        drawRay(start, end);
        result.reset();
        world.raycast(result, rayClosest);
        drawRayResult(result, rayClosest);
        start[1] += 0.5;
        end[1] += 0.5;

        // All
        p2.vec2.copy(rayAll.from, start);
        p2.vec2.copy(rayAll.to, end);
        rayAll.update();
        ctx.strokeStyle = 'green';
        drawRay(start, end);
        result.reset();
        world.raycast(result, rayAll); // drawRayResult
        start[1] += 0.5;
        end[1] += 0.5;

        // Any
        p2.vec2.copy(rayAny.from, start);
        p2.vec2.copy(rayAny.to, end);
        rayAny.update();
        ctx.strokeStyle = 'red';
        drawRay(start, end);
        result.reset();
        world.raycast(result, rayAny);
        drawRayResult(result, rayAny);
        ctx.strokeStyle = 'black';
    }

    function render(time){
        // 清除画布
        ctx.clearRect(0,0,w,h);

        // 将画布放大,中心移至画布中央
        ctx.save();
        ctx.translate(w/2, h/2);
        ctx.scale(scaleX, scaleY);

        // 绘制所有形状
        drawbox();
        drawPlane();
        drawCircle();
        drawCapsule();
        drawConvex();
        drawRays(time);

        ctx.restore();
    }

    var lastTime, timeStep = 1 / 60, maxSubSteps = 5;

    // 动画循环
    function animate(time) {
        requestAnimationFrame(animate);

        var dt = lastTime ? (time - lastTime) / 1000 : 0;
        dt = Math.min(1 / 10, dt);
        lastTime = time;

        // 进行物理模拟
        world.step(timeStep, dt, maxSubSteps);

        // 绘制场景
        render(time);
    }
}

发布时间:2017/1/2 下午10:18:42  阅读次数:4885

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号