├── .gitignore ├── LICENSE ├── README.md ├── haxelib.json ├── spriter ├── definitions │ ├── CharacterMap.hx │ ├── CustomCharMap.hx │ ├── EventlineKey.hx │ ├── MainlineKey.hx │ ├── MapInstruction.hx │ ├── Metaline.hx │ ├── PivotInfo.hx │ ├── Quadrilateral.hx │ ├── Ref.hx │ ├── ScmlObject.hx │ ├── SoundlineKey.hx │ ├── SpatialInfo.hx │ ├── SpriteInfo.hx │ ├── SpriterAnimation.hx │ ├── SpriterBox.hx │ ├── SpriterEntity.hx │ ├── SpriterFile.hx │ ├── SpriterFolder.hx │ ├── SpriterTimeline.hx │ ├── SubEntityInfo.hx │ ├── TaglineKey.hx │ ├── TimelineKey.hx │ └── VarlineKey.hx ├── engine │ ├── Spriter.hx │ ├── SpriterEngine.hx │ └── SpriterEngineParam.hx ├── interfaces │ ├── ISpriterDestroyable.hx │ └── ISpriterPooled.hx ├── library │ ├── AbstractLibrary.hx │ ├── BitmapLibrary.hx │ ├── DrawListLibrary.hx │ ├── FlixelLibrary.hx │ ├── H2dBitmapLibrary.hx │ ├── LuxeLibrary.hx │ ├── SpriterLibrary.hx │ ├── TilelayerLibrary.hx │ └── TilemapLibrary.hx ├── macros │ └── SpriterMacros.hx ├── util │ ├── AtlasUtil.hx │ ├── ColorUtils.hx │ ├── MathUtils.hx │ ├── SpriterPool.hx │ └── SpriterUtil.hx ├── vars │ ├── Variable.hx │ ├── VariableFloat.hx │ ├── VariableInt.hx │ └── VariableString.hx └── xml │ └── Access.hx └── texturePackerExporter └── spriterhaxeengine ├── exporter.xml └── template.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | assets/ 10 | spriter/example/ 11 | src/ 12 | SpriterHaxeEngine.hxproj 13 | openfl-readme.txt 14 | .gitignore 15 | pass.txt 16 | application.xml 17 | tmp/ 18 | *.tmp 19 | *.bak 20 | *.swp 21 | *~.nib 22 | local.properties 23 | .classpath 24 | .settings/ 25 | .loadpath 26 | 27 | # External tool builders 28 | .externalToolBuilders/ 29 | 30 | # Locally stored "Eclipse launch configurations" 31 | *.launch 32 | 33 | # CDT-specific 34 | .cproject 35 | 36 | # PDT-specific 37 | .buildpath 38 | 39 | # haxelib 40 | *.zip 41 | 42 | 43 | ################# 44 | ## Visual Studio 45 | ################# 46 | 47 | ## Ignore Visual Studio temporary files, build results, and 48 | ## files generated by popular Visual Studio add-ons. 49 | 50 | # User-specific files 51 | *.suo 52 | *.user 53 | *.sln.docstates 54 | 55 | # Build results 56 | [Dd]ebug/ 57 | [Rr]elease/ 58 | *_i.c 59 | *_p.c 60 | *.ilk 61 | *.meta 62 | *.obj 63 | *.pch 64 | *.pdb 65 | *.pgc 66 | *.pgd 67 | *.rsp 68 | *.sbr 69 | *.tlb 70 | *.tli 71 | *.tlh 72 | *.tmp 73 | *.vspscc 74 | .builds 75 | *.dotCover 76 | 77 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 78 | #packages/ 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opensdf 85 | *.sdf 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | 91 | # ReSharper is a .NET coding add-in 92 | _ReSharper* 93 | 94 | # Installshield output folder 95 | [Ee]xpress 96 | 97 | # DocProject is a documentation generator add-in 98 | DocProject/buildhelp/ 99 | DocProject/Help/*.HxT 100 | DocProject/Help/*.HxC 101 | DocProject/Help/*.hhc 102 | DocProject/Help/*.hhk 103 | DocProject/Help/*.hhp 104 | DocProject/Help/Html2 105 | DocProject/Help/html 106 | 107 | # Click-Once directory 108 | publish 109 | 110 | # Others 111 | [Bb]in 112 | [Oo]bj 113 | sql 114 | TestResults 115 | *.Cache 116 | ClientBin 117 | stylecop.* 118 | ~$* 119 | *.dbmdl 120 | Generated_Code #added for RIA/Silverlight projects 121 | 122 | # Backup & report files from converting an old project file to a newer 123 | # Visual Studio version. Backup files are not needed, because we have git ;-) 124 | _UpgradeReport_Files/ 125 | Backup*/ 126 | UpgradeLog*.XML 127 | 128 | 129 | 130 | ############ 131 | ## Windows 132 | ############ 133 | 134 | # Windows image file caches 135 | Thumbs.db 136 | 137 | # Folder config file 138 | Desktop.ini 139 | 140 | 141 | ############# 142 | ## Python 143 | ############# 144 | 145 | *.py[co] 146 | 147 | # Packages 148 | *.egg 149 | *.egg-info 150 | dist 151 | build 152 | eggs 153 | parts 154 | bin 155 | var 156 | sdist 157 | develop-eggs 158 | .installed.cfg 159 | 160 | # Installer logs 161 | pip-log.txt 162 | 163 | # Unit test / coverage reports 164 | .coverage 165 | .tox 166 | 167 | #Translations 168 | *.mo 169 | 170 | #Mr Developer 171 | .mr.developer.cfg 172 | 173 | # Mac crap 174 | .DS_Store 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Ludovic Bas. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SpriterHaxeEngine 2 | ============= 3 | 4 | The point of this project is to offer a Brashmonkey's Spriter SCML renderer compatible with Haxe 3 and openfl. 5 | Base code of SCML definitions from http://www.brashmonkey.com/ScmlDocs/ScmlReference.html 6 | 7 | Inspired by 8 | - https://github.com/Acemobe/SpriterAS3Anim 9 | - https://bitbucket.org/ClockworkMagpie/haxe-spriter/ 10 | - https://github.com/ibilon/HaxePunk-Spriter 11 | 12 | Install: 13 | ``haxelib install SpriterHaxeEngine`` 14 | 15 | Choose your drawing library: 16 | 17 | ```as3 18 | /** 19 | * Example with the TilemapLibrary which uses openfl-atlas (haxelib install openfl-atlas) 20 | */ 21 | //set the root canvas where to add all the animations 22 | var spriterRoot:Sprite = new Sprite(); 23 | addChild(spriterRoot); 24 | //get an atlas 25 | var tileset:TilesetEx = new SpriterTileset(Assets.getBitmapData('assets/atlas.png'), Assets.getText('assets/atlas.xml')); 26 | //choose a rendering method. 27 | var lib = new TilemapLibrary([tileset], spriterRoot, stage.stageWidth, stage.stageHeight); 28 | 29 | /** 30 | * Example with the BitmapLibrary which uses BitmapData.copypixels() and BitmapData.draw() 31 | */ 32 | //set the root canvas where to add all the animations 33 | var canvas:BitmapData = new BitmapData(800, 480); 34 | var spriterRoot:Bitmap = new Bitmap(canvas, PixelSnapping.AUTO, true); 35 | addChild(spriterRoot); 36 | //choose a rendering method. 37 | var lib:BitmapLibrary = new BitmapLibrary('assets/', canvas); 38 | 39 | /** 40 | * Other libraries exist to use Spriter with flixel and other rendering method! 41 | */ 42 | ``` 43 | 44 | Instantiate the engine: 45 | 46 | ```as3 47 | //Create the engine. 48 | //you can specify a default scml or you can specify it later in addSpriter() 49 | engine = new SpriterEngine(Assets.getText('assets/test.scml'), lib ); 50 | 51 | //Add a Spriter in the engine. A Spriter contains all data from the scml (all entities, animations, boxes, tags...) 52 | //By default, it will play the first animation of the first entity of your scml 53 | engine.addSpriter('uniqueId', x, y); 54 | 55 | //Set the "run" animation of the entity 56 | engine.getSpriter('uniqueId').playAnim('run', myCallback); 57 | 58 | //Apply the "gun" map of the entity 59 | engine.getSpriter('uniqueId').applyCharacterMap('gun', true); 60 | 61 | //Update on enter frame to draw all Spriters on screen 62 | engine.update(); 63 | 64 | //Callback on end anim 65 | function myCallback(s:Spriter):Void 66 | 67 | //callback 68 | engine.getSpriterAt(0).onVarChanged = function varCallback(name:String, value:Dynamic):Void{} 69 | engine.getSpriterAt(0).onEvent = function eventCallback(name:String):Void{} 70 | engine.getSpriterAt(0).onSound = function soundCallback(name:String):Void{} 71 | 72 | //current points and boxes 73 | var points:Array = engine.getSpriterAt(0).points; 74 | var boxes:Array = engine.getSpriterAt(0).boxes; 75 | 76 | //current tags 77 | var tags:Array = engine.getSpriterAt(0).tags; 78 | 79 | //current variables values 80 | var value:Dynamic = engine.getSpriterAt(0).getVariable('myVar'); 81 | 82 | 83 | //stack anims 84 | engine.getSpriter('uniqueId').playAnimsStackFromEntity("entityName", ["anim1","anim2"], myCallback). 85 | 86 | ``` 87 | 88 | Spriter Haxe Engine Features 89 | -------------- 90 | 91 | **SCML API** 92 | 93 | **Engine** 94 | - Can be overrided to fit your need 95 | - simple z-ordering 96 | - Fixed tick, variable tick or use your own time 97 | - Pause 98 | - simple auto removal 99 | - default scml 100 | 101 | **Spriter entity** 102 | - character mapping by name 103 | - change animation easily by name in a Spriter entity 104 | - callback when animation ended 105 | - play, stack anim, pause 106 | - you can display duplicate of spriter entity and manipulate them separatly 107 | - callback when events, sounds are triggered 108 | - callback when variables change 109 | - Points (usage example : to shot a bullet when gun fire) 110 | - Boxes (usage example : hitbox) 111 | - Tags (usage example : state vulnerable) 112 | - sub entities 113 | - playing backward and reflect 114 | 115 | **Libraries** 116 | - TilemapLibrary (use openfl tilemap, dependency: [openfl](https://github.com/openfl/openfl), [openfl-atlas](https://github.com/loudoweb/openfl-atlas)) 117 | - Simple bitmap library (bitmaps handled with addChild, dependency : [openfl](https://github.com/openfl/openfl)) 118 | - BitmapData library (copypixels, dependency : [openfl](https://github.com/openfl/openfl)) 119 | - Tilelayer library (drawTiles using only one tilesheet)(dependency : [openfl-tilelayer](https://github.com/elsassph/openfl-tilelayer) and [openfl](https://github.com/openfl/openfl)). 120 | - DrawTiles library (using many tilesheets)(dependency : [openfl-tilelayer](https://github.com/elsassph/openfl-tilelayer) and [openfl](https://github.com/openfl/openfl)). 121 | - Flixel Library (atlas support or bitmaps handled with addChild, dependency : [flixel](https://github.com/HaxeFlixel/flixel)) by Zaphod 122 | - Heaps Library (h3d/heaps, dependency : [heaps](https://github.com/ncannasse/heaps) ) by Delahee 123 | - Luxe Library (dependency : [luxe](https://github.com/underscorediscovery/luxe) ) 124 | - override the AbstractLibrary to provide a new library 125 | 126 | **Other features** 127 | - own texture packer exporter 128 | - macro to parse scml into binaries 129 | 130 | **Cross-platform** 131 | - flash 132 | - windows 133 | - neko 134 | - android 135 | - html5 136 | 137 | TODO 138 | ---- 139 | - interpolation on variable 140 | - variable/tags of sub entities 141 | - add tilesheet stage 3d support : https://github.com/as3boyan/TilesheetStage3D/ 142 | - add ash and haxepunk support 143 | - add Flambe support (waiting for pull request, see here https://github.com/quinnhoener/SpriterHaxeEngine) 144 | - add Kha support (waiting for pull request, see here https://github.com/sh-dave/SpriterHaxeEngine/tree/dev) 145 | - Optimized engine : draw call only when needed. So "instant" keys are not updated between keys. 146 | - animation callback optimization 147 | - check Garbage collector 148 | 149 | WIKI 150 | ----------- 151 | The [wiki](https://github.com/loudoweb/SpriterHaxeEngine/wiki) provides more details on features and how it works. 152 | 153 | Examples 154 | ------------ 155 | - Please see this repo : https://github.com/loudoweb/Spriter-Example 156 | 157 | Additional information 158 | ------------ 159 | - compatible with Spriter r6.1 160 | - With Tilelayer library, don't use openfl-bitfive for html5 target. 161 | 162 | 163 | Known issues 164 | ------------ 165 | - Please use the best rendering method according to your target. 166 | 167 | -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SpriterHaxeEngine", 3 | "url" : "https://github.com/loudoweb/SpriterHaxeEngine/", 4 | "license": "MIT", 5 | "tags": ["openfl", "game", "animation", "cross", "flash", "cpp", "html5", "android", "neko"], 6 | "description": "Brashmonkey's Spriter SCML renderer compatible with Haxe and openfl.", 7 | "version": "2.2.0", 8 | "releasenote": "fix newer haxe version; improve hl; improve TilemapLibrary; newer examples; add ability to know how many spriters are in the engine and a way to shorten the path of the files", 9 | "contributors": ["loudoweb"] 10 | } -------------------------------------------------------------------------------- /spriter/definitions/CharacterMap.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class CharacterMap 9 | { 10 | public var id:Int; 11 | public var name:String; 12 | public var maps:Array; 13 | 14 | public function new(xml:Access) 15 | { 16 | maps = new Array(); 17 | 18 | id = Std.parseInt(xml.att.id); 19 | name = xml.att.name; 20 | 21 | for (m in xml.nodes.map) 22 | { 23 | maps.push(new MapInstruction(m)); 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /spriter/definitions/CustomCharMap.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | /** 4 | * This class is not part of the scml reference. 5 | * It's a custom feature from SpriterHaxeEngine. 6 | * Allow to set dynamically character map replacing strings in the name of the file. 7 | * @author Loudo 8 | */ 9 | class CustomCharMap 10 | { 11 | public var name:String; 12 | public var sub:String; 13 | public var by:String; 14 | public var folder:Int; 15 | public var length:Int; 16 | /** 17 | * 18 | * @param name String unique name of your character mapping 19 | * @param sub String you need to replace 20 | * @param by String will replace your sub string 21 | * @param folder Int id of the folder you need to map 22 | * @param length Int you can target following folders if needed. Default: target only the folder specified in the folder parameter. 23 | */ 24 | public function new(name:String, sub:String, by:String, folder:Int, length:Int = 1) 25 | { 26 | this.name = name; 27 | this.sub = sub; 28 | this.by = by; 29 | this.folder = folder; 30 | this.length = length; 31 | } 32 | } -------------------------------------------------------------------------------- /spriter/definitions/EventlineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author loudo 7 | */ 8 | class EventlineKey 9 | { 10 | public var id:Int; 11 | public var time:Int = 0; 12 | public function new(xml:Access) 13 | { 14 | if(xml != null){ 15 | id = xml.has.id ? Std.parseInt(xml.att.id) : 0; 16 | time = xml.has.time ? Std.parseInt(xml.att.time) : 0; 17 | } 18 | } 19 | public function copy ():EventlineKey 20 | { 21 | var copy:EventlineKey = new EventlineKey(null); 22 | return clone (copy); 23 | } 24 | 25 | public function clone (clone:EventlineKey):EventlineKey 26 | { 27 | clone.id = id; 28 | clone.time = time; 29 | return clone; 30 | } 31 | } -------------------------------------------------------------------------------- /spriter/definitions/MainlineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | import spriter.engine.SpriterEngineParam; 4 | 5 | /** 6 | * ... 7 | * @author Loudo 8 | */ 9 | class MainlineKey 10 | { 11 | public var id:Int; 12 | public var time:Int; 13 | public var boneRefs:Array; // tags 14 | public var objectRefs:Array; // tags 15 | 16 | public function new(xml:Access) 17 | { 18 | boneRefs = new Array(); 19 | objectRefs = new Array(); 20 | 21 | id = Std.parseInt(xml.att.id); 22 | time = xml.has.time ? Std.parseInt(xml.att.time) : 0; 23 | 24 | for (br in xml.nodes.bone_ref) 25 | { 26 | boneRefs.push(new Ref(br)); 27 | } 28 | 29 | for (or in xml.nodes.object_ref) 30 | { 31 | objectRefs.push(new Ref(or)); 32 | } 33 | /*sort objects by z_index 34 | *On Spriter, when you change the z-index of an object, it will change the xml tag position and the id 35 | *so z_index wasn't even used on SpriterHaxeEngine, it was the objet order that determined the z order 36 | *If you have some external tool and change only the z_index attribute, it should works now... 37 | * The following sorting is used only on demand because it extends the parse time. 38 | **/ 39 | if(SpriterEngineParam.NEED_ZORDER_REORDERING) 40 | objectRefs.sort(zOrdering); 41 | } 42 | private function zOrdering(ref1:Ref, ref2:Ref):Int 43 | { 44 | if (ref1.z_index < ref2.z_index) 45 | return -1; 46 | return 1; 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /spriter/definitions/MapInstruction.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class MapInstruction 9 | { 10 | public var folder:Int; 11 | public var file:Int; 12 | public var tarFolder:Int; 13 | public var tarFile:Int; 14 | 15 | public function new(xml:Access) 16 | { 17 | folder = Std.parseInt(xml.att.folder); 18 | file = Std.parseInt(xml.att.file); 19 | tarFolder = xml.has.target_folder ? Std.parseInt(xml.att.target_folder) : -1; 20 | tarFile = xml.has.target_file ? Std.parseInt(xml.att.target_file) : -1; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /spriter/definitions/Metaline.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | 9 | //Internal helpers 10 | #if (haxe_ver < 3.3) 11 | typedef ConstructibleKey = { 12 | public function new(xml:Access):Void; 13 | } 14 | #end 15 | 16 | @:generic 17 | #if (haxe_ver >= 3.3) 18 | class MetalineVoid>> { 19 | #else 20 | class Metaline { 21 | #end 22 | 23 | public var id:Int; 24 | public var name:String; 25 | public var keys:Array; 26 | 27 | public function new(xml:Access) 28 | { 29 | if(xml != null){ 30 | id = xml.has.def ? Std.parseInt(xml.att.def) : xml.has.id ? Std.parseInt(xml.att.id) : -1; 31 | if (xml.has.name) 32 | name = xml.att.name; 33 | keys = []; 34 | for (key in xml.nodes.key) 35 | { 36 | keys.push(new T(key)); 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /spriter/definitions/PivotInfo.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class PivotInfo 8 | { 9 | public var pivotX:Float; 10 | public var pivotY:Float; 11 | public var useDefaultPivot:Bool; 12 | 13 | inline public function new(pivotX:Float = 0, pivotY:Float = 1, useDefault:Bool = true) 14 | { 15 | this.pivotX = pivotX; 16 | this.pivotY = pivotY; 17 | this.useDefaultPivot = useDefault; 18 | } 19 | inline public function setToDefault():Void 20 | { 21 | this.pivotX = 0; 22 | this.pivotY = 1; 23 | this.useDefaultPivot = true; 24 | } 25 | /** 26 | * Clone this to out. 27 | * @param out 28 | * @return 29 | */ 30 | inline public function clone(out:PivotInfo):PivotInfo 31 | { 32 | out.pivotX = pivotX; 33 | out.pivotY = pivotY; 34 | out.useDefaultPivot = useDefaultPivot; 35 | return out; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /spriter/definitions/Quadrilateral.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | #if openfl 3 | import openfl.geom.Point; 4 | #elseif flambe 5 | import flambe.math.Point; 6 | #elseif luxe 7 | typedef Point = phoenix.Vector; 8 | #elseif heaps 9 | typedef Point = h2d.col.Point; 10 | #end 11 | 12 | /** 13 | * Spriter box that can be useful to check collision. 14 | * Spriter manual: http://www.brashmonkey.com/spriter_manual/adding%20collision%20rectangles%20to%20frames.htm 15 | * @author Loudo 16 | */ 17 | class Quadrilateral 18 | { 19 | public var p1:Point; 20 | public var p2:Point; 21 | public var p3:Point; 22 | public var p4:Point; 23 | public function new(p1:Point, p2:Point, p3:Point, p4:Point) 24 | { 25 | this.p1 = p1; 26 | this.p2 = p2; 27 | this.p3 = p3; 28 | this.p4 = p4; 29 | } 30 | } -------------------------------------------------------------------------------- /spriter/definitions/Ref.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class Ref 9 | { 10 | public var id : Int; 11 | public var parent:Int; // -1==no parent - uses ScmlObject spatialInfo as parentInfo 12 | public var timeline:Int; 13 | public var key:Int; 14 | public var z_index:Int; 15 | 16 | public function new(xml:Access) 17 | { 18 | id = Std.parseInt(xml.att.id); 19 | parent = xml.has.parent ? Std.parseInt(xml.att.parent) : -1; 20 | timeline = Std.parseInt(xml.att.timeline); 21 | key = Std.parseInt(xml.att.key); 22 | z_index = xml.has.z_index ? Std.parseInt(xml.att.z_index) : 0; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /spriter/definitions/ScmlObject.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | import haxe.Unserializer; 4 | import spriter.xml.Access; 5 | 6 | /** 7 | * ... 8 | * @author Loudo 9 | */ 10 | class ScmlObject 11 | { 12 | 13 | public var folders:Array; 14 | public var entities:Map; 15 | /** 16 | * Get the names of all entities in the scml file 17 | */ 18 | public var entitiesName:Array; 19 | public var defaultEntity:String = ""; 20 | public var defaultAnimation:String = ""; 21 | 22 | #if !SPRITER_NO_TAG 23 | public var tags:Array; 24 | #end 25 | 26 | public var currentTime:Float; 27 | 28 | public var isShortenedPath(default, null):Bool; 29 | 30 | public static function unserialize(bin:String):ScmlObject 31 | { 32 | var serializer:Unserializer = new Unserializer(bin); 33 | var scml:ScmlObject = cast serializer.unserialize(); 34 | return scml; 35 | } 36 | public static function unserializePack(bin:String):Map 37 | { 38 | var serializer:Unserializer = new Unserializer(bin); 39 | var map:Map = cast serializer.unserialize(); 40 | return map; 41 | } 42 | 43 | /** 44 | * Parse the scml 45 | * @param source 46 | * @param isShortenedPath = false; if true remove path and extension of the files 47 | */ 48 | public function new(source:Xml = null, isShortenedPath = false) 49 | { 50 | this.isShortenedPath = isShortenedPath; 51 | 52 | if(source != null){ 53 | folders = new Array(); 54 | entities = new Map(); 55 | entitiesName = []; 56 | 57 | var xml = new Access(source.firstElement()); 58 | 59 | if (xml.att.scml_version != "1.0") 60 | trace("Warning, unsupported format."); 61 | 62 | for(el in xml.elements) 63 | { 64 | if(el.name == "folder") 65 | { 66 | folders.push(new SpriterFolder(el, isShortenedPath)); 67 | } 68 | else if(el.name == "entity") 69 | { 70 | entities.set(el.att.name, new SpriterEntity(el)); 71 | entitiesName.push(el.att.name); 72 | if (el.att.id == "0") { 73 | defaultEntity = el.att.name; 74 | defaultAnimation = el.node.animation.att.name; 75 | } 76 | }else if (el.name == "tag_list") 77 | { 78 | #if !SPRITER_NO_TAG 79 | if (tags == null) 80 | tags = []; 81 | for (t in el.elements) 82 | { 83 | tags.push(t.att.name); 84 | } 85 | #end 86 | 87 | } 88 | } 89 | } 90 | } 91 | /** 92 | * Get the names of all animations in the scml file 93 | * @param entity you have to speficy an entity where we can search the animations. 94 | */ 95 | public function getAnimationsName(entity:String):Array 96 | { 97 | if (entities.exists(entity)) { 98 | var entity:SpriterEntity = entities.get(entity); 99 | return entity.animationsName; 100 | } 101 | return null; 102 | } 103 | /** 104 | * Get the names of all character mapping in the scml file 105 | * @param entity you have to speficy an entity where we can search the character mapping. 106 | */ 107 | public function getCharMaps(entity:String):Array 108 | { 109 | if (entities.exists(entity)) { 110 | var entity:SpriterEntity = entities.get(entity); 111 | var returnCharMaps:Array = []; 112 | for (key in entity.characterMaps.keys()) 113 | { 114 | returnCharMaps.push(key); 115 | } 116 | return returnCharMaps; 117 | } 118 | return null; 119 | } 120 | 121 | public function copyFolders():Array 122 | { 123 | var newFolders = new Array(); 124 | for (i in 0...folders.length) 125 | { 126 | newFolders[i] = folders[i].copy(); 127 | } 128 | return newFolders; 129 | } 130 | 131 | public function copy():ScmlObject 132 | { 133 | var newSCML:ScmlObject = new ScmlObject(); 134 | newSCML.folders = copyFolders(); 135 | newSCML.entities = entities;//TODO copy ? 136 | newSCML.entitiesName = entitiesName; 137 | #if !SPRITER_NO_TAG 138 | newSCML.tags = tags; 139 | #end 140 | 141 | newSCML.defaultEntity = Std.string(defaultEntity); 142 | newSCML.defaultAnimation = Std.string(defaultAnimation); 143 | newSCML.currentTime = 0; 144 | return newSCML; 145 | } 146 | 147 | public function destroy():Void 148 | { 149 | folders = null; 150 | entities = null; 151 | entitiesName = null; 152 | #if !SPRITER_NO_TAG 153 | tags = null; 154 | #end 155 | } 156 | } -------------------------------------------------------------------------------- /spriter/definitions/SoundlineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | import spriter.xml.Access; 4 | 5 | /** 6 | * ... 7 | * @author loudo 8 | */ 9 | class SoundlineKey extends EventlineKey 10 | { 11 | 12 | public var folder:Int; 13 | public var file:Int; 14 | public function new(xml:Access) 15 | { 16 | super(xml); 17 | if(xml != null){ 18 | folder = Std.parseInt(xml.node.object.att.folder); 19 | file = Std.parseInt(xml.node.object.att.file); 20 | } 21 | } 22 | 23 | override public function copy ():EventlineKey 24 | { 25 | var copy:EventlineKey = new SoundlineKey(null); 26 | return clone (copy); 27 | } 28 | 29 | override public function clone (clone:EventlineKey):EventlineKey 30 | { 31 | super.clone(clone); 32 | var c:SoundlineKey = cast clone; 33 | c.folder = folder; 34 | c.file = file; 35 | return c; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /spriter/definitions/SpatialInfo.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.interfaces.ISpriterPooled; 3 | import spriter.util.SpriterPool; 4 | import spriter.util.SpriterUtil; 5 | 6 | /** 7 | * ... 8 | * @author Loudo 9 | */ 10 | class SpatialInfo implements ISpriterPooled 11 | { 12 | public var x:Float = 0; 13 | public var y:Float = 0; 14 | public var angle:Float = 0; 15 | public var scaleX:Float = 1; 16 | public var scaleY:Float = 1; 17 | /** 18 | * Alpha 19 | */ 20 | public var a:Float = 1; 21 | public var spin:Int = 1; 22 | 23 | private static var _pool = new SpriterPool(SpatialInfo); 24 | private var _inPool:Bool = false; 25 | 26 | /** 27 | * Recycle or create a new SpatialInfo. 28 | * Be sure to put() them back into the pool after you're done with them! 29 | * 30 | * @param X The X-coordinate of the point in space. 31 | * @param Y The Y-coordinate of the point in space. 32 | * @return This point. 33 | */ 34 | public static inline function get(x:Float = 0, y:Float = 0, angle:Float = 0, scaleX:Float = 1, scaleY:Float = 1, a:Float = 1, spin:Int = 1):SpatialInfo 35 | { 36 | var pooledInfo = _pool.get().init(x, y, angle, scaleX, scaleY, a, spin); 37 | pooledInfo._inPool = false; 38 | return pooledInfo; 39 | } 40 | 41 | public function new(x:Float = 0, y:Float = 0, angle:Float = 0, scaleX:Float = 1, scaleY:Float = 1, a:Float = 1, spin:Int = 1) 42 | { 43 | this.x = x; 44 | this.y = y; 45 | this.angle = angle; 46 | this.scaleX = scaleX; 47 | this.scaleY = scaleY; 48 | this.a = a; 49 | this.spin = spin; 50 | } 51 | 52 | public function init(x:Float = 0, y:Float = 0, angle:Float = 0, scaleX:Float = 1, scaleY:Float = 1, a:Float = 1, spin:Int = 1):SpatialInfo 53 | { 54 | this.x = x; 55 | this.y = y; 56 | this.angle = angle; 57 | this.scaleX = scaleX; 58 | this.scaleY = scaleY; 59 | this.a = a; 60 | this.spin = spin; 61 | return this; 62 | } 63 | 64 | public function setPos(x:Float = 0, y:Float = 0):SpatialInfo 65 | { 66 | this.x = x; 67 | this.y = y; 68 | return this; 69 | } 70 | 71 | public function setScale(scale:Float):SpatialInfo 72 | { 73 | this.scaleX = scale; 74 | this.scaleY = scale; 75 | return this; 76 | } 77 | /** 78 | * 79 | * @param parentInfo 80 | * @param out if null, this method will override this SpatialInfo 81 | * @return 82 | */ 83 | public function unmapFromParent(parentInfo:SpatialInfo, out:SpatialInfo = null):SpatialInfo 84 | { 85 | if (out == null) 86 | out = this; 87 | else 88 | out.init(x, y, angle, scaleX, scaleY, a, spin);//initializing the out object with the values of this object 89 | 90 | if (parentInfo.scaleX * parentInfo.scaleY < 0) 91 | out.angle *= -1; //allow flipping using negative scaling 92 | 93 | out.angle += parentInfo.angle; 94 | out.scaleX *= parentInfo.scaleX; 95 | out.scaleY *= parentInfo.scaleY; 96 | out.a *= parentInfo.a; 97 | 98 | if (out.x != 0 || out.y != 0) 99 | { 100 | var preMultX = out.x * parentInfo.scaleX; 101 | var preMultY = out.y * parentInfo.scaleY; 102 | var parentRad = SpriterUtil.toRadians(SpriterUtil.under360(parentInfo.angle)); 103 | var s = Math.sin(parentRad); 104 | var c = Math.cos(parentRad); 105 | 106 | out.x = (preMultX * c) - (preMultY * s) + parentInfo.x; 107 | out.y = (preMultX * s) + (preMultY * c) + parentInfo.y; 108 | } 109 | else 110 | { 111 | out.x = parentInfo.x; 112 | out.y = parentInfo.y; 113 | } 114 | 115 | return out; 116 | } 117 | 118 | inline public function copy():SpatialInfo 119 | { 120 | return new SpatialInfo(x, y, angle, scaleX, scaleY, a, spin); 121 | } 122 | public function clone(out:SpatialInfo):Void 123 | { 124 | out.init(x, y, angle, scaleX, scaleY, a, spin);//initializing the out object with the values of this object 125 | } 126 | 127 | /*public function linear(infoA:SpatialInfo, infoB:SpatialInfo, spin:Int, t:Float):SpatialInfo 128 | { 129 | var resultInfo:SpatialInfo; 130 | resultInfo.x = linear(infoA.x,infoB.x,t); 131 | resultInfo.y = linear(infoA.y,infoB.y,t); 132 | resultInfo.angle = angleLinear(infoA.angle,infoB.angle,spin,t); 133 | resultInfo.scaleX = linear(infoA.scaleX,infoB.scaleX,t); 134 | resultInfo.scaleY = linear(infoA.scaleY,infoB.scaleY,t); 135 | resultInfo.a = linear(infoA.a,infoB.a,t); 136 | }*/ 137 | /** 138 | * Add this SpatialInfo to the recycling pool. 139 | */ 140 | public function put():Void 141 | { 142 | if (!_inPool) 143 | { 144 | _inPool = true; 145 | _pool.putUnsafe(this); 146 | } 147 | } 148 | public function destroy():Void 149 | { 150 | 151 | } 152 | 153 | public function toString():String 154 | { 155 | return '[SpatialInfo: $x, $y, $scaleX, $scaleY, $angle, $a]'; 156 | } 157 | 158 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriteInfo.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class SpriteInfo 8 | { 9 | public var folder:Int; // index of the folder within the ScmlObject 10 | public var file:Int; 11 | 12 | inline public function new(folder:Int, file:Int) 13 | { 14 | this.folder = folder; 15 | this.file = file; 16 | } 17 | /** 18 | * Clone this to out. 19 | * @param out 20 | * @return 21 | */ 22 | inline public function clone(out:SpriteInfo):SpriteInfo 23 | { 24 | out.folder = folder; 25 | out.file = file; 26 | return out; 27 | } 28 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterAnimation.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | import spriter.definitions.SpriterAnimation.LoopType; 4 | import spriter.definitions.SpriterTimeline.ObjectType; 5 | import spriter.engine.Spriter; 6 | import spriter.library.AbstractLibrary; 7 | import spriter.util.SpriterUtil; 8 | 9 | /** 10 | * ... 11 | * @author Loudo 12 | */ 13 | enum LoopType 14 | { 15 | LOOPING; 16 | NO_LOOPING; 17 | } 18 | 19 | class SpriterAnimation 20 | { 21 | //spatial info cached and shared between all animations to avoid allocating new ones. Used by libraries to compute final coordinates and draw the object on the screen. 22 | static var _cachedSpatialInfo:SpatialInfo = new SpatialInfo(); 23 | static var _cachedTK:TimelineKey = TimelineKey.createDefault(); 24 | static var _cachedTransformedBoneKeys:Array = []; 25 | 26 | /* 27 | * SCML definitions 28 | */ 29 | public var id:Int; 30 | public var name:String; 31 | public var length:Int; 32 | public var loopType:LoopType; 33 | public var mainlineKeys:Array; 34 | public var timelines:Array; 35 | 36 | #if !SPRITER_NO_TAG 37 | public var taglines:Array; 38 | #end 39 | #if !SPRITER_NO_VAR 40 | public var varlines:Array>; 41 | #end 42 | #if !SPRITER_NO_EVENT 43 | public var eventlines:Array>; 44 | #end 45 | #if !SPRITER_NO_SOUND 46 | public var soundlines:Array>; 47 | #end 48 | 49 | 50 | 51 | public function new(xml:Access) 52 | { 53 | mainlineKeys = []; 54 | timelines = []; 55 | 56 | 57 | id = Std.parseInt(xml.att.id); 58 | name = xml.att.name; 59 | length = Std.parseInt(xml.att.length); 60 | //loopType = xml.has.looping ? Type.createEnum(LoopType, xml.att.looping.toUpperCase()) : LOOPING; 61 | if (xml.has.looping) { 62 | if (xml.att.looping == "true") { 63 | loopType = LOOPING; 64 | }else { 65 | loopType = NO_LOOPING; 66 | } 67 | }else { 68 | loopType = LOOPING; 69 | } 70 | 71 | for (mk in xml.node.mainline.nodes.key) 72 | { 73 | mainlineKeys.push(new MainlineKey(mk)); 74 | } 75 | 76 | for (t in xml.nodes.timeline) 77 | { 78 | timelines.push(new SpriterTimeline(t)); 79 | } 80 | #if !SPRITER_NO_SOUND 81 | if (xml.hasNode.soundline) 82 | { 83 | soundlines = []; 84 | for (tag in xml.nodes.soundline) 85 | { 86 | soundlines.push(new Metaline(tag)); 87 | } 88 | } 89 | #end 90 | #if !SPRITER_NO_EVENT 91 | if (xml.hasNode.eventline) 92 | { 93 | eventlines = []; 94 | for (tag in xml.nodes.eventline) 95 | { 96 | eventlines.push(new Metaline(tag)); 97 | } 98 | } 99 | #end 100 | if (xml.hasNode.meta) 101 | { 102 | xml = xml.node.meta; 103 | #if !SPRITER_NO_TAG 104 | if (xml.hasNode.tagline) 105 | { 106 | taglines = []; 107 | for (tag in xml.node.tagline.nodes.key) 108 | { 109 | taglines.push(new TaglineKey(tag)); 110 | } 111 | } 112 | #end 113 | #if !SPRITER_NO_VAR 114 | if (xml.hasNode.varline) 115 | { 116 | varlines = []; 117 | for (currVar in xml.nodes.varline) 118 | { 119 | varlines.push(new Metaline(currVar)); 120 | } 121 | } 122 | #end 123 | } 124 | } 125 | /** 126 | * 127 | * @param newTime Use a time between [0,length] 128 | * @param library library to compute and draw the final graphics 129 | * @param root IScml to use some features 130 | * @param currentEntity to use some features 131 | * @param parentSpatialInfo SpatialInfo from the Spriter (positions, etc.) 132 | */ 133 | inline public function setCurrentTime(newTime:Int, elapsedTime:Int, library:AbstractLibrary, spriter:Spriter, currentEntity:SpriterEntity, parentSpatialInfo:SpatialInfo):Void 134 | { 135 | //update 136 | updateCharacter(mainlineKeyFromTime(newTime), newTime, elapsedTime, library, spriter, currentEntity, parentSpatialInfo); 137 | } 138 | 139 | public function updateCharacter(mainKey:MainlineKey, newTime:Int, elapsedTime:Int, library:AbstractLibrary, spriter:Spriter, currentEntity:SpriterEntity, parentSpatialInfo:SpatialInfo):Void 140 | { 141 | var currentKey:TimelineKey; 142 | var currentRef:Ref; 143 | var spatialInfo:SpatialInfo = null; 144 | 145 | //BONES 146 | var len:Int = mainKey.boneRefs.length; 147 | ensureNumOfCachedTransformedBoneKeys(len); 148 | 149 | for (b in 0...len) 150 | { 151 | currentRef = mainKey.boneRefs[b]; 152 | currentKey = keyFromRef(currentRef, newTime, spriter); 153 | if (currentRef.parent >= 0) 154 | { 155 | spatialInfo = _cachedTransformedBoneKeys[currentRef.parent]; 156 | } 157 | else 158 | { 159 | spatialInfo = parentSpatialInfo; 160 | } 161 | 162 | currentKey.info.unmapFromParent(spatialInfo, _cachedTransformedBoneKeys[b]);//update _transformedBoneKeys[b] 163 | } 164 | 165 | //POINTS/BOXES 166 | #if !SPRITER_NO_POINT 167 | SpriterUtil.clearArray(spriter.points);//instead of creating an array each time, clear it 168 | #end 169 | #if !SPRITER_NO_BOX 170 | SpriterUtil.clearArray(spriter.boxes);//instead of creating an array each time, clear it 171 | #end 172 | 173 | //TIMELINE KEYS 174 | len = mainKey.objectRefs.length; 175 | for(o in 0...len) 176 | { 177 | currentRef = mainKey.objectRefs[o]; 178 | currentKey = keyFromRef(currentRef, newTime, spriter); 179 | //trace(currentKey.info.a); 180 | if(currentRef.parent >= 0) 181 | { 182 | spatialInfo = _cachedTransformedBoneKeys[currentRef.parent]; 183 | } 184 | else 185 | { 186 | spatialInfo = parentSpatialInfo; 187 | } 188 | 189 | currentKey.info.unmapFromParent(spatialInfo, _cachedSpatialInfo);//update _cachedSpatialInfo 190 | 191 | if (currentKey.objectType == SPRITE) { 192 | 193 | //render from library 194 | var currentKeyName:String = spriter.getFileName(currentKey.sprite.folder, currentKey.sprite.file); 195 | if (currentKeyName != null) {//hidden object test (via mapping) 196 | library.addGraphic(currentKeyName, _cachedSpatialInfo, currentKey.pivots); 197 | } 198 | 199 | }else if(currentKey.objectType == ENTITY) { 200 | 201 | spriter.setSubEntityCurrentTime(currentKey.subentity.t, currentKey.subentity.entity, currentKey.subentity.animation, _cachedSpatialInfo.copy()); 202 | 203 | } 204 | #if !SPRITER_NO_POINT 205 | else if(currentKey.objectType == POINT){ 206 | 207 | currentKey.pivots.setToDefault(); 208 | spriter.points.push(library.compute(_cachedSpatialInfo.copy(), currentKey.pivots, 0, 0)); 209 | 210 | } 211 | #end 212 | #if !SPRITER_NO_BOX 213 | else if(currentKey.objectType == BOX){ 214 | 215 | var currentBox:SpriterBox = currentEntity.boxes_info.get(getTimelineName(currentRef.timeline)); 216 | spriter.boxes.push(library.computeRectCoordinates(_cachedSpatialInfo, currentKey.pivots, currentBox.width, currentBox.height)); 217 | 218 | } 219 | #end 220 | 221 | 222 | //objectKeys.push(currentKey); 223 | } 224 | 225 | //SCML REF : 226 | //(devnote :I'm not doing that, instead I add directly the element in the library (since libraries compute differently)) 227 | 228 | /*len = objectKeys.length; 229 | for(k in 0...len) 230 | { 231 | objectKeys[k].paint(); 232 | }*/ 233 | 234 | //following lines not in scml references yet 235 | #if !SPRITER_NO_TAG 236 | if (taglines != null) 237 | { 238 | for (tag in taglines) 239 | { 240 | if (isTriggered(tag.time, mainKey.time, newTime, elapsedTime)) 241 | { 242 | spriter.clearTag(); 243 | for (i in 0...tag.t.length) 244 | { 245 | spriter.addTag(tag.t[i]); 246 | } 247 | break; 248 | } 249 | } 250 | } 251 | #end 252 | #if !SPRITER_NO_VAR 253 | if (varlines != null) 254 | { 255 | for (_var in varlines) 256 | { 257 | for (keyVar in _var.keys) 258 | { 259 | if (isTriggered(keyVar.time, mainKey.time, newTime, elapsedTime)) 260 | { 261 | spriter.updateVar(_var.id, keyVar.value); 262 | } 263 | } 264 | } 265 | } 266 | #end 267 | #if !SPRITER_NO_SOUND 268 | if (soundlines != null) 269 | { 270 | for (sound in soundlines) 271 | { 272 | for (soundKey in sound.keys) 273 | { 274 | if (isTriggered(soundKey.time, mainKey.time, newTime, elapsedTime)) 275 | { 276 | spriter.dispatchSound(soundKey.folder, soundKey.file); 277 | } 278 | } 279 | } 280 | } 281 | #end 282 | #if !SPRITER_NO_EVENT 283 | if (eventlines != null) 284 | { 285 | for (event in eventlines) 286 | { 287 | for (eventKey in event.keys) 288 | { 289 | if (isTriggered(eventKey.time, mainKey.time, newTime, elapsedTime)) 290 | { 291 | spriter.dispatchEvent(event.name); 292 | } 293 | } 294 | } 295 | } 296 | #end 297 | //clean up 298 | spatialInfo = null; 299 | } 300 | 301 | function isTriggered(triggerTime:Int, keyTime:Int, newTime:Int, elapsedTime:Int):Bool 302 | { 303 | if (triggerTime == keyTime) 304 | { 305 | if (newTime - elapsedTime < keyTime) 306 | { 307 | return true; 308 | }else if (triggerTime == 0 && newTime == elapsedTime) { //allow to trigger the first frame 309 | return true; 310 | }else { 311 | return false; 312 | } 313 | }else { 314 | return false; 315 | } 316 | } 317 | 318 | inline function ensureNumOfCachedTransformedBoneKeys(num:Int):Void { 319 | if (num <= _cachedTransformedBoneKeys.length) return; 320 | for (i in _cachedTransformedBoneKeys.length...num) { 321 | _cachedTransformedBoneKeys.push(new SpatialInfo()); 322 | } 323 | } 324 | 325 | public function mainlineKeyFromTime(time:Int):MainlineKey 326 | { 327 | var currentMainKey:Int = 0; 328 | var len:Int = mainlineKeys.length; 329 | for (m in 0...len) 330 | { 331 | if(mainlineKeys[m].time <= time) 332 | { 333 | currentMainKey = m; 334 | } 335 | 336 | if(mainlineKeys[m].time >= time) 337 | { 338 | break; 339 | } 340 | } 341 | 342 | return mainlineKeys[currentMainKey]; 343 | } 344 | 345 | public function keyFromRef(ref:Ref, newTime:Int, spriter:Spriter):TimelineKey 346 | { 347 | var timeline:SpriterTimeline = timelines[ref.timeline]; 348 | var keyA:TimelineKey = timeline.keys[ref.key]; 349 | keyA.clone(_cachedTK); 350 | 351 | if(timeline.keys.length == 1 || keyA.curveType == INSTANT) 352 | { 353 | _cachedTK.writeDefaultPivots(spriter); 354 | return _cachedTK; 355 | } 356 | 357 | var nextKeyIndex:Int = ref.key + 1; 358 | 359 | if(nextKeyIndex >= timeline.keys.length) 360 | { 361 | if(loopType == LOOPING) 362 | { 363 | nextKeyIndex = 0; 364 | } 365 | else 366 | { 367 | _cachedTK.writeDefaultPivots(spriter); 368 | return _cachedTK; 369 | } 370 | } 371 | 372 | var keyB:TimelineKey = timeline.keys[nextKeyIndex]; 373 | var keyBTime:Int = keyB.time; 374 | //this line is for interpolation between last key and first key when looping 375 | if(keyBTime < keyA.time) 376 | { 377 | keyBTime = keyBTime+length; 378 | } 379 | 380 | _cachedTK.interpolate(keyB, keyBTime, newTime, spriter); 381 | return _cachedTK; 382 | } 383 | 384 | inline function getTimelineName(id:Int):String 385 | { 386 | return timelines[id].name; 387 | } 388 | 389 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterBox.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class SpriterBox 9 | { 10 | public var name:String; 11 | public var pivotX:Float; 12 | public var pivotY:Float; 13 | public var width:Float; 14 | public var height:Float; 15 | 16 | public function new(xml:Access = null) 17 | { 18 | if(xml != null){ 19 | name = xml.att.name; 20 | pivotX = xml.has.pivot_x ? Std.parseFloat(xml.att.pivot_x) : 0; 21 | pivotY = xml.has.pivot_y ? Std.parseFloat(xml.att.pivot_y) : 0; 22 | 23 | width = xml.has.w ? Std.parseFloat(xml.att.w) : 0; 24 | height = xml.has.h ? Std.parseFloat(xml.att.h) : 0; 25 | } 26 | } 27 | 28 | public function copy():SpriterBox 29 | { 30 | var copy:SpriterBox = new SpriterBox(); 31 | copy.name = name; 32 | copy.pivotX = pivotX; 33 | copy.pivotY = pivotY; 34 | copy.width = width; 35 | copy.height = height; 36 | return copy; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterEntity.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | #if !SPRITER_NO_VAR 4 | import spriter.vars.Variable; 5 | import spriter.vars.VariableFloat; 6 | import spriter.vars.VariableInt; 7 | import spriter.vars.VariableString; 8 | #end 9 | 10 | /** 11 | * ... 12 | * @author Loudo 13 | */ 14 | class SpriterEntity 15 | { 16 | 17 | public var id:Int; 18 | public var name:String; 19 | public var characterMaps:Map; 20 | public var animations:Map; 21 | public var animationsName:Array; 22 | public var boxes_info:Map; 23 | #if !SPRITER_NO_VAR 24 | public var variables:Array>; 25 | #end 26 | 27 | 28 | public function new(xml:Access) 29 | { 30 | characterMaps = new Map(); 31 | animations = new Map(); 32 | animationsName = []; 33 | boxes_info = new Map(); 34 | #if !SPRITER_NO_VAR 35 | variables = new Array>(); 36 | #end 37 | 38 | id = Std.parseInt(xml.att.id); 39 | name = xml.att.name; 40 | 41 | for (cm in xml.nodes.character_map) 42 | { 43 | characterMaps.set(cm.att.name, new CharacterMap(cm)); 44 | } 45 | 46 | #if !SPRITER_NO_VAR 47 | if(xml.hasNode.var_defs){ 48 | for (v in xml.node.var_defs.elements) 49 | { 50 | switch(v.att.type) 51 | { 52 | case "int": 53 | variables.push(new VariableInt(v.att.name, Std.parseInt(v.att.resolve("default")))); 54 | case "string": 55 | variables.push(new VariableString(v.att.name, Std.string(v.att.resolve("default")))); 56 | case "float": 57 | variables.push(new VariableFloat(v.att.name, Std.parseFloat(v.att.resolve("default")))); 58 | } 59 | } 60 | } 61 | #end 62 | 63 | for (oi in xml.nodes.obj_info) 64 | { 65 | boxes_info.set(oi.att.name, new SpriterBox(oi)); 66 | } 67 | 68 | 69 | for (a in xml.nodes.animation) 70 | { 71 | animations.set(a.att.name, new SpriterAnimation(a)); 72 | animationsName.push(a.att.name); 73 | } 74 | /* 75 | 76 | */ 77 | } 78 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterFile.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class SpriterFile 9 | { 10 | public var id:Int; 11 | public var name:String; 12 | public var pivotX:Float; 13 | public var pivotY:Float; 14 | public var width:Float; 15 | public var height:Float; 16 | 17 | public function new(xml:Access = null, isShortenedPath = false) 18 | { 19 | if(xml != null){ 20 | id = Std.parseInt(xml.att.id); 21 | name = xml.att.name; 22 | 23 | if(isShortenedPath) 24 | { 25 | var parts = name.split("/"); 26 | name = parts[parts.length - 1]; 27 | var dot = name.indexOf('.'); 28 | name = name.substring(0, dot == -1 ? name.length : dot); 29 | } 30 | 31 | pivotX = xml.has.pivot_x ? Std.parseFloat(xml.att.pivot_x) : 0; 32 | pivotY = xml.has.pivot_y ? Std.parseFloat(xml.att.pivot_y) : 1; 33 | 34 | width = xml.has.width ? Std.parseFloat(xml.att.width) : 0; 35 | height = xml.has.height ? Std.parseFloat(xml.att.height) : 0; 36 | } 37 | } 38 | 39 | public function copy():SpriterFile 40 | { 41 | var copy:SpriterFile = new SpriterFile(); 42 | copy.name = name; 43 | copy.id = id; 44 | copy.pivotX = pivotX; 45 | copy.pivotY = pivotY; 46 | copy.width = width; 47 | copy.height = height; 48 | return copy; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterFolder.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class SpriterFolder 9 | { 10 | 11 | public var id:Int; 12 | public var name:String = ''; 13 | public var files:Array; 14 | 15 | /** 16 | * @param xml 17 | * @param isShortenedPath = false; if true remove path and extension of the files 18 | */ 19 | public function new(xml:Access = null, isShortenedPath = false) 20 | { 21 | files = new Array(); 22 | 23 | if(xml != null){ 24 | id = Std.parseInt(xml.att.id); 25 | if(xml.hasNode.name){ 26 | name = xml.att.name; 27 | } 28 | 29 | for (f in xml.nodes.file) 30 | { 31 | files.push(new SpriterFile(f, isShortenedPath)); 32 | } 33 | } 34 | } 35 | 36 | public function copy():SpriterFolder 37 | { 38 | var copy:SpriterFolder = new SpriterFolder(); 39 | copy.name = name; 40 | copy.id = id; 41 | for (i in 0...files.length) 42 | { 43 | copy.files[i] = files[i].copy(); 44 | } 45 | return copy; 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /spriter/definitions/SpriterTimeline.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | import spriter.definitions.SpriterTimeline.ObjectType; 4 | 5 | /** 6 | * ... 7 | * @author Loudo 8 | */ 9 | enum ObjectType 10 | { 11 | SPRITE; 12 | BONE; 13 | BOX; 14 | POINT; 15 | SOUND; 16 | ENTITY; 17 | VARIABLE; 18 | } 19 | 20 | class SpriterTimeline 21 | { 22 | public var id:Int; 23 | public var name:String; 24 | public var objectType:ObjectType; // enum : SPRITE,BONE,BOX,POINT,SOUND,ENTITY,VARIABLE 25 | public var keys:Array; 26 | 27 | public function new(xml:Access) 28 | { 29 | keys = new Array(); 30 | 31 | id = Std.parseInt(xml.att.id); 32 | name = xml.has.name ? xml.att.name : ""; 33 | objectType = xml.has.object_type ? Type.createEnum(ObjectType, xml.att.object_type.toUpperCase()) : ObjectType.SPRITE; 34 | 35 | 36 | for (k in xml.nodes.key) 37 | { 38 | keys.push(new TimelineKey(k, objectType)); 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /spriter/definitions/SubEntityInfo.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | 3 | /** 4 | * SubEntityTimelineKey 5 | * @author Loudo 6 | */ 7 | class SubEntityInfo 8 | { 9 | public var entity:Int; // id of the sub entity 10 | public var animation:Int; //id of the animation 11 | public var t:Float; //ratio of the time within the animation. So t*animationLength=current time in animation. 12 | 13 | inline public function new(entity:Int, animation:Int, t:Float) 14 | { 15 | this.entity = entity; 16 | this.animation = animation; 17 | this.t = t; 18 | } 19 | 20 | /** 21 | * Clone this to out. 22 | * @param out 23 | * @return 24 | */ 25 | inline public function clone(out:SubEntityInfo):SubEntityInfo 26 | { 27 | out.entity = entity; 28 | out.animation = animation; 29 | out.t = t; 30 | return out; 31 | } 32 | } -------------------------------------------------------------------------------- /spriter/definitions/TaglineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class TaglineKey 9 | { 10 | public var id:Int; 11 | public var time:Int = 0; 12 | public var t:Array; 13 | public function new(xml:Access = null) 14 | { 15 | if(xml != null){ 16 | id = xml.has.id ? Std.parseInt(xml.att.id) : 0; 17 | time = xml.has.time ? Std.parseInt(xml.att.time) : 0; 18 | t = []; 19 | for (tag in xml.nodes.tag) 20 | { 21 | t.push(Std.parseInt(tag.att.t)); 22 | } 23 | } 24 | } 25 | public function copy ():TaglineKey 26 | { 27 | var copy:TaglineKey = new TaglineKey(); 28 | return clone (copy); 29 | } 30 | 31 | public function clone (clone:TaglineKey):TaglineKey 32 | { 33 | clone.id = id; 34 | clone.time = time; 35 | clone.t = t; 36 | return clone; 37 | } 38 | } -------------------------------------------------------------------------------- /spriter/definitions/TimelineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | import spriter.definitions.SpriterTimeline.ObjectType; 4 | import spriter.definitions.TimelineKey.CurveType; 5 | import spriter.engine.Spriter; 6 | import spriter.util.MathUtils; 7 | 8 | /** 9 | * ... 10 | * @author Loudo 11 | */ 12 | enum CurveType 13 | { 14 | INSTANT; 15 | LINEAR; 16 | QUADRATIC; 17 | CUBIC; 18 | QUARTIC; 19 | QUINTIC; 20 | BEZIER; 21 | } 22 | 23 | class TimelineKey 24 | { 25 | static var _cachedPivoInfo:PivotInfo = new PivotInfo(); 26 | 27 | public var objectType:ObjectType; // enum : SPRITE,BONE,BOX,POINT,SOUND,ENTITY,VARIABLE 28 | 29 | public var id:Int; 30 | public var time:Int = 0; 31 | public var curveType:CurveType; // enum : INSTANT,LINEAR,QUADRATIC,CUBIC 32 | public var c1:Float; 33 | public var c2:Float; 34 | public var c3:Float; 35 | public var c4:Float; 36 | 37 | //spatial 38 | public var info:SpatialInfo; 39 | 40 | //sprite 41 | public var sprite:SpriteInfo; 42 | 43 | //pivots 44 | public var pivots:PivotInfo; 45 | 46 | //sub entity 47 | public var subentity:SubEntityInfo; 48 | 49 | static public function createDefault():TimelineKey 50 | { 51 | var c:TimelineKey = new TimelineKey(null, SPRITE); 52 | c.info = new SpatialInfo(); 53 | c.sprite = new SpriteInfo(0, 0); 54 | c.pivots = new PivotInfo(); 55 | c.subentity = new SubEntityInfo(0, 0, 0); 56 | return c; 57 | } 58 | 59 | public function new(xml:Access, type:ObjectType) 60 | { 61 | this.objectType = type; 62 | 63 | if (xml != null) { 64 | id = xml.has.id ? Std.parseInt(xml.att.id) : 0; 65 | time = xml.has.time ? Std.parseInt(xml.att.time) : 0; 66 | curveType = xml.has.curve_type ? Type.createEnum(CurveType, xml.att.curve_type.toUpperCase()) : CurveType.LINEAR; 67 | c1 = xml.has.c1 ? Std.parseFloat(xml.att.c1) : 0; 68 | c2 = xml.has.c2 ? Std.parseFloat(xml.att.c2) : 0; 69 | c3 = xml.has.c3 ? Std.parseFloat(xml.att.c3) : 0; 70 | c4 = xml.has.c4 ? Std.parseFloat(xml.att.c4) : 0; 71 | 72 | var child:Access; 73 | if (objectType != VARIABLE) 74 | { 75 | var spin = xml.has.spin ? Std.parseInt(xml.att.spin) : 1; 76 | 77 | if (xml.hasNode.object){ 78 | child = xml.node.object; 79 | }else { 80 | child = xml.node.bone; 81 | } 82 | 83 | var x = child.has.x ? Std.parseFloat(child.att.x) : 0; 84 | var y = child.has.y ? Std.parseFloat(child.att.y) : 0; 85 | var angle = child.has.angle ? Std.parseFloat(child.att.angle) : 0; 86 | var scale_x = child.has.scale_x ? Std.parseFloat(child.att.scale_x) : 1; 87 | var scale_y = child.has.scale_y ? Std.parseFloat(child.att.scale_y) : 1; 88 | var alpha = child.has.a ? Std.parseFloat(child.att.a) : 1; 89 | 90 | info = new SpatialInfo(x, y, angle, scale_x, scale_y, alpha, spin);//we don't get from pool here because a macro use this constructor :/ 91 | }else { 92 | return; 93 | } 94 | if (objectType != BONE) 95 | { 96 | pivots = new PivotInfo( 97 | child.has.pivot_x ? Std.parseFloat(child.att.pivot_x) : 0, 98 | child.has.pivot_y ? Std.parseFloat(child.att.pivot_y) : 1, 99 | (!child.has.pivot_x && !child.has.pivot_y) 100 | ); 101 | }else { 102 | return; 103 | } 104 | if (objectType == SPRITE) 105 | { 106 | sprite = new SpriteInfo(Std.parseInt(child.att.folder), Std.parseInt(child.att.file)); 107 | } 108 | if (objectType == ENTITY) 109 | { 110 | subentity = new SubEntityInfo(Std.parseInt(child.att.entity), Std.parseInt(child.att.animation), child.has.t ? Std.parseFloat(child.att.t) : 0); 111 | } 112 | } 113 | } 114 | /** 115 | * Clone this to out. 116 | * @param clone 117 | * @return 118 | */ 119 | public function clone(out:TimelineKey):Void 120 | { 121 | out.objectType = objectType; 122 | out.id = id; 123 | out.time = time; 124 | out.curveType = curveType; 125 | out.c1 = c1; 126 | out.c2 = c2; 127 | out.c3 = c3; 128 | out.c4 = c4; 129 | if (objectType != VARIABLE) 130 | { 131 | info.clone(out.info); 132 | }else { 133 | return; 134 | } 135 | if (objectType != BONE) 136 | { 137 | pivots.clone(out.pivots); 138 | }else { 139 | return; 140 | } 141 | if (objectType == SPRITE) 142 | { 143 | sprite.clone(out.sprite); 144 | } 145 | if (objectType == ENTITY) 146 | { 147 | subentity.clone(out.subentity); 148 | } 149 | } 150 | 151 | public function writeDefaultPivots(spriter:Spriter):Void 152 | { 153 | if (objectType == SPRITE) 154 | { 155 | if(pivots.useDefaultPivot) 156 | { 157 | spriter.writeDefaultPivots(pivots, sprite.folder, sprite.file); 158 | } 159 | } 160 | } 161 | 162 | inline public function interpolate(nextKey:TimelineKey, nextKeyTime:Int, currentTime:Float, spriter:Spriter):Void 163 | { 164 | linearKey(nextKey, getTWithNextKey(nextKeyTime, currentTime), spriter); 165 | } 166 | 167 | function getTWithNextKey(nextKeyTime:Int, currentTime:Float):Float 168 | { 169 | if(curveType == INSTANT || time == nextKeyTime) 170 | { 171 | return 0; 172 | } 173 | 174 | var t:Float = (currentTime - time) / (nextKeyTime - time); 175 | 176 | if(curveType == LINEAR) 177 | { 178 | return t; 179 | } 180 | else if(curveType == QUADRATIC) 181 | { 182 | return(MathUtils.quadratic(0.0,c1,1.0,t)); 183 | } 184 | else if(curveType == CUBIC) 185 | { 186 | return(MathUtils.cubic(0.0,c1,c2,1.0,t)); 187 | } 188 | else if(curveType == QUARTIC) 189 | { 190 | return(MathUtils.quartic(0.0,c1,c2,c3,1.0,t)); 191 | } 192 | else if(curveType == QUINTIC) 193 | { 194 | return(MathUtils.quintinc(0.0,c1,c2,c3,c4,1.0,t)); 195 | } 196 | else if(curveType == BEZIER) 197 | { 198 | return(MathUtils.cubicBezierAtTime(c1,c2,c3,c4,t)); 199 | } 200 | 201 | return 0; // Runtime should never reach here 202 | } 203 | 204 | 205 | function linearKey(keyB:TimelineKey, t:Float, spriter:Spriter):Void 206 | { 207 | if (objectType != VARIABLE) 208 | { 209 | info.x = MathUtils.linear(info.x, keyB.info.x, t); 210 | info.y = MathUtils.linear(info.y, keyB.info.y, t); 211 | info.angle = MathUtils.angleLinear(info.angle, keyB.info.angle, info.spin, t); 212 | info.scaleX = MathUtils.linear(info.scaleX, keyB.info.scaleX, t); 213 | info.scaleY = MathUtils.linear(info.scaleY, keyB.info.scaleY, t); 214 | info.a = MathUtils.linear(info.a, keyB.info.a, t); 215 | }else { 216 | return; 217 | } 218 | if (objectType != BONE) 219 | { 220 | if (objectType == SPRITE) 221 | { 222 | if(pivots.useDefaultPivot) 223 | { 224 | spriter.writeDefaultPivots(pivots, sprite.folder, sprite.file); 225 | } 226 | if (keyB.pivots.useDefaultPivot) 227 | { 228 | spriter.writeDefaultPivots(_cachedPivoInfo, keyB.sprite.folder, keyB.sprite.file); 229 | pivots.pivotX = MathUtils.linear(pivots.pivotX, _cachedPivoInfo.pivotX, t); 230 | pivots.pivotY = MathUtils.linear(pivots.pivotY, _cachedPivoInfo.pivotY, t); 231 | }else { 232 | pivots.pivotX = MathUtils.linear(pivots.pivotX, keyB.pivots.pivotX, t); 233 | pivots.pivotY = MathUtils.linear(pivots.pivotY, keyB.pivots.pivotY, t); 234 | } 235 | }else { 236 | pivots.pivotX = MathUtils.linear(pivots.pivotX, keyB.pivots.pivotX, t); 237 | pivots.pivotY = MathUtils.linear(pivots.pivotY, keyB.pivots.pivotY, t); 238 | } 239 | }else { 240 | return; 241 | } 242 | if (objectType == ENTITY) 243 | { 244 | subentity.t = MathUtils.linear(subentity.t, keyB.subentity.t, t); 245 | } 246 | } 247 | } -------------------------------------------------------------------------------- /spriter/definitions/VarlineKey.hx: -------------------------------------------------------------------------------- 1 | package spriter.definitions; 2 | import spriter.xml.Access; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | */ 8 | class VarlineKey extends TimelineKey 9 | { 10 | public var value:String; 11 | public function new(xml:Access) 12 | { 13 | if(xml != null){ 14 | value = xml.att.val; 15 | } 16 | super(xml, VARIABLE); 17 | } 18 | } -------------------------------------------------------------------------------- /spriter/engine/Spriter.hx: -------------------------------------------------------------------------------- 1 | package spriter.engine; 2 | import spriter.definitions.CharacterMap; 3 | import spriter.definitions.MapInstruction; 4 | import spriter.definitions.PivotInfo; 5 | import spriter.definitions.Quadrilateral; 6 | import spriter.definitions.ScmlObject; 7 | import spriter.definitions.SpatialInfo; 8 | import spriter.definitions.SpriterAnimation; 9 | import spriter.definitions.SpriterEntity; 10 | import spriter.definitions.SpriterFile; 11 | import spriter.definitions.SpriterFolder; 12 | import spriter.library.AbstractLibrary; 13 | import spriter.util.MathUtils; 14 | import spriter.util.SpriterUtil; 15 | import spriter.vars.Variable; 16 | #if SPRITER_CUSTOM_MAP 17 | import spriter.definitions.CustomCharMap; 18 | #end 19 | 20 | /** 21 | * ... 22 | * @author Loudo 23 | */ 24 | class Spriter 25 | { 26 | 27 | public var scml:ScmlObject; 28 | public var library:AbstractLibrary; 29 | public var spriterName:String; 30 | /** 31 | * Time elapsed since beginning of current animation 32 | */ 33 | public var timeMS:Int = 0; 34 | /** 35 | * Time in the range of [0,currentAnimation.length] 36 | */ 37 | public var normalizedTime:Int = 0; 38 | 39 | /** 40 | * Manipulate positions (x,y), scale, alpha and rotation through this object. 41 | */ 42 | public var info:SpatialInfo; 43 | 44 | /** 45 | * If the Spriter is paused. 46 | */ 47 | public var paused:Bool = false; 48 | 49 | /** 50 | * To slow down or speed up an animation. 51 | */ 52 | public var playbackSpeed:Float = 1; 53 | 54 | public var currentEntityName(default, null):String = ""; 55 | public var currentAnimationName(default, null):String = ""; 56 | 57 | //Removable features 58 | #if !SPRITER_NO_EVENT 59 | public var onEvent:String->Void; 60 | #end 61 | #if !SPRITER_NO_SOUND 62 | public var onSound:String->Void; 63 | #end 64 | #if !SPRITER_NO_POINT 65 | public var points:Array; 66 | #end 67 | #if !SPRITER_NO_BOX 68 | public var boxes:Array; 69 | #end 70 | #if !SPRITER_NO_TAG 71 | public var tags:Array; 72 | #end 73 | #if !SPRITER_NO_VAR 74 | public var onVarChanged:String->Dynamic->Void; 75 | var variables:Array; 76 | #end 77 | 78 | var activeCharacterMap:Array; 79 | 80 | var loop:Int = 0; 81 | var lastLoop:Int = 0; 82 | var currentAnimation:SpriterAnimation; 83 | var currentEntity:SpriterEntity; 84 | /** 85 | * Callback called at the end of the anim 86 | */ 87 | var onComplete:Void->Void; 88 | /** 89 | * Auto Remove the callback at the end of the anim 90 | */ 91 | var onCompleteOnce:Bool = true; 92 | 93 | //Optional features 94 | #if SPRITER_CUSTOM_MAP 95 | var _customMap:Map; 96 | #end 97 | 98 | var hasReflect:Bool = false; 99 | 100 | 101 | public function new(_name:String, _scml:ScmlObject, _library:AbstractLibrary, _info:SpatialInfo) 102 | { 103 | this.scml = _scml; 104 | this.library = _library; 105 | this.spriterName = _name; 106 | this.currentEntityName = scml.defaultEntity; 107 | this.currentAnimationName = scml.defaultAnimation; 108 | this.currentEntity = scml.entities.get(currentEntityName); 109 | this.currentAnimation = currentEntity.animations.get(currentAnimationName); 110 | this.info = _info; 111 | #if !SPRITER_NO_POINT 112 | this.points = []; 113 | #end 114 | #if !SPRITER_NO_BOX 115 | this.boxes = []; 116 | #end 117 | #if !SPRITER_NO_TAG 118 | this.tags = []; 119 | #end 120 | #if !SPRITER_NO_VAR 121 | this.variables = []; 122 | #end 123 | 124 | this.activeCharacterMap = this.scml.copyFolders(); 125 | } 126 | 127 | public function advanceTime(elapsedMS:Int):Void 128 | { 129 | if (!paused) 130 | { 131 | timeMS += Std.int(elapsedMS * playbackSpeed); 132 | 133 | if (currentAnimation.loopType == LOOPING) 134 | { 135 | normalizedTime += Std.int(elapsedMS * playbackSpeed); 136 | if (normalizedTime >= currentAnimation.length)//forward 137 | { 138 | normalizedTime -= currentAnimation.length; 139 | ++loop; 140 | }else if (normalizedTime <= 0)//backward 141 | { 142 | normalizedTime += currentAnimation.length; 143 | ++loop; 144 | } 145 | 146 | }else{//no looping 147 | normalizedTime = Std.int(Math.max(0, Math.min(timeMS, currentAnimation.length))); 148 | } 149 | } 150 | //even if paused we need to draw it 151 | currentAnimation.setCurrentTime(normalizedTime, Std.int(MathUtils.fabs(elapsedMS * playbackSpeed)), library, this, currentEntity, info); 152 | //callback 153 | if (currentAnimation.loopType == LOOPING) 154 | { 155 | if (loop > lastLoop) { 156 | lastLoop = loop; 157 | dispatchComplete(); 158 | } 159 | }else {//no looping 160 | var when:Int = playbackSpeed > 0 ? currentAnimation.length : 0; 161 | if (normalizedTime == when) { 162 | if (!onCompleteOnce && !hasReflect) onCompleteOnce = true;//force to avoid dispatching every frame when it's done 163 | ++loop; 164 | dispatchComplete(); 165 | } 166 | } 167 | } 168 | /** 169 | * Apply character mapping to change an element in the animation. 170 | * @param name of the character map in the xml 171 | * @param reset to apply only the new character map, if not, you can have multiple character map at the same time. Default is false. 172 | * @return this 173 | */ 174 | public function applyCharacterMap(name:String, reset:Bool = false):Spriter 175 | { 176 | if (reset){ 177 | activeCharacterMap = scml.copyFolders(); 178 | } 179 | #if SPRITER_CUSTOM_MAP 180 | if (_customMap != null && _customMap.exists(name)) 181 | { 182 | var currentMap = _customMap.get(name); 183 | for (i in currentMap.folder...currentMap.folder + currentMap.length) 184 | { 185 | for (j in 0...activeCharacterMap[i].files.length) 186 | { 187 | activeCharacterMap[i].files[j].name = StringTools.replace(activeCharacterMap[i].files[j].name, currentMap.sub, currentMap.by); 188 | } 189 | } 190 | }else{ 191 | #end 192 | if (currentEntity.characterMaps.exists(name)) { 193 | 194 | var charMap:CharacterMap = currentEntity.characterMaps.get(name); 195 | 196 | var len:Int = charMap.maps.length; 197 | for(m in 0...len) 198 | { 199 | var currentMap:MapInstruction = charMap.maps[m]; 200 | if(currentMap.tarFolder > -1 && currentMap.tarFile > -1) 201 | { 202 | var targetFolder:SpriterFolder = activeCharacterMap[currentMap.tarFolder]; 203 | var targetFile:SpriterFile = targetFolder.files[currentMap.tarFile]; 204 | activeCharacterMap[currentMap.folder].files[currentMap.file] = targetFile; 205 | }else { 206 | activeCharacterMap[currentMap.folder].files[currentMap.file] = null;//hidden 207 | } 208 | } 209 | } 210 | #if SPRITER_CUSTOM_MAP 211 | } 212 | #end 213 | return this; 214 | } 215 | #if SPRITER_CUSTOM_MAP 216 | /** 217 | * @see CustomCharMap 218 | * @return this 219 | */ 220 | public function addCustomCharacterMap(name:String, sub:String, by:String, folder:Int, folderLength:Int = 1):Spriter 221 | { 222 | if (_customMap == null) 223 | _customMap = new Map(); 224 | _customMap.set(name, new CustomCharMap(name, sub, by, folder, folderLength)); 225 | return this; 226 | } 227 | #end 228 | 229 | /** 230 | * Play a specific animation 231 | * @param name of the animation 232 | * @param endAnimCallback function callback, return (s:Spriter, entity:String, anim:String) 233 | * @param removeCallback remove function callback after dispatch 234 | * @return this 235 | */ 236 | public function playAnim(?name:String, ?endAnimCallback:Spriter->Void, removeCallback:Bool = true):Spriter 237 | { 238 | if (name == null) { 239 | if (paused) 240 | paused = false; 241 | resetTime(); 242 | if (endAnimCallback != null) { 243 | this.onComplete = endAnimCallback.bind(this); 244 | this.onCompleteOnce = removeCallback; 245 | } 246 | }else if (scml.entities.get(currentEntityName).animations.exists(name)) { 247 | if (paused) 248 | paused = false; 249 | resetTime(); 250 | currentAnimationName = name; 251 | currentAnimation = currentEntity.animations.get(currentAnimationName); 252 | if (endAnimCallback != null) { 253 | this.onComplete = endAnimCallback.bind(this); 254 | this.onCompleteOnce = removeCallback; 255 | } 256 | }else { 257 | #if SPRITER_DEBUG 258 | trace('animation $name does not exist in entity $currentEntityName'); 259 | #end 260 | } 261 | return this; 262 | } 263 | /** 264 | * Play a specific entity. 265 | * @param entity name of the entity 266 | * @param anim name of the animation (optional) 267 | * @param endAnimCallback function callback, return (s:Spriter, entity:String, anim:String) 268 | * @param removeCallback remove function callback after dispatch 269 | * @return this 270 | */ 271 | public function playAnimFromEntity(entity:String, anim:String = '', ?endAnimCallback:Spriter->Void, removeCallback:Bool = true):Spriter 272 | { 273 | if (scml.entities.exists(entity)) { 274 | if (paused) 275 | paused = false; 276 | resetTime(); 277 | currentEntityName = entity; 278 | currentEntity = scml.entities.get(currentEntityName); 279 | if(anim != ''){ 280 | if (currentEntity.animations.exists(anim)) { 281 | currentAnimationName = anim; 282 | currentAnimation = currentEntity.animations.get(currentAnimationName); 283 | }else { 284 | #if SPRITER_DEBUG 285 | trace('animation $anim does not exist in entity $entity'); 286 | #end 287 | } 288 | } 289 | if(endAnimCallback != null){ 290 | this.onComplete = endAnimCallback.bind(this); 291 | this.onCompleteOnce = removeCallback; 292 | } 293 | }else { 294 | #if SPRITER_DEBUG 295 | trace('entity $entity does not exist'); 296 | #end 297 | } 298 | return this; 299 | } 300 | /** 301 | * Play a stack of animations 302 | * @param names of the animations in order 303 | * @param endAnimCallback function callback, return (s:Spriter, entity:String, anim:String) 304 | * @param removeCallback remove function callback after dispatch 305 | * @return this 306 | */ 307 | public function playAnimsStack(names:Array, ?endAnimCallback:Spriter->Void):Spriter 308 | { 309 | if (currentEntity.animations.exists(names[0])) { 310 | if (paused) 311 | paused = false; 312 | resetTime(); 313 | currentAnimationName = names[0]; 314 | currentAnimation = currentEntity.animations.get(currentAnimationName); 315 | this.onComplete = stackAnims.bind(names, 1, endAnimCallback); 316 | this.onCompleteOnce = true; 317 | }else { 318 | #if SPRITER_DEBUG 319 | trace('animation ${names[0]} does not exist in entity $currentEntityName'); 320 | #end 321 | } 322 | return this; 323 | } 324 | /** 325 | * Play a stack of animations from a specific entity. 326 | * @param entity name of the entity 327 | * @param anims names of the animations in order 328 | * @param endAnimCallback function callback, return (s:Spriter, entity:String, anim:String) 329 | * @param removeCallback remove function callback after dispatch 330 | * @return true if the entity exist, false if doesn't exist 331 | */ 332 | public function playAnimsStackFromEntity(entity:String, anims:Array, ?endAnimCallback:Spriter->Void):Bool 333 | { 334 | if (scml.entities.exists(entity)) { 335 | if (paused) 336 | paused = false; 337 | resetTime(); 338 | currentEntityName = entity; 339 | currentEntity = scml.entities.get(currentEntityName); 340 | if (currentEntity.animations.exists(anims[0])) { 341 | currentAnimationName = anims[0]; 342 | currentAnimation = currentEntity.animations.get(currentAnimationName); 343 | } 344 | this.onComplete = stackAnims.bind(anims, 1, endAnimCallback); 345 | this.onCompleteOnce = true; 346 | return true; 347 | }else { 348 | return false; 349 | } 350 | } 351 | 352 | /** 353 | * Play a stack of animations whatever the entity. 354 | * @param name of the entity 355 | * @param anims names of the animations in order 356 | * @param endAnimCallback function callback, return (s:Spriter, entity:String, anim:String) 357 | * @param removeCallback remove function callback after dispatch 358 | * @return true if the entity exist, false if doesn't exist 359 | */ 360 | public function playAnimsStackFromEntities(entities:Array, anims:Array, ?endAnimCallback:Spriter->Void):Bool 361 | { 362 | if (scml.entities.exists(entities[0])) { 363 | if (paused) 364 | paused = false; 365 | resetTime(); 366 | currentEntityName = entities[0]; 367 | currentEntity = scml.entities.get(currentEntityName); 368 | if (currentEntity.animations.exists(anims[0])) { 369 | currentAnimationName = anims[0]; 370 | currentAnimation = currentEntity.animations.get(currentAnimationName); 371 | } 372 | this.onComplete = stackAnimsWithEntitiesChange.bind(entities , anims, 1, endAnimCallback); 373 | this.onCompleteOnce = true; 374 | return true; 375 | }else { 376 | return false; 377 | } 378 | } 379 | 380 | public function resetTime():Void 381 | { 382 | if (playbackSpeed > 0) 383 | { 384 | loop = lastLoop = normalizedTime = timeMS = 0; 385 | }else{ 386 | loop = lastLoop = 0; 387 | normalizedTime = timeMS = currentAnimation.length; 388 | } 389 | } 390 | public function reverse(value:Bool = true):Spriter 391 | { 392 | if (value) 393 | { 394 | if (playbackSpeed > 0) 395 | playbackSpeed = SpriterUtil.changeSign(playbackSpeed); 396 | }else { 397 | if (playbackSpeed < 0) 398 | playbackSpeed = SpriterUtil.changeSign(playbackSpeed); 399 | } 400 | resetTime(); 401 | return this; 402 | } 403 | public function reflect(value:Bool = true):Spriter 404 | { 405 | if (value) 406 | { 407 | onComplete = reflectOnEndAnim; 408 | onCompleteOnce = false; 409 | hasReflect = true; 410 | }else { 411 | onComplete = null; 412 | onCompleteOnce = true; 413 | hasReflect = false; 414 | } 415 | return this; 416 | } 417 | /** 418 | * Set positions of the Spriter 419 | * @param x 420 | * @param y (spriter uses inverted y, so it will automatically inverted in this function) 421 | */ 422 | inline public function set(x:Float, y:Float):Spriter 423 | { 424 | //-y because use inverted y coordinates 425 | info.setPos(x, -y); 426 | return this; 427 | } 428 | 429 | public function destroy():Void 430 | { 431 | //info.put(); 432 | info = null; 433 | scml = null;//don't destroy scml here since it can be shared between many Spriter 434 | library = null;//don't destroy library here since library is shared between all Spriter in the engine 435 | } 436 | 437 | #if !SPRITER_NO_VAR 438 | /** 439 | * Get a variable value from the main current entity playing 440 | * @param name 441 | * @return 442 | */ 443 | public function getVariable(name:String):Dynamic 444 | { 445 | for (i in 0...currentEntity.variables.length) 446 | { 447 | if (currentEntity.variables[i].name == name) 448 | return variables[i]; 449 | } 450 | return null; 451 | } 452 | 453 | /** 454 | * Get a variable value from the main current entity playing 455 | * @param id if you know the id, should be faster to retrieve the variable 456 | * @return 457 | */ 458 | inline public function getVariableFromId(id:Int):Dynamic 459 | { 460 | return variables[id]; 461 | } 462 | #end 463 | 464 | //INTERNAL 465 | 466 | function dispatchComplete():Void 467 | { 468 | if (onComplete != null) { 469 | var tempCallback:Void->Void = onComplete; 470 | if (onCompleteOnce) 471 | onComplete = null; 472 | tempCallback(); 473 | } 474 | } 475 | 476 | function stackAnims(anims:Array, nextAnim:Int, endAnimsCallback:Spriter->Void):Void 477 | { 478 | if (currentEntity.animations.exists(anims[nextAnim])) { 479 | if (paused) 480 | paused = false; 481 | resetTime(); 482 | currentAnimationName = anims[nextAnim]; 483 | currentAnimation = currentEntity.animations.get(currentAnimationName); 484 | } 485 | #if SPRITER_DEBUG 486 | trace('stackAnims', currentAnimationName); 487 | #end 488 | //anim after next anim handler 489 | if (++nextAnim >= anims.length) { 490 | if(endAnimsCallback != null) 491 | this.onComplete = endAnimsCallback.bind(this); 492 | }else { 493 | this.onComplete = stackAnims.bind(anims, nextAnim, endAnimsCallback); 494 | } 495 | } 496 | 497 | function stackAnimsWithEntitiesChange(entities:Array, anims:Array, nextAnim:Int, endAnimsCallback:Spriter->Void):Void 498 | { 499 | if (scml.entities.exists(entities[nextAnim])) { 500 | currentEntityName = entities[nextAnim]; 501 | currentEntity = scml.entities.get(currentEntityName); 502 | if (currentEntity.animations.exists(anims[nextAnim])) { 503 | if (paused) 504 | paused = false; 505 | resetTime(); 506 | currentAnimationName = anims[nextAnim]; 507 | currentAnimation = currentEntity.animations.get(currentAnimationName); 508 | } 509 | } 510 | #if SPRITER_DEBUG 511 | trace('stackAnims', currentAnimationName); 512 | #end 513 | //anim after next anim handler 514 | if (++nextAnim >= anims.length) { 515 | if(endAnimsCallback != null) 516 | this.onComplete = endAnimsCallback.bind(this); 517 | }else { 518 | this.onComplete = stackAnimsWithEntitiesChange.bind(entities, anims, nextAnim, endAnimsCallback); 519 | } 520 | } 521 | inline function reflectOnEndAnim():Void 522 | { 523 | reverse(playbackSpeed > 0); 524 | } 525 | 526 | //ACCESS FROM SpriterAnimation 527 | 528 | @:allow(spriter.definitions.SpriterAnimation) 529 | function setSubEntityCurrentTime(t:Float, entity:Int, animation:Int, spatialInfo:SpatialInfo):Void 530 | { 531 | var tempEntityName:String = scml.entitiesName[entity]; 532 | var tempEntity:SpriterEntity = scml.entities.get(tempEntityName); 533 | var tempAnimName:String = tempEntity.animationsName[animation]; 534 | var tempAnimation:SpriterAnimation = tempEntity.animations.get(tempAnimName); 535 | var newTime:Int = Std.int(t * tempAnimation.length); 536 | tempAnimation.setCurrentTime(newTime, tempAnimation.length, library, this, tempEntity, spatialInfo); 537 | } 538 | 539 | @:allow(spriter.definitions) 540 | function writeDefaultPivots(out:PivotInfo, folder:Int, file:Int):Void 541 | { 542 | var currentFile:SpriterFile = activeCharacterMap[folder].files[file]; 543 | if(currentFile != null){ 544 | out.pivotX = currentFile.pivotX; 545 | out.pivotY = currentFile.pivotY; 546 | } 547 | } 548 | 549 | @:allow(spriter.definitions.SpriterAnimation) 550 | function getFileName(folder:Int, file:Int):String 551 | { 552 | var currentFile:SpriterFile = activeCharacterMap[folder].files[file]; 553 | if(currentFile != null){ 554 | return currentFile.name; 555 | }else { 556 | return null; 557 | } 558 | } 559 | 560 | #if !SPRITER_NO_SOUND 561 | @:allow(spriter.definitions.SpriterAnimation) 562 | inline function dispatchSound(folder:Int, file:Int):Void 563 | { 564 | if(onSound != null) 565 | onSound(scml.folders[folder].files[file].name); 566 | } 567 | #end 568 | 569 | #if !SPRITER_NO_EVENT 570 | @:allow(spriter.definitions.SpriterAnimation) 571 | inline function dispatchEvent(name:String):Void 572 | { 573 | if(onEvent != null) 574 | onEvent(name); 575 | } 576 | #end 577 | 578 | #if !SPRITER_NO_TAG 579 | @:allow(spriter.definitions.SpriterAnimation) 580 | inline function clearTag():Void 581 | { 582 | SpriterUtil.clearArray(tags);//instead of creating an array each time, clear it 583 | } 584 | 585 | @:allow(spriter.definitions.SpriterAnimation) 586 | inline function addTag(t:Int):Void 587 | { 588 | tags.push(scml.tags[t]); 589 | } 590 | #end 591 | 592 | #if !SPRITER_NO_VAR 593 | @:allow(spriter.definitions.SpriterAnimation) 594 | function updateVar(id:Int, value:String):Void 595 | { 596 | var variable:Variable = currentEntity.variables[id]; 597 | 598 | var temp:Dynamic = variable.convert(value); 599 | var changed:Bool = false; 600 | 601 | if (variables.length > id) 602 | { 603 | if (variables[id] != temp) 604 | { 605 | variables[id] = temp; 606 | changed = true; 607 | } 608 | }else { 609 | for (i in variables.length...id) 610 | { 611 | variables[i] = i == id ? temp : null; 612 | } 613 | changed = true; 614 | } 615 | 616 | 617 | if (onVarChanged != null && changed) { 618 | onVarChanged(variable.name, temp); 619 | } 620 | } 621 | #end 622 | } -------------------------------------------------------------------------------- /spriter/engine/SpriterEngine.hx: -------------------------------------------------------------------------------- 1 | package spriter.engine; 2 | import spriter.util.SpriterUtil; 3 | import spriter.definitions.ScmlObject; 4 | import spriter.definitions.SpatialInfo; 5 | import spriter.library.AbstractLibrary; 6 | #if openfl 7 | import openfl.Lib; 8 | #end 9 | 10 | /** 11 | * ... 12 | * @author Loudo 13 | */ 14 | class SpriterEngine 15 | { 16 | /* 17 | * ############################################# 18 | * Data structure 19 | * ############################################# 20 | */ 21 | /** 22 | * Contains all the Spriter entities currently playing, ordering by index (z-ordering). 23 | */ 24 | var _spriters:Array; 25 | /** 26 | * Contains all the Spriter entities currently playing, ordering by their name. 27 | */ 28 | var _spritersNamed:Map; 29 | /* 30 | * ############################################# 31 | * Spriter stuff 32 | * ############################################# 33 | */ 34 | /** 35 | * SCML library created with the Brashmonkey Spriter document (.scml). 36 | */ 37 | public var default_scml:ScmlObject; 38 | /** 39 | * Used to retrieve a graphic and add it to the custom rendering system. 40 | */ 41 | var _lib:AbstractLibrary; 42 | /* 43 | * ############################################# 44 | * Time 45 | * ############################################# 46 | */ 47 | /** 48 | * If the engine is paused. 49 | */ 50 | public var paused(default, null):Bool = false; 51 | /** 52 | * Time in Milliseconds when engine starts, unpauses, or updates. 53 | */ 54 | var _lastTime:Int = 0; 55 | /** 56 | * How many ticks each second. 57 | */ 58 | public var framerate(get, set):Int; 59 | var _framerate:Int; 60 | /** 61 | * Fixed Time in Milliseconds between each tick. 62 | */ 63 | var _frameDuration:Int = 0; 64 | /** 65 | * Time in Milliseconds since last frame. 66 | */ 67 | var _elapsed:Int; 68 | /** 69 | * Framerate locked to avoid frameskip. Used with variable tick (= fixedTick=false). 70 | * @default 100 (10 fps) 71 | * @see fixedTick 72 | */ 73 | public var maxElapsed:Int = 100; 74 | /** 75 | * Total number of milliseconds elapsed since game start. 76 | */ 77 | var _total:Int = 0; 78 | /** 79 | * Total Ticks passed since game start. 80 | */ 81 | var _totalTicks:Int = 0; 82 | /** 83 | * Fixed or variable tick. 84 | * @default true 85 | */ 86 | public var fixedTick:Bool = true; 87 | 88 | /* 89 | * If you want to clear and render the library outside of SpriterEngine, please set to false. 90 | * @default true 91 | */ 92 | public var handleLibraryClearAndRender:Bool = true; 93 | 94 | /** 95 | * 96 | * @param scml_toParse String to parse as xml to create the scmlObject 97 | * @param scml_parsed ScmlObject already created that you can passed instead of the scml_toParse paremeter 98 | * @param library AbstractLibrary 99 | * @param fixedTick Bool, default true. Means that when you call update(), 100 | * @param frameRate Int framerate used by fixedTick, default framerate 60 101 | * @param handleRender Bool, default true. Means that this engine will handle the rendering from AbstractLibrary for you. You can handle by yourself by setting it to false. 102 | */ 103 | public function new(?scml_toParse:String, ?scml_parsed:ScmlObject, library:AbstractLibrary, fixedTick:Bool = true, frameRate:Int = 60, handleRender:Bool = true) 104 | { 105 | _spriters = new Array(); 106 | _spritersNamed = new Map(); 107 | 108 | if(scml_toParse != null && scml_toParse != "") 109 | { 110 | default_scml = new ScmlObject(Xml.parse(scml_toParse)); 111 | } 112 | else if (scml_parsed != null) 113 | { 114 | default_scml = scml_parsed; 115 | }else { 116 | #if SPRITER_DEBUG 117 | trace('WARNING: If you don\'t add default scml with scml_ToParse or scml_parsed parameter in the constructor, you will have to specify each time the scml with addSpriter()'); 118 | #end 119 | } 120 | _lib = library; 121 | this.fixedTick = fixedTick; 122 | this.framerate = frameRate; 123 | this.handleLibraryClearAndRender = handleRender; 124 | #if openfl 125 | _lastTime = Lib.getTimer(); 126 | #end 127 | } 128 | /** 129 | * Allow you to add a Spriter on screen. By default, the first animation of the first entity of the scml is played. 130 | * @param id unique name of your Spriter. This id helps you retrieve the Spriter in getSpriter(id:String) and other methods 131 | * @param x position of your Spriter 132 | * @param y position of your Spriter 133 | * @param scml scml of this Spriter if you don't want to use the default scml 134 | * @param ?index if null same result as addChild, else same result as addChildAt(index). Spriters at and after the replaced index move up. You can use index out of range but negative means 0. 135 | * @param autoRemoval if true, the Spriter will be removed after the animation is ended 136 | * @return the Spriter created 137 | */ 138 | public function addSpriter(id:String, ?x:Float = 0, ?y:Float = 0, ?scml:ScmlObject, ?index:Null, ?autoRemoval:Bool = false):Spriter 139 | { 140 | 141 | //create spatial info for the current Spriter 142 | var info:SpatialInfo = new SpatialInfo(x, -y);//-y because use inverted y coordinates 143 | 144 | if (scml == null) 145 | scml = default_scml; 146 | //create the Spriter 147 | var spriter:Spriter = new Spriter(id, scml, _lib, info); 148 | if (autoRemoval) { 149 | spriter.playAnim(autoRemoveSpriter, true); 150 | } 151 | 152 | //store in array 153 | if(index == null || index > _spriters.length){ 154 | _spriters.push(spriter); 155 | }else if(index <= 0){ 156 | _spriters.unshift(spriter); 157 | }else { 158 | _spriters.insert(index, spriter); 159 | } 160 | 161 | //store by name/id 162 | _spritersNamed.set(id, spriter); 163 | //return the spriter 164 | return spriter; 165 | } 166 | 167 | /** 168 | * Retrieve the index of a Spriter 169 | * @param spriter 170 | * @return index 171 | */ 172 | public function getIndex(spriter:Spriter):Int 173 | { 174 | return _spriters.indexOf(spriter); 175 | } 176 | 177 | /** 178 | * Moves a Spriter to a certain index. Spriters at and after the replaced position move up. 179 | * @param spriter 180 | * @param index 181 | */ 182 | public function setIndex(spriter:Spriter, index:Int):Void 183 | { 184 | var oldIndex:Int = getIndex(spriter); 185 | if (oldIndex == index) return; 186 | if (oldIndex == -1) trace("Not in this container"); 187 | _spriters.splice(oldIndex, 1); 188 | _spriters.insert(index, spriter); 189 | } 190 | 191 | /** 192 | * Swaps the indexes of two children. 193 | * @param spriter1 194 | * @param spriter2 195 | */ 196 | public function swap(spriter1:Spriter, spriter2:Spriter):Void 197 | { 198 | var index1:Int = getIndex(spriter1); 199 | var index2:Int = getIndex(spriter2); 200 | if (index1 == -1 || index2 == -1) trace("Not in this container"); 201 | swapAt(index1, index2); 202 | } 203 | 204 | /** 205 | * Swaps the indexes of two children. 206 | * @param index1 207 | * @param index2 208 | */ 209 | public function swapAt(index1:Int, index2:Int):Void 210 | { 211 | var spriter1:Spriter = getSpriterAt(index1); 212 | var spriter2:Spriter = getSpriterAt(index2); 213 | _spriters[index1] = spriter2; 214 | _spriters[index2] = spriter1; 215 | } 216 | /** 217 | * Remove a Spriter from screen and destroy it 218 | * @param id of the Spriter you want to remove 219 | */ 220 | public function removeSpriter(id:String):Void 221 | { 222 | if (_spritersNamed.exists(id)) { 223 | var current:Spriter = _spritersNamed.get(id); 224 | _spritersNamed.remove(id); 225 | var index:Int = getIndex(current); 226 | _spriters.splice(index, 1); 227 | current.destroy(); 228 | current = null; 229 | }else { 230 | trace("id doesn't exist"); 231 | } 232 | } 233 | /** 234 | * Remove a Spriter from screen and destroy it 235 | * @param index of the Spriter you want to remove 236 | */ 237 | public function removeSpriterAt(index:Int):Void 238 | { 239 | if (index >= 0 && index < _spriters.length) { 240 | var current:Spriter = _spriters[index]; 241 | _spriters.splice(index, 1); 242 | _spritersNamed.remove(current.spriterName); 243 | current.destroy(); 244 | current = null; 245 | }else { 246 | trace('index outside range'); 247 | } 248 | } 249 | /** 250 | * Remove all Spriters from screen and destroy them 251 | */ 252 | public function removeAll():Void 253 | { 254 | var current:Spriter; 255 | for (i in 0..._spriters.length) 256 | { 257 | current = _spriters[i]; 258 | current.destroy(); 259 | current = null; 260 | } 261 | SpriterUtil.clearArray(_spriters); 262 | _spritersNamed = new Map(); 263 | _lib.clear(); 264 | } 265 | function autoRemoveSpriter(spriter:Spriter):Void 266 | { 267 | removeSpriter(spriter.spriterName); 268 | } 269 | /** 270 | * Get a Spriter from its id 271 | * @param id 272 | * @return 273 | */ 274 | public function getSpriter(id:String):Spriter 275 | { 276 | if (_spritersNamed.exists(id)) 277 | return _spritersNamed.get(id); 278 | return null; 279 | } 280 | /** 281 | * Get a Spriter from its index 282 | * @param index 283 | * @return 284 | */ 285 | public function getSpriterAt(index:Int):Spriter 286 | { 287 | if (index >= 0 && index < _spriters.length) 288 | return _spriters[index]; 289 | else 290 | trace("index outside range"); 291 | return null; 292 | } 293 | /** 294 | * You should call this function to have Spriter animation working. 295 | * Call it from ENTER_FRAME or your own update engine. 296 | * @param ?customElapsed (optional) if you have your own engine handles elapsedTime. MilliSeconds! 297 | */ 298 | public function update(?customElapsed:Int):Void 299 | { 300 | if (!paused) 301 | { 302 | if (customElapsed != null) { 303 | _elapsed = customElapsed; 304 | }else { 305 | computeTime(); 306 | } 307 | 308 | if(handleLibraryClearAndRender) 309 | _lib.clear();//TODO handle different for other platform? 310 | 311 | var numSpriters:Int = _spriters.length; 312 | if(numSpriters > 0){ 313 | var spriter:Spriter; 314 | for (i in 0...numSpriters) 315 | { 316 | spriter = _spriters[i]; 317 | spriter.advanceTime(_elapsed); 318 | } 319 | if(handleLibraryClearAndRender) 320 | _lib.render(); 321 | } 322 | } 323 | } 324 | public function destroy():Void 325 | { 326 | removeAll(); 327 | _spritersNamed = null; 328 | _spriters = null; 329 | _lib.destroy(); 330 | default_scml.destroy(); 331 | default_scml = null; 332 | } 333 | /** 334 | * Pauses animations. Use unpause() after. 335 | * @see unpause(); 336 | */ 337 | public function pause():Void 338 | { 339 | if(!paused){ 340 | paused = true; 341 | } 342 | } 343 | /** 344 | * Starts animations after a pause. 345 | * @see pause(); 346 | */ 347 | public function unpause():Void 348 | { 349 | if(paused){ 350 | paused = false; 351 | #if openfl 352 | _lastTime = Lib.getTimer(); 353 | #end 354 | } 355 | 356 | } 357 | /** 358 | * Time 359 | */ 360 | function computeTime():Void 361 | { 362 | _totalTicks++; 363 | if (fixedTick) 364 | { 365 | _elapsed = _frameDuration; 366 | } 367 | else 368 | { 369 | #if openfl 370 | var previous:Int = _lastTime; 371 | _lastTime = Lib.getTimer(); 372 | _elapsed = _lastTime - previous; 373 | 374 | if (_elapsed > maxElapsed) 375 | _elapsed = maxElapsed; 376 | #elseif SPRITER_DEBUG 377 | trace('variable tick not supported outside openfl, please set fixedTick to true or use your own elapsedTime in update()'); 378 | #end 379 | } 380 | _total += _elapsed; 381 | } 382 | 383 | function set_framerate(framerate_:Int):Int 384 | { 385 | _frameDuration = Std.int(Math.abs(1000 / framerate_)); 386 | _framerate = framerate_; 387 | return _framerate; 388 | } 389 | function get_framerate():Int 390 | { 391 | return _framerate; 392 | } 393 | 394 | /** 395 | * How many spriters are currently playing 396 | * @return Int 397 | */ 398 | inline public function numSpriters():Int 399 | { 400 | return _spriters.length; 401 | } 402 | 403 | } -------------------------------------------------------------------------------- /spriter/engine/SpriterEngineParam.hx: -------------------------------------------------------------------------------- 1 | package spriter.engine; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class SpriterEngineParam 8 | { 9 | inline public static var NEED_ZORDER_REORDERING:Bool = false; 10 | } -------------------------------------------------------------------------------- /spriter/interfaces/ISpriterDestroyable.hx: -------------------------------------------------------------------------------- 1 | package spriter.interfaces; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | interface ISpriterDestroyable 8 | { 9 | public function destroy():Void; 10 | } -------------------------------------------------------------------------------- /spriter/interfaces/ISpriterPooled.hx: -------------------------------------------------------------------------------- 1 | package spriter.interfaces; 2 | 3 | import spriter.interfaces.ISpriterDestroyable; 4 | 5 | /** 6 | * @flixel 7 | */ 8 | interface ISpriterPooled extends ISpriterDestroyable 9 | { 10 | public function put():Void; 11 | private var _inPool:Bool; 12 | } -------------------------------------------------------------------------------- /spriter/library/AbstractLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import haxe.io.Path; 3 | import spriter.definitions.PivotInfo; 4 | import spriter.definitions.Quadrilateral; 5 | import spriter.definitions.SpatialInfo; 6 | import spriter.util.SpriterUtil; 7 | #if openfl 8 | import openfl.geom.Point; 9 | #elseif flambe 10 | import flambe.math.Point; 11 | #end 12 | 13 | /** 14 | * ... 15 | * @author Loudo 16 | */ 17 | class AbstractLibrary 18 | { 19 | private var _basePath:String; 20 | 21 | 22 | 23 | /** 24 | * 25 | * @param _basePath 26 | */ 27 | public function new(basePath :String) 28 | { 29 | _basePath = basePath; 30 | } 31 | 32 | /** 33 | * 34 | * @param name of the image 35 | * @return dynamic 36 | */ 37 | public function getFile(name:String):Dynamic 38 | { 39 | throw ("must be overrided"); 40 | return null; 41 | } 42 | 43 | public function clear():Void 44 | { 45 | throw ("must be overrided"); 46 | } 47 | 48 | public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 49 | { 50 | throw ("must be overrided"); 51 | } 52 | 53 | public function compute(info:SpatialInfo, pivots:PivotInfo, width:Float, height:Float):SpatialInfo 54 | { 55 | var degreesUnder360 = SpriterUtil.under360(info.angle); 56 | var rad = SpriterUtil.toRadians(degreesUnder360); 57 | var s = Math.sin(rad); 58 | var c = Math.cos(rad); 59 | 60 | var pivotX = info.x; 61 | var pivotY = info.y; 62 | 63 | var preX = info.x - pivots.pivotX * width * info.scaleX; 64 | var preY = info.y + (1 - pivots.pivotY) * height * info.scaleY; 65 | 66 | var x2 = (preX - pivotX) * c - (preY - pivotY) * s + pivotX; 67 | var y2 = (preX - pivotX) * s + (preY - pivotY) * c + pivotY; 68 | return info.init(x2, -y2, degreesUnder360, info.scaleX, info.scaleY, info.a, info.spin);//TODO pool? 69 | } 70 | 71 | public function computeRectCoordinates(info:SpatialInfo, pivots:PivotInfo, width:Float, height:Float):Quadrilateral 72 | { 73 | var degreesUnder360 = -SpriterUtil.under360(info.angle); 74 | var rad = SpriterUtil.toRadians(degreesUnder360); 75 | var s = Math.sin(rad); 76 | var c = Math.cos(rad); 77 | 78 | var pivotX = info.x; 79 | var pivotY = -info.y; 80 | 81 | //1 82 | var x1 = pivotX - width * info.scaleX * pivots.pivotX; 83 | var y1 = pivotY - height * info.scaleY * (1 - pivots.pivotY); 84 | 85 | //2 86 | var x2 = x1 + width * info.scaleX; 87 | var y2 = y1; 88 | 89 | x2 = (x2 - pivotX) * c - (y2 - pivotY) * s + pivotX; 90 | y2 = (x2 - pivotX) * s + (y2 - pivotY) * c + pivotY; 91 | 92 | //3 93 | var x3 = x1 + width * info.scaleX; 94 | var y3 = y1 + height * info.scaleY; 95 | 96 | x3 = (x3 - pivotX) * c - (y3 - pivotY) * s + pivotX; 97 | y3 = (x3 - pivotX) * s + (y3 - pivotY) * c + pivotY; 98 | 99 | //4 100 | var x4 = x1; 101 | var y4 = y1 + height * info.scaleY; 102 | 103 | x4 = (x4 - pivotX) * c - (y4 - pivotY) * s + pivotX; 104 | y4 = (x4 - pivotX) * s + (y4 - pivotY) * c + pivotY; 105 | 106 | x1 = (x1 - pivotX) * c - (y1 - pivotY) * s + pivotX; 107 | y1 = (x1 - pivotX) * s + (y1 - pivotY) * c + pivotY; 108 | 109 | return new Quadrilateral(new Point(x1, y1), new Point(x2, y2), new Point(x3, y3), new Point(x4, y4));//TODO pool? 110 | } 111 | 112 | public function render():Void 113 | { 114 | 115 | } 116 | 117 | public function destroy():Void 118 | { 119 | 120 | } 121 | 122 | private function getFullPath(relativePath:String):String 123 | { 124 | return Path.join([_basePath, relativePath]); 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /spriter/library/BitmapLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import flash.display.BitmapData; 3 | import flash.geom.ColorTransform; 4 | import flash.geom.Matrix; 5 | import flash.geom.Point; 6 | import openfl.Assets; 7 | import spriter.definitions.PivotInfo; 8 | import spriter.definitions.SpatialInfo; 9 | import spriter.util.ColorUtils; 10 | import spriter.util.SpriterUtil; 11 | 12 | /** 13 | * Simple OpenFL renderer using BitmapData and BitmapData.copypixels(). 14 | * @author Loudo 15 | */ 16 | class BitmapLibrary extends AbstractLibrary 17 | { 18 | 19 | var _canvas : BitmapData; 20 | 21 | var _point : Point; 22 | var _matrix : Matrix; 23 | var _alphaTransform:ColorTransform; 24 | var _currentBd:BitmapData; 25 | var _alphaBd:BitmapData; 26 | 27 | public function new(basePath:String, canvas : BitmapData) 28 | { 29 | super(basePath); 30 | _canvas = canvas; 31 | _point = new Point(); 32 | _matrix = new Matrix(); 33 | _alphaTransform = new ColorTransform(1, 1, 1, 1); 34 | } 35 | override public function getFile(name:String):Dynamic 36 | { 37 | return Assets.getBitmapData(_basePath+name,true); 38 | } 39 | override public function clear():Void 40 | { 41 | _canvas.fillRect(_canvas.rect, 0x00ffffff); 42 | _canvas.lock(); 43 | } 44 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 45 | { 46 | _currentBd = cast getFile(name); 47 | 48 | info = compute(info, pivots, _currentBd.width, _currentBd.height); 49 | 50 | 51 | if(info.angle == 0 && info.scaleX == 1 && info.scaleY == 1) 52 | { 53 | _point.x = info.x; 54 | _point.y = info.y; 55 | _alphaBd = new BitmapData(_currentBd.width, _currentBd.height, true, ColorUtils.multiplyAlpha(Math.abs(info.a))); 56 | _canvas.copyPixels(_currentBd, _currentBd.rect, _point,_alphaBd, _point,true); 57 | } 58 | else 59 | { 60 | _matrix.identity(); 61 | _matrix.scale(info.scaleX, info.scaleY); 62 | _matrix.rotate(SpriterUtil.toRadians(SpriterUtil.fixRotation(info.angle))); 63 | _matrix.translate(info.x, info.y); 64 | _alphaTransform.alphaMultiplier = Math.abs(info.a); 65 | _canvas.draw(_currentBd, _matrix, _alphaTransform, null, null, true); 66 | } 67 | info = null; 68 | } 69 | override public function render():Void 70 | { 71 | _canvas.unlock(); 72 | } 73 | override public function destroy():Void 74 | { 75 | clear(); 76 | _alphaTransform = null; 77 | _point = null; 78 | _matrix = null; 79 | _currentBd.dispose(); 80 | _currentBd = null; 81 | if(_alphaBd != null) 82 | _alphaBd.dispose(); 83 | _alphaBd = null; 84 | render();//to unlock canvas and make it available 85 | _canvas.dispose(); 86 | _canvas = null; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /spriter/library/DrawListLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import aze.display.TilesheetEx; 3 | import openfl.display.Graphics; 4 | import openfl.display.Sprite; 5 | import openfl.geom.Rectangle; 6 | import spriter.definitions.PivotInfo; 7 | import spriter.definitions.SpatialInfo; 8 | import spriter.library.AbstractLibrary; 9 | import spriter.library.DrawListLibrary.DrawList; 10 | import spriter.util.SpriterUtil; 11 | import spriter.interfaces.ISpriterPooled; 12 | import spriter.util.SpriterPool; 13 | 14 | /** 15 | * This class is a modified version of Tilelayer allowing to use many Tilesheet in one single view with Spriter. 16 | * Automatic merge of DrawLists that use the same tilesheet. 17 | * It will merge only if applicable (no merge between background and foreground if there is something between using an other tilesheet). 18 | * Beware of not making too much drawCalls, try to organize your atlas in the best way you can... 19 | * Not working with flash. 20 | * @author loudo (Ludovic Bas) 21 | * It uses some code of TileLayer by @author Philippe / http://philippe.elsass.me 22 | * @example var tile1:SparrowTilesheet = new SparrowTilesheet(atlas1, atlasText1); 23 | var tile2:SparrowTilesheet = new SparrowTilesheet(atlas2, atlasText2); 24 | var lib:DrawListLibrary = new DrawListLibrary([tile1, tile2], this); 25 | var engine = new SpriterEngine(Assets.getText('assets/test.scml'), lib, null ); 26 | */ 27 | class DrawListLibrary extends AbstractLibrary 28 | { 29 | public var view:Sprite; 30 | public var useSmoothing:Bool; 31 | public var useAdditive:Bool; 32 | public var useAlpha:Bool; 33 | public var useTransforms:Bool; 34 | public var useTint:Bool; 35 | 36 | var layerDrawingList:Array;//all your drawList (one drawCall per drawList) 37 | var tilesheetLibrary:Array;//all your tilesheet 38 | //var tilesheetCache:Map;//cache all your assets to find quickly which tilesheet to use 39 | var lastTilesheetUsed:Int = -1;//allow to set your data in the current drawList if it uses the same tilesheet 40 | var currentDrawListIndex:Int = 0; 41 | var currentDrawList:DrawList; 42 | #if mobile 43 | public var maxDrawCalls:Int = 30; 44 | #else 45 | public var maxDrawCalls:Int = 42; 46 | #end 47 | /** 48 | * 49 | * @param tilesheets Find this class in Tilelayer on haxelib (https://github.com/elsassph/openfl-tilelayer) 50 | * @param view the single canvas you want to draw into 51 | * @param smooth 52 | * @param additive 53 | */ 54 | public function new(tilesheets:Array, view:Sprite, smooth:Bool=true, tint:Bool = false, additive:Bool=false) 55 | { 56 | super(""); 57 | 58 | this.view = view; 59 | this.view.mouseEnabled = false; 60 | this.view.mouseChildren = false; 61 | 62 | useSmoothing = smooth; 63 | useAdditive = additive; 64 | useAlpha = true; 65 | useTransforms = true; 66 | useTint = tint; 67 | 68 | layerDrawingList = []; 69 | //tilesheetCache = new Map(); 70 | tilesheetLibrary = tilesheets; 71 | } 72 | 73 | override public function getFile(name:String):Dynamic 74 | { 75 | return 0; 76 | } 77 | /** 78 | * Call clear() every frame to clear the graphic before a new rendering 79 | */ 80 | override public function clear():Void 81 | { 82 | #if !flash 83 | view.graphics.clear(); 84 | #end 85 | lastTilesheetUsed = -1; 86 | currentDrawListIndex = 0; 87 | for (drawList in layerDrawingList) 88 | { 89 | drawList.put(); 90 | } 91 | layerDrawingList.splice(0, layerDrawingList.length); // compact buffer 92 | } 93 | /** 94 | * Call render() every frame to render the graphic 95 | */ 96 | override public function render():Void 97 | { 98 | #if debugDrawCalls 99 | trace('${layerDrawingList.length} of $maxDrawCalls drawCalls'); 100 | #end 101 | #if !flash 102 | if (layerDrawingList.length > maxDrawCalls) { 103 | throw ("max DrawCalls of "+ maxDrawCalls + "reached"); 104 | } 105 | 106 | for (drawList in layerDrawingList) 107 | { 108 | tilesheetLibrary[drawList.tilesheet].drawTiles(view.graphics, drawList.list, useSmoothing, drawList.flags); 109 | } 110 | #elseif debugDrawCalls 111 | trace("can't render on flash target"); 112 | #end 113 | } 114 | 115 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 116 | { 117 | //get file indice and size 118 | var size:Rectangle = null; 119 | /** 120 | * Index of the image in the atlas 121 | */ 122 | var imageIndex:Int = -1; 123 | /** 124 | * Index of the tilesheet in the library 125 | */ 126 | var tilesheetIndex:Int = 0; 127 | /*if (tilesheetCache.exists(name)) 128 | { 129 | tilesheetIndex = tilesheetCache.get(name); 130 | var sheet = tilesheetLibrary[tilesheetIndex]; 131 | imageIndex = sheet.getIndex(name); 132 | size = sheet.getSize(imageIndex); 133 | }else {*/ 134 | for (sheet in tilesheetLibrary) 135 | { 136 | imageIndex = sheet.getIndex(name);//TODO should we store from which sheets the tile comes from? 137 | if (imageIndex != -1) { 138 | size = sheet.getSize(imageIndex); 139 | break; 140 | } 141 | tilesheetIndex++; 142 | } 143 | /*tilesheetCache.set(name, tilesheetIndex); 144 | }*/ 145 | 146 | 147 | if (imageIndex == -1 || info.a == 0)//if image not finded or not visible, exit method 148 | return; 149 | 150 | info = compute(info, pivots, size.width, size.height); 151 | 152 | if (tilesheetIndex != lastTilesheetUsed) { 153 | currentDrawListIndex = 0; 154 | currentDrawList = DrawList.get(); 155 | currentDrawList.tilesheet = tilesheetIndex; 156 | currentDrawList.begin(true, true, useTint, useAdditive); 157 | lastTilesheetUsed = tilesheetIndex; 158 | addDrawList(currentDrawList); 159 | }else { 160 | currentDrawList = layerDrawingList[layerDrawingList.length - 1]; 161 | } 162 | 163 | 164 | 165 | var offsetTransform = currentDrawList.offsetTransform; 166 | var offsetAlpha = currentDrawList.offsetAlpha; 167 | 168 | currentDrawList.list[currentDrawListIndex+2] = imageIndex; 169 | currentDrawList.list[currentDrawListIndex] = info.x; 170 | currentDrawList.list[currentDrawListIndex + 1] = info.y; 171 | 172 | if (offsetTransform > 0) { 173 | var rotation:Float = SpriterUtil.toRadians(SpriterUtil.fixRotation(info.angle)); 174 | if (rotation != 0) { 175 | var cos = Math.cos(rotation); 176 | var sin = Math.sin(rotation); 177 | currentDrawList.list[currentDrawListIndex+offsetTransform] = cos * info.scaleX; 178 | currentDrawList.list[currentDrawListIndex+offsetTransform+1] = sin * info.scaleX; 179 | currentDrawList.list[currentDrawListIndex+offsetTransform+2] = -1 * sin * info.scaleY; 180 | currentDrawList.list[currentDrawListIndex + offsetTransform + 3] = cos * info.scaleY; 181 | } 182 | else { 183 | currentDrawList.list[currentDrawListIndex+offsetTransform] = info.scaleX; 184 | currentDrawList.list[currentDrawListIndex+offsetTransform+1] = 0; 185 | currentDrawList.list[currentDrawListIndex+offsetTransform+2] = 0; 186 | currentDrawList.list[currentDrawListIndex+offsetTransform+3] = info.scaleY; 187 | } 188 | } 189 | /* 190 | var offsetRGB = currentDrawList.offsetRGB; 191 | if (offsetRGB > 0) {//TODO add tint 192 | currentDrawList.list[currentDrawListIndex+offsetRGB] = info.r; 193 | currentDrawList.list[currentDrawListIndex+offsetRGB+1] = info.g; 194 | currentDrawList.list[currentDrawListIndex+offsetRGB+2] = info.b; 195 | } 196 | */ 197 | if (offsetAlpha > 0) currentDrawList.list[currentDrawListIndex+offsetAlpha] = info.a; 198 | 199 | currentDrawListIndex += currentDrawList.fields; 200 | currentDrawList.index = currentDrawListIndex; 201 | info = null; 202 | pivots = null; 203 | } 204 | 205 | //overrided because tilelayer use the center of the sprite for the coordinates 206 | override public function compute(info:SpatialInfo, pivots:PivotInfo, width:Float, height:Float):SpatialInfo 207 | { 208 | var degreesUnder360 = SpriterUtil.under360(info.angle); 209 | var rad = SpriterUtil.toRadians(degreesUnder360); 210 | var s = Math.sin(rad); 211 | var c = Math.cos(rad); 212 | 213 | var pivotX = info.x; 214 | var pivotY = info.y; 215 | 216 | var preX = info.x - pivots.pivotX * width * info.scaleX + 0.5 * width * info.scaleX; 217 | var preY = info.y + (1 - pivots.pivotY) * height * info.scaleY - 0.5 * height * info.scaleY; 218 | 219 | var x2 = (preX - pivotX) * c - (preY - pivotY) * s + pivotX; 220 | var y2 = (preX - pivotX) * s + (preY - pivotY) * c + pivotY; 221 | 222 | return info.init(x2, -y2, degreesUnder360, info.scaleX, info.scaleY, info.a, info.spin); 223 | } 224 | 225 | override public function destroy():Void 226 | { 227 | clear(); 228 | view = null; 229 | layerDrawingList = null; 230 | tilesheetLibrary = null;//TODO proper destroy function for tilesheet? 231 | currentDrawList = null; 232 | } 233 | 234 | inline public function addDrawList(list:DrawList):Void 235 | { 236 | layerDrawingList.push(list); 237 | } 238 | } 239 | /** 240 | * Modified DrawList from Tilelayer which allows to compare the current tilesheet used. 241 | * @author loudo (Ludovic Bas) 242 | * @author forked from TileLayer by Philippe / http://philippe.elsass.me 243 | */ 244 | class DrawList implements ISpriterPooled 245 | { 246 | /** 247 | * Tilesheet index 248 | */ 249 | public var tilesheet:Int; 250 | public var list:Array; 251 | public var index:Int; 252 | public var fields:Int; 253 | public var offsetTransform:Int; 254 | public var offsetRGB:Int; 255 | public var offsetAlpha:Int; 256 | public var flags:Int; 257 | 258 | private static var _pool = new SpriterPool(DrawList); 259 | private var _inPool:Bool = false; 260 | 261 | 262 | public function new() 263 | { 264 | list = new Array(); 265 | } 266 | 267 | /** 268 | * Recycle or create a new SpatialInfo. 269 | * Be sure to put() them back into the pool after you're done with them! 270 | * 271 | * @param X The X-coordinate of the point in space. 272 | * @param Y The Y-coordinate of the point in space. 273 | * @return This point. 274 | */ 275 | public static inline function get():DrawList 276 | { 277 | var pooledInfo = _pool.get(); 278 | pooledInfo._inPool = false; 279 | return pooledInfo; 280 | } 281 | 282 | public function begin(useTransforms:Bool, useAlpha:Bool, useTint:Bool, useAdditive:Bool) 283 | { 284 | #if !flash 285 | flags = 0; 286 | fields = 3; 287 | if (useTransforms) { 288 | offsetTransform = fields; 289 | fields += 4; 290 | flags |= Graphics.TILE_TRANS_2x2; 291 | } 292 | else offsetTransform = 0; 293 | if (useTint) { 294 | offsetRGB = fields; 295 | fields+=3; 296 | flags |= Graphics.TILE_RGB; 297 | } 298 | else offsetRGB = 0; 299 | if (useAlpha) { 300 | offsetAlpha = fields; 301 | fields++; 302 | flags |= Graphics.TILE_ALPHA; 303 | } 304 | else offsetAlpha = 0; 305 | if (useAdditive) flags |= Graphics.TILE_BLEND_ADD; 306 | #end 307 | } 308 | public function destroy():Void 309 | { 310 | index = 0; 311 | list.splice(index, list.length); // compact buffer 312 | } 313 | /** 314 | * Add this SpatialInfo to the recycling pool. 315 | */ 316 | public function put():Void 317 | { 318 | if (!_inPool) 319 | { 320 | _inPool = true; 321 | _pool.putUnsafe(this); 322 | } 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /spriter/library/FlixelLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | 3 | import flixel.FlxSprite; 4 | import flixel.graphics.frames.FlxAtlasFrames; 5 | import flixel.group.FlxGroup; 6 | import openfl.Assets; 7 | import spriter.definitions.PivotInfo; 8 | import spriter.definitions.SpatialInfo; 9 | import spriter.library.AbstractLibrary; 10 | import spriter.util.SpriterUtil; 11 | 12 | /** 13 | * ... 14 | * @author Loudo 15 | * @author Zaphod 16 | */ 17 | class FlixelLibrary extends AbstractLibrary 18 | { 19 | private var _flxGroup:FlxTypedGroup; 20 | 21 | private var _sprites:Map>; 22 | 23 | private var _atlasFrames:FlxAtlasFrames; 24 | 25 | /** 26 | * Flixel lib constructor 27 | * @param group flixel group to render spriter animation to 28 | * @param basePath Path to folder with bitmap assets. Used only if you are not using atlases 29 | * @param atlasData texture packer data object. Used when you are using atlases 30 | * 31 | * Usage examples: 32 | * 1) without atlases 33 | * var spriterGroup:FlxTypedGroup = new FlxTypedGroup(); 34 | * var lib:FlixelLibrary = new FlixelLibrary(spriterGroup, 'assets/sprites/brawler/'); 35 | * engine = new SpriterEngine(Assets.getText('assets/sprites/brawler/brawler.scml'), lib, null); 36 | * var len:Int = 1; 37 | * for (i in 0...len) { 38 | * engine.addSpriter('lib_' + Std.int(i+1), 100 + 50 * (i % 10), 100 + 50 * (Std.int(i / 10) % 6)); 39 | * } 40 | * 41 | * add(spriterGroup); 42 | * 43 | * 2) with atlases 44 | * var spriterGroup:FlxTypedGroup = new FlxTypedGroup(); 45 | * var atlasFrames:FlxAtlasFrames = FlxAtlasFrames.fromSparrow("assets/ugly/ugly.png", "assets/ugly/ugly.xml"); 46 | * var lib:FlixelLibrary = new FlixelLibrary(spriterGroup, null, atlasFrames); 47 | * engine = new SpriterEngine(Assets.getText('assets/ugly/ugly.scml'), lib, null); 48 | * var len:Int = 1; 49 | * for (i in 0...len) { 50 | * engine.addSpriter('lib_' + Std.int(i+1), 100 + 50 * (i % 10), 100 + 50 * (Std.int(i / 10) % 6)); 51 | * } 52 | * 53 | * add(spriterGroup); 54 | * 55 | * 56 | * and don't forget to call engine.update(Std.int(1000 * elapsed)); at the state update() method 57 | */ 58 | public function new(group:FlxTypedGroup, basePath:String = null, atlasFrames:FlxAtlasFrames = null) 59 | { 60 | super(basePath); 61 | 62 | _flxGroup = group; 63 | _sprites = new Map>(); 64 | _atlasFrames = atlasFrames; 65 | } 66 | 67 | override public function getFile(name:String):Dynamic 68 | { 69 | if (_atlasFrames != null) 70 | { 71 | var sprite:FlxSprite = null; 72 | 73 | if (_sprites.exists(_atlasFrames.parent.key) && _sprites.get(_atlasFrames.parent.key).length > 0) 74 | { 75 | sprite = _sprites.get(_atlasFrames.parent.key).shift(); 76 | } 77 | else 78 | { 79 | sprite = new FlxSprite(); 80 | sprite.frames = _atlasFrames; 81 | } 82 | 83 | sprite.animation.frameName = name; 84 | 85 | return sprite; 86 | } 87 | 88 | var key:String = _basePath + name; 89 | 90 | if (_sprites.exists(key) && _sprites.get(key).length > 0) 91 | { 92 | return _sprites.get(key).shift(); 93 | } 94 | 95 | return new FlxSprite(0, 0, key); 96 | } 97 | 98 | override public function clear():Void 99 | { 100 | var len:Int = _flxGroup.members.length; 101 | var sprite:FlxSprite; 102 | 103 | for (i in 0...len) 104 | { 105 | sprite = _flxGroup.members.shift(); 106 | if (sprite == null) 107 | continue; 108 | 109 | var key:String = sprite.graphic.key; 110 | 111 | if (!_sprites.exists(key)) 112 | { 113 | _sprites.set(key, new Array()); 114 | } 115 | 116 | _sprites.get(key).push(sprite); 117 | } 118 | 119 | untyped _flxGroup.length = 0; 120 | } 121 | 122 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 123 | { 124 | var sprite:FlxSprite = cast getFile(name); 125 | info = compute(info, pivots, sprite.frameWidth, sprite.frameHeight); 126 | 127 | sprite.origin.set(0, 0); 128 | sprite.x = info.x; 129 | sprite.y = info.y; 130 | sprite.angle = SpriterUtil.fixRotation(info.angle); 131 | sprite.scale.x = info.scaleX; 132 | sprite.scale.y = info.scaleY; 133 | sprite.alpha = info.a; 134 | 135 | _flxGroup.add(sprite); 136 | info = null; 137 | } 138 | 139 | override public function render():Void { } 140 | 141 | override public function destroy():Void 142 | { 143 | if (_flxGroup != null) 144 | { 145 | _flxGroup.clear(); 146 | } 147 | 148 | var spritesArr:Array; 149 | var numSprites:Int; 150 | var sprite:FlxSprite; 151 | 152 | for (key in _sprites.keys()) 153 | { 154 | spritesArr = _sprites.get(key); 155 | numSprites = spritesArr.length; 156 | 157 | for (i in 0...numSprites) 158 | { 159 | sprite = spritesArr.shift(); 160 | sprite.destroy(); 161 | } 162 | } 163 | 164 | _sprites = null; 165 | 166 | if (_atlasFrames != null) 167 | { 168 | _atlasFrames.destroy(); 169 | _atlasFrames = null; 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /spriter/library/H2dBitmapLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import haxe.io.Path; 3 | import hxd.res.Loader; 4 | import spriter.definitions.PivotInfo; 5 | import spriter.definitions.SpatialInfo; 6 | import spriter.util.ColorUtils; 7 | import spriter.util.SpriterUtil; 8 | 9 | /** 10 | * ... 11 | * @author david Blackmagic elahee 12 | * @author Tommy Brosman 13 | */ 14 | class H2dBitmapLibrary extends AbstractLibrary 15 | { 16 | public var _root : h2d.Object; 17 | var _parent : h2d.Object; 18 | var _tileCache : Map; 19 | 20 | public function new(basePath:String, parent:h2d.Object) 21 | { 22 | super(basePath); 23 | _parent = parent; 24 | _tileCache = new Map(); 25 | _root = new h2d.Object(_parent); 26 | } 27 | 28 | override public function getFile(name:String):Dynamic 29 | { 30 | if ( _tileCache.exists( name )) 31 | return _tileCache.get(name); 32 | 33 | var loader:Loader = hxd.Res.loader; 34 | var tile:h2d.Tile = loader.load(name).toTile(); 35 | _tileCache.set(name, tile); 36 | 37 | return _tileCache.get(name); 38 | } 39 | 40 | override public function clear():Void 41 | { 42 | _root.visible = false; 43 | _root.removeChildren(); 44 | } 45 | 46 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 47 | { 48 | var fullPath:String = getFullPath(name); 49 | var tile:h2d.Tile = cast getFile(fullPath); 50 | var bmp = new h2d.Bitmap(tile); 51 | var spatialResult:SpatialInfo = compute(info, pivots, tile.width, tile.height); 52 | 53 | bmp.scaleX = spatialResult.scaleX; 54 | bmp.scaleY = spatialResult.scaleY; 55 | bmp.rotation = SpriterUtil.toRadians(SpriterUtil.fixRotation(spatialResult.angle)); 56 | bmp.x = spatialResult.x; 57 | bmp.y = spatialResult.y; 58 | bmp.alpha = Math.abs(spatialResult.a); 59 | _root.addChild( bmp ); 60 | _root.visible = false; 61 | } 62 | 63 | override public function render():Void 64 | { 65 | _root.visible = true; 66 | } 67 | 68 | override public function destroy():Void 69 | { 70 | clear(); 71 | 72 | for ( t in _tileCache) 73 | t.dispose(); 74 | _tileCache = null; 75 | } 76 | } -------------------------------------------------------------------------------- /spriter/library/LuxeLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | 3 | import luxe.Quaternion; 4 | import luxe.Sprite; 5 | import luxe.Vector; 6 | import phoenix.Texture; 7 | import spriter.definitions.PivotInfo; 8 | import spriter.definitions.SpatialInfo; 9 | import spriter.library.AbstractLibrary; 10 | import spriter.util.SpriterUtil; 11 | 12 | /** 13 | * ... 14 | * @author loudo 15 | */ 16 | class LuxeLibrary extends AbstractLibrary 17 | { 18 | var texture:Texture; 19 | var sprite:Sprite; 20 | var list:Array; 21 | public function new(basePath :String) 22 | { 23 | super(basePath); 24 | list = []; 25 | } 26 | override public function getFile(name:String):Dynamic 27 | { 28 | return Luxe.resources.texture(_basePath + name); 29 | } 30 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 31 | { 32 | texture = getFile(name); 33 | info = compute(info, pivots, texture.width, texture.height); 34 | sprite = new Sprite( { 35 | centered : false, 36 | pos : new Vector(info.x, info.y), 37 | scale : new Vector(info.scaleX, info.scaleY), 38 | texture : texture, 39 | depth : list.length 40 | }); 41 | sprite.rotation_z = SpriterUtil.fixRotation(info.angle); 42 | sprite.color.a = Math.abs(info.a); 43 | list.push(sprite.name); 44 | } 45 | override public function render():Void 46 | { 47 | //handled by Luxe Engine 48 | } 49 | 50 | override public function clear():Void 51 | { 52 | for (i in 0...list.length) 53 | { 54 | Luxe.scene.entities.get(list[i]).destroy(); 55 | } 56 | list.splice(0, list.length); // compact buffer 57 | } 58 | 59 | override public function destroy():Void 60 | { 61 | clear(); 62 | list = null; 63 | sprite = null; 64 | texture = null; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /spriter/library/SpriterLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import flash.display.Bitmap; 3 | import flash.display.PixelSnapping; 4 | import flash.display.Sprite; 5 | import openfl.Assets; 6 | import spriter.definitions.PivotInfo; 7 | import spriter.definitions.SpatialInfo; 8 | import spriter.util.SpriterUtil; 9 | 10 | /** 11 | * Simple OpenFL renderer using Bitmap and dislayList. 12 | * Use for quick test purpose. Not recommended for production. 13 | * TODO optimization: cache Bitmap and such things 14 | * @author Loudo 15 | */ 16 | class SpriterLibrary extends AbstractLibrary 17 | { 18 | /** 19 | * Sprite where we add childs (Bitmap) 20 | */ 21 | var _canvas:Sprite; 22 | 23 | var _currentBitmap:Bitmap; 24 | 25 | 26 | /** 27 | * Simple OpenFL renderer using Bitmap and dislayList. 28 | * @param basePath path used to find the BitmapData using Assets.getBitmapData(); 29 | * @param canvas Sprite where we add childs (Bitmap) 30 | */ 31 | public function new(basePath:String, canvas:Sprite) 32 | { 33 | _canvas = canvas; 34 | super(basePath); 35 | } 36 | 37 | /** 38 | * 39 | * @param name of the image 40 | * @return 41 | */ 42 | override public function getFile(name:String):Dynamic 43 | { 44 | return Assets.getBitmapData(_basePath+name,true); 45 | } 46 | 47 | override public function clear():Void 48 | { 49 | var i:Int = _canvas.numChildren; 50 | while (--i >= 0) 51 | { 52 | _canvas.removeChildAt(i); 53 | 54 | } 55 | 56 | } 57 | 58 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 59 | { 60 | 61 | _currentBitmap = new Bitmap (getFile(name), PixelSnapping.AUTO, true); 62 | 63 | info = compute(info, pivots, _currentBitmap.bitmapData.width, _currentBitmap.bitmapData.height); 64 | _currentBitmap.x = info.x; 65 | _currentBitmap.y = info.y; 66 | _currentBitmap.scaleX = info.scaleX; 67 | _currentBitmap.scaleY = info.scaleY; 68 | _currentBitmap.rotation = SpriterUtil.fixRotation(info.angle); 69 | _currentBitmap.alpha = Math.abs(info.a); 70 | _canvas.addChild(_currentBitmap); 71 | info = null; 72 | } 73 | 74 | override public function render():Void 75 | { 76 | //we use display list so we use addChild in addGraphic() 77 | } 78 | 79 | override public function destroy():Void 80 | { 81 | clear(); 82 | _currentBitmap.bitmapData.dispose(); 83 | _currentBitmap = null; 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /spriter/library/TilelayerLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import aze.display.SparrowTilesheet; 3 | import aze.display.TileGroup; 4 | import aze.display.TileLayer; 5 | import aze.display.TileSprite; 6 | import flash.display.Sprite; 7 | import flash.geom.Point; 8 | import openfl.Assets; 9 | import spriter.definitions.PivotInfo; 10 | import spriter.definitions.SpatialInfo; 11 | import spriter.util.SpriterUtil; 12 | 13 | /** 14 | * Advanced OpenFL renderer using Texture Atlas thanks to Tilelayer (https://github.com/elsassph/openfl-tilelayer) 15 | * @author Loudo 16 | */ 17 | class TilelayerLibrary extends AbstractLibrary 18 | { 19 | /** 20 | * Tilelayer used for rendering 21 | */ 22 | var _layer:TileLayer; 23 | 24 | /** 25 | * Sprite where we render 26 | */ 27 | var _canvas:Sprite; 28 | var _cache:Map>; 29 | 30 | /** 31 | * Advanced OpenFL renderer using Texture Atlas thanks to Tilelayer (https://github.com/elsassph/openfl-tilelayer) 32 | * 33 | * @param dataPath .xml of the atlas 34 | * @param atlasPath .png of the atlas 35 | * @param canvas rendering Sprite 36 | */ 37 | public function new(dataPath:String = "", atlasPath:String = "", canvas:Sprite) 38 | { 39 | super(dataPath); 40 | _canvas = canvas; 41 | 42 | _cache = new Map>(); 43 | 44 | var sheetData = Assets.getText(dataPath); 45 | var tilesheet = new SparrowTilesheet(Assets.getBitmapData(atlasPath), sheetData); 46 | _layer = new TileLayer(tilesheet, true); 47 | _canvas.addChild(_layer.view); // layer is NOT a DisplayObject 48 | } 49 | 50 | override public function getFile(name:String):Dynamic 51 | { 52 | if (_cache.exists(name) && _cache.get(name).length > 0) 53 | { 54 | return _cache.get(name).shift(); 55 | } 56 | return new TileSprite(_layer, name); 57 | } 58 | 59 | override public function clear():Void 60 | { 61 | var sprite:TileSprite; 62 | for (tile in _layer.removeAllChildren()) 63 | { 64 | sprite = cast tile; 65 | if (!_cache.exists(sprite.tile)) 66 | { 67 | _cache.set(sprite.tile, new Array()); 68 | } 69 | _cache.get(sprite.tile).push(sprite); 70 | } 71 | } 72 | 73 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 74 | { 75 | 76 | var sprite:TileSprite = getFile(name); 77 | _layer.addChild(sprite); 78 | 79 | info = compute(info, pivots, sprite.width, sprite.height); 80 | 81 | 82 | //sprite.offset = getPivotsRelativeToCenter(info, pivots, sprite.width, sprite.height);//TOFIX tilelayer seems buggy 83 | sprite.x = info.x; 84 | sprite.y = info.y; 85 | sprite.rotation = SpriterUtil.toRadians(SpriterUtil.fixRotation(info.angle)); 86 | sprite.scaleX = info.scaleX; 87 | sprite.scaleY = info.scaleY; 88 | sprite.alpha = info.a; 89 | 90 | //info.put();//back to pool 91 | info = null; 92 | } 93 | 94 | private function getPivotsRelativeToCenter(info:SpatialInfo, pivots:PivotInfo, width:Float, height:Float):Point 95 | { 96 | var x:Float = (pivots.pivotX - 0.5) * width * info.scaleX; 97 | var y:Float = (0.5 - pivots.pivotY) * height * info.scaleY; 98 | return new Point(x, y); 99 | } 100 | 101 | //overrided because tilelayer use the center of the sprite for the coordinates 102 | override public function compute(info:SpatialInfo, pivots:PivotInfo, width:Float, height:Float):SpatialInfo 103 | { 104 | var degreesUnder360 = SpriterUtil.under360(info.angle); 105 | var rad = SpriterUtil.toRadians(degreesUnder360); 106 | var s = Math.sin(rad); 107 | var c = Math.cos(rad); 108 | 109 | var pivotX = info.x; 110 | var pivotY = info.y; 111 | 112 | var preX = info.x - pivots.pivotX * width * info.scaleX + 0.5 * width * info.scaleX; 113 | var preY = info.y + (1 - pivots.pivotY) * height * info.scaleY - 0.5 * height * info.scaleY; 114 | 115 | var x2 = (preX - pivotX) * c - (preY - pivotY) * s + pivotX; 116 | var y2 = (preX - pivotX) * s + (preY - pivotY) * c + pivotY; 117 | 118 | return info.init(x2, -y2, degreesUnder360, info.scaleX, info.scaleY, info.a, info.spin); 119 | } 120 | 121 | override public function render():Void 122 | { 123 | _layer.render(); 124 | } 125 | 126 | override public function destroy():Void 127 | { 128 | clear(); 129 | if (_layer.view != null && _layer.view.parent != null) 130 | _layer.view.parent.removeChild(_layer.view); 131 | _layer = null; 132 | _canvas = null; 133 | } 134 | 135 | 136 | 137 | } -------------------------------------------------------------------------------- /spriter/library/TilemapLibrary.hx: -------------------------------------------------------------------------------- 1 | package spriter.library; 2 | import openfl.display.Sprite; 3 | import openfl.display.Tile; 4 | import openfl.display.Tilemap; 5 | import openfl.display.TilesetEx; 6 | import openfl.geom.Rectangle; 7 | import spriter.definitions.PivotInfo; 8 | import spriter.definitions.SpatialInfo; 9 | import spriter.util.SpriterUtil; 10 | 11 | /** 12 | * Openfl renderer with new tilemap renderer 13 | * Openfl version >= 4.0.4 14 | * You will need openfl-atlas on haxelib which is an Tileset extension to get tiles by name and have their size (offset...) 15 | * @author loudo 16 | */ 17 | class TilemapLibrary extends AbstractLibrary 18 | { 19 | 20 | public var view:Sprite; 21 | public var tilemap:Tilemap; 22 | public var tilesetsLibrary:Array;//all your Tileset 23 | 24 | var tilesCache:Array; 25 | /** 26 | * 27 | * @param tilesetEx Find this class in openfl-atlas on haxelib (https://github.com/loudoweb/openfl-atlas) 28 | * @param view the single canvas you want to draw into 29 | * @param width size of bitmapData 30 | * @param height size of bitmapData 31 | * @param smooth 32 | */ 33 | public function new(tilesets:Array, view:Sprite, width:Int, height:Int, smoothing:Bool = true) 34 | { 35 | super(""); 36 | 37 | this.view = view; 38 | this.view.mouseEnabled = false; 39 | this.view.mouseChildren = false; 40 | 41 | tilesetsLibrary = tilesets; 42 | 43 | tilemap = new Tilemap(width, height, tilesetsLibrary[0], smoothing); 44 | 45 | view.addChild(tilemap); 46 | 47 | tilesCache = []; 48 | } 49 | override public function getFile(name:String):Dynamic 50 | { 51 | return 0; 52 | } 53 | /** 54 | * Call clear() every frame to clear the graphic before a new rendering 55 | */ 56 | override public function clear():Void 57 | { 58 | while (tilemap.numTiles > 0) 59 | { 60 | tilesCache.push(tilemap.removeTileAt(0)); 61 | } 62 | } 63 | override public function render():Void 64 | { 65 | //Not needed. Openfl render automatically when added to Stage 66 | } 67 | 68 | override public function addGraphic(name:String, info:SpatialInfo, pivots:PivotInfo):Void 69 | { 70 | var lib:TilesetEx = null; 71 | var tileID:Int = -1; 72 | var size:Rectangle = null; 73 | 74 | for (i in 0...tilesetsLibrary.length) 75 | { 76 | lib = tilesetsLibrary[i]; 77 | tileID = lib.getImageID(name);//TODO should we store from which sheets the tile comes from? 78 | if (tileID != -1) { 79 | size = lib.getSize(tileID); 80 | break; 81 | } 82 | } 83 | if (tileID == -1 || info.a == 0) 84 | return; 85 | 86 | info.angle = SpriterUtil.fixRotation(info.angle);//fix anti clockwise rotation 87 | 88 | //compute (note: outside compute method because of offsets needed : size.x, size.y) 89 | var rad = SpriterUtil.toRadians(info.angle); 90 | var s = Math.sin(rad); 91 | var c = Math.cos(rad); 92 | 93 | var posX = info.x; 94 | var posY = -info.y;//fix y inverted on Spriter 95 | 96 | var pivotX = size.x + pivots.pivotX * size.width; 97 | var pivotY = size.y + (1 - pivots.pivotY) * size.height; 98 | 99 | info.x = (0 - pivotX) * c - (0 - pivotY) * s + posX; 100 | info.y = (0 - pivotX) * s + (0 - pivotY) * c + posY; 101 | //end compute 102 | 103 | 104 | var tile:Tile; 105 | if (tilesCache.length > 0) 106 | { 107 | tile = tilesCache.pop(); 108 | tile.id = tileID; 109 | tile.tileset = lib; 110 | tile.x = info.x; 111 | tile.y = info.y; 112 | tile.scaleX = info.scaleX; 113 | tile.scaleY = info.scaleY; 114 | tile.rotation = info.angle; 115 | tile.alpha = info.a; 116 | }else { 117 | 118 | tile = new Tile(tileID, info.x, info.y, info.scaleX, info.scaleY, info.angle); 119 | tile.tileset = lib; 120 | } 121 | 122 | tilemap.addTile(tile); 123 | 124 | //info.put();//back to pool 125 | info = null; 126 | pivots = null; 127 | } 128 | 129 | override public function destroy():Void 130 | { 131 | destroyData(); 132 | destroyTilemap(); 133 | view = null; 134 | } 135 | 136 | inline public function destroyData():Void 137 | { 138 | tilesetsLibrary = null; 139 | tilesCache = null; 140 | } 141 | 142 | inline public function destroyTilemap():Void 143 | { 144 | if (tilemap != null && tilemap.parent != null) 145 | tilemap.parent.removeChild(tilemap); 146 | tilemap = null; 147 | } 148 | /** 149 | * If you need to remove Tiles from the cache to release for GC. 150 | * For example if one frame you need 1000 Tiles but after you only need few Tiles most of the times, you can clamp the cache to free some memory 151 | * @usage right after calling clear() because you won't have any data in your cache overwise 152 | * @param maxTiles 153 | */ 154 | public function clampCache(maxTiles:Int):Void 155 | { 156 | if (maxTiles < tilesCache.length) { 157 | tilesCache.splice(0, tilesCache.length - maxTiles); 158 | } 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /spriter/macros/SpriterMacros.hx: -------------------------------------------------------------------------------- 1 | package spriter.macros; 2 | import haxe.io.Bytes; 3 | import haxe.io.Output; 4 | import haxe.macro.Expr; 5 | import haxe.Serializer; 6 | import spriter.definitions.ScmlObject; 7 | #if (!flash && !js) 8 | import sys.FileSystem; 9 | import sys.io.FileOutput; 10 | #end 11 | 12 | /** 13 | * ... 14 | * @author Loudo 15 | */ 16 | class SpriterMacros 17 | { 18 | /** 19 | * Check texture packer atlas to add .png at the end of files. 20 | * Because Spriter's SCML use file name with .png at the end and Sparrow atlas not. 21 | * So it will correct this for you. 22 | * You can use other atlas file instead : Spriter, spriterhaxeengine (https://github.com/loudoweb/SpriterHaxeEngine/tree/master/texturePackerExporter/spriterhaxeengine) 23 | * Data format : Sparrow, Starling only 24 | * @param pathToXml 25 | * @return pathToXml 26 | */ 27 | macro public static function texturePackerChecker(pathToXml:Expr) : Expr 28 | { 29 | var xml_path:String = ''; 30 | switch(pathToXml.expr){ 31 | case EConst(c): switch(c){ 32 | case CString(s): xml_path = s; 33 | default:{} 34 | } 35 | default:{} 36 | } 37 | trace('checking texture packer file : '+xml_path); 38 | var xml_s = sys.io.File.getContent(xml_path); 39 | var xml = Xml.parse(xml_s); 40 | var save:Bool = parseTexturePacker(xml.firstElement()); 41 | if(save) 42 | sys.io.File.saveContent(xml_path, xml.toString()); 43 | 44 | return pathToXml; 45 | } 46 | static function parseTexturePacker(xml:Xml):Bool 47 | { 48 | var path:String; 49 | var corrected:Bool = false; 50 | for (el in xml.elements()) 51 | { 52 | if (el.nodeName == 'SubTexture') { 53 | path = el.get('name'); 54 | if (path.indexOf('.png') == -1) { 55 | el.set('name', path + '.png'); 56 | corrected = true; 57 | }else { 58 | trace('texturePacker XML is ok. No change.'); 59 | break; 60 | } 61 | }else { 62 | trace('texturePacker XML not supported.'); 63 | break; 64 | } 65 | } 66 | if (corrected) 67 | trace('texturePacker XML is corrected.'); 68 | return corrected; 69 | } 70 | /** 71 | * Macro to create an animation with image ordering starting with offset number. Not very useful since Spriter b8. 72 | * @example SpriterMacros.createSpriterTimeline('assets/test.scml', 35, 0, 'folder/subfolder', 'myImage_', 200, 100, 24); 73 | * will create an scml with 35 images at 24 fps like these data : 74 | * 75 | * 76 | * 77 | * 78 | * etc. 79 | * 80 | * @param pathToXml where the .scml file is 81 | * @param frames number of total key frames 82 | * @param offset if your image name doesn't start with 0 83 | * @param folder the folder name 84 | * @param filePrefix the file prefix (before the number) 85 | * @param width of image 86 | * @param height of image 87 | * @param framerate of the animation 88 | * @return pathToXml 89 | */ 90 | macro public static function createSpriterTimeline(pathToXml:Expr, frames:Expr, offset:Expr, folder:Expr, filePrefix:Expr, width:Expr, height:Expr, framerate:Expr):Expr 91 | { 92 | var xml_path:String = ''; 93 | var frames_num:Int = 0; 94 | var offset_num:Int = 0; 95 | var file_prefix:String = ''; 96 | var _folder:String = ''; 97 | var _width:Int = 0; 98 | var _height:Int = 0; 99 | var _framerate:Int = 24; 100 | switch(pathToXml.expr){ 101 | case EConst(c): switch(c){ 102 | case CString(s): xml_path = s; 103 | default:{} 104 | } 105 | default:{} 106 | } 107 | switch(folder.expr){ 108 | case EConst(c): switch(c){ 109 | case CString(s): _folder = s; 110 | default:{} 111 | } 112 | default:{} 113 | } 114 | switch(filePrefix.expr){ 115 | case EConst(c): switch(c){ 116 | case CString(s): file_prefix = s; 117 | default:{} 118 | } 119 | default:{} 120 | } 121 | switch(frames.expr){ 122 | case EConst(c): switch(c){ 123 | case CInt(s): 124 | frames_num = Std.parseInt(s); 125 | default:{} 126 | } 127 | default:{} 128 | } 129 | switch(framerate.expr){ 130 | case EConst(c): switch(c){ 131 | case CInt(s): _framerate = Std.parseInt(s); 132 | default:{} 133 | } 134 | default:{} 135 | } 136 | switch(offset.expr){ 137 | case EConst(c): switch(c){ 138 | case CInt(s): offset_num = Std.parseInt(s); 139 | default:{} 140 | } 141 | default:{} 142 | } 143 | switch(width.expr){ 144 | case EConst(c): switch(c){ 145 | case CInt(s): _width = Std.parseInt(s); 146 | default:{} 147 | } 148 | default:{} 149 | } 150 | switch(height.expr){ 151 | case EConst(c): switch(c){ 152 | case CInt(s): _height = Std.parseInt(s); 153 | default:{} 154 | } 155 | default:{} 156 | } 157 | trace('creating timeline file : '+xml_path); 158 | var xml:Xml = createSCML(_folder, frames_num, offset_num, _framerate, file_prefix, _width, _height); 159 | sys.io.File.saveContent(xml_path, xml.toString()); 160 | 161 | return pathToXml; 162 | } 163 | static function createSCML(folder:String, frames:Int, offset:Int, framerate:Int, filePrefix:String, _width:Int, _height:Int ):Xml 164 | { 165 | var tag:String; 166 | var done:Bool = false; 167 | var totalTime:Int = Std.int((frames / framerate) * 1000); 168 | var stepTime:Int = Std.int(1000 / framerate); 169 | var xml_s:String = '\n'+ 170 | '\n'+ 171 | ' \n'+ 172 | ' \n'+ 173 | ' \n'+ 174 | ' \n'+ 175 | ' \n'+ 176 | ' \n'+ 177 | ' \n'+ 178 | ' \n'+ 179 | ' \n'+ 180 | ' \n'+ 181 | '\n'; 182 | 183 | var xml:Xml = Xml.parse(xml_s).firstElement(); 184 | for (el in xml.elements()) 185 | { 186 | if (el.nodeName == 'folder') { 187 | for (i in 0...(frames+1)) 188 | { 189 | tag = ' \n'; 190 | el.addChild(Xml.parse(tag)); 191 | } 192 | }else if (el.nodeName == 'entity') { 193 | var ent:Xml = el.firstElement(); 194 | if (ent.nodeName == 'animation') { 195 | for (anim in ent.elements()) 196 | { 197 | if (anim.nodeName == 'mainline') { 198 | for (i in 0...(frames+1)) 199 | { 200 | tag = 201 | ' \n'+ 202 | ' \n'+ 203 | ' \n'; 204 | anim.addChild(Xml.parse(tag)); 205 | } 206 | }else if (anim.nodeName == 'timeline') { 207 | for (i in 0...(frames+1)) 208 | { 209 | tag = 210 | ' \n'+ 211 | ' \n'+ 212 | ' \n'; 213 | anim.addChild(Xml.parse(tag)); 214 | } 215 | } 216 | } 217 | } 218 | } 219 | } 220 | return xml; 221 | } 222 | macro public static function cacheSCML(scmlPath:String, output:String) 223 | { 224 | #if (!flash && !js) 225 | var file = sys.io.File.getContent(scmlPath); 226 | var result:String = createCache(file); 227 | if (result != "") { 228 | trace('saving cache scml file in : ' + output); 229 | var fo:FileOutput = sys.io.File.write(output, true); 230 | fo.writeString(result); 231 | fo.close(); 232 | } 233 | #end 234 | return macro null; 235 | } 236 | #if (!flash && !js) 237 | static function createCache(file:String):String 238 | { 239 | var serializer:Serializer = new Serializer(); 240 | serializer.serialize(new ScmlObject(Xml.parse(file))); 241 | return serializer.toString(); 242 | } 243 | #end 244 | macro public static function cacheFolderSCML(scmlFolderPath:String, output:String) 245 | { 246 | #if (!flash && !js) 247 | trace('checking scml files in : ' + scmlFolderPath); 248 | 249 | var files:Array = FileSystem.readDirectory(scmlFolderPath); 250 | var result:String = createCacheFromFolder(scmlFolderPath, files); 251 | if (result != "") { 252 | trace('saving cache scml files in : ' + output); 253 | var fo:FileOutput = sys.io.File.write(output, true); 254 | fo.writeString(result); 255 | fo.close(); 256 | } 257 | #end 258 | return macro null; 259 | } 260 | #if (!flash && !js) 261 | static function createCacheFromFolder(path:String, files:Array):String 262 | { 263 | var map:Map = new Map(); 264 | var serializer:Serializer = new Serializer(); 265 | for (file in files) { 266 | if (file.indexOf(".scml") != -1) 267 | { 268 | var xml_s = sys.io.File.getContent(path + file); 269 | map.set(file.substr(0, file.length - 5), new ScmlObject(Xml.parse(xml_s))); 270 | } 271 | } 272 | serializer.serialize(map); 273 | return serializer.toString(); 274 | } 275 | #end 276 | } -------------------------------------------------------------------------------- /spriter/util/AtlasUtil.hx: -------------------------------------------------------------------------------- 1 | package spriter.util; 2 | import aze.display.TilesheetEx; 3 | import openfl.display.BitmapData; 4 | import openfl.geom.Point; 5 | import openfl.geom.Rectangle; 6 | 7 | typedef SpriterJSON = { 8 | var name:String; 9 | var tags:Array; 10 | } 11 | /** 12 | * Create TilesheetEx to make Spriter atlases compatible with Tilelayer. 13 | * TODO rotation 14 | * @author loudo (Ludovic Bas) 15 | * @deprecated since Tilemap feature in OpenFL >= 6.0 16 | */ 17 | class AtlasUtil 18 | { 19 | 20 | public function new() 21 | { 22 | 23 | } 24 | /** 25 | * Allow to use the json file exported from Spriter. 26 | * @return TilesheetEx to use with Tilelayer 27 | */ 28 | static public function getSpriterTilesheet(img:BitmapData, json:String, textureScale:Float = 1.0, useCenterPoint:Bool = true):TilesheetEx 29 | { 30 | var tilesheet:TilesheetEx = new TilesheetEx(img, textureScale); 31 | 32 | var ins = new Point(0, 0); 33 | 34 | var x = haxe.Json.parse(json); 35 | for (texture in Reflect.fields(x.frames)) 36 | { 37 | var name = texture; 38 | var data = Reflect.field(x.frames, texture); 39 | var rect = if(!data.rotated) 40 | new Rectangle( 41 | data.frame.x, data.frame.y, 42 | data.frame.w, data.frame.h); 43 | else//rotated 44 | new Rectangle( 45 | data.frame.x, data.frame.y, 46 | data.frame.h, data.frame.w); 47 | 48 | var size = if (data.trimmed) // trimmed 49 | new Rectangle( 50 | -data.spriteSourceSize.x, -data.spriteSourceSize.y, 51 | data.sourceSize.w, data.sourceSize.h); 52 | else 53 | new Rectangle(0, 0, rect.width, rect.height); 54 | 55 | //trace([name, rect.x, rect.y, rect.width, rect.height, size.x, size.y, size.width, size.height]); 56 | 57 | #if flash 58 | var bmp = new BitmapData(cast size.width, cast size.height, true, 0); 59 | ins.x = -size.left; 60 | ins.y = -size.top; 61 | bmp.copyPixels(img, rect, ins); 62 | tilesheet.addDefinition(name, size, bmp); 63 | #else 64 | var center = useCenterPoint ? new Point((size.x + size.width / 2), (size.y + size.height / 2)) : (data.trimmed ? new Point(size.x, size.y) : null); 65 | tilesheet.addDefinition(name, size, rect, center); 66 | #end 67 | x = null; 68 | } 69 | return tilesheet; 70 | } 71 | /** 72 | * Custom Sparrow tilesheet xml have smaller tag and attributes name and use positive value for FrameX/FrameY. 73 | * In TexturePacker preferences, set texturePackerExporter folder as your custom exporter directory or copy spriterhaxeengine folder to your custom exporter directory. 74 | * @example 75 | * @return TilesheetEx to use with Tilelayer 76 | */ 77 | static public function getCustomSparrowTilesheet(img:BitmapData, xml:String, textureScale:Float = 1.0, useCenterPoint:Bool = true):TilesheetEx 78 | { 79 | var tilesheet:TilesheetEx = new TilesheetEx(img, textureScale); 80 | 81 | var ins = new Point(0, 0); 82 | var x = new spriter.xml.Access( Xml.parse(xml).firstElement() ); 83 | 84 | for (texture in x.nodes.Sub) 85 | { 86 | var name = texture.att.name; 87 | var rect = new Rectangle( 88 | Std.parseFloat(texture.att.x), Std.parseFloat(texture.att.y), 89 | Std.parseFloat(texture.att.w), Std.parseFloat(texture.att.h)); 90 | 91 | var size = if (texture.has.frameX) // trimmed 92 | new Rectangle( 93 | -Std.parseInt(texture.att.frameX), -Std.parseInt(texture.att.frameY), 94 | Std.parseInt(texture.att.frameW), Std.parseInt(texture.att.frameH)); 95 | else 96 | new Rectangle(0, 0, rect.width, rect.height); 97 | 98 | #if flash 99 | var bmp = new BitmapData(cast size.width, cast size.height, true, 0); 100 | ins.x = -size.left; 101 | ins.y = -size.top; 102 | bmp.copyPixels(img, rect, ins); 103 | tilesheet.addDefinition(name, size, bmp); 104 | #else 105 | var center = useCenterPoint ? new Point((size.x + size.width / 2), (size.y + size.height / 2)) : (texture.has.frameX ? new Point(size.x, size.y) : null); 106 | tilesheet.addDefinition(name, size, rect, center); 107 | #end 108 | x = null; 109 | } 110 | return tilesheet; 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /spriter/util/ColorUtils.hx: -------------------------------------------------------------------------------- 1 | package spriter.util; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class ColorUtils 8 | { 9 | 10 | /** 11 | * Get int from Multiply alpha 12 | * 13 | * @param factor Number from 0.0 to 1.0. 14 | * @return The lightened color 15 | */ 16 | public static inline function multiplyAlpha(factor:Float = 1, color:Int = 0xffffffff):Int 17 | { 18 | var r:Int = getRed(color); 19 | var g:Int = getGreen(color); 20 | var b:Int = getBlue(color); 21 | 22 | return makeFromARGB(factor, r, g, b); 23 | } 24 | 25 | public static inline function getRed(Color:Int):Int 26 | { 27 | return Color >> 16 & 0xFF; 28 | } 29 | 30 | public static inline function getGreen(Color:Int):Int 31 | { 32 | return Color >> 8 & 0xFF; 33 | } 34 | 35 | public static inline function getBlue(Color:Int):Int 36 | { 37 | return Color & 0xFF; 38 | } 39 | 40 | public static inline function makeFromARGB(alpha:Float = 1.0, r:Int, g:Int, b:Int):Int 41 | { 42 | return (Std.int((alpha > 1) ? alpha : (alpha * 255)) & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /spriter/util/MathUtils.hx: -------------------------------------------------------------------------------- 1 | package spriter.util; 2 | 3 | /** 4 | * ... 5 | * @author loudo 6 | */ 7 | class MathUtils 8 | { 9 | inline static public function fabs(n:Float):Float 10 | { 11 | if(n >= 0) { 12 | return n; 13 | }else { 14 | return -n; 15 | } 16 | } 17 | 18 | inline static public function linear(a:Float, b:Float, t:Float):Float 19 | { 20 | return ((b-a)*t)+a; 21 | } 22 | 23 | static public function angleLinear(angleA:Float, angleB:Float, spin:Int, t:Float):Float 24 | { 25 | if(spin == 0) 26 | { 27 | return angleA; 28 | } 29 | if(spin > 0) 30 | { 31 | if((angleB-angleA) < 0) 32 | { 33 | angleB += 360; 34 | } 35 | } 36 | else if(spin < 0) 37 | { 38 | if((angleB-angleA) > 0) 39 | { 40 | angleB -= 360; 41 | } 42 | } 43 | 44 | return linear(angleA,angleB,t); 45 | } 46 | 47 | inline static public function quadratic(a:Float, b:Float, c:Float, t:Float):Float 48 | { 49 | return linear(linear(a,b,t),linear(b,c,t),t); 50 | } 51 | 52 | inline static public function cubic(a:Float, b:Float, c:Float, d:Float, t:Float):Float 53 | { 54 | return linear(quadratic(a,b,c,t),quadratic(b,c,d,t),t); 55 | } 56 | inline static public function quartic(a:Float, b:Float, c:Float, d:Float, e:Float, t:Float):Float 57 | { 58 | return linear(cubic(a,b,c,d,t),cubic(b,c,d,e,t),t); 59 | } 60 | inline static public function quintinc(a:Float, b:Float, c:Float, d:Float, e:Float, f:Float,t:Float):Float 61 | { 62 | return linear(quartic(a,b,c,d,e,t),quartic(b,c,d,e,f,t),t); 63 | } 64 | static public function cubicBezierAtTime( p1x:Float, p1y:Float, p2x:Float, p2y:Float, t:Float):Float 65 | { 66 | var duration:Float=1; 67 | var ax:Float=0; 68 | var bx:Float=0; 69 | var cx:Float=0; 70 | var ay:Float=0; 71 | var by:Float=0; 72 | var cy:Float=0; 73 | 74 | // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. 75 | // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). 76 | 77 | cx = 3.0 * p1x; 78 | bx = 3.0 * (p2x - p1x) - cx; 79 | ax = 1.0 - cx - bx; 80 | cy = 3.0 * p1y; 81 | by = 3.0 * (p2y - p1y) - cy; 82 | ay = 1.0 - cy - by; 83 | 84 | // Convert from input time to parametric value in curve, then from that to output time. 85 | return solve(ax,bx,cx,ay,by,cy,t, solveEpsilon(duration)); 86 | } 87 | // Bezier requires a few helper functions, but the main function you actually use is going to be: float CubicBezierAtTime(float p1x,float p1y,float p2x,float p2y,float t) 88 | 89 | inline static function sampleCurve(a:Float, b:Float, c:Float, t:Float):Float 90 | { 91 | return ((a*t+b)*t+c)*t; 92 | } 93 | 94 | inline static function sampleCurveDerivativeX(ax:Float, bx:Float, cx:Float, t:Float):Float 95 | { 96 | return (3.0*ax*t+2.0*bx)*t+cx; 97 | } 98 | 99 | // The epsilon value to pass given that the animation is going to run over |dur| seconds. The longer the 100 | 101 | // animation, the more precision is needed in the timing function result to avoid ugly discontinuities. 102 | 103 | inline static function solveEpsilon(duration:Float):Float 104 | { 105 | return 1.0/(200.0*duration); 106 | } 107 | 108 | 109 | inline static function solve(ax:Float, bx:Float, cx:Float, ay:Float, by:Float, cy:Float, x:Float, epsilon:Float):Float 110 | { 111 | return sampleCurve(ay,by,cy,solveCurveX(ax,bx,cx,x,epsilon)); 112 | } 113 | 114 | static function solveCurveX(ax:Float, bx:Float, cx:Float, x:Float, epsilon:Float):Float 115 | { 116 | var t0:Float; 117 | var t1:Float; 118 | var t2:Float = x; 119 | var x2:Float; 120 | var d2:Float; 121 | var i:Int; 122 | 123 | // First try a few iterations of Newton's method -- normally very fast. 124 | for (i in 0...8) 125 | { 126 | x2 = sampleCurve(ax, bx, cx, t2) - x; 127 | if(fabs(x2) < epsilon) { 128 | return t2; 129 | } 130 | 131 | d2 = sampleCurveDerivativeX(ax, bx, cx, t2); 132 | 133 | if(fabs(d2) < 1e-6) { 134 | break; 135 | } 136 | 137 | t2=t2-x2/d2; 138 | } 139 | 140 | // Fall back to the bisection method for reliability. 141 | t0 = 0.0; 142 | t1 = 1.0; 143 | t2 = x; 144 | 145 | if(t2 < t0) { 146 | return t0; 147 | } 148 | 149 | if(t2 > t1) 150 | { 151 | return t1; 152 | } 153 | 154 | 155 | while(t0 < t1) 156 | { 157 | x2 = sampleCurve(ax, bx, cx, t2); 158 | if(fabs(x2-x) < epsilon) { 159 | return t2; 160 | } 161 | if(x > x2) { 162 | t0=t2; 163 | }else { 164 | t1=t2; 165 | } 166 | t2=(t1-t0)*.5+t0; 167 | } 168 | 169 | return t2; // Failure. 170 | } 171 | } -------------------------------------------------------------------------------- /spriter/util/SpriterPool.hx: -------------------------------------------------------------------------------- 1 | package spriter.util; 2 | import spriter.interfaces.ISpriterDestroyable; 3 | 4 | /** 5 | * ... 6 | * @author Loudo 7 | * @author flixel 8 | */ 9 | /* 10 | #if flixel 11 | typedef TSpriterPool = flixel.util.FlxPool; 12 | #elseif openfl 13 | typedef TSpriterPool = SpriterPool 14 | #elseif flambe 15 | 16 | #end 17 | */ 18 | 19 | /** 20 | * A generic container that facilitates pooling and recycling of objects. 21 | * WARNING: Pooled objects must have parameterless constructors: function new() 22 | */ 23 | class SpriterPool 24 | { 25 | private var _pool:Array; 26 | private var _class:Class; 27 | 28 | public var length(get, never):Int; 29 | 30 | public function new(classObj:Class) 31 | { 32 | _pool = []; 33 | _class = classObj; 34 | } 35 | 36 | public function get():T 37 | { 38 | var obj:T = _pool.pop(); 39 | if (obj == null) 40 | { 41 | obj = Type.createInstance(_class, []); 42 | } 43 | return obj; 44 | } 45 | 46 | public function put(obj:T):Void 47 | { 48 | // we don't want to have the same object in pool twice 49 | if (obj != null && _pool.indexOf(obj) < 0) 50 | { 51 | obj.destroy(); 52 | _pool.push(obj); 53 | } 54 | } 55 | 56 | public function putUnsafe(obj:T):Void 57 | { 58 | if (obj != null) 59 | { 60 | obj.destroy(); 61 | _pool.push(obj); 62 | } 63 | } 64 | 65 | public function preAllocate(numObjects:Int):Void 66 | { 67 | for (i in 0...numObjects) 68 | { 69 | _pool.push(Type.createInstance(_class, [])); 70 | } 71 | } 72 | 73 | public function clear():Array 74 | { 75 | var oldPool = _pool; 76 | _pool = []; 77 | return oldPool; 78 | } 79 | 80 | private inline function get_length():Int 81 | { 82 | return _pool.length; 83 | } 84 | } -------------------------------------------------------------------------------- /spriter/util/SpriterUtil.hx: -------------------------------------------------------------------------------- 1 | package spriter.util; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class SpriterUtil 8 | { 9 | 10 | inline static public function toRadians(deg : Float) : Float 11 | { 12 | return deg * Math.PI / 180; 13 | } 14 | //because rotation on spriter vs flash are inverted 15 | inline static public function fixRotation(rotation : Float) : Float 16 | { 17 | if (rotation == 0) 18 | rotation = 360; 19 | 20 | return 360 - rotation; 21 | } 22 | 23 | inline static public function under360(rotation : Float) : Float 24 | { 25 | while (rotation > 360) 26 | { 27 | rotation -= 360; 28 | } 29 | 30 | while (rotation < 0) 31 | { 32 | rotation += 360; 33 | } 34 | return rotation; 35 | } 36 | 37 | inline static public function normalizeRotation(rotation : Float) : Float 38 | { 39 | return rotation / 360; 40 | } 41 | 42 | inline static public function fixPivotY(pivotY : Float) : Float 43 | { 44 | return 1 - pivotY; 45 | } 46 | 47 | inline static public function signOf(f:Float):Int 48 | { 49 | return (f < 0) ? -1 : 1; 50 | } 51 | 52 | inline static public function sameSign(f1:Float, f2:Float):Bool 53 | { 54 | return signOf(f1) == signOf(f2); 55 | } 56 | 57 | inline static public function changeSign(f:Float):Float 58 | { 59 | return f *= -1; 60 | } 61 | 62 | inline static public function clearArray(array:Array):Void 63 | { 64 | if (array.length > 0) 65 | { 66 | #if flash 67 | untyped array.length = 0; 68 | #else 69 | array.splice(0, array.length);//allocates in hxcpp but fastest 70 | #end 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /spriter/vars/Variable.hx: -------------------------------------------------------------------------------- 1 | package spriter.vars; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class Variable 8 | { 9 | public var name:String; 10 | public var value(default,null):T; 11 | public var def:T; 12 | public function new(name:String, def:T) 13 | { 14 | this.name = name; 15 | this.value = def; 16 | this.def = def; 17 | } 18 | /** 19 | * Update the var 20 | * @param value 21 | * @return true if value changes 22 | */ 23 | public function set(value:String):Bool 24 | { 25 | return false; 26 | } 27 | /** 28 | * Don't update the var but get the value in the right format (float, int, string). 29 | * @param value 30 | * @return 31 | */ 32 | public function convert(value:String):T 33 | { 34 | return null; 35 | } 36 | public function toString():String 37 | { 38 | return '[var $name : v:$value, d:$def]'; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /spriter/vars/VariableFloat.hx: -------------------------------------------------------------------------------- 1 | package spriter.vars; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class VariableFloat extends Variable 8 | { 9 | 10 | public function new(name:String,value:Float) 11 | { 12 | super(name,value); 13 | } 14 | 15 | override public function set(value:String):Bool 16 | { 17 | var temp = this.value; 18 | this.value = Std.parseFloat(value); 19 | if (temp != this.value) 20 | return true; 21 | return false; 22 | } 23 | override public function convert(value:String):Float 24 | { 25 | return Std.parseFloat(value); 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /spriter/vars/VariableInt.hx: -------------------------------------------------------------------------------- 1 | package spriter.vars; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class VariableInt extends Variable 8 | { 9 | 10 | public function new(name:String,value:Int) 11 | { 12 | super(name,value); 13 | } 14 | 15 | override public function set(value:String):Bool 16 | { 17 | var temp:Int = this.value; 18 | this.value = Std.parseInt(value); 19 | if (temp != this.value) 20 | return true; 21 | return false; 22 | } 23 | 24 | override public function convert(value:String):Int 25 | { 26 | return Std.parseInt(value); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /spriter/vars/VariableString.hx: -------------------------------------------------------------------------------- 1 | package spriter.vars; 2 | 3 | /** 4 | * ... 5 | * @author Loudo 6 | */ 7 | class VariableString extends Variable 8 | { 9 | 10 | public function new(name:String,value:String) 11 | { 12 | super(name,value); 13 | } 14 | 15 | override public function set(value:String):Bool 16 | { 17 | var temp:String = this.value; 18 | this.value = value; 19 | if (temp != this.value) 20 | return true; 21 | return false; 22 | } 23 | 24 | override public function convert(value:String):String 25 | { 26 | return value; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /spriter/xml/Access.hx: -------------------------------------------------------------------------------- 1 | package spriter.xml; 2 | 3 | // haxe.xml.Access replaced haxe.xml.Fast in Haxe 4 4 | #if (haxe_ver >= 4) 5 | typedef Access = haxe.xml.Access; 6 | #else 7 | typedef Access = haxe.xml.Fast; 8 | #end 9 | -------------------------------------------------------------------------------- /texturePackerExporter/spriterhaxeengine/exporter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SpriterHaxeEngine 4 | 5 | 6 | SpriterHaxeEngine 7 | 8 | 9 | Exporter for SpriterHaxeEngine (from Starling xml file format). 10 | 11 | 12 | 1.1 13 | 14 | 15 | 16 | 17 | 18 | xml 19 | 20 | 21 | Data file 22 | 23 | 24 | xml 25 | 26 | 27 | XML file for SpriterHaxeEngine using optimized Starling xml file format. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | 38 | false 39 | 40 | 41 | cw 42 | 43 | 44 | false 45 | 46 | 47 | false 48 | 49 | 50 | yes 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /texturePackerExporter/spriterhaxeengine/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% for sprite in allSprites %} 5 | {% endfor %} 6 | --------------------------------------------------------------------------------