Draft version for jumping with backend sync.

This commit is contained in:
genxium
2022-12-13 18:10:30 +08:00
parent 5a463239bb
commit c171ebc211
9 changed files with 342 additions and 323 deletions

View File

@@ -128,7 +128,7 @@ cc.Class({
_interruptPlayingAnimAndPlayNewAnimDragonBones(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl, playingAnimName) {
if (window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) {
// No "framesToRecover"
console.warn(`#DragonBones JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
// console.warn(`#DragonBones JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, 0, -1);
} else {
const animationData = underlyingAnimationCtrl._animations[newAnimName];

View File

@@ -1079,7 +1079,7 @@ cc.Class({
velX: currPlayerDownsync.velX,
velY: currPlayerDownsync.velY,
characterState: currPlayerDownsync.characterState,
inAir: true, // will be updated if collided with a barrier with "0 > pushbackY"
inAir: true,
speed: currPlayerDownsync.speed,
battleState: currPlayerDownsync.battleState,
score: currPlayerDownsync.score,
@@ -1106,7 +1106,6 @@ cc.Class({
const joinIndex = parseInt(j) + 1;
const playerRichInfo = self.playerRichInfoArr[j];
const playerId = playerRichInfo.id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const [currPlayerDownsync, thatPlayerInNextFrame] = [currRenderFrame.players[playerId], nextRenderFramePlayers[playerId]];
if (0 < thatPlayerInNextFrame.framesToRecover) {
// No need to process inputs for this player, but there might be bullet pushbacks on this player
@@ -1131,23 +1130,19 @@ cc.Class({
}
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}`);
if (self.bulletTriggerEnabled) {
const punchSkillId = 1;
const punch = window.pb.protos.MeleeBullet.create(self.meleeSkillConfig[punchSkillId]);
thatPlayerInNextFrame.framesToRecover = punch.recoveryFrames;
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
punch.offenderJoinIndex = joinIndex;
punch.offenderPlayerId = playerId;
punch.originatedRenderFrameId = currRenderFrame.id;
nextRenderFrameMeleeBullets.push(punch);
// console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}: ${self._stringifyRecentInputCache(true)}`);
console.log(`A rising-edge of meleeBullet is created at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
const punchSkillId = 1;
const punch = window.pb.protos.MeleeBullet.create(self.meleeSkillConfig[punchSkillId]);
thatPlayerInNextFrame.framesToRecover = punch.recoveryFrames;
punch.battleLocalId = self.bulletBattleLocalIdCounter++;
punch.offenderJoinIndex = joinIndex;
punch.offenderPlayerId = playerId;
punch.originatedRenderFrameId = currRenderFrame.id;
nextRenderFrameMeleeBullets.push(punch);
// console.log(`playerId=${playerId}, joinIndex=${joinIndex} triggered a rising-edge of btnA at renderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.inputFrameId}`);
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
if (false == currPlayerDownsync.inAir) {
thatPlayerInNextFrame.velX = 0; // prohibits simultaneous movement with Atk1 on the ground
}
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atk1[0];
if (false == currPlayerDownsync.inAir) {
thatPlayerInNextFrame.velX = 0; // prohibits simultaneous movement with Atk1 on the ground
}
} 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}`);
@@ -1215,7 +1210,6 @@ cc.Class({
newBulletCollider.data = meleeBullet;
collisionSysMap.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)}`);
}
}
@@ -1231,35 +1225,48 @@ cc.Class({
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const potentials = playerCollider.potentials();
hardPushbackNorms[joinIndex - 1] = self.calcHardPushbacksNorms(playerCollider, potentials, result, self.snapIntoPlatformThreshold, self.snapIntoPlatformOverlap, joinIndex, effPushbacks);
hardPushbackNorms[joinIndex - 1] = self.calcHardPushbacksNorms(playerCollider, potentials, result, self.snapIntoPlatformOverlap, effPushbacks[joinIndex - 1]);
const [currPlayerDownsync, thatPlayerInNextFrame] = [currRenderFrame.players[playerId], nextRenderFramePlayers[playerId]];
let fallStopping = false;
let possiblyFallStoppedOnAnotherPlayer = false;
for (const potential of potentials) {
let [isBarrier, isAnotherPlayer, isBullet] = [true == potential.data.hardPushback, null != potential.data.joinIndex, null != potential.data.offenderJoinIndex];
// ignore bullets for this step
if (null != potential.data && null != potential.data.offenderJoinIndex) continue;
if (isBullet) continue;
// Test if the player collides with the wall/another player
if (!playerCollider.collides(potential, result)) continue;
const normAlignmentWithGravity = (result.overlap_x * 0 + result.overlap_y * (-1.0));
const landedOnGravityPushback = (self.snapIntoPlatformThreshold < normAlignmentWithGravity); // prevents false snapping on the lateral sides
// Push the player out of the wall/another player
let [pushbackX, pushbackY] = [result.overlap * result.overlap_x, result.overlap * result.overlap_y];
let pushback = [result.overlap * result.overlap_x, result.overlap * result.overlap_y];
if (landedOnGravityPushback) {
// kindly note that one player might land on top of another player, and snapping is also required in such case
[pushbackX, pushbackY] = [(result.overlap - self.snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - self.snapIntoPlatformOverlap) * result.overlap_y];
}
// kindly note that one player might land on top of another player, and snapping is also required in such case
pushback = [(result.overlap - self.snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - self.snapIntoPlatformOverlap) * result.overlap_y];
thatPlayerInNextFrame.inAir = false;
}
for (let hardPushbackNorm of hardPushbackNorms[joinIndex - 1]) {
// remove pushback component on the directions of "hardPushbackNorms[joinIndex-1]" (by now those hardPushbacks are already accounted in "effPushbacks[joinIndex-1]")
const projectedMagnitude = pushbackX * hardPushbackNorm[0] + pushbackY * hardPushbackNorm[1];
pushbackX -= projectedMagnitude * hardPushbackNorm[0];
pushbackY -= projectedMagnitude * hardPushbackNorm[1];
const projectedMagnitude = pushback[0] * hardPushbackNorm[0] + pushback[1] * hardPushbackNorm[1];
if (isBarrier
||
(isAnotherPlayer && 0 > projectedMagnitude)
) {
// [WARNING] Pushing by another player is different from pushing by barrier!
// Otherwise the player couldn't be pushed by another player to opposite dir of a side wall
pushback[0] -= projectedMagnitude * hardPushbackNorm[0];
pushback[1] -= projectedMagnitude * hardPushbackNorm[1];
}
}
if (currPlayerDownsync.inAir && landedOnGravityPushback) {
fallStopping = true;
if (isAnotherPlayer) {
possiblyFallStoppedOnAnotherPlayer = true;
}
}
thatPlayerInNextFrame.inAir &= !landedOnGravityPushback;
fallStopping |= (currPlayerDownsync.inAir && landedOnGravityPushback);
effPushbacks[joinIndex - 1][0] += pushbackX;
effPushbacks[joinIndex - 1][1] += pushbackY;
effPushbacks[joinIndex - 1][0] += pushback[0];
effPushbacks[joinIndex - 1][1] += pushback[1];
}
if (fallStopping) {
@@ -1267,6 +1274,9 @@ cc.Class({
thatPlayerInNextFrame.velY = 0;
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
thatPlayerInNextFrame.framesToRecover = 0;
if (possiblyFallStoppedOnAnotherPlayer) {
console.log(`playerId=${playerId}, joinIndex=${joinIndex} possiblyFallStoppedOnAnotherPlayer with effPushback=${effPushbacks[joinIndex - 1]} at renderFrame.id=${currRenderFrame.id}`);
}
}
if (currPlayerDownsync.inAir) {
thatPlayerInNextFrame.characterState = window.toInAirConjugate(thatPlayerInNextFrame.characterState);
@@ -1282,21 +1292,27 @@ cc.Class({
if (null != potential.data && potential.data.joinIndex == bulletCollider.data.offenderJoinIndex) continue;
if (!bulletCollider.collides(potential, result)) continue;
if (null != potential.data && null != potential.data.joinIndex) {
const playerId = potential.data.id;
const joinIndex = potential.data.joinIndex;
let xfac = 1;
if (0 > offender.dirX) {
xfac = -1;
}
let [pushbackX, pushbackY] = [-xfac * bulletCollider.data.pushback, 0]; // Only for straight punch, there's no y-pushback
// Only for straight punch, there's no y-pushback
let bulletPushback = [-xfac * bulletCollider.data.pushback, 0];
// console.log(`playerId=${playerId}, joinIndex=${joinIndex} is supposed to be pushed back by meleeBullet for bulletPushback=${JSON.stringify(bulletPushback)} at renderFrame.id=${currRenderFrame.id}`);
for (let hardPushbackNorm of hardPushbackNorms[joinIndex - 1]) {
// remove pushback component on the directions of "hardPushbackNorms[joinIndex-1]" (by now those hardPushbacks are already accounted in "effPushbacks[joinIndex-1]")
const projectedMagnitude = pushbackX * hardPushbackNorm[0] + pushbackY * hardPushbackNorm[1];
pushbackX -= projectedMagnitude * hardPushbackNorm[0];
pushbackY -= projectedMagnitude * hardPushbackNorm[1];
// TODO: What if a bullet knocks down the attacked player into ground?
const projectedMagnitude = bulletPushback[0] * hardPushbackNorm[0] + bulletPushback[1] * hardPushbackNorm[1];
if (0 > projectedMagnitude) {
// Otherwise when smashing into a wall the atked player would be pushed into the wall first and only got back in the next renderFrame, not what I want here
bulletPushback[0] -= (projectedMagnitude * hardPushbackNorm[0]);
bulletPushback[1] -= (projectedMagnitude * hardPushbackNorm[1]);
// console.log(`playerId=${playerId}, joinIndex=${joinIndex} reducing bulletPushback=${JSON.stringify(bulletPushback)} by ${JSON.stringify([projectedMagnitude * hardPushbackNorm[0], projectedMagnitude * hardPushbackNorm[1]])} where hardPushbackNorm=${JSON.stringify(hardPushbackNorm)}, projectedMagnitude=${projectedMagnitude} at renderFrame.id=${currRenderFrame.id}`);
}
}
effPushbacks[joinIndex - 1][0] += pushbackX;
effPushbacks[joinIndex - 1][1] += pushbackY;
// console.log(`playerId=${playerId}, joinIndex=${joinIndex} is actually pushed back by meleeBullet for bulletPushback=${JSON.stringify(bulletPushback)} at renderFrame.id=${currRenderFrame.id}`);
effPushbacks[joinIndex - 1][0] += bulletPushback[0];
effPushbacks[joinIndex - 1][1] += bulletPushback[1];
const [atkedPlayerInCurFrame, atkedPlayerInNextFrame] = [currRenderFrame.players[potential.data.id], nextRenderFramePlayers[potential.data.id]];
atkedPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Atked1[0];
if (atkedPlayerInCurFrame.inAir) {
@@ -1331,6 +1347,7 @@ cc.Class({
const playerId = self.playerRichInfoArr[j].id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
// Update "virtual grid position"
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
[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);
}
@@ -1478,21 +1495,20 @@ cc.Class({
return self.worldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH)
},
calcHardPushbacksNorms(collider, potentials, result, snapIntoPlatformThreshold, snapIntoPlatformOverlap, joinIndex, effPushbacks) {
let hardPushbackNorms = [];
let fallStopping = false;
calcHardPushbacksNorms(collider, potentials, result, snapIntoPlatformOverlap, effPushback) {
let ret = [];
for (const potential of potentials) {
if (null == potential.data || !(true == potential.data.hardPushback)) continue;
if (!collider.collides(potential, result)) continue;
// allow snapping into all colliders with {hardPushback: true}
// ALWAY snap into hardPushbacks!
// [overlay_x, overlap_y] is the unit vector that points into the platform
const [pushbackX, pushbackY] = [(result.overlap - snapIntoPlatformOverlap) * result.overlap_x, (result.overlap - snapIntoPlatformOverlap) * result.overlap_y];
// [overlay_x, overlap_y] is the unit vector that points into the platform; FIXME: Should only assign to [snappedIntoPlatformEx, snappedIntoPlatformEy] at most once!
hardPushbackNorms.push([result.overlap_x, result.overlap_y]);
effPushbacks[joinIndex - 1][0] += pushbackX;
effPushbacks[joinIndex - 1][1] += pushbackY;
ret.push([result.overlap_x, result.overlap_y]);
effPushback[0] += pushbackX;
effPushback[1] += pushbackY;
}
return hardPushbackNorms;
return ret;
},
});

View File

@@ -75,7 +75,7 @@ cc.Class({
Moreover, "snapIntoPlatformOverlap" should be small enough such that the walking "velX" or jumping initial "velY" can escape from it by 1 renderFrame (when jumping is triggered, the character is waived from snappig for 1 renderFrame).
*/
self.snapIntoPlatformOverlap = 0.1;
self.snapIntoPlatformOverlap = 0.01;
self.snapIntoPlatformThreshold = 0.5; // a platform must be "horizontal enough" for a character to "stand on"
self.jumpingInitVelY = 6 * self.worldToVirtualGridRatio; // unit: (virtual grid length/renderFrame)
[self.gravityX, self.gravityY] = [0, -Math.ceil(4 * self.jumpingInitVelY / self.serverFps)]; // unit: (virtual grid length/renderFrame^2)