renamed particle* to animation*, added asesprite animation loading

This commit is contained in:
Thomas von Dein 2024-04-02 20:05:23 +02:00
parent 20fa2639c6
commit 001b67f97a
14 changed files with 673 additions and 162 deletions

11
TODO.md
View File

@ -37,6 +37,17 @@
"collectible-detonating*.png" or we could use an animation map and "collectible-detonating*.png" or we could use an animation map and
specify a list of coordinates, etcpp. specify a list of coordinates, etcpp.
- Part I of the above: DONE.
- Part II:
- grid/grid.go:
- add Animation component to mappers
- check if the tile has tile.AnimateOnDestruct (or any other
triggers) set and configure animation compontent accordingly
- check if the tile has tile.AnimationSpriteSheet, assign to Animation.Tiles[]
- do it for any entity not just collectible so that any entity can have an animation
- rename Animation.Tiles to Sprites
- change animation_system to use this stuff instead of any hardcoded values.
## Collider Rework [abandoned: see branch collider-system, fails] ## Collider Rework [abandoned: see branch collider-system, fails]

View File

@ -11,7 +11,7 @@
"iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52", "iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52",
"jsonVersion": "1.5.3", "jsonVersion": "1.5.3",
"appBuildId": 473703, "appBuildId": 473703,
"nextUid": 76, "nextUid": 78,
"identifierStyle": "Capitalize", "identifierStyle": "Capitalize",
"toc": [], "toc": [],
"worldLayout": "Free", "worldLayout": "Free",
@ -215,7 +215,88 @@
"limitBehavior": "MoveLastOne", "limitBehavior": "MoveLastOne",
"pivotX": 0, "pivotX": 0,
"pivotY": 0, "pivotY": 0,
"fieldDefs": [] "fieldDefs": [
{
"identifier": "AnimateOnDestruct",
"doc": null,
"__type": "Bool",
"uid": 76,
"type": "F_Bool",
"isArray": false,
"canBeNull": false,
"arrayMinLength": null,
"arrayMaxLength": null,
"editorDisplayMode": "Hidden",
"editorDisplayScale": 1,
"editorDisplayPos": "Above",
"editorLinkStyle": "StraightArrow",
"editorDisplayColor": null,
"editorAlwaysShow": false,
"editorShowInWorld": true,
"editorCutLongValues": true,
"editorTextSuffix": null,
"editorTextPrefix": null,
"useForSmartColor": false,
"exportToToc": false,
"searchable": false,
"min": null,
"max": null,
"regex": null,
"acceptFileTypes": null,
"defaultOverride": {
"id": "V_Bool",
"params": [ true ]
},
"textLanguageMode": null,
"symmetricalRef": false,
"autoChainRef": true,
"allowOutOfLevelRef": true,
"allowedRefs": "OnlySame",
"allowedRefsEntityUid": null,
"allowedRefTags": [],
"tilesetUid": null
},
{
"identifier": "AnimateSpriteSheet",
"doc": null,
"__type": "String",
"uid": 77,
"type": "F_String",
"isArray": false,
"canBeNull": true,
"arrayMinLength": null,
"arrayMaxLength": null,
"editorDisplayMode": "Hidden",
"editorDisplayScale": 1,
"editorDisplayPos": "Above",
"editorLinkStyle": "StraightArrow",
"editorDisplayColor": null,
"editorAlwaysShow": false,
"editorShowInWorld": true,
"editorCutLongValues": true,
"editorTextSuffix": null,
"editorTextPrefix": null,
"useForSmartColor": false,
"exportToToc": false,
"searchable": false,
"min": null,
"max": null,
"regex": null,
"acceptFileTypes": null,
"defaultOverride": {
"id": "V_String",
"params": ["collectible-detonating"]
},
"textLanguageMode": null,
"symmetricalRef": false,
"autoChainRef": true,
"allowOutOfLevelRef": true,
"allowedRefs": "OnlySame",
"allowedRefsEntityUid": null,
"allowedRefTags": [],
"tilesetUid": null
}
]
}, },
{ {
"identifier": "ObstacleWest", "identifier": "ObstacleWest",
@ -1429,7 +1510,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,224], "px": [352,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 128, "__worldX": 128,
"__worldY": 224 "__worldY": 224
}, },
@ -1445,7 +1529,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,160], "px": [288,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 64, "__worldX": 64,
"__worldY": 160 "__worldY": 160
}, },
@ -1461,7 +1548,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [384,160], "px": [384,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 160, "__worldX": 160,
"__worldY": 160 "__worldY": 160
} }
@ -1613,7 +1703,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [128,288], "px": [128,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 608, "__worldX": 608,
"__worldY": 288 "__worldY": 288
}, },
@ -1629,7 +1722,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [480,192], "px": [480,192],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 960, "__worldX": 960,
"__worldY": 192 "__worldY": 192
} }
@ -1786,7 +1882,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [96,160], "px": [96,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1280, "__worldX": 1280,
"__worldY": 160 "__worldY": 160
} }
@ -1906,7 +2005,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [448,128], "px": [448,128],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2336, "__worldX": 2336,
"__worldY": 128 "__worldY": 128
}, },
@ -1922,7 +2024,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [128,384], "px": [128,384],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2016, "__worldX": 2016,
"__worldY": 384 "__worldY": 384
} }
@ -2095,7 +2200,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,224], "px": [288,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 64, "__worldX": 64,
"__worldY": 768 "__worldY": 768
}, },
@ -2159,7 +2267,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,224], "px": [352,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 128, "__worldX": 128,
"__worldY": 768 "__worldY": 768
}, },
@ -2349,7 +2460,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,224], "px": [288,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 768, "__worldX": 768,
"__worldY": 768 "__worldY": 768
}, },
@ -2571,7 +2685,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [96,160], "px": [96,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1280, "__worldX": 1280,
"__worldY": 704 "__worldY": 704
}, },
@ -2619,7 +2736,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [320,352], "px": [320,352],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1504, "__worldX": 1504,
"__worldY": 896 "__worldY": 896
}, },
@ -2651,7 +2771,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [128,128], "px": [128,128],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1312, "__worldX": 1312,
"__worldY": 672 "__worldY": 672
}, },
@ -2831,7 +2954,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [224,160], "px": [224,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2112, "__worldX": 2112,
"__worldY": 704 "__worldY": 704
}, },
@ -2863,7 +2989,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [416,224], "px": [416,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2304, "__worldX": 2304,
"__worldY": 768 "__worldY": 768
}, },
@ -2911,7 +3040,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,288], "px": [288,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2176, "__worldX": 2176,
"__worldY": 832 "__worldY": 832
}, },
@ -3089,7 +3221,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [160,224], "px": [160,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": -64, "__worldX": -64,
"__worldY": 1312 "__worldY": 1312
}, },
@ -3105,7 +3240,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [192,288], "px": [192,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": -32, "__worldX": -32,
"__worldY": 1376 "__worldY": 1376
}, },
@ -3309,7 +3447,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,288], "px": [352,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 832, "__worldX": 832,
"__worldY": 1376 "__worldY": 1376
}, },
@ -3561,7 +3702,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [416,288], "px": [416,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1600, "__worldX": 1600,
"__worldY": 1376 "__worldY": 1376
}, },
@ -3609,7 +3753,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [416,160], "px": [416,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1600, "__worldX": 1600,
"__worldY": 1248 "__worldY": 1248
}, },
@ -3917,7 +4064,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [192,256], "px": [192,256],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2080, "__worldX": 2080,
"__worldY": 1344 "__worldY": 1344
}, },
@ -4113,7 +4263,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [320,192], "px": [320,192],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 96, "__worldX": 96,
"__worldY": 1824 "__worldY": 1824
}, },
@ -4129,7 +4282,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,256], "px": [288,256],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 64, "__worldX": 64,
"__worldY": 1888 "__worldY": 1888
}, },
@ -4145,7 +4301,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,256], "px": [352,256],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 128, "__worldX": 128,
"__worldY": 1888 "__worldY": 1888
}, },
@ -4369,7 +4528,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [160,320], "px": [160,320],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 640, "__worldX": 640,
"__worldY": 1952 "__worldY": 1952
}, },
@ -4385,7 +4547,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [192,320], "px": [192,320],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 672, "__worldX": 672,
"__worldY": 1952 "__worldY": 1952
}, },
@ -4417,7 +4582,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [416,224], "px": [416,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 896, "__worldX": 896,
"__worldY": 1856 "__worldY": 1856
}, },
@ -4623,7 +4791,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [480,320], "px": [480,320],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1664, "__worldX": 1664,
"__worldY": 1952 "__worldY": 1952
}, },
@ -4655,7 +4826,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [288,192], "px": [288,192],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1472, "__worldX": 1472,
"__worldY": 1824 "__worldY": 1824
}, },
@ -4687,7 +4861,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,128], "px": [352,128],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1536, "__worldX": 1536,
"__worldY": 1760 "__worldY": 1760
}, },
@ -4703,7 +4880,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [192,288], "px": [192,288],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1376, "__worldX": 1376,
"__worldY": 1920 "__worldY": 1920
}, },
@ -5106,7 +5286,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [224,192], "px": [224,192],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2112, "__worldX": 2112,
"__worldY": 1824 "__worldY": 1824
} }
@ -5269,7 +5452,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [256,224], "px": [256,224],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 32, "__worldX": 32,
"__worldY": 2432 "__worldY": 2432
}, },
@ -5525,7 +5711,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [384,256], "px": [384,256],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 896, "__worldX": 896,
"__worldY": 2464 "__worldY": 2464
}, },
@ -5605,7 +5794,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [352,192], "px": [352,192],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 864, "__worldX": 864,
"__worldY": 2400 "__worldY": 2400
} }
@ -5767,7 +5959,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [320,256], "px": [320,256],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1536, "__worldX": 1536,
"__worldY": 2464 "__worldY": 2464
}, },
@ -5783,7 +5978,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [224,128], "px": [224,128],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1440, "__worldX": 1440,
"__worldY": 2336 "__worldY": 2336
}, },
@ -5799,7 +5997,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [384,320], "px": [384,320],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 1600, "__worldX": 1600,
"__worldY": 2528 "__worldY": 2528
} }
@ -5997,7 +6198,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [416,352], "px": [416,352],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2304, "__worldX": 2304,
"__worldY": 2560 "__worldY": 2560
}, },
@ -6013,7 +6217,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [160,128], "px": [160,128],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": 2048, "__worldX": 2048,
"__worldY": 2336 "__worldY": 2336
}, },
@ -6255,7 +6462,10 @@
"height": 32, "height": 32,
"defUid": 4, "defUid": 4,
"px": [160,160], "px": [160,160],
"fieldInstances": [], "fieldInstances": [
{ "__identifier": "AnimateOnDestruct", "__type": "Bool", "__value": true, "__tile": null, "defUid": 76, "realEditorValues": [] },
{ "__identifier": "AnimateSpriteSheet", "__type": "String", "__value": "collectible-detonating", "__tile": null, "defUid": 77, "realEditorValues": [] }
],
"__worldX": -64, "__worldX": -64,
"__worldY": 2912 "__worldY": 2912
} }

