优化 碰撞检测 ,避免穿模问题

This commit is contained in:
COMPUTER\EDY 2026-07-02 19:51:52 +08:00
parent e614279f1f
commit 83b23cf958
2 changed files with 258 additions and 9 deletions

View File

@ -125,6 +125,13 @@ export default class Block extends cc.Component {
private lastMoveTime = 0;
/*最大移动速度 - 限制方块跟随手指的最大距离,防止移动过快*/
private maxSpeed = 300;
/*拖动碰撞缓存 - 仅缓存本次拖动涉及的方块,减少移动热路径中的组件查找和临时对象*/
private dragGroupNodes: cc.Node[] = [];
private dragObstacleCells: any[] = [];
private lastSafeDragPositions: any[] = [];
/*地图格子间距为120116与现有边缘碰撞器的有效阻挡距离一致*/
private readonly dragCellSize: number = 120;
private readonly dragCollisionDistance: number = 116;
// ============================================
// Cocos Creator 属性定义
@ -1556,6 +1563,7 @@ export default class Block extends cc.Component {
this.isTouch = true;
this.moveCorner = 0;
this.relative_Position = cc.v2(this.node.x - local.x, this.node.y - local.y);
this.prepareDragCollisionState();
if (this.block_Info.floor == undefined && this.flowerType != 2) {
MapConroler._instance.changeRiseFall(this.color, true);
if (this.block_Info.lock != undefined && this.block_Info.lock != null && this.type != BlockType.) {
@ -1638,6 +1646,7 @@ export default class Block extends cc.Component {
touchEnd(event) {
MapConroler._instance.touchIng = false;
if (MapConroler._instance.gameOver) {
this.clearDragCollisionState();
return;
}
let Simulate = {
@ -1645,8 +1654,11 @@ export default class Block extends cc.Component {
direction: 0,
};
if (this.isTouch) {
// 松手前重新构建一次障碍缓存。若外部状态在拖动期间变化,仍回退到最后合法位置。
this.refreshDragObstacleCells();
if (this.wouldDragGroupOverlap(0, 0)) {
this.restoreLastSafeDragPositions();
}
// //先检测 是否有块在障碍物上,如果有回归位置
let jg = this.resetStartPos();
let otherCanJg = false;
@ -1746,8 +1758,7 @@ export default class Block extends cc.Component {
}
}
}
this.clearDragCollisionState();
}
// ============================================
@ -1777,6 +1788,7 @@ export default class Block extends cc.Component {
MapConroler._instance.changeRiseFall(this.color, false);
MapConroler._instance.removeOneBlock();
this.isTouch = false;
this.clearDragCollisionState();
if (this.type != BlockType.) this.node.zIndex = this.level;
// else this.node.zIndex = 1000;
this.hit.active = false;
@ -1831,6 +1843,186 @@ export default class Block extends cc.Component {
}
}
/**
* 使
*
*/
private prepareDragCollisionState() {
this.dragGroupNodes = [];
this.addDragGroupNode(this.node);
if ((this.type === BlockType. || this.type === BlockType.) &&
this.block_Info && this.block_Info.node) {
this.addDragGroupNode(this.block_Info.node);
}
if (this.moveFloorPd === true && this.teamBlocks && this.teamBlocks.length > 1) {
for (let i = 0; i < this.teamBlocks.length; i++) {
this.addDragGroupNode(this.teamBlocks[i]);
}
}
this.refreshDragObstacleCells();
this.saveLastSafeDragPositions(true);
}
private addDragGroupNode(node: cc.Node) {
if (!node || !cc.isValid(node)) return;
for (let i = 0; i < this.dragGroupNodes.length; i++) {
if (this.dragGroupNodes[i].uuid === node.uuid) return;
}
this.dragGroupNodes.push(node);
}
/**
* 使
* getComponent或创建Vec2
*/
private refreshDragObstacleCells() {
this.dragObstacleCells = [];
if (!this.node || !this.node.parent) return;
const groupMap: { [uuid: string]: boolean } = {};
for (let i = 0; i < this.dragGroupNodes.length; i++) {
const node = this.dragGroupNodes[i];
if (node && cc.isValid(node)) groupMap[node.uuid] = true;
}
const children = this.node.parent.children;
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (!node || !cc.isValid(node) || !node.activeInHierarchy || groupMap[node.uuid]) continue;
const block = node.getComponent("Block");
if (!block || block.over || block.type === BlockType. ||
!block.allBlocks || block.allBlocks.length === 0) {
continue;
}
for (let j = 0; j < block.allBlocks.length; j++) {
const cell = block.allBlocks[j];
this.dragObstacleCells.push({
x: node.x - 60 + cell.x * this.dragCellSize,
y: node.y + 60 + cell.y * this.dragCellSize,
block: block
});
}
}
}
/** 保持“单色地块与同色普通块可互相穿过”的原有规则。 */
private ignoreDragBlockCollision(movingBlock: any, obstacleBlock: any): boolean {
if (!movingBlock || !obstacleBlock) return true;
const oneIsSingleColor = (movingBlock.type === BlockType.) !==
(obstacleBlock.type === BlockType.);
return oneIsSingleColor && movingBlock.color === obstacleBlock.color &&
obstacleBlock.block_Info && obstacleBlock.block_Info.floor === undefined;
}
/**
*
* 沿LQ碰撞规则
*/
private wouldDragGroupOverlap(offsetX: number, offsetY: number): boolean {
if (!this.dragGroupNodes || this.dragGroupNodes.length === 0) return false;
const minDistance = this.dragCollisionDistance;
for (let i = 0; i < this.dragGroupNodes.length; i++) {
const node = this.dragGroupNodes[i];
if (!node || !cc.isValid(node)) continue;
const movingBlock = node.getComponent("Block");
// 叠加块上层仅负责视觉跟随,原碰撞器也是关闭状态,不作为实体重复计算。
if (!movingBlock || movingBlock.type === BlockType. || !movingBlock.allBlocks) continue;
for (let j = 0; j < movingBlock.allBlocks.length; j++) {
const movingCell = movingBlock.allBlocks[j];
const centerX = node.x + offsetX - 60 + movingCell.x * this.dragCellSize;
const centerY = node.y + offsetY + 60 + movingCell.y * this.dragCellSize;
for (let k = 0; k < this.dragObstacleCells.length; k++) {
const obstacle = this.dragObstacleCells[k];
if (this.ignoreDragBlockCollision(movingBlock, obstacle.block)) continue;
if (Math.abs(centerX - obstacle.x) < minDistance &&
Math.abs(centerY - obstacle.y) < minDistance) {
return true;
}
}
}
}
return false;
}
/**
*
*
*/
private getSafeDragAxisOffset(offsetX: number, offsetY: number): number {
const requestedOffset = offsetX !== 0 ? offsetX : offsetY;
if (requestedOffset === 0 || !this.wouldDragGroupOverlap(offsetX, offsetY)) {
return requestedOffset;
}
let safeRatio = 0;
let blockedRatio = 1;
for (let i = 0; i < 6; i++) {
const ratio = (safeRatio + blockedRatio) * 0.5;
if (this.wouldDragGroupOverlap(offsetX * ratio, offsetY * ratio)) {
blockedRatio = ratio;
} else {
safeRatio = ratio;
}
}
return requestedOffset * safeRatio;
}
/** 同步组合方块。使用实际位移增量,不改变它们原有的相对位置。 */
private moveDragGroupBy(offsetX: number, offsetY: number) {
if (offsetX === 0 && offsetY === 0) return;
for (let i = 0; i < this.dragGroupNodes.length; i++) {
const node = this.dragGroupNodes[i];
if (!node || !cc.isValid(node) || node.uuid === this.node.uuid) continue;
node.x += offsetX;
node.y += offsetY;
}
}
private saveLastSafeDragPositions(reset: boolean = false) {
if (reset || this.lastSafeDragPositions.length !== this.dragGroupNodes.length) {
this.lastSafeDragPositions = [];
for (let i = 0; i < this.dragGroupNodes.length; i++) {
const node = this.dragGroupNodes[i];
if (node && cc.isValid(node)) {
this.lastSafeDragPositions.push({ node: node, x: node.x, y: node.y });
}
}
return;
}
for (let i = 0; i < this.lastSafeDragPositions.length; i++) {
const info = this.lastSafeDragPositions[i];
if (info.node && cc.isValid(info.node)) {
info.x = info.node.x;
info.y = info.node.y;
}
}
}
private restoreLastSafeDragPositions() {
for (let i = 0; i < this.lastSafeDragPositions.length; i++) {
const info = this.lastSafeDragPositions[i];
if (info.node && cc.isValid(info.node)) {
info.node.x = info.x;
info.node.y = info.y;
}
}
}
private clearDragCollisionState() {
this.dragGroupNodes = [];
this.dragObstacleCells = [];
this.lastSafeDragPositions = [];
}
// ============================================
// 重置方块起始位置方法
// 功能:检测方块当前位置是否有效,如果无效则需要回归原位
@ -2992,6 +3184,9 @@ export default class Block extends cc.Component {
// ============================================
update(dt: number) {
if (this.isTouch && this.touchDelta.mag() > 0) {
if (this.dragGroupNodes.length === 0) {
this.prepareDragCollisionState();
}
//this.moveLeft = this.moveRight = this.moveUp = this.moveDown = true;
const delta = this.touchDelta;
const newX = this.node.x + delta.x;
@ -3040,8 +3235,11 @@ export default class Block extends cc.Component {
for (let index = 0; index < mag; index++) {
this.moveCorner = 0;
const previousX = this.node.x;
const previousY = this.node.y;
const tempX = this.node.x + stepx;
const tempY = this.node.y + stepy;
const preferX = Math.abs(stepx) > Math.abs(stepy);
if (!this.checkCollision) {
if (this.type !== 8 && this.type !== 10) {
@ -3056,8 +3254,7 @@ export default class Block extends cc.Component {
}
}
} else {
const isXMain = Math.abs(stepx) > Math.abs(stepy);
if (isXMain) {
if (preferX) {
if (this.node.x > tempX) {
if (this.moveLeft && this.moveX === 0 && this.type !== 8 && this.type !== 10) {
if (this.block_Info.vertical == undefined) {
@ -3139,7 +3336,61 @@ export default class Block extends cc.Component {
}
}
}
// 上面的旧逻辑负责方向、机关和特殊方块限制;这里将结果改为“检测后提交”。
// X/Y分轴处理碰到角落时只截断受阻轴另一轴仍能继续跟随手指滑动。
const requestedX = this.node.x;
const requestedY = this.node.y;
this.node.x = previousX;
this.node.y = previousY;
if (preferX) {
const offsetX = requestedX - previousX;
const safeOffsetX = this.getSafeDragAxisOffset(offsetX, 0);
if (safeOffsetX !== 0) {
this.node.x = previousX + safeOffsetX;
this.moveDragGroupBy(safeOffsetX, 0);
}
const offsetY = requestedY - previousY;
const safeOffsetY = this.getSafeDragAxisOffset(0, offsetY);
if (safeOffsetY !== 0) {
this.node.y = previousY + safeOffsetY;
this.moveDragGroupBy(0, safeOffsetY);
}
} else {
const offsetY = requestedY - previousY;
const safeOffsetY = this.getSafeDragAxisOffset(0, offsetY);
if (safeOffsetY !== 0) {
this.node.y = previousY + safeOffsetY;
this.moveDragGroupBy(0, safeOffsetY);
}
const offsetX = requestedX - previousX;
const safeOffsetX = this.getSafeDragAxisOffset(offsetX, 0);
if (safeOffsetX !== 0) {
this.node.x = previousX + safeOffsetX;
this.moveDragGroupBy(safeOffsetX, 0);
}
}
let movedX = this.node.x - previousX;
let movedY = this.node.y - previousY;
LQCollideSystem.update_logic(dt);
// LQ碰撞在坐标更新后回调。若本步撞到墙/门/机关,只回滚对应轴。
if ((movedX < 0 && !this.moveLeft) || (movedX > 0 && !this.moveRight)) {
this.node.x -= movedX;
this.moveDragGroupBy(-movedX, 0);
movedX = 0;
}
if ((movedY < 0 && !this.moveDown) || (movedY > 0 && !this.moveUp)) {
this.node.y -= movedY;
this.moveDragGroupBy(0, -movedY);
movedY = 0;
}
if ((movedX !== 0 || movedY !== 0) && !this.wouldDragGroupOverlap(0, 0)) {
this.saveLastSafeDragPositions();
}
}
// 移动完成后重置触摸增量
this.touchDelta = cc.v2(0, 0);
@ -3166,7 +3417,6 @@ export default class Block extends cc.Component {
if (this.block_Info.node.getComponent("Block")) {
if (this.isTouch == true && this.block_Info.node.getComponent("Block").isTouch == false) {
// console.log("队友跟着走");
LQCollideSystem.update_logic(dt);
this.block_Info.node.x = this.node.x - this.adhesive.x;
this.block_Info.node.y = this.node.y - this.adhesive.y;
}
@ -3184,7 +3434,6 @@ export default class Block extends cc.Component {
if (this.teamBlocks[i]) {
if (this.teamBlocks[i].getComponent("Block")) {
if (this.teamBlocks[i].uuid != this.node.uuid && this.isTouch == true && this.teamBlocks[i].getComponent("Block").isTouch == false) {
LQCollideSystem.update_logic(dt);
this.teamBlocks[i].x = this.node.x - this.floorOffset[i].x;
this.teamBlocks[i].y = this.node.y - this.floorOffset[i].y;
}

View File

@ -302,7 +302,7 @@ export class GameConfig {
vibrateOpen: true, //震动
coinnum: 0, //每局的金币数
paid_user: false, //是否是付费用户
version: "1.9.92", //版本号
version: "1.9.93", //版本号
shushu_DistinctId: "", //数数访客ID
shushu_AccountId: "", //数数账号ID
uid: "", //用户和后端唯一id