Applied ringbuff to resolv_tailored for reducing memory usage.

This commit is contained in:
genxium 2023-02-15 12:02:07 +08:00
parent 2d179d0cdf
commit 5b7f35b874
19 changed files with 7279 additions and 407 deletions

View File

@ -1771,7 +1771,7 @@ func (pR *Room) startBattleUdpTunnel() {
} }
_, wrerr := conn.WriteTo(bytes, otherPlayer.BattleUdpTunnelAddr) _, wrerr := conn.WriteTo(bytes, otherPlayer.BattleUdpTunnelAddr)
if nil != wrerr { if nil != wrerr {
Logger.Warn(fmt.Sprintf("`BattleUdpTunnel` for roomId=%d failed to forward upsync from (playerId:%d, joinIndex:%d, addr:%s) to (otherPlayerId:%d, otherPlayerJoinIndex:%d, otherPlayerAddr:%s)\n", pR.Id, playerId, peerJoinIndex, remote, otherPlayer.Id, otherPlayer.JoinIndex, otherPlayer.BattleUdpTunnelAddr)) //Logger.Debug(fmt.Sprintf("`BattleUdpTunnel` for roomId=%d failed to forward upsync from (playerId:%d, joinIndex:%d, addr:%s) to (otherPlayerId:%d, otherPlayerJoinIndex:%d, otherPlayerAddr:%s)\n", pR.Id, playerId, peerJoinIndex, remote, otherPlayer.Id, otherPlayer.JoinIndex, otherPlayer.BattleUdpTunnelAddr))
} }
} }
pR.OnBattleCmdReceived(pReq, true) // To help advance "pR.LastAllConfirmedInputFrameId" asap, and even if "pR.LastAllConfirmedInputFrameId" is not advanced due to packet loss, these UDP packets would help prefill the "InputsBuffer" with correct player "future inputs (compared to ws session)" such that when "forceConfirmation" occurs we have as many correct predictions as possible pR.OnBattleCmdReceived(pReq, true) // To help advance "pR.LastAllConfirmedInputFrameId" asap, and even if "pR.LastAllConfirmedInputFrameId" is not advanced due to packet loss, these UDP packets would help prefill the "InputsBuffer" with correct player "future inputs (compared to ws session)" such that when "forceConfirmation" occurs we have as many correct predictions as possible

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="64" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="137"> <map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="40" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="138">
<tileset firstgid="1" source="tiles0.tsx"/> <tileset firstgid="1" source="tiles0.tsx"/>
<tileset firstgid="65" source="tiles1.tsx"/> <tileset firstgid="65" source="tiles1.tsx"/>
<tileset firstgid="129" source="tiles2.tsx"/> <tileset firstgid="129" source="tiles2.tsx"/>
<layer id="6" name="Ground" width="128" height="64"> <layer id="7" name="Ground" width="128" height="40">
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJzt201uwjAQhuEIxCY7KugeqTepumHXC/T+xyCqYimy4sQ4E2bE9y6eDf+e1w4bOHZddwQAAAAAAAAAAACAF/jBP+8Onv29P4M35Rkor50ZaK+dGWivnRlor50ZtK39UmnL5/owegz94/X/on8Ykfsv9V27n/72a0/drPqfBucZeeMSjxm8m5b+W1m9Dv3fv//c9eG88f3p397fytr1vNR9j31A/1hq21vtg4gziNz/ZvQY+vubrr0fefdv3QOt+0O9/3cm3ZffntwqlZ4fTZqBdwuv/odROv9/g3vBwUDpjFq8dst7q5//vP/0O2B6m1V/T/Qv979nvedY9/jcwTPXHPov9+lXZldjrb/1fqL/c/2vo6W5bTn/S3tg7/Nfsz/pX+5v+Z1L/3i8f3cdhXcHz/6/4uivjf7a6K+N/tror43+2uivjf7a6K+N/tror43+2uivjf7a6K+N/tror43+2uivjf7a6K+N/trSf6C8W9Cf/vSn/6v7Y7/+Dyz1uAA= eJzt2k0OgjAQQGECccMOo+5JvIlx484LeP9jaAxNSEOhlMGZOG/xbfyl81rc2FRV1QAAAAAAAAAAAADAD9zxpd1Bs7/2NWjzPAPPa2cGvtfODHyvnRn4XjszKFv7KdOW6zoKvYb+9vpf6W+G5f5zfZeep7/82kM3qf6Hj25C3DhFYwb/pqT/VlKfQ///7z91f+g2fj/9y/tLWbqfp7rvsQ/ob0tue6l9YHEGlvv3Qq+hv77x2tuBdv/SPVC6P7z3v0XCc/HjQZ8p9X5rwgy0W2j1rwfh/L8+Hgm1gNQZlfjsku/2fv7j/uPfgPFjUv010T/d/xH1niLd47KDNfcc+s/3aRdml2Opv/R+ov+6/ufB3Ny2nP+5PbD3+c/Zn/RP95f8zaW/Pdr/u7ZCu4Nm/6dz9Pdtz/5vZSq2hg==
</data> </data>
</layer> </layer>
<objectgroup id="1" name="PlayerStartingPos"> <objectgroup id="1" name="PlayerStartingPos">
@ -84,12 +84,12 @@
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
</object> </object>
<object id="84" x="640" y="224" width="16" height="800"> <object id="84" x="640" y="224" width="16" height="416">
<properties> <properties>
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
</object> </object>
<object id="85" x="1680" y="224" width="16" height="800"> <object id="85" x="1680" y="224" width="16" height="416">
<properties> <properties>
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
@ -149,17 +149,12 @@
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
</object> </object>
<object id="113" x="640" y="1008" width="1056" height="16">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
</object>
<object id="114" x="640" y="224" width="1056" height="16"> <object id="114" x="640" y="224" width="1056" height="16">
<properties> <properties>
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
</object> </object>
<object id="119" x="656" y="592" width="1024" height="416"> <object id="119" x="656" y="592" width="512" height="48">
<properties> <properties>
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
@ -169,5 +164,10 @@
<property name="boundary_type" value="barrier"/> <property name="boundary_type" value="barrier"/>
</properties> </properties>
</object> </object>
<object id="137" x="1168" y="592" width="512" height="48">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
</object>
</objectgroup> </objectgroup>
</map> </map>

View File

