ActionScript Read Sprite's Graphics? - actionscript-3

how can i retrieve the set graphics properties of a sprite? for example, in the code below, i'd like to get the color of the sprite, also it's rounded corners setting and other graphics attributes.
var sp:Sprite = new Sprite();
sp.graphics.beginFill(0xFF0000, 0.75);
sp.graphics.drawRoundRect(0, 0, 300, 50, 10, 10);
sp.graphics.endFill();
addChild(sp);
trace(sp.graphics.color); //pseudo trace - this doesn't work

I am almost certain this is not possible. However, there are certainly other ways to do this. What about having a valueObject for each property that stores the values used. Then you could have a GraphicalDisplayObject that you either inherit from or use via composition. For instance:
package {
class FillVO extends Object {
public var fill_color:Number = 0xFF0000;
public var fill_opacity:Number = 0.75;
}
}
package {
import FillVO;
class GraphicalDisplayObject extends Sprite {
public var fill_vo:FillVO;
public function beginFill($vo:FillVO) {
graphics.beginFill($vo.fill_color, $vo.fill_opacity);
}
...
}
}
package {
import GraphicalDisplayObject;
class ObjectWithGraphicalProperties extends Sprite {
public var gfx:GraphicalDisplayObject;
public function ObjectWithGraphicalProperties() {
gfx = new GraphicalDisplayObject();
addChild(gfx);
}
public function beginFill($color:Number, $opactity:Number) {
var fill_vo:FillVO = new FillVO();
fill_vo.fill_color = $color;
fill_vo.fill_opacity = $opacity;
gfx.beginFill(fill_vo);
}
...
}
}
Then to use it...
var obj:ObjectWithGraphicalProperties = new ObjectWithGraphicalProperties();
addChild(obj);
obj.beginFill(0xffff00, .2);
...
...
trace(obj.gfx.fill_vo.fill_color);
This is obviously via composition, and you would need to write the additional proxied methods and corresponding valueObjects... but it should work.

Related

Trying to essentially push details through classes

I am working on a game that creates three circles" red, green, and blue who have 3,2,1 health respectively. They get removed from the stage after their health reaches 0 and it is decremented by 1 per click. I am using a Main.mxml file then I have a Target.as file, as well as RedTarget.as, GreenTarget.as, and BlueTarget.as. My question is that I would like to set everything up in my Target.as file, then push the details such as color, health, and if they are dead or not through those functions. I am having trouble doing that though because I am not sure what I would need in the Target.as and then what I would need to code in each of the colored target files.
Here is my Target.as file:
package com.multiClicker {
//import the needed classes
import flash.display.Shape;
import flash.events.MouseEvent;
import spark.components.Image;
public class Target extends Image {
public function Target() {
//add event listeners
this.addEventListener(MouseEvent.CLICK, onClick);
}
//sets the hp of the target
public function hp():Number {
return hp;
}
//get function that returns false if alpha is <= 0
public function dead():Boolean {
if(alpha <= 0){
return false;
}
return true;
}
//subtracts one from targets HP when clicked
public function onClick(e:MouseEvent = null):void {
//subtracts one from hp each click
hp --;
if(hp <=0) {
this.addEventListener(onEnterFrame);
}
}
//subtracts .1 from the classes alpha
public function onEnterFrame():void{
this.alpha =- .1;
}
//draws the target
public function drawTarget(color):void {
var circle:Shape = new Shape();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(0,0,30);
}
}
}
and then my RedTarget.as file, which is the same as blue and green, except for that they are labeled as such in the variables:
package com.multiClicker {
import flash.events.MouseEvent;
public class RedTarget extends Target{
private var redHP:Number = 3;
private var redDead:Boolean = false;
private var redColor:String = "red";
public function RedTarget()
{
redHP = hp;
redDead = dead;
redColor = color;
//include the super function
super();
}
//subtracts one from targets HP when clicked
override public function onClick(e:MouseEvent=null):void {
super.onClick(e);
//push all to super
}
}
}
Any help on the issue would be great. I have been trying to figure it out throughout the day but have not figured it out.
Are you just asking how to pass variables in when you create the Target object?
public class Target extends Image {
public function Target(hp:Number, dead:Boolean, color:String) {
this.hp = hp;
this.dead = dead;
this.color = color;
}
}
Then you instantiate each target like this:
var redTarget:Target = new Target(3, false, "red");
var greenTarget:Target = new Target(2, false, "green");
etc...

Unable to have Flash Component (SWC) access the library in live preview

