Improvements on using integer positioning.

This commit is contained in:
genxium 2022-11-10 18:18:00 +08:00
parent e5ed8124e8
commit 901b189c5a
5 changed files with 128 additions and 115 deletions

View File

@ -60,36 +60,17 @@ const (
MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED = -2 MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED = -2
) )
// These directions are chosen such that when speed is changed to "(speedX+delta, speedY+delta)" for any of them, the direction is unchanged.
var DIRECTION_DECODER = [][]int32{ var DIRECTION_DECODER = [][]int32{
{0, 0}, {0, 0},
{0, +1}, {0, +2},
{0, -1}, {0, -2},
{+2, 0}, {+2, 0},
{-2, 0}, {-2, 0},
{+2, +1}, {+1, +1},
{-2, -1}, {-1, -1},
{+2, -1}, {+1, -1},
{-2, +1}, {-1, +1},
{+2, 0},
{-2, 0},
{0, +1},
{0, -1},
}
var DIRECTION_DECODER_INVERSE_LENGTH = []float64{
0.0,
1.0,
1.0,
0.5,
0.5,
0.44, // Actually it should be "0.4472", but truncated for better precision sync as well as a reduction of speed in diagonal direction
0.44,
0.44,
0.44,
0.5,
0.5,
1.0,
1.0,
} }
type RoomBattleState struct { type RoomBattleState struct {
@ -802,7 +783,7 @@ func (pR *Room) OnDismissed() {
// Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference. // Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference.
pR.WorldToVirtualGridRatio = float64(10) pR.WorldToVirtualGridRatio = float64(10)
pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations
pR.PlayerDefaultSpeed = int32(3 * pR.WorldToVirtualGridRatio) // Hardcoded in virtual grids per frame pR.PlayerDefaultSpeed = 10 // Hardcoded in virtual grids per frame
pR.Players = make(map[int32]*Player) pR.Players = make(map[int32]*Player)
pR.PlayersArr = make([]*Player, pR.Capacity) pR.PlayersArr = make([]*Player, pR.Capacity)
pR.CollisionSysMap = make(map[int32]*resolv.Object) pR.CollisionSysMap = make(map[int32]*resolv.Object)
@ -1209,24 +1190,37 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
inputList := delayedInputFrame.InputList inputList := delayedInputFrame.InputList
// Ordered by joinIndex to guarantee determinism // Ordered by joinIndex to guarantee determinism
// Move players according to inputs
for _, player := range pR.PlayersArr { for _, player := range pR.PlayersArr {
joinIndex := player.JoinIndex joinIndex := player.JoinIndex
encodedInput := inputList[joinIndex-1] encodedInput := inputList[joinIndex-1]
decodedInput := DIRECTION_DECODER[encodedInput] decodedInput := DIRECTION_DECODER[encodedInput]
decodedInputSpeedFactor := DIRECTION_DECODER_INVERSE_LENGTH[encodedInput] player.Dir.Dx = decodedInput[0]
if 0.0 == decodedInputSpeedFactor { player.Dir.Dy = decodedInput[1]
if 0 == decodedInput[0] && 0 == decodedInput[1] {
continue continue
} }
baseChange := float64(player.Speed) * pR.VirtualGridToWorldRatio * decodedInputSpeedFactor
oldDx, oldDy := baseChange*float64(decodedInput[0]), baseChange*float64(decodedInput[1])
dx, dy := oldDx, oldDy
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := pR.CollisionSysMap[collisionPlayerIndex] playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
// Reset playerCollider position from the "virtual grid position"
playerCollider.X, playerCollider.Y = pR.virtualGridToPlayerColliderPos(player.VirtualGridX, player.VirtualGridY, player)
if collision := playerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil { // Reset playerCollider position from the "virtual grid position"
newVx := (player.VirtualGridX + (decodedInput[0] + decodedInput[0]*player.Speed))
newVy := (player.VirtualGridY + (decodedInput[1] + decodedInput[1]*player.Speed))
playerCollider.X, playerCollider.Y = pR.virtualGridToPlayerColliderPos(newVx, newVy, player)
// Update in "collision space"
playerCollider.Update()
}
// handle pushbacks upon collision
for _, player := range pR.PlayersArr {
joinIndex := player.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
oldDx, oldDy := float64(0), float64(0)
dx, dy := oldDx, oldDy
if collision := playerCollider.Check(oldDx, oldDy); collision != nil {
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon) playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
for _, obj := range collision.Objects { for _, obj := range collision.Objects {
barrierShape := obj.Shape.(*resolv.ConvexPolygon) barrierShape := obj.Shape.(*resolv.ConvexPolygon)
@ -1242,16 +1236,15 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
playerCollider.X += dx playerCollider.X += dx
playerCollider.Y += dy playerCollider.Y += dy
// Update in "collision space" // Update again in "collision space"
playerCollider.Update() playerCollider.Update()
player.Dir.Dx = decodedInput[0] // Update "virtual grid position"
player.Dir.Dy = decodedInput[1]
player.VirtualGridX, player.VirtualGridY = pR.playerColliderAnchorToVirtualGridPos(playerCollider.X, playerCollider.Y, player) player.VirtualGridX, player.VirtualGridY = pR.playerColliderAnchorToVirtualGridPos(playerCollider.X, playerCollider.Y, player)
} }
} }
pbPlayers := toPbPlayers(pR.Players) pbPlayers := toPbPlayers(pR.Players)
newRenderFrame := RoomDownsyncFrame{ newRenderFrame := RoomDownsyncFrame{
Id: collisionSysRenderFrameId + 1, Id: collisionSysRenderFrameId + 1,
@ -1295,13 +1288,15 @@ func (pR *Room) printBarrier(barrierCollider *resolv.Object) {
} }
func (pR *Room) worldToVirtualGridPos(wx, wy float64) (int32, int32) { func (pR *Room) worldToVirtualGridPos(wx, wy float64) (int32, int32) {
// [WARNING] Introduces loss of precision!
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains. // In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
var virtualGridX int32 = int32(wx * pR.WorldToVirtualGridRatio) var virtualGridX int32 = int32(math.Round(wx * pR.WorldToVirtualGridRatio))
var virtualGridY int32 = int32(wy * pR.WorldToVirtualGridRatio) var virtualGridY int32 = int32(math.Round(wy * pR.WorldToVirtualGridRatio))
return virtualGridX, virtualGridY return virtualGridX, virtualGridY
} }
func (pR *Room) virtualGridToWorldPos(vx, vy int32) (float64, float64) { func (pR *Room) virtualGridToWorldPos(vx, vy int32) (float64, float64) {
// No loss of precision
var wx float64 = float64(vx) * pR.VirtualGridToWorldRatio var wx float64 = float64(vx) * pR.VirtualGridToWorldRatio
var wy float64 = float64(vy) * pR.VirtualGridToWorldRatio var wy float64 = float64(vy) * pR.VirtualGridToWorldRatio
return wx, wy return wx, wy

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
216.67592045656244, 378.4531014537997,
0, 0,
0, 0,
0, 0,

View File

@ -44,14 +44,14 @@ module.export = cc.Class({
onLoad() { onLoad() {
const self = this; const self = this;
self.clips = { self.clips = {
'01': 'Top', '02': 'Top',
'0-1': 'Bottom', '0-2': 'Bottom',
'-20': 'Left', '-20': 'Left',
'20': 'Right', '20': 'Right',
'-21': 'TopLeft', '-11': 'TopLeft',
'21': 'TopRight', '11': 'TopRight',
'-2-1': 'BottomLeft', '-1-1': 'BottomLeft',
'2-1': 'BottomRight' '1-1': 'BottomRight'
}; };
const canvasNode = self.mapNode.parent; const canvasNode = self.mapNode.parent;
self.mapIns = self.mapNode.getComponent("Map"); self.mapIns = self.mapNode.getComponent("Map");

View File

@ -111,7 +111,7 @@ cc.Class({
type: cc.Integer, type: cc.Integer,
default: 4 // implies (renderFrameIdLagTolerance >> inputScaleFrames) count of inputFrameIds default: 4 // implies (renderFrameIdLagTolerance >> inputScaleFrames) count of inputFrameIds
}, },
teleportEps1D: { jigglingEps1D: {
type: cc.Float, type: cc.Float,
default: 1e-3 default: 1e-3
}, },
@ -749,7 +749,8 @@ cc.Class({
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1])); newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
newPlayerNode.getComponent("SelfPlayer").mapNode = self.node; newPlayerNode.getComponent("SelfPlayer").mapNode = self.node;
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo); const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo);
const d = playerRichInfo.colliderRadius*2, x0 = cpos[0], const d = playerRichInfo.colliderRadius * 2,
x0 = cpos[0],
y0 = cpos[1]; y0 = cpos[1];
let pts = [[0, 0], [d, 0], [d, d], [0, d]]; let pts = [[0, 0], [d, 0], [d, d], [0, d]];
@ -990,6 +991,23 @@ cc.Class({
rdf.id > self.lastAllConfirmedRenderFrameId rdf.id > self.lastAllConfirmedRenderFrameId
) { ) {
// We got a more up-to-date "all-confirmed-render-frame". // We got a more up-to-date "all-confirmed-render-frame".
let predictedRdf = self.recentRenderCache.getByFrameId(rdf.id);
if (null != predictedRdf) {
let renderFrameCorrectlyPredicted = true;
for (let playerId in predictedRdf.players) {
const predictedPlayer = predictedRdf.players[playerId];
const confirmedPlayer = rdf.players[playerId];
if (predictedPlayer.virtualGridX != confirmedPlayer.virtualGridX || predictedPlayer.virtualGridY != confirmedPlayer.virtualGridY) {
renderFrameCorrectlyPredicted = false;
break;
}
}
if (!renderFrameCorrectlyPredicted) {
// TODO: Can I also check whether the applied inputFrame on predictedRdf was "correctly predicted"? If it wasn't then a mismatch of positions is expected.
console.warn("render frame was incorrectly predicted\npredictedRdf=" + predictedRdf.toString() + "\nrdf=" + rdf.toString());
}
}
self.lastAllConfirmedRenderFrameId = rdf.id; self.lastAllConfirmedRenderFrameId = rdf.id;
if (rdf.id > self.chaserRenderFrameId) { if (rdf.id > self.chaserRenderFrameId) {
// it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId" // it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId"
@ -1008,13 +1026,14 @@ cc.Class({
const wpos = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY); const wpos = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY);
const dx = (wpos[0] - playerRichInfo.node.x); const dx = (wpos[0] - playerRichInfo.node.x);
const dy = (wpos[1] - playerRichInfo.node.y); const dy = (wpos[1] - playerRichInfo.node.y);
const justJiggling = (self.teleportEps1D >= Math.abs(dx) && self.teleportEps1D >= Math.abs(dy)); const justJiggling = (self.jigglingEps1D >= Math.abs(dx) && self.jigglingEps1D >= Math.abs(dy));
if (!justJiggling) { if (!justJiggling) {
console.log("@renderFrameId=" + self.renderFrameId + ", teleporting playerId=" + playerId + ": '(" + playerRichInfo.node.x + ", " + playerRichInfo.node.y, ")' to '(" + wpos[0] + ", " + wpos[1] + ")'");
playerRichInfo.node.setPosition(wpos[0], wpos[1]); playerRichInfo.node.setPosition(wpos[0], wpos[1]);
playerRichInfo.virtualGridX = immediatePlayerInfo.virtualGridX;
playerRichInfo.virtualGridY = immediatePlayerInfo.virtualGridY;
playerRichInfo.scriptIns.scheduleNewDirection(immediatePlayerInfo.dir, false);
playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed);
} }
playerRichInfo.scriptIns.scheduleNewDirection(immediatePlayerInfo.dir, false);
playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed);
}); });
}, },
@ -1062,18 +1081,20 @@ cc.Class({
const playerCollider = collisionSysMap.get(collisionPlayerIndex); const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = renderFrame.players[playerId]; const player = renderFrame.players[playerId];
const encodedInput = inputList[joinIndex - 1];
const decodedInput = self.ctrl.decodeDirection(encodedInput);
if (0 == decodedInput.dx && 0 == decodedInput.dy) {
continue;
}
/* /*
Reset "position" of players in "collisionSys" according to "virtual grid position". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics. Reset "position" of players in "collisionSys" according to "virtual grid position". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics.
*/ */
const cpos = self.virtualGridToPlayerColliderPos(player.virtualGridX, player.virtualGridY, self.playerRichInfoArr[j]); const newVx = player.virtualGridX + (decodedInput.dx + player.speed * decodedInput.dx);
playerCollider.x = cpos[0]; const newVy = player.virtualGridY + (decodedInput.dy + player.speed * decodedInput.dy);
playerCollider.y = cpos[1]; const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[j]);
playerCollider.x = newCpos[0];
const encodedInput = inputList[joinIndex - 1]; playerCollider.y = newCpos[1];
const decodedInput = self.ctrl.decodeDirection(encodedInput);
const baseChange = player.speed * self.virtualGridToWorldRatio * decodedInput.speedFactor;
playerCollider.x += baseChange * decodedInput.dx;
playerCollider.y += baseChange * decodedInput.dy;
} }
collisionSys.update(); collisionSys.update();
@ -1154,14 +1175,16 @@ cc.Class({
}, },
worldToVirtualGridPos(x, y) { worldToVirtualGridPos(x, y) {
// [WARNING] Introduces loss of precision!
const self = this; const self = this;
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains. // In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
let virtualGridX = parseInt(x * self.worldToVirtualGridRatio); let virtualGridX = Math.round(x * self.worldToVirtualGridRatio);
let virtualGridY = parseInt(y * self.worldToVirtualGridRatio); let virtualGridY = Math.round(y * self.worldToVirtualGridRatio);
return [virtualGridX, virtualGridY]; return [virtualGridX, virtualGridY];
}, },
virtualGridToWorldPos(vx, vy) { virtualGridToWorldPos(vx, vy) {
// No loss of precision
const self = this; const self = this;
let wx = parseFloat(vx) * self.virtualGridToWorldRatio; let wx = parseFloat(vx) * self.virtualGridToWorldRatio;
let wy = parseFloat(vy) * self.virtualGridToWorldRatio; let wy = parseFloat(vy) * self.virtualGridToWorldRatio;

View File

@ -1,18 +1,14 @@
window.DIRECTION_DECODER = [ window.DIRECTION_DECODER = [
// The 3rd value matches low-precision constants in backend. // The 3rd value matches low-precision constants in backend.
[0, 0, 0.0], [0, 0],
[0, +1, 1.0], [0, +2],
[0, -1, 1.0], [0, -2],
[+2, 0, 0.5], [+2, 0],
[-2, 0, 0.5], [-2, 0],
[+2, +1, 0.44], [+1, +1],
[-2, -1, 0.44], [-1, -1],
[+2, -1, 0.44], [+1, -1],
[-2, +1, 0.44], [-1, +1],
[+2, 0, 0.5],
[-2, 0, 0.5],
[0, +1, 1.0],
[0, -1, 1.0],
]; ];
cc.Class({ cc.Class({
@ -40,11 +36,11 @@ cc.Class({
type: cc.Float type: cc.Float
}, },
magicLeanLowerBound: { magicLeanLowerBound: {
default: 0.414, // Tangent of (PI/8). default: 0.9, // Tangent of (PI/4) is 1.0.
type: cc.Float type: cc.Float
}, },
magicLeanUpperBound: { magicLeanUpperBound: {
default: 2.414, // Tangent of (3*PI/8). default: 1.1,
type: cc.Float type: cc.Float
}, },
// For joystick ends. // For joystick ends.
@ -117,8 +113,8 @@ cc.Class({
_initTouchEvent() { _initTouchEvent() {
const self = this; const self = this;
const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode); const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode);
const zoomingListenerNode = (self.zoomingListenerNode ? self.zoomingListenerNode : self.mapNode); const zoomingListenerNode = (self.zoomingListenerNode ? self.zoomingListenerNode : self.mapNode);
translationListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) { translationListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) {
self._touchStartEvent(event); self._touchStartEvent(event);
@ -132,7 +128,7 @@ cc.Class({
translationListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) { translationListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) {
self._touchEndEvent(event); self._touchEndEvent(event);
}); });
translationListenerNode.inTouchPoints = new Map(); translationListenerNode.inTouchPoints = new Map();
zoomingListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) { zoomingListenerNode.on(cc.Node.EventType.TOUCH_START, function(event) {
self._touchStartEvent(event); self._touchStartEvent(event);
@ -146,7 +142,7 @@ cc.Class({
zoomingListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) { zoomingListenerNode.on(cc.Node.EventType.TOUCH_CANCEL, function(event) {
self._touchEndEvent(event); self._touchEndEvent(event);
}); });
zoomingListenerNode.inTouchPoints = new Map(); zoomingListenerNode.inTouchPoints = new Map();
}, },
_isMapOverMoved(mapTargetPos) { _isMapOverMoved(mapTargetPos) {
@ -155,7 +151,7 @@ cc.Class({
}, },
_touchStartEvent(event) { _touchStartEvent(event) {
const theListenerNode = event.target; const theListenerNode = event.target;
for (let touch of event._touches) { for (let touch of event._touches) {
theListenerNode.inTouchPoints.set(touch._id, touch); theListenerNode.inTouchPoints.set(touch._id, touch);
} }
@ -165,12 +161,12 @@ cc.Class({
if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) { if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) {
return; return;
} }
const theListenerNode = event.target; const theListenerNode = event.target;
const linearScaleFacBase = this.linearScaleFacBase; // Not used yet. const linearScaleFacBase = this.linearScaleFacBase; // Not used yet.
if (1 != theListenerNode.inTouchPoints.size) { if (1 != theListenerNode.inTouchPoints.size) {
return; return;
} }
if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) { if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) {
return; return;
} }
const diffVec = event.currentTouch._point.sub(event.currentTouch._startPoint); const diffVec = event.currentTouch._point.sub(event.currentTouch._startPoint);
@ -189,9 +185,9 @@ cc.Class({
if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) { if (ALL_MAP_STATES.VISUAL != this.mapScriptIns.state) {
return; return;
} }
const theListenerNode = event.target; const theListenerNode = event.target;
if (2 != theListenerNode.inTouchPoints.size) { if (2 != theListenerNode.inTouchPoints.size) {
return; return;
} }
if (2 == event._touches.length) { if (2 == event._touches.length) {
const firstTouch = event._touches[0]; const firstTouch = event._touches[0];
@ -219,13 +215,13 @@ cc.Class({
} }
this.mainCamera.zoomRatio = targetScale; this.mainCamera.zoomRatio = targetScale;
for (let child of this.mainCameraNode.children) { for (let child of this.mainCameraNode.children) {
child.setScale(1/targetScale); child.setScale(1 / targetScale);
} }
} }
}, },
_touchEndEvent(event) { _touchEndEvent(event) {
const theListenerNode = event.target; const theListenerNode = event.target;
do { do {
if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) { if (!theListenerNode.inTouchPoints.has(event.currentTouch._id)) {
break; break;
@ -241,7 +237,7 @@ cc.Class({
break; break;
} }
// TODO: Handle single-finger-click event. // TODO: Handle single-finger-click event.
} while (false); } while (false);
this.cachedStickHeadPosition = cc.v2(0.0, 0.0); this.cachedStickHeadPosition = cc.v2(0.0, 0.0);
for (let touch of event._touches) { for (let touch of event._touches) {
@ -266,16 +262,16 @@ cc.Class({
encodedIdx: 0 encodedIdx: 0
}; };
if (Math.abs(continuousDx) < eps && Math.abs(continuousDy) < eps) { if (Math.abs(continuousDx) < eps && Math.abs(continuousDy) < eps) {
return ret; return ret;
} }
if (Math.abs(continuousDx) < eps) { if (Math.abs(continuousDx) < eps) {
ret.dx = 0; ret.dx = 0;
if (0 < continuousDy) { if (0 < continuousDy) {
ret.dy = +1; // up ret.dy = +2; // up
ret.encodedIdx = 1; ret.encodedIdx = 1;
} else { } else {
ret.dy = -1; // down ret.dy = -2; // down
ret.encodedIdx = 2; ret.encodedIdx = 2;
} }
} else if (Math.abs(continuousDy) < eps) { } else if (Math.abs(continuousDy) < eps) {
@ -291,42 +287,42 @@ cc.Class({
const criticalRatio = continuousDy / continuousDx; const criticalRatio = continuousDy / continuousDx;
if (criticalRatio > this.magicLeanLowerBound && criticalRatio < this.magicLeanUpperBound) { if (criticalRatio > this.magicLeanLowerBound && criticalRatio < this.magicLeanUpperBound) {
if (0 < continuousDx) { if (0 < continuousDx) {
ret.dx = +2; ret.dx = +1;
ret.dy = +1; ret.dy = +1;
ret.encodedIdx = 5; ret.encodedIdx = 5;
} else { } else {
ret.dx = -2; ret.dx = -1;
ret.dy = -1; ret.dy = -1;
ret.encodedIdx = 6; ret.encodedIdx = 6;
} }
} else if (criticalRatio > -this.magicLeanUpperBound && criticalRatio < -this.magicLeanLowerBound) { } else if (criticalRatio > -this.magicLeanUpperBound && criticalRatio < -this.magicLeanLowerBound) {
if (0 < continuousDx) { if (0 < continuousDx) {
ret.dx = +2; ret.dx = +1;
ret.dy = -1; ret.dy = -1;
ret.encodedIdx = 7; ret.encodedIdx = 7;
} else { } else {
ret.dx = -2; ret.dx = -1;
ret.dy = +1; ret.dy = +1;
ret.encodedIdx = 8; ret.encodedIdx = 8;
} }
} else { } else {
if (Math.abs(criticalRatio) < 1) { if (Math.abs(criticalRatio) < 0.1) {
ret.dy = 0; ret.dy = 0;
if (0 < continuousDx) { if (0 < continuousDx) {
ret.dx = +2; ret.dx = +2; // right
ret.encodedIdx = 9; ret.encodedIdx = 3;
} else { } else {
ret.dx = -2; ret.dx = -2; // left
ret.encodedIdx = 10; ret.encodedIdx = 4;
} }
} else { } else if (Math.abs(criticalRatio) > 0.9) {
ret.dx = 0; ret.dx = 0;
if (0 < continuousDy) { if (0 < continuousDy) {
ret.dy = +1; ret.dy = +2; // up
ret.encodedIdx = 11; ret.encodedIdx = 1;
} else { } else {
ret.dy = -1; ret.dy = -2; // down
ret.encodedIdx = 12; ret.encodedIdx = 2;
} }
} }
} }
@ -337,16 +333,15 @@ cc.Class({
decodeDirection(encodedDirection) { decodeDirection(encodedDirection) {
const mapped = window.DIRECTION_DECODER[encodedDirection]; const mapped = window.DIRECTION_DECODER[encodedDirection];
if (null == mapped) { if (null == mapped) {
console.error("Unexpected encodedDirection = ", encodedDirection); console.error("Unexpected encodedDirection = ", encodedDirection);
} }
return { return {
dx: mapped[0], dx: mapped[0],
dy: mapped[1], dy: mapped[1],
speedFactor: mapped[2], };
}
}, },
getDiscretizedDirection() { getDiscretizedDirection() {
return this.discretizeDirection(this.cachedStickHeadPosition.x, this.cachedStickHeadPosition.y, this.joyStickEps); return this.discretizeDirection(this.cachedStickHeadPosition.x, this.cachedStickHeadPosition.y, this.joyStickEps);
} }
}); });