mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 19:58:56 +00:00
Updated frontend animation trigger mechanism.
This commit is contained in:
parent
1593965950
commit
c58e690a47
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
||||||
|
|
||||||
![gif_demo](./charts/along_wall_interaction_with_reconnection.gif)
|
_(the following gif is sped up to 2x for file size reduction)_
|
||||||
|
![gif_demo](./charts/melee_attack_2.gif)
|
||||||
|
|
||||||
Please also checkout [this demo video](https://pan.baidu.com/s/1YkfuHjNLzlFVnKiEj6wrDQ?pwd=tkr5) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
Please also checkout [this demo video](https://pan.baidu.com/s/1fy0CuFKnVP_Gn2cDfrj6yg?pwd=q5uc) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||||
|
|
||||||
The video mainly shows the following features.
|
The video mainly shows the following features.
|
||||||
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
||||||
|
@ -69,6 +69,10 @@ const (
|
|||||||
ATK_CHARACTER_STATE_ATKED1 = 3
|
ATK_CHARACTER_STATE_ATKED1 = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT_PLAYER_RADIUS = float64(16)
|
||||||
|
)
|
||||||
|
|
||||||
// These directions are chosen such that when speed is changed to "(speedX+delta, speedY+delta)" for any of them, the direction is unchanged.
|
// 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},
|
||||||
@ -192,8 +196,8 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke
|
|||||||
pPlayerFromDbInit.AckingInputFrameId = -1
|
pPlayerFromDbInit.AckingInputFrameId = -1
|
||||||
pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED
|
pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED
|
||||||
pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK
|
pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK
|
||||||
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||||
pPlayerFromDbInit.ColliderRadius = float64(12) // Hardcoded
|
pPlayerFromDbInit.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||||
|
|
||||||
pR.Players[playerId] = pPlayerFromDbInit
|
pR.Players[playerId] = pPlayerFromDbInit
|
||||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||||
@ -218,15 +222,16 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso
|
|||||||
* -- YFLu
|
* -- YFLu
|
||||||
*/
|
*/
|
||||||
defer pR.onPlayerReAdded(playerId)
|
defer pR.onPlayerReAdded(playerId)
|
||||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
|
||||||
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
|
||||||
pEffectiveInRoomPlayerInstance := pR.Players[playerId]
|
pEffectiveInRoomPlayerInstance := pR.Players[playerId]
|
||||||
pEffectiveInRoomPlayerInstance.AckingFrameId = -1
|
pEffectiveInRoomPlayerInstance.AckingFrameId = -1
|
||||||
pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1
|
pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1
|
||||||
pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED
|
pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED
|
||||||
pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK
|
pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK
|
||||||
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||||
pEffectiveInRoomPlayerInstance.ColliderRadius = float64(16) // Hardcoded
|
pEffectiveInRoomPlayerInstance.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||||
|
|
||||||
|
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||||
|
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
||||||
|
|
||||||
Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("joinIndex", pEffectiveInRoomPlayerInstance.JoinIndex), zap.Any("playerBattleState", pEffectiveInRoomPlayerInstance.BattleState), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId), zap.Any("LastSentInputFrameId", pEffectiveInRoomPlayerInstance.LastSentInputFrameId))
|
Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("joinIndex", pEffectiveInRoomPlayerInstance.JoinIndex), zap.Any("playerBattleState", pEffectiveInRoomPlayerInstance.BattleState), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId), zap.Any("LastSentInputFrameId", pEffectiveInRoomPlayerInstance.LastSentInputFrameId))
|
||||||
return true
|
return true
|
||||||
@ -813,7 +818,7 @@ func (pR *Room) OnDismissed() {
|
|||||||
X: 0,
|
X: 0,
|
||||||
Y: 0,
|
Y: 0,
|
||||||
},
|
},
|
||||||
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
|
HitboxOffset: float64(24.0), // should be about the radius of the PlayerCollider
|
||||||
HitboxSize: &Vec2D{
|
HitboxSize: &Vec2D{
|
||||||
X: float64(45.0),
|
X: float64(45.0),
|
||||||
Y: float64(32.0),
|
Y: float64(32.0),
|
||||||
@ -945,6 +950,14 @@ func (pR *Room) onPlayerAdded(playerId int32) {
|
|||||||
panic(fmt.Sprintf("onPlayerAdded error, nil == playerPos, roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
panic(fmt.Sprintf("onPlayerAdded error, nil == playerPos, roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
||||||
}
|
}
|
||||||
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
|
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
|
||||||
|
// Hardcoded initial character orientation/facing
|
||||||
|
if 0 == (pR.Players[playerId].JoinIndex % 2) {
|
||||||
|
pR.Players[playerId].DirX = -2
|
||||||
|
pR.Players[playerId].DirY = 0
|
||||||
|
} else {
|
||||||
|
pR.Players[playerId].DirX = +2
|
||||||
|
pR.Players[playerId].DirY = 0
|
||||||
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
# Double playback speed of a video
|
||||||
|
```
|
||||||
|
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
|
||||||
|
```
|
||||||
|
|
||||||
# GIF creation cmd reference
|
# GIF creation cmd reference
|
||||||
```
|
```
|
||||||
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
|
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.7 MiB |
BIN
charts/melee_attack_2.gif
Normal file
BIN
charts/melee_attack_2.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 MiB |
@ -529,8 +529,8 @@
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
0.5,
|
1,
|
||||||
0.5,
|
1,
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -40,27 +40,26 @@ cc.Class({
|
|||||||
BaseCharacter.prototype.onLoad.call(this);
|
BaseCharacter.prototype.onLoad.call(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCharacterAnim(newScheduledDirection, rdfPlayer, forceAnimSwitch) {
|
updateCharacterAnim(rdfPlayer, prevRdfPlayer, forceAnimSwitch) {
|
||||||
if (0 == rdfPlayer.framesToRecover) {
|
// Update directions
|
||||||
// Update directions
|
if (this.animComp && this.animComp.node) {
|
||||||
if (forceAnimSwitch || null == this.activeDirection || (null != newScheduledDirection && (newScheduledDirection.dx != this.activeDirection.dx || newScheduledDirection.dy != this.activeDirection.dy))) {
|
if (0 > rdfPlayer.dirX) {
|
||||||
this.activeDirection = newScheduledDirection;
|
this.animComp.node.scaleX = (-1.0);
|
||||||
if (this.animComp && this.animComp.node) {
|
} else if (0 < rdfPlayer.dirX) {
|
||||||
if (0 > newScheduledDirection.dx) {
|
this.animComp.node.scaleX = (1.0);
|
||||||
this.animComp.node.scaleX = (-1.0);
|
|
||||||
} else if (0 < newScheduledDirection.dx) {
|
|
||||||
this.animComp.node.scaleX = (1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update per character state
|
// Update per character state
|
||||||
let newCharacterState = rdfPlayer.characterState;
|
let newCharacterState = rdfPlayer.characterState;
|
||||||
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
let prevCharacterState = (null == prevRdfPlayer ? window.ATK_CHARACTER_STATE.Idle1[0] : prevRdfPlayer.characterState);
|
||||||
if (newAnimName != this.animComp.animationName) {
|
if (newCharacterState != prevCharacterState) {
|
||||||
this.animComp.playAnimation(newAnimName);
|
// Anim is edge-triggered
|
||||||
// console.log(`JoinIndex=${this.joinIndex}, Resetting anim to ${newAnimName}, dir changed: (${oldDx}, ${oldDy}) -> (${newScheduledDirection.dx}, ${newScheduledDirection.dy})`);
|
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
||||||
|
if (newAnimName != this.animComp.animationName) {
|
||||||
|
this.animComp.playAnimation(newAnimName);
|
||||||
|
console.log(`JoinIndex=${rdfPlayer.joinIndex}, Resetting anim to ${newAnimName}, state changed: (${prevCharacterState}, prevRdfPlayer is null? ${null == prevRdfPlayer}) -> (${newCharacterState})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -537,7 +537,7 @@ cc.Class({
|
|||||||
window.initPersistentSessionClient(self.initAfterWSConnected, boundRoomId);
|
window.initPersistentSessionClient(self.initAfterWSConnected, boundRoomId);
|
||||||
} else {
|
} else {
|
||||||
self.showPopupInCanvas(self.gameRuleNode);
|
self.showPopupInCanvas(self.gameRuleNode);
|
||||||
// Deliberately left blank. -- YFLu
|
// Deliberately left blank. -- YFLu
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -630,7 +630,7 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
self.transitToState(ALL_MAP_STATES.VISUAL);
|
self.transitToState(ALL_MAP_STATES.VISUAL);
|
||||||
self.battleState = ALL_BATTLE_STATES.IN_BATTLE;
|
self.battleState = ALL_BATTLE_STATES.IN_BATTLE;
|
||||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
self.applyRoomDownsyncFrameDynamics(rdf, self.recentRenderCache.getByFrameId(rdf.id - 1));
|
||||||
|
|
||||||
return dumpRenderCacheRet;
|
return dumpRenderCacheRet;
|
||||||
},
|
},
|
||||||
@ -752,20 +752,14 @@ cc.Class({
|
|||||||
playerScriptIns.setSpecies("SoldierWaterGhost");
|
playerScriptIns.setSpecies("SoldierWaterGhost");
|
||||||
} else if (2 == joinIndex) {
|
} else if (2 == joinIndex) {
|
||||||
playerScriptIns.setSpecies("SoldierFireGhost");
|
playerScriptIns.setSpecies("SoldierFireGhost");
|
||||||
if (0 == playerDownsyncInfo.dirX && 0 == playerDownsyncInfo.dirY) {
|
|
||||||
playerScriptIns.animComp.node.scaleX = (-1.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const wpos = self.virtualGridToWorldPos(vx, vy);
|
const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
|
||||||
|
newPlayerNode.setPosition(wx, wy);
|
||||||
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
|
|
||||||
playerScriptIns.mapNode = self.node;
|
playerScriptIns.mapNode = self.node;
|
||||||
const cpos = self.virtualGridToPolygonColliderAnchorPos(vx, vy, playerDownsyncInfo.colliderRadius, playerDownsyncInfo.colliderRadius);
|
|
||||||
const d = playerDownsyncInfo.colliderRadius * 2,
|
const d = playerDownsyncInfo.colliderRadius * 2,
|
||||||
x0 = cpos[0],
|
[x0, y0] = self.virtualGridToPolygonColliderAnchorPos(vx, vy, playerDownsyncInfo.colliderRadius, playerDownsyncInfo.colliderRadius),
|
||||||
y0 = cpos[1];
|
pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||||
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
|
||||||
|
|
||||||
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
|
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
@ -776,10 +770,7 @@ cc.Class({
|
|||||||
setLocalZOrder(newPlayerNode, 5);
|
setLocalZOrder(newPlayerNode, 5);
|
||||||
|
|
||||||
newPlayerNode.active = true;
|
newPlayerNode.active = true;
|
||||||
playerScriptIns.updateCharacterAnim({
|
playerScriptIns.updateCharacterAnim(playerDownsyncInfo, null, true);
|
||||||
dx: playerDownsyncInfo.dirX,
|
|
||||||
dy: playerDownsyncInfo.dirY,
|
|
||||||
}, playerDownsyncInfo, true);
|
|
||||||
|
|
||||||
return [newPlayerNode, playerScriptIns];
|
return [newPlayerNode, playerScriptIns];
|
||||||
},
|
},
|
||||||
@ -798,9 +789,7 @@ cc.Class({
|
|||||||
currSelfInput = null;
|
currSelfInput = null;
|
||||||
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
|
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
|
||||||
if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) {
|
if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) {
|
||||||
const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId);
|
[prevSelfInput, currSelfInput] = self._generateInputFrameUpsync(noDelayInputFrameId);
|
||||||
prevSelfInput = prevAndCurrInputs[0];
|
|
||||||
currSelfInput = prevAndCurrInputs[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let t0 = performance.now();
|
let t0 = performance.now();
|
||||||
@ -820,14 +809,15 @@ cc.Class({
|
|||||||
let t2 = performance.now();
|
let t2 = performance.now();
|
||||||
|
|
||||||
// Inside the following "self.rollbackAndChase" actually ROLLS FORWARD w.r.t. the corresponding delayedInputFrame, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now.
|
// Inside the following "self.rollbackAndChase" actually ROLLS FORWARD w.r.t. the corresponding delayedInputFrame, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now.
|
||||||
const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||||
/*
|
/*
|
||||||
const nonTrivialChaseEnded = (prevChaserRenderFrameId < nextChaserRenderFrameId && nextChaserRenderFrameId == self.renderFrameId);
|
const nonTrivialChaseEnded = (prevChaserRenderFrameId < nextChaserRenderFrameId && nextChaserRenderFrameId == self.renderFrameId);
|
||||||
if (nonTrivialChaseEnded) {
|
if (nonTrivialChaseEnded) {
|
||||||
console.debug("Non-trivial chase ended, prevChaserRenderFrameId=" + prevChaserRenderFrameId + ", nextChaserRenderFrameId=" + nextChaserRenderFrameId);
|
console.debug("Non-trivial chase ended, prevChaserRenderFrameId=" + prevChaserRenderFrameId + ", nextChaserRenderFrameId=" + nextChaserRenderFrameId);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
// [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls!
|
||||||
|
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||||
let t3 = performance.now();
|
let t3 = performance.now();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error during Map.update", err);
|
console.error("Error during Map.update", err);
|
||||||
@ -962,26 +952,17 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
applyRoomDownsyncFrameDynamics(rdf) {
|
applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(rdf.id - 1, self.inputDelayFrames));
|
for (let [playerId, playerRichInfo] of self.playerRichInfoDict.entries()) {
|
||||||
|
|
||||||
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
|
|
||||||
const immediatePlayerInfo = rdf.players[playerId];
|
const immediatePlayerInfo = rdf.players[playerId];
|
||||||
const wpos = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY);
|
const prevRdfPlayer = (null == prevRdf ? null : prevRdf.players[playerId]);
|
||||||
const dx = (wpos[0] - playerRichInfo.node.x);
|
const [wx, wy] = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY);
|
||||||
const dy = (wpos[1] - playerRichInfo.node.y);
|
//const justJiggling = (self.jigglingEps1D >= Math.abs(wx - playerRichInfo.node.x) && self.jigglingEps1D >= Math.abs(wy - playerRichInfo.node.y));
|
||||||
//const justJiggling = (self.jigglingEps1D >= Math.abs(dx) && self.jigglingEps1D >= Math.abs(dy));
|
playerRichInfo.node.setPosition(wx, wy);
|
||||||
playerRichInfo.node.setPosition(wpos[0], wpos[1]);
|
|
||||||
// TODO: check "rdf.players[playerId].characterState" instead, might have to play Atk/Atked anim!
|
|
||||||
if (null != delayedInputFrameForPrevRenderFrame) {
|
|
||||||
const decodedInput = self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[playerRichInfo.joinIndex - 1]);
|
|
||||||
playerRichInfo.scriptIns.updateCharacterAnim(decodedInput, immediatePlayerInfo, false);
|
|
||||||
} else {
|
|
||||||
playerRichInfo.scriptIns.updateCharacterAnim(null, immediatePlayerInfo, false);
|
|
||||||
}
|
|
||||||
playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed);
|
playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed);
|
||||||
});
|
playerRichInfo.scriptIns.updateCharacterAnim(immediatePlayerInfo, prevRdfPlayer, false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
|
getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
|
||||||
@ -1044,9 +1025,7 @@ cc.Class({
|
|||||||
|
|
||||||
const newVx = currPlayerDownsync.virtualGridX;
|
const newVx = currPlayerDownsync.virtualGridX;
|
||||||
const newVy = currPlayerDownsync.virtualGridY;
|
const newVy = currPlayerDownsync.virtualGridY;
|
||||||
const newCpos = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
[playerCollider.x, playerCollider.y] = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
||||||
playerCollider.x = newCpos[0];
|
|
||||||
playerCollider.y = newCpos[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
||||||
@ -1068,16 +1047,15 @@ cc.Class({
|
|||||||
if (0 > offender.dirX) {
|
if (0 > offender.dirX) {
|
||||||
xfac = -1;
|
xfac = -1;
|
||||||
}
|
}
|
||||||
const offenderWpos = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
||||||
const bulletWx = offenderWpos[0] + xfac * meleeBullet.hitboxOffset;
|
const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
|
||||||
const bulletWy = offenderWpos[1];
|
const bulletWy = offenderWy;
|
||||||
const bulletCpos = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5);
|
const [bulletCx, bulletCy] = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5), pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||||
const pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
const newBulletCollider = collisionSys.createPolygon(bulletCx, bulletCy, pts);
|
||||||
const newBulletCollider = collisionSys.createPolygon(bulletCpos[0], bulletCpos[1], pts);
|
|
||||||
newBulletCollider.data = meleeBullet;
|
newBulletCollider.data = meleeBullet;
|
||||||
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
||||||
bulletColliders.set(collisionBulletIndex, newBulletCollider);
|
bulletColliders.set(collisionBulletIndex, newBulletCollider);
|
||||||
// console.log(`A meleeBullet is added to collisionSys at currRenderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
// console.log(`A meleeBullet is added to collisionSys at currRenderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1180,9 +1158,9 @@ cc.Class({
|
|||||||
} else {
|
} else {
|
||||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||||
}
|
}
|
||||||
const movement = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
const [movementX, movementY] = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
||||||
playerCollider.x += movement[0];
|
playerCollider.x += movementX;
|
||||||
playerCollider.y += movement[1];
|
playerCollider.y += movementY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1209,10 +1187,8 @@ cc.Class({
|
|||||||
const playerId = self.playerRichInfoArr[j].id;
|
const playerId = self.playerRichInfoArr[j].id;
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
const newVpos = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
|
||||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||||
thatPlayerInNextFrame.virtualGridX = newVpos[0];
|
[thatPlayerInNextFrame.virtualGridX, thatPlayerInNextFrame.virtualGridY] = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
||||||
thatPlayerInNextFrame.virtualGridY = newVpos[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1225,29 +1201,30 @@ cc.Class({
|
|||||||
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd" if not interruptted.
|
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd" if not interruptted.
|
||||||
*/
|
*/
|
||||||
const self = this;
|
const self = this;
|
||||||
|
let prevLatestRdf = null;
|
||||||
let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame"
|
let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame"
|
||||||
if (null == latestRdf) {
|
if (null == latestRdf) {
|
||||||
console.error(`Couldn't find renderFrameId=${renderFrameIdSt}, to rollback, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`);
|
console.error(`Couldn't find renderFrameId=${renderFrameIdSt}, to rollback, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`);
|
||||||
return latestRdf;
|
return [prevLatestRdf, latestRdf];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderFrameIdSt >= renderFrameIdEd) {
|
if (renderFrameIdSt >= renderFrameIdEd) {
|
||||||
return latestRdf;
|
return [prevLatestRdf, latestRdf];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) {
|
for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) {
|
||||||
const currRenderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; [WARNING] When "true == isChasing", this function can be interruptted by "onRoomDownsyncFrame(rdf)" asynchronously anytime, making this line return "null"!
|
const currRenderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; [WARNING] When "true == isChasing", this function can be interruptted by "onRoomDownsyncFrame(rdf)" asynchronously anytime, making this line return "null"!
|
||||||
if (null == currRenderFrame) {
|
if (null == currRenderFrame) {
|
||||||
console.warn(`Couldn't find renderFrame for i=${i} to rollback, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, might've been interruptted by onRoomDownsyncFrame`);
|
console.warn(`Couldn't find renderFrame for i=${i} to rollback, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, might've been interruptted by onRoomDownsyncFrame`);
|
||||||
return latestRdf;
|
return [prevLatestRdf, latestRdf];
|
||||||
}
|
}
|
||||||
const j = self._convertToInputFrameId(i, self.inputDelayFrames);
|
const j = self._convertToInputFrameId(i, self.inputDelayFrames);
|
||||||
const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j);
|
const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j);
|
||||||
if (null == delayedInputFrame) {
|
if (null == delayedInputFrame) {
|
||||||
console.warn(`Failed to get cached delayedInputFrame for i=${i}, j=${j}, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}`);
|
console.warn(`Failed to get cached delayedInputFrame for i=${i}, j=${j}, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}`);
|
||||||
return latestRdf;
|
return [prevLatestRdf, latestRdf];
|
||||||
}
|
}
|
||||||
|
prevLatestRdf = latestRdf;
|
||||||
latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap);
|
latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap);
|
||||||
if (
|
if (
|
||||||
self._allConfirmed(delayedInputFrame.confirmedList)
|
self._allConfirmed(delayedInputFrame.confirmedList)
|
||||||
@ -1269,7 +1246,7 @@ cc.Class({
|
|||||||
self.dumpToRenderCache(latestRdf);
|
self.dumpToRenderCache(latestRdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
return latestRdf;
|
return [prevLatestRdf, latestRdf];
|
||||||
},
|
},
|
||||||
|
|
||||||
_initPlayerRichInfoDict(players) {
|
_initPlayerRichInfoDict(players) {
|
||||||
@ -1280,16 +1257,16 @@ cc.Class({
|
|||||||
const immediatePlayerInfo = players[playerId];
|
const immediatePlayerInfo = players[playerId];
|
||||||
self.playerRichInfoDict.set(playerId, immediatePlayerInfo);
|
self.playerRichInfoDict.set(playerId, immediatePlayerInfo);
|
||||||
|
|
||||||
const nodeAndScriptIns = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, self.playerRichInfoDict.get(playerId));
|
const [theNode, theScriptIns] = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, immediatePlayerInfo);
|
||||||
|
|
||||||
Object.assign(self.playerRichInfoDict.get(playerId), {
|
Object.assign(self.playerRichInfoDict.get(playerId), {
|
||||||
node: nodeAndScriptIns[0],
|
node: theNode,
|
||||||
scriptIns: nodeAndScriptIns[1]
|
scriptIns: theScriptIns,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (self.selfPlayerInfo.id == playerId) {
|
if (self.selfPlayerInfo.id == playerId) {
|
||||||
self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo);
|
self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo);
|
||||||
nodeAndScriptIns[1].showArrowTipNode();
|
theScriptIns.showArrowTipNode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.playerRichInfoArr = new Array(self.playerRichInfoDict.size);
|
self.playerRichInfoArr = new Array(self.playerRichInfoDict.size);
|
||||||
@ -1351,13 +1328,13 @@ cc.Class({
|
|||||||
|
|
||||||
polygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH) {
|
polygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const wpos = self.polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH);
|
const [wx, wy] = self.polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH);
|
||||||
return self.worldToVirtualGridPos(wpos[0], wpos[1])
|
return self.worldToVirtualGridPos(wx, wy)
|
||||||
},
|
},
|
||||||
|
|
||||||
virtualGridToPolygonColliderAnchorPos(vx, vy, halfBoundingW, halfBoundingH) {
|
virtualGridToPolygonColliderAnchorPos(vx, vy, halfBoundingW, halfBoundingH) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const wpos = self.virtualGridToWorldPos(vx, vy);
|
const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
|
||||||
return self.worldToPolygonColliderAnchorPos(wpos[0], wpos[1], halfBoundingW, halfBoundingH)
|
return self.worldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -191,8 +191,8 @@ cc.Class({
|
|||||||
currSelfInput = prevAndCurrInputs[1];
|
currSelfInput = prevAndCurrInputs[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||||
let t3 = performance.now();
|
let t3 = performance.now();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error during Map.update", err);
|
console.error("Error during Map.update", err);
|
||||||
|
Loading…
Reference in New Issue
Block a user