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)
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

View File

@ -1,11 +1,11 @@
<?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="65" source="tiles1.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">
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>
</layer>
<objectgroup id="1" name="PlayerStartingPos">
@ -84,12 +84,12 @@
<property name="boundary_type" value="barrier"/>
</properties>
</object>
<object id="84" x="640" y="224" width="16" height="800">
<object id="84" x="640" y="224" width="16" height="416">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
</object>
<object id="85" x="1680" y="224" width="16" height="800">
<object id="85" x="1680" y="224" width="16" height="416">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
@ -149,17 +149,12 @@
<property name="boundary_type" value="barrier"/>
</properties>
</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">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
</object>
<object id="119" x="656" y="592" width="1024" height="416">
<object id="119" x="656" y="592" width="512" height="48">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
@ -169,5 +164,10 @@
<property name="boundary_type" value="barrier"/>
</properties>
</object>
<object id="137" x="1168" y="592" width="512" height="48">
<properties>
<property name="boundary_type" value="barrier"/>
</properties>
</object>
</objectgroup>
</map>

View File

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

View File

@ -72,11 +72,14 @@
"__id__": 3
},
{
"__id__": 10
"__id__": 9
}
],
"_active": true,
"_components": [
{
"__id__": 50
},
{
"__id__": 51
},
@ -88,9 +91,6 @@
},
{
"__id__": 54
},
{
"__id__": 55
}
],
"_prefab": null,
@ -156,9 +156,6 @@
},
{
"__id__": 5
},
{
"__id__": 6
}
],
"_prefab": null,
@ -220,34 +217,6 @@
"_tmxFile": null,
"_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",
"_name": "",
@ -266,7 +235,7 @@
"__uuid__": "d92d4831-cd65-4eb5-90bd-b77021aec35b"
},
"joystickInputControllerNode": {
"__id__": 7
"__id__": 6
},
"confirmLogoutPrefab": null,
"simplePressToGoDialogPrefab": null,
@ -279,19 +248,19 @@
"forceBigEndianFloatingNumDecoding": false,
"renderFrameIdLagTolerance": 4,
"inputFrameFrontLabel": {
"__id__": 14
"__id__": 13
},
"sendingQLabel": {
"__id__": 16
"__id__": 15
},
"inputFrameDownsyncQLabel": {
"__id__": 18
"__id__": 17
},
"peerInputFrameUpsyncQLabel": {
"__id__": 20
"__id__": 19
},
"rollbackFramesLabel": {
"__id__": 22
"__id__": 21
},
"skippedRenderFrameCntLabel": null,
"_id": "e5xQdv12xLoIRr0b36Pie+"
@ -301,17 +270,17 @@
"_name": "JoystickContainer",
"_objFlags": 0,
"_parent": {
"__id__": 8
"__id__": 7
},
"_children": [
{
"__id__": 45
"__id__": 44
}
],
"_active": true,
"_components": [
{
"__id__": 50
"__id__": 49
}
],
"_prefab": null,
@ -367,26 +336,26 @@
"_name": "Interactive",
"_objFlags": 0,
"_parent": {
"__id__": 9
"__id__": 8
},
"_children": [
{
"__id__": 7
"__id__": 6
},
{
"__id__": 34
"__id__": 33
},
{
"__id__": 36
"__id__": 35
},
{
"__id__": 40
"__id__": 39
}
],
"_active": true,
"_components": [
{
"__id__": 44
"__id__": 43
}
],
"_prefab": null,
@ -442,20 +411,20 @@
"_name": "WidgetsAboveAll",
"_objFlags": 0,
"_parent": {
"__id__": 10
"__id__": 9
},
"_children": [
{
"__id__": 12
"__id__": 11
},
{
"__id__": 8
"__id__": 7
}
],
"_active": true,
"_components": [
{
"__id__": 33
"__id__": 32
}
],
"_prefab": null,
@ -515,13 +484,13 @@
},
"_children": [
{
"__id__": 9
"__id__": 8
}
],
"_active": true,
"_components": [
{
"__id__": 11
"__id__": 10
}
],
"_prefab": null,
@ -549,7 +518,7 @@
"array": [
0,
0,
216.50635094610968,
210.4441731196186,
0,
0,
0,
@ -577,7 +546,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 10
"__id__": 9
},
"_enabled": true,
"_cullingMask": 4294967295,
@ -613,35 +582,35 @@
"_name": "DebugInfo",
"_objFlags": 0,
"_parent": {
"__id__": 9
"__id__": 8
},
"_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,
"_components": [
{
"__id__": 32
"__id__": 31
}
],
"_prefab": null,
@ -697,13 +666,13 @@
"_name": "inputFrameFront",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 14
"__id__": 13
}
],
"_prefab": null,
@ -759,7 +728,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 13
"__id__": 12
},
"_enabled": true,
"_materials": [
@ -789,13 +758,13 @@
"_name": "sendingQ",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 16
"__id__": 15
}
],
"_prefab": null,
@ -851,7 +820,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 15
"__id__": 14
},
"_enabled": true,
"_materials": [
@ -881,13 +850,13 @@
"_name": "inputFrameDownsyncQ",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 18
"__id__": 17
}
],
"_prefab": null,
@ -943,7 +912,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 17
"__id__": 16
},
"_enabled": true,
"_materials": [
@ -973,13 +942,13 @@
"_name": "peerInputFrameUpsyncQ",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 20
"__id__": 19
}
],
"_prefab": null,
@ -1035,7 +1004,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 19
"__id__": 18
},
"_enabled": true,
"_materials": [
@ -1065,13 +1034,13 @@
"_name": "rollbackFrames",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 22
"__id__": 21
}
],
"_prefab": null,
@ -1127,7 +1096,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 21
"__id__": 20
},
"_enabled": true,
"_materials": [
@ -1157,13 +1126,13 @@
"_name": "skippedCnt",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 24
"__id__": 23
}
],
"_prefab": null,
@ -1219,7 +1188,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 23
"__id__": 22
},
"_enabled": true,
"_materials": [
@ -1251,23 +1220,23 @@
"_name": "RoomIdIndicator",
"_objFlags": 0,
"_parent": {
"__id__": 12
"__id__": 11
},
"_children": [
{
"__id__": 26
"__id__": 25
},
{
"__id__": 28
"__id__": 27
}
],
"_active": false,
"_components": [
{
"__id__": 30
"__id__": 29
},
{
"__id__": 31
"__id__": 30
}
],
"_prefab": null,
@ -1323,13 +1292,13 @@
"_name": "label",
"_objFlags": 0,
"_parent": {
"__id__": 25
"__id__": 24
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 27
"__id__": 26
}
],
"_prefab": null,
@ -1385,7 +1354,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 26
"__id__": 25
},
"_enabled": true,
"_materials": [],
@ -1413,13 +1382,13 @@
"_name": "BoundRoomIdLabel",
"_objFlags": 0,
"_parent": {
"__id__": 25
"__id__": 24
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 29
"__id__": 28
}
],
"_prefab": null,
@ -1475,7 +1444,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 28
"__id__": 27
},
"_enabled": true,
"_materials": [],
@ -1503,7 +1472,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 25
"__id__": 24
},
"_enabled": true,
"_layoutSize": {
@ -1536,7 +1505,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 25
"__id__": 24
},
"_enabled": true,
"_materials": [],
@ -1566,7 +1535,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 12
"__id__": 11
},
"_enabled": true,
"_layoutSize": {
@ -1599,7 +1568,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 9
"__id__": 8
},
"_enabled": true,
"alignMode": 1,
@ -1626,13 +1595,13 @@
"_name": "CountdownSeconds",
"_objFlags": 0,
"_parent": {
"__id__": 8
"__id__": 7
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 35
"__id__": 34
}
],
"_prefab": null,
@ -1688,7 +1657,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 34
"__id__": 33
},
"_enabled": true,
"_materials": [
@ -1718,11 +1687,11 @@
"_name": "BtnA",
"_objFlags": 0,
"_parent": {
"__id__": 8
"__id__": 7
},
"_children": [
{
"__id__": 37
"__id__": 36
}
],
"_active": true,
@ -1780,16 +1749,16 @@
"_name": "Background",
"_objFlags": 0,
"_parent": {
"__id__": 36
"__id__": 35
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 38
"__id__": 37
},
{
"__id__": 39
"__id__": 38
}
],
"_prefab": null,
@ -1845,7 +1814,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 37
"__id__": 36
},
"_enabled": true,
"_materials": [
@ -1879,7 +1848,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 37
"__id__": 36
},
"_enabled": true,
"alignMode": 0,
@ -1906,11 +1875,11 @@
"_name": "BtnB",
"_objFlags": 0,
"_parent": {
"__id__": 8
"__id__": 7
},
"_children": [
{
"__id__": 41
"__id__": 40
}
],
"_active": true,
@ -1968,16 +1937,16 @@
"_name": "Background",
"_objFlags": 0,
"_parent": {
"__id__": 40
"__id__": 39
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 42
"__id__": 41
},
{
"__id__": 43
"__id__": 42
}
],
"_prefab": null,
@ -2033,7 +2002,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 41
"__id__": 40
},
"_enabled": true,
"_materials": [
@ -2067,7 +2036,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 41
"__id__": 40
},
"_enabled": true,
"alignMode": 0,
@ -2094,7 +2063,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 8
"__id__": 7
},
"_enabled": true,
"alignMode": 1,
@ -2121,20 +2090,20 @@
"_name": "JoystickBG",
"_objFlags": 0,
"_parent": {
"__id__": 7
"__id__": 6
},
"_children": [
{
"__id__": 46
"__id__": 45
}
],
"_active": true,
"_components": [
{
"__id__": 48
"__id__": 47
},
{
"__id__": 49
"__id__": 48
}
],
"_prefab": null,
@ -2190,13 +2159,13 @@
"_name": "Joystick",
"_objFlags": 0,
"_parent": {
"__id__": 45
"__id__": 44
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 47
"__id__": 46
}
],
"_prefab": null,
@ -2252,7 +2221,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 46
"__id__": 45
},
"_enabled": true,
"_materials": [
@ -2286,7 +2255,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 45
"__id__": 44
},
"_enabled": true,
"_materials": [
@ -2320,7 +2289,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 45
"__id__": 44
},
"_enabled": true,
"alignMode": 0,
@ -2347,7 +2316,7 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 7
"__id__": 6
},
"_enabled": true,
"alignMode": 0,
@ -2449,16 +2418,16 @@
},
"_enabled": true,
"translationListenerNode": {
"__id__": 7
"__id__": 6
},
"zoomingListenerNode": {
"__id__": 3
},
"stickhead": {
"__id__": 46
"__id__": 45
},
"base": {
"__id__": 45
"__id__": 44
},
"joyStickEps": 0.1,
"magicLeanLowerBound": 0.414,
@ -2479,10 +2448,10 @@
"linearMovingEps": 0.1,
"scaleByEps": 0.0375,
"btnA": {
"__id__": 36
"__id__": 35
},
"btnB": {
"__id__": 40
"__id__": 39
},
"_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. */
self.inputFrameUpsyncDelayTolerance = 2;
self.collisionMinStep = 2;
self.collisionMinStep = 8;
self.renderCacheSize = 1024;
self.serverFps = 60;
@ -98,7 +98,7 @@ cc.Class({
const p2Vpos = gopkgs.WorldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y);
const colliderRadiusV = gopkgs.WorldToVirtualGridPos(12.0, 0);
const speciesIdList = [4096, 0];
const speciesIdList = [1, 0];
const chConfigsOrderedByJoinIndex = gopkgs.GetCharacterConfigsOrderedByJoinIndex(speciesIdList);
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:usesCleartextTraffic="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:taskAffinity="" >
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

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

View File

@ -16,7 +16,7 @@ const (
PATTERN_ID_UNABLE_TO_OP = -2
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
GRAVITY_X = int32(0)
@ -226,16 +226,31 @@ func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool
}
if 1 < aCnt {
for _, axis := range a.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
// 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]
}
dy := v[1] - u[1]
dx := v[0] - u[0]
axis := resolv.Vector{dy, -dx}.Unit()
if isPolygonPairSeparatedByDir(a, b, axis, result) {
return false
}
}
}
if 1 < bCnt {
for _, axis := range b.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
for i, _ := range b.Points {
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
}
}
@ -411,8 +426,7 @@ func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBounding
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 {
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D, pHardPushback *[]Vec2D, collision *resolv.Collision) int {
virtualGripToWall := float64(0)
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
}
collision := playerCollider.Check(virtualGripToWall, 0)
if nil == collision {
return &ret
retCnt := 0
collided := playerCollider.CheckAllWithHolder(virtualGripToWall, 0, collision)
if !collided {
return retCnt
}
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//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
switch obj.Data.(type) {
case *PlayerDownsync, *MeleeBullet, *FireballBullet:
@ -456,15 +475,16 @@ func calcHardPushbacksNorms(joinIndex int32, currPlayerDownsync, thatPlayerInNex
// ALWAY snap into hardPushbacks!
// [OverlapX, OverlapY] is the unit vector that points into the platform
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.Y += pushbackY
retCnt++
//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)
delayedInputFrameId := ConvertToDelayedInputFrameId(currRenderFrame.Id)
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.
*/
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!
roomCapacity := len(currRenderFrame.PlayersArr)
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?
nextRenderFrameFireballBullets := make([]*FireballBullet, 0, len(currRenderFrame.FireballBullets))
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)
bulletLocalId := currRenderFrame.BulletLocalIdCounter
@ -813,7 +838,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
playerColliders[i] = playerCollider
// Add to collision system
collisionSys.Add(playerCollider)
collisionSys.AddSingle(playerCollider)
if currPlayerDownsync.InAir {
if ATK_CHARACTER_STATE_ONWALL == currPlayerDownsync.CharacterState && !jumpedOrNotList[i] {
@ -850,7 +875,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
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")
collisionSys.Add(newBulletCollider)
collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider)
fireballBullet.BlState = BULLET_ACTIVE
if fireballBullet.BlState != prevFireball.BlState {
@ -891,7 +916,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
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")
collisionSys.Add(newBulletCollider)
collisionSys.AddSingle(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider)
meleeBullet.BlState = BULLET_ACTIVE
if meleeBullet.BlState != prevMelee.BlState {
@ -908,12 +933,16 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
playerCollider := playerColliders[i]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
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]
landedOnGravityPushback := false
if collision := playerCollider.Check(0, 0); nil != collision {
for _, obj := range collision.Objects {
if collided := playerCollider.CheckAllWithHolder(0, 0, collision); collided {
for true {
obj := collision.FirstCollidedObject()
if nil == obj {
break
}
isBarrier, isAnotherPlayer, isBullet := false, false, false
switch v := obj.Data.(type) {
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.
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
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
if SNAP_INTO_PLATFORM_THRESHOLD < normAlignmentWithGravity {
landedOnGravityPushback = true
//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 {
thatPlayerInNextFrame.InAir = false
fallStopping := (currPlayerDownsync.InAir && 0 >= currPlayerDownsync.VelY)
@ -1004,9 +1037,10 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
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 {
if _, existent := noOpSet[currPlayerDownsync.CharacterState]; !existent && 0 < hardPushbackCnt {
// [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))
normAlignmentWithHorizon2 := (hardPushbackNorm.X*float64(-1.0) + hardPushbackNorm.Y*float64(0.0))
if VERTICAL_PLATFORM_THRESHOLD < normAlignmentWithHorizon1 {
@ -1031,14 +1065,15 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
// 5. Check bullet-anything collisions
for _, bulletCollider := range bulletColliders {
collision := bulletCollider.Check(0, 0)
bulletCollider.Space.Remove(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
exploded := false
explodedOnAnotherPlayer := false
if nil == collision {
collided := bulletCollider.CheckAllWithHolder(0, 0, collision)
bulletCollider.Space.RemoveSingle(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
if !collided {
continue
}
exploded := false
explodedOnAnotherPlayer := false
var bulletStaticAttr *BulletConfig = nil
var bulletBattleAttr *BulletBattleAttr = nil
switch v := bulletCollider.Data.(type) {
@ -1052,7 +1087,11 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
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)
switch t := obj.Data.(type) {
case *PlayerDownsync:
@ -1170,7 +1209,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer
}
for _, playerCollider := range playerColliders {
playerCollider.Space.Remove(playerCollider)
playerCollider.Space.RemoveSingle(playerCollider)
}
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 {
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)
collider.SetShape(shape)
collider.Data = data

View File

@ -107,7 +107,7 @@ var Characters = map[int]*CharacterConfig{
GetUpInvinsibleFrames: int32(10),
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),
JumpingFramesToRecover: int32(2),
@ -234,7 +234,7 @@ var skills = map[int]*Skill{
HitStunFrames: int32(13),
BlockStunFrames: int32(9),
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,
PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO),
PushbackVelY: int32(0),
@ -332,7 +332,7 @@ var skills = map[int]*Skill{
HitStunFrames: int32(13),
BlockStunFrames: int32(9),
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,
PushbackVelX: int32(float64(0.5) * WORLD_TO_VIRTUAL_GRID_RATIO),
PushbackVelY: int32(0),

View File

@ -15,7 +15,7 @@ func NewInputFrameDownsync(inputFrameId int32, inputList []uint64, confirmedList
}
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 {
@ -113,7 +113,7 @@ func GetCharacterConfigsOrderedByJoinIndex(speciesIdList []int) []*js.Object {
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
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.
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.
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.
@ -11,25 +11,27 @@ func newCell(x, y int) *Cell {
return &Cell{
X: x,
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.
func (cell *Cell) register(obj *Object) {
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.
func (cell *Cell) unregister(obj *Object) {
for i, o := range cell.Objects {
rb := cell.Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if o == obj {
cell.Objects[i] = cell.Objects[len(cell.Objects)-1]
cell.Objects = cell.Objects[:len(cell.Objects)-1]
// swap with the st element
rb.SetByFrameId(rb.GetByFrameId(rb.StFrameId), i)
// pop the current st element
rb.Pop()
break
}
@ -39,7 +41,9 @@ func (cell *Cell) unregister(obj *Object) {
// Contains returns whether a Cell contains the specified Object at its position.
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 {
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.
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...) {
return true
}
@ -59,5 +65,5 @@ func (cell *Cell) ContainsTags(tags ...string) bool {
// Occupied returns whether a Cell contains any Objects at all.
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 {
checkingObject *Object // The checking object
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.
Cells []*Cell // Slice of cells 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 *RingBuffer // Slice of cells that were collided with; sorted according to distance to calling Object.
}
func NewCollision() *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().
func (cc *Collision) HasTags(tags ...string) bool {
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 {
continue
}
@ -38,8 +54,9 @@ func (cc *Collision) ObjectsByTags(tags ...string) []*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 {
continue
}
@ -105,7 +122,7 @@ func (cc *Collision) SlideAgainstCell(cell *Cell, avoidTags ...string) Vector {
sp := cc.checkingObject.Space
collidingCell := cc.Cells[0]
collidingCell := cc.Cells.GetByFrameId(cc.Cells.StFrameId).(*Cell)
ccX, ccY := sp.SpaceToWorld(collidingCell.X, collidingCell.Y)
hX := float64(sp.CellWidth) / 2.0
hY := float64(sp.CellHeight) / 2.0

View File

@ -2,7 +2,6 @@ package resolv
import (
"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.
@ -10,19 +9,34 @@ type Object struct {
Shape Shape // A shape for more specific collision-checking.
Space *Space // Reference to the Space the Object exists within
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
ignoreList map[*Object]bool // Set of Objects to ignore when checking for collisions
tags []string // A list of tags the Object has
}
// 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 {
o := &Object{
X: x,
Y: y,
W: w,
H: h,
TouchingCells: NewRingBuffer(512),
tags: []string{},
ignoreList: map[*Object]bool{},
}
@ -59,7 +73,7 @@ func (obj *Object) Update() {
space := obj.Space
obj.Space.Remove(obj)
obj.Space.RemoveSingle(obj)
obj.Space = space
@ -73,7 +87,7 @@ func (obj *Object) Update() {
if c != nil {
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.
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) {
return true
}
}
return false
}
// SharesCellsTags returns if the Cells the Object occupies have an object with the specified tags.
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...) {
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
// 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.
func (obj *Object) Check(dx, dy float64, tags ...string) *Collision {
func (obj *Object) CheckAllWithHolder(dx, dy float64, cc *Collision) bool {
if obj.Space == nil {
return nil
return false
}
cc := NewCollision()
cc.checkingObject = obj
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 {
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.
if ignored := obj.ignoreList[o]; o == obj || ignored {
continue
}
if _, added := objectsAdded[o]; (len(tags) == 0 || o.HasTags(tags...)) && !added {
cc.Objects = append(cc.Objects, o)
if _, added := objectsAdded[o]; !added {
cc.Objects.Put(o)
objectsAdded[o] = true
if _, added := cellsAdded[c]; !added {
cc.Cells = append(cc.Cells, c)
cc.Cells.Put(c)
cellsAdded[c] = true
}
continue
}
}
}
}
}
if 0 >= cc.Objects.Cnt {
return false
}
if len(cc.Objects) == 0 {
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
return true
}
// Overlaps returns if an Object overlaps another Object.

View File

@ -1,4 +1,4 @@
package battle
package resolv
const (
RING_BUFF_CONSECUTIVE_SET = int32(0)
@ -20,6 +20,8 @@ func NewRingBuffer(n int32) *RingBuffer {
return &RingBuffer{
Ed: 0,
St: 0,
EdFrameId: 0,
StFrameId: 0,
N: n,
Cnt: 0,
Eles: make([]interface{}, n),
@ -122,3 +124,13 @@ func (rb *RingBuffer) SetByFrameId(pItem interface{}, frameId int32) (int32, int
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 {
v := line.Vector()
return Vector{v[1], -v[0]}.Unit()
dy := line.End[1] - line.Start[1]
dx := line.End[0] - line.Start[0]
return Vector{dy, -dx}.Unit()
}
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.
func (cp *ConvexPolygon) Lines() []*Line {
lines := []*Line{}
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]
if i < len(vertices)-1 {
end = vertices[i+1]
} else if !cp.Closed {
break
}
line := NewLine(start[0], start[1], end[0], end[1])
lines = append(lines, line)
lines[i] = line
}
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.
func (cp *ConvexPolygon) Transformed() []Vector {
transformed := []Vector{}
for _, point := range cp.Points {
transformed = append(transformed, Vector{point[0] + cp.X, point[1] + cp.Y})
transformed := make([]Vector, len(cp.Points))
for i, point := range cp.Points {
transformed[i] = Vector{point[0] + cp.X, point[1] + cp.Y}
}
return transformed
}
@ -275,12 +272,14 @@ func (cp *ConvexPolygon) Center() Vector {
pos := Vector{0, 0}
for _, v := range cp.Transformed() {
vertices := cp.Transformed()
for _, v := range vertices {
pos.Add(v)
}
pos[0] /= float64(len(cp.Transformed()))
pos[1] /= float64(len(cp.Transformed()))
denom := float64(len(vertices))
pos[0] /= denom
pos[1] /= denom
return pos
@ -305,10 +304,10 @@ func (cp *ConvexPolygon) Project(axis Vector) Projection {
// SATAxes returns the axes of the ConvexPolygon for SAT intersection testing.
func (cp *ConvexPolygon) SATAxes() []Vector {
axes := []Vector{}
for _, line := range cp.Lines() {
axes = append(axes, line.Normal())
lines := cp.Lines()
axes := make([]Vector, len(lines))
for i, line := range lines {
axes[i] = line.Normal()
}
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.
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) {
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
// 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) {
if sp == nil {
@ -57,13 +84,11 @@ func (sp *Space) Remove(objects ...*Object) {
}
for _, obj := range objects {
for _, cell := range obj.TouchingCells {
for 0 < obj.TouchingCells.Cnt {
cell := obj.TouchingCells.Pop().(*Cell)
cell.unregister(obj)
}
obj.TouchingCells = []*Cell{}
obj.Space = nil
}
@ -80,16 +105,14 @@ func (sp *Space) Objects() []*Object {
for cy := range sp.Cells {
for cx := range sp.Cells[cy] {
for _, o := range sp.Cells[cy][cx].Objects {
rb := sp.Cells[cy][cx].Objects
for i := rb.StFrameId; i < rb.EdFrameId; i++ {
o := rb.GetByFrameId(i).(*Object)
if _, added := objectsAdded[o]; !added {
objects = append(objects, o)
objectsAdded[o] = true
}
}
}
}
@ -100,19 +123,13 @@ func (sp *Space) Objects() []*Object {
// Resize resizes the internal Cells array.
func (sp *Space) Resize(width, height int) {
sp.Cells = [][]*Cell{}
sp.Cells = make([][]*Cell, height)
for y := 0; y < height; y++ {
sp.Cells = append(sp.Cells, []*Cell{})
sp.Cells[y] = make([]*Cell, width)
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
@ -137,25 +154,23 @@ func (sp *Space) CheckCells(x, y, w, h int, tags ...string) *Object {
cell := sp.Cell(ix, iy)
if cell != nil {
rb := cell.Objects
if len(tags) > 0 {
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...) {
return obj
}
}
}
} else if cell.Occupied() {
return cell.Objects[0]
return rb.GetByFrameId(rb.StFrameId).(*Object)
}
}
}
}
return nil
@ -178,10 +193,13 @@ func (sp *Space) CheckCellsWorld(x, y, w, h float64, tags ...string) *Object {
func (sp *Space) UnregisterAllObjects() {
for y := 0; y < len(sp.Cells); y++ {
for x := 0; x < len(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)
}
}
}