【Cocos Creator游戏开发教程】仿微信趣味画赛车小游戏(三)代码实现

【Cocos Creator游戏开发教程】仿微信趣味画赛车小游戏(一)前言,界面UI
【Cocos Creator游戏开发教程】仿微信趣味画赛车小游戏(二)物理刚体关节

项目地址已放到 github 上,需要的小伙伴可自行下载。
这节我们讲一下游戏代码实现

1.开启物理世界

// GameMain.ts
protected onLoad(): void {
    cc.director.getPhysicsManager().enabled = true; // 开启物理引擎需要写在 onLoad 中
}

protected start(): void {
    cc.debug.setDisplayStats(false);
	this.openPhysics();
	this.node.on(DrawArea.DRAW_VEHICLE, this.onDrawVehicle, this);
}

private openPhysics(): void {
    let manager: cc.PhysicsManager = cc.director.getPhysicsManager();
	manager.enabledAccumulator = true;
	manager.debugDrawFlags = 1;     // 设置调试绘制标志
	cc.PhysicsManager.FIXED_TIME_STEP = 1 / 30;
	cc.PhysicsManager.VELOCITY_ITERATIONS = 8;
	cc.PhysicsManager.POSITION_ITERATIONS = 8;
}

2.画车

监听触摸DrawArea开始事件,将绘画节点移到触摸点,记录存储当前触摸点

// DrawArea.ts
private onTouchStart(event: cc.Event.EventTouch): void {
    // 转换到局部坐标
    let pos = this.node.convertToNodeSpaceAR(event.getLocation());
    this.drawLine.moveTo(pos.x, pos.y);
    this.recordPos = cc.v2(pos.x, pos.y);
    this.drawPoints.push(cc.v2(pos.x, pos.y));
}

监听触摸DrawArea移动事件

// DrawArea.ts
private onTouchMove(event: cc.Event.EventTouch): void {
    let pos: cc.Vec2 = this.node.convertToNodeSpaceAR(event.getLocation());

    let widthLimit: number = (this.node.width + DrawArea.DRAW_CIRCLE_R) / 2;
    let heightLimit: number = (this.node.height + DrawArea.DRAW_CIRCLE_R) / 2;

    // 检查当前触摸点是否超出绘画区域
    if (pos.x < -widthLimit || pos.x > widthLimit) {
        return;
    }
    if (pos.y < -heightLimit || pos.y > heightLimit) {
        return;
    }
    // 绘画
    this.drawLine.lineTo(pos.x, pos.y);
    this.drawLine.stroke();
    this.drawLine.moveTo(pos.x, pos.y);

    // 记录当前手移动到的点
    this.currentPos = cc.v2(pos.x, pos.y);
    //求两点之间的距离
    let subVec: cc.Vec2 = this.currentPos.sub(this.recordPos);
    let distance: number = subVec.mag() + 5;    // 防止线段间出现空隙,所以+5长度

    // 为了减少触摸点数量,如果距离大于等于预制体的width,记录存储该触摸点
    if (distance >= 25) {
        // 将此时的触摸点设为记录点
        this.recordPos = cc.v2(pos.x, pos.y);

        this.drawPoints.push(cc.v2(pos.x, pos.y));
    }
}

监听触摸DrawArea结束事件,发送自定义画车事件,将触摸点数组发送出去

// DrawArea.ts
private onTouchEnd(event: cc.Event.EventTouch): void {
    // 判断触摸点数量是否小于2,不是才发送画车事件
    if (this.drawPoints.length <= 1) {
        this.drawPoints = [];
        return;
    }
    // 发送自定义画车事件 DrawArea.DRAW_VEHICLE
    let customEvent: cc.Event.EventCustom = new cc.Event.EventCustom(DrawArea.DRAW_VEHICLE, true);
    customEvent.setUserData(this.drawPoints);
    this.node.dispatchEvent(customEvent);

    // 清空绘画区域,存储的触摸点
    this.drawLine.clear();
    this.drawPoints = [];
}

3.创建小车

监听自定义画车事件,获取绘画节点数组,调用Vehicle.ts脚本刷新小车

// GameMain.ts
private onDrawVehicle(event: cc.Event.EventCustom): void {
    event.stopPropagation();
    (this.vehicle.getComponent('Vehicle') as Vehicle).updateVehicle(event.getUserData());
}

收到监听,调用刷新车方法

// Vehicle.ts
public updateVehicle(drawPoints: Array<cc.Vec2>): void {
    if (drawPoints.length <= 0) {
        return;
    }
    let _self = this;

    // 重置车位置
    _self.node.x = 0;
    _self.node.y = 200;

    // 重置车
    _self.resetVehicle();
    // 画车身
    _self.drawVehicleBody(drawPoints);
    // 画轮子
    _self.drawWheel(drawPoints);

    // 激活车节点
    if (!_self.node.active) {
        _self.node.active = true;
    }
}

重置车,清空子节点

// Vehicle.ts
private resetVehicle(): void {
    let _self = this;
    let vehicleNode: cc.Node = _self.vehicle.node;   // 获取车节点

    vehicleNode.removeAllChildren();
    _self.vehicleBodys = [];

    _self.wheelRear = null;
    _self.wheelFront = null;
}

