diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index e86b29c..ab80052 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -193,7 +193,7 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded - pPlayerFromDbInit.ColliderRadius = float64(24) // Hardcoded + pPlayerFromDbInit.ColliderRadius = float64(12) // Hardcoded pR.Players[playerId] = pPlayerFromDbInit pR.PlayerDownsyncSessionDict[playerId] = session @@ -787,26 +787,26 @@ func (pR *Room) OnDismissed() { pR.RenderFrameId = 0 pR.CurDynamicsRenderFrameId = 0 pR.InputDelayFrames = 8 - pR.NstDelayFrames = 8 + pR.NstDelayFrames = 4 pR.InputScaleFrames = uint32(2) pR.ServerFps = 60 pR.RollbackEstimatedDtMillis = 16.667 // Use fixed-and-low-precision to mitigate the inconsistent floating-point-number issue between Golang and JavaScript pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for preventing FAST FRAME - dilutionFactor := 8 + dilutionFactor := 12 pR.dilutedRollbackEstimatedDtNanos = int64(16666666 * (dilutionFactor) / (dilutionFactor - 1)) // [WARNING] Only used in controlling "battleMainLoop" to be keep a frame rate lower than that of the frontends, such that upon resync(i.e. BackendDynamicsEnabled=true), the frontends would have bigger chances to keep up with or even surpass the backend calculation pR.BattleDurationFrames = 30 * pR.ServerFps pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1) pR.InputFrameUpsyncDelayTolerance = 2 - pR.MaxChasingRenderFramesPerUpdate = 8 + pR.MaxChasingRenderFramesPerUpdate = 5 pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work! punchSkillId := int32(1) pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0) pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{ // for offender - StartupFrames: int32(18), - ActiveFrames: int32(42), - RecoveryFrames: int32(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 + StartupFrames: int32(23), + ActiveFrames: int32(3), + RecoveryFrames: int32(61), // 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: int32(61), RecoveryFramesOnHit: int32(61), Moveforward: &Vec2D{ @@ -822,7 +822,7 @@ func (pR *Room) OnDismissed() { // for defender HitStunFrames: int32(18), BlockStunFrames: int32(9), - Pushback: float64(22.0), + Pushback: float64(11.0), ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge Damage: int32(5), } @@ -1244,20 +1244,20 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF offenderCollider := collisionSysMap[collisionOffenderIndex] offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId] - xfac, yfac := float64(1.0), float64(0) // By now, straight Punch offset doesn't respect "y-axis" + xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis" if 0 > offender.DirX { xfac = float64(-1.0) } + offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, pR.VirtualGridToWorldRatio) + bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy - bulletCx, bulletCy := offenderCollider.X+xfac*meleeBullet.HitboxOffset, offenderCollider.Y+yfac*meleeBullet.HitboxOffset - - newBulletCollider := GenerateRectColliderInCollisionSpace(bulletCx, bulletCy, xfac*meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, "MeleeBullet") + newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "MeleeBullet") newBulletCollider.Data = meleeBullet pR.Space.Add(newBulletCollider) collisionSysMap[collisionBulletIndex] = newBulletCollider bulletColliders[collisionBulletIndex] = newBulletCollider - Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet is added to collisionSys at currRenderFrame.id=%v as start-up frames ended and active frame is not yet ended: %v, from offenderCollider=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(newBulletCollider.Shape.(*resolv.ConvexPolygon)), ConvexPolygonStr(offenderCollider.Shape.(*resolv.ConvexPolygon)))) + Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet is added to collisionSys at currRenderFrame.id=%v as start-up frames ended and active frame is not yet ended: %v, from offenderCollider=%v, xfac=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(newBulletCollider.Shape.(*resolv.ConvexPolygon)), ConvexPolygonStr(offenderCollider.Shape.(*resolv.ConvexPolygon)), xfac)) } } @@ -1267,13 +1267,11 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon) if collision := bulletCollider.Check(0, 0); collision != nil { - // FIXME: A bullet going to the "right" can hit a wall or a player (though couldn't identify the player), but if it goes to the "left" then it couldn't hit anything, why? offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId] - Logger.Info(fmt.Sprintf("roomId=%v, a meleeBullet collides w/ sth at currRenderFrame.id=%v: %v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(bulletShape))) for _, obj := range collision.Objects { + defenderShape := obj.Shape.(*resolv.ConvexPolygon) switch t := obj.Data.(type) { - case Player: - defenderShape := obj.Shape.(*resolv.ConvexPolygon) + case *Player: if meleeBullet.OffenderPlayerId != t.Id { if overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape); overlapped { xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis" @@ -1286,15 +1284,14 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF if meleeBullet.HitStunFrames > oldFramesToRecover { nextRenderFramePlayers[t.Id].FramesToRecover = meleeBullet.HitStunFrames } + Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet collides w/ player at currRenderFrame.id=%v: b=%v, p=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape))) } } default: - Logger.Debug(fmt.Sprintf("Bullet %v collided with non-player: roomId=%v, currRenderFrame.Id=%v, delayedInputFrame.Id=%v", bulletShape, pR.Id, currRenderFrame.Id, delayedInputFrame.InputFrameId)) + Logger.Debug(fmt.Sprintf("Bullet %v collided with non-player %v: roomId=%v, currRenderFrame.Id=%v, delayedInputFrame.Id=%v, objDataType=%t, objData=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape), pR.Id, currRenderFrame.Id, delayedInputFrame.InputFrameId, obj.Data, obj.Data)) } } shouldRemove = true - } else { - Logger.Info(fmt.Sprintf("roomId=%v, meleeBullet doesn't collider w/ anything at currRenderFrame.id=%v: %v; p1=%v, p2=%v; bulletSpace=%p, p1Space=%p, p2Space=%p", pR.Id, currRenderFrame.Id, ConvexPolygonStr(bulletShape), ConvexPolygonStr(collisionSysMap[COLLISION_PLAYER_INDEX_PREFIX+1].Shape.(*resolv.ConvexPolygon)), ConvexPolygonStr(collisionSysMap[COLLISION_PLAYER_INDEX_PREFIX+2].Shape.(*resolv.ConvexPolygon)), bulletCollider.Space, collisionSysMap[COLLISION_PLAYER_INDEX_PREFIX+1].Space, collisionSysMap[COLLISION_PLAYER_INDEX_PREFIX+2].Space)) } if shouldRemove { removedBulletsAtCurrFrame[collisionBulletIndex] = 1 @@ -1360,7 +1357,7 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF Logger.Info(fmt.Sprintf("roomId=%v, playerId=%v triggered a rising-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId)) } else if decodedInput.BtnALevel < prevBtnALevel { - Logger.Info(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId)) + Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId)) } else { // No bullet trigger, process movement inputs if 0 != decodedInput.Dx || 0 != decodedInput.Dy { @@ -1434,7 +1431,7 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool { func (pR *Room) refreshColliders(spaceW, spaceH int32) { // Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups" - minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) >> 1) // the approx minimum distance a player can move per frame in world coordinate + minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 1) // the approx minimum distance a player can move per frame in world coordinate pR.Space = resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled for _, player := range pR.Players { wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio) diff --git a/collider_visualizer/go.mod b/collider_visualizer/go.mod index 9d2c1eb..d9deb8b 100644 --- a/collider_visualizer/go.mod +++ b/collider_visualizer/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( dnmshared v0.0.0 + battle_srv v0.0.0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/hajimehoshi/ebiten/v2 v2.4.7 github.com/solarlune/resolv v0.5.1 @@ -26,3 +27,4 @@ require ( ) replace dnmshared => ../dnmshared +replace battle_srv => ../battle_srv diff --git a/collider_visualizer/worldColliderDisplay.go b/collider_visualizer/worldColliderDisplay.go index 0c2b0ed..8a89c11 100644 --- a/collider_visualizer/worldColliderDisplay.go +++ b/collider_visualizer/worldColliderDisplay.go @@ -1,6 +1,7 @@ package main import ( + . "battle_srv/protos" . "dnmshared" . "dnmshared/sharedprotos" "fmt" @@ -36,7 +37,7 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi virtualGridToWorldRatio := 0.1 playerDefaultSpeed := 20 minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 2) - playerColliderRadius := float64(16) + playerColliderRadius := float64(24) playerColliders := make([]*resolv.Object, len(playerPosList.Eles)) space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) for i, playerPos := range playerPosList.Eles { @@ -84,6 +85,75 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y)) } } + meleeBullet := &MeleeBullet{ + // for offender + StartupFrames: int32(18), + ActiveFrames: int32(1), + RecoveryFrames: int32(61), + RecoveryFramesOnBlock: int32(61), + RecoveryFramesOnHit: int32(61), + Moveforward: &Vec2D{ + X: 0, + Y: 0, + }, + HitboxOffset: float64(24.0), + HitboxSize: &Vec2D{ + X: float64(45.0), + Y: float64(32.0), + }, + + // for defender + HitStunFrames: int32(18), + BlockStunFrames: int32(9), + Pushback: float64(22.0), + ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge + Damage: int32(5), + } + bulletLeftToRight := true + if bulletLeftToRight { + xfac := float64(1.0) + offenderWx, offenderWy := playerPosList.Eles[0].X, playerPosList.Eles[0].Y + bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy + + newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet") + space.Add(newBulletCollider) + bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon) + Logger.Warn(fmt.Sprintf("bullet ->: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape))) + + if collision := newBulletCollider.Check(0, 0); collision != nil { + for _, obj := range collision.Objects { + objShape := obj.Shape.(*resolv.ConvexPolygon) + if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped { + Logger.Warn(fmt.Sprintf("bullet ->: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY)) + } else { + Logger.Warn(fmt.Sprintf("bullet ->: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult)) + } + } + } + } + + bulletRightToLeft := true + if bulletRightToLeft { + xfac := float64(-1.0) + offenderWx, offenderWy := playerPosList.Eles[1].X, playerPosList.Eles[1].Y + bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy + + newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet") + space.Add(newBulletCollider) + bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon) + Logger.Warn(fmt.Sprintf("bullet <-: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape))) + + if collision := newBulletCollider.Check(0, 0); collision != nil { + for _, obj := range collision.Objects { + objShape := obj.Shape.(*resolv.ConvexPolygon) + if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped { + Logger.Warn(fmt.Sprintf("bullet <-: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY)) + } else { + Logger.Warn(fmt.Sprintf("bullet <-: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult)) + } + } + } + } return world } @@ -98,6 +168,9 @@ func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) { if o.HasTags("Player") { drawColor := color.RGBA{0, 255, 0, 255} DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor) + } else if o.HasTags("MeleeBullet") { + drawColor := color.RGBA{0, 0, 255, 255} + DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor) } else { drawColor := color.RGBA{60, 60, 60, 255} DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor) diff --git a/frontend/assets/resources/prefabs/ControlledCharacter.prefab b/frontend/assets/resources/prefabs/ControlledCharacter.prefab index 297775a..55b81b1 100644 --- a/frontend/assets/resources/prefabs/ControlledCharacter.prefab +++ b/frontend/assets/resources/prefabs/ControlledCharacter.prefab @@ -529,8 +529,8 @@ 0, 0, 1, - 1, - 1, + 0.5, + 0.5, 1 ] }, @@ -648,7 +648,7 @@ "_N$_defaultCacheMode": 0, "_N$timeScale": 1, "_N$debugBones": false, - "_N$enableBatch": false, + "_N$enableBatch": true, "_id": "" }, { @@ -763,7 +763,7 @@ "_N$_defaultCacheMode": 0, "_N$timeScale": 1, "_N$debugBones": false, - "_N$enableBatch": false, + "_N$enableBatch": true, "_id": "" }, { @@ -878,7 +878,7 @@ "_N$_defaultCacheMode": 0, "_N$timeScale": 1, "_N$debugBones": false, - "_N$enableBatch": false, + "_N$enableBatch": true, "_id": "" }, { diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index e3317d0..b327a6c 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -761,7 +761,7 @@ cc.Class({ newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1])); playerScriptIns.mapNode = self.node; - const cpos = self.virtualGridToPlayerColliderPos(vx, vy, playerDownsyncInfo); + const cpos = self.virtualGridToPolygonColliderAnchorPos(vx, vy, playerDownsyncInfo.colliderRadius, playerDownsyncInfo.colliderRadius); const d = playerDownsyncInfo.colliderRadius * 2, x0 = cpos[0], y0 = cpos[1]; @@ -1044,7 +1044,7 @@ cc.Class({ const newVx = currPlayerDownsync.virtualGridX; const newVy = currPlayerDownsync.virtualGridY; - const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1]); + const newCpos = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius); playerCollider.x = newCpos[0]; playerCollider.y = newCpos[1]; } @@ -1064,15 +1064,16 @@ cc.Class({ const offenderCollider = collisionSysMap.get(collisionOffenderIndex); const offender = currRenderFrame.players[meleeBullet.offenderPlayerId]; - let xfac = 1, - yfac = 0; // By now, straight Punch offset doesn't respect "y-axis" + let xfac = 1; // 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); + const offenderWpos = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY); + const bulletWx = offenderWpos[0] + xfac * meleeBullet.hitboxOffset; + const bulletWy = offenderWpos[1]; + const bulletCpos = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5); + const pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]]; + const newBulletCollider = collisionSys.createPolygon(bulletCpos[0], bulletCpos[1], pts); newBulletCollider.data = meleeBullet; collisionSysMap.set(collisionBulletIndex, newBulletCollider); bulletColliders.set(collisionBulletIndex, newBulletCollider); @@ -1208,7 +1209,7 @@ cc.Class({ 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]); + 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]; thatPlayerInNextFrame.virtualGridX = newVpos[0]; thatPlayerInNextFrame.virtualGridY = newVpos[1]; @@ -1340,23 +1341,23 @@ cc.Class({ return [wx, wy]; }, - playerWorldToCollisionPos(wx, wy, playerRichInfo) { - return [wx - playerRichInfo.colliderRadius, wy - playerRichInfo.colliderRadius]; + worldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH) { + return [wx - halfBoundingW, wy - halfBoundingH]; }, - playerColliderAnchorToWorldPos(cx, cy, playerRichInfo) { - return [cx + playerRichInfo.colliderRadius, cy + playerRichInfo.colliderRadius]; + polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH) { + return [cx + halfBoundingW, cy + halfBoundingH]; }, - playerColliderAnchorToVirtualGridPos(cx, cy, playerRichInfo) { + polygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH) { const self = this; - const wpos = self.playerColliderAnchorToWorldPos(cx, cy, playerRichInfo); + const wpos = self.polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH); return self.worldToVirtualGridPos(wpos[0], wpos[1]) }, - virtualGridToPlayerColliderPos(vx, vy, playerRichInfo) { + virtualGridToPolygonColliderAnchorPos(vx, vy, halfBoundingW, halfBoundingH) { const self = this; const wpos = self.virtualGridToWorldPos(vx, vy); - return self.playerWorldToCollisionPos(wpos[0], wpos[1], playerRichInfo) + return self.worldToPolygonColliderAnchorPos(wpos[0], wpos[1], halfBoundingW, halfBoundingH) }, }); diff --git a/frontend/assets/scripts/OfflineMap.js b/frontend/assets/scripts/OfflineMap.js index ded4aca..5f18bf2 100644 --- a/frontend/assets/scripts/OfflineMap.js +++ b/frontend/assets/scripts/OfflineMap.js @@ -42,8 +42,8 @@ cc.Class({ self.meleeSkillConfig = { 1: { // for offender - startupFrames: 18, - activeFrames: 42, + startupFrames: 23, + activeFrames: 3, 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, @@ -60,7 +60,7 @@ cc.Class({ // for defender hitStunFrames: 18, blockStunFrames: 9, - pushback: 22.0, + pushback: 11.0, releaseTriggerType: 1, // 1: rising-edge, 2: falling-edge damage: 5 } @@ -140,7 +140,7 @@ cc.Class({ joinIndex: 1, virtualGridX: 0, virtualGridY: 0, - speed: 2 * self.worldToVirtualGridRatio, + speed: 1 * self.worldToVirtualGridRatio, colliderRadius: 12, characterState: window.ATK_CHARACTER_STATE.Idle1[0], framesToRecover: 0, @@ -152,7 +152,7 @@ cc.Class({ joinIndex: 2, virtualGridX: 80 * self.worldToVirtualGridRatio, virtualGridY: 40 * self.worldToVirtualGridRatio, - speed: 2 * self.worldToVirtualGridRatio, + speed: 1 * self.worldToVirtualGridRatio, colliderRadius: 12, characterState: window.ATK_CHARACTER_STATE.Idle1[0], framesToRecover: 0,