CocosCreator之KUOKUO带你做物理切割(第一部分)

摘要

有趣的物理小游戏总能让小伙伴们爱不释手,而 CocosCreator 的内置物理引擎让其开发变得更加简单。今天 KUOKUO 就为大家带来物理切割,从零开始做出切割单个物体!

正文

使用版本

CocosCreator 版本 2.1.3

最终效果

层级管理

如图,在层级管理器中有 3 部分:画布,物理层,绘图层。其中画布只是作为触摸节点,绘图层用于绘制红线,物理层中放入物理刚体。脚本有三个,其中的 setting 仅仅是开启物理和物理绘制,cut-item 脚本用于绘制刚体,下面给出代码。

draw 方法会根据物理包围盒的 points 填充绘图,注意要使用多边形碰撞盒。

onLoad () {
    this.draw();
}
draw () {
    const points = this.getComponent(cc.PhysicsPolygonCollider).points;
    const ctx = this.getComponent(cc.Graphics);
    ctx.clear();
    const len = points.length;
    ctx.moveTo(points[len - 1].x, points[len - 1].y);
    for (let i = 0; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y);
    }
    ctx.fill();
}
鼠标划线

鼠标划线很简单,就是获取起点和当前点然后绘图线段。在 cut-main 脚本里注册 touchmove 事件。

this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
    this.draw.clear();
    const startPoint = e.getStartLocation();
    this.draw.moveTo(startPoint.x, startPoint.y);
    this.draw.lineTo(e.getLocationX(), e.getLocationY());
    this.draw.stroke();
}, this);
射线检测

什么是射线检测?就是给定两个点,返回这两个点之间的物理碰撞盒。很显然,我们松手的那一刻,划线起点与终点就是我们要的那两个点!我们把事件监听方法提出来,放在 onLoad 里面。

onLoad () {
    this.registerEvent();
}
registerEvent () {
    this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
        this.draw.clear();
        const startPoint = e.getStartLocation();
        this.draw.moveTo(startPoint.x, startPoint.y);
        this.draw.lineTo(e.getLocationX(), e.getLocationY());
        this.draw.stroke();
    }, this);
    this.node.on(cc.Node.EventType.TOUCH_END, (e) => {
        this.draw.clear();
        const p1 = e.getStartLocation();
        const p2 = e.getLocation();
        // 核心逻辑
        this.cut(p1, p2);
    }, this);
}

接下来我们讲 cut 方法。

核心逻辑

首先我们要调用射线检测的方法,模式分为四种:Any,Closest,All,AllClosest。本次教程针对单个碰撞体,使用 Closest 模式,每个模式更详细的信息可以去查下官方文档。因为我们要获取所有的交点,所以要正反来两次射线检测。

const result1 = cc.director.getPhysicsManager().rayCast(point1, point2, cc.RayCastType.Closest);
const result2 = cc.director.getPhysicsManager().rayCast(point2, point1, cc.RayCastType.Closest);

这样我们就获取到了结果,然后对是否为单个碰撞体作检测:

if (result1.length === 0 || result2.length === 0) {
    cc.warn('无碰撞体');
    return;
}
if (result1[0].collider !== result2[0].collider) {
    cc.warn('不是单独碰撞体');
    return;
}
if (!(result1[0].collider instanceof cc.PhysicsPolygonCollider)) {
    cc.warn('非多边形物理碰撞盒无points属性');
    return;
}

检测都通过后,我们就可以放心的取数据做逻辑处理了。接下来我们要将基于世界的坐标转化为本地的坐标数据。

const collider = result1[0].collider;
let localPoint1 = cc.Vec2.ZERO;
let localPoint2 = cc.Vec2.ZERO;
collider.body.getLocalPoint(result1[0].point, localPoint1);
collider.body.getLocalPoint(result2[0].point, localPoint2);

万事俱备,我们现在知道了碰撞体与射线的两个交点,那么我们只要把碰撞体的 points 以两个点为界限分割为两部分即可!但是,首先我们要先找到交点在那条线上,先封装个判断方法:

/** 近似判断点在线上 */
pointInLine (point, start, end) {
    const dis = 1;
    return cc.Interp.pointLineDistance(point, start, end, true) < dis;
}

然后我们去查找那两个点究竟在那两条线上,用下标去表示。

const points = collider.points;
let index1 = undefined;
let index2 = undefined;
for (let i = 0; i < points.length; i++) {
    let p1 = points[i];
    let p2 = i === points.length - 1 ? points[0] : points[i + 1];
    if (this.pointInLine(localPoint1, p1, p2)) {
        index1 = i;
    }
    if (this.pointInLine(localPoint2, p1, p2)) {
        index2 = i;
    }
    if (index1 !== undefined && index2 !== undefined) {
        break;
    }
}
cc.log(`点1下标${index1}`);
cc.log(`点2下标${index2}`);

OK!我们已经知道了那两个交点在哪里,我们下一步就是新建两个数组,按照顺序分好类,一边一个!

const array1 = [];
const array2 = [];
// 碰到 index1 或 index2 的标志量
let time = 0;
for (let i = 0; i < points.length; i++) {
    let temp = points[i].clone();
    if (time === 0) {
        array1.push(temp);
    } else {
        array2.push(temp);
    }
    if ((i === index1 || i === index2) && time === 0) {
        array1.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
        array2.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
        time = 1;
    } else if ((i === index1 || i === index2) && time === 1) {
        array2.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
        array1.push(i === index1 ? localPoint1.clone() : localPoint2.clone());
        time = 0;
    }
}
cc.log(array1,array2);

逻辑不难,拿 time 作为碰到交点的标志量,顺利的分为两个数组。

拷贝节点

接下来就简单了,将本体重置为 array1 状态,拷贝一个本体,设置 array2 状态,再执行下自身脚本上的 draw 方法绘图。

// 设置第一个碰撞体
collider.points = array1;
collider.apply();
collider.node.getComponent(Item).draw();
// 克隆一个本体作为第二个
const cloneNode = cc.instantiate(collider.node);
this.gameLayer.addChild(cloneNode);
const comp = cloneNode.getComponent(cc.PhysicsPolygonCollider);
comp.points = array2;
comp.apply();
cloneNode.getComponent(Item).draw();

怎么样?如果感觉理解的差点意思,下方有源码获取方式哦!

结语

有意思吧!我们在第二部分教程讲讲多个物体的切割。

O(∩_∩)O~~

源码在我的微信公众号回复关键词【物理切割】即可获得

回答: 在Cocos Creator中,常用的设计模式包括子弹工厂模式、单例模式和建造者模式。子弹工厂模式是通过工厂来创建子弹节点,根据提供的子弹种类、速度和方向来自动创建子弹节点并返回,实现了代码的精简和灵活性。\[1\]单例模式用于创建一个全局唯一的实例,可以在整个应用程序中共享和访问该实例,避免了多次实例化的问题。\[2\]建造者模式用于创建复杂的对象,通过将对象的构建过程分解为多个步骤,使得对象的构建更加灵活和可扩展。\[3\]这些设计模式在Cocos Creator中可以帮助开发者更好地组织和管理代码,提高开发效率和代码质量。 #### 引用[.reference_title] - *1* [CocosCreator进阶实战第四部分:工厂模式](https://blog.csdn.net/kuokuo666/article/details/103722679)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [设计模式---创建型模式(工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式)](https://blog.csdn.net/sinat_36499762/article/details/115624011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值