Controlling anchors/curves in AS3 - actionscript-3

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
}

Related

Swapping the image used for my character, to a seperate image on button click

So first off i'd like to state that i am not very good at AS3, i'm completely self taught so i am sure that there are many things i've done badly, inefficiently or plain wrong and i'm happy for any comments on these if there is things i can improve.
Okay so, this seems like a really simple problem but i don't know the exact code for it and i can't find an example anywhere
what i want to do is to change the image which i am using for my character to a separate image on a button click, then after a certain amount of time for him to go back to the original.
My image is set as a movie clip and i am calling it like so :
public var Smallclock_hero = new Smallclock;
it is worth noting that smallclock has it's own separate class.
is there a way that i can change this image on a mouse click event ?
A simple way would be:
Add a new image on the second frame of the Smallclock movieclip. Have this new image span several frames of the timeline for the duration you want it to appear for.
Place a stop(); action on the first frame of the Smallclock movieclip.
btn.addEventListener(MouseEvent.CLICK, btnChangeCharacterFrame);
function btnChangeCharacterFrame(e:MouseEvent)
{
Smallclock_hero.gotoAndPlay(2);
// Or use a frame label
// Smallclock_hero.gotoAndPlay("jump");
}
This can be achieved relatively simply using the visible property and setTimeout. When you click on your MovieClip you'll want to make the old image invisible and the new image visible:
oldImage.visible = false;
newImage.visible = true;
Then you'll want to use setTimeout to change the visible properties back to:
oldImage.visible = true;
newImage.visible = false;
setTimeout can run a function after a specified number of milliseconds. Here is an example of this technique on wonderfl : http://wonderfl.net/c/acxl
private var imageA:ImageA = new ImageA();
private var imageB:ImageB = new ImageB();
private var revertId:int;
public function Main() {
imageB.visible = false;
addChild(imageA);
addChild(imageB);
addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void{
clearTimeout(revertId);
revertId = setTimeout(revert, 1000);
imageB.visible = true;
imageA.visible = false;
}
private function revert():void{
imageB.visible = false;
imageA.visible = true;
}

The most lightweight button implementation in actionscript 3.0 [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
Since button is one of the most popular GUI components this question becomes hot when we talk about memory usage. Especially when you have tons of buttons in your application.
So how you can implement a button that uses minimum CPU and memory resources and yes, acts like normal button with mouse up, down and hand pointer behavior implemented. Label text is also required.
If you want to have tonnes of buttons with text and graphics, using the least amount of RAM and processing power, you should be using Bitmaps.
This gets a lot more complicated and involves your own preparation of the following:
A font in the form of a sprite sheet.
Classes that manage rendering text onto a Bitmap using that sprite sheet.
Bitmaps don't respond to MouseEvents, so you'll need to architect your own system for managing mouse input on the Bitmaps.
Firstly, lets take a look at the base memory consumption for some of the DisplayObjects that you would think we would be best using. This is our testing method:
function ram(type:Class):void
{
trace(getSize(new type()));
}
And this is the test:
ram(Sprite); // 408
ram(Shape); // 236
ram(TextField); // 1316
In your case, drawing 1000 buttons would result in over 1,724,000 bytes of memory being used.
Now let's look at what we will be using:
1x Bitmap acting as a Canvas that holds all buttons: 236 bytes.
1x BitmapData representing the initial state of every button.
1x BitmapData representing the rollover state of every button.
1x BitmapData storing our text as a sprite sheet for use by all buttons.
Each BitmapData will be quite large in memory consumption, and varies greatly depending on its content. But the trick here is that we only use one and refer to its content for every button that we want to draw.
I've set up a small amount of code to get you started. You still need to implement a click manager which loops over all the buttons and works out which is most relevant to trigger a click, as well as rendering the text on the buttons.
Here's the Button class:
public class BitmapButton
{
private var _text:String;
private var _position:Point = new Point();
public function BitmapButton(text:String)
{
_text = text;
}
public function render(canvas:BitmapData, font:BitmapData, state:BitmapData):void
{
canvas.copyPixels(state, state.rect, _position);
// Use font argument to render text.
// For you to implement.
}
public function get position():Point{ return _position; }
}
And here's the class that will manage rendering those buttons:
public class ButtonCanvas extends Bitmap
{
private var _fontSprite:BitmapData;
private var _baseState:BitmapData = new BitmapData(100, 30, false, 0xFF0000);
private var _overState:BitmapData = new BitmapData(100, 30, false, 0x00FF00);
private var _buttons:Vector.<BitmapButton> = new <BitmapButton>[];
private var _checkRect:Rectangle = new Rectangle();
public function ButtonCanvas(width:int, height:int)
{
bitmapData = new BitmapData(width, height, true, 0x00000000);
// Replace with actual loaded sprite sheet.
_fontSprite = new BitmapData(1, 1);
}
public function add(button:BitmapButton):void
{
_buttons.push(button);
}
public function render():void
{
if(stage === null) return;
bitmapData.lock();
for each(var i:BitmapButton in _buttons)
{
_checkRect.x = i.position.x;
_checkRect.y = i.position.y;
_checkRect.width = _baseState.width;
_checkRect.height = _baseState.height;
if(_checkRect.contains(mouseX, mouseY))
{
// Use roll over style.
// Need to implement depth check so you can't roll over buttons
// that fall behind others.
i.render(bitmapData, _fontSprite, _overState);
}
else
{
i.render(bitmapData, _fontSprite, _baseState);
}
}
bitmapData.unlock();
}
public function get buttons():Vector.<BitmapButton>{ return _buttons; }
}
And a small test:
var canvas:ButtonCanvas = new ButtonCanvas(stage.stageWidth, stage.stageHeight);
addChild(canvas);
for(var i:int = 0; i < 20; i++)
{
var button:BitmapButton = new BitmapButton("Hello");
button.position.x = Math.random() * stage.stageWidth;
button.position.y = Math.random() * stage.stageHeight;
canvas.add(button);
}
stage.addEventListener(MouseEvent.MOUSE_MOVE, update);
function update(e:MouseEvent):void
{
canvas.render();
}
canvas.render();
Now that you've read all of that, I'll point out that it's really unlikely you need to anywhere near this extreme, unless you have some type of game that revolves around buttons and buttons are actually particles that get generated every frame in the 100's. Using a standard Sprite + TextField is perfectly fine in almost all cases.
One of the traditional patterns is using Sprite + TextField
Adobe recommends using Shape instead of Sprite (when it makes sense):
a Sprite object is a display object container, whereas a Shape object is not. For this reason, Shape objects consume less memory than Sprite objects that contain the same graphics.
It would be great to use Shape, and we can do it, but we can not add TextField on it.
Now lets look at TextField inheritance chain:
TextField: InteractiveObject -> DisplayObject -> EventDispatcher -> Object
We can observe that a TextField object is much lighter than a Sprite object - wrong. Using only TextField will be lighter than using TextField + Sprite. I came up with this decision:
import flash.events.MouseEvent;
import flash.filters.BevelFilter;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
public class Button extends TextField
{
private static const MOUSE_UP:Array =
[new BevelFilter(2, 45, 0xEEEEEE, .7, 0x444444, .7, 1, 1)];
private static const MOUSE_DOWN:Array =
[new BevelFilter(2, 225, 0xEEEEEE, .7, 0x444444, .7, 1, 1)];
private static const TEXT_FORMAT:TextFormat =
new TextFormat('Verdana', 12, 0xDDDDDD,
null, null, null, null, null, 'center');
public function Button(label:String, color:int = 0x166488)
{
width = 80;
height = 20;
background = true;
backgroundColor = color;
selectable = false;
defaultTextFormat = TEXT_FORMAT;
text = label;
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(MouseEvent.ROLL_OVER, onMouseRollOver);
addEventListener(MouseEvent.ROLL_OUT, onMouseRollOut);
onMouseUp();
}
private function onMouseRollOut(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.AUTO;
}
private function onMouseRollOver(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.BUTTON;
}
private function onMouseDown(e:MouseEvent):void
{
filters = MOUSE_DOWN;
}
private function onMouseUp(e:MouseEvent = null):void
{
filters = MOUSE_UP;
}
//kill method
}
This code draws nice lightweight button BUT I can not adjust vertical position of a text label so height of this button depends of font-size. Another issue is that I cann't move the text label a bit down-right when somebody clicks it.
Any ideas will be appreciated.
We can observe that a TextField object is much lighter than a Sprite object. That's completely incorrect. A Sprite uses 408 bytes in memory whereas a TextField uses 1316
Yes TextField will consume way more memory.
I would create the label text in a graphics program and create a sprite menu class.
TextField is not really lightweight but a pretty powerful class. If you want user input then TextField is the way to go.
Avoid any of the buttons built into the Flash library and just start simple and build functionality on the sprite class.
If you really want to optimize your interface, reduce the event handlers and any sort of transparency. This might just be good Flash advice in general but often overlooked.
Make a function that gets called every frame, tick();, think();, update(); something like this. Add a single event handler to the main class and within that call your update() function within the menu elements.
Adding a dozen event handlers to your menu elements is not only cumbersome but unsightly.
I would venture to say a sprite with the buttonMode property set to true for the "hand pointer" and then functions to handle the ROLL_OVER and ROLL_OUT MouseEvents.

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;
}

ActionScript - Dictionaries Best For Adding Bool Properties To Non-Dynamic Objects?

i'm currently using a dictionary to associate a boolean to my (non-dynamic) sprites, but i would like to know if there is a smarter way of doing this? i could just use MovieClips to assign my properties instead of Sprites since MovieClips are dynamic, but i will not be using any of the MovieClip properties or functions so it comes down to a best practice issue.
basically i want to create a state boolean property on my sprites - they are either on or off so my boolean variable is called isOn.
var mySprite:Sprite = new Sprite();
var isOn:Boolean = false;
var dict:Dictionary = new Dictionar();
dict[mySprite] = isOn;
then i will poll my sprite to check its "isOn" property. if it's on, i will turn it off - or set it to false.
if (dict[mySprite] == true)
{
dict[mySprite] = false;
}
this is the first time i'm actually using dictionaries, so please correct me if i'm using it wrong. and, of course, my original question stands: is this the best way of adding a boolean property to a non-dynamic object?
Can't you just write your own Sprite that has an isOn property? That seems like a much simpler way to achieve what you want, without using a MovieClip.
isOn could be a public var or a pair of getter/setter if you want to perform some logic when reading/writting it.
public class MySprite extends Sprite {
private var _isOn:Boolean;
public function get isOn():Boolean {
return _isOn;
}
public function set isOn(v:Boolean):void {
_isOn = v;
}
}
And then:
var mySprite:MySprite = new MySprite();
mySprite.isOn = false;
// at some later point...
if (mySprite.isOn)
{
mySprite.isOn = false;
}

How to read data from shape/graphics object

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;