How to read data from shape/graphics object - actionscript-3

I am wondering if it is possible to get the data that is stored in a shape/graphics object in flash using actionscript 3?
In my project I would like to be able to draw a shape and then read all the points in that shape into my script. The reason for that is that I need go generate lines from those points that I later can use to check if my characters velocity intersects with any of them.

You can read all parts of Shape.
New feature added to Flash Player 11.6 and AIR 3.6:
flash.display.Grapics.readGraphicsData()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html#readGraphicsData%28%29
Example:
var s :Shape = new Shape();
s.graphics.lineStyle(2, 0xFF0000);
s.graphics.drawCircle(0, 0, 50)
var gd:Vector.<IGraphicsData> = s.graphics.readGraphicsData(false);
var copy_of_s :Shape = new Shape();
copy_of_s.graphics.drawGraphicsData(gd);
addChild(copy_of_s);
To use the new version, you have to update playerglobal.swc
http://www.adobe.com/support/flashplayer/downloads.html

You cannot read the shape info once it's drawn. But if you are drawing it, you can store the info at the time of drawing itself and use it later.

OK, looks like its not possible then, to bad.
I am doing a 2d topdown racing game, and I wanted to generate lines along the walls of the track and check the players velocity against thouse lines. That way I would be able to implement some basic collision response by reflection the players velocity around the normal of the line that it collides with and make it bounce off walls. Does anyone have any good ideas about how to get the same type of collision behavior without the actual lines?
Is it possible to overload the graphics object in flash somehow so that when I draw something it is recorded? Or does the flash IDE not use the Graphics drawing api?
Regards

You can not instantiate or subclass Graphics class. But you can use you own custom graphics class.public class CustomGraphics extends Object{
private static const CLEAR = -1;
private static const MOVETO = 0;
private static const LINETO = 1;
...
...
private var _graphics:Graphics;
private var _actions:Array;
public function CustomGraphics(g:Graphics):void {
_graphics = g;
_actions = new Array();
}
private function recordAction(obj:Object):void {
if (obj.action == -1) {
_actions = null;
_actions = new Array();
return;
}
_actions.push(obj);
}
public function moveTo(x:number, y:Number):void {
g.moveTo(x, y);
recordAction({action:MOVETO, X:x, Y:y});
}
...
...
public function get actions():Array {
return _actions;
}
} Now whenever you want to draw something then you can use CustomGraphics. var cg:CustomGraphics = new CustomGraphics(someDisplacyObject.graphics);
cg.moveTo(0, 0);
cg.drawRect(0, 0, 10,, 200);
...
a:Array = cg.actions;

Related

AS3 tracking and using coordinates of a rotated object