@ -461,7 +461,7 @@
"array": [ "array": [
0, 0,
0, 0,
216.50635094610968, 217.52040535921228,
0, 0,
0, 0,
0, 0,

View File

@ -72,11 +72,14 @@
"__id__": 3 "__id__": 3
}, },
{ {
"__id__": 10 "__id__": 9
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{
"__id__": 50
},
{ {
"__id__": 51 "__id__": 51
}, },
@ -88,9 +91,6 @@
}, },
{ {
"__id__": 54 "__id__": 54
},
{
"__id__": 55
} }
], ],
"_prefab": null, "_prefab": null,
@ -156,9 +156,6 @@
}, },
{ {
"__id__": 5 "__id__": 5
},
{
"__id__": 6
} }
], ],
"_prefab": null, "_prefab": null,
@ -220,34 +217,6 @@
"_tmxFile": null, "_tmxFile": null,
"_id": "c8MqKDLJdKz7VhPwMjScDw" "_id": "c8MqKDLJdKz7VhPwMjScDw"
}, },
{
"__type__": "09e1b/tEy5K2qaPIpqHDbae",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 3
},
"_enabled": true,
"BGMEffect": {
"__uuid__": "64a79efa-97de-4cb5-b2a9-01500c60573a"
},
"crashedByTrapBullet": {
"__uuid__": "1d604e42-8cee-466f-884d-e74cae21ce3b"
},
"highScoreTreasurePicked": {
"__uuid__": "0164d22c-d965-461f-867e-b30e2d56cc5c"
},
"treasurePicked": {
"__uuid__": "7704b97e-6367-420c-b7af-d0750a2bbb30"
},
"countDown10SecToEnd": {
"__uuid__": "261d1d7d-a5cc-4cb7-a737-194427055fd4"
},
"mapNode": {
"__id__": 3
},
"_id": "3crA1nz5xPSLAnCSLQIPOq"
},
{ {
"__type__": "b3810kDSWtD14RhiYzulYVI", "__type__": "b3810kDSWtD14RhiYzulYVI",
"_name": "", "_name": "",
@ -266,7 +235,7 @@
"__uuid__": "d92d4831-cd65-4eb5-90bd-b77021aec35b" "__uuid__": "d92d4831-cd65-4eb5-90bd-b77021aec35b"
}, },
"joystickInputControllerNode": { "joystickInputControllerNode": {
"__id__": 7 "__id__": 6
}, },
"confirmLogoutPrefab": null, "confirmLogoutPrefab": null,
"simplePressToGoDialogPrefab": null, "simplePressToGoDialogPrefab": null,
@ -279,19 +248,19 @@
"forceBigEndianFloatingNumDecoding": false, "forceBigEndianFloatingNumDecoding": false,
"renderFrameIdLagTolerance": 4, "renderFrameIdLagTolerance": 4,
"inputFrameFrontLabel": { "inputFrameFrontLabel": {
"__id__": 14 "__id__": 13
}, },
"sendingQLabel": { "sendingQLabel": {
"__id__": 16 "__id__": 15
}, },
"inputFrameDownsyncQLabel": { "inputFrameDownsyncQLabel": {
"__id__": 18 "__id__": 17
}, },
"peerInputFrameUpsyncQLabel": { "peerInputFrameUpsyncQLabel": {
"__id__": 20 "__id__": 19
}, },
"rollbackFramesLabel": { "rollbackFramesLabel": {
"__id__": 22 "__id__": 21
}, },
"skippedRenderFrameCntLabel": null, "skippedRenderFrameCntLabel": null,
"_id": "e5xQdv12xLoIRr0b36Pie+" "_id": "e5xQdv12xLoIRr0b36Pie+"
@ -301,17 +270,17 @@
"_name": "JoystickContainer", "_name": "JoystickContainer",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 8 "__id__": 7
}, },
"_children": [ "_children": [
{ {
"__id__": 45 "__id__": 44
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 50 "__id__": 49
} }
], ],
"_prefab": null, "_prefab": null,
@ -367,26 +336,26 @@
"_name": "Interactive", "_name": "Interactive",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 9 "__id__": 8
}, },
"_children": [ "_children": [
{ {
"__id__": 7 "__id__": 6
}, },
{ {
"__id__": 34 "__id__": 33
}, },
{ {
"__id__": 36 "__id__": 35
}, },
{ {
"__id__": 40 "__id__": 39
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 44 "__id__": 43
} }
], ],
"_prefab": null, "_prefab": null,
@ -442,20 +411,20 @@
"_name": "WidgetsAboveAll", "_name": "WidgetsAboveAll",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 10 "__id__": 9
}, },
"_children": [ "_children": [
{ {
"__id__": 12 "__id__": 11
}, },
{ {
"__id__": 8 "__id__": 7
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 33 "__id__": 32
} }
], ],
"_prefab": null, "_prefab": null,
@ -515,13 +484,13 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 9 "__id__": 8
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 10
} }
], ],
"_prefab": null, "_prefab": null,
@ -549,7 +518,7 @@
"array": [ "array": [
0, 0,
0, 0,
216.50635094610968, 210.4441731196186,
0, 0,
0, 0,
0, 0,
@ -577,7 +546,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 10 "__id__": 9
}, },
"_enabled": true, "_enabled": true,
"_cullingMask": 4294967295, "_cullingMask": 4294967295,
@ -613,35 +582,35 @@
"_name": "DebugInfo", "_name": "DebugInfo",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 9 "__id__": 8
}, },
"_children": [ "_children": [
{ {
"__id__": 13 "__id__": 12
}, },
{ {
"__id__": 15 "__id__": 14
}, },
{ {
"__id__": 17 "__id__": 16
}, },
{ {
"__id__": 19 "__id__": 18
}, },
{ {
"__id__": 21 "__id__": 20
}, },
{ {
"__id__": 23 "__id__": 22
}, },
{ {
"__id__": 25 "__id__": 24
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 32 "__id__": 31
} }
], ],
"_prefab": null, "_prefab": null,
@ -697,13 +666,13 @@
"_name": "inputFrameFront", "_name": "inputFrameFront",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 14 "__id__": 13
} }
], ],
"_prefab": null, "_prefab": null,
@ -759,7 +728,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 13 "__id__": 12
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -789,13 +758,13 @@
"_name": "sendingQ", "_name": "sendingQ",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 16 "__id__": 15
} }
], ],
"_prefab": null, "_prefab": null,
@ -851,7 +820,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 15 "__id__": 14
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -881,13 +850,13 @@
"_name": "inputFrameDownsyncQ", "_name": "inputFrameDownsyncQ",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 18 "__id__": 17
} }
], ],
"_prefab": null, "_prefab": null,
@ -943,7 +912,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 17 "__id__": 16
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -973,13 +942,13 @@
"_name": "peerInputFrameUpsyncQ", "_name": "peerInputFrameUpsyncQ",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 20 "__id__": 19
} }
], ],
"_prefab": null, "_prefab": null,
@ -1035,7 +1004,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 19 "__id__": 18
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -1065,13 +1034,13 @@
"_name": "rollbackFrames", "_name": "rollbackFrames",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 22 "__id__": 21
} }
], ],
"_prefab": null, "_prefab": null,
@ -1127,7 +1096,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 21 "__id__": 20
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -1157,13 +1126,13 @@
"_name": "skippedCnt", "_name": "skippedCnt",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 24 "__id__": 23
} }
], ],
"_prefab": null, "_prefab": null,
@ -1219,7 +1188,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 23 "__id__": 22
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -1251,23 +1220,23 @@
"_name": "RoomIdIndicator", "_name": "RoomIdIndicator",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 12 "__id__": 11
}, },
"_children": [ "_children": [
{ {
"__id__": 26 "__id__": 25
}, },
{ {
"__id__": 28 "__id__": 27
} }
], ],
"_active": false, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 30 "__id__": 29
}, },
{ {
"__id__": 31 "__id__": 30
} }
], ],
"_prefab": null, "_prefab": null,
@ -1323,13 +1292,13 @@
"_name": "label", "_name": "label",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 25 "__id__": 24
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 27 "__id__": 26
} }
], ],
"_prefab": null, "_prefab": null,
@ -1385,7 +1354,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 26 "__id__": 25
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@ -1413,13 +1382,13 @@
"_name": "BoundRoomIdLabel", "_name": "BoundRoomIdLabel",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 25 "__id__": 24
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 29 "__id__": 28
} }
], ],
"_prefab": null, "_prefab": null,
@ -1475,7 +1444,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 28 "__id__": 27
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@ -1503,7 +1472,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 25 "__id__": 24
}, },
"_enabled": true, "_enabled": true,
"_layoutSize": { "_layoutSize": {
@ -1536,7 +1505,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 25 "__id__": 24
}, },
"_enabled": true, "_enabled": true,
"_materials": [], "_materials": [],
@ -1566,7 +1535,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 12 "__id__": 11
}, },
"_enabled": true, "_enabled": true,
"_layoutSize": { "_layoutSize": {
@ -1599,7 +1568,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 9 "__id__": 8
}, },
"_enabled": true, "_enabled": true,
"alignMode": 1, "alignMode": 1,
@ -1626,13 +1595,13 @@
"_name": "CountdownSeconds", "_name": "CountdownSeconds",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 8 "__id__": 7
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 35 "__id__": 34
} }
], ],
"_prefab": null, "_prefab": null,
@ -1688,7 +1657,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 34 "__id__": 33
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -1718,11 +1687,11 @@
"_name": "BtnA", "_name": "BtnA",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 8 "__id__": 7
}, },
"_children": [ "_children": [
{ {
"__id__": 37 "__id__": 36
} }
], ],
"_active": true, "_active": true,
@ -1780,16 +1749,16 @@
"_name": "Background", "_name": "Background",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 36 "__id__": 35
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 38 "__id__": 37
}, },
{ {
"__id__": 39 "__id__": 38
} }
], ],
"_prefab": null, "_prefab": null,
@ -1845,7 +1814,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 37 "__id__": 36
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -1879,7 +1848,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 37 "__id__": 36
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@ -1906,11 +1875,11 @@
"_name": "BtnB", "_name": "BtnB",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 8 "__id__": 7
}, },
"_children": [ "_children": [
{ {
"__id__": 41 "__id__": 40
} }
], ],
"_active": true, "_active": true,
@ -1968,16 +1937,16 @@
"_name": "Background", "_name": "Background",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 40 "__id__": 39
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 42 "__id__": 41
}, },
{ {
"__id__": 43 "__id__": 42
} }
], ],
"_prefab": null, "_prefab": null,
@ -2033,7 +2002,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 41 "__id__": 40
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -2067,7 +2036,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 41 "__id__": 40
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@ -2094,7 +2063,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 8 "__id__": 7
}, },
"_enabled": true, "_enabled": true,
"alignMode": 1, "alignMode": 1,
@ -2121,20 +2090,20 @@
"_name": "JoystickBG", "_name": "JoystickBG",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 7 "__id__": 6
}, },
"_children": [ "_children": [
{ {
"__id__": 46 "__id__": 45
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 48 "__id__": 47
}, },
{ {
"__id__": 49 "__id__": 48
} }
], ],
"_prefab": null, "_prefab": null,
@ -2190,13 +2159,13 @@
"_name": "Joystick", "_name": "Joystick",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 45 "__id__": 44
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 47 "__id__": 46
} }
], ],
"_prefab": null, "_prefab": null,
@ -2252,7 +2221,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 46 "__id__": 45
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -2286,7 +2255,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 45 "__id__": 44
}, },
"_enabled": true, "_enabled": true,
"_materials": [ "_materials": [
@ -2320,7 +2289,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 45 "__id__": 44
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@ -2347,7 +2316,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 7 "__id__": 6
}, },
"_enabled": true, "_enabled": true,
"alignMode": 0, "alignMode": 0,
@ -2449,16 +2418,16 @@
}, },
"_enabled": true, "_enabled": true,
"translationListenerNode": { "translationListenerNode": {
"__id__": 7 "__id__": 6
}, },
"zoomingListenerNode": { "zoomingListenerNode": {
"__id__": 3 "__id__": 3
}, },
"stickhead": { "stickhead": {
"__id__": 46 "__id__": 45
}, },
"base": { "base": {
"__id__": 45 "__id__": 44
}, },
"joyStickEps": 0.1, "joyStickEps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,
@ -2479,10 +2448,10 @@
"linearMovingEps": 0.1, "linearMovingEps": 0.1,
"scaleByEps": 0.0375, "scaleByEps": 0.0375,
"btnA": { "btnA": {
"__id__": 36 "__id__": 35
}, },
"btnB": { "btnB": {
"__id__": 40 "__id__": 39
}, },
"_id": "e9oVYTr7ROlpp/IrNjBUmR" "_id": "e9oVYTr7ROlpp/IrNjBUmR"
} }

