CocosCreator射线检测之范围检测

国际惯例先来上一组效果:

image
image

这样的操作可以提前预测物体是否会碰撞,并且描绘出射线路径

场景配置:

image

检测代码如下:


const AIM_LINE_MAX_LENGTH = 2440;

const { ccclass, property } = cc._decorator;

@ccclass

export default class Main extends cc.Component {

    @property({ type: cc.Graphics, tooltip: '瞄准线作图' })

    graphic_line: cc.Graphics = null;

    @property({ type: cc.Graphics, tooltip: '瞄准线作图_1' })

    graphic_line1: cc.Graphics = null;

    @property({ type: cc.Node, tooltip: '小球节点' })

    ballNode: cc.Node = null;

    @property({ type: cc.Node, tooltip: 'a1' })

    a1: cc.Node = null;

    @property({ type: cc.Node, tooltip: 'a2' })

    a2: cc.Node = null;

    @property({ type: cc.Node, tooltip: 'a3' })

    a3: cc.Node = null;

    @property({ type: cc.Node, tooltip: 'a4' })

    a4: cc.Node = null;

    startLocation :cc.Vec2;

    location :cc.Vec2;

    ballNodePos: cc.Vec2;

    a1WorldPos: cc.Vec2;

    a2WorldPos: cc.Vec2;

    a3WorldPos: cc.Vec2;

    a4WorldPos: cc.Vec2;

    private _isHaveGold: boolean = false;

    private _cur_length: number = 0;

    private _cur_length1: number = 0;

    onLoad() {

        cc.director.getPhysicsManager().enabled = true;

        // cc.director.getPhysicsManager().debugDrawFlags = 1;

        this.graphic_line.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);