How does one track and use the coordinates of an object that is rotated on initialization?
Let's say I have a sword that is put on the stage in Main init(); and rotated (adjusted) so that it would look ok together with the character perspective. In another class however, I am making the sword rotate some more on a keypress timer event so to create a 'swing' animation.
All this is done through flashdevelop. I only used CS6 to create the symbols. And as this 'swing' is happening, I want to add another symbol onto the tip of the sword which is a collision point object. It's being added to the stage when the swing starts and removed after every swing. I want this object to follow the very tip of the sword, yet it seems like I can only achieve that it follows the coordinates of the original sword object, as if I hadn't initially modified the rotation of the said sword. I tried to implement GlobalToLocal() and LocalToGlobal() methods, but I don't think I fully understand what is happening with that.
I hope I'm being clear enough of what I'm trying to do. Thank you. This is the relevant code in question. The code is as was before I tried the two mentioned methods and the issue currently is exactly as described before that. Do I want any of those methods or am I just doing something else wrong?
Main initialization:
sword = new Sword();
sword.x = 53;
sword.y = 90;
addChild(sword);
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
Coll_Point = new coll_point();
The class that deals with the swing has a method like this:
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Main.MazeNr1.addChild(Main.Coll_Point);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
Edit:
A more holistic version of the code:
public class Main extends MovieClip
{
public static var from_point:Point = null;
public static var to_point:Point = new Point();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Puts everything on the stage here.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
PlayerInst = new Dorf();
PlayerInst.x = 45;
PlayerInst.y = 51;
addChild(PlayerInst);
sword = new Sword();
sword.x = 53;
sword.y = 90;
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
from_point = new Point (Main.sword.width, Main.sword.height);
to_point = sword.localToGlobal(from_point);
addChild(sword);
swordBD = new BitmapData(32, 32, true, 0x0000000000);
swordBD.draw(sword);
Coll_Point = new coll_point();
Coll_PointBD = new BitmapData(2, 2, true, 0x0000000000);
Coll_PointBD.draw(Coll_Point);
}
}
This is how the Main looks like and literally every single object instantiation is added onto the stage this way. Including collision points, background, characters, gradient fills of line of sight radius, etc. And the relevant symbol class goes somewhat like this:
public class Creature extends MovieClip
{
protected var Swing:Boolean;
private var SwingTimer:Timer = new Timer (5, 0);
private var SwingBckTimer:Timer = new Timer (150, 1);
// Constructor.
public function Creature()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Initializer.
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
SwingTimer.addEventListener(TimerEvent.TIMER, SwingTime);
SwingBckTimer.addEventListener(TimerEvent.TIMER, SwingBack);
}
private function SwingAction():void
{
if (Swing == true)
{
SwingTimer.start();
}
}
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.localToGlobal(Main.from_point).x;
Main.Coll_Point.y = Main.sword.localToGlobal(Main.from_point).y;
Main.sword.addChild(Main.Coll_Point);
trace(Main.Coll_Point.x);
trace(Main.Coll_Point.y);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
private function SwingBack(event:Event):void
{
Main.sword.rotationZ = -150;
Main.sword.rotationX = -15;
//Main.MazeNr1.removeChild(Main.Coll_Point);
}
There is also a rather long update(); function that animates and moves every single object that needs moving.
I think your problem might be in
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Coll_Point expects global coordinates.
The parts + Main.sword.width and + Main.sword.height only work as expected if the sword is not rotated so that height is aligned with the y-axis and width with the x-axis.
You should use localToGlobal() on the position that is local to Main.sword (
Main.sword.width, Main.sword.height) to get the global position that represents the swords rotated tip before you add it as a child.
There are two ways you can approach this (you seem to have somewhat combined both). You can either
Add the Coll_Point as a child to something above the sword in hierarchy (Stage, MazeNr1, ...) and update the position manually every timer callback. You would have to recalculate the position everytime, so take the localToGlobal() from init to your timer function. It won't update if it doesn't get called.
For that you should have this kind of code in the timer callback:
var local:Point = new Point(Main.sword.width, Main.sword.height);
var global:Point = Main.sword.localToGlobal(local);
Main.Coll_Point.x = global.x;
Main.Coll_Point.y = global.y;
Add the point as a child to the sword. This might be a better approach as then the position will be updated automatically. What I did not remember before was that you then give the coordinates in "local" form, so do not use localToGlobal()
Run this once where you create the Collision_Point:
Coll_Point.x = <your x offset>;
Coll_Point.y = <your y offset>;
Main.sword.attachChild(Coll_Point);
Instead of sword height and width you might want to try something like -height and width/2.
Here is a quick (and not the prettiest) picture to demonstrate the problem. Local space is rotated with the object:
The only thing I can imagine to help you with this problem is to have your collision object have the same registration point as the sword. What I mean is that the orientation points should match, and the collision graphic should be moved inside of the sprite so that it matches the position of the top of the sword.
This way you can put the collision object at the very same location of the sword and apply the very same rotation. This way it will move along with the top of the sword and still have hitTest working properly.
I cannot imagine any other way to figure this out as any code will get bounds and positions. But the real thing that matters is the registration point and the top of the sword, which is a graphic thing and cannot be dealt with coding.
I hope you can imagine what I mean - if now, just say and I will provide an image for you :)