View File

@ -1,66 +0,0 @@
cc.Class({
extends: cc.Component,
properties: {
BGMEffect: {
type: cc.AudioClip,
default: null
},
crashedByTrapBullet: {
type: cc.AudioClip,
default: null
},
highScoreTreasurePicked: {
type: cc.AudioClip,
default: null
},
treasurePicked: {
type: cc.AudioClip,
default: null
},
countDown10SecToEnd: {
type: cc.AudioClip,
default: null
},
mapNode: {
type: cc.Node,
default: null
},
},
// LIFE-CYCLE CALLBACKS:
onLoad() {
cc.audioEngine.setEffectsVolume(1);
cc.audioEngine.setMusicVolume(0.5);
},
stopAllMusic() {
cc.audioEngine.stopAll();
},
playBGM() {
if(this.BGMEffect) {
cc.audioEngine.playMusic(this.BGMEffect, true);
}
},
playCrashedByTrapBullet() {
if(this.crashedByTrapBullet) {
cc.audioEngine.playEffect(this.crashedByTrapBullet, false);
}
},
playHighScoreTreasurePicked() {
if(this.highScoreTreasurePicked) {
cc.audioEngine.playEffect(this.highScoreTreasurePicked, false);
}
},
playTreasurePicked() {
if(this.treasurePicked) {
cc.audioEngine.playEffect(this.treasurePicked, false);
}
},
playCountDown10SecToEnd() {
if(this.countDown10SecToEnd) {
cc.audioEngine.playEffect(this.countDown10SecToEnd, false);
}
},
});

View File