画车身

// Vehicle.ts
private drawVehicleBody(drawPoints: Array<cc.Vec2>): void {
    let _self = this;
    let vehicleNode: cc.Node = _self.vehicle.node;   // 获取车节点

    let beforePos: cc.Vec2 = drawPoints[0];
    for (let i = 1; i < drawPoints.length; i++) {
        let curPos: cc.Vec2 = drawPoints[i];
        let subVec: cc.Vec2 = curPos.sub(beforePos);
        let distance: number = subVec.mag() + 5;    // 防止线段间出现空隙,所以+5长度

        // 给定方向向量
        let tempVec: cc.Vec2 = cc.v2(0, 10);

        // 求两点的方向角度
        let rotateVec: number = subVec.signAngle(tempVec) / Math.PI * 180 - 90;

        // 创建车刚体小部件
        let lineItem: cc.Node = cc.instantiate(_self.linePrefab);
        lineItem.angle = -rotateVec;
        lineItem.setPosition(beforePos);
        lineItem.setParent(vehicleNode);

        // 这一步是为了防止两个线段之间出现空隙,动态改变预制体的长度
        lineItem.width = distance;

        // 设置刚体属性
        lineItem.getComponent(cc.PhysicsBoxCollider).offset.x = lineItem.width / 2;
        lineItem.getComponent(cc.PhysicsBoxCollider).size.width = lineItem.width;
        lineItem.getComponent(cc.PhysicsBoxCollider).sensor = true;
        lineItem.getComponent(cc.PhysicsBoxCollider).restitution = 100;
        lineItem.getComponent(cc.PhysicsBoxCollider).apply();

        // 首刚体不需要创建刚体焊接组件
        if (_self.vehicleBodys.length > 0) {
            let beforeLine: cc.Node = _self.vehicleBodys[_self.vehicleBodys.length - 1];

            // 焊接车刚体小部件
            let weldJoint: cc.WeldJoint = lineItem.addComponent(cc.WeldJoint);
            weldJoint.referenceAngle = -rotateVec - beforeLine.angle;

            weldJoint.connectedBody = beforeLine.getComponent(cc.RigidBody);
            weldJoint.connectedAnchor = cc.v2(beforeLine.width - 5, 0);
        }
        _self.vehicleBodys.push(lineItem);

        beforePos = cc.v2(curPos.x, curPos.y);
    }
}

画轮子

// Vehicle.ts
private drawWheel(drawPoints: Array<cc.Vec2>): void {
    let _self = this;

    let vehicleNode: cc.Node = _self.vehicle.node;
    // 首尾车身刚体
    let lineRear: cc.Node = _self.vehicleBodys[0];
    let lineFront: cc.Node = _self.vehicleBodys[_self.vehicleBodys.length - 1];

    let wheelSF: cc.SpriteFrame = new cc.SpriteFrame("HelloWorld");     // 轮胎精灵帧
    // 首尾触摸点
    let rearPos: cc.Vec2 = drawPoints[0];
    let frontPos: cc.Vec2 = drawPoints[drawPoints.length - 1];

    // 通过预制节点,创建后轮
    let wheelRear: cc.Node = cc.instantiate(_self.wheelPrefab);
    wheelRear.getComponent(cc.Sprite).spriteFrame = wheelSF;
    wheelRear.setPosition(rearPos);
    vehicleNode.addChild(wheelRear);

    // 通过预制节点,创建前轮
    let wheelFront: cc.Node = cc.instantiate(_self.wheelPrefab);
    wheelRear.getComponent(cc.Sprite).spriteFrame = wheelSF;
    wheelFront.setPosition(frontPos);
    vehicleNode.addChild(wheelFront);

    // 添加轮子刚体标识
    wheelRear.getComponent(cc.PhysicsCircleCollider).tag = VehicleEnum.COLLIDER_TAG.REAR_TAG;       // 后轮
    wheelFront.getComponent(cc.PhysicsCircleCollider).tag = VehicleEnum.COLLIDER_TAG.FRONT_TAG;      // 前轮

    wheelRear.getComponent(cc.PhysicsCircleCollider).apply();
    wheelFront.getComponent(cc.PhysicsCircleCollider).apply();

    // 加入轮子关节
    // 后轮
    let wheelJointRear: cc.WheelJoint = ComponentUtil.addWheelJointToObj(lineRear, {
        connectedNode: wheelRear,
        anchor: cc.v2(0, 0)
    });

    // 前轮
    let wheelJointFront: cc.WheelJoint = ComponentUtil.addWheelJointToObj(lineFront, {
        connectedNode: wheelFront,
        anchor: cc.v2(lineFront.width - 5, 0)
    });

    // 添加距离关节,约束两个轮子
    let distanceJoint: cc.DistanceJoint = wheelRear.addComponent(cc.DistanceJoint);
    distanceJoint.connectedBody = wheelFront.getComponent(cc.RigidBody);
    distanceJoint.distance = frontPos.sub(rearPos).mag();

    _self.wheelRear = wheelRear;
    _self.wheelFront = wheelFront;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值