Starling: Scaling vector sprite for image crops instead of scales - actionscript-3

I've got vector sprites in an external SWF. I can draw them onto a Starling image just fine. But when I try to scale the sprite before hand, the resulting image has been cropped instead of scaled.
This works fine outside of starling with plain sprites, but I'm new to Starling so I'm sure I'm missing something basic. Any help would be appreciated. Thanks!
FlashDevelop + AIR + Starling
Main.as
package
{
import flash.display.Sprite;
import starling.core.Starling;
public class Main extends Sprite
{
private var starling:Starling;
public function Main():void
{
starling = new Starling(Game, stage);
starling.start();
}
}
}
Game.as
package
{
import flash.display.BitmapData;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;
public class Game extends Sprite
{
[Embed(source = "../assets/Sprites.swf", symbol = "Sprites_Tiger")]
private var Sprites_Tiger:Class;
public var Tiger:flash.display.Sprite = new Sprites_Tiger();
public function Game()
{
Tiger.width = 256;
Tiger.height = 256;
var bitmapdata:BitmapData = new BitmapData(Tiger.width, Tiger.height);
bitmapdata.draw(Tiger);
var texture:Texture = Texture.fromBitmapData(bitmapdata);
var image:Image = new Image(texture);
addChild(image);
}
}
}
EDIT: I found a work-around by adding a scale matrix to the bitmapdata.draw. But I still don't know why the scaling is converted to cropping without it.

I think i remember similar issue in my last project , try this solution:
bitmapdata.draw(Tiger, Tiger.transform.matrix);

Related

AS3 debugger stops responding while trying to load image into sprite using Loader

I'm trying to create a simple Menu in AS3. There is a sprite called startButton, which when pressed will call the function startGame, and that's it! But, not so easy. I'm using flashdevelop IDE so I'm trying to call a loader to get a .png image file for the spite startButton. But, it doesn't work. There are no error messages, the debugger just does not respond. Any help? Here is the code for both files
Main code:
package {
//Other Files
import Menu;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.ui.Mouse;
public class Main extends Sprite {
//Game values
public static var gameWidth:int = 750;
public static var gameHeight:int = 750;
public function Main() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
addChild(Menu.startButton);
Menu.startButton.addEventListener(MouseEvent.CLICK, startGame);
stage.addEventListener(Event.ENTER_FRAME, update);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
}
//Function starts game
public function startGame(evt:MouseEvent):void {
removeChild(Menu.startButton);
}
//Updates every 60 seconds
public function update():void {
trace("Updated");
}
}
}
And Menu Image code:
package {
//Other files
import Main;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class Menu extends Sprite {
public static function imageLoaded():void {
startButton.addChild(loader);
//initizlize values for startButton Bitmap
startButton.x = (Main.gameWidth / 2) - (startButton.width / 2);
startButton.y = (Main.gameHeight / 2) - (startButton.height / 2);
}
//create startButton Bitmap
public static var startButton:Sprite = new Sprite();
public static var loader:Loader = new Loader();
loader.load(new URLRequest("lib/menustartbutton.png"));
loader.addEventListener(Event.COMPLETE, imageLoaded);
}
}
By the way, I wait for the loader to successfully load the image before working with it, just in case the image takes more time and it draws errors.
The problem is that you misuse static. all static methods/properties are initialized before the classes themselves. As a result static can receive values but they cannot run any code. Running code has to happen after all classes are ready to go which is not the case when static is initialized. In your case startButton and loader are created correctly but the next line never runs 'loader.load'.
Don't misuse static, you are obviously trying to use static to make you code writing and life easier but at the end because you are misusing it you will always end up with more problems.

Starling library - When I try and change cursor to image...it doesn't work (and a sprite element dissapears from stage)