@ -1,9 +0,0 @@
{
"ver": "1.0.5",
"uuid": "09e1bfed-132e-4ada-a68f-229a870db69e",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -33,7 +33,7 @@ cc.Class({
/** Init required prefab ended. */ /** Init required prefab ended. */
self.inputFrameUpsyncDelayTolerance = 2; self.inputFrameUpsyncDelayTolerance = 2;
self.collisionMinStep = 2; self.collisionMinStep = 8;
self.renderCacheSize = 1024; self.renderCacheSize = 1024;
self.serverFps = 60; self.serverFps = 60;
@ -98,7 +98,7 @@ cc.Class({
const p2Vpos = gopkgs.WorldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y); const p2Vpos = gopkgs.WorldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y);
const colliderRadiusV = gopkgs.WorldToVirtualGridPos(12.0, 0); const colliderRadiusV = gopkgs.WorldToVirtualGridPos(12.0, 0);
const speciesIdList = [4096, 0]; const speciesIdList = [1, 0];
const chConfigsOrderedByJoinIndex = gopkgs.GetCharacterConfigsOrderedByJoinIndex(speciesIdList); const chConfigsOrderedByJoinIndex = gopkgs.GetCharacterConfigsOrderedByJoinIndex(speciesIdList);
const startRdf = window.pb.protos.RoomDownsyncFrame.create({ const startRdf = window.pb.protos.RoomDownsyncFrame.create({

File diff suppressed because one or more lines are too long

View File

@ -27,8 +27,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask" android:launchMode="singleTask">
android:taskAffinity="" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -17,7 +17,8 @@
}, },
"encryptJs": false, "encryptJs": false,
"excludeScenes": [ "excludeScenes": [
"8491a86c-bec9-4813-968a-128ca01639e0" "2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4",
"92160186-3e0d-4e0a-ae20-97286170ba58"
], ],
"fb-instant-games": {}, "fb-instant-games": {},
"includeSDKBox": false, "includeSDKBox": false,
@ -37,7 +38,7 @@
"REMOTE_SERVER_ROOT": "", "REMOTE_SERVER_ROOT": "",
"orientation": "portrait" "orientation": "portrait"
}, },
"startScene": "2ff474d9-0c9e-4fe3-87ec-fbff7cae85b4", "startScene": "8491a86c-bec9-4813-968a-128ca01639e0",
"title": "DelayNoMore", "title": "DelayNoMore",
"webOrientation": "landscape", "webOrientation": "landscape",
"wechatgame": { "wechatgame": {

View File

@ -16,7 +16,7 @@ const (
PATTERN_ID_UNABLE_TO_OP = -2 PATTERN_ID_UNABLE_TO_OP = -2
PATTERN_ID_NO_OP = -1 PATTERN_ID_NO_OP = -1
WORLD_TO_VIRTUAL_GRID_RATIO = float64(100.0) WORLD_TO_VIRTUAL_GRID_RATIO = float64(10.0)
VIRTUAL_GRID_TO_WORLD_RATIO = float64(1.0) / WORLD_TO_VIRTUAL_GRID_RATIO VIRTUAL_GRID_TO_WORLD_RATIO = float64(1.0) / WORLD_TO_VIRTUAL_GRID_RATIO
GRAVITY_X = int32(0) GRAVITY_X = int32(0)
@ -226,16 +226,31 @@ func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool
} }
if 1 < aCnt { if 1 < aCnt {
for _, axis := range a.SATAxes() { // Deliberately using "Points" instead of "SATAxes" to avoid unnecessary heap memory alloc
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) { for i, _ := range a.Points {
u, v := a.Points[i], a.Points[0]
if i != len(a.Points)-1 {
v = a.Points[i+1]
}
dy := v[1] - u[1]
dx := v[0] - u[0]
axis := resolv.Vector{dy, -dx}.Unit()
if isPolygonPairSeparatedByDir(a, b, axis, result) {
return false return false
} }
} }
} }
if 1 < bCnt { if 1 < bCnt {
for _, axis := range b.SATAxes() { for i, _ := range b.Points {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) { u, v := b.Points[i], b.Points[0]
if i != len(b.Points)-1 {
v = b.Points[i+1]
}
dy := v[1] - u[1]
dx := v[0] - u[0]
axis := resolv.Vector{dy, -dx}.Unit()
if isPolygonPairSeparatedByDir(a, b, axis, result) {
return false return false
} }
} }
@ -411,8 +426,7 @@ func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBounding
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY) return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
} }
func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) *[]Vec2D { func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D, pHardPushback *[]Vec2D, collision *resolv.Collision) int {
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
virtualGripToWall := float64(0) virtualGripToWall := float64(0)
if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && 0 == thatPlayerInNextFrame.VelX && currPlayerDownsync.DirX == thatPlayerInNextFrame.DirX { if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && 0 == thatPlayerInNextFrame.VelX && currPlayerDownsync.DirX == thatPlayerInNextFrame.DirX {
/* /*
@ -429,14 +443,19 @@ func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNex
} }
virtualGripToWall = xfac * float64(currPlayerDownsync.Speed) * VIRTUAL_GRID_TO_WORLD_RATIO virtualGripToWall = xfac * float64(currPlayerDownsync.Speed) * VIRTUAL_GRID_TO_WORLD_RATIO
} }
collision := playerCollider.Check(virtualGripToWall, 0) retCnt := 0
if nil == collision { collided := playerCollider.CheckAllWithHolder(virtualGripToWall, 0, collision)
return &ret if !collided {
return retCnt
} }
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center() //playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms has non-empty collision;playerColliderPos=(%.2f,%.2f)\n", joinIndex, playerColliderCenterX, playerColliderCenterY) //fmt.Printf("joinIndex=%d calcHardPushbacksNorms has non-empty collision;playerColliderPos=(%.2f,%.2f)\n", joinIndex, playerColliderCenterX, playerColliderCenterY)
for _, obj := range collision.Objects { for true {
obj := collision.FirstCollidedObject()
if nil == obj {
break
}
isBarrier := false isBarrier := false
switch obj.Data.(type) { switch obj.Data.(type) {
case *PlayerDownsync, *MeleeBullet, *FireballBullet: case *PlayerDownsync, *MeleeBullet, *FireballBullet:
@ -456,15 +475,16 @@ func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNex
// ALWAY snap into hardPushbacks! // ALWAY snap into hardPushbacks!
// [OverlapX, OverlapY] is the unit vector that points into the platform // [OverlapX, OverlapY] is the unit vector that points into the platform
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
ret = append(ret, Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY}) (*pHardPushback)[retCnt] = Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY}
pEffPushback.X += pushbackX pEffPushback.X += pushbackX
pEffPushback.Y += pushbackY pEffPushback.Y += pushbackY
retCnt++
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms found one hardpushback; immediatePushback=(%.2f,%.2f)\n", joinIndex, pushbackX, pushbackY) //fmt.Printf("joinIndex=%d calcHardPushbacksNorms found one hardpushback; immediatePushback=(%.2f,%.2f)\n", joinIndex, pushbackX, pushbackY)
} }
return &ret return retCnt
} }
func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, currRenderFrame *RoomDownsyncFrame, chConfig *CharacterConfig, inputsBuffer *RingBuffer) (int, bool, int32, int32) { func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, currRenderFrame *RoomDownsyncFrame, chConfig *CharacterConfig, inputsBuffer *resolv.RingBuffer) (int, bool, int32, int32) {
// returns (patternId, jumpedOrNot, effectiveDx, effectiveDy) // returns (patternId, jumpedOrNot, effectiveDx, effectiveDy)
delayedInputFrameId := ConvertToDelayedInputFrameId(currRenderFrame.Id) delayedInputFrameId := ConvertToDelayedInputFrameId(currRenderFrame.Id)
delayedInputFrameIdForPrevRdf := ConvertToDelayedInputFrameId(currRenderFrame.Id - 1) delayedInputFrameIdForPrevRdf := ConvertToDelayedInputFrameId(currRenderFrame.Id - 1)
@ -558,7 +578,9 @@ then pass in the whole "renderFrameBuffer *SpecificRingBuffer" to this function
However, the enhancement for "playerColliders & bulletColliders" of each room is even more difficult, because the feasibility of doing in-place overwrites depends on the collision library in use. However, the enhancement for "playerColliders & bulletColliders" of each room is even more difficult, because the feasibility of doing in-place overwrites depends on the collision library in use.
*/ */
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *RoomDownsyncFrame { func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *resolv.RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *RoomDownsyncFrame {
collision := resolv.NewCollision() // TODO: Pass this holder in from params
// [WARNING] On backend this function MUST BE called while "InputsBufferLock" is locked! // [WARNING] On backend this function MUST BE called while "InputsBufferLock" is locked!
roomCapacity := len(currRenderFrame.PlayersArr) roomCapacity := len(currRenderFrame.PlayersArr)
nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity) nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity)
@ -607,7 +629,10 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
nextRenderFrameMeleeBullets := make([]*MeleeBullet, 0, len(currRenderFrame.MeleeBullets)) // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation? nextRenderFrameMeleeBullets := make([]*MeleeBullet, 0, len(currRenderFrame.MeleeBullets)) // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation?
nextRenderFrameFireballBullets := make([]*FireballBullet, 0, len(currRenderFrame.FireballBullets)) nextRenderFrameFireballBullets := make([]*FireballBullet, 0, len(currRenderFrame.FireballBullets))
effPushbacks := make([]Vec2D, roomCapacity) effPushbacks := make([]Vec2D, roomCapacity)
hardPushbackNorms := make([]*[]Vec2D, roomCapacity) hardPushbackNorms := make([][]Vec2D, roomCapacity)
for i, _ := range currRenderFrame.PlayersArr {
hardPushbackNorms[i] = make([]Vec2D, 5) // In reality no more than 4 simultaneous hard pushbacks
}
jumpedOrNotList := make([]bool, roomCapacity) jumpedOrNotList := make([]bool, roomCapacity)
bulletLocalId := currRenderFrame.BulletLocalIdCounter bulletLocalId := currRenderFrame.BulletLocalIdCounter
@ -813,7 +838,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
playerColliders[i] = playerCollider playerColliders[i] = playerCollider
// Add to collision system // Add to collision system
collisionSys.Add(playerCollider) collisionSys.AddSingle(playerCollider)
if currPlayerDownsync.InAir { if currPlayerDownsync.InAir {
if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && !jumpedOrNotList[i] { if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && !jumpedOrNotList[i] {
@ -850,7 +875,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY) bulletWx, bulletWy := VirtualGridToWorldPos(fireballBullet.VirtualGridX, fireballBullet.VirtualGridY)
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(fireballBullet.Bullet.HitboxSizeX, fireballBullet.Bullet.HitboxSizeY) 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 := 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")
collisionSys.Add(newBulletCollider) collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider) bulletColliders = append(bulletColliders, newBulletCollider)
fireballBullet.BlState = BULLET_ACTIVE fireballBullet.BlState = BULLET_ACTIVE
if fireballBullet.BlState != prevFireball.BlState { if fireballBullet.BlState != prevFireball.BlState {
@ -891,7 +916,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.Bullet.HitboxOffsetX, offender.VirtualGridY) bulletWx, bulletWy := VirtualGridToWorldPos(offender.VirtualGridX+xfac*meleeBullet.Bullet.HitboxOffsetX, offender.VirtualGridY)
hitboxSizeWx, hitboxSizeWy := VirtualGridToWorldPos(meleeBullet.Bullet.HitboxSizeX, meleeBullet.Bullet.HitboxSizeY) 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 := 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")
collisionSys.Add(newBulletCollider) collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider) bulletColliders = append(bulletColliders, newBulletCollider)
meleeBullet.BlState = BULLET_ACTIVE meleeBullet.BlState = BULLET_ACTIVE
if meleeBullet.BlState != prevMelee.BlState { if meleeBullet.BlState != prevMelee.BlState {
@ -908,12 +933,16 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
playerCollider := playerColliders[i] playerCollider := playerColliders[i]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon) playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
thatPlayerInNextFrame := nextRenderFramePlayers[i] thatPlayerInNextFrame := nextRenderFramePlayers[i]
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(joinIndex, currPlayerDownsync, thatPlayerInNextFrame, playerCollider, playerShape, SNAP_INTO_PLATFORM_OVERLAP, &(effPushbacks[joinIndex-1])) hardPushbackCnt := calcHardPushbacksNorms(joinIndex, currPlayerDownsync, thatPlayerInNextFrame, playerCollider, playerShape, SNAP_INTO_PLATFORM_OVERLAP, &(effPushbacks[joinIndex-1]), &(hardPushbackNorms[joinIndex-1]), collision)
chConfig := chConfigsOrderedByJoinIndex[i] chConfig := chConfigsOrderedByJoinIndex[i]
landedOnGravityPushback := false landedOnGravityPushback := false
if collision := playerCollider.Check(0, 0); nil != collision { if collided := playerCollider.CheckAllWithHolder(0, 0, collision); collided {
for _, obj := range collision.Objects { for true {
obj := collision.FirstCollidedObject()
if nil == obj {
break
}
isBarrier, isAnotherPlayer, isBullet := false, false, false isBarrier, isAnotherPlayer, isBullet := false, false, false
switch v := obj.Data.(type) { switch v := obj.Data.(type) {
case *PlayerDownsync: case *PlayerDownsync:
@ -943,23 +972,27 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
// [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. // [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 pushbackX, pushbackY = (overlapResult.Overlap-SNAP_INTO_PLATFORM_OVERLAP*2)*overlapResult.OverlapX, (overlapResult.Overlap-SNAP_INTO_PLATFORM_OVERLAP*2)*overlapResult.OverlapY
} }
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] { if 0 < hardPushbackCnt {
for i := 0; i < hardPushbackCnt; i++ {
hardPushbackNorm := hardPushbackNorms[joinIndex-1][i]
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) { if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
pushbackX -= projectedMagnitude * hardPushbackNorm.X pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y pushbackY -= projectedMagnitude * hardPushbackNorm.Y
} }
} }
}
effPushbacks[joinIndex-1].X += pushbackX effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY effPushbacks[joinIndex-1].Y += pushbackY
if SNAP_INTO_PLATFORM_THRESHOLD < normAlignmentWithGravity { if SNAP_INTO_PLATFORM_THRESHOLD < normAlignmentWithGravity {
landedOnGravityPushback = true landedOnGravityPushback = true
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center() //playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//fmt.Printf("joinIndex=%d landedOnGravityPushback\n{renderFrame.id: %d, isBarrier: %v, isAnotherPlayer: %v}\nhardPushbackNormsOfThisPlayer=%v, playerColliderPos=(%.2f,%.2f), immediatePushback={%.3f, %.3f}, effPushback={%.3f, %.3f}, overlapMag=%.4f\n", joinIndex, currRenderFrame.Id, isBarrier, isAnotherPlayer, *hardPushbackNorms[joinIndex-1], playerColliderCenterX, playerColliderCenterY, pushbackX, pushbackY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap) //fmt.Printf("joinIndex=%d landedOnGravityPushback\n{renderFrame.id: %d, isBarrier: %v, isAnotherPlayer: %v}\nhardPushbackNormsOfThisPlayer=%v, playerColliderPos=(%.2f,%.2f), immediatePushback={%.3f, %.3f}, effPushback={%.3f, %.3f}, overlapMag=%.4f\n", joinIndex, currRenderFrame.Id, isBarrier, isAnotherPlayer, hardPushbackNorms[joinIndex-1], playerColliderCenterX, playerColliderCenterY, pushbackX, pushbackY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap)
} }
} }
} }
if landedOnGravityPushback { if landedOnGravityPushback {
thatPlayerInNextFrame.InAir = false thatPlayerInNextFrame.InAir = false
fallStopping := (currPlayerDownsync.InAir && 0 >= currPlayerDownsync.VelY) fallStopping := (currPlayerDownsync.InAir && 0 >= currPlayerDownsync.VelY)
@ -1004,9 +1037,10 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
if chConfig.OnWallEnabled { if chConfig.OnWallEnabled {
if thatPlayerInNextFrame.InAir { if thatPlayerInNextFrame.InAir {
// [WARNING] Sticking to wall MUST BE based on "InAir", otherwise we would get gravity reduction from ground up incorrectly! // [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 { if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent && 0 < hardPushbackCnt {
// [WARNING] Sticking to wall could only be triggered by proactive player input // [WARNING] Sticking to wall could only be triggered by proactive player input
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] { for i := 0; i < hardPushbackCnt; i++ {
hardPushbackNorm := hardPushbackNorms[joinIndex-1][i]
normAlignmentWithHorizon1 := (hardPushbackNorm.X*float64(1.0) + hardPushbackNorm.Y*float64(0.0)) normAlignmentWithHorizon1 := (hardPushbackNorm.X*float64(1.0) + hardPushbackNorm.Y*float64(0.0))
normAlignmentWithHorizon2 := (hardPushbackNorm.X*float64(-1.0) + hardPushbackNorm.Y*float64(0.0)) normAlignmentWithHorizon2 := (hardPushbackNorm.X*float64(-1.0) + hardPushbackNorm.Y*float64(0.0))
if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon1 { if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon1 {
@ -1031,14 +1065,15 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
// 5. Check bullet-anything collisions // 5. Check bullet-anything collisions
for _, bulletCollider := range bulletColliders { for _, bulletCollider := range bulletColliders {
collision := bulletCollider.Check(0, 0) collided := bulletCollider.CheckAllWithHolder(0, 0, collision)
bulletCollider.Space.Remove(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame bulletCollider.Space.RemoveSingle(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
exploded := false if !collided {
explodedOnAnotherPlayer := false
if nil == collision {
continue continue
} }
exploded := false
explodedOnAnotherPlayer := false
var bulletStaticAttr *BulletConfig = nil var bulletStaticAttr *BulletConfig = nil
var bulletBattleAttr *BulletBattleAttr = nil var bulletBattleAttr *BulletBattleAttr = nil
switch v := bulletCollider.Data.(type) { switch v := bulletCollider.Data.(type) {
@ -1052,7 +1087,11 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon) bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
offender := currRenderFrame.PlayersArr[bulletBattleAttr.OffenderJoinIndex-1] offender := currRenderFrame.PlayersArr[bulletBattleAttr.OffenderJoinIndex-1]
for _, obj := range collision.Objects { for true {
obj := collision.FirstCollidedObject()
if nil == obj {
break
}
defenderShape := obj.Shape.(*resolv.ConvexPolygon) defenderShape := obj.Shape.(*resolv.ConvexPolygon)
switch t := obj.Data.(type) { switch t := obj.Data.(type) {
case *PlayerDownsync: case *PlayerDownsync:
@ -1170,7 +1209,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
} }
for _, playerCollider := range playerColliders { for _, playerCollider := range playerColliders {
playerCollider.Space.Remove(playerCollider) playerCollider.Space.RemoveSingle(playerCollider)
} }
return &RoomDownsyncFrame{ return &RoomDownsyncFrame{
@ -1188,7 +1227,7 @@ func GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding,
} }
func generateRectColliderInCollisionSpace(blX, blY, w, h float64, data interface{}, tag string) *resolv.Object { func generateRectColliderInCollisionSpace(blX, blY, w, h float64, data interface{}, tag string) *resolv.Object {
collider := resolv.NewObject(blX, blY, w, h, tag) // Unlike its frontend counter part, the position of a "resolv.Object" must be specified by "bottom-left point" because "w" and "h" must be positive, see "resolv.Object.BoundsToSpace" for details collider := resolv.NewObjectSingleTag(blX, blY, w, h, tag) // Unlike its frontend counter part, the position of a "resolv.Object" must be specified by "bottom-left point" because "w" and "h" must be positive, see "resolv.Object.BoundsToSpace" for details
shape := resolv.NewRectangle(0, 0, w, h) shape := resolv.NewRectangle(0, 0, w, h)
collider.SetShape(shape) collider.SetShape(shape)
collider.Data = data collider.Data = data

View File

@ -107,7 +107,7 @@ var Characters = map[int]*CharacterConfig{
GetUpInvinsibleFrames: int32(10), GetUpInvinsibleFrames: int32(10),
GetUpFramesToRecover: int32(27), GetUpFramesToRecover: int32(27),
Speed: int32(float64(2.19) * WORLD_TO_VIRTUAL_GRID_RATIO), // I don't know why "2.2" is so special that it throws a compile error Speed: int32(float64(2.2) * WORLD_TO_VIRTUAL_GRID_RATIO), // I don't know why "2.2" is so special that it throws a compile error
JumpingInitVelY: int32(float64(7.5) * WORLD_TO_VIRTUAL_GRID_RATIO), JumpingInitVelY: int32(float64(7.5) * WORLD_TO_VIRTUAL_GRID_RATIO),
JumpingFramesToRecover: int32(2), JumpingFramesToRecover: int32(2),
@ -234,7 +234,7 @@ var skills = map[int]*Skill{
HitStunFrames: int32(13), HitStunFrames: int32(13),
BlockStunFrames: int32(9), BlockStunFrames: int32(9),
Damage: int32(5), Damage: int32(5),
SelfLockVelX: int32(float64(0.05) * WORLD_TO_VIRTUAL_GRID_RATIO), SelfLockVelX: int32(float64(0.1) * WORLD_TO_VIRTUAL_GRID_RATIO),
SelfLockVelY: NO_LOCK_VEL, SelfLockVelY: NO_LOCK_VEL,
PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO), PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO),
PushbackVelY: int32(0), PushbackVelY: int32(0),
@ -332,7 +332,7 @@ var skills = map[int]*Skill{
HitStunFrames: int32(13), HitStunFrames: int32(13),
BlockStunFrames: int32(9), BlockStunFrames: int32(9),
Damage: int32(5), Damage: int32(5),
SelfLockVelX: int32(float64(0.05) * WORLD_TO_VIRTUAL_GRID_RATIO), SelfLockVelX: int32(float64(0.1) * WORLD_TO_VIRTUAL_GRID_RATIO),
SelfLockVelY: NO_LOCK_VEL, SelfLockVelY: NO_LOCK_VEL,
PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO), PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO),
PushbackVelY: int32(0), PushbackVelY: int32(0),

View File

@ -15,7 +15,7 @@ func NewInputFrameDownsync(inputFrameId int32, inputList []uint64, confirmedList
} }
func NewRingBufferJs(n int32) *js.Object { func NewRingBufferJs(n int32) *js.Object {
return js.MakeFullWrapper(NewRingBuffer(n)) return js.MakeFullWrapper(resolv.NewRingBuffer(n))
} }
func NewCollisionSpaceJs(spaceW, spaceH, minStepW, minStepH int) *js.Object { func NewCollisionSpaceJs(spaceW, spaceH, minStepW, minStepH int) *js.Object {
@ -113,7 +113,7 @@ func GetCharacterConfigsOrderedByJoinIndex(speciesIdList []int) []*js.Object {
return ret return ret
} }
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *js.Object { func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *resolv.RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, collisionSpaceOffsetX, collisionSpaceOffsetY float64, chConfigsOrderedByJoinIndex []*CharacterConfig) *js.Object {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend // We need access to all fields of RoomDownsyncFrame for displaying in frontend
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrame, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, chConfigsOrderedByJoinIndex)) return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrame, collisionSys, collisionSysMap, collisionSpaceOffsetX, collisionSpaceOffsetY, chConfigsOrderedByJoinIndex))
} }

View File

@ -3,7 +3,7 @@ package resolv
// Cell is used to contain and organize Object information. // Cell is used to contain and organize Object information.
type Cell struct { type Cell struct {
X, Y int // The X and Y position of the cell in the Space - note that this is in Grid position, not World position. X, Y int // The X and Y position of the cell in the Space - note that this is in Grid position, not World position.
Objects []*Object // The Objects that a Cell contains. Objects *RingBuffer // The Objects that a Cell contains.
} }
// newCell creates a new cell at the specified X and Y position. Should not be used directly. // newCell creates a new cell at the specified X and Y position. Should not be used directly.
@ -11,25 +11,27 @@ func newCell(x, y int) *Cell {
return &Cell{ return &Cell{
X: x, X: x,
Y: y, Y: y,
Objects: []*Object{}, Objects: NewRingBuffer(16), // A single cell is so small thus wouldn't have many touching objects simultaneously
} }
} }
// register registers an object with a Cell. Should not be used directly. // register registers an object with a Cell. Should not be used directly.
func (cell *Cell) register(obj *Object) { func (cell *Cell) register(obj *Object) {
if !cell.Contains(obj) { if !cell.Contains(obj) {
cell.Objects = append(cell.Objects, obj) cell.Objects.Put(obj)
} }
} }
// unregister unregisters an object from a Cell. Should not be used directly. // unregister unregisters an object from a Cell. Should not be used directly.
func (cell *Cell) unregister(obj *Object) { func (cell *Cell) unregister(obj *Object) {
rb := cell.Objects
for i, o := range cell.Objects { for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o == obj { if o == obj {
cell.Objects[i] = cell.Objects[len(cell.Objects)-1] // swap with the st element
cell.Objects = cell.Objects[:len(cell.Objects)-1] rb.SetByFrameId(rb.GetByFrameId(rb.StFrameId), i)
// pop the current st element
rb.Pop()
break break
} }
@ -39,7 +41,9 @@ func (cell *Cell) unregister(obj *Object) {
// Contains returns whether a Cell contains the specified Object at its position. // Contains returns whether a Cell contains the specified Object at its position.
func (cell *Cell) Contains(obj *Object) bool { func (cell *Cell) Contains(obj *Object) bool {
for _, o := range cell.Objects { rb := cell.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o == obj { if o == obj {
return true return true
} }
@ -49,7 +53,9 @@ func (cell *Cell) Contains(obj *Object) bool {
// ContainsTags returns whether a Cell contains an Object that has the specified tag at its position. // ContainsTags returns whether a Cell contains an Object that has the specified tag at its position.
func (cell *Cell) ContainsTags(tags ...string) bool { func (cell *Cell) ContainsTags(tags ...string) bool {
for _, o := range cell.Objects { rb := cell.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o.HasTags(tags...) { if o.HasTags(tags...) {
return true return true
} }
@ -59,5 +65,5 @@ func (cell *Cell) ContainsTags(tags ...string) bool {
// Occupied returns whether a Cell contains any Objects at all. // Occupied returns whether a Cell contains any Objects at all.
func (cell *Cell) Occupied() bool { func (cell *Cell) Occupied() bool {
return len(cell.Objects) > 0 return 0 < cell.Objects.Cnt
} }

View File

@ -5,21 +5,37 @@ package resolv
type Collision struct { type Collision struct {
checkingObject *Object // The checking object checkingObject *Object // The checking object
dx, dy float64 // The delta the checking object was moving on that caused this collision dx, dy float64 // The delta the checking object was moving on that caused this collision
Objects []*Object // Slice of objects that were collided with; sorted according to distance to calling Object. Objects *RingBuffer // Slice of objects that were collided with; sorted according to distance to calling Object.
Cells []*Cell // Slice of cells that were collided with; sorted according to distance to calling Object. Cells *RingBuffer // Slice of cells that were collided with; sorted according to distance to calling Object.
} }
func NewCollision() *Collision { func NewCollision() *Collision {
return &Collision{ return &Collision{
Objects: []*Object{}, Objects: NewRingBuffer(16), // I don't expect it to exceed 10 actually
Cells: NewRingBuffer(16),
} }
} }
func (cc *Collision) Clear() {
cc.checkingObject = nil
cc.dx = 0
cc.dy = 0
cc.Objects.Clear()
cc.Cells.Clear()
}
func (cc *Collision) FirstCollidedObject() *Object {
if 0 >= cc.Objects.Cnt {
return nil
}
return cc.Objects.Pop().(*Object)
}
// HasTags returns whether any objects within the Collision have all of the specified tags. This slice does not contain the Object that called Check(). // HasTags returns whether any objects within the Collision have all of the specified tags. This slice does not contain the Object that called Check().
func (cc *Collision) HasTags(tags ...string) bool { func (cc *Collision) HasTags(tags ...string) bool {
rb := cc.Objects
for _, o := range cc.Objects { for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o == cc.checkingObject { if o == cc.checkingObject {
continue continue
} }
@ -38,8 +54,9 @@ func (cc *Collision) ObjectsByTags(tags ...string) []*Object {
objects := []*Object{} objects := []*Object{}
for _, o := range cc.Objects { rb := cc.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o == cc.checkingObject { if o == cc.checkingObject {
continue continue
} }
@ -105,7 +122,7 @@ func (cc *Collision) SlideAgainstCell(cell *Cell, avoidTags ...string) Vector {
sp := cc.checkingObject.Space sp := cc.checkingObject.Space
collidingCell := cc.Cells[0] collidingCell := cc.Cells.GetByFrameId(cc.Cells.StFrameId).(*Cell)
ccX, ccY := sp.SpaceToWorld(collidingCell.X, collidingCell.Y) ccX, ccY := sp.SpaceToWorld(collidingCell.X, collidingCell.Y)
hX := float64(sp.CellWidth) / 2.0 hX := float64(sp.CellWidth) / 2.0
hY := float64(sp.CellHeight) / 2.0 hY := float64(sp.CellHeight) / 2.0

View File

@ -2,7 +2,6 @@ package resolv
import ( import (
"math" "math"
//"sort"
) )
// Object represents an object that can be spread across one or more Cells in a Space. An Object is essentially an AABB (Axis-Aligned Bounding Box) Rectangle. // Object represents an object that can be spread across one or more Cells in a Space. An Object is essentially an AABB (Axis-Aligned Bounding Box) Rectangle.
@ -10,19 +9,34 @@ type Object struct {
Shape Shape // A shape for more specific collision-checking. Shape Shape // A shape for more specific collision-checking.
Space *Space // Reference to the Space the Object exists within Space *Space // Reference to the Space the Object exists within
X, Y, W, H float64 // Position and size of the Object in the Space X, Y, W, H float64 // Position and size of the Object in the Space
TouchingCells []*Cell // An array of Cells the Object is touching TouchingCells *RingBuffer // An array of Cells the Object is touching
Data interface{} // A pointer to a user-definable object Data interface{} // A pointer to a user-definable object
ignoreList map[*Object]bool // Set of Objects to ignore when checking for collisions ignoreList map[*Object]bool // Set of Objects to ignore when checking for collisions
tags []string // A list of tags the Object has tags []string // A list of tags the Object has
} }
// NewObject returns a new Object of the specified position and size. // NewObject returns a new Object of the specified position and size.
func NewObjectSingleTag(x, y, w, h float64, tag string) *Object {
o := &Object{
X: x,
Y: y,
W: w,
H: h,
TouchingCells: NewRingBuffer(512), // [WARNING] Should make N large enough to cover all "TouchingCells", otherwise some cells would fail to unregister an object, resulting in memory corruption and incorrect detection result!
tags: []string{tag},
ignoreList: map[*Object]bool{},
}
return o
}
func NewObject(x, y, w, h float64, tags ...string) *Object { func NewObject(x, y, w, h float64, tags ...string) *Object {
o := &Object{ o := &Object{
X: x, X: x,
Y: y, Y: y,
W: w, W: w,
H: h, H: h,
TouchingCells: NewRingBuffer(512),
tags: []string{}, tags: []string{},
ignoreList: map[*Object]bool{}, ignoreList: map[*Object]bool{},
} }
@ -59,7 +73,7 @@ func (obj *Object) Update() {
space := obj.Space space := obj.Space
obj.Space.Remove(obj) obj.Space.RemoveSingle(obj)
obj.Space = space obj.Space = space
@ -73,7 +87,7 @@ func (obj *Object) Update() {
if c != nil { if c != nil {
c.register(obj) c.register(obj)
obj.TouchingCells = append(obj.TouchingCells, c) obj.TouchingCells.Put(c)
} }
} }
@ -154,17 +168,22 @@ func (obj *Object) BoundsToSpace(dx, dy float64) (int, int, int, int) {
// SharesCells returns whether the Object occupies a cell shared by the specified other Object. // SharesCells returns whether the Object occupies a cell shared by the specified other Object.
func (obj *Object) SharesCells(other *Object) bool { func (obj *Object) SharesCells(other *Object) bool {
for _, cell := range obj.TouchingCells { rb := obj.TouchingCells
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
cell := rb.GetByFrameId(i).(*Cell)
if cell.Contains(other) { if cell.Contains(other) {
return true return true
} }
} }
return false return false
} }
// SharesCellsTags returns if the Cells the Object occupies have an object with the specified tags. // SharesCellsTags returns if the Cells the Object occupies have an object with the specified tags.
func (obj *Object) SharesCellsTags(tags ...string) bool { func (obj *Object) SharesCellsTags(tags ...string) bool {
for _, cell := range obj.TouchingCells { rb := obj.TouchingCells
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
cell := rb.GetByFrameId(i).(*Cell)
if cell.ContainsTags(tags...) { if cell.ContainsTags(tags...) {
return true return true
} }
@ -218,13 +237,12 @@ func (obj *Object) SetBounds(topLeft, bottomRight Vector) {
// Check checks the space around the object using the designated delta movement (dx and dy). This is done by querying the containing Space's Cells // Check checks the space around the object using the designated delta movement (dx and dy). This is done by querying the containing Space's Cells
// so that it can see if moving it would coincide with a cell that houses another Object (filtered using the given selection of tag strings). If so, // so that it can see if moving it would coincide with a cell that houses another Object (filtered using the given selection of tag strings). If so,
// Check returns a Collision. If no objects are found or the Object does not exist within a Space, this function returns nil. // Check returns a Collision. If no objects are found or the Object does not exist within a Space, this function returns nil.
func (obj *Object) Check(dx, dy float64, tags ...string) *Collision { func (obj *Object) CheckAllWithHolder(dx, dy float64, cc *Collision) bool {
if obj.Space == nil { if obj.Space == nil {
return nil return false
} }
cc := NewCollision()
cc.checkingObject = obj cc.checkingObject = obj
if dx < 0 { if dx < 0 {
@ -253,63 +271,36 @@ func (obj *Object) Check(dx, dy float64, tags ...string) *Collision {
if c := obj.Space.Cell(x, y); c != nil { if c := obj.Space.Cell(x, y); c != nil {
for _, o := range c.Objects { rb := c.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
// We only want cells that have objects other than the checking object, or that aren't on the ignore list. // We only want cells that have objects other than the checking object, or that aren't on the ignore list.
if ignored := obj.ignoreList[o]; o == obj || ignored { if ignored := obj.ignoreList[o]; o == obj || ignored {
continue continue
} }
if _, added := objectsAdded[o]; (len(tags) == 0 || o.HasTags(tags...)) && !added { if _, added := objectsAdded[o]; !added {
cc.Objects.Put(o)
cc.Objects = append(cc.Objects, o)
objectsAdded[o] = true objectsAdded[o] = true
if _, added := cellsAdded[c]; !added { if _, added := cellsAdded[c]; !added {
cc.Cells = append(cc.Cells, c) cc.Cells.Put(c)
cellsAdded[c] = true cellsAdded[c] = true
} }
continue continue
} }
}
} }
} }
} }
if 0 >= cc.Objects.Cnt {
return false
} }
if len(cc.Objects) == 0 { return true
return nil
}
/*
// In my use case, order of objects within a collision instance is not needed, and this also favors both runtime performance & size reduction of `jsexport.js`.
ox, oy := cc.checkingObject.Center()
oc := Vector{ox, oy}
sort.Slice(cc.Objects, func(i, j int) bool {
ix, iy := cc.Objects[i].Center()
jx, jy := cc.Objects[j].Center()
return Vector{ix, iy}.Sub(oc).Magnitude2() < Vector{jx, jy}.Sub(oc).Magnitude2()
})
cw := cc.checkingObject.Space.CellWidth
ch := cc.checkingObject.Space.CellHeight
sort.Slice(cc.Cells, func(i, j int) bool {
return Vector{float64(cc.Cells[i].X*cw + (cw / 2)), float64(cc.Cells[i].Y*ch + (ch / 2))}.Sub(oc).Magnitude2() <
Vector{float64(cc.Cells[j].X*cw + (cw / 2)), float64(cc.Cells[j].Y*ch + (ch / 2))}.Sub(oc).Magnitude2()
})
*/
return cc
} }
// Overlaps returns if an Object overlaps another Object. // Overlaps returns if an Object overlaps another Object.

View File

@ -1,4 +1,4 @@
package battle package resolv
const ( const (
RING_BUFF_CONSECUTIVE_SET = int32(0) RING_BUFF_CONSECUTIVE_SET = int32(0)
@ -20,6 +20,8 @@ func NewRingBuffer(n int32) *RingBuffer {
return &RingBuffer{ return &RingBuffer{
Ed: 0, Ed: 0,
St: 0, St: 0,
EdFrameId: 0,
StFrameId: 0,
N: n, N: n,
Cnt: 0, Cnt: 0,
Eles: make([]interface{}, n), Eles: make([]interface{}, n),
@ -122,3 +124,13 @@ func (rb *RingBuffer) SetByFrameId(pItem interface{}, frameId int32) (int32, int
return ret, oldStFrameId, oldEdFrameId return ret, oldStFrameId, oldEdFrameId
} }
func (rb *RingBuffer) Clear() {
for 0 < rb.Cnt {
rb.Pop()
}
rb.St = 0
rb.Ed = 0
rb.StFrameId = 0
rb.EdFrameId = 0
}

View File

@ -38,8 +38,9 @@ func (line *Line) Project(axis Vector) Vector {
} }
func (line *Line) Normal() Vector { func (line *Line) Normal() Vector {
v := line.Vector() dy := line.End[1] - line.Start[1]
return Vector{v[1], -v[0]}.Unit() dx := line.End[0] - line.Start[0]
return Vector{dy, -dx}.Unit()
} }
func (line *Line) Vector() Vector { func (line *Line) Vector() Vector {
@ -177,24 +178,20 @@ func (cp *ConvexPolygon) AddPoints(vertexPositions ...float64) {
// Lines returns a slice of transformed Lines composing the ConvexPolygon. // Lines returns a slice of transformed Lines composing the ConvexPolygon.
func (cp *ConvexPolygon) Lines() []*Line { func (cp *ConvexPolygon) Lines() []*Line {
lines := []*Line{}
vertices := cp.Transformed() vertices := cp.Transformed()
linesCnt := len(vertices)
if !cp.Closed {
linesCnt -= 1
}
lines := make([]*Line, linesCnt)
for i := 0; i < len(vertices); i++ { for i := 0; i < linesCnt; i++ {
start, end := vertices[i], vertices[0] start, end := vertices[i], vertices[0]
if i < len(vertices)-1 { if i < len(vertices)-1 {
end = vertices[i+1] end = vertices[i+1]
} else if !cp.Closed {
break
} }
line := NewLine(start[0], start[1], end[0], end[1]) line := NewLine(start[0], start[1], end[0], end[1])
lines[i] = line
lines = append(lines, line)
} }
return lines return lines
@ -203,9 +200,9 @@ func (cp *ConvexPolygon) Lines() []*Line {
// Transformed returns the ConvexPolygon's points / vertices, transformed according to the ConvexPolygon's position. // Transformed returns the ConvexPolygon's points / vertices, transformed according to the ConvexPolygon's position.
func (cp *ConvexPolygon) Transformed() []Vector { func (cp *ConvexPolygon) Transformed() []Vector {
transformed := []Vector{} transformed := make([]Vector, len(cp.Points))
for _, point := range cp.Points { for i, point := range cp.Points {
transformed = append(transformed, Vector{point[0] + cp.X, point[1] + cp.Y}) transformed[i] = Vector{point[0] + cp.X, point[1] + cp.Y}
} }
return transformed return transformed
} }
@ -275,12 +272,14 @@ func (cp *ConvexPolygon) Center() Vector {
pos := Vector{0, 0} pos := Vector{0, 0}
for _, v := range cp.Transformed() { vertices := cp.Transformed()
for _, v := range vertices {
pos.Add(v) pos.Add(v)
} }
pos[0] /= float64(len(cp.Transformed())) denom := float64(len(vertices))
pos[1] /= float64(len(cp.Transformed())) pos[0] /= denom
pos[1] /= denom
return pos return pos
@ -305,10 +304,10 @@ func (cp *ConvexPolygon) Project(axis Vector) Projection {
// SATAxes returns the axes of the ConvexPolygon for SAT intersection testing. // SATAxes returns the axes of the ConvexPolygon for SAT intersection testing.
func (cp *ConvexPolygon) SATAxes() []Vector { func (cp *ConvexPolygon) SATAxes() []Vector {
lines := cp.Lines()
axes := []Vector{} axes := make([]Vector, len(lines))
for _, line := range cp.Lines() { for i, line := range lines {
axes = append(axes, line.Normal()) axes[i] = line.Normal()
} }
return axes return axes

View File

@ -30,7 +30,20 @@ func NewSpace(spaceWidth, spaceHeight, cellWidth, cellHeight int) *Space {
} }
// [WARNING] The slice type boxing/unboxing is proved by profiling to be heavy after transpiled to JavaScript, thus adding some "XxxSingle" shortcuts here.
// Add adds the specified Objects to the Space, updating the Space's cells to refer to the Object. // Add adds the specified Objects to the Space, updating the Space's cells to refer to the Object.
func (sp *Space) AddSingle(obj *Object) {
if sp == nil {
panic("ERROR: space is nil")
}
obj.Space = sp
// We call Update() once to make sure the object gets its cells added.
obj.Update()
}
func (sp *Space) Add(objects ...*Object) { func (sp *Space) Add(objects ...*Object) {
if sp == nil { if sp == nil {
@ -50,6 +63,20 @@ func (sp *Space) Add(objects ...*Object) {
// Remove removes the specified Objects from being associated with the Space. This should be done whenever an Object is removed from the // Remove removes the specified Objects from being associated with the Space. This should be done whenever an Object is removed from the
// game. // game.
func (sp *Space) RemoveSingle(obj *Object) {
if sp == nil {
panic("ERROR: space is nil")
}
for 0 < obj.TouchingCells.Cnt {
cell := obj.TouchingCells.Pop().(*Cell)
cell.unregister(obj)
}
obj.Space = nil
}
func (sp *Space) Remove(objects ...*Object) { func (sp *Space) Remove(objects ...*Object) {
if sp == nil { if sp == nil {
@ -57,13 +84,11 @@ func (sp *Space) Remove(objects ...*Object) {
} }
for _, obj := range objects { for _, obj := range objects {
for 0 < obj.TouchingCells.Cnt {
for _, cell := range obj.TouchingCells { cell := obj.TouchingCells.Pop().(*Cell)
cell.unregister(obj) cell.unregister(obj)
} }
obj.TouchingCells = []*Cell{}
obj.Space = nil obj.Space = nil
} }
@ -80,16 +105,14 @@ func (sp *Space) Objects() []*Object {
for cy := range sp.Cells { for cy := range sp.Cells {
for cx := range sp.Cells[cy] { for cx := range sp.Cells[cy] {
rb := sp.Cells[cy][cx].Objects
for _, o := range sp.Cells[cy][cx].Objects { for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if _, added := objectsAdded[o]; !added { if _, added := objectsAdded[o]; !added {
objects = append(objects, o) objects = append(objects, o)
objectsAdded[o] = true objectsAdded[o] = true
} }
} }
} }
} }
@ -100,19 +123,13 @@ func (sp *Space) Objects() []*Object {
// Resize resizes the internal Cells array. // Resize resizes the internal Cells array.
func (sp *Space) Resize(width, height int) { func (sp *Space) Resize(width, height int) {
sp.Cells = make([][]*Cell, height)
sp.Cells = [][]*Cell{}
for y := 0; y < height; y++ { for y := 0; y < height; y++ {
sp.Cells[y] = make([]*Cell, width)
sp.Cells = append(sp.Cells, []*Cell{})
for x := 0; x < width; x++ { for x := 0; x < width; x++ {
sp.Cells[y] = append(sp.Cells[y], newCell(x, y)) sp.Cells[y][x] = newCell(x, y)
} }
} }
} }
// Cell returns the Cell at the given cellular / spatial (not world) X and Y position in the Space. If the X and Y position are // Cell returns the Cell at the given cellular / spatial (not world) X and Y position in the Space. If the X and Y position are
@ -137,25 +154,23 @@ func (sp *Space) CheckCells(x, y, w, h int, tags ...string) *Object {
cell := sp.Cell(ix, iy) cell := sp.Cell(ix, iy)
if cell != nil { if cell != nil {
rb := cell.Objects
if len(tags) > 0 { if len(tags) > 0 {
if cell.ContainsTags(tags...) { if cell.ContainsTags(tags...) {
for _, obj := range cell.Objects { for i := rb.StFrameId; i < rb.EdFrameId; i++ {
obj := rb.GetByFrameId(i).(*Object)
if obj.HasTags(tags...) { if obj.HasTags(tags...) {
return obj return obj
} }
} }
} }
} else if cell.Occupied() { } else if cell.Occupied() {
return cell.Objects[0] return rb.GetByFrameId(rb.StFrameId).(*Object)
} }
} }
} }
} }
return nil return nil
@ -178,10 +193,13 @@ func (sp *Space) CheckCellsWorld(x, y, w, h float64, tags ...string) *Object {
func (sp *Space) UnregisterAllObjects() { func (sp *Space) UnregisterAllObjects() {
for y := 0; y < len(sp.Cells); y++ { for y := 0; y < len(sp.Cells); y++ {
for x := 0; x < len(sp.Cells[y]); x++ { for x := 0; x < len(sp.Cells[y]); x++ {
cell := sp.Cells[y][x] cell := sp.Cells[y][x]
sp.Remove(cell.Objects...) rb := cell.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
sp.RemoveSingle(o)
}
} }
} }