A temp dirty commit having mysterious left moving players.

This commit is contained in:
genxium 2023-02-17 12:26:07 +08:00
parent c6b98855af
commit 2b304eaa75
8 changed files with 478 additions and 450 deletions

View File

@ -518,7 +518,7 @@
"array": [
0,
0,
216.50635094610968,
210.4441731196186,
0,
0,
0,

View File

@ -380,6 +380,8 @@ cc.Class({
}
self.pJumpedOrNotList = [];
for (let i = 0; i < window.boundRoomCapacity; i++) self.pJumpedOrNotList.push(false);
self.dynamicRectangleColliders = gopkgs.NewDynamicRectangleColliders(64);
self.recentRenderCache = gopkgs.NewRingBufferJs(self.renderCacheSize);
self.recentInputCache = gopkgs.NewRingBufferJs((self.renderCacheSize >> 1) + 1);
@ -1401,7 +1403,7 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
};
self.rdfIdToActuallyUsedInput.set(i, inputFrameDownsyncClone);
}
const renderRes = gopkgs.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(self.recentInputCache, i, collisionSys, collisionSysMap, self.spaceOffsetX, self.spaceOffsetY, self.chConfigsOrderedByJoinIndex, self.recentRenderCache, self.collisionHolder, self.pEffPushbacks, self.pHardPushbackNormsArr, self.pJumpedOrNotList);
const renderRes = gopkgs.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(self.recentInputCache, i, collisionSys, collisionSysMap, self.spaceOffsetX, self.spaceOffsetY, self.chConfigsOrderedByJoinIndex, self.recentRenderCache, self.collisionHolder, self.pEffPushbacks, self.pHardPushbackNormsArr, self.pJumpedOrNotList, self.dynamicRectangleColliders);
const nextRdf = gopkgs.GetRoomDownsyncFrame(self.recentRenderCache, self.renderFrameId + 1);
if (true == isChasing) {

File diff suppressed because it is too large Load Diff

View File

@ -217,21 +217,23 @@ func calcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.Conve
}
func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
aCnt, bCnt := len(a.Points), len(b.Points)
aCnt, bCnt := a.Points.Cnt, b.Points.Cnt
// Single point case
if 1 == aCnt && 1 == bCnt {
if nil != result {
result.Overlap = 0
}
return a.Points[0][0] == b.Points[0][0] && a.Points[0][1] == b.Points[0][1]
aPoint := a.GetPointByOffset(0)
bPoint := b.GetPointByOffset(0)
return aPoint[0] == bPoint[0] && aPoint[1] == bPoint[1]
}
if 1 < aCnt {
// Deliberately using "Points" instead of "SATAxes" to avoid unnecessary heap memory alloc
for i, _ := range a.Points {
u, v := a.Points[i], a.Points[0]
if i != len(a.Points)-1 {
v = a.Points[i+1]
for i := int32(0); i < a.Points.Cnt; i++ {
u, v := a.GetPointByOffset(i), a.GetPointByOffset(0)
if i != a.Points.Cnt-1 {
v = a.GetPointByOffset(i + 1)
}
dy := v[1] - u[1]
dx := v[0] - u[0]
@ -243,10 +245,10 @@ func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool
}
if 1 < bCnt {
for i, _ := range b.Points {
u, v := b.Points[i], b.Points[0]
if i != len(b.Points)-1 {
v = b.Points[i+1]
for i := int32(0); i < b.Points.Cnt; i++ {
u, v := b.GetPointByOffset(i), b.GetPointByOffset(0)
if i != b.Points.Cnt-1 {
v = b.GetPointByOffset(i + 1)
}
dy := v[1] - u[1]
dx := v[0] - u[0]
@ -307,7 +309,8 @@ func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e resolv.Vector, re
*/
var aStart, aEnd, bStart, bEnd float64 = MAX_FLOAT64, -MAX_FLOAT64, MAX_FLOAT64, -MAX_FLOAT64
for _, p := range a.Points {
for i := int32(0); i < a.Points.Cnt; i++ {
p := a.GetPointByOffset(i)
dot := (p[0]+a.X)*e[0] + (p[1]+a.Y)*e[1]
if aStart > dot {
@ -319,7 +322,8 @@ func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e resolv.Vector, re
}
}
for _, p := range b.Points {
for i := int32(0); i < b.Points.Cnt; i++ {
p := b.GetPointByOffset(i)
dot := (p[0]+b.X)*e[0] + (p[1]+b.Y)*e[1]
if bStart > dot {
@ -454,7 +458,7 @@ func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNex
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms has non-empty collision;playerColliderPos=(%.2f,%.2f)\n", joinIndex, playerColliderCenterX, playerColliderCenterY)
for true {
obj := collision.FirstCollidedObject()
obj := collision.PopFirstCollidedObject()
if nil == obj {
break
}
@ -558,11 +562,9 @@ func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync,
/*
[LONG TERM PERFORMANCE ENHANCEMENT PLAN]
The function "ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame" is creating new heap-memory blocks at 60fps, e.g. nextRenderFramePlayers & nextRenderFrameMeleeBullets & nextRenderFrameFireballBullets & effPushbacks & hardPushbackNorms & jumpedOrNotList & playerColliders & bulletColliders, which would induce "possibly performance impacting garbage collections" when many rooms are running simultaneously.
It's not easy to remove all of the dynamic heap-memory blocks allocation/deallocation, but we can reduce them to some extent.
The function "ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame" is creating new heap-memory blocks at 60fps, e.g. nextRenderFramePlayers & nextRenderFrameMeleeBullets & nextRenderFrameFireballBullets & effPushbacks & hardPushbackNorms & jumpedOrNotList & dynamicRectangleColliders("player" & "bullet"), which would induce "possibly performance impacting garbage collections" when many rooms are running simultaneously.
*/
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.RingBuffer, currRenderFrameId int32, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig, renderFrameBuffer *resolv.RingBuffer, collision *resolv.Collision, effPushbacks []*Vec2D, hardPushbackNormsArr [][]*Vec2D, jumpedOrNotList []bool) bool {
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.RingBuffer, currRenderFrameId int32, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig, renderFrameBuffer *resolv.RingBuffer, collision *resolv.Collision, effPushbacks []*Vec2D, hardPushbackNormsArr [][]*Vec2D, jumpedOrNotList []bool, dynamicRectangleColliders []*resolv.Object) bool {
currRenderFrame := renderFrameBuffer.GetByFrameId(currRenderFrameId).(*RoomDownsyncFrame)
nextRenderFrameId := currRenderFrameId + 1
roomCapacity := len(currRenderFrame.PlayersArr)
@ -759,8 +761,14 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
}
}
/*
[WARNING]
1. The dynamic colliders will all be removed from "Space" at the end of this function due to the need for being rollback-compatible.
2. To achieve "zero gc" in "ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame", I deliberately chose a collision system that doesn't use dynamic tree node alloc.
*/
colliderCnt := 0
// 2. Process player movement
playerColliders := make([]*resolv.Object, len(currRenderFrame.PlayersArr), len(currRenderFrame.PlayersArr)) // Will all be removed at the end of this function due to the need for being rollback-compatible
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
@ -819,8 +827,9 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
colliderWorldWidth, colliderWorldHeight := VirtualGridToWorldPos(colliderWidth, colliderHeight)
playerCollider := GenerateRectCollider(wx, wy, colliderWorldWidth, colliderWorldHeight, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, currPlayerDownsync, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
playerColliders[i] = playerCollider
playerCollider := dynamicRectangleColliders[colliderCnt]
UpdateRectCollider(playerCollider, wx, wy, colliderWorldWidth, colliderWorldHeight, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, currPlayerDownsync, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
colliderCnt++
// Add to collision system
collisionSys.AddSingle(playerCollider)
@ -838,9 +847,8 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
}
}
// 3. Add bullet colliders into collision system
// 3. Add bullet colliders into collision system; [DIRTY TRICK] Players always precede bullets in "dynamicRectangleColliders".
// [WARNING] For rollback compatibility, static data of "BulletConfig" & "BattleAttr(static since instantiated)" can just be copies of the pointers in "RenderFrameBuffer", however, FireballBullets movement data as well as bullet animation data must be copies of instances for each RenderFrame!
bulletColliders := make([]*resolv.Object, 0, ((len(currRenderFrame.MeleeBullets) + len(currRenderFrame.FireballBullets)) << 1)) // Will all be removed at the end of this function due to the need for being rollback-compatible
for _, prevFireball := range currRenderFrame.FireballBullets {
if TERMINATING_BULLET_LOCAL_ID == prevFireball.BattleAttr.BulletLocalId {
break
@ -862,9 +870,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
if IsFireballBulletActive(fireballBullet, currRenderFrame) {
bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.Bullet.HitboxSizeX, fireballBullet.Bullet.HitboxSizeY)
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, fireballBullet, "FireballBullet")
newBulletCollider := dynamicRectangleColliders[colliderCnt]
UpdateRectCollider(newBulletCollider, bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, fireballBullet, "FireballBullet")
colliderCnt++
collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider)
fireballBullet.BlState = BULLET_ACTIVE
if fireballBullet.BlState != prevFireball.BlState {
fireballBullet.FramesInBlState = 0
@ -906,9 +917,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
}
bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.Bullet.HitboxOffsetX, offender.VirtualGridY)
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(meleeBullet.Bullet.HitboxSizeX, meleeBullet.Bullet.HitboxSizeY)
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
newBulletCollider := dynamicRectangleColliders[colliderCnt]
UpdateRectCollider(newBulletCollider, bulletWx, bulletWy, hitboxSizeWx, hitboxSizeWy, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, SNAP_INTO_PLATFORM_OVERLAP, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
colliderCnt++
collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider)
meleeBullet.BlState = BULLET_ACTIVE
if meleeBullet.BlState != prevMelee.BlState {
meleeBullet.FramesInBlState = 0
@ -921,7 +935,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
// 4. Calc pushbacks for each player (after its movement) w/o bullets
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
playerCollider := playerColliders[i]
playerCollider := dynamicRectangleColliders[i]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
thatPlayerInNextFrame := nextRenderFramePlayers[i]
hardPushbackCnt := calcHardPushbacksNorms(joinIndex, currPlayerDownsync, thatPlayerInNextFrame, playerCollider, playerShape, SNAP_INTO_PLATFORM_OVERLAP, effPushbacks[joinIndex-1], hardPushbackNormsArr[joinIndex-1], collision)
@ -930,7 +944,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
if collided := playerCollider.CheckAllWithHolder(0, 0, collision); collided {
for true {
obj := collision.FirstCollidedObject()
obj := collision.PopFirstCollidedObject()
if nil == obj {
break
}
@ -963,7 +977,6 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
pushbackX, pushbackY = (overlapResult.Overlap-SNAP_INTO_PLATFORM_OVERLAP*2)*overlapResult.OverlapX, (overlapResult.Overlap-SNAP_INTO_PLATFORM_OVERLAP*2)*overlapResult.OverlapY
}
if 0 < hardPushbackCnt {
for i := 0; i < hardPushbackCnt; i++ {
hardPushbackNorm := hardPushbackNormsArr[joinIndex-1][i]
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
@ -972,7 +985,6 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
@ -1028,7 +1040,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
if chConfig.OnWallEnabled {
if thatPlayerInNextFrame.InAir {
// [WARNING] Sticking to wall MUST BE based on "InAir", otherwise we would get gravity reduction from ground up incorrectly!
if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent && 0 < hardPushbackCnt {
if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent {
// [WARNING] Sticking to wall could only be triggered by proactive player input
for i := 0; i < hardPushbackCnt; i++ {
hardPushbackNorm := hardPushbackNormsArr[joinIndex-1][i]
@ -1051,13 +1063,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
thatPlayerInNextFrame.OnWallNormX, thatPlayerInNextFrame.OnWallNormY = 0, 0
}
}
}
// 5. Check bullet-anything collisions
for _, bulletCollider := range bulletColliders {
for i := len(nextRenderFramePlayers); i < colliderCnt; i++ {
bulletCollider := dynamicRectangleColliders[i]
collided := bulletCollider.CheckAllWithHolder(0, 0, collision)
bulletCollider.Space.RemoveSingle(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
if !collided {
continue
}
@ -1079,7 +1090,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
offender := currRenderFrame.PlayersArr[bulletBattleAttr.OffenderJoinIndex-1]
for true {
obj := collision.FirstCollidedObject()
obj := collision.PopFirstCollidedObject()
if nil == obj {
break
}
@ -1153,7 +1164,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
// 6. Get players out of stuck barriers if there's any
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
playerCollider := playerColliders[i]
playerCollider := dynamicRectangleColliders[i]
// Update "virtual grid position"
thatPlayerInNextFrame := nextRenderFramePlayers[i]
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY)
@ -1199,8 +1210,9 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.Rin
}
}
for _, playerCollider := range playerColliders {
playerCollider.Space.RemoveSingle(playerCollider)
for i := 0; i < colliderCnt; i++ {
dynamicCollider := dynamicRectangleColliders[i]
dynamicCollider.Space.RemoveSingle(dynamicCollider)
}
ret.Id = nextRenderFrameId
@ -1222,6 +1234,15 @@ func generateRectColliderInCollisionSpace(blX, blY, w, h float64, data interface
return collider
}
func UpdateRectCollider(collider *resolv.Object, wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) {
blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY)
collider.X, collider.Y, collider.W, collider.H = blX, blY, w, h
rectShape := collider.Shape.(*resolv.ConvexPolygon)
rectShape.UpdateAsRectangle(0, 0, w, h)
collider.Data = data
// Ignore "tag" for now
}
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *resolv.Object {
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
var w, h float64 = 0, 0

View File

@ -9,6 +9,14 @@ import (
/*
[WARNING] Should avoid using "MakeFullWrapper" as much as possible, and completely remove its usage in 60fps calls like "update(dt)" on frontend!
*/
func NewDynamicRectangleColliders(cnt int) []*js.Object {
ret := make([]*js.Object, cnt)
for i := 0; i < cnt; i++ {
ret[i] = js.MakeWrapper(GenerateRectCollider(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nil, ""))
}
return ret
}
func NewCollisionHolder() *js.Object {
return js.MakeWrapper(resolv.NewCollision())
}
@ -103,9 +111,9 @@ func GetCharacterConfigsOrderedByJoinIndex(speciesIdList []int) []*js.Object {
return ret
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *resolv.RingBuffer, currRenderFrameId int32, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig, renderFrameBuffer *resolv.RingBuffer, collision *resolv.Collision, effPushbacks []*Vec2D, hardPushbackNormsArr [][]*Vec2D, jumpedOrNotList []bool) bool {
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *resolv.RingBuffer, currRenderFrameId int32, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig, renderFrameBuffer *resolv.RingBuffer, collision *resolv.Collision, effPushbacks []*Vec2D, hardPushbackNormsArr [][]*Vec2D, jumpedOrNotList []bool, dynamicRectangleColliders []*resolv.Object) bool {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
return ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrameId, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, chConfigsOrderedByJoinIndex, renderFrameBuffer, collision, effPushbacks, hardPushbackNormsArr, jumpedOrNotList)
return ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrameId, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, chConfigsOrderedByJoinIndex, renderFrameBuffer, collision, effPushbacks, hardPushbackNormsArr, jumpedOrNotList, dynamicRectangleColliders)
}
func GetRoomDownsyncFrame(renderFrameBuffer *resolv.RingBuffer, frameId int32) *js.Object {
@ -183,5 +191,6 @@ func main() {
"GetMeleeBullet": GetMeleeBullet,
"GetFireballBullet": GetFireballBullet,
"GetInput": GetInput,
"NewDynamicRectangleColliders": NewDynamicRectangleColliders,
})
}

View File

@ -24,7 +24,7 @@ func (cc *Collision) Clear() {
cc.Cells.Clear()
}
func (cc *Collision) FirstCollidedObject() *Object {
func (cc *Collision) PopFirstCollidedObject() *Object {
if 0 >= cc.Objects.Cnt {
return nil
}

View File

@ -254,7 +254,7 @@ func (obj *Object) CheckAllWithHolder(dx, dy float64, cc *Collision) bool {
if obj.Space == nil {
return false
}
cc.Clear()
cc.checkingObject = obj
if dx < 0 {

View File

@ -125,7 +125,7 @@ func (line *Line) IntersectionPointsCircle(circle *Circle) []Vector {
}
type ConvexPolygon struct {
Points []Vector
Points *RingBuffer
X, Y float64
Closed bool
}
@ -135,46 +135,72 @@ type ConvexPolygon struct {
// polygon square, with the vertices at {0,0}, {10,0}, {10, 10}, and {0, 10}.
func NewConvexPolygon(points ...float64) *ConvexPolygon {
// if len(points)/2 < 2 {
// return nil
// }
cp := &ConvexPolygon{Points: []Vector{}, Closed: true}
cp := &ConvexPolygon{
Points: NewRingBuffer(6), // I don't expected more points to be coped with in this particular game
Closed: true,
}
cp.AddPoints(points...)
return cp
}
func (cp *ConvexPolygon) Clone() Shape {
points := []Vector{}
for _, point := range cp.Points {
points = append(points, point.Clone())
func (cp *ConvexPolygon) GetPointByOffset(offset int32) Vector {
if cp.Points.Cnt <= offset {
return nil
}
return cp.Points.GetByFrameId(cp.Points.StFrameId + offset).(Vector)
}
func (cp *ConvexPolygon) Clone() Shape {
newPoly := NewConvexPolygon()
newPoly.X = cp.X
newPoly.Y = cp.Y
newPoly.AddPointsVec(points...)
for i := int32(0); i < cp.Points.Cnt; i++ {
newPoly.Points.Put(cp.GetPointByOffset(i))
}
newPoly.Closed = cp.Closed
return newPoly
}
// AddPointsVec allows you to add points to the ConvexPolygon with a slice of Vectors, each indicating a point / vertex.
func (cp *ConvexPolygon) AddPointsVec(points ...Vector) {
cp.Points = append(cp.Points, points...)
}
// AddPoints allows you to add points to the ConvexPolygon with a slice or selection of float64s, with each pair indicating an X or Y value for
// a point / vertex (i.e. AddPoints(0, 1, 2, 3) would add two points - one at {0, 1}, and another at {2, 3}).
func (cp *ConvexPolygon) AddPoints(vertexPositions ...float64) {
for v := 0; v < len(vertexPositions); v += 2 {
cp.Points = append(cp.Points, Vector{vertexPositions[v], vertexPositions[v+1]})
// "resolv.Vector" is an alias of "[]float64", thus already a pointer type
cp.Points.Put(Vector{vertexPositions[v], vertexPositions[v+1]})
}
}
func (cp *ConvexPolygon) UpdateAsRectangle(x, y, w, h float64) bool {
// This function might look ugly but it's a fast in-place update!
if 4 != cp.Points.Cnt {
panic("ConvexPolygon not having exactly 4 vertices to form a rectangle#1!")
}
for i := int32(0); i < cp.Points.Cnt; i++ {
thatVec := cp.GetPointByOffset(i)
if nil == thatVec {
panic("ConvexPolygon not having exactly 4 vertices to form a rectangle#2!")
}
switch i {
case 0:
thatVec[0] = x
thatVec[1] = y
case 1:
thatVec[0] = x + w
thatVec[1] = y
case 2:
thatVec[0] = x + w
thatVec[1] = y + h
case 3:
thatVec[0] = x
thatVec[1] = y + h
}
}
return true
}
// Lines returns a slice of transformed Lines composing the ConvexPolygon.
func (cp *ConvexPolygon) Lines() []*Line {
@ -200,8 +226,9 @@ func (cp *ConvexPolygon) Lines() []*Line {
// Transformed returns the ConvexPolygon's points / vertices, transformed according to the ConvexPolygon's position.
func (cp *ConvexPolygon) Transformed() []Vector {
transformed := make([]Vector, len(cp.Points))
for i, point := range cp.Points {
transformed := make([]Vector, cp.Points.Cnt)
for i := int32(0); i < cp.Points.Cnt; i++ {
point := cp.GetPointByOffset(i)
transformed[i] = Vector{point[0] + cp.X, point[1] + cp.Y}
}
return transformed
@ -331,10 +358,6 @@ func (polygon *ConvexPolygon) PointInside(point Vector) bool {
return contactCount == 1
}
func (polygon *ConvexPolygon) GetPoints() []Vector {
return polygon.Points
}
type ContactSet struct {
Points []Vector // Slice of Points indicating contact between the two Shapes.
MTV Vector // Minimum Translation Vector; this is the vector to move a Shape on to move it outside of its contacting Shape.
@ -553,42 +576,6 @@ func (cp *ConvexPolygon) ContainedBy(otherShape Shape) bool {
return true
}
// FlipH flips the ConvexPolygon's vertices horizontally according to their initial offset when adding the points.
func (cp *ConvexPolygon) FlipH() {
for _, v := range cp.Points {
v[0] = -v[0]
}
// We have to reverse vertex order after flipping the vertices to ensure the winding order is consistent between Objects (so that the normals are consistently outside or inside, which is important
// when doing Intersection tests). If we assume that the normal of a line, going from vertex A to vertex B, is one direction, then the normal would be inverted if the vertices were flipped in position,
// but not in order. This would make Intersection tests drive objects into each other, instead of giving the delta to move away.
cp.ReverseVertexOrder()
}
// FlipV flips the ConvexPolygon's vertices vertically according to their initial offset when adding the points.
func (cp *ConvexPolygon) FlipV() {
for _, v := range cp.Points {
v[1] = -v[1]
}
cp.ReverseVertexOrder()
}
// ReverseVertexOrder reverses the vertex ordering of the ConvexPolygon.
func (cp *ConvexPolygon) ReverseVertexOrder() {
verts := []Vector{cp.Points[0]}
for i := len(cp.Points) - 1; i >= 1; i-- {
verts = append(verts, cp.Points[i])
}
cp.Points = verts
}
// NewRectangle returns a rectangular ConvexPolygon with the vertices in clockwise order. In actuality, an AABBRectangle should be its own
// "thing" with its own optimized Intersection code check.
func NewRectangle(x, y, w, h float64) *ConvexPolygon {