I am using Starling to make a really, really, really easy game - I am just trying to add a stationary sprite to the stage...and make it so that when the mouse touches the sprite...the game "stops" and a score is sent. I haven't tried implementing hitTest yet for the collision, but I have run into a sort of conflict problem where, when I comment out the line(s) that is supposed to change the cursor image (see Startup.as - stage.addEventListener(TouchEvent.TOUCH, touchHandler); and createCustomeCursor), the instance of AvatarEnemy (see enemy in Game.as) does what it should, and is placed in the center of the screen. When I comment in the line that is supposed to change the cursor: a). the cursor doesn't change, and b.) the enemy sprite disappears. When I comment out the same lines - the enemy sprite reappears (but obviously, the cursor doesn't work - not that it was working in the first place). Why is this happening? My code is below - someone mentioned something about not doing things before Starling is initialized here (a question I asked, which is a precursor to this one) - but I'm not sure what they meant because it at least seems like all my code is in the right place.
Game.as
package
{
import Classes.AvatarEnemy;
import starling.display.Sprite;
public class Game extends Sprite
{
//private var juggler:Juggler = Starling.juggler;
private var enemy:AvatarEnemy;
public function Game()
{
enemy = new AvatarEnemy();
addChild(enemy);
}
}
}
Startup.as
package
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.geom.Point;
import starling.core.Starling;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.display.DisplayObject;
import flash.display.BitmapData;
import flash.ui.MouseCursorData;
import flash.ui.Mouse;
[SWF(width="500", height="500", frameRate="30", backgroundColor="#FFFFFF")]
public class Startup extends Sprite
{
private var mStarling:Starling;
[Embed(source="Classes/Avatarpic.png")]
private const Cursor:Class;
public var cursor:DisplayObject;
public function Startup()
{
// Create a Starling instance that will run the "Game" class
mStarling = new Starling(Game, stage);
mStarling.start();
// These settings are recommended to avoid problems with touch handling
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
createCustomCursor();
stage.addEventListener(TouchEvent.TOUCH, touchHandler);
}
private function touchHandler(event:TouchEvent):void
{
var touch:Touch = event.getTouch(cursor);
if(touch.phase){
{
var localPos:Point = touch.getLocation(cursor);
trace("Touched object at position: " + localPos);
}
}
}
public function createCustomCursor():void
{
var cursorBitmaps:Vector.<BitmapData> = new Vector.<BitmapData>();
cursorBitmaps.push((new Cursor() as Bitmap).bitmapData);
var mouseCursorData:MouseCursorData = new MouseCursorData();
mouseCursorData.data = cursorBitmaps;
mouseCursorData.frameRate = 30;
mouseCursorData.hotSpot = new Point(0, 0);
Mouse.registerCursor("customCursor", mouseCursorData);
Mouse.cursor = "customCursor";
}
}
}
Any help would be greatly appreciated (if you need a copy of the code (it's an Adobe Flash Builder 4.7 project - I have made a git repo - just comment if you want the link).
UPDATE
I realized I wasn't registering the image as the cursor - I updated my Startup.as file to reflect changes (take a look at createCustomCursor function) - the cursor still isn't working, and the sprite that is supposed to appear is still not appearing.
Also - just in case you want to see where AvatarEnemy is coming from:
AvatarEnemy.as
package Classes
{
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;
public class AvatarEnemy extends Sprite
{
[Embed(source='Enemypic.png')]
private static var Enemypic:Class;
private var texture:Texture = Texture.fromBitmap(new Enemypic());
private var image:Image = new Image(texture);
public function AvatarEnemy()
{
image.x = 0;
image.y = 200;
addChild(image);
}
}
}
UPDATE
I resolved why the sprite was disappearing - I needed to put the starling initialize code (mStarling.start() and mStarling = new Starling(Game, stage);) above all the stage.something lines. I edited code to reflect what I did in Startup.as. I still need help with the cursor though.
I have been practicing with your code and it all seems fine, but ive been using a different image, so that must be the problem.
Make sure the size of the image is below 32x32, thats the max size of an cursor image, otherwise the OS won't accept it.

AS3 - What is the best, or recommended way of wrapping Bitmaps into Sprite containers?

I'm learning AS3 using FlashDevelop IDE and Flex to compile.
I add images by creating a Bitmap class and then embedding a .png in the code, example:
Enemy.as
package enemies
{
import flash.display.Bitmap;
import flash.events.MouseEvent
[Embed(source="../../assets/gardengnome.png")]
public class Enemy extends Bitmap
{
public function Enemy()
{
trace("enemy constructed");
}
}
}
I have learned that in order to be able to handle MouseEvent I need to put this Bitmap into a Sprite container.
Now, not knowing any better this is the way I have done it:
I create a new variable to hold enemyContainer in Main.as and add it to the stage:
package
{
import enemies.Enemy;
import enemies.EnemyContainer;
import flash.display.Sprite;
import Player.Player;
public class Main extends Sprite
{
public var enemyContainer:EnemyContainer = new EnemyContainer();
public function Main():void
{
addChild(enemyContainer);
}
}
}
And then the EnemyContainer class calls the Enemy Bitmap that holds the graphic and adds it to itself as a child:
package enemies
{
import flash.display.Sprite;
import flash.events.MouseEvent
public class EnemyContainer extends Sprite
{
private var enemy:Enemy = new Enemy();
public function EnemyContainer()
{
trace("enemyContainer constructed");
addChild(enemy);
addEventListener(MouseEvent.CLICK, handleClick);
}
private function handleClick(e:MouseEvent):void
{
trace("Clicked Enemy");
}
}
}
I don't have enough experience yet to see any problems doing it this way. I can change the graphic in the Enemy Bitmap class without having to deal with anything else, and Main.as handles positioning of the EnemyContainer.
However, if there is a recommended, or more efficient way to handle this I'd like to learn it now before I get into a habit. Is there a better way to do this?
Thanks for any advice!
In your example it looks like you are creating 3 classes to implement the bitmap inside a sprite:
Enemy extends Bitmap
EnemyContainer extends Sprite
Main extends Sprite
Your method is totally valid, and you will not have a problem with it. In fact, there is no one best way to do this. What is best depends on the context. However, you may find it more easy to manage if you implement Enemy as part of EnemyContainer so that you have only two classes:
Main
EnemyContainer
In EmemyContainer you make a private class for the enemy bitmap like so:
package enemies
{
import flash.display.Sprite;
import flash.events.MouseEvent
public class EnemyContainer extends Sprite
{
[Embed(source="../../assets/gardengnome.png")]
private EnemyBitmap:Class;
public function EnemyContainer()
{
trace("enemyContainer constructed");
addChild(new EnemyBitmap()); // Creates a new instance of your bitmap class.
addEventListener(MouseEvent.CLICK, handleClick);
}
private function handleClick(e:MouseEvent):void
{
trace("Clicked Enemy");
}
}
}
You may additionally find it helpful to use the mouseChildren and buttonMode property of the Sprite, but that will depend on how you want the mouse interaction to work.
== In reply To Comments ==
To position the bitmap on registration center:
var temp:Bitmap = new EnemyBitmap() as Bitmap;
temp.x = -temp.width/2;
temp.y = -temp.height/2;
addChild(temp);
What you are doing is OK
If you want less classes you could also embed the bitmap in your container class
public class Enemy extends Sprite
{
[Embed(source="../../assets/gardengnome.png")]
private var assetClass:Class;
private var asset:Bitmap;
public function Enemy()
{
//init the class
//do this in a seperate method, because the constructor is executed by a just in time compiler. Means less code better performance.
init();
}
private function init():void
{
asset = new assetClass();
addChild(asset);
}
}
A noble effort, but try this instead as a best practice:
public class Enemy extends Sprite
{
public function Enemy()
{
var bmp = new EnemyBitmap(); // what is currently your Enemy class should be EnemyBitmap
addChild(bmp);
}
}
This technique is called "implementation" where the desired class (Bitmap) is loaded into a container instead of the container extending the Bitmap class itself ("inheritance").

gotoAndPlay in embedded movieclip

I am working on an Air 2.6 project with an embedded SWF.
I am using the following embed code:
[Embed(source = "../../assets/click_feedback.swf", symbol="sub_circle")]
[Bindable]
public static var click_feedback:Class;
And the following code to get an instance of the click_feedback class:
private var cfb:MovieClip = new Assets.click_feedback() as MovieClip
The problems are:
The asset sub_circle has a frame labeled 'respond'. However, it just plays non stop whether or not the label is called with gotoAndPlay.
And, at the end of the animation, there is an Event.COMPLETE called, which is not being picked-up by my code.
I have tested the sub_circle asset in CS5 where I built it, and, in that environment it does not animate until 'respond' is called, and the event it triggers can be heard by my script.
Is this the correct way to handle embedded assets from an SWF?
Embedding of separate symbols from swf in general isn't a good idea:
It slows the compilation of your project, because of compiler must transcode swf format.
Embedding delete ALL AS code from timeline (I didn't check, but may be it remove labels as well, and it can be the reason of your issue).
I recommend embed the hole swf file and loads its bytes in runtime, a little more code to handle the async loading, but much more freedom and flexibility in use:
you safe all as3 code
you can choose ApplicationDomain where to load classes
you can easily switching from Embedding to runtime swf loading by url at any time, if you want to separate the as code and art/sounds assets.
organize your assets to library with the AssetsManager (with api like load(ByteArray or URL), getSkin(name):DisplyObject)
Code example adapted for your assets:
package
{
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
[SWF(width="800", height="200", backgroundColor="0x8B8B8B")]
public class astest extends Sprite
{
[Embed(source="../../assets/click_feedback.swf", mimeType="application/octet-stream")]
private static const common_art:Class;
private var loader:Loader;
private var domain:ApplicationDomain = ApplicationDomain.currentDomain;
public function astest()
{
init();
}
public function init():void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
var loaderContext:LoaderContext = new LoaderContext(false, domain);
loaderContext.allowCodeImport = true;
loader.loadBytes(new common_art() as ByteArray, loaderContext);
}
private function onLoaded(event:Event):void
{
var clip:MovieClip = getSkin("sub_circle") as MovieClip;
addChild(clip);
}
private function getSkin(name:String):DisplayObject
{
if(domain.hasDefinition(name))
{
var clazz:Class = domain.getDefinition(name) as Class;
return new clazz() as DisplayObject;
}
return null;
}
}
}

as3 Adobe Air access iPad device camera

Is there a way you can use as3 to access iPad's camera?
I mean start the Camera within the app itself
have ability take a shoot and save the image to byteArray and applying the image to the background or doing some manipulation
I have done some research most of them just showing how to access the android devices.
Thanks for any suggestion or help.
Yes, you can absolutely do this. The beauty of Flash is that the code to do it is the same that you would use on Android or a PC.
Literally, you can do this to connect the camera to a Video object:
var camera:Camera = Camera.getCamera();
var video=new Video();
video.attachCamera(camera);
this.addChild(video); // 'this' would be a Sprite or UIComponent, etc...
There's a lot more to do if you want to do something useful, but it's fairly straight forward once you get started :)
bluebill1049, I'm not certain from the thread if you got what you were looking for, but I did see your request for the whole class. I found the same information (that Jason Sturges posted in his answer) in this post.
take photo using Adobe Builder (flex) for iOS
Unlike his reply here, his reply to that post had had a link to a great tutorial on building a mobile app and it was from that tutorial that this code was lifted/quoted. It requires an event class (event.CameraEvent - only a few lines) that's contained in that project/tutorial so it's important to be able to go back to the source, as it were. That source is located here:
http://devgirl.org/files/RIAUnleashed/
My thanks to Jason. Just so you don't have to dig, here's the event class that's missing from the quote:
package events
{
import flash.events.Event;
import flash.filesystem.File;
public class CameraEvent extends Event
{
public static const FILE_READY:String = "fileReady";
public var file:File;
public function CameraEvent(type:String, file:File=null, bubbles:Boolean = true, cancelable:Boolean = true)
{
super(type, bubbles, cancelable);
this.file = file;
}
}
}
Hope that helps!
Using the loader is not the only way to access the image bytes on iOS. It turns out the data is already in JPEG format to begin with, so encoding it again is not necessary.
Just do a mediaPromise.open() to get at the bytes and save them directly instead.
XpenseIt example code offers this camera implementation:
Class: CameraUtil:
package utils
{
import events.CameraEvent;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MediaEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.media.CameraRoll;
import flash.media.CameraUI;
import flash.media.MediaPromise;
import flash.media.MediaType;
import flash.utils.ByteArray;
import mx.events.DynamicEvent;
import mx.graphics.codec.JPEGEncoder;
[Event(name = "fileReady", type = "events.CameraEvent")]
public class CameraUtil extends EventDispatcher
{
protected var camera:CameraUI;
protected var loader:Loader;
public var file:File;
public function CameraUtil()
{
if (CameraUI.isSupported)
{
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, mediaEventComplete);
}
}
public function takePicture():void
{
if (camera)
camera.launch(MediaType.IMAGE);
}
protected function mediaEventComplete(event:MediaEvent):void
{
var mediaPromise:MediaPromise = event.data;
if (mediaPromise.file == null)
{
// For iOS we need to load with a Loader first
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleted);
loader.loadFilePromise(mediaPromise);
return;
}
else
{
// Android we can just dispatch the event that it's complete
file = new File(mediaPromise.file.url);
dispatchEvent(new CameraEvent(CameraEvent.FILE_READY, file));
}
}
protected function loaderCompleted(event:Event):void
{
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
if (CameraRoll.supportsAddBitmapData)
{
var bitmapData:BitmapData = new BitmapData(loaderInfo.width, loaderInfo.height);
bitmapData.draw(loaderInfo.loader);
file = File.applicationStorageDirectory.resolvePath("receipt" + new Date().time + ".jpg");
var stream:FileStream = new FileStream()
stream.open(file, FileMode.WRITE);
var j:JPEGEncoder = new JPEGEncoder();
var bytes:ByteArray = j.encode(bitmapData);
stream.writeBytes(bytes, 0, bytes.bytesAvailable);
stream.close();
trace(file.url);
dispatchEvent(new CameraEvent(CameraEvent.FILE_READY, file));
}
}
}
}