mirror of
https://github.com/genxium/DelayNoMore
synced 2025-10-09 00:26:39 +00:00
A broken commit during backend bullet adaptation.
This commit is contained in:
@@ -95,6 +95,9 @@ cc.Class({
|
||||
type: cc.Float,
|
||||
default: 1e-3
|
||||
},
|
||||
bulletTriggerEnabled: {
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
_inputFrameIdDebuggable(inputFrameId) {
|
||||
@@ -321,10 +324,10 @@ cc.Class({
|
||||
|
||||
self.battleState = ALL_BATTLE_STATES.WAITING;
|
||||
|
||||
self.countdownNanos = null;
|
||||
if (self.countdownLabel) {
|
||||
self.countdownLabel.string = "";
|
||||
}
|
||||
self.countdownNanos = null;
|
||||
if (self.findingPlayerNode) {
|
||||
const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
|
||||
findingPlayerScriptIns.init();
|
||||
@@ -741,17 +744,15 @@ cc.Class({
|
||||
self.playersInfoNode.getComponent("PlayersInfo").clearInfo();
|
||||
},
|
||||
|
||||
spawnPlayerNode(joinIndex, vx, vy, playerRichInfo) {
|
||||
spawnPlayerNode(joinIndex, vx, vy, playerDownsyncInfo) {
|
||||
const self = this;
|
||||
const newPlayerNode = cc.instantiate(self.controlledCharacterPrefab)
|
||||
const playerScriptIns = newPlayerNode.getComponent("ControlledCharacter");
|
||||
playerScriptIns.joinIndex = joinIndex;
|
||||
|
||||
if (1 == joinIndex) {
|
||||
playerScriptIns.setSpecies("SoldierWaterGhost");
|
||||
} else if (2 == joinIndex) {
|
||||
playerScriptIns.setSpecies("SoldierFireGhost");
|
||||
if (0 == playerRichInfo.dir.dx && 0 == playerRichInfo.dir.dy) {
|
||||
if (0 == playerDownsyncInfo.dirX && 0 == playerDownsyncInfo.dirY) {
|
||||
playerScriptIns.animComp.node.scaleX = (-1.0);
|
||||
}
|
||||
}
|
||||
@@ -760,16 +761,15 @@ cc.Class({
|
||||
|
||||
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
|
||||
playerScriptIns.mapNode = self.node;
|
||||
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo);
|
||||
const d = playerRichInfo.colliderRadius * 2,
|
||||
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerDownsyncInfo);
|
||||
const d = playerDownsyncInfo.colliderRadius * 2,
|
||||
x0 = cpos[0],
|
||||
y0 = cpos[1];
|
||||
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||
|
||||
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
newPlayerCollider.collisionPlayerIndex = parseInt(collisionPlayerIndex);
|
||||
newPlayerCollider.playerId = parseInt(playerRichInfo.id);
|
||||
newPlayerCollider.data = playerDownsyncInfo;
|
||||
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
|
||||
|
||||
safelyAddChild(self.node, newPlayerNode);
|
||||
@@ -777,9 +777,9 @@ cc.Class({
|
||||
|
||||
newPlayerNode.active = true;
|
||||
playerScriptIns.updateCharacterAnim({
|
||||
dx: playerRichInfo.dir.dx,
|
||||
dy: playerRichInfo.dir.dy
|
||||
}, playerRichInfo, true);
|
||||
dx: playerDownsyncInfo.dirX,
|
||||
dy: playerDownsyncInfo.dirY,
|
||||
}, playerDownsyncInfo, true);
|
||||
|
||||
return [newPlayerNode, playerScriptIns];
|
||||
},
|
||||
@@ -973,8 +973,6 @@ cc.Class({
|
||||
const dy = (wpos[1] - playerRichInfo.node.y);
|
||||
//const justJiggling = (self.jigglingEps1D >= Math.abs(dx) && self.jigglingEps1D >= Math.abs(dy));
|
||||
playerRichInfo.node.setPosition(wpos[0], wpos[1]);
|
||||
playerRichInfo.virtualGridX = immediatePlayerInfo.virtualGridX;
|
||||
playerRichInfo.virtualGridY = immediatePlayerInfo.virtualGridY;
|
||||
// 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]);
|
||||
@@ -1003,17 +1001,15 @@ cc.Class({
|
||||
// TODO: Write unit-test for this function to compare with its backend counter part
|
||||
applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
|
||||
const self = this;
|
||||
const nextRenderFramePlayers = {}
|
||||
const nextRenderFramePlayers = {};
|
||||
for (let playerId in currRenderFrame.players) {
|
||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||
nextRenderFramePlayers[playerId] = {
|
||||
id: playerId,
|
||||
virtualGridX: currPlayerDownsync.virtualGridX,
|
||||
virtualGridY: currPlayerDownsync.virtualGridY,
|
||||
dir: {
|
||||
dx: currPlayerDownsync.dir.dx,
|
||||
dy: currPlayerDownsync.dir.dy,
|
||||
},
|
||||
dirX: currPlayerDownsync.dirX,
|
||||
dirY: currPlayerDownsync.dirY,
|
||||
characterState: currPlayerDownsync.characterState,
|
||||
speed: currPlayerDownsync.speed,
|
||||
battleState: currPlayerDownsync.battleState,
|
||||
@@ -1034,6 +1030,8 @@ cc.Class({
|
||||
|
||||
const bulletPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
||||
const effPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
||||
|
||||
// Reset playerCollider position from the "virtual grid position"
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||
@@ -1042,17 +1040,17 @@ cc.Class({
|
||||
const playerId = playerRichInfo.id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const player = currRenderFrame.players[playerId];
|
||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||
|
||||
const newVx = player.virtualGridX;
|
||||
const newVy = player.virtualGridY;
|
||||
const newVx = currPlayerDownsync.virtualGridX;
|
||||
const newVy = currPlayerDownsync.virtualGridY;
|
||||
const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1]);
|
||||
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
|
||||
const colliderBullets = new Map(); // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
|
||||
const bulletColliders = new Map(); // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
|
||||
const removedBulletsAtCurrFrame = new Set();
|
||||
for (let k in currRenderFrame.meleeBullets) {
|
||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
||||
@@ -1066,39 +1064,39 @@ cc.Class({
|
||||
const offenderCollider = collisionSysMap.get(collisionOffenderIndex);
|
||||
const offender = currRenderFrame.players[meleeBullet.offenderPlayerId];
|
||||
|
||||
const xfac = Math.sign(offender.dir.dx),
|
||||
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
|
||||
let xfac = 1,
|
||||
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if (0 > offender.dirX) {
|
||||
xfac = -1;
|
||||
}
|
||||
const x0 = offenderCollider.x + xfac * meleeBullet.hitboxOffset,
|
||||
y0 = offenderCollider.y + yfac * meleeBullet.hitboxOffset;
|
||||
const pts = [[0, 0], [xfac * meleeBullet.hitboxSize.x, 0], [xfac * meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||
const newBulletCollider = collisionSys.createPolygon(x0, y0, pts);
|
||||
newBulletCollider.collisionBulletIndex = collisionBulletIndex;
|
||||
newBulletCollider.offenderPlayerId = meleeBullet.offenderPlayerId;
|
||||
newBulletCollider.pushback = meleeBullet.pushback;
|
||||
newBulletCollider.hitStunFrames = meleeBullet.hitStunFrames;
|
||||
newBulletCollider.data = meleeBullet;
|
||||
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
||||
colliderBullets.set(collisionBulletIndex, newBulletCollider);
|
||||
console.log(`A meleeBullet=${JSON.stringify(meleeBullet)} is added to collisionSys at renderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended`);
|
||||
bulletColliders.set(collisionBulletIndex, newBulletCollider);
|
||||
// console.log(`A meleeBullet is added to collisionSys at renderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
||||
}
|
||||
}
|
||||
|
||||
collisionSys.update();
|
||||
const result1 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
||||
|
||||
colliderBullets.forEach((bulletCollider, collisionBulletIndex) => {
|
||||
bulletColliders.forEach((bulletCollider, collisionBulletIndex) => {
|
||||
const potentials = bulletCollider.potentials();
|
||||
let shouldRemove = false;
|
||||
for (const potential of potentials) {
|
||||
if (null != potential.playerId && potential.playerId == bulletCollider.offenderPlayerId) continue;
|
||||
if (null != potential.data && potential.data.joinIndex == bulletCollider.data.offenderJoinIndex) continue;
|
||||
if (!bulletCollider.collides(potential, result1)) continue;
|
||||
if (null != potential.playerId) {
|
||||
const joinIndex = potential.collisionPlayerIndex - self.collisionPlayerIndexPrefix;
|
||||
bulletPushbacks[joinIndex - 1][0] += result1.overlap_x * bulletCollider.pushback;
|
||||
bulletPushbacks[joinIndex - 1][1] += result1.overlap_y * bulletCollider.pushback;
|
||||
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.playerId];
|
||||
if (null != potential.data && null !== potential.data.joinIndex) {
|
||||
const joinIndex = potential.data.joinIndex;
|
||||
bulletPushbacks[joinIndex - 1][0] += bulletCollider.data.pushback; // Only for straight punch, there's no y-pushback
|
||||
bulletPushbacks[joinIndex - 1][1] += 0;
|
||||
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.data.id];
|
||||
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
|
||||
const oldFrameToRecover = thatAckedPlayerInNextFrame.framesToRecover;
|
||||
thatAckedPlayerInNextFrame.framesToRecover = oldFrameToRecover > bulletCollider.hitStunFrames ? oldFrameToRecover : bulletCollider.hitStunFrames; // In case the hit player is already stun, we take the larger "hitStunFrames"
|
||||
thatAckedPlayerInNextFrame.framesToRecover = (oldFrameToRecover > bulletCollider.data.hitStunFrames ? oldFrameToRecover : bulletCollider.data.hitStunFrames); // In case the hit player is already stun, we extend it
|
||||
}
|
||||
shouldRemove = true;
|
||||
}
|
||||
@@ -1107,9 +1105,9 @@ cc.Class({
|
||||
}
|
||||
});
|
||||
|
||||
// [WARNING] Remove bullets from collisionSys ANYWAY for the convenience of rollback
|
||||
for (let k in currRenderFrame.meleeBullets) {
|
||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
||||
// [WARNING] remove from collisionSys ANYWAY for the convenience of rollback
|
||||
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
|
||||
if (collisionSysMap.has(collisionBulletIndex)) {
|
||||
const bulletCollider = collisionSysMap.get(collisionBulletIndex);
|
||||
@@ -1120,10 +1118,10 @@ cc.Class({
|
||||
toRet.meleeBullets.push(meleeBullet);
|
||||
}
|
||||
|
||||
// Process player inputs
|
||||
if (null != delayedInputFrame) {
|
||||
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(currRenderFrame.id - 1, self.inputDelayFrames));
|
||||
const inputList = delayedInputFrame.inputList;
|
||||
// Process player inputs
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||
@@ -1131,13 +1129,14 @@ cc.Class({
|
||||
const playerId = playerRichInfo.id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const player = currRenderFrame.players[playerId];
|
||||
if (0 < nextRenderFramePlayers[playerId].framesToRecover) {
|
||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||
if (0 < thatPlayerInNextFrame.framesToRecover) {
|
||||
// No need to process inputs for this player, but there might be bullet pushbacks on this player
|
||||
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
|
||||
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
|
||||
if (0 != bulletPushbacks[joinIndex - 1][0] || 0 != bulletPushbacks[joinIndex - 1][1]) {
|
||||
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
|
||||
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
|
||||
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${player.framesToRecover}`);
|
||||
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${currPlayerDownsync.framesToRecover}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1148,33 +1147,32 @@ cc.Class({
|
||||
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
|
||||
|
||||
if (1 == decodedInput.btnALevel && 0 == prevBtnALevel) {
|
||||
console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
// The online map is not yet ready for bullet shooting!
|
||||
/*
|
||||
nextRenderFramePlayers[playerId].framesToRecover = PunchAtkConfig.recoveryFrames;
|
||||
const punch = window.pb.protos.MeleeBullet.create(PunchAtkConfig);
|
||||
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
|
||||
punch.offenderJoinIndex = joinIndex;
|
||||
punch.offenderPlayerId = playerId;
|
||||
punch.originatedRenderFrameId = currRenderFrame.id;
|
||||
toRet.meleeBullets.push(punch);
|
||||
console.log(`A rising-edge of meleeBullet=${JSON.stringify(punch)} is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
// console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
if (self.bulletTriggerEnabled) {
|
||||
thatPlayerInNextFrame.framesToRecover = window.PunchAtkConfig.recoveryFrames;
|
||||
const punch = window.pb.protos.MeleeBullet.create(window.PunchAtkConfig);
|
||||
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
|
||||
punch.offenderJoinIndex = joinIndex;
|
||||
punch.offenderPlayerId = playerId;
|
||||
punch.originatedRenderFrameId = currRenderFrame.id;
|
||||
toRet.meleeBullets.push(punch);
|
||||
console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}: ${JSON.stringify(punch)}`);
|
||||
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
||||
*/
|
||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
||||
}
|
||||
} else if (0 == decodedInput.btnALevel && 1 == prevBtnALevel) {
|
||||
console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
// console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
} else {
|
||||
// No trigger, process movement inputs
|
||||
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
|
||||
// Update directions and thus would eventually update moving animation accordingly
|
||||
nextRenderFramePlayers[playerId].dir.dx = decodedInput.dx;
|
||||
nextRenderFramePlayers[playerId].dir.dy = decodedInput.dy;
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
||||
thatPlayerInNextFrame.dirX = decodedInput.dx;
|
||||
thatPlayerInNextFrame.dirY = decodedInput.dy;
|
||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
||||
} else {
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||
}
|
||||
const movement = self.virtualGridToWorldPos(decodedInput.dx + player.speed * decodedInput.dx, decodedInput.dy + player.speed * decodedInput.dy);
|
||||
const movement = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
||||
playerCollider.x += movement[0];
|
||||
playerCollider.y += movement[1];
|
||||
}
|
||||
@@ -1204,8 +1202,9 @@ cc.Class({
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j]);
|
||||
nextRenderFramePlayers[playerId].virtualGridX = newVpos[0];
|
||||
nextRenderFramePlayers[playerId].virtualGridY = newVpos[1];
|
||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||
thatPlayerInNextFrame.virtualGridX = newVpos[0];
|
||||
thatPlayerInNextFrame.virtualGridY = newVpos[1];
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,27 +3,27 @@ i18n.init(window.language); // languageID should be equal to the one we input in
|
||||
|
||||
const OnlineMap = require('./Map');
|
||||
|
||||
const PunchAtkConfig = {
|
||||
window.PunchAtkConfig = {
|
||||
// for offender
|
||||
startupFrames: 18,
|
||||
activeFrames: 42,
|
||||
recoveryFrames: 60, // usually but not always "startupFrames+activeFrames"
|
||||
recoveryFramesOnBlock: 60, // usually but not always the same as "recoveryFrames"
|
||||
recoveryFramesOnHit: 60, // usually but not always the same as "recoveryFrames"
|
||||
recoveryFrames: 61, // usually but not always "startupFrames+activeFrames", I hereby set it to be 1 frame more than the actual animation to avoid critical transition, i.e. when the animation is 1 frame from ending but "rdfPlayer.framesToRecover" is already counted 0 and the player triggers an other same attack, making an effective bullet trigger but no animation is played due to same animName is still playing
|
||||
recoveryFramesOnBlock: 61,
|
||||
recoveryFramesOnHit: 61,
|
||||
moveforward: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
hitboxOffset: 12.0, // should be about the radius of the PlayerCollider
|
||||
hitboxSize: {
|
||||
x: 32.0,
|
||||
x: 45.0,
|
||||
y: 32.0,
|
||||
},
|
||||
|
||||
// for defender
|
||||
hitStunFrames: 18,
|
||||
blockStunFrames: 9,
|
||||
pushback: 11.0,
|
||||
pushback: 22.0,
|
||||
releaseTriggerType: 1, // 1: rising-edge, 2: falling-edge
|
||||
damage: 5
|
||||
};
|
||||
@@ -35,45 +35,6 @@ cc.Class({
|
||||
console.warn("+++++++ Map onDestroy()");
|
||||
},
|
||||
|
||||
spawnPlayerNode(joinIndex, vx, vy, playerRichInfo) {
|
||||
const self = this;
|
||||
const newPlayerNode = cc.instantiate(self.controlledCharacterPrefab)
|
||||
const playerScriptIns = newPlayerNode.getComponent("ControlledCharacter");
|
||||
if (1 == joinIndex) {
|
||||
playerScriptIns.setSpecies("SoldierWaterGhost");
|
||||
} else if (2 == joinIndex) {
|
||||
playerScriptIns.setSpecies("SoldierFireGhost");
|
||||
playerScriptIns.animComp.node.scaleX = (-1.0);
|
||||
}
|
||||
const wpos = self.virtualGridToWorldPos(vx, vy);
|
||||
|
||||
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
|
||||
|
||||
playerScriptIns.mapNode = self.node;
|
||||
const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerRichInfo);
|
||||
const d = playerRichInfo.colliderRadius * 2,
|
||||
x0 = cpos[0],
|
||||
y0 = cpos[1];
|
||||
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||
|
||||
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
newPlayerCollider.collisionPlayerIndex = parseInt(collisionPlayerIndex);
|
||||
newPlayerCollider.playerId = parseInt(playerRichInfo.id);
|
||||
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
|
||||
|
||||
safelyAddChild(self.node, newPlayerNode);
|
||||
setLocalZOrder(newPlayerNode, 5);
|
||||
|
||||
newPlayerNode.active = true;
|
||||
playerScriptIns.updateCharacterAnim({
|
||||
dx: playerRichInfo.dir.dx,
|
||||
dy: playerRichInfo.dir.dy
|
||||
}, playerRichInfo, true);
|
||||
|
||||
return [newPlayerNode, playerScriptIns];
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const self = this;
|
||||
window.mapIns = self;
|
||||
@@ -182,10 +143,8 @@ cc.Class({
|
||||
colliderRadius: 12,
|
||||
characterState: window.ATK_CHARACTER_STATE.Idle1[0],
|
||||
framesToRecover: 0,
|
||||
dir: {
|
||||
dx: 0,
|
||||
dy: 0
|
||||
}
|
||||
dirX: 0,
|
||||
dirY: 0,
|
||||
},
|
||||
11: {
|
||||
id: 11,
|
||||
@@ -196,10 +155,8 @@ cc.Class({
|
||||
colliderRadius: 12,
|
||||
characterState: window.ATK_CHARACTER_STATE.Idle1[0],
|
||||
framesToRecover: 0,
|
||||
dir: {
|
||||
dx: 0,
|
||||
dy: 0
|
||||
}
|
||||
dirX: 0,
|
||||
dirY: 0,
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -243,214 +200,4 @@ cc.Class({
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Overriding this function to test experimental dynamics
|
||||
applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
|
||||
const self = this;
|
||||
const nextRenderFramePlayers = {}
|
||||
for (let playerId in currRenderFrame.players) {
|
||||
const currPlayerDownsync = currRenderFrame.players[playerId];
|
||||
nextRenderFramePlayers[playerId] = {
|
||||
id: playerId,
|
||||
virtualGridX: currPlayerDownsync.virtualGridX,
|
||||
virtualGridY: currPlayerDownsync.virtualGridY,
|
||||
dir: {
|
||||
dx: currPlayerDownsync.dir.dx,
|
||||
dy: currPlayerDownsync.dir.dy,
|
||||
},
|
||||
characterState: currPlayerDownsync.characterState,
|
||||
speed: currPlayerDownsync.speed,
|
||||
battleState: currPlayerDownsync.battleState,
|
||||
score: currPlayerDownsync.score,
|
||||
removed: currPlayerDownsync.removed,
|
||||
joinIndex: currPlayerDownsync.joinIndex,
|
||||
framesToRecover: (0 < currPlayerDownsync.framesToRecover ? currPlayerDownsync.framesToRecover - 1 : 0),
|
||||
hp: currPlayerDownsync.hp,
|
||||
maxHp: currPlayerDownsync.maxHp,
|
||||
};
|
||||
}
|
||||
|
||||
const toRet = {
|
||||
id: currRenderFrame.id + 1,
|
||||
players: nextRenderFramePlayers,
|
||||
meleeBullets: []
|
||||
};
|
||||
|
||||
const bulletPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
||||
const effPushbacks = new Array(self.playerRichInfoArr.length); // Guaranteed determinism regardless of traversal order
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
bulletPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||
const playerRichInfo = self.playerRichInfoArr[j];
|
||||
const playerId = playerRichInfo.id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const player = currRenderFrame.players[playerId];
|
||||
|
||||
const newVx = player.virtualGridX;
|
||||
const newVy = player.virtualGridY;
|
||||
const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1]);
|
||||
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
|
||||
const colliderBullets = new Map(); // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
|
||||
const removedBulletsAtCurrFrame = new Set();
|
||||
for (let k in currRenderFrame.meleeBullets) {
|
||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
||||
if (
|
||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames <= currRenderFrame.id
|
||||
&&
|
||||
meleeBullet.originatedRenderFrameId + meleeBullet.startupFrames + meleeBullet.activeFrames > currRenderFrame.id
|
||||
) {
|
||||
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
|
||||
const collisionOffenderIndex = self.collisionPlayerIndexPrefix + meleeBullet.offenderJoinIndex;
|
||||
const offenderCollider = collisionSysMap.get(collisionOffenderIndex);
|
||||
const offender = currRenderFrame.players[meleeBullet.offenderPlayerId];
|
||||
|
||||
const xfac = Math.sign(offender.dir.dx),
|
||||
yfac = 0; // By now, straight Punch offset doesn't respect "y-axis"
|
||||
const x0 = offenderCollider.x + xfac * meleeBullet.hitboxOffset,
|
||||
y0 = offenderCollider.y + yfac * meleeBullet.hitboxOffset;
|
||||
const pts = [[0, 0], [xfac * meleeBullet.hitboxSize.x, 0], [xfac * meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||
const newBulletCollider = collisionSys.createPolygon(x0, y0, pts);
|
||||
newBulletCollider.collisionBulletIndex = collisionBulletIndex;
|
||||
newBulletCollider.offenderPlayerId = meleeBullet.offenderPlayerId;
|
||||
newBulletCollider.pushback = meleeBullet.pushback;
|
||||
newBulletCollider.hitStunFrames = meleeBullet.hitStunFrames;
|
||||
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
||||
colliderBullets.set(collisionBulletIndex, newBulletCollider);
|
||||
console.log(`A meleeBullet=${JSON.stringify(meleeBullet)} is added to collisionSys at renderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended`);
|
||||
}
|
||||
}
|
||||
|
||||
collisionSys.update();
|
||||
const result1 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
||||
|
||||
colliderBullets.forEach((bulletCollider, collisionBulletIndex) => {
|
||||
const potentials = bulletCollider.potentials();
|
||||
let shouldRemove = false;
|
||||
for (const potential of potentials) {
|
||||
if (null != potential.playerId && potential.playerId == bulletCollider.offenderPlayerId) continue;
|
||||
if (!bulletCollider.collides(potential, result1)) continue;
|
||||
if (null != potential.playerId) {
|
||||
const joinIndex = potential.collisionPlayerIndex - self.collisionPlayerIndexPrefix;
|
||||
bulletPushbacks[joinIndex - 1][0] += result1.overlap_x * bulletCollider.pushback;
|
||||
bulletPushbacks[joinIndex - 1][1] += result1.overlap_y * bulletCollider.pushback;
|
||||
const thatAckedPlayerInNextFrame = nextRenderFramePlayers[potential.playerId];
|
||||
thatAckedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
|
||||
const oldFrameToRecover = thatAckedPlayerInNextFrame.framesToRecover;
|
||||
thatAckedPlayerInNextFrame.framesToRecover = oldFrameToRecover > bulletCollider.hitStunFrames ? oldFrameToRecover : bulletCollider.hitStunFrames; // In case the hit player is already stun, we take the larger "hitStunFrames"
|
||||
}
|
||||
shouldRemove = true;
|
||||
}
|
||||
if (shouldRemove) {
|
||||
removedBulletsAtCurrFrame.add(collisionBulletIndex);
|
||||
}
|
||||
});
|
||||
|
||||
for (let k in currRenderFrame.meleeBullets) {
|
||||
const meleeBullet = currRenderFrame.meleeBullets[k];
|
||||
// [WARNING] remove from collisionSys ANYWAY for the convenience of rollback
|
||||
const collisionBulletIndex = self.collisionBulletIndexPrefix + meleeBullet.battleLocalId;
|
||||
if (collisionSysMap.has(collisionBulletIndex)) {
|
||||
const bulletCollider = collisionSysMap.get(collisionBulletIndex);
|
||||
bulletCollider.remove();
|
||||
collisionSysMap.delete(collisionBulletIndex);
|
||||
}
|
||||
if (removedBulletsAtCurrFrame.has(collisionBulletIndex)) continue;
|
||||
toRet.meleeBullets.push(meleeBullet);
|
||||
}
|
||||
|
||||
if (null != delayedInputFrame) {
|
||||
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(currRenderFrame.id - 1, self.inputDelayFrames));
|
||||
const inputList = delayedInputFrame.inputList;
|
||||
// Process player inputs
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
effPushbacks[joinIndex - 1] = [0.0, 0.0];
|
||||
const playerRichInfo = self.playerRichInfoArr[j];
|
||||
const playerId = playerRichInfo.id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const player = currRenderFrame.players[playerId];
|
||||
if (0 < nextRenderFramePlayers[playerId].framesToRecover) {
|
||||
// No need to process inputs for this player, but there might be bullet pushbacks on this player
|
||||
if (0 != bulletPushbacks[joinIndex - 1][0] || 0 != bulletPushbacks[joinIndex - 1][1]) {
|
||||
playerCollider.x += bulletPushbacks[joinIndex - 1][0];
|
||||
playerCollider.y += bulletPushbacks[joinIndex - 1][1];
|
||||
console.log(`playerId=${playerId}, joinIndex=${joinIndex} is pushbacked back by ${bulletPushbacks[joinIndex - 1]} by bullet impacts, now its framesToRecover is ${player.framesToRecover}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const decodedInput = self.ctrl.decodeInput(inputList[joinIndex - 1]);
|
||||
|
||||
const prevDecodedInput = (null == delayedInputFrameForPrevRenderFrame ? null : self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[joinIndex - 1]));
|
||||
const prevBtnALevel = (null == prevDecodedInput ? 0 : prevDecodedInput.btnALevel);
|
||||
|
||||
if (1 == decodedInput.btnALevel && 0 == prevBtnALevel) {
|
||||
console.log(`playerId=${playerId} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
nextRenderFramePlayers[playerId].framesToRecover = PunchAtkConfig.recoveryFrames;
|
||||
const punch = window.pb.protos.MeleeBullet.create(PunchAtkConfig);
|
||||
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
|
||||
punch.offenderJoinIndex = joinIndex;
|
||||
punch.offenderPlayerId = playerId;
|
||||
punch.originatedRenderFrameId = currRenderFrame.id;
|
||||
toRet.meleeBullets.push(punch);
|
||||
console.log(`A rising-edge of meleeBullet=${JSON.stringify(punch)} is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Atk1[0];
|
||||
} else if (0 == decodedInput.btnALevel && 1 == prevBtnALevel) {
|
||||
console.log(`playerId=${playerId} triggered a falling-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
|
||||
} else {
|
||||
// No trigger, process movement inputs
|
||||
if (0 != decodedInput.dx || 0 != decodedInput.dy) {
|
||||
// Update directions and thus would eventually update moving animation accordingly
|
||||
nextRenderFramePlayers[playerId].dir.dx = decodedInput.dx;
|
||||
nextRenderFramePlayers[playerId].dir.dy = decodedInput.dy;
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Walking[0];
|
||||
} else {
|
||||
nextRenderFramePlayers[playerId].characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||
}
|
||||
const movement = self.virtualGridToWorldPos(decodedInput.dx + player.speed * decodedInput.dx, decodedInput.dy + player.speed * decodedInput.dy);
|
||||
playerCollider.x += movement[0];
|
||||
playerCollider.y += movement[1];
|
||||
}
|
||||
}
|
||||
|
||||
collisionSys.update(); // by now all "bulletCollider"s are removed
|
||||
const result2 = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
|
||||
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
const playerId = self.playerRichInfoArr[j].id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const potentials = playerCollider.potentials();
|
||||
for (const potential of potentials) {
|
||||
// Test if the player collides with the wall
|
||||
if (!playerCollider.collides(potential, result2)) continue;
|
||||
// Push the player out of the wall
|
||||
effPushbacks[joinIndex - 1][0] += result2.overlap * result2.overlap_x;
|
||||
effPushbacks[joinIndex - 1][1] += result2.overlap * result2.overlap_y;
|
||||
}
|
||||
}
|
||||
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
const playerId = self.playerRichInfoArr[j].id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j]);
|
||||
nextRenderFramePlayers[playerId].virtualGridX = newVpos[0];
|
||||
nextRenderFramePlayers[playerId].virtualGridY = newVpos[1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return toRet;
|
||||
},
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user