import Target, { TOUCH_ID, Z_INDEX } from './Target';
const { ccclass, property } = cc._decorator;
@ccclass
export default class Drag extends cc.Component {
private static touchId = TOUCH_ID.HIT;
@property(cc.String)
param = '';
@property([cc.Node])
protected targetArray: cc.Node[] = [];
@property(cc.Boolean)
isCopy = false;
protected lastCheckTime = 0;
protected leftMax = 0;
protected rightMax = 0;
protected topMax = 0;
protected bottomMax = 0;
protected readonly diffTime = 100;
private lastCoverTarget: cc.Node | null = null;
private initPos: cc.Vec2 = cc.Vec2.ZERO;
protected override async onLoad() {
await this.waitFitNode();
this.init();
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
protected waitFitNode(): Promise<void> {
return new Promise((resolve) => {
cc.director.once(
cc.Director.EVENT_AFTER_DRAW,
() => {
resolve();
},
this
);
});
}
private init() {
this.node.zIndex = Z_INDEX.DRAG;
this.initPos = this.node.getPosition();
this.leftMax = -this.node.parent.width / 2 + this.node.width / 2;
this.rightMax = this.node.parent.width / 2 - this.node.width / 2;
this.topMax = this.node.parent.height / 2 - this.node.height / 2;
this.bottomMax = -this.node.parent.height / 2 + this.node.height / 2;
}
protected onTouchStart(e: cc.Event.EventTouch) {
if (Drag.touchId !== TOUCH_ID.HIT) {
return;
}
Drag.touchId = e.getID();
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.onDragBegin();
}
protected onTouchMove(e: cc.Event.EventTouch) {
if (Drag.touchId !== e.touch.getID()) {
return;
}
const tempX = this.node.x + e.getDeltaX();
const tempY = this.node.y + e.getDeltaY();
if (
tempX < this.leftMax ||
tempX > this.rightMax ||
tempY < this.bottomMax ||
tempY > this.topMax
) {
return;
}
this.node.x = tempX;
this.node.y = tempY;
const time = new Date().getTime();
if (time - this.lastCheckTime < this.diffTime) {
return;
}
this.onDragCheck();
}
protected onTouchEnd(e: cc.Event.EventTouch) {
if (Drag.touchId !== e.touch.getID()) {
return;
}
Drag.touchId = TOUCH_ID.HIT;
this.node.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.onDragEnd();
}
protected copyOne() {
const node = cc.instantiate(this.node);
node.parent = this.node.parent;
node.position = this.node.position;
node.zIndex = Z_INDEX.DRAG;
const drag = node.getComponent(Drag);
drag.isCopy = true;
drag.param = this.param;
drag.targetArray = this.targetArray;
}
protected onDragBegin() {
if (this.isCopy && this.node.x === this.initPos.x && this.node.y === this.initPos.y) {
this.copyOne();
}
this.node.zIndex = Z_INDEX.DRAG_FLY;
this.targetArray.forEach((target) => {
const targetComponent = target.getComponent(Target);
targetComponent.showTip();
targetComponent.delDrag(this.node);
});
}
protected onDragEnd() {
this.node.zIndex = Z_INDEX.DRAG;
this.targetArray.forEach((target) => {
const targetComponent = target.getComponent(Target);
targetComponent.hideTip();
targetComponent.hideCover();
});
this.lastCoverTarget = null;
const minCoverTarget = this.getMinCoverTarget();
if (minCoverTarget) {
this.onDragHit(minCoverTarget);
} else {
this.onDragMiss();
}
}
protected onDragHit(minCoverTarget: cc.Node) {
minCoverTarget.getComponent(Target).addDrag(this.node);
}
protected onDragMiss() {
this.flyBack();
}
protected onDragCheck() {
if (this.lastCoverTarget) {
this.lastCoverTarget.getComponent(Target).hideCover();
}
const minCoverTarget = this.getMinCoverTarget();
if (minCoverTarget) {
this.lastCoverTarget = minCoverTarget;
minCoverTarget.getComponent(Target).showCover();
}
}
protected getMinCoverTarget(): cc.Node | null {
let minCoverTarget: cc.Node | null = null;
let minDistance: number | null = null;
this.targetArray.forEach((target) => {
const targetRect = target.getBoundingBox();
const nodeRect = this.node.getBoundingBox();
const isCover = targetRect.intersects(nodeRect);
if (isCover) {
const distance = cc.Vec2.distance(target.position, this.node.position);
if (minDistance === null || distance < minDistance) {
minCoverTarget = target;
minDistance = distance;
}
}
});
return minCoverTarget;
}
flyBack() {
Drag.touchId = TOUCH_ID.MISS;
cc.Tween.stopAllByTarget(this.node);
cc.tween(this.node)
.to(0.5, { x: this.initPos.x, y: this.initPos.y })
.call(() => {
Drag.touchId = TOUCH_ID.HIT;
if (this.isCopy) {
this.node.destroy();
}
})
.start();
}
}
import Drag from './Drag';
export const enum Z_INDEX {
TARGET = 5,
DRAG = 10,
DRAG_FLY = 11,
}
export const enum TOUCH_ID {
HIT = -1,
MISS = 100,
}
const { ccclass, property } = cc._decorator;
@ccclass
export default class Target extends cc.Component {
@property(cc.Node)
tipNode: cc.Node = nn<cc.Node>();
@property(cc.Node)
coverNode: cc.Node = nn<cc.Node>();
@property(cc.Node)
pitNodes: cc.Node = nn<cc.Node>();
drags: Array<cc.Node> = [];
private list: Array<{ drag: cc.Node | null; pit: cc.Node }> = [];
protected override onLoad(): void {
this.initNode();
this.hideTip();
this.hideCover();
}
getDragsParam(): string[] {
return this.drags.map((drag) => {
return drag.getComponent(Drag).param;
});
}
protected initNode() {
this.node.zIndex = Z_INDEX.TARGET;
if (!this.pitNodes) {
this.pitNodes = new cc.Node();
this.pitNodes.setPosition(0, 0);
this.node.addChild(this.pitNodes);
}
if (this.pitNodes.children.length === 0) {
const pit = new cc.Node();
pit.setPosition(0, 0);
this.pitNodes.addChild(pit);
}
this.pitNodes.children.forEach((pitNode) => {
const param = {
drag: null,
pit: pitNode,
};
this.list.push(param);
});
}
delDrag(drag: cc.Node) {
this.drags = this.drags.filter((item) => {
return item.uuid !== drag.uuid;
});
this.list.forEach((item) => {
if (item.drag?.uuid === drag.uuid) {
item.drag = null;
}
});
}
addDrag(drag: cc.Node) {
let emptyPit = this.list.find((item) => {
return !item.drag;
});
if (!emptyPit) {
emptyPit = this.list[this.list.length - 1];
emptyPit.drag?.getComponent(Drag)?.flyBack();
emptyPit.drag = null;
}
this.drags.push(drag);
emptyPit.drag = drag;
const pitNode = emptyPit.pit;
const dragNodePos = drag.parent.convertToNodeSpaceAR(
pitNode.parent.convertToWorldSpaceAR(pitNode.getPosition())
);
drag.setPosition(dragNodePos);
}
showTip() {
if (this.tipNode) {
this.tipNode.active = true;
}
}
hideTip() {
if (this.tipNode) {
this.tipNode.active = false;
}
}
showCover() {
if (this.coverNode) {
this.coverNode.active = true;
}
}
hideCover() {
if (this.coverNode) {
this.coverNode.active = false;
}
}
}