I am building a set of Flash components with the ability to replace the skin of the component with another one in the library.
Currently, I am able to access the library after running the application, but not during live preview and I'd like to know if it is possible for the component to access the library while running in live preview mode (the mode where you can drag the component around the stage and change its properties in the Component Parameters window)
Here is a simplified code that just looks to see if there is a symbol of the name specified and than instantiates it and adds it as a child.
package
{
import fl.core.UIComponent;
import flash.display.MovieClip;
import flash.system.ApplicationDomain;
/**
* ...
* #author Roy Lazarovich
*/
public class CompTest extends UIComponent
{
private var customfile :String;
public function CompTest()
{
}
override protected function configUI():void
{
}
override protected function draw():void
{
super.draw();
}
private function setCustomFile():void
{
if (ApplicationDomain.currentDomain.hasDefinition(customfile))
{
var c:Class = Class(ApplicationDomain.currentDomain.getDefinition(customfile));
var mc:MovieClip = new c();
addChild(mc);
}
}
[Inspectable(name = "_Custom File", defaultValue = "")]
public function set _customfile(value:String):void
{
customfile = value;
setCustomFile();
drawNow();
}
}
}
Thanks!
I'm not entirely sure what you have already tried to fix this situation. But hopefully this might help.
Right click on the MovieClip in your library, select Linkage and give it a class name, ie. MyThing.
In your code,
newMyThing = new MyThing();
this.addChild(newMyThing);
trace("tada!");
Hope that helps or gets you closer to a solution.
This works for me in LivePreview, as long as I apply it in configUI, and not draw:
public class EditableBitmap extends UIComponent
{
protected var placeholder:String = "None";
protected var bitmap:Bitmap;
protected var scale:Number = 1;
[Inspectable(name = "Placeholder", type = String, defaultValue="None")]
public function set Placeholder($value:String):void
{
placeholder = $value;
configUI();
}
public function get Placeholder():String { return placeholder; }
public function EditableBitmap()
{
//Console.Debug("NEW EditableBitmap");
super();
}
override protected function configUI():void
{
//Console.Debug("EditableBitmap configUI: " + width);
if (!isNaN(width))
{
wDim = width;
hDim = height;
graphics.clear();
graphics.beginFill(0x000000, 0.1);
graphics.drawRect(0, 0, wDim, hDim);
}
if (placeholder != "None" && placeholder != "")
{
var asset:Class = getDefinitionByName(placeholder) as Class;
var data:BitmapData = new asset() as BitmapData;
bitmap = new Bitmap(data);
}
super.configUI();
}
override protected function draw():void
{
if (bitmap)
{
addChild(bitmap);
bitmap.x = off_x * scale;
bitmap.y = off_y * scale;
bitmap.scaleX = bitmap.scaleY = scale;
}
}
}
NOTE: When I'm working on the FLA where I am editing the component, the bitmap is only displayed from the library inconsistenty. Sometimes it works, sometimes it doesn't. But when I export the SWC and then import the component to another movie, it works every time, in LivePreview as well as at runtime.
UPDATE
It seems this doesn't work in CS6 unless the symbol is already embedded in the component .SWC I wanted to see if I could trick it, by embedding one image in the SWC and then replacing it with another of the same name in the destination file. This didn't work, but it did point me to how you can do this:
So, it's a bit laborious, but you could work around this by:
1) Creating a dummy asset for each property in the component SWC.
2) Overriding this using a custom class in the file where you deploy the component
... all of which may be more trouble than it's worth, but which should provide a solution to the problem.

AS3 Error 1000 on Vector

