mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-27 12:08:21 +00:00
517 lines
15 KiB
JavaScript
517 lines
15 KiB
JavaScript
window.DIRECTION_DECODER = [
|
|
// The 3rd value matches low-precision constants in backend.
|
|
[0, 0],
|
|
[0, +2],
|
|
[0, -2],
|
|
[+2, 0],
|
|
[-2, 0],
|
|
[+1, +1],
|
|
[-1, -1],
|
|
[+1, -1],
|
|
[-1, +1],
|
|
];
|
|
|
|
cc.Class({
|
|
extends: cc.Component,
|
|
properties: {
|
|
// For joystick begins.
|
|
translationListenerNode: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
zoomingListenerNode: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
stickhead: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
base: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
joyStickEps: {
|
|
default: 0.10,
|
|
type: cc.Float
|
|
},
|
|
magicLeanLowerBound: {
|
|
default: 0.1,
|
|
type: cc.Float
|
|
},
|
|
magicLeanUpperBound: {
|
|
default: 0.9,
|
|
type: cc.Float
|
|
},
|
|
// For joystick ends.
|
|
linearScaleFacBase: {
|
|
default: 1.00,
|
|
type: cc.Float
|
|
},
|
|
minScale: {
|
|
default: 1.00,
|
|
type: cc.Float
|
|
},
|
|
maxScale: {
|
|
default: 2.50,
|
|
type: cc.Float
|
|
},
|
|
maxMovingBufferLength: {
|
|
default: 1,
|
|
type: cc.Integer
|
|
},
|
|
zoomingScaleFacBase: {
|
|
default: 0.10,
|
|
type: cc.Float
|
|
},
|
|
zoomingSpeedBase: {
|
|
default: 4.0,
|
|
type: cc.Float
|
|
},
|
|
linearSpeedBase: {
|
|
default: 320.0,
|
|
type: cc.Float
|
|
},
|
|
canvasNode: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
mapNode: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
linearMovingEps: {
|
|
default: 0.10,
|
|
type: cc.Float
|
|
},
|
|
scaleByEps: {
|
|
default: 0.0375,
|
|
type: cc.Float
|
|
},
|
|
btnA: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
btnB: {
|
|
default: null,
|
|
type: cc.Node
|
|
},
|
|
},
|
|
|
|
start() {},
|
|
|
|
onLoad() {
|
|
this.cachedStickHeadPosition = cc.v2(0.0, 0.0);
|
|
this.cachedBtnUpLevel = 0;
|
|
this.cachedBtnDownLevel = 0;
|
|
this.cachedBtnLeftLevel = 0;
|
|
this.cachedBtnRightLevel = 0;
|
|
|
|
this.realtimeBtnALevel = 0;
|
|
this.cachedBtnALevel = 0;
|
|
this.btnAEdgeTriggerLock = false;
|
|
this.realtimeBtnBLevel = 0;
|
|
this.cachedBtnBLevel = 0;
|
|
this.btnBEdgeTriggerLock = false;
|
|
|
|
this.canvasNode = this.mapNode.parent;
|
|
this.mainCameraNode = this.canvasNode.getChildByName("Main Camera"); // Cannot drag and assign the `mainCameraNode` from CocosCreator EDITOR directly, otherwise it'll cause an infinite loading time, till v2.1.0.
|
|
this.mainCamera = this.mainCameraNode.getComponent(cc.Camera);
|
|
this.activeDirection = {
|
|
dx: 0.0,
|
|
dy: 0.0
|
|
};
|
|
this.maxHeadDistance = (0.5 * this.base.width);
|
|
|
|
this._initTouchEvent();
|
|
this._cachedMapNodePosTarget = [];
|
|
this._cachedZoomRawTarget = null;
|
|
|
|
this.mapScriptIns = this.mapNode.getComponent("Map");
|
|
this.initialized = true;
|
|
},
|
|
|
|
_initTouchEvent() {
|
|
const self = this;
|
|
const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode);
|
|
const zoomingListenerNode = (self.zoomingListenerNode ? self.zoomingListenerNode : self.mapNode);
|
|
|
|
translationListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) {
|
|
self._touchStartEvent(event);
|
|
});
|
|
translationListenerNode.on(cc.Node.EventType.TOUCH_MOVE, function(event) {
|
|
self._translationEvent(event);
|
|
});
|
|
translationListenerNode.on(cc.Node.EventType.TOUCH_END, function(event) {
|
|
self._touchEndEvent(event);
|
|
});
|
|
translationListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) {
|
|
self._touchEndEvent(event);
|
|
});
|
|
translationListenerNode.inTouchPoints = new Map();
|
|
|
|
/*
|
|
zoomingListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) {
|
|
self._touchStartEvent(event);
|
|
});
|
|
zoomingListenerNode.on(cc.Node.EventType.TOUCH_MOVE, function(event) {
|
|
self._zoomingEvent(event);
|
|
});
|
|
zoomingListenerNode.on(cc.Node.EventType.TOUCH_END, function(event) {
|
|
self._touchEndEvent(event);
|
|
});
|
|
zoomingListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) {
|
|
self._touchEndEvent(event);
|
|
});
|
|
zoomingListenerNode.inTouchPoints = new Map();
|
|
*/
|
|
|
|
if (self.btnA) {
|
|
self.btnA.on(cc.Node.EventType.TOUCH_START, function(evt) {
|
|
self._triggerEdgeBtnA(true);
|
|
});
|
|
self.btnA.on(cc.Node.EventType.TOUCH_END, function(evt) {
|
|
self._triggerEdgeBtnA(false);
|
|
});
|
|
self.btnA.on(cc.Node.EventType.TOUCH_CANCEL, function(evt) {
|
|
self._triggerEdgeBtnA(false);
|
|
});
|
|
}
|
|
|
|
if (self.btnB) {
|
|
self.btnB.on(cc.Node.EventType.TOUCH_START, function(evt) {
|
|
self._triggerEdgeBtnB(true);
|
|
});
|
|
self.btnB.on(cc.Node.EventType.TOUCH_END, function(evt) {
|
|
self._triggerEdgeBtnB(false);
|
|
});
|
|
self.btnB.on(cc.Node.EventType.TOUCH_CANCEL, function(evt) {
|
|
self._triggerEdgeBtnB(false);
|
|
});
|
|
}
|
|
|
|
// Setup keyboard controls for the ease of attach debugging
|
|
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, function(evt) {
|
|
switch (evt.keyCode) {
|
|
case cc.macro.KEY.w:
|
|
self.cachedBtnUpLevel = 0;
|
|
self.cachedBtnDownLevel = 0;
|
|
self.cachedBtnLeftLevel = 0;
|
|
self.cachedBtnRightLevel = 0;
|
|
self.cachedBtnUpLevel = 1;
|
|
break;
|
|
case cc.macro.KEY.s:
|
|
self.cachedBtnUpLevel = 0;
|
|
self.cachedBtnDownLevel = 0;
|
|
self.cachedBtnLeftLevel = 0;
|
|
self.cachedBtnRightLevel = 0;
|
|
self.cachedBtnDownLevel = 1;
|
|
break;
|
|
case cc.macro.KEY.a:
|
|
self.cachedBtnUpLevel = 0;
|
|
self.cachedBtnDownLevel = 0;
|
|
self.cachedBtnLeftLevel = 0;
|
|
self.cachedBtnRightLevel = 0;
|
|
self.cachedBtnLeftLevel = 1;
|
|
break;
|
|
case cc.macro.KEY.d:
|
|
self.cachedBtnUpLevel = 0;
|
|
self.cachedBtnDownLevel = 0;
|
|
self.cachedBtnLeftLevel = 0;
|
|
self.cachedBtnRightLevel = 0;
|
|
self.cachedBtnRightLevel = 1;
|
|
break;
|
|
case cc.macro.KEY.h:
|
|
self._triggerEdgeBtnA(true);
|
|
break;
|
|
case cc.macro.KEY.j:
|
|
self._triggerEdgeBtnB(true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}, this);
|
|
|
|
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, function(evt) {
|
|
switch (evt.keyCode) {
|
|
case cc.macro.KEY.w:
|
|
self.cachedBtnUpLevel = 0;
|
|
break;
|
|
case cc.macro.KEY.s:
|
|
self.cachedBtnDownLevel = 0;
|
|
break;
|
|
case cc.macro.KEY.a:
|
|
self.cachedBtnLeftLevel = 0;
|
|
break;
|
|
case cc.macro.KEY.d:
|
|
self.cachedBtnRightLevel = 0;
|
|
break;
|
|
case cc.macro.KEY.h:
|
|
self._triggerEdgeBtnA(false);
|
|
break;
|
|
case cc.macro.KEY.j:
|
|
self._triggerEdgeBtnB(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
_isMapOverMoved(mapTargetPos) {
|
|
const virtualPlayerPos = cc.v2(-mapTargetPos.x, -mapTargetPos.y);
|
|
return tileCollisionManager.isOutOfMapNode(this.mapNode, virtualPlayerPos);
|
|
},
|
|
|
|
_touchStartEvent(event) {
|
|
const theListenerNode = event.target;
|
|
for (let touch of event._touches) {
|
|
theListenerNode.inTouchPoints.set(touch._id, touch);
|
|
}
|
|
},
|
|
|
|
_translationEvent(event) {
|
|
if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) {
|
|
return;
|
|
}
|
|
const theListenerNode = event.target;
|
|
const linearScaleFacBase = this.linearScaleFacBase; // Not used yet.
|
|
if (1 != theListenerNode.inTouchPoints.size) {
|
|
return;
|
|
}
|
|
if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) {
|
|
return;
|
|
}
|
|
const diffVec = event.currentTouch._point.sub(event.currentTouch._startPoint);
|
|
const distance = diffVec.mag();
|
|
const overMoved = (distance > this.maxHeadDistance);
|
|
if (overMoved) {
|
|
const ratio = (this.maxHeadDistance / distance);
|
|
this.cachedStickHeadPosition = diffVec.mul(ratio);
|
|
} else {
|
|
const ratio = (distance / this.maxHeadDistance);
|
|
this.cachedStickHeadPosition = diffVec.mul(ratio);
|
|
}
|
|
},
|
|
|
|
_zoomingEvent(event) {
|
|
if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) {
|
|
return;
|
|
}
|
|
const theListenerNode = event.target;
|
|
if (2 != theListenerNode.inTouchPoints.size) {
|
|
return;
|
|
}
|
|
if (2 == event._touches.length) {
|
|
const firstTouch = event._touches[0];
|
|
const secondTouch = event._touches[1];
|
|
|
|
const startMagnitude = firstTouch._startPoint.sub(secondTouch._startPoint).mag();
|
|
const currentMagnitude = firstTouch._point.sub(secondTouch._point).mag();
|
|
|
|
let scaleBy = (currentMagnitude / startMagnitude);
|
|
scaleBy = 1 + (scaleBy - 1) * this.zoomingScaleFacBase;
|
|
if (1 < scaleBy && Math.abs(scaleBy - 1) < this.scaleByEps) {
|
|
// Jitterring.
|
|
cc.log(`ScaleBy == ${scaleBy} is just jittering.`);
|
|
return;
|
|
}
|
|
if (1 > scaleBy && Math.abs(scaleBy - 1) < 0.5 * this.scaleByEps) {
|
|
// Jitterring.
|
|
cc.log(`ScaleBy == ${scaleBy} is just jittering.`);
|
|
return;
|
|
}
|
|
if (!this.mainCamera) return;
|
|
const targetScale = this.mainCamera.zoomRatio * scaleBy;
|
|
if (this.minScale > targetScale || targetScale > this.maxScale) {
|
|
return;
|
|
}
|
|
this.mainCamera.zoomRatio = targetScale;
|
|
for (let child of this.mainCameraNode.children) {
|
|
child.setScale(1 / targetScale);
|
|
}
|
|
}
|
|
},
|
|
|
|
_touchEndEvent(event) {
|
|
const theListenerNode = event.target;
|
|
do {
|
|
if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) {
|
|
break;
|
|
}
|
|
const diffVec = event.currentTouch._point.sub(event.currentTouch._startPoint);
|
|
const diffVecMag = diffVec.mag();
|
|
if (this.linearMovingEps <= diffVecMag) {
|
|
break;
|
|
}
|
|
// Only triggers map-state-switch when `diffVecMag` is sufficiently small.
|
|
|
|
if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) {
|
|
break;
|
|
}
|
|
|
|
// TODO: Handle single-finger-click event.
|
|
} while (false);
|
|
for (let touch of event._touches) {
|
|
if (touch) {
|
|
theListenerNode.inTouchPoints.delete(touch._id);
|
|
}
|
|
}
|
|
},
|
|
|
|
_touchCancelEvent(event) {},
|
|
|
|
update(dt) {
|
|
if (this.inMultiTouch) return;
|
|
if (true != this.initialized) return;
|
|
const self = this;
|
|
// Keyboard takes top priority
|
|
let keyboardDiffVec = cc.v2(0, 0);
|
|
if (1 == this.cachedBtnUpLevel) {
|
|
if (1 == this.cachedBtnLeftLevel) {
|
|
keyboardDiffVec = cc.v2(-1.0, +1.0);
|
|
} else if (1 == this.cachedBtnRightLevel) {
|
|
keyboardDiffVec = cc.v2(+1.0, +1.0);
|
|
} else {
|
|
keyboardDiffVec = cc.v2(0.0, +1.0);
|
|
}
|
|
} else if (1 == this.cachedBtnDownLevel) {
|
|
if (1 == this.cachedBtnLeftLevel) {
|
|
keyboardDiffVec = cc.v2(-1.0, -1.0);
|
|
} else if (1 == this.cachedBtnRightLevel) {
|
|
keyboardDiffVec = cc.v2(+1.0, -1.0);
|
|
} else {
|
|
keyboardDiffVec = cc.v2(0.0, -1.0);
|
|
}
|
|
} else if (1 == this.cachedBtnLeftLevel) {
|
|
keyboardDiffVec = cc.v2(-1.0, 0.0);
|
|
} else if (1 == this.cachedBtnRightLevel) {
|
|
keyboardDiffVec = cc.v2(+1.0, 0.0);
|
|
}
|
|
if (0 != keyboardDiffVec.x || 0 != keyboardDiffVec.y) {
|
|
this.cachedStickHeadPosition = keyboardDiffVec.mul(this.maxHeadDistance / keyboardDiffVec.mag());
|
|
}
|
|
this.stickhead.setPosition(this.cachedStickHeadPosition);
|
|
|
|
const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode);
|
|
if (0 == translationListenerNode.inTouchPoints.size
|
|
&&
|
|
(0 == keyboardDiffVec.x && 0 == keyboardDiffVec.y)
|
|
) {
|
|
this.cachedStickHeadPosition = cc.v2(0, 0); // Important reset!
|
|
}
|
|
},
|
|
|
|
discretizeDirection(continuousDx, continuousDy, eps) {
|
|
let ret = {
|
|
dx: 0,
|
|
dy: 0,
|
|
encodedIdx: 0
|
|
};
|
|
if (Math.abs(continuousDx) < eps && Math.abs(continuousDy) < eps) {
|
|
return ret;
|
|
}
|
|
|
|
const criticalRatio = continuousDy / continuousDx;
|
|
if (Math.abs(criticalRatio) < this.magicLeanLowerBound) {
|
|
ret.dy = 0;
|
|
if (0 < continuousDx) {
|
|
ret.dx = +2; // right
|
|
ret.encodedIdx = 3;
|
|
} else {
|
|
ret.dx = -2; // left
|
|
ret.encodedIdx = 4;
|
|
}
|
|
} else if (Math.abs(criticalRatio) > this.magicLeanUpperBound) {
|
|
ret.dx = 0;
|
|
if (0 < continuousDy) {
|
|
ret.dy = +2; // up
|
|
ret.encodedIdx = 1;
|
|
} else {
|
|
ret.dy = -2; // down
|
|
ret.encodedIdx = 2;
|
|
}
|
|
} else {
|
|
if (0 < continuousDx) {
|
|
if (0 < continuousDy) {
|
|
ret.dx = +1;
|
|
ret.dy = +1;
|
|
ret.encodedIdx = 5;
|
|
} else {
|
|
ret.dx = +1;
|
|
ret.dy = -1;
|
|
ret.encodedIdx = 7;
|
|
}
|
|
} else {
|
|
// 0 >= continuousDx
|
|
if (0 < continuousDy) {
|
|
ret.dx = -1;
|
|
ret.dy = +1;
|
|
ret.encodedIdx = 8;
|
|
} else {
|
|
ret.dx = -1;
|
|
ret.dy = -1;
|
|
ret.encodedIdx = 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
|
|
getEncodedInput() {
|
|
const discretizedDir = this.discretizeDirection(this.stickhead.x, this.stickhead.y, this.joyStickEps).encodedIdx; // There're only 9 dirs, thus using only the lower 4-bits
|
|
const btnALevel = (this.cachedBtnALevel << 4);
|
|
const btnBLevel = (this.cachedBtnBLevel << 5);
|
|
|
|
this.cachedBtnALevel = this.realtimeBtnALevel;
|
|
this.cachedBtnBLevel = this.realtimeBtnBLevel;
|
|
|
|
this.btnAEdgeTriggerLock = false;
|
|
this.btnBEdgeTriggerLock = false;
|
|
return (btnBLevel + btnALevel + discretizedDir);
|
|
},
|
|
|
|
decodeInput(encodedInput) {
|
|
const encodedDirection = (encodedInput & 15);
|
|
const mappedDirection = window.DIRECTION_DECODER[encodedDirection];
|
|
if (null == mappedDirection) {
|
|
console.error("Unexpected encodedDirection = ", encodedDirection);
|
|
}
|
|
const btnALevel = ((encodedInput >> 4) & 1);
|
|
const btnBLevel = ((encodedInput >> 5) & 1);
|
|
return window.pb.protos.InputFrameDecoded.create({
|
|
dx: mappedDirection[0],
|
|
dy: mappedDirection[1],
|
|
btnALevel: btnALevel,
|
|
btnBLevel: btnBLevel,
|
|
});
|
|
},
|
|
|
|
_triggerEdgeBtnA(rising) {
|
|
this.realtimeBtnALevel = (rising ? 1 : 0);
|
|
if (!this.btnAEdgeTriggerLock && (1 - this.realtimeBtnALevel) == this.cachedBtnALevel) {
|
|
this.cachedBtnALevel = this.realtimeBtnALevel;
|
|
this.btnAEdgeTriggerLock = true;
|
|
}
|
|
if (rising) {
|
|
this.btnA.runAction(cc.scaleTo(0.1, 0.3));
|
|
} else {
|
|
this.btnA.runAction(cc.scaleTo(0.1, 0.5));
|
|
}
|
|
},
|
|
|
|
_triggerEdgeBtnB(rising, evt) {
|
|
this.realtimeBtnBLevel = (rising ? 1 : 0);
|
|
if (!this.btnBEdgeTriggerLock && (1 - this.realtimeBtnBLevel) == this.cachedBtnBLevel) {
|
|
this.cachedBtnBLevel = this.realtimeBtnBLevel;
|
|
this.btnBEdgeTriggerLock = true;
|
|
}
|
|
if (rising) {
|
|
this.btnB.runAction(cc.scaleTo(0.1, 0.3));
|
|
} else {
|
|
this.btnB.runAction(cc.scaleTo(0.1, 0.5));
|
|
}
|
|
},
|
|
});
|