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(mx<px&&my>py){//鼠标在第三象限

            angle = 180+angle;

        }

        if(mx<px&&my==py){//鼠标在x轴负方向

            angle = 270;

        }

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

            angle = 360 - angle;

        }

        // cc.log("angle=======",angle)

        return angle;

    }

    /**

     * @description 画瞄准线

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

     * @param endLocation 结束位置 世界坐标系

     */

    private drawAimLine(startLocation: cc.Vec2, endLocation: cc.Vec2,graphic:cc.Graphics) {

        // 转换坐标

        // cc.log("startLocation=====",startLocation.x,startLocation.y)

        const graphic_startLocation = graphic.node.convertToNodeSpace(startLocation);

        // cc.log("graphic_startLocation====",graphic_startLocation.x,graphic_startLocation.y)

        graphic.moveTo(graphic_startLocation.x, graphic_startLocation.y);

        // 画小圆圆

        // 间隔

        const delta = 20;

        // 方向

        const vector_dir = endLocation.sub(startLocation);

        // 数量

        const total_count = Math.round(vector_dir.mag() / delta);

        // 每次间隔向量​

        vector_dir.normalizeSelf().mulSelf(delta);

        for (let index = 0; index < total_count; index++) {

            graphic_startLocation.addSelf(vector_dir)

            graphic.circle(graphic_startLocation.x, graphic_startLocation.y, 2);

        }

    }

}

image.gif

检测类型介绍

  • cc.RayCastType.Any

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

  • cc.RayCastType.Closest

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

  • cc.RayCastType.All

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

  • cc.RayCastType.AllClosest

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

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

感谢关注个人博客

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351