        this.graphic_line.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);

        this.graphic_line.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);

        this.graphic_line.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);

        cc.log(this.ballNode.getPosition())

        this.ballNodePos = this.node.convertToWorldSpace(this.ballNode.getPosition())

        this.a1WorldPos = this.node.convertToWorldSpace(this.a1.getPosition())

        this.a2WorldPos = this.node.convertToWorldSpace(this.a2.getPosition())

        this.a3WorldPos = this.node.convertToWorldSpace(this.a3.getPosition())

        this.a4WorldPos = this.node.convertToWorldSpace(this.a4.getPosition())

        cc.log("this.ballNodePos=====",this.ballNodePos.x,this.ballNodePos.y)

    }

    private refashPos(){

        // cc.log("this.ballNode.convertToNodeSpace(this.a1.getPosition())===",this.ballNode.convertToWorldSpace(this.a1.getPosition()))

        this.a1WorldPos = this.ballNode.convertToWorldSpaceAR(this.a1.getPosition())

        this.a2WorldPos = this.ballNode.convertToWorldSpaceAR(this.a2.getPosition())

        this.a3WorldPos = this.ballNode.convertToWorldSpaceAR(this.a3.getPosition())

        this.a4WorldPos = this.ballNode.convertToWorldSpaceAR(this.a4.getPosition())

        cc.log("this.a1WorldPos=====",this.a1WorldPos.x,this.a1WorldPos.y)

        cc.log("this.a2WorldPos=====",this.a2WorldPos.x,this.a2WorldPos.y)

        cc.log("this.a3WorldPos=====",this.a3WorldPos.x,this.a3WorldPos.y)

        cc.log("this.a4WorldPos=====",this.a4WorldPos.x,this.a4WorldPos.y)

        cc.log("----------------------------------------优美的分割线----------------------------------------")

    }

    private onTouchStart(touch: cc.Event.EventTouch) {

        this._isHaveGold = false

        this.graphic_line.clear();

        this.graphic_line1.clear();

        const start = touch.getStartLocation();

        let angle = this.getAngle(start.x,start.y,this.ballNodePos.x,this.ballNodePos.y)

        this.ballNode.setRotation(180-angle)

        this._cur_length = 0;

        this._cur_length1 = 0;

        this.refashPos()

        // 计算射线

        this.drawRayCast(this.a1WorldPos, this.a3WorldPos.subSelf(this.a1WorldPos).normalizeSelf(),this.graphic_line);

        this.drawRayCast(this.a2WorldPos, this.a4WorldPos.subSelf(this.a2WorldPos).normalizeSelf(),this.graphic_line1);

        this.graphic_line.stroke();

        this.graphic_line1.stroke();

        if(this._isHaveGold == false){

            cc.log("检测物体丢失了")

        }

        else{

            cc.log("检测到物体了")

        }

    }

    private onTouchMove(touch: cc.Event.EventTouch) {

        this._isHaveGold = false

        this.graphic_line.clear();

        this.graphic_line1.clear();

        const start = touch.getLocation();

        let angle = this.getAngle(start.x,start.y,this.ballNodePos.x,this.ballNodePos.y)

        this.ballNode.setRotation(180-angle)

        this._cur_length = 0;

        this._cur_length1 = 0;

        this.refashPos()

        // 计算射线

        this.drawRayCast(this.a1WorldPos, this.a3WorldPos.subSelf(this.a1WorldPos).normalizeSelf(),this.graphic_line);

        this.drawRayCast(this.a2WorldPos, this.a4WorldPos.subSelf(this.a2WorldPos).normalizeSelf(),this.graphic_line1);

        this.graphic_line.stroke();

        this.graphic_line1.stroke();

        if(this._isHaveGold == false){

            cc.log("检测物体丢失了")

        }

        else{

            cc.log("检测到物体了")

        }

    }

    private onTouchEnd(touch: cc.Event.EventTouch) {

        this.graphic_line.clear();

        this.graphic_line1.clear();

    }

    /**

     * @description 计算射线

     * @param startLocation 起始位置 世界坐标系

     * @param vector_dir 单位方向向量

     */

    private drawRayCast(startLocation: cc.Vec2, vector_dir: cc.Vec2,graphic:cc.Graphics) {

        let lenghtType = 0

        let Type = 0 

        if(graphic==this.graphic_line1){

            lenghtType=this._cur_length1

            Type=1

        }

        else{

            lenghtType=this._cur_length

            Type=0

        }

        // 剩余长度

        const left_length = AIM_LINE_MAX_LENGTH - lenghtType;

        if (left_length <= 0) return;

        // 计算线的终点位置

        const endLocation = startLocation.add(vector_dir.mul(left_length));

        // 射线测试

        // 检测给定的线段穿过哪些碰撞体,可以获取到碰撞体在线段穿过碰撞体的那个点的法线向量和其他一些有用的信息。 

        const results = cc.director.getPhysicsManager().rayCast(startLocation, endLocation, cc.RayCastType.Closest);

        if (results.length > 0) {

            this._isHaveGold = true

            const result = results[0];

            // 指定射线与穿过的碰撞体在哪一点相交。

            const point = result.point;

            // 画入射线段

            this.drawAimLine(startLocation, point,graphic);

            // 计算长度

            const line_length = point.sub(startLocation).mag();

            // 计算已画长度

            if(Type==1){

                this._cur_length1 += line_length;

            }

            else{

                this._cur_length += line_length;

            }

            // 指定碰撞体在相交点的表面的法线单位向量。

            const vector_n = result.normal;

            // 入射单位向量

            const vector_i = vector_dir;

            // 反射单位向量

            const vector_r = vector_i.sub(vector_n.mul(2 * vector_i.dot(vector_n)));

            // 接着计算下一段

            this.drawRayCast(point, vector_r,graphic);

        } else {

            // 画剩余线段

            this.drawAimLine(startLocation, endLocation,graphic);

        }

    }

    getAngle(px,py,mx,my){//获得人物中心和鼠标坐标连线,与y轴正半轴之间的夹角

        var x = Math.abs(px-mx);

        var y = Math.abs(py-my);

        var z = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));

        var cos = y/z;

        var radina = Math.acos(cos);//用反三角函数求弧度

        var angle = Math.floor(180/(Math.PI/radina));//将弧度转换成角度

        if(mx>px&&my>py){//鼠标在第四象限

            angle = 180 - angle;

        }

        if(mx==px&&my>py){//鼠标在y轴负方向上

            angle = 180;

        }

        if(mx>px&&my==py){//鼠标在x轴正方向上

            angle = 90;

        }

        if(mxpy){//鼠标在第三象限

            angle = 180+angle;

        }

        if(mx
image.gif

检测类型介绍

  • cc.RayCastType.Any

检测射线路径上任意的碰撞体,一旦检测到任何碰撞体,将立刻结束检测其他的碰撞体,最快。

  • cc.RayCastType.Closest

检测射线路径上最近的碰撞体,这是射线检测的默认值,稍慢。

  • cc.RayCastType.All

检测射线路径上的所有碰撞体,检测到的结果顺序不是固定的。在这种检测类型下一个碰撞体可能会返回多个结果,这是因为 box2d 是通过检测夹具(fixture)来进行物体检测的,而一个碰撞体中可能由多个夹具(fixture)组成的,慢。更多细节可到 物理碰撞组件 查看。

  • cc.RayCastType.AllClosest

检测射线路径上所有碰撞体,但是会对返回值进行删选,只返回每一个碰撞体距离射线起始点最近的那个点的相关信息,最慢

  • result返回值介绍
在这里插入图片描述

感谢关注个人博客

你可能感兴趣的:(CocosCreator射线检测之范围检测)