View File

@ -17,53 +17,57 @@ var Tiles = InitTiles()
// Tile: contains image, identifier (as used in level data) and // Tile: contains image, identifier (as used in level data) and
// additional properties // additional properties
type Tile struct { type Tile struct {
Id, Ref string Id, Ref string
Sprite *ebiten.Image Sprite *ebiten.Image
ToggleSprite *ebiten.Image ToggleSprite *ebiten.Image
Solid bool // wall brick Solid bool // wall brick
Player bool // player sphere Player bool // player sphere
IsPrimary bool // primary player sphere IsPrimary bool // primary player sphere
Renderable bool // visible, has sprite Renderable bool // visible, has sprite
Velocity bool // movable Velocity bool // movable
Collectible bool // collectible, vanishes once collected Collectible bool // collectible, vanishes once collected
Transient bool // turns into brick wall when traversed Transient bool // turns into brick wall when traversed
Destroyable bool // turns into empty floor when bumped into twice Destroyable bool // turns into empty floor when bumped into twice
Particle int // -1=unused, 0-3 = show image of slice Animation int // -1=unused, 0-3 = show image of slice
Tiles []*ebiten.Image // has N sprites Tiles []*ebiten.Image // has N sprites
TileNames []string // same thing, only the names TileNames []string // same thing, only the names
Obstacle bool // is an obstacle/enemy Obstacle bool // is an obstacle/enemy
Direction int // obstacle business end shows into this direction Direction int // obstacle business end shows into this direction
Shader *ebiten.Shader Shader *ebiten.Shader
Alpha *ebiten.Image Alpha *ebiten.Image
Bond bool // denotes an entity which can have a relation to another Bond bool // denotes an entity which can have a relation to another
Door bool // a door, can be manipulated by a switch Door bool // a door, can be manipulated by a switch
Switch bool // opens|closes a door Switch bool // opens|closes a door
AnimateOnDestruct bool // wether to animate destruction
AnimationSpriteSheet AnimationSet // which sprites to use (refers to an entry in assets.Animations[name])
} }
func (tile *Tile) Clone() *Tile { func (tile *Tile) Clone() *Tile {
newtile := &Tile{ newtile := &Tile{
Id: tile.Id, Id: tile.Id,
Ref: tile.Ref, Ref: tile.Ref,
Sprite: tile.Sprite, Sprite: tile.Sprite,
ToggleSprite: tile.ToggleSprite, ToggleSprite: tile.ToggleSprite,
Solid: tile.Solid, Solid: tile.Solid,
Player: tile.Player, Player: tile.Player,
IsPrimary: tile.IsPrimary, IsPrimary: tile.IsPrimary,
Renderable: tile.Renderable, Renderable: tile.Renderable,
Velocity: tile.Velocity, Velocity: tile.Velocity,
Collectible: tile.Collectible, Collectible: tile.Collectible,
Transient: tile.Transient, Transient: tile.Transient,
Destroyable: tile.Destroyable, Destroyable: tile.Destroyable,
Particle: tile.Particle, Animation: tile.Animation,
Tiles: tile.Tiles, Tiles: tile.Tiles,
TileNames: tile.TileNames, TileNames: tile.TileNames,
Obstacle: tile.Obstacle, Obstacle: tile.Obstacle,
Direction: tile.Direction, Direction: tile.Direction,
Alpha: tile.Alpha, Alpha: tile.Alpha,
Shader: tile.Shader, Shader: tile.Shader,
Bond: tile.Bond, Bond: tile.Bond,
Door: tile.Door, Door: tile.Door,
Switch: tile.Switch, Switch: tile.Switch,
AnimateOnDestruct: tile.AnimateOnDestruct,
AnimationSpriteSheet: tile.AnimationSpriteSheet,
} }
return newtile return newtile
@ -129,13 +133,13 @@ func NewTileObstacle(direction int) *Tile {
} }
} }
func NewTileParticle(class []string) *Tile { func NewTileAnimation(class []string) *Tile {
sprites := GetSprites(class) sprites := GetSprites(class)
return &Tile{ return &Tile{
Solid: false, Solid: false,
Renderable: false, Renderable: false,
Particle: 0, Animation: 0,
Tiles: sprites, Tiles: sprites,
} }
} }
@ -192,7 +196,7 @@ func InitTiles() TileRegistry {
"ObstacleSouth": NewTileObstacle(config.South), "ObstacleSouth": NewTileObstacle(config.South),
"ObstacleWest": NewTileObstacle(config.West), "ObstacleWest": NewTileObstacle(config.West),
"ObstacleEast": NewTileObstacle(config.East), "ObstacleEast": NewTileObstacle(config.East),
"Particle": NewTileParticle([]string{ "Animation": NewTileAnimation([]string{
"collectible-detonating1", "collectible-detonating1",
"collectible-detonating2", "collectible-detonating2",
"collectible-detonating3", "collectible-detonating3",

View File

@ -1,7 +1,9 @@
package assets package assets
import ( import (
"bytes"
"embed" "embed"
"encoding/json"
"image" "image"
_ "image/png" _ "image/png"
"log" "log"
@ -9,19 +11,55 @@ import (
"path" "path"
"strings" "strings"
"github.com/alecthomas/repr"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
// Maps image name to image data // Maps image name to image data
type AssetRegistry map[string]*ebiten.Image type AssetRegistry map[string]*ebiten.Image
//go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg type AnimationGeo struct {
X int `json:"x"`
Y int `json:"y"`
Width int `json:"w"`
Height int `json:"h"`
}
type AnimationFrame struct {
Position AnimationGeo `json:"frame"`
// FIXME: maybe also add delay etc? might be cool to tweak these things from LDTK
}
type AnimationMeta struct {
Name string `json:"image"`
Geo AnimationGeo `json:"size"`
}
// Needed to thaw sprite set written by asesprite
type AnimationJSON struct {
Meta AnimationMeta `json:"Meta"`
Frames []AnimationFrame `json:"frames"`
}
// Animation data
type AnimationSet struct {
Width, Height int
Sprites []*ebiten.Image
File string
}
type AnimationRegistry map[string]AnimationSet
//go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json
var assetfs embed.FS var assetfs embed.FS
var Assets = LoadImages("sprites") var Assets, Animations = LoadImages()
func LoadImages(dir string) AssetRegistry { func LoadImages() (AssetRegistry, AnimationRegistry) {
dir := "sprites"
images := AssetRegistry{} images := AssetRegistry{}
rawanimations := []AnimationJSON{}
animations := AnimationRegistry{}
// we use embed.FS to iterate over all files in ./assets/ // we use embed.FS to iterate over all files in ./assets/
entries, err := assetfs.ReadDir(dir) entries, err := assetfs.ReadDir(dir)
@ -31,23 +69,69 @@ func LoadImages(dir string) AssetRegistry {
for _, imagefile := range entries { for _, imagefile := range entries {
path := path.Join(dir, imagefile.Name()) path := path.Join(dir, imagefile.Name())
fd, err := assetfs.Open(path)
if err != nil { switch {
log.Fatalf("failed to open image file %s: %s", imagefile.Name(), err) case strings.HasSuffix(path, ".png"):
fd, err := assetfs.Open(path)
if err != nil {
log.Fatalf("failed to open image file %s: %s", imagefile.Name(), err)
}
defer fd.Close()
name := strings.TrimSuffix(imagefile.Name(), ".png")
img, _, err := image.Decode(fd)
if err != nil {
log.Fatalf("failed to decode image %s: %s", imagefile.Name(), err)
}
images[name] = ebiten.NewImageFromImage(img)
slog.Debug("loaded asset", "path", path)
case strings.HasSuffix(path, ".json"):
fd, err := assetfs.Open(path)
if err != nil {
log.Fatalf("failed to open json file %s: %s", imagefile.Name(), err)
}
defer fd.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(fd)
animationjson := AnimationJSON{}
err = json.Unmarshal(buf.Bytes(), &animationjson)
if err != nil {
log.Fatalf("failed to parse JSON: %s", err)
}
rawanimations = append(rawanimations, animationjson)
} }
defer fd.Close()
name := strings.TrimSuffix(imagefile.Name(), ".png")
img, _, err := image.Decode(fd)
if err != nil {
log.Fatalf("failed to decode image %s: %s", imagefile.Name(), err)
}
images[name] = ebiten.NewImageFromImage(img)
slog.Debug("loaded asset", "path", path)
} }
return images // preprocess animation sprites
for _, animation := range rawanimations {
animationset := AnimationSet{}
animationset.File = strings.TrimSuffix(animation.Meta.Name, ".png")
animationset.Width = animation.Meta.Geo.Width
animationset.Height = animation.Meta.Geo.Height
for _, frame := range animation.Frames {
sprite := images[animationset.File].SubImage(
image.Rect(
frame.Position.X,
frame.Position.Y,
frame.Position.Width,
frame.Position.Height,
)).(*ebiten.Image)
animationset.Sprites = append(animationset.Sprites, sprite)
}
animations[animationset.File] = animationset
}
repr.Println(animations)
return images, animations
} }

View File

@ -0,0 +1,164 @@
{ "frames": [
{
"filename": "collectible-detonating 0.ase",
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 1.ase",
"frame": { "x": 64, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 2.ase",
"frame": { "x": 128, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 3.ase",
"frame": { "x": 192, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 4.ase",
"frame": { "x": 256, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 5.ase",
"frame": { "x": 320, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 6.ase",
"frame": { "x": 384, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 7.ase",
"frame": { "x": 448, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 8.ase",
"frame": { "x": 512, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 9.ase",
"frame": { "x": 576, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 10.ase",
"frame": { "x": 640, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 11.ase",
"frame": { "x": 704, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 12.ase",
"frame": { "x": 768, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 13.ase",
"frame": { "x": 832, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
},
{
"filename": "collectible-detonating 14.ase",
"frame": { "x": 896, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"duration": 100
}
],
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.x-dev",
"image": "collectible-detonating.png",
"format": "RGBA8888",
"size": { "w": 960, "h": 64 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Yellow Sphere", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 8", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 7", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 6", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 5", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 4", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 3", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 2", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" },
{ "name": "Blitz Outer", "opacity": 70, "blendMode": "normal" },
{ "name": "Blitz Middle", "opacity": 84, "blendMode": "normal" },
{ "name": "Blitz Inner", "opacity": 97, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

11
components/animation.go Normal file
View File

@ -0,0 +1,11 @@
package components
import "github.com/hajimehoshi/ebiten/v2"
type Animation struct {
Show bool
Index int
Loop bool
Tiles []*ebiten.Image
Width, Height int // single sprite measurements
}

View File

@ -14,12 +14,6 @@ type Renderable struct {
Shader *ebiten.Shader Shader *ebiten.Shader
} }
type Particle struct {
Show bool
Index int
Tiles []*ebiten.Image
}
// only tile entities will have those // only tile entities will have those
type Tilish struct{} type Tilish struct{}
type Solid struct{} type Solid struct{}

View File

@ -14,11 +14,11 @@ const (
) )
const ( const (
PLAYERSPEED int = 5 PLAYERSPEED int = 5
PARTICLE_STARTWAIT time.Duration = 30 * time.Millisecond // how long to wait to start collectible particle ANIMATION_STARTWAIT time.Duration = 30 * time.Millisecond // how long to wait to start collectible animation
PARTICLE_LOOPWAIT time.Duration = 40 * time.Millisecond // how much time to wait between the sprites ANIMATION_LOOPWAIT time.Duration = 40 * time.Millisecond // how much time to wait between the sprites
LEVEL_END_WAIT time.Duration = 500 * time.Millisecond LEVEL_END_WAIT time.Duration = 500 * time.Millisecond
version string = "1.2.3" version string = "1.2.3"
MenuRectX int = 600 MenuRectX int = 600
MenuRectY int = 0 MenuRectY int = 0

View File

@ -2,6 +2,7 @@ package game
import ( import (
"image" "image"
"log"
"log/slog" "log/slog"
"openquell/assets" "openquell/assets"
"openquell/components" "openquell/components"
@ -51,7 +52,7 @@ func NewLevel(game *Game, cellsize int, plan *ldtkgo.Level) *Level {
systemlist = append(systemlist, systemlist = append(systemlist,
systems.NewPlayerSystem(game.World, gridcontainer, game.ScreenWidth, game.ScreenHeight)) systems.NewPlayerSystem(game.World, gridcontainer, game.ScreenWidth, game.ScreenHeight))
systemlist = append(systemlist, systems.NewParticleSystem(game.World, game.Cellsize)) systemlist = append(systemlist, systems.NewAnimationSystem(game.World, game.Cellsize))
systemlist = append(systemlist, systems.NewTransientSystem(game.World, gridcontainer)) systemlist = append(systemlist, systems.NewTransientSystem(game.World, gridcontainer))
@ -197,6 +198,22 @@ func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) {
} }
tileRect := entity.TileRect tileRect := entity.TileRect
animateondestruct := util.GetPropertyBool(entity, "AnimateOnDestruct")
// FIXME: also check for AnimationLoop and other animation reasons
if animateondestruct {
tile.AnimateOnDestruct = true
animation := util.GetPropertyString(entity, "AnimateSpriteSheet")
if animation != "" {
if !util.Exists(assets.Animations, animation) {
log.Fatalf("entity %s refers to non existent animation set %s",
entity.Identifier, animation)
}
}
tile.AnimationSpriteSheet = assets.Animations[animation]
}
tile.Sprite = tileset.SubImage( tile.Sprite = tileset.SubImage(
image.Rect(tileRect.X, tileRect.Y, image.Rect(tileRect.X, tileRect.Y,
tileRect.X+tileRect.W, tileRect.X+tileRect.W,

View File

@ -57,16 +57,16 @@ func NewGameObserver(
playerID := ecs.ComponentID[components.Player](world) playerID := ecs.ComponentID[components.Player](world)
obstacleID := ecs.ComponentID[components.Obstacle](world) obstacleID := ecs.ComponentID[components.Obstacle](world)
particleID := ecs.ComponentID[components.Particle](world) animationID := ecs.ComponentID[components.Animation](world)
playerListener := observer.GetListenerCallback(playerID) playerListener := observer.GetListenerCallback(playerID)
obstacleListener := observer.GetListenerCallback(obstacleID) obstacleListener := observer.GetListenerCallback(obstacleID)
particleListener := observer.GetListenerCallback(particleID) animationListener := observer.GetListenerCallback(animationID)
listen := listener.NewDispatch( listen := listener.NewDispatch(
&playerListener, &playerListener,
&obstacleListener, &obstacleListener,
&particleListener, &animationListener,
) )
world.SetListener(&listen) world.SetListener(&listen)
@ -74,7 +74,7 @@ func NewGameObserver(
observer.Entities = make(map[ecs.ID]map[ecs.Entity]int) observer.Entities = make(map[ecs.ID]map[ecs.Entity]int)
observer.Entities[playerID] = make(map[ecs.Entity]int) observer.Entities[playerID] = make(map[ecs.Entity]int)
observer.Entities[obstacleID] = make(map[ecs.Entity]int) observer.Entities[obstacleID] = make(map[ecs.Entity]int)
observer.Entities[particleID] = make(map[ecs.Entity]int) observer.Entities[animationID] = make(map[ecs.Entity]int)
resmanger := generic.NewResource[GameObserver](world) resmanger := generic.NewResource[GameObserver](world)
resmanger.Add(observer) resmanger.Add(observer)
@ -119,20 +119,20 @@ func (observer *GameObserver) GetObstacles() []ecs.Entity {
return observer.GetEntities(obstacleID) return observer.GetEntities(obstacleID)
} }
func (observer *GameObserver) GetParticles() []ecs.Entity { func (observer *GameObserver) GetAnimations() []ecs.Entity {
particleID := ecs.ComponentID[components.Particle](observer.World) animationID := ecs.ComponentID[components.Animation](observer.World)
return observer.GetEntities(particleID) return observer.GetEntities(animationID)
} }
func (observer *GameObserver) RemoveEntities() { func (observer *GameObserver) RemoveEntities() {
playerID := ecs.ComponentID[components.Player](observer.World) playerID := ecs.ComponentID[components.Player](observer.World)
obstacleID := ecs.ComponentID[components.Obstacle](observer.World) obstacleID := ecs.ComponentID[components.Obstacle](observer.World)
particleID := ecs.ComponentID[components.Particle](observer.World) animationID := ecs.ComponentID[components.Animation](observer.World)
observer.Entities = make(map[ecs.ID]map[ecs.Entity]int) observer.Entities = make(map[ecs.ID]map[ecs.Entity]int)
observer.Entities[playerID] = make(map[ecs.Entity]int) observer.Entities[playerID] = make(map[ecs.Entity]int)
observer.Entities[obstacleID] = make(map[ecs.Entity]int) observer.Entities[obstacleID] = make(map[ecs.Entity]int)
observer.Entities[particleID] = make(map[ecs.Entity]int) observer.Entities[animationID] = make(map[ecs.Entity]int)
} }
func (observer *GameObserver) SetupLevelScore(min []int) { func (observer *GameObserver) SetupLevelScore(min []int) {

View File

@ -9,15 +9,15 @@ import (
"github.com/mlange-42/arche/generic" "github.com/mlange-42/arche/generic"
) )
type ParticleSystem struct { type AnimationSystem struct {
World *ecs.World World *ecs.World
Selector *generic.Filter3[Position, Particle, Timer] Selector *generic.Filter3[Position, Animation, Timer]
Cellsize int Cellsize int
} }
func NewParticleSystem(world *ecs.World, cellsize int) System { func NewAnimationSystem(world *ecs.World, cellsize int) System {
system := &ParticleSystem{ system := &AnimationSystem{
Selector: generic.NewFilter3[Position, Particle, Timer](), Selector: generic.NewFilter3[Position, Animation, Timer](),
World: world, World: world,
Cellsize: cellsize, Cellsize: cellsize,
} }
@ -25,7 +25,7 @@ func NewParticleSystem(world *ecs.World, cellsize int) System {
return system return system
} }
func (system *ParticleSystem) Update() error { func (system *AnimationSystem) Update() error {
// display debris after collecting // display debris after collecting
EntitiesToRemove := []ecs.Entity{} EntitiesToRemove := []ecs.Entity{}
@ -33,16 +33,16 @@ func (system *ParticleSystem) Update() error {
for query.Next() { for query.Next() {
// we loop, but it's only one anyway // we loop, but it's only one anyway
_, particle, timer := query.Get() _, animation, timer := query.Get()
particle.Show = true animation.Show = true
if timer.IsReady() { if timer.IsReady() {
switch { switch {
// particle shows from earlier tick, animate // animation shows from earlier tick, animate
case particle.Index > -1 && particle.Index < len(particle.Tiles)-1: case animation.Index > -1 && animation.Index < len(animation.Tiles)-1:
particle.Index++ animation.Index++
timer.Start(config.PARTICLE_LOOPWAIT) timer.Start(config.ANIMATION_LOOPWAIT)
default: default:
// last sprite reached, remove it // last sprite reached, remove it
EntitiesToRemove = append(EntitiesToRemove, query.Entity()) EntitiesToRemove = append(EntitiesToRemove, query.Entity())
@ -60,18 +60,18 @@ func (system *ParticleSystem) Update() error {
return nil return nil
} }
func (system *ParticleSystem) Draw(screen *ebiten.Image) { func (system *AnimationSystem) Draw(screen *ebiten.Image) {
// write particles (these are no tiles!) // write animations (these are no tiles!)
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
query := system.Selector.Query(system.World) query := system.Selector.Query(system.World)
for query.Next() { for query.Next() {
pos, particle, _ := query.Get() pos, animation, _ := query.Get()
if particle.Show { if animation.Show {
op.GeoM.Reset() op.GeoM.Reset()
op.GeoM.Translate(float64(pos.X), float64(pos.Y)) op.GeoM.Translate(float64(pos.X), float64(pos.Y))
screen.DrawImage(particle.Tiles[particle.Index], op) screen.DrawImage(animation.Tiles[animation.Index], op)
} }
} }
} }

View File

@ -33,7 +33,7 @@ func (system *CollectibleSystem) Update() error {
posID := ecs.ComponentID[components.Position](system.World) posID := ecs.ComponentID[components.Position](system.World)
veloID := ecs.ComponentID[components.Velocity](system.World) veloID := ecs.ComponentID[components.Velocity](system.World)
particlepositions := []*components.Position{} animationpositions := []*components.Position{}
EntitiesToRemove := []ecs.Entity{} EntitiesToRemove := []ecs.Entity{}
query := system.Selector.Query(system.World) query := system.Selector.Query(system.World)
@ -58,14 +58,14 @@ func (system *CollectibleSystem) Update() error {
ok, _ := colposition.Intersects(playerposition, playervelocity) ok, _ := colposition.Intersects(playerposition, playervelocity)
if ok { if ok {
//slog.Debug("bumped into collectible", "collectible", collectible) //slog.Debug("bumped into collectible", "collectible", collectible)
particlepositions = append(particlepositions, colposition) animationpositions = append(animationpositions, colposition)
EntitiesToRemove = append(EntitiesToRemove, query.Entity()) EntitiesToRemove = append(EntitiesToRemove, query.Entity())
} }
} }
} }
for _, pos := range particlepositions { for _, pos := range animationpositions {
system.AddParticle(pos) system.AddAnimation(pos)
} }
for _, entity := range EntitiesToRemove { for _, entity := range EntitiesToRemove {
@ -99,23 +99,23 @@ func (system *CollectibleSystem) Draw(screen *ebiten.Image) {
} }
} }
func (system *CollectibleSystem) AddParticle(position *components.Position) { func (system *CollectibleSystem) AddAnimation(position *components.Position) {
observer := observers.GetGameObserver(system.World) observer := observers.GetGameObserver(system.World)
ptmapper := generic.NewMap3[ ptmapper := generic.NewMap3[
components.Position, components.Position,
components.Particle, components.Animation,
components.Timer, components.Timer,
](system.World) ](system.World)
particleID := ecs.ComponentID[components.Particle](system.World) animationID := ecs.ComponentID[components.Animation](system.World)
entity := ptmapper.New() entity := ptmapper.New()
pos, particle, timer := ptmapper.Get(entity) pos, animation, timer := ptmapper.Get(entity)
observer.AddEntity(entity, particleID) observer.AddEntity(entity, animationID)
particle.Index = assets.Tiles["Particle"].Particle animation.Index = assets.Tiles["Animation"].Animation
particle.Tiles = assets.Tiles["Particle"].Tiles animation.Tiles = assets.Tiles["Animation"].Tiles
pos.Update( pos.Update(
position.X-(16), // FIXME: use global tilesize! position.X-(16), // FIXME: use global tilesize!
@ -123,5 +123,5 @@ func (system *CollectibleSystem) AddParticle(position *components.Position) {
64, 64,
) )
timer.Start(config.PARTICLE_STARTWAIT) timer.Start(config.ANIMATION_STARTWAIT)
} }

View File

@ -11,7 +11,6 @@ type TileSetSubRect struct {
} }
func Map2Subrect(raw map[string]any) *TileSetSubRect { func Map2Subrect(raw map[string]any) *TileSetSubRect {
// we need to translate this map for less typing // we need to translate this map for less typing
return &TileSetSubRect{ return &TileSetSubRect{
W: int(raw["w"].(float64)), W: int(raw["w"].(float64)),
@ -37,8 +36,25 @@ func GetPropertyRef(entity *ldtkgo.Entity) string {
return "" return ""
} }
func GetPropertyToggleTile(entity *ldtkgo.Entity) *TileSetSubRect { func GetPropertyString(entity *ldtkgo.Entity, property string) string {
ref := entity.PropertyByIdentifier(property)
if ref != nil {
return ref.AsString()
}
return ""
}
func GetPropertyBool(entity *ldtkgo.Entity, property string) bool {
ref := entity.PropertyByIdentifier(property)
if ref != nil {
return ref.AsBool()
}
return false
}
func GetPropertyToggleTile(entity *ldtkgo.Entity) *TileSetSubRect {
ref := entity.PropertyByIdentifier(config.LDTK_Toggle_Tile) ref := entity.PropertyByIdentifier(config.LDTK_Toggle_Tile)
if ref != nil { if ref != nil {
return Map2Subrect(ref.AsMap()) return Map2Subrect(ref.AsMap())