mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 11:48:56 +00:00
Added more helper functions for backend collision debugging.
This commit is contained in:
parent
89a54211e1
commit
bd9beec5e5
@ -201,7 +201,7 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke
|
|||||||
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 = float64(24) // Hardcoded
|
||||||
|
|
||||||
pR.Players[playerId] = pPlayerFromDbInit
|
pR.Players[playerId] = pPlayerFromDbInit
|
||||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||||
@ -234,7 +234,7 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso
|
|||||||
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(12) // Hardcoded
|
pEffectiveInRoomPlayerInstance.ColliderRadius = float64(16) // Hardcoded
|
||||||
|
|
||||||
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
|
||||||
@ -252,7 +252,7 @@ func (pR *Room) ChooseStage() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
stageNameList := []string{ /*"simple" ,*/ "richsoil"}
|
stageNameList := []string{"simple" /* "richsoil" */}
|
||||||
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
|
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
|
||||||
|
|
||||||
pR.StageName = stageNameList[chosenStageIndex]
|
pR.StageName = stageNameList[chosenStageIndex]
|
||||||
@ -792,7 +792,7 @@ func (pR *Room) OnDismissed() {
|
|||||||
// Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference.
|
// Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference.
|
||||||
pR.WorldToVirtualGridRatio = float64(10)
|
pR.WorldToVirtualGridRatio = float64(10)
|
||||||
pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations
|
pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations
|
||||||
pR.PlayerDefaultSpeed = 10 // Hardcoded in virtual grids per frame
|
pR.PlayerDefaultSpeed = 20 // Hardcoded in virtual grids per frame
|
||||||
pR.Players = make(map[int32]*Player)
|
pR.Players = make(map[int32]*Player)
|
||||||
pR.PlayersArr = make([]*Player, pR.Capacity)
|
pR.PlayersArr = make([]*Player, pR.Capacity)
|
||||||
pR.CollisionSysMap = make(map[int32]*resolv.Object)
|
pR.CollisionSysMap = make(map[int32]*resolv.Object)
|
||||||
@ -940,7 +940,7 @@ func (pR *Room) onPlayerAdded(playerId int32) {
|
|||||||
if nil == playerPos {
|
if nil == playerPos {
|
||||||
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 = pR.worldToVirtualGridPos(playerPos.X, playerPos.Y)
|
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1240,15 +1240,19 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
|||||||
currPlayerDownsync := currRenderFrame.Players[playerId]
|
currPlayerDownsync := currRenderFrame.Players[playerId]
|
||||||
encodedInput := inputList[joinIndex-1]
|
encodedInput := inputList[joinIndex-1]
|
||||||
decodedInput := DIRECTION_DECODER[encodedInput]
|
decodedInput := DIRECTION_DECODER[encodedInput]
|
||||||
newVx := (currPlayerDownsync.VirtualGridX + (decodedInput[0] + decodedInput[0]*currPlayerDownsync.Speed))
|
proposedVirtualGridDx, proposedVirtualGridDy := (decodedInput[0] + decodedInput[0]*currPlayerDownsync.Speed), (decodedInput[1] + decodedInput[1]*currPlayerDownsync.Speed)
|
||||||
newVy := (currPlayerDownsync.VirtualGridY + (decodedInput[1] + decodedInput[1]*currPlayerDownsync.Speed))
|
newVx, newVy := (currPlayerDownsync.VirtualGridX + proposedVirtualGridDx), (currPlayerDownsync.VirtualGridY + proposedVirtualGridDy)
|
||||||
// Reset playerCollider position from the "virtual grid position"
|
// Reset playerCollider position from the "virtual grid position"
|
||||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||||
playerCollider.X, playerCollider.Y = pR.virtualGridToPlayerColliderPos(newVx, newVy, player)
|
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderAnchorPos(newVx, newVy, player.ColliderRadius, player.ColliderRadius, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
|
||||||
|
|
||||||
// Update in the collision system
|
// Update in the collision system
|
||||||
playerCollider.Update()
|
playerCollider.Update()
|
||||||
|
|
||||||
|
if 0 < encodedInput {
|
||||||
|
Logger.Debug(fmt.Sprintf("Moved playerId=%v: virtual (%d, %d) -> (%d, %d), now playerCollider at (%.2f, %.2f)", playerId, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, newVx, newVy, playerCollider.X, playerCollider.Y))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle pushbacks upon collision after all movements treated as simultaneous
|
// handle pushbacks upon collision after all movements treated as simultaneous
|
||||||
@ -1256,17 +1260,17 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
|||||||
joinIndex := player.JoinIndex
|
joinIndex := player.JoinIndex
|
||||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||||
oldDx, oldDy := float64(0), float64(0)
|
if collision := playerCollider.Check(0, 0); collision != nil {
|
||||||
if collision := playerCollider.Check(oldDx, oldDy); collision != nil {
|
|
||||||
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
|
Logger.Warn(fmt.Sprintf("Collided: a=%v", ConvexPolygonStr(playerShape)))
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
if overlapped, pushbackX, pushbackY, _ := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape); overlapped {
|
||||||
Logger.Debug(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
Logger.Warn(fmt.Sprintf("Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
effPushbacks[joinIndex-1].X += pushbackX
|
effPushbacks[joinIndex-1].X += pushbackX
|
||||||
effPushbacks[joinIndex-1].Y += pushbackY
|
effPushbacks[joinIndex-1].Y += pushbackY
|
||||||
} else {
|
} else {
|
||||||
Logger.Debug(fmt.Sprintf("Collided BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
|
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), overlapResult))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1278,7 +1282,7 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
|||||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||||
|
|
||||||
// Update "virtual grid position"
|
// Update "virtual grid position"
|
||||||
newVx, newVy := pR.playerColliderAnchorToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, player)
|
newVx, newVy := PolygonColliderAnchorToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, player.ColliderRadius, player.ColliderRadius, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.WorldToVirtualGridRatio)
|
||||||
nextRenderFramePlayers[playerId].VirtualGridX = newVx
|
nextRenderFramePlayers[playerId].VirtualGridX = newVx
|
||||||
nextRenderFramePlayers[playerId].VirtualGridY = newVy
|
nextRenderFramePlayers[playerId].VirtualGridY = newVy
|
||||||
}
|
}
|
||||||
@ -1296,10 +1300,10 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
|
|||||||
func (pR *Room) refreshColliders(spaceW, spaceH int32) {
|
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"
|
// Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups"
|
||||||
|
|
||||||
minStep := int(pR.PlayerDefaultSpeed) // the approx minimum distance a player can move per frame in world coordinate
|
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 2) // the approx minimum distance a player can move per frame in world coordinate
|
||||||
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
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 {
|
for _, player := range pR.Players {
|
||||||
wx, wy := pR.virtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY)
|
wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
|
||||||
playerCollider := GenerateRectCollider(wx, wy, player.ColliderRadius*2, player.ColliderRadius*2, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Player")
|
playerCollider := GenerateRectCollider(wx, wy, player.ColliderRadius*2, player.ColliderRadius*2, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Player")
|
||||||
space.Add(playerCollider)
|
space.Add(playerCollider)
|
||||||
// Keep track of the collider in "pR.CollisionSysMap"
|
// Keep track of the collider in "pR.CollisionSysMap"
|
||||||
@ -1319,37 +1323,3 @@ func (pR *Room) refreshColliders(spaceW, spaceH int32) {
|
|||||||
func (pR *Room) printBarrier(barrierCollider *resolv.Object) {
|
func (pR *Room) printBarrier(barrierCollider *resolv.Object) {
|
||||||
Logger.Info(fmt.Sprintf("Barrier in roomId=%v: w=%v, h=%v, shape=%v", pR.Id, barrierCollider.W, barrierCollider.H, barrierCollider.Shape))
|
Logger.Info(fmt.Sprintf("Barrier in roomId=%v: w=%v, h=%v, shape=%v", pR.Id, barrierCollider.W, barrierCollider.H, barrierCollider.Shape))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pR *Room) worldToVirtualGridPos(wx, wy float64) (int32, int32) {
|
|
||||||
// [WARNING] Introduces loss of precision!
|
|
||||||
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
|
||||||
var virtualGridX int32 = int32(math.Round(wx * pR.WorldToVirtualGridRatio))
|
|
||||||
var virtualGridY int32 = int32(math.Round(wy * pR.WorldToVirtualGridRatio))
|
|
||||||
return virtualGridX, virtualGridY
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pR *Room) virtualGridToWorldPos(vx, vy int32) (float64, float64) {
|
|
||||||
// No loss of precision
|
|
||||||
var wx float64 = float64(vx) * pR.VirtualGridToWorldRatio
|
|
||||||
var wy float64 = float64(vy) * pR.VirtualGridToWorldRatio
|
|
||||||
return wx, wy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pR *Room) playerWorldToCollisionPos(wx, wy float64, player *Player) (float64, float64) {
|
|
||||||
// TODO: remove this duplicate code w.r.t. "dnmshared/resolv_helper.go"
|
|
||||||
return wx - player.ColliderRadius + pR.collisionSpaceOffsetX, wy - player.ColliderRadius + pR.collisionSpaceOffsetY
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pR *Room) playerColliderAnchorToWorldPos(cx, cy float64, player *Player) (float64, float64) {
|
|
||||||
return cx + player.ColliderRadius - pR.collisionSpaceOffsetX, cy + player.ColliderRadius - pR.collisionSpaceOffsetY
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pR *Room) playerColliderAnchorToVirtualGridPos(cx, cy float64, player *Player) (int32, int32) {
|
|
||||||
wx, wy := pR.playerColliderAnchorToWorldPos(cx, cy, player)
|
|
||||||
return pR.worldToVirtualGridPos(wx, wy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pR *Room) virtualGridToPlayerColliderPos(vx, vy int32, player *Player) (float64, float64) {
|
|
||||||
wx, wy := pR.virtualGridToWorldPos(vx, vy)
|
|
||||||
return pR.playerWorldToCollisionPos(wx, wy, player)
|
|
||||||
}
|
|
||||||
|
@ -33,14 +33,15 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
spaceOffsetX := float64(spaceW) * 0.5
|
spaceOffsetX := float64(spaceW) * 0.5
|
||||||
spaceOffsetY := float64(spaceH) * 0.5
|
spaceOffsetY := float64(spaceH) * 0.5
|
||||||
|
|
||||||
playerDefaultSpeed := int32(10)
|
virtualGridToWorldRatio := 0.1
|
||||||
minStep := int(playerDefaultSpeed)
|
playerDefaultSpeed := 20
|
||||||
playerColliderRadius := float64(12)
|
minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 2)
|
||||||
|
playerColliderRadius := float64(24)
|
||||||
playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
|
playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
|
||||||
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
|
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
|
||||||
for i, playerPos := range playerPosList.Eles {
|
for i, playerPos := range playerPosList.Eles {
|
||||||
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
|
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
|
||||||
Logger.Info(fmt.Sprintf("Player Collider#%d: playerPos.X=%v, playerPos.Y=%v, radius=%v, spaceOffsetX=%v, spaceOffsetY=%v, shape=%v", i, playerPos.X, playerPos.Y, playerColliderRadius, spaceOffsetX, spaceOffsetY, playerCollider.Shape))
|
Logger.Info(fmt.Sprintf("Player Collider#%d: player world pos =(%.2f, %.2f), shape=%v", i, playerPos.X, playerPos.Y, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
|
||||||
playerColliders[i] = playerCollider
|
playerColliders[i] = playerCollider
|
||||||
space.Add(playerCollider)
|
space.Add(playerCollider)
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
barrierLocalId := 0
|
barrierLocalId := 0
|
||||||
for _, barrierUnaligned := range barrierList.Eles {
|
for _, barrierUnaligned := range barrierList.Eles {
|
||||||
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
|
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
|
||||||
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", barrierCollider.Shape))
|
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", ConvexPolygonStr(barrierCollider.Shape.(*resolv.ConvexPolygon))))
|
||||||
space.Add(barrierCollider)
|
space.Add(barrierCollider)
|
||||||
barrierLocalId++
|
barrierLocalId++
|
||||||
}
|
}
|
||||||
@ -57,22 +58,22 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
|
|
||||||
moveToCollide := true
|
moveToCollide := true
|
||||||
if moveToCollide {
|
if moveToCollide {
|
||||||
|
proposedDx, proposedDy := -50.0, -60.0
|
||||||
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
||||||
toTestPlayerCollider := playerColliders[0]
|
toTestPlayerCollider := playerColliders[0]
|
||||||
toTestPlayerCollider.X += -50.0
|
toTestPlayerCollider.X += proposedDx
|
||||||
toTestPlayerCollider.Y += -60.0
|
toTestPlayerCollider.Y += proposedDy
|
||||||
toTestPlayerCollider.Update()
|
toTestPlayerCollider.Update()
|
||||||
oldDx, oldDy := float64(0), float64(0)
|
if collision := toTestPlayerCollider.Check(0, 0); collision != nil {
|
||||||
if collision := toTestPlayerCollider.Check(oldDx, oldDy); collision != nil {
|
|
||||||
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape); overlapped {
|
||||||
Logger.Info(fmt.Sprintf("Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
Logger.Warn(fmt.Sprintf("Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
effPushback.X += pushbackX
|
effPushback.X += pushbackX
|
||||||
effPushback.Y += pushbackY
|
effPushback.Y += pushbackY
|
||||||
} else {
|
} else {
|
||||||
Logger.Info(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), overlapResult))
|
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), overlapResult))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toTestPlayerCollider.X -= effPushback.X
|
toTestPlayerCollider.X -= effPushback.X
|
||||||
|
@ -12,14 +12,15 @@ import (
|
|||||||
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
||||||
var s []string = make([]string, len(body.Points))
|
var s []string = make([]string, len(body.Points))
|
||||||
for i, p := range body.Points {
|
for i, p := range body.Points {
|
||||||
s[i] = fmt.Sprintf("[%v, %v]", p[0]+body.X, p[1]+body.Y)
|
s[i] = fmt.Sprintf("[%.2f, %.2f]", p[0]+body.X, p[1]+body.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("[%s]", strings.Join(s, ", "))
|
return fmt.Sprintf("{\n%s\n}", strings.Join(s, ",\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||||
collider := resolv.NewObject(origX-w*0.5+spaceOffsetX, origY-h*0.5+spaceOffsetY, w, h, tag)
|
cx, cy := WorldToPolygonColliderAnchorPos(origX, origY, w*0.5, h*0.5, spaceOffsetX, spaceOffsetY)
|
||||||
|
collider := resolv.NewObject(cx, cy, w, h, tag)
|
||||||
shape := resolv.NewRectangle(0, 0, w, h)
|
shape := resolv.NewRectangle(0, 0, w, h)
|
||||||
collider.SetShape(shape)
|
collider.SetShape(shape)
|
||||||
return collider
|
return collider
|
||||||
@ -219,3 +220,36 @@ func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, re
|
|||||||
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
|
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
|
||||||
|
// [WARNING] Introduces loss of precision!
|
||||||
|
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
||||||
|
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
|
||||||
|
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
|
||||||
|
return virtualGridX, virtualGridY
|
||||||
|
}
|
||||||
|
|
||||||
|
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
|
||||||
|
// No loss of precision
|
||||||
|
var wx float64 = float64(vx) * virtualGridToWorldRatio
|
||||||
|
var wy float64 = float64(vy) * virtualGridToWorldRatio
|
||||||
|
return wx, wy
|
||||||
|
}
|
||||||
|
|
||||||
|
func WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
|
||||||
|
return wx - halfBoundingW + collisionSpaceOffsetX, wy - halfBoundingH + collisionSpaceOffsetY
|
||||||
|
}
|
||||||
|
|
||||||
|
func PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
|
||||||
|
return cx + halfBoundingW - collisionSpaceOffsetX, cy + halfBoundingH - collisionSpaceOffsetY
|
||||||
|
}
|
||||||
|
|
||||||
|
func PolygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
|
||||||
|
wx, wy := PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||||
|
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VirtualGridToPolygonColliderAnchorPos(vx, vy int32, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
|
||||||
|
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
|
||||||
|
return WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</objectgroup>
|
</objectgroup>
|
||||||
<layer id="3" name="FirsrtFloor" width="50" height="50">
|
<layer id="3" name="FirsrtFloor" width="50" height="50">
|
||||||
<data encoding="base64" compression="zlib">
|
<data encoding="base64" compression="zlib">
|
||||||
eJztwQENAAAAwqD3T20ON6AAAAAAAAAAAADg3wAnEAAB
|
eJzt1jEKgDAQRNE0GtD739fGaQIhqJHdCf81aSz2oyspBQAAAADWcUYPMIEaaugU37TvwbGl9y05tYz2wWFfaMiBhhyNKzRs99n7lzo0SG1OcWqQtsWxQdSwD57L3CCjO4dDg7zd+Yye7nxmajlCp5jD6Y4OAAAA4H8XE6wBrA==
|
||||||
</data>
|
</data>
|
||||||
</layer>
|
</layer>
|
||||||
<objectgroup id="4" name="Barrier">
|
<objectgroup id="4" name="Barrier">
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"__id__": 1
|
"__id__": 1
|
||||||
},
|
},
|
||||||
"_children": [],
|
"_children": [],
|
||||||
"_active": false,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 3
|
"__id__": 3
|
||||||
@ -119,8 +119,8 @@
|
|||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 93.36,
|
"width": 46.68,
|
||||||
"height": 40
|
"height": 27.72
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@ -132,7 +132,7 @@
|
|||||||
"ctor": "Float64Array",
|
"ctor": "Float64Array",
|
||||||
"array": [
|
"array": [
|
||||||
-5,
|
-5,
|
||||||
101,
|
50,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@ -164,12 +164,16 @@
|
|||||||
"__id__": 2
|
"__id__": 2
|
||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"_materials": [],
|
"_materials": [
|
||||||
|
{
|
||||||
|
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
|
||||||
|
}
|
||||||
|
],
|
||||||
"_useOriginalSize": false,
|
"_useOriginalSize": false,
|
||||||
"_string": "(0, 0)",
|
"_string": "(0, 0)",
|
||||||
"_N$string": "(0, 0)",
|
"_N$string": "(0, 0)",
|
||||||
"_fontSize": 40,
|
"_fontSize": 20,
|
||||||
"_lineHeight": 40,
|
"_lineHeight": 22,
|
||||||
"_enableWrapText": true,
|
"_enableWrapText": true,
|
||||||
"_N$file": null,
|
"_N$file": null,
|
||||||
"_isSystemFontUsed": true,
|
"_isSystemFontUsed": true,
|
||||||
@ -557,15 +561,13 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"animComp": null,
|
"animComp": null,
|
||||||
"baseSpeed": 50,
|
|
||||||
"speed": 50,
|
|
||||||
"lastMovedAt": 0,
|
"lastMovedAt": 0,
|
||||||
"eps": 0.1,
|
|
||||||
"magicLeanLowerBound": 0.414,
|
|
||||||
"magicLeanUpperBound": 2.414,
|
|
||||||
"arrowTipNode": {
|
"arrowTipNode": {
|
||||||
"__id__": 8
|
"__id__": 8
|
||||||
},
|
},
|
||||||
|
"coordLabel": {
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"__id__": 1
|
"__id__": 1
|
||||||
},
|
},
|
||||||
"_children": [],
|
"_children": [],
|
||||||
"_active": false,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 3
|
"__id__": 3
|
||||||
@ -119,8 +119,8 @@
|
|||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 93.36,
|
"width": 46.68,
|
||||||
"height": 40
|
"height": 27.72
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@ -132,7 +132,7 @@
|
|||||||
"ctor": "Float64Array",
|
"ctor": "Float64Array",
|
||||||
"array": [
|
"array": [
|
||||||
-5,
|
-5,
|
||||||
101,
|
50,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@ -164,12 +164,16 @@
|
|||||||
"__id__": 2
|
"__id__": 2
|
||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"_materials": [],
|
"_materials": [
|
||||||
|
{
|
||||||
|
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
|
||||||
|
}
|
||||||
|
],
|
||||||
"_useOriginalSize": false,
|
"_useOriginalSize": false,
|
||||||
"_string": "(0, 0)",
|
"_string": "(0, 0)",
|
||||||
"_N$string": "(0, 0)",
|
"_N$string": "(0, 0)",
|
||||||
"_fontSize": 40,
|
"_fontSize": 20,
|
||||||
"_lineHeight": 40,
|
"_lineHeight": 22,
|
||||||
"_enableWrapText": true,
|
"_enableWrapText": true,
|
||||||
"_N$file": null,
|
"_N$file": null,
|
||||||
"_isSystemFontUsed": true,
|
"_isSystemFontUsed": true,
|
||||||
@ -557,15 +561,13 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"animComp": null,
|
"animComp": null,
|
||||||
"baseSpeed": 50,
|
|
||||||
"speed": 50,
|
|
||||||
"lastMovedAt": 0,
|
"lastMovedAt": 0,
|
||||||
"eps": 0.1,
|
|
||||||
"magicLeanLowerBound": 0.414,
|
|
||||||
"magicLeanUpperBound": 2.414,
|
|
||||||
"arrowTipNode": {
|
"arrowTipNode": {
|
||||||
"__id__": 8
|
"__id__": 8
|
||||||
},
|
},
|
||||||
|
"coordLabel": {
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -6,30 +6,10 @@ module.export = cc.Class({
|
|||||||
type: cc.Animation,
|
type: cc.Animation,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
baseSpeed: {
|
|
||||||
type: cc.Float,
|
|
||||||
default: 50,
|
|
||||||
},
|
|
||||||
speed: {
|
|
||||||
type: cc.Float,
|
|
||||||
default: 50
|
|
||||||
},
|
|
||||||
lastMovedAt: {
|
lastMovedAt: {
|
||||||
type: cc.Float,
|
type: cc.Float,
|
||||||
default: 0 // In "GMT milliseconds"
|
default: 0 // In "GMT milliseconds"
|
||||||
},
|
}
|
||||||
eps: {
|
|
||||||
default: 0.10,
|
|
||||||
type: cc.Float
|
|
||||||
},
|
|
||||||
magicLeanLowerBound: {
|
|
||||||
default: 0.414, // Tangent of (PI/8).
|
|
||||||
type: cc.Float
|
|
||||||
},
|
|
||||||
magicLeanUpperBound: {
|
|
||||||
default: 2.414, // Tangent of (3*PI/8).
|
|
||||||
type: cc.Float
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// LIFE-CYCLE CALLBACKS:
|
// LIFE-CYCLE CALLBACKS:
|
||||||
@ -86,11 +66,9 @@ module.export = cc.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
update(dt) {
|
update(dt) {},
|
||||||
},
|
|
||||||
|
|
||||||
lateUpdate(dt) {
|
lateUpdate(dt) {},
|
||||||
},
|
|
||||||
|
|
||||||
_generateRandomDirection() {
|
_generateRandomDirection() {
|
||||||
return ALL_DISCRETE_DIRECTIONS_CLOCKWISE[Math.floor(Math.random() * ALL_DISCRETE_DIRECTIONS_CLOCKWISE.length)];
|
return ALL_DISCRETE_DIRECTIONS_CLOCKWISE[Math.floor(Math.random() * ALL_DISCRETE_DIRECTIONS_CLOCKWISE.length)];
|
||||||
|
@ -352,6 +352,8 @@ cc.Class({
|
|||||||
window.mapIns = self;
|
window.mapIns = self;
|
||||||
window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
|
window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
|
||||||
|
|
||||||
|
self.showCriticalCoordinateLabels = true;
|
||||||
|
|
||||||
console.warn("+++++++ Map onLoad()");
|
console.warn("+++++++ Map onLoad()");
|
||||||
window.handleClientSessionError = function() {
|
window.handleClientSessionError = function() {
|
||||||
console.warn('+++++++ Common handleClientSessionError()');
|
console.warn('+++++++ Common handleClientSessionError()');
|
||||||
@ -473,9 +475,36 @@ cc.Class({
|
|||||||
const x0 = boundaryObj[0].x,
|
const x0 = boundaryObj[0].x,
|
||||||
y0 = boundaryObj[0].y;
|
y0 = boundaryObj[0].y;
|
||||||
let pts = [];
|
let pts = [];
|
||||||
// TODO: Simplify this redundant coordinate conversion within "extractBoundaryObjects", but since this routine is only called once per battle, not urgent.
|
|
||||||
for (let i = 0; i < boundaryObj.length; ++i) {
|
for (let i = 0; i < boundaryObj.length; ++i) {
|
||||||
pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]);
|
const dx = boundaryObj[i].x - x0;
|
||||||
|
const dy = boundaryObj[i].y - y0;
|
||||||
|
pts.push([dx, dy]);
|
||||||
|
if (self.showCriticalCoordinateLabels) {
|
||||||
|
const barrierVertLabelNode = new cc.Node();
|
||||||
|
switch (i % 4) {
|
||||||
|
case 0:
|
||||||
|
barrierVertLabelNode.color = cc.Color.RED;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
barrierVertLabelNode.color = cc.Color.GRAY;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
barrierVertLabelNode.color = cc.Color.BLACK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
barrierVertLabelNode.color = cc.Color.MAGENTA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
barrierVertLabelNode.setPosition(cc.v2(x0+0.95*dx, y0+0.5*dy));
|
||||||
|
const barrierVertLabel = barrierVertLabelNode.addComponent(cc.Label);
|
||||||
|
barrierVertLabel.fontSize = 20;
|
||||||
|
barrierVertLabel.lineHeight = 22;
|
||||||
|
barrierVertLabel.string = `(${boundaryObj[i].x.toFixed(1)}, ${boundaryObj[i].y.toFixed(1)})`;
|
||||||
|
safelyAddChild(self.node, barrierVertLabelNode);
|
||||||
|
setLocalZOrder(barrierVertLabelNode, 5);
|
||||||
|
|
||||||
|
barrierVertLabelNode.active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const newBarrier = self.collisionSys.createPolygon(x0, y0, pts);
|
const newBarrier = self.collisionSys.createPolygon(x0, y0, pts);
|
||||||
// console.log("Created barrier: ", newBarrier);
|
// console.log("Created barrier: ", newBarrier);
|
||||||
|
@ -7,6 +7,10 @@ cc.Class({
|
|||||||
arrowTipNode: {
|
arrowTipNode: {
|
||||||
type: cc.Node,
|
type: cc.Node,
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
coordLabel: {
|
||||||
|
type: cc.Label,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
start() {
|
start() {
|
||||||
@ -44,6 +48,9 @@ cc.Class({
|
|||||||
|
|
||||||
update(dt) {
|
update(dt) {
|
||||||
BasePlayer.prototype.update.call(this, dt);
|
BasePlayer.prototype.update.call(this, dt);
|
||||||
|
if (this.mapIns.showCriticalCoordinateLabels) {
|
||||||
|
this.coordLabel.string = `(${this.node.x.toFixed(2)}, ${this.node.y.toFixed(2)})`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -371,8 +371,8 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
|
|||||||
const tilesElListUnderTilesets = {};
|
const tilesElListUnderTilesets = {};
|
||||||
for (let tsxFilenameIdx = 0; tsxFilenameIdx < tsxFileNames.length; ++tsxFilenameIdx) {
|
for (let tsxFilenameIdx = 0; tsxFilenameIdx < tsxFileNames.length; ++tsxFilenameIdx) {
|
||||||
const tsxOrientation = tileSets[tsxFilenameIdx].orientation;
|
const tsxOrientation = tileSets[tsxFilenameIdx].orientation;
|
||||||
if (cc.TiledMap.Orientation.ORTHO != tsxOrientation) {
|
if (cc.TiledMap.Orientation.ORTHO == tsxOrientation) {
|
||||||
cc.error("Error at tileset %s: We proceed with ONLY tilesets in ORTHO orientation for all map orientations by now.", tsxFileNames[tsxFilenameIdx]);
|
cc.error("Error at tileset %s: We don't proceed with tilesets in ORTHO orientation by now.", tsxFileNames[tsxFilenameIdx]);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user