So I've never worked with Vectors in Flash before and I wanted to write a little test application with a Vector using a custom object. But I'm getting: error 1000: Ambiguous reference to Vector when I try launching the application. I can't figure out for the life of me what's wrong. I tried not using a custom object and just instantiating a string Vector from an online tutorial and I'm getting the same thing.
Here's what I got:
package
{
import TestPlayer; // The custom player class
import flash.display.MovieClip;
public class Vector extends MovieClip
{
private var array:Array = new Array();
private var vector:Vector.<TestPlayer>;
public function Vector()
{
array[0] = [0, 0, "Bob", false];
array[1] = [1, 0, "Frank", true];
array[2] = [2, 1, "Sarah", true];
Load();
}
private function Load():void
{
var aPlayer:Player = null;
vector = new Vector.<TestPlayer>();
try
{
var numRows:int = array.length;
for (var i = 0; i < numRows; i++)
{
aPlayer = new Player();
aPlayer.playerID = array[i][0];
aPlayer.playerName = array[i][1];
aPlayer.playerTypeID = array[i][2];
aPlayer.hasProgress = array[i][3];
vector.push(aPlayer);
}
}
catch (error:Error) { }
}
}
The custom player class looks like this:
package
{
public class TestPlayer
{
private var _playerID:int;
private var _playerName:String = "";
public function get playerID():int
{
return _playerID;
}
public function set playerID(value:int):void
{
_playerID = value;
}
public function get playerName():String
{
return _playerName;
}
public function set playerName(value:String):void
{
_playerName = value;
}
[...]
}
}
I don't know if it matters, but I'm working in Flash CS5, and I have a blank FLA that imports the class. No other errors so far. Hope you can help. Let me know if you need anymore info, thanks.
The ambiguous reference is because you've got a naming collision. The class you've written is named "Vector", which it can't distinguish from the top-level class Vector. The fix is simple, avoid naming your classes the same as a pre-existing class.
If both classes belong to separate namespaces, you can reuse class names, as long as you use thier fully-qualified name whenever you call the class.
Assume you have a class:
package foo.bar
{
class MovieClip
...
}
You could instantiate both types of MovieClips as follows:
flashMovieClip = new flash.display.MovieClip();
myMovieClip = new foo.bar.MovieClip();
Unfortunately, both your Vector and the flash Vector exist in the top-level namespace, so (AFAIK) there's no way of removing the ambiguity without renaming your class. For simplicity sake, avoid naming collisions and you should be golden.

ActionScript - Global Custom Events?

up until now, the way i've been needing to handle my own custom events is by adding an event listener to the object that was dispatching the custom event. while this method of event handling works just fine, i've come to the point where i would like my custom events to be globally accessible, where the listening object does not need to be the same object that is dispatching the event.
in this example, my main Controller class is instantiating and adding to the display list 2 sprite classes: Square and Triangle. the 4th and final class is a custom event called ColorChangeEvent.
i'm attempting to dispatch a new ColorChangeEvent from the Square class, which uses a timer to dispatch a new random color once every second, while Triangle will listen for the dispatched event and change its fill color to the color that was dispatched by Square.
Controller.as:
package
{
import flash.display.Sprite;
public class Controller extends Sprite
{
public function Controller()
{
var sq:Square = new Square();
sq.x = sq.y = 100;
var tr:Triangle = new Triangle();
tr.x = tr.y = 250;
addChild(sq);
addChild(tr);
}
}
}
Square.as:
package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Square extends Sprite
{
public function Square()
{
graphics.beginFill(0x999999);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
var myTimer:Timer = new Timer(1000);
myTimer.addEventListener(TimerEvent.TIMER, dispatchNewColor);
myTimer.start();
}
private function dispatchNewColor(evt:TimerEvent):void
{
var randomColor:Number = Math.random() * 0xFFFFFF;
trace("Square Class Dispatched: " + randomColor);
dispatchEvent(new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor));
}
}
}
Triangle.as:
package
{
import flash.display.Sprite;
import flash.geom.ColorTransform;
public class Triangle extends Sprite
{
public function Triangle()
{
graphics.beginFill(0x999999);
graphics.moveTo(0, 0);
graphics.lineTo(100, 50);
graphics.lineTo(-50, 150);
graphics.endFill();
addEventListener(ColorChangeEvent.CHANGE, changeColor);
}
private function changeColor(evt:ColorChangeEvent):void
{
var ct:ColorTransform = new ColorTransform;
ct.color = evt.color;
transform.colorTransform = ct;
trace("Triangle Class Received: " + evt.color);
}
}
}
ColorChangeEvent.as:
package
{
import flash.events.Event;
public class ColorChangeEvent extends Event
{
public static const CHANGE:String = "change";
public var color:Number;
public function ColorChangeEvent(type:String, color:Number)
{
super(type);
this.color = color;
}
override public function clone():Event
{
return new ColorChangeEvent(type, color);
}
}
}
needless to say, this isn't working.
of course, i could add the event listener to the Square instance in the Controller class, who's event handler could pass that value to Triangle via a public function to change the color, but this is exactly the kind of limitation i'm trying to avoid.
it's not always easy to access and pass a value to a class from where the custom event is dispatched, which is why i'm looking for an actual global solution to handling custom events.
I have been using this class for some time now. To use it you would do this in square:
data.EventManager.instance.publish("someName", randomColor);
and then in your Triangle:
data.EventManager.instance.subscribe("someName", handleColorChange);
private function handleColorChange(color:Number):void {
// implementation here
}
You can even pass the ColorChangeEvent instead of just the color.
data.EventManager.instance.publish(ColorChangeEvent.CHANGE, new ColorChangeEvent(ColorChangeEvent.CHANGE, randomColor);
And then
data.EventManager.instance.subscribe(ColorChangeEvent.CHANGE, handleColorChange);
private function handleColorChange(colorChangeEvent:ColorChangeEvent):void {
// implement here
}
I removed a lot of code that is specific to my projects, so I am not 100% it is usable exactly as-is. But, you should be able to modify it to get it working correctly. If not, let me know and I can try to work it out with you.
This class handles additional things that I will not go into, though you are free to explore. Be aware, however, that anything that subscribes for event notification has a strong reference by the EventManager. That means that if you want to destroy something for garbage collection, you need to call EventManager.instance.cancel(ColorChangeEvent.CHANGE, handleColorChange) before the Triangle instances can be collected.
package data {
import flash.utils.*;
public class EventManager extends Object {
private var _subscribers:Dictionary;
private var _calls:Dictionary;
private var _feeds:Dictionary;
private var _requests:Dictionary;
private var _notify:Dictionary;
private var _services:Dictionary;
private static var __instance:EventManager;
public function EventManager() {
if (__instance) {
trace("EventManager is a Singleton class which should only be accessed via getInstance()");
}
_feeds = new Dictionary(true);
_subscribers = new Dictionary(true);
_requests = new Dictionary(true);
_services = new Dictionary(true);
_notify = new Dictionary(true);
}
public function getFeedData($name:String) {
if (_feeds[$name]) {
return _feeds[$name];
}
return undefined;
}
public function unpublish($name:String) {
var _post:* = _feeds[$name];
delete _feeds[$name];
return _post;
}
public function cancel($name:String, $subscriberFunc:Function, ...args): void {
var _cnt:Number;
var _subscriberArray:Array;
if (_subscribers[$name]) {
for (_cnt = 0; _cnt < _subscribers[$name].length; _cnt++) {
if (_subscribers[$name][_cnt] == $subscriberFunc) {
_subscribers[$name].splice(_cnt, 1);
}
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name];
_cnt = _subscriberArray.length;
while (_cnt > 0) {
if (_subscriberArray[_cnt] == $subscriberFunc) {
_subscriberArray.splice(_cnt, 1);
}
_cnt--;
}
}
}
public function subscribe($name:String, $subscriber:Function, ...args): void {
var _funcArray:Array;
var _func:Function;
if (_feeds[$name]) {
$subscriber(_feeds[$name]);
}
if (! _subscribers[$name]) {
_subscribers[$name] = new Array();
}
_subscribers[$name].push($subscriber);
if (_notify[$name]) {
_funcArray = _notify[$name];
for each (_func in _funcArray) {
_func();
}
delete _notify[$name];
}
}
public function request($name:String, $feedFunction:Function): void {
var _requestArray:Array;
var _request:Function;
if (! _feeds[$name]) {
if (! _requests[$name]) {
_requests[$name] = new Array();
}
_requests[$name].push($feedFunction);
} else {
$feedFunction(_feeds[$name]);
}
if (_notify[$name]) {
_requestArray = _notify[$name];
for each (_request in _requestArray) {
_request();
}
delete _notify[$name];
}
}
public function publish($name:String, $data:*, $args:Object = null): void {
var _subscriberArray:Array;
var _func:Function;
var cnt:Number = 0;
_feeds[$name] = $data;
if (_subscribers[$name] != undefined) {
_subscriberArray = _subscribers[$name].slice();
_cnt = 0;
while (_cnt < _subscriberArray.length) {
_func = _subscriberArray[_cnt] as Function;
if ($args) {
_func($data, $args);
}else {
_func($data);
}
_cnt++;
}
}
if (_requests[$name]) {
_subscriberArray = _requests[$name].slice();
delete _requests[$name];
_cnt = 0;
while (_cnt < _subscriberArray.length) {
if (_subscriberArray[_cnt] != null) {
_subscriberArray[_cnt]($data);
}
_cnt++;
}
}
}
public function notify($name:String, $subscriber:Function): void {
if (_requests[$name] || _subscribers[$name]) {
$subscriber();
}else {
if (! _notify[$name]) {
_notify[$name] = new Array();
}
_notify[$name].push($subscriber);
}
}
public static function getInstance(): EventManager {
if (! __instance) {
__instance = new EventManager();
}
return __instance;
}
public static function get instance(): EventManager {
return getInstance();
}
}
}
I got this to work by creating a singleton: EventDispatchSingleton that extends EventDispatcher. It's basically an empty singleton that provides the dispatchEvent and add/removeEventListener methods (these are automatically provided by extending EventDispatcher).
Anywhere I want to dispatch an event I import EventDispatchSingleton and then call EventDispatchSingleton.instance.dispatchEvent(<someEvent>);.
Then, wherever I want to listen to that event, I just import EventDispatchSingleton and call EventDispatchSingleton.instance.addEventListener(eventName, callback);
You should look into event bubbling, specificly I think you will find the Capturing phase of the event propagation useful. Take a read of Event propagation from Adobe LiveDocs. It's in the Flex docs, but it is about AS3 Events.
Also Senocular has a good post on Flash Event Bubbling.

How to override the transform.matrix setter

I have a class which extends the Sprite object in as3. I need to be able to override the transform.matrix setter in this class but haven't been successful in doing so.
I've tried many things, along with creating my own separate class which extends the Transform class and then overrides its set matrix function, and set my transform = new CustomTransform(). Sadly this didn't work.
In code this is what i tried:
public class MyClass extends Sprite
{
public function MyClass()
{
super(); transform = new MyTransform(this);
}
}
class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject)
{
super();
}
override public function set matrix(value:Matrix)
{
super.matrix = value;
customcode();
}
}
All help is greatly appreciated!
This seems to work:
public class MyClass extends Sprite
{
public function MyClass()
{
super();
transform = new MyTransform(this,super.transform);
// i'm drawing a rect just to see the results of scaling
graphics.beginFill(0xff0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
override public function get transform():Transform {
var tmp:Transform;
if(super.transform is MyTransform) {
tmp = super.transform;
} else {
tmp = new MyTransform(this,super.transform);
}
return tmp;
}
override public function set transform(value:Transform):void {
var tmp:Transform;
if(value is MyTransform) {
tmp = value;
} else {
tmp = new MyTransform(this,value);
}
super.transform = tmp;
}
}
public class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
}
Use:
var sp:MyClass = new MyClass();
var mat:Matrix = sp.transform.matrix;
mat.scale(3,3);
trace(sp.transform);
sp.transform.matrix = mat;
addChild(sp);
The problem is that, even if you create and assign your tranform to be of type MyTransform, the getter returns a regular Transform object. There's something weird about how transform objects work in Flash (this is also true for SoundTransform, for instance). There's some kind of cache mechanism implemented in a rather lame way that forces you to reassign the instance if you want to commit your changes.
I mean this pattern:
var t:Transform = mc.transform;
// do something with t
mc.transform = t;
So I think this is related to why your code doesn't work as expected.
To get around this, I'm checking both in the setter and the getter if the trasnform object passed is of type MyTransform. If it is, I use it as is. If it's not, I create a MyTransform object and copy all of the properties from the original Transform. It'd be nice if the Transform class had a clone method, but it doesn't, so I implemented this simple copy mechanism. Not sure if this doesn't mess up with some internal state in Transform (could be the case). I haven't tested it apart from applying a scale, once. You might want to do it, as there could be other side effects I'm not considering. Also, this is probably not the most performant. But I can't think of another way to have your matrix setter called.
Edit
Using a static/global dispatcher is not a good idea except you really need it to be global. Implementing IEventDispatcher, since you can't directly extend EventDispatcher, is what you want.
The code needed for that is a bit verbose, but it's a no-brainer anyway. All you need is having an internal instance of event dispatcher and implement the methods of the interface. In said methods, you forward the parameteres to the actual dispatcher.
public class MyTransform extends Transform implements IEventDispatcher
{
private var _dispatcher:EventDispatcher;
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
_dispatcher = new EventDispatcher(this);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
public function dispatchEvent(event:Event):Boolean {
return _dispatcher.dispatchEvent(event);
}
public function addEventListener(type:String,listener:Function,useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
_dispatcher.addEventListener(type,listener,useCapture,priority,useWeakReference);
}
public function removeEventListener(type:String,listener:Function,useCapture:Boolean = false):void {
_dispatcher.removeEventListener(type,listener,useCapture);
}
public function hasEventListener(type:String):Boolean {
return _dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean {
return _dispatcher.willTrigger(type);
}
}