Adding ID to a new created object as3

i got a question , i create a new child, a circle but i dont know how can i give it an ID, so i can access it whenever i want, even if i move it , the problem is my function new_sond creates more than 1 object, so i want to give them the ID in the function for example for the 1 object "1" for the 2nd "2" and so on, i dont have any idea how to do it, i tried to search but didnt find anything, the trace(name) won`t be usefull becouse i create more objects with the same name...
here is the code for creating the object :
function new_sond(event:MouseEvent):void
{
if (i<9)
{
i++;
id[i]=i;
var btn:Sprite = new Sprite();
btn.graphics.beginFill(0x0066FF, 1);
btn.graphics.drawCircle(400, 300, 25);
btn.graphics.endFill();
var textField = new TextField();
textField.mouseEnabled=false;
textField.text = i;
textField.width = 10;
textField.height = 17;
textField.x = 395; // center it horizontally
textField.y = 292; // center it vertically
cx[i]=textField.x;
cy[i]=textField.y;
btn.addChild(textField);
this.addChild(btn);
}
}
And this is the code for moving the object :
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownH);
this.addEventListener(MouseEvent.MOUSE_UP, mouseUpH);
function mouseDownH(evt:MouseEvent):void {
var object = evt.target;
object.startDrag();
}
function mouseUpH(evt:MouseEvent):void {
var obj = evt.target;
obj.stopDrag();
}
The question is how do i give an ID to each created object so i can check it even if i move the object.
Thank you very much !!!!
You can set the name property on the Sprite class. This property is inherited from the DIsplayObject class. Here is a summary of the property from the documentation.
The property is a String and you set or retrieve it from its setter/getter implementations in DisplayObject:
public function get name():String
public function set name(value:String):void
This property is part of ActionScript 3.0 and is available in runtime versions starting with AIR 1.0, Flash Player 9, Flash Lite 4 (which means it is available in later version as well).
It can throw an IllegalOperationError though. This is thrown if you attempt to set the property on an object placed on the timeline via the Flash authoring tool.
Here is the example given in the DisplayObject#name property documentation. The example creates two Sprite objects and traces their names when they are clicked.
import flash.display.Sprite;
import flash.events.MouseEvent;
var circle1:Sprite = new Sprite();
circle1.graphics.beginFill(0xFF0000);
circle1.graphics.drawCircle(40, 40, 40);
circle1.name = "circle1";
addChild(circle1);
circle1.addEventListener(MouseEvent.CLICK, traceName);
var circle2:Sprite = new Sprite();
circle2.graphics.beginFill(0x0000FF);
circle2.graphics.drawCircle(140, 40, 40);
circle2.name = "circle2";
addChild(circle2);
circle2.addEventListener(MouseEvent.CLICK, traceName);
function traceName(event:MouseEvent):void {
trace(event.target.name);
}
If this does not work for you, you can always create your own class that is a sub-class of Sprite and add your own properties to track an "id" field for whatever purposes you seek.
Or you could put your objects in an array and rely on their array position as an Id.
I just stumbled on this question and thought it relevant to point out that AS3 also has a built-in utility for generating unique names for Objects
NameUtil.createUniqueName

Making a flash country map

Hello I want to make a flash map of Europe where when you mouse over or out of each country something will happen, like changing the color of the country.
See this image
If I roll over Norway and then Sweden, Sweden will not be set to blue because Norway's bounding box is on top of Sweden, so I have to go a bit lower to roll over Sweden where it does not instersect with Norway.
Is there a better way to achieve the same functionality? Like using Bitmap pixels.
I have a sprite sheet of all cointries in png format. I embed this sprite sheet and than create Sprite AS3 class that uses copyPixels() to copy from the sprite sheet to innet variable BitmapData.
Here is my class
public class Country extends Sprite
{
private var newBitmap:Bitmap;
private var tintColor:Color = new Color();
public function Country(bitmapData:BitmapData)
{
newBitmap = new Bitmap(bitmapData);
tintColor.setTint (0x0000ff, 0.6);
this.useHandCursor = true;
this.buttonMode = true;
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void
{
addChild(newBitmap);
addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
}
private function onMouseOver(e:MouseEvent = null):void
{
newBitmap.transform.colorTransform = tintColor;
}
private function onMouseOut(e:MouseEvent = null):void
{
newBitmap.transform.colorTransform = new ColorTransform();
}
}
bitmapData is created at runtime using copyPixels() like this
var countiresArray:Array = Resources.allCointriesJSON["frames"];
var newX:Number = countiresArray[i]["frame"]["x"];
var newY:Number = countiresArray[i]["frame"]["y"];
var newW:Number = countiresArray[i]["frame"]["w"];
var newH:Number = countiresArray[i]["frame"]["h"];
bitmapData .copyPixels(Resources.allCountries.bitmapData, new Rectangle(newX, newY, newW, newH), new Point(0, 0));
where newX,NewY, newW and newH are taken from exported JSON file that is created when using TexturePacker
Without converting your country images to vectors, you will need to check the alpha values of pixels under the mouse to get accurate interactions. Luckily, someone's already written and open sourced a utility that should do the job: InteractivePNG.
You'll just need to put your bitmaps inside an InteractivePNG instance instead of a Sprite.
I had a similar problem but with an interactive object. The solution was to have a image that acted as a map for the clicks. This is a technique used in OpenGL for detecting clicks on 3d objects and scenes.
For example in your case, you'd have an image with all the countries. This image would not be loaded in the display list. Each country would have an exact different color. for example Sweden would be 0xff0000, Norway 0x00ff00, etc. Let's call this image colored-map.jpg
Then after clicking on the Europe-map.jpg, the one that the user sees, you'd check the mouse coordinates, and then go look in the colored-map.jpg to know the color of the clicked pixel. Then you'd know which country the user clicked, and could overlay the Norway.png.
This technique is way better in performance terms that having lots of PNGs in the display list. At any time you would only have the Europe-map.jpg, maybe 1 PNG of the selected country, and then the colored-map.jpg loaded into a Bitmap object. If you are targeting mobile performance optimization is a must.
Check this function. It's just as an example to get you going... it's not a full implementation.
var countriesArray = [{color:0xff0000, image:"Norway.png"},{color:0x00ff00, image:"Sweden.png"}];
public function checkPoint(mouseX,mouseY):String {
var testPixel:uint = coloredMap.bitmapData.getPixel(mouseX,mouseY);
for (var country in countriesArray) {
if( country.color == testPixel ) {
return country.image;
}
}
return null;
}

Controlling anchors/curves in AS3

I've been looking for a method to control the curves and/or anchors within a path, drawn by usage of curveTo for example.
Basically, when I draw something like this:
thingy.graphics.curveTo(220,100,150,140);
thingy.graphics.curveTo(60,200,50,300);
thingy.graphics.curveTo(40,495,250,500);
thingy.graphics.curveTo(460,495,450,300);
I'd like to dynamically change these curves when I do something in a function (for example dragging an anchor point). In other words, I simply want to update the coordinates of a certain curve, like:
overwriting
thingy.graphics.curveTo(220,100,150,140);
to
thingy.graphics.curveTo(120,100,250,670);
or
thingy.graphics.curveTo(220,100,mouseX,mouseY);
for instance.
While Googling for an answer to my question I've only been able to find long articles with very complex formulas, most of the time not even accompanied by any bit of AS3 code. As I am not very good with maths, I'd prefer a simple answer that just shows a method to do this.
Alternatively, I wouldn't mind controlling curves drawn in the Flash IDE instead, if that's much easier.
Here's how I would approach it.
private var curveVals:Array = [];
private var thingy:Sprite;
public function constructor() {
thingy = new Sprite();
var obj:Object = {cx:220,cy:100,ax:150,ay:140}
curveVals.push(obj);
thingy.graphics.curveTo(220,100,150,140);
obj = {cx:60,cy:200,ax:50,ay:300}
curVals.push(obj);
thingy.graphics.curveTo(60,200,50,300);
}
private function storeVals(cx,cy,ax,ay,index):void {
var obj:Object = {cx:cx,cy:cy,ax:ax,ay:ay};
curveVal[index] = obj;
redraw();
}
private function redraw():void {
thingy.graphics.clear();
// loop through values and redraw
}

AS3/FLEX - how to convert Sprite graphics into bytes

I wrote application that lets people paint together via internet (using Adobe cirrus). Everything works great but when for example I run my application and paint something before my friend connects, he dont see that what I have painted. So I'm looking for the method, that would let me convert my canvas into something(object) that it is possible to send by internet (I cant send whole Sprite, it's not possible to copy its graphics on the friend's application, it's null).
So let's get this clear. The main question is: How to convert graphic's of Sprite into object, that would let me convert it back to Sprite and copy its canvas.
ANSWER:
I used DisplayConverter library from "www.Flextras.com" post with his mod to convert Sprite to BitmapData and then to ByteArray and it works. I couldn't receive BitmapData on the friend's app, but It worked with ByteArray.
Sprite -> BitmapData -> ByteArray;
ByteArray -> BitmapData -> Sprite;
//TO SEND
var bitmapdata:BitmapData = DisplayConverter.spriteToBitmapData(palette);
var bytearr:ByteArray = bitmapdata.getPixels(bitmapdata.rect);
//TO RECEIVE
var bmd:BitmapData = new BitmapData(530,430);
bmd.setPixels(bmd.rect, bytearr);
mysprite.graphics.beginBitmapFill(bmd);
mysprite.graphics.drawRect(0,0,530,430);
mysprite.graphics.endFill();
Hope this will help someone
I think you want to convert your Canvas into a BitMap or BitMapData (and back). A Flex Canvas extends Sprite, so you can use a library like this one. To copy the relevant code, this will convert a Sprite to a BitMap:
public static function spriteToBitmap(sprite:Sprite, smoothing:Boolean = false):Bitmap
{
var bitmapData:BitmapData = new BitmapData(sprite.width, sprite.height, true, 0x00FFFFFF);
bitmapData.draw(sprite);
return new Bitmap(bitmapData, "auto", smoothing);
} // END FUNCTION spriteToBitmap
This will convert a Bit Map to a Sprite:
public static function bitmapToSprite(bitmap:Bitmap, smoothing:Boolean = false):Sprite
{
var sprite:Sprite = new Sprite();
sprite.addChild( new Bitmap(bitmap.bitmapData.clone(), "auto", smoothing) );
return sprite;
} // END FUNCTION bitmapToSprite
In my own development, I have a mod to this library, which allows me to get the BitMapData instead of an actual BitMap. So, this will turn a Sprite into BitMapData:
public static function spriteToBitmapData(sprite:Sprite):BitmapData
{
var bitmapData:BitmapData = new BitmapData(sprite.width, sprite.height, true, 0x00FFFFFF);
bitmapData.draw(sprite);
return bitmapData;
} // END FUNCTION spriteToBitmapData
This will take BitMapData and turn it back into a Sprite:
public static function bitmapDataToSprite(bitmapData:BitmapData, smoothing:Boolean = false):Sprite
{
var sprite:Sprite = new Sprite();
sprite.addChild( new Bitmap(bitmapData.clone(), "auto", smoothing) );
return sprite;
} // END FUNCTION bitmapToSprite
You do want to keep in mind that when converting the BitMap or BitMapData back into a Sprite you will probably not be able to cast it as a Canvas. For information on sending BitMapData to a server; look at this question.
A better approach is, instead of going directly to pixels, have the user gestures create data, then reflect that data as a drawing on your canvas. Transmit the same data to the other user and he/she will get the same drawing.