diff --git a/spritesheet/AnimatedSprite.hx b/spritesheet/AnimatedSprite.hx index db34cc0..5c3b862 100644 --- a/spritesheet/AnimatedSprite.hx +++ b/spritesheet/AnimatedSprite.hx @@ -1,28 +1,37 @@ package spritesheet; -import flash.display.Bitmap; +import spritesheet.render.IRenderTarget; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.Lib; import spritesheet.data.BehaviorData; +enum Flag { + BLEND_ADD; +} +@:access(spritesheet.Spritesheet) class AnimatedSprite extends Sprite { - public var bitmap:Bitmap; + public var renderTarget:IRenderTarget; public var currentBehavior:BehaviorData; public var currentFrameIndex:Int; public var smoothing:Bool; public var spritesheet:Spritesheet; + + public var currentFrameOrigWidth(default,null):Int=0; + public var currentFrameOrigHeight(default,null):Int=0; private var behaviorComplete:Bool; private var behaviorQueue:Array ; private var behavior:BehaviorData; private var loopTime:Int; private var timeElapsed:Int; + + private var isAFrameShown : Bool = false; //Inidicates if any frame has been drawn at all public function new (sheet:Spritesheet, smoothing:Bool = false) { @@ -33,12 +42,18 @@ class AnimatedSprite extends Sprite { this.spritesheet = sheet; behaviorQueue = new Array (); - bitmap = new Bitmap (); - addChild (bitmap); - + renderTarget = switch(sheet.imageData) { + case BITMAP_DATA(_,_): + if (sheet.useSingleBitmapData) { + new spritesheet.render.RenderBitmapRectToGraphics (this); + } else { + new spritesheet.render.RenderWholeBitmapToGraphics (this); + } + case TILESHEET(ts): + new spritesheet.render.RenderTilesheetToGraphics(this, ts); + } } - public function getFrameData (index:Int):Dynamic { if (currentBehavior != null && currentBehavior.frameData.length > index) { @@ -118,27 +133,22 @@ class AnimatedSprite extends Sprite { } } - - + public function update (deltaTime:Int):Void { if (!behaviorComplete) { timeElapsed += deltaTime; - var ratio = timeElapsed / loopTime; - - if (ratio >= 1) { + if (timeElapsed >= loopTime) { if (currentBehavior.loop) { - - ratio -= Math.floor (ratio); - + timeElapsed -= loopTime; } else { - + behaviorComplete = true; - ratio = 1; - + timeElapsed = loopTime-1; //Stop time + } } @@ -150,17 +160,20 @@ class AnimatedSprite extends Sprite { // This is the number of ms we have been in this animation var timeInAnimation:Int = timeElapsed % loopTime; // The raw frame index is the number of frames we have had time to show - var rawFrameIndex:Int = Math.round(timeInAnimation / frameDuration); + currentFrameIndex = Std.int(timeInAnimation / frameDuration); // Make sure we loop correctly - currentFrameIndex = rawFrameIndex % frameCount; - + if (currentFrameIndex >= frameCount) { + currentFrameIndex = frameCount - 1; + } + var frame = spritesheet.getFrame (currentBehavior.frames [currentFrameIndex]); - - bitmap.bitmapData = frame.bitmapData; - bitmap.smoothing = smoothing; - bitmap.x = frame.offsetX - currentBehavior.originX; - bitmap.y = frame.offsetY - currentBehavior.originY; + isAFrameShown = true; + renderTarget.drawFrame(frame, -currentBehavior.originX, -currentBehavior.originY, smoothing); + + // Set frame data + currentFrameOrigWidth = frame.origWidth; + currentFrameOrigHeight = frame.origHeight; if (behaviorComplete) { @@ -193,17 +206,13 @@ class AnimatedSprite extends Sprite { loopTime = Std.int ((behavior.frames.length / behavior.frameRate) * 1000); - if (bitmap.bitmapData == null) { - - update (0); - - } + update (0); } } else { - bitmap.bitmapData = null; + isAFrameShown = false; currentBehavior = null; currentFrameIndex = -1; behaviorComplete = true; @@ -212,5 +221,14 @@ class AnimatedSprite extends Sprite { } + public function setFlag(flag : Flag) { + renderTarget.enableFlag(flag); + update(0); + } + + public function unsetFlag(flag : Flag) { + renderTarget.disableFlag(flag); + update(0); + } -} \ No newline at end of file +} diff --git a/spritesheet/Spritesheet.hx b/spritesheet/Spritesheet.hx index 52b269a..fff2aef 100644 --- a/spritesheet/Spritesheet.hx +++ b/spritesheet/Spritesheet.hx @@ -2,11 +2,22 @@ package spritesheet; import flash.display.BitmapData; +import openfl.display.Tilemap; import flash.geom.Point; import flash.geom.Rectangle; import spritesheet.data.BehaviorData; import spritesheet.data.SpritesheetFrame; +private enum ImageData { + BITMAP_DATA(sourceImage : BitmapData, sourceImageAlpha : BitmapData); + TILESHEET(sheet : openfl.display.Tilemap); +} + +enum StorageType { + INDIVIDUAL_BITMAPS;// Store every frame in its own BITMAP_DATA + SINGLE_BITMAP; // Store all frames in onw single bitmap + TILESHEET; // Store using openfl.display.Tilemap +} class Spritesheet { @@ -16,14 +27,29 @@ class Spritesheet { public var totalFrames:Int; private var frames:Array ; + private var imageData : ImageData; private var sourceImage:BitmapData; private var sourceImageAlpha:BitmapData; + public var useSingleBitmapData(default,null):Bool; - - public function new (image:BitmapData = null, frames:Array = null, behaviors:Map = null, imageAlpha:BitmapData = null) { - - this.sourceImage = image; - this.sourceImageAlpha = imageAlpha; + public function new (image:BitmapData = null, frames:Array = null, behaviors:Map = null, + imageAlpha:BitmapData = null, storageType : StorageType = null) { + + if (storageType == null) { + #if flash + storageType = INDIVIDUAL_BITMAPS; //Tilemap does not work with blendmode = ADD on flash + #else + storageType = TILESHEET; //Only tilesheets work for blendmode = ADD on other targets + #end + } + + if (storageType == TILESHEET) { + this.imageData = TILESHEET(new Tilemap(image)); + this.useSingleBitmapData = true; + } else { + this.imageData = BITMAP_DATA(image, imageAlpha); + this.useSingleBitmapData = storageType == SINGLE_BITMAP; + } if (frames == null) { @@ -46,6 +72,12 @@ class Spritesheet { this.behaviors = behaviors; } + + if (useSingleBitmapData && imageAlpha != null) { + var targetRect = new Rectangle(0,0,image.width,image.height); + var targetPoint = new Point(); + image.copyChannel (imageAlpha, targetRect, targetPoint, 2, 8); + } } @@ -80,20 +112,25 @@ class Spritesheet { var frame = frames[index]; - var bitmapData = new BitmapData (frame.width, frame.height, true); var sourceRectangle = new Rectangle (frame.x, frame.y, frame.width, frame.height); var targetPoint = new Point (); - bitmapData.copyPixels (sourceImage, sourceRectangle, targetPoint); - - if (sourceImageAlpha != null) { - - bitmapData.copyChannel (sourceImageAlpha, sourceRectangle, targetPoint, 2, 8); - + switch(imageData) { + case BITMAP_DATA(sourceImage, sourceImageAlpha): + if (useSingleBitmapData) { + frame.bitmapData = sourceImage; + } else { + var bitmapData = new BitmapData (frame.width, frame.height, true); + bitmapData.copyPixels (sourceImage, sourceRectangle, targetPoint); + + if (sourceImageAlpha != null) { + bitmapData.copyChannel (sourceImageAlpha, sourceRectangle, targetPoint, 2, 8); + } + frame.bitmapData = bitmapData; + } + case TILESHEET(sheet): + frame.tilesheetIndex = sheet.addTileRect(sourceRectangle, new Point(0,0)); } - - frame.bitmapData = bitmapData; - } @@ -225,4 +262,4 @@ class Spritesheet { } -} \ No newline at end of file +} diff --git a/spritesheet/data/BehaviorData.hx b/spritesheet/data/BehaviorData.hx index d6ce0e8..6ba6108 100644 --- a/spritesheet/data/BehaviorData.hx +++ b/spritesheet/data/BehaviorData.hx @@ -55,6 +55,11 @@ class BehaviorData { return new BehaviorData ("behavior" + (uniqueID++), frames.copy (), loop, frameRate, originX, originY); } - + + public function reverse():BehaviorData { + var nFrames = frames.copy(); + nFrames.reverse(); + return new BehaviorData (name + "Reverse", nFrames, loop, frameRate, originX, originY); + } } \ No newline at end of file diff --git a/spritesheet/data/SpritesheetFrame.hx b/spritesheet/data/SpritesheetFrame.hx index 2ef199c..b08499a 100644 --- a/spritesheet/data/SpritesheetFrame.hx +++ b/spritesheet/data/SpritesheetFrame.hx @@ -8,6 +8,7 @@ class SpritesheetFrame { public var name:String; + public var tilesheetIndex:Int;//Used when drawing this frame from a tilesheet public var bitmapData:BitmapData; public var height:Int; public var offsetX:Int; @@ -15,9 +16,11 @@ class SpritesheetFrame { public var width:Int; public var x:Int; public var y:Int; + public var origWidth:Int; + public var origHeight:Int; - public function new (x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, offsetX:Int = 0, offsetY:Int = 0) { + public function new (x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, offsetX:Int = 0, offsetY:Int = 0, origWidth:Int = -1, origHeight:Int = -1) { this.x = x; this.y = y; @@ -25,8 +28,10 @@ class SpritesheetFrame { this.height = height; this.offsetX = offsetX; this.offsetY = offsetY; + this.origWidth = (origWidth == -1)?width:origWidth; + this.origHeight = (origHeight == -1)?height:origHeight; } -} \ No newline at end of file +} diff --git a/spritesheet/importers/LibGDXImporter.hx b/spritesheet/importers/LibGDXImporter.hx index d29f631..0de2ea6 100644 --- a/spritesheet/importers/LibGDXImporter.hx +++ b/spritesheet/importers/LibGDXImporter.hx @@ -61,7 +61,7 @@ class LibGDXImporter { for (i in 0...frames.length) { var gdxFrame = frames[i]; - var sFrame = new SpritesheetFrame (gdxFrame.xy.x, gdxFrame.xy.y, gdxFrame.size.w, gdxFrame.size.h, gdxFrame.offset.x, gdxFrame.orig.h - gdxFrame.size.h - gdxFrame.offset.y); + var sFrame = new SpritesheetFrame (gdxFrame.xy.x, gdxFrame.xy.y, gdxFrame.size.w, gdxFrame.size.h, gdxFrame.offset.x, gdxFrame.orig.h - gdxFrame.size.h - gdxFrame.offset.y, gdxFrame.orig.w, gdxFrame.orig.h); sFrame.name = gdxFrame.filename; indexes.push (allFrames.length); diff --git a/spritesheet/importers/TexturePackerImporter.hx b/spritesheet/importers/TexturePackerImporter.hx index 9766080..99af180 100644 --- a/spritesheet/importers/TexturePackerImporter.hx +++ b/spritesheet/importers/TexturePackerImporter.hx @@ -100,7 +100,7 @@ class TexturePackerImporter { for (i in 0...frames.length) { var tpFrame:TPFrame = frames[i]; - var sFrame = new SpritesheetFrame ( tpFrame.frame.x, tpFrame.frame.y, tpFrame.frame.w, tpFrame.frame.h ); + var sFrame = new SpritesheetFrame ( tpFrame.frame.x, tpFrame.frame.y, tpFrame.frame.w, tpFrame.frame.h, tpFrame.sourceSize.w, tpFrame.sourceSize.h ); if( tpFrame.trimmed ) { diff --git a/spritesheet/render/IRenderTarget.hx b/spritesheet/render/IRenderTarget.hx new file mode 100644 index 0000000..88757b0 --- /dev/null +++ b/spritesheet/render/IRenderTarget.hx @@ -0,0 +1,10 @@ +package spritesheet.render; + +import spritesheet.data.SpritesheetFrame; +import spritesheet.AnimatedSprite; + +interface IRenderTarget { + function drawFrame(frame : SpritesheetFrame, offsetX : Float, offsetY : Float, smoothing : Bool) : Void; + function enableFlag(flag : Flag) : Void; + function disableFlag(flag : Flag) : Void; +} \ No newline at end of file diff --git a/spritesheet/render/RenderBitmapRectToGraphics.hx b/spritesheet/render/RenderBitmapRectToGraphics.hx new file mode 100644 index 0000000..5807d5c --- /dev/null +++ b/spritesheet/render/RenderBitmapRectToGraphics.hx @@ -0,0 +1,31 @@ +package spritesheet.render; + +import spritesheet.data.SpritesheetFrame; +import flash.display.Sprite; +import flash.display.BlendMode; +import spritesheet.AnimatedSprite; + +class RenderBitmapRectToGraphics implements IRenderTarget { + private var sprite : Sprite; + public function new(o : Sprite) {this.sprite = o;} + public function drawFrame(frame : SpritesheetFrame, offsetX : Float, offsetY : Float, smoothing : Bool) { + var m = new flash.geom.Matrix(); + m.translate(offsetX + frame.offsetX - frame.x, offsetY + frame.offsetY - frame.y); + sprite.graphics.clear(); + sprite.graphics.beginBitmapFill(frame.bitmapData, m, false, smoothing); + sprite.graphics.drawRect(offsetX + frame.offsetX,offsetY + frame.offsetY,frame.width, frame.height); + sprite.graphics.endFill(); + } + public function enableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + sprite.blendMode = BlendMode.ADD; + } + } + public function disableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + sprite.blendMode = BlendMode.NORMAL; + } + } +} \ No newline at end of file diff --git a/spritesheet/render/RenderTilesheetToGraphics.hx b/spritesheet/render/RenderTilesheetToGraphics.hx new file mode 100644 index 0000000..2cebc31 --- /dev/null +++ b/spritesheet/render/RenderTilesheetToGraphics.hx @@ -0,0 +1,55 @@ +package spritesheet.render; + +import openfl.display.Tilemap +import spritesheet.data.SpritesheetFrame; +import flash.display.Sprite; +import spritesheet.AnimatedSprite; + + class RenderTilesheetToGraphics implements IRenderTarget { + private var sprite : Sprite; + private var tilesheet : Tilemap; + private var flags : Int = 0; + + // We store the tile data here because we do not want to recreate it every frame and run into garbage collector problems + // But since it is refilled every time a frame is drawn, it can be static + static private var tileDataWithColorTransform : Array = [0.0,0.0,0.0,0.0,0.0,0.0]; + static private var tileDataWithoutColorTransform : Array = [0.0,0.0,0.0]; + + public function new(o : Sprite, tilesheet : Tilemap) { + this.sprite = o; + this.tilesheet = tilesheet; + } + public function drawFrame(frame : SpritesheetFrame, offsetX : Float , offsetY : Float, smoothing : Bool) { + sprite.graphics.clear(); + var tileData : Array = null;// First data = offset + var usedFlags = flags; + if (sprite.transform.colorTransform == null) { + tileData = tileDataWithoutColorTransform; + tileData[0] = offsetX + frame.offsetX; + tileData[1] = offsetY + frame.offsetY; + tileData[2] = frame.tilesheetIndex; + } else { + tileData = tileDataWithColorTransform; + tileData[0] = offsetX + frame.offsetX; + tileData[1] = offsetY + frame.offsetY; + tileData[2] = frame.tilesheetIndex; + tileData[3] = sprite.transform.colorTransform.redMultiplier; + tileData[4] = sprite.transform.colorTransform.greenMultiplier; + tileData[5] = sprite.transform.colorTransform.blueMultiplier; + usedFlags |= Tilemap.TILE_RGB; + } + tilesheet.drawTiles(sprite.graphics,tileData, smoothing, usedFlags); + } + public function enableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + flags = flags | Tilemap.TILE_BLEND_ADD; + } + } + public function disableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + flags &= 0xFFFFFFF - Tilemap.TILE_BLEND_ADD; + } + } +} diff --git a/spritesheet/render/RenderWholeBitmapToGraphics.hx b/spritesheet/render/RenderWholeBitmapToGraphics.hx new file mode 100644 index 0000000..90ab39e --- /dev/null +++ b/spritesheet/render/RenderWholeBitmapToGraphics.hx @@ -0,0 +1,30 @@ +package spritesheet.render; + +import flash.display.Sprite; +import flash.display.BlendMode; +import spritesheet.AnimatedSprite; + +class RenderWholeBitmapToGraphics implements IRenderTarget { + private var sprite : Sprite; + public function new(o : Sprite) {this.sprite = o;} + public function drawFrame(frame : spritesheet.data.SpritesheetFrame, offsetX : Float, offsetY : Float, smoothing : Bool) { + var m = new flash.geom.Matrix(); + m.translate(offsetX + frame.offsetX, offsetY + frame.offsetY); + sprite.graphics.clear(); + sprite.graphics.beginBitmapFill(frame.bitmapData, m, false, smoothing); + sprite.graphics.drawRect(offsetX + frame.offsetX, offsetY + frame.offsetY ,frame.width, frame.height); + sprite.graphics.endFill(); + } + public function enableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + sprite.blendMode = BlendMode.ADD; + } + } + public function disableFlag(flag : Flag) { + switch(flag) { + case BLEND_ADD: + sprite.blendMode = BlendMode.NORMAL; + } + } +} \ No newline at end of file