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

View File

@ -11,7 +11,7 @@
"iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52",
"jsonVersion": "1.5.3",
"appBuildId": 473703,
"nextUid": 76,
"nextUid": 78,
"identifierStyle": "Capitalize",
"toc": [],
"worldLayout": "Free",
@ -215,7 +215,88 @@
"limitBehavior": "MoveLastOne",
"pivotX": 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",
@ -1429,7 +1510,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 224
},
@ -1445,7 +1529,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 160
},
@ -1461,7 +1548,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 160
}
@ -1613,7 +1703,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 288
},
@ -1629,7 +1722,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 192
}
@ -1786,7 +1882,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 160
}
@ -1906,7 +2005,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 128
},
@ -1922,7 +2024,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 384
}
@ -2095,7 +2200,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 768
},
@ -2159,7 +2267,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 768
},
@ -2349,7 +2460,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 768
},
@ -2571,7 +2685,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 704
},
@ -2619,7 +2736,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 896
},
@ -2651,7 +2771,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 672
},
@ -2831,7 +2954,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 704
},
@ -2863,7 +2989,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 768
},
@ -2911,7 +3040,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 832
},
@ -3089,7 +3221,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1312
},
@ -3105,7 +3240,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1376
},
@ -3309,7 +3447,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1376
},
@ -3561,7 +3702,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1376
},
@ -3609,7 +3753,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1248
},
@ -3917,7 +4064,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1344
},
@ -4113,7 +4263,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1824
},
@ -4129,7 +4282,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1888
},
@ -4145,7 +4301,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1888
},
@ -4369,7 +4528,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1952
},
@ -4385,7 +4547,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1952
},
@ -4417,7 +4582,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1856
},
@ -4623,7 +4791,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1952
},
@ -4655,7 +4826,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1824
},
@ -4687,7 +4861,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1760
},
@ -4703,7 +4880,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1920
},
@ -5106,7 +5286,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 1824
}
@ -5269,7 +5452,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2432
},
@ -5525,7 +5711,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2464
},
@ -5605,7 +5794,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2400
}
@ -5767,7 +5959,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2464
},
@ -5783,7 +5978,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2336
},
@ -5799,7 +5997,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2528
}
@ -5997,7 +6198,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2560
},
@ -6013,7 +6217,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2336
},
@ -6255,7 +6462,10 @@
"height": 32,
"defUid": 4,
"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,
"__worldY": 2912
}

View File

@ -28,7 +28,7 @@ type Tile struct {
Collectible bool // collectible, vanishes once collected
Transient bool // turns into brick wall when traversed
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
TileNames []string // same thing, only the names
Obstacle bool // is an obstacle/enemy
@ -38,6 +38,8 @@ type Tile struct {
Bond bool // denotes an entity which can have a relation to another
Door bool // a door, can be manipulated by a switch
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 {
@ -54,7 +56,7 @@ func (tile *Tile) Clone() *Tile {
Collectible: tile.Collectible,
Transient: tile.Transient,
Destroyable: tile.Destroyable,
Particle: tile.Particle,
Animation: tile.Animation,
Tiles: tile.Tiles,
TileNames: tile.TileNames,
Obstacle: tile.Obstacle,
@ -64,6 +66,8 @@ func (tile *Tile) Clone() *Tile {
Bond: tile.Bond,
Door: tile.Door,
Switch: tile.Switch,
AnimateOnDestruct: tile.AnimateOnDestruct,
AnimationSpriteSheet: tile.AnimationSpriteSheet,
}
return newtile
@ -129,13 +133,13 @@ func NewTileObstacle(direction int) *Tile {
}
}
func NewTileParticle(class []string) *Tile {
func NewTileAnimation(class []string) *Tile {
sprites := GetSprites(class)
return &Tile{
Solid: false,
Renderable: false,
Particle: 0,
Animation: 0,
Tiles: sprites,
}
}
@ -192,7 +196,7 @@ func InitTiles() TileRegistry {
"ObstacleSouth": NewTileObstacle(config.South),
"ObstacleWest": NewTileObstacle(config.West),
"ObstacleEast": NewTileObstacle(config.East),
"Particle": NewTileParticle([]string{
"Animation": NewTileAnimation([]string{
"collectible-detonating1",
"collectible-detonating2",
"collectible-detonating3",

View File

@ -1,7 +1,9 @@
package assets
import (
"bytes"
"embed"
"encoding/json"
"image"
_ "image/png"
"log"
@ -9,19 +11,55 @@ import (
"path"
"strings"
"github.com/alecthomas/repr"
"github.com/hajimehoshi/ebiten/v2"
)
// Maps image name to image data
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 Assets = LoadImages("sprites")
var Assets, Animations = LoadImages()
func LoadImages(dir string) AssetRegistry {
func LoadImages() (AssetRegistry, AnimationRegistry) {
dir := "sprites"
images := AssetRegistry{}
rawanimations := []AnimationJSON{}
animations := AnimationRegistry{}
// we use embed.FS to iterate over all files in ./assets/
entries, err := assetfs.ReadDir(dir)
@ -31,6 +69,9 @@ func LoadImages(dir string) AssetRegistry {
for _, imagefile := range entries {
path := path.Join(dir, imagefile.Name())
switch {
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)
@ -47,7 +88,50 @@ func LoadImages(dir string) AssetRegistry {
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)
}
return images
rawanimations = append(rawanimations, animationjson)
}
}
// 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
}
type Particle struct {
Show bool
Index int
Tiles []*ebiten.Image
}
// only tile entities will have those
type Tilish struct{}
type Solid struct{}

View File

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

View File

@ -2,6 +2,7 @@ package game
import (
"image"
"log"
"log/slog"
"openquell/assets"
"openquell/components"
@ -51,7 +52,7 @@ func NewLevel(game *Game, cellsize int, plan *ldtkgo.Level) *Level {
systemlist = append(systemlist,
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))
@ -197,6 +198,22 @@ func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) {
}
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(
image.Rect(tileRect.X, tileRect.Y,
tileRect.X+tileRect.W,

View File

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

View File

@ -9,15 +9,15 @@ import (
"github.com/mlange-42/arche/generic"
)
type ParticleSystem struct {
type AnimationSystem struct {
World *ecs.World
Selector *generic.Filter3[Position, Particle, Timer]
Selector *generic.Filter3[Position, Animation, Timer]
Cellsize int
}
func NewParticleSystem(world *ecs.World, cellsize int) System {
system := &ParticleSystem{
Selector: generic.NewFilter3[Position, Particle, Timer](),
func NewAnimationSystem(world *ecs.World, cellsize int) System {
system := &AnimationSystem{
Selector: generic.NewFilter3[Position, Animation, Timer](),
World: world,
Cellsize: cellsize,
}
@ -25,7 +25,7 @@ func NewParticleSystem(world *ecs.World, cellsize int) System {
return system
}
func (system *ParticleSystem) Update() error {
func (system *AnimationSystem) Update() error {
// display debris after collecting
EntitiesToRemove := []ecs.Entity{}
@ -33,16 +33,16 @@ func (system *ParticleSystem) Update() error {
for query.Next() {
// 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() {
switch {
// particle shows from earlier tick, animate
case particle.Index > -1 && particle.Index < len(particle.Tiles)-1:
particle.Index++
timer.Start(config.PARTICLE_LOOPWAIT)
// animation shows from earlier tick, animate
case animation.Index > -1 && animation.Index < len(animation.Tiles)-1:
animation.Index++
timer.Start(config.ANIMATION_LOOPWAIT)
default:
// last sprite reached, remove it
EntitiesToRemove = append(EntitiesToRemove, query.Entity())
@ -60,18 +60,18 @@ func (system *ParticleSystem) Update() error {
return nil
}
func (system *ParticleSystem) Draw(screen *ebiten.Image) {
// write particles (these are no tiles!)
func (system *AnimationSystem) Draw(screen *ebiten.Image) {
// write animations (these are no tiles!)
op := &ebiten.DrawImageOptions{}
query := system.Selector.Query(system.World)
for query.Next() {
pos, particle, _ := query.Get()
pos, animation, _ := query.Get()
if particle.Show {
if animation.Show {
op.GeoM.Reset()
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)
veloID := ecs.ComponentID[components.Velocity](system.World)
particlepositions := []*components.Position{}
animationpositions := []*components.Position{}
EntitiesToRemove := []ecs.Entity{}
query := system.Selector.Query(system.World)
@ -58,14 +58,14 @@ func (system *CollectibleSystem) Update() error {
ok, _ := colposition.Intersects(playerposition, playervelocity)
if ok {
//slog.Debug("bumped into collectible", "collectible", collectible)
particlepositions = append(particlepositions, colposition)
animationpositions = append(animationpositions, colposition)
EntitiesToRemove = append(EntitiesToRemove, query.Entity())
}
}
}
for _, pos := range particlepositions {
system.AddParticle(pos)
for _, pos := range animationpositions {
system.AddAnimation(pos)
}
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)
ptmapper := generic.NewMap3[
components.Position,
components.Particle,
components.Animation,
components.Timer,
](system.World)
particleID := ecs.ComponentID[components.Particle](system.World)
animationID := ecs.ComponentID[components.Animation](system.World)
entity := ptmapper.New()
pos, particle, timer := ptmapper.Get(entity)
observer.AddEntity(entity, particleID)
pos, animation, timer := ptmapper.Get(entity)
observer.AddEntity(entity, animationID)
particle.Index = assets.Tiles["Particle"].Particle
particle.Tiles = assets.Tiles["Particle"].Tiles
animation.Index = assets.Tiles["Animation"].Animation
animation.Tiles = assets.Tiles["Animation"].Tiles
pos.Update(
position.X-(16), // FIXME: use global tilesize!
@ -123,5 +123,5 @@ func (system *CollectibleSystem) AddParticle(position *components.Position) {
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 {
// we need to translate this map for less typing
return &TileSetSubRect{
W: int(raw["w"].(float64)),
@ -37,8 +36,25 @@ func GetPropertyRef(entity *ldtkgo.Entity) string {
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)
if ref != nil {
return Map2Subrect(ref.AsMap())