消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
10 KiB

4 days ago
import {
_decorator, CCInteger, Collider2D, Color, Component, Contact2DType,
ERigidBody2DType, IPhysics2DContact, Node, PolygonCollider2D, RigidBody2D, Sprite, Vec2,
} from 'cc';
import { LuosiDing } from './LuosiDing';
1 week ago
const { ccclass, property } = _decorator;
/** 螺丝全部拔完后木板掉落时的整体色调 */
const BOARD_FALL_GRAY = '#646464';
4 days ago
/** 进入自由掉落后:本地 Y 低于此值(相对 layer)即可销毁 */
const BOARD_FALL_DESTROY_LOCAL_Y = -60;
/** 进入自由掉落后最长存活(秒) */
const BOARD_FALL_MAX_LIFE_SEC = 2.5;
/** 碰到 floor 后延迟销毁(秒) */
const BOARD_FALL_FLOOR_DESTROY_DELAY = 0.15;
1 week ago
/**木板子元素*/
@ccclass('Aelement')
export class Aelement extends Component {
@property(CCInteger)
aelementHoleNum: number = 2;
4 days ago
/** 生成时孔洞螺丝总数 */
private _initialScrewCount = 0;
/** 木板上尚未脱落的螺丝数量(仅按 onScrewLeft 递减,不含铰链锚点) */
1 week ago
private _remainingScrews = 0;
/** 螺丝全部脱落后,开始检测木板掉落 */
private _fallCheckEnabled = false;
4 days ago
/** 已启用铰链约束物理(仅剩一颗螺丝) */
private _constrainedPhysicsActive = false;
/** 进入自由掉落累计时间 */
private _fallLifeAcc = 0;
private _fallContactBound = false;
private _physicsActivating = false;
1 week ago
/** 木板是否已进入掉落态(置灰),不再参与遮挡其它螺丝 */
get isFalling(): boolean {
return this._fallCheckEnabled;
}
4 days ago
/** 仅剩一颗螺丝、绕铰链摆动中 */
get isSwinging(): boolean {
return this._constrainedPhysicsActive && !this._fallCheckEnabled;
}
1 week ago
/** 获取木板上所有孔洞节点(孔洞1、孔洞2…) */
getHoleNodes(): Node[] {
return this.node.children
.filter((c) => c.name.startsWith('孔洞'))
.sort((a, b) => a.name.localeCompare(b.name, 'zh'));
}
/** 生成螺丝后登记数量 */
initScrewCount(count: number) {
4 days ago
this._initialScrewCount = count;
1 week ago
this._remainingScrews = count;
this._fallCheckEnabled = false;
4 days ago
this._constrainedPhysicsActive = false;
this._fallLifeAcc = 0;
this._physicsActivating = false;
1 week ago
}
/** 螺丝从木板上脱落(弹出或消除)时由 LuosiDing 调用 */
onScrewLeft() {
4 days ago
if (this._remainingScrews > 0) {
this._remainingScrews--;
}
this._applyPhysicsForScrewCount();
}
/** 复活 / 广告消钉后:按孔洞内实际螺丝数恢复 */
reconcilePhysicsState() {
if (!this.node?.isValid) return;
this._remainingScrews = LuosiDing.countAttachedScrewsInHoles(this.node);
this._applyPhysicsForScrewCount();
}
/**
* prefab
* - 2+
* - 1 Dynamic +
* - 0
*/
private _applyPhysicsForScrewCount() {
if (this._remainingScrews > 1) {
this._fallCheckEnabled = false;
this._fallLifeAcc = 0;
this._constrainedPhysicsActive = false;
this._unbindFallContact();
LuosiDing.detachAllBoardHinges(this.node);
this.prepareColliderForGameplay();
return;
}
if (this._remainingScrews === 1) {
this._enterConstrainedPhysics();
return;
}
if (this._initialScrewCount > 0) {
this._enterFreeFall();
}
}
private _enterConstrainedPhysics() {
this._fallCheckEnabled = false;
this._fallLifeAcc = 0;
this._unbindFallContact();
this.beginConstrainedPhysics();
}
private _enterFreeFall() {
if (!this.node?.isValid) return;
if (!this._fallCheckEnabled) {
1 week ago
this._applyFallGrayColor();
}
4 days ago
this._fallCheckEnabled = true;
this._constrainedPhysicsActive = false;
this._fallLifeAcc = 0;
LuosiDing.detachAllBoardHinges(this.node);
this.beginFallingPhysics();
1 week ago
}
/** 螺丝拔完开始掉落时,将木板及子节点 Sprite 整体置灰 */
private _applyFallGrayColor() {
const gray = new Color().fromHEX(BOARD_FALL_GRAY);
const visit = (n: Node) => {
const sp = n.getComponent(Sprite);
if (sp) sp.color = gray;
for (const child of n.children) visit(child);
};
visit(this.node);
}
4 days ago
/**
* Polygon LuosiDing
*/
prepareColliderForGameplay() {
this.unschedule(this._activateConstrainedRigidbody);
this.unschedule(this._activateFallRigidbody);
this._physicsActivating = false;
this._unbindFallContact();
LuosiDing.detachAllBoardHinges(this.node);
this._constrainedPhysicsActive = false;
1 week ago
const rb = this.node.getComponent(RigidBody2D);
4 days ago
if (rb) {
rb.enabled = false;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
1 week ago
}
const poly = this.node.getComponent(PolygonCollider2D);
if (poly) {
poly.enabled = false;
}
}
4 days ago
/** 仅剩一颗螺丝:Dynamic + 单铰链,受重力自然摆 */
beginConstrainedPhysics() {
if (this._physicsActivating) return;
this.unschedule(this._activateConstrainedRigidbody);
this.scheduleOnce(this._activateConstrainedRigidbody, 0.06);
}
private _activateConstrainedRigidbody = () => {
if (!this.node?.isValid || this._fallCheckEnabled || this._remainingScrews !== 1) return;
this._physicsActivating = true;
const rb = this.node.getComponent(RigidBody2D);
if (!rb) {
this._physicsActivating = false;
return;
}
if (rb.enabled) {
rb.enabled = false;
}
// 传感器:不与堆叠木板/斜坡产生分离冲量,只靠铰链+重力按重心摆
this._enableBoardCollider(true);
rb.enabled = true;
rb.type = ERigidBody2DType.Dynamic;
rb.gravityScale = 5;
rb.linearDamping = 0.2;
rb.angularDamping = 5;
rb.fixedRotation = false;
rb.allowSleep = true;
rb.bullet = false;
rb.enabledContactListener = false;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
LuosiDing.refreshBoardHinges(this.node);
rb.wakeUp();
this._constrainedPhysicsActive = true;
this._physicsActivating = false;
};
/** 螺丝全部拔完后脱离铰链,沿斜坡自然滑落 */
beginFallingPhysics() {
LuosiDing.detachAllBoardHinges(this.node);
this.unschedule(this._activateFallRigidbody);
this.scheduleOnce(this._activateFallRigidbody, 0.06);
}
private _activateFallRigidbody = () => {
1 week ago
if (!this._fallCheckEnabled || !this.node?.isValid) return;
4 days ago
this._physicsActivating = true;
const rb = this.node.getComponent(RigidBody2D);
if (!rb) {
this._physicsActivating = false;
return;
1 week ago
}
4 days ago
if (rb.enabled) {
rb.enabled = false;
}
this._enableBoardCollider(false);
this._bindFallContact();
rb.enabled = true;
rb.type = ERigidBody2DType.Dynamic;
rb.gravityScale = 5;
rb.linearDamping = 0.2;
rb.angularDamping = 5;
rb.fixedRotation = false;
rb.allowSleep = true;
rb.bullet = false;
rb.enabledContactListener = true;
rb.linearVelocity = Vec2.ZERO;
rb.angularVelocity = 0;
rb.wakeUp();
this._physicsActivating = false;
};
private _bindFallContact() {
if (this._fallContactBound) return;
const col = this.node.getComponent(Collider2D);
if (!col) return;
col.on(Contact2DType.BEGIN_CONTACT, this._onFallFloorContact, this);
this._fallContactBound = true;
}
private _unbindFallContact() {
if (!this._fallContactBound) return;
const col = this.node.getComponent(Collider2D);
col?.off(Contact2DType.BEGIN_CONTACT, this._onFallFloorContact, this);
this._fallContactBound = false;
}
private _scheduleFloorDestroy = () => {
this._destroyFallenBoard();
};
private _onFallFloorContact(_self: Collider2D, other: Collider2D, _contact: IPhysics2DContact | null) {
if (!this._fallCheckEnabled || !other?.node?.isValid) return;
const n = other.node.name;
const p = other.node.parent?.name ?? '';
if (n.indexOf('floor') >= 0 || p === 'ui_floorNode') {
this.scheduleOnce(this._scheduleFloorDestroy, BOARD_FALL_FLOOR_DESTROY_DELAY);
}
}
private _enableBoardCollider(sensor: boolean) {
const poly = this.node.getComponent(PolygonCollider2D);
if (!poly) return;
poly.enabled = true;
poly.sensor = sensor;
poly.friction = 0.4;
poly.restitution = 0;
const apply = (poly as { apply?: () => void }).apply;
if (typeof apply === 'function') apply.call(poly);
}
private _destroyFallenBoard() {
if (!this.node?.isValid) return;
this.unschedule(this._scheduleFloorDestroy);
this._unbindFallContact();
const rb = this.node.getComponent(RigidBody2D);
if (rb) rb.enabled = false;
this.node.destroy();
}
onDestroy() {
this._unbindFallContact();
1 week ago
}
4 days ago
update(dt: number) {
if (!this.node?.isValid) return;
1 week ago
4 days ago
if (!this._fallCheckEnabled) {
if (this._remainingScrews <= 0 && this._initialScrewCount > 0
&& LuosiDing.countAttachedScrewsInHoles(this.node) === 0) {
this._enterFreeFall();
}
return;
}
this._fallLifeAcc += dt;
if (this._fallLifeAcc >= BOARD_FALL_MAX_LIFE_SEC) {
this._destroyFallenBoard();
return;
}
if (this.node.position.y < BOARD_FALL_DESTROY_LOCAL_Y) {
this._destroyFallenBoard();
return;
}
const rb = this.node.getComponent(RigidBody2D);
if (!rb?.enabled && !this._physicsActivating) {
this._activateFallRigidbody();
}
}
}