ActionScript 3 - Tweening rotateAroundExternalPoint - actionscript-3

i'm unsuccessfully trying to rotate a rectangle around an external point while tweening. i'm trying to lock the top of the red rectangle to the line while it tweens from left to right and rotates from 0º to 90º.
alt text http://www.freeimagehosting.net/uploads/0b937c92e6.png
the image above shows 3 states of the tween. state 1 shows the red rectangle at the start of the line with no angle. state 2 shows the red rectangle has tweened half way along the line with an angle of 45º that is also half the total angle of 90º. state 3 shows the final position of the tween where the red rectangle has an angle of 90º and is placed at the edge of the line.
it seems the problem i'm experiencing is that while tweening, the rotation causes the top of the red rectangle to lose sync with the black line.
here is my code that doesn't work, but hopefully will give you a clearer picture of what i'm attempting.
var angle:Number = 90;
var previousAngle:Number = 0;
var distanceObject:Object = new Object();
distanceObject.distance = line.width;
distanceTween = new Tween(distanceObject, "distance", None.easeNone, 0, distanceObject.distance, 5, true);
distanceTween.addEventListener(TweenEvent.MOTION_CHANGE, tweenHandler);
function tweenHandler(evt:TweenEvent):void
{
var angleShift:Number = (angle / distance) * distanceObject.distance;
//1:tween RedBox position
redBox.x = line.x + line.width * distanceObject.distance;
//2:tween RedBox angle
var externalPointMatrix:Matrix = redBox.transform.matrix;
MatrixTransformer.rotateAroundExternalPoint(externalPointMatrix, 0 + redBox.width * distanceObject.distance, 0, angleShift - previousAngle);
redBox.transform.matrix = externalPointMatrix;
previousAngle = angleShift;
}

I don't think you have specified the problem well enough for a generic solution. There are 3 things changing here: x, y and rotation. Each of these is calculated as a result of a point on the rectangle (the blue "x" in your diagram) that changes over time. That means the thing you need to focus on first is the point on the rectangle that changes over time. Next you need to know that the x and y can be calculated using that point along with the rotation.
So break it down into steps.
find the location of the "x" point on the line
rotate the object
find the location of the "x" point wrt to the rectangle
based on the angle of rotation and the known location of the "x" point calculate the x and y position of the rectangle (SOHCAHTOA)
Here is some code to illustrate:
package
{
import com.greensock.TweenNano;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width='500', height='300', backgroundColor='#ffffff', frameRate='30')]
public class BoxAnim extends Sprite
{
private static const LINE_WIDTH:int = 350;
private static const RECT_WIDTH:int = 150;
private static const RECT_HEIGHT:int = 100;
private static const FINAL_ROTATION:Number = Math.PI/2;
public var point:Number;
private var line:Sprite;
private var rect:Sprite;
private var cross:Sprite;
public function BoxAnim()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStage);
}
private function addedToStage(event:Event):void
{
line = new Sprite();
addChild(line);
line.graphics.lineStyle(10, 0x0);
line.graphics.lineTo(LINE_WIDTH, 0);
line.x = 50;
line.y = 175;
rect = new Sprite();
addChild(rect);
rect.graphics.lineStyle(4, 0xFF0000);
rect.graphics.beginFill(0xFF0000, 0.5);
rect.graphics.drawRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
rect.x = 50;
rect.y = 175;
cross = new Sprite();
addChild(cross);
cross.graphics.lineStyle(5, 0x41a9f4);
cross.graphics.moveTo(-5, -5);
cross.graphics.lineTo(5, 5);
cross.graphics.moveTo(5, -5);
cross.graphics.lineTo(-5, 5);
cross.x = 50;
cross.y = 175;
point = 0;
TweenNano.to(this, 3, {point: 1, onUpdate: tick});
}
private function tick():void
{
// first calculate where the point should be on the line
cross.x = (point * LINE_WIDTH) + line.x;
// calculate the angle of rotation
var rotationRadians:Number = (point * FINAL_ROTATION);
rect.rotation = rotationRadians*180/Math.PI;
// calculate where on the rectangle the point would be
var rectCrossX:Number = (point * RECT_WIDTH);
// use trig to find the x & y points
rect.x = cross.x - Math.cos(rotationRadians)*rectCrossX;
rect.y = cross.y - Math.sin(rotationRadians)*rectCrossX;
}
}
}
I'm just using the variable point as a percentage that goes from 0 to 1. I then scale it to find the position of the "x" point on the line. Scale it again to figure out the rotation. Scale it again to find where it lies along the top of the rectangle. Then trig solves the location of the corner of the rectangle wrt the point.

Related

Adding object within another

I have my main stage as 550x400. The header area is a stats bar. So I have an element underneath it which I named gameStage which is 550x350.
I am creating circles on a 1 second interval and then trying to randomly place them within my gameStage. It does not appear to be working. It seems like they're being added to a 550x350 element, but it starts at the top of my main stage -- not within my gameStage.
Also if I simply do addChild(circle) it creates an even 25 radius circle. As soon as I do gameStage.addChild(circle), the circle gets skewed slightly.
What am I doing wrong?
private function createCircle():void {
var stageSafeX:Number = Math.random()*gameStage.width;
var stageSafeY:Number = Math.random()*gameStage.height;
var circle:Sprite = new Sprite();
circle.graphics.clear();
circle.graphics.beginFill(Math.random()*0xFFFFFF, 1);
circle.graphics.drawCircle(0, 0, circleRadius);
circle.graphics.endFill();
circle.x = stageSafeX;
circle.y = stageSafeY;
circle.name = String(circleCount);
gameStage.addChild(circle);
}
Okay I'm using Flash Develop, so you'll have to forgive me as this program doesn't have FLA files, only classes and it uses a Main class to start the program (more reminiscent of Java if you've ever programmed in that). But the code I'll show you is more or less the same of how you want to do it.
First I would recommend you make a randomNumber function, I used it in making this code so if you want to use it here's the one I use (I put this in the Main class, you can put this wherever you want):
public static function randomNumber(minValue:Number, maxValue:Number):uint {
return Math.floor(Math.random() * (1 + maxValue - minValue)) + minValue;
}
This is inclusive, meaning if you put randomNumber(1, 10) it will give you a number between 1 to 10, including 1 and 10. It's more or less common sense, but I figured I might as well mention it just to clarify.
Now on to the addCircle function:
public static function addCircle(gameStage:Sprite, circleRadius:uint):void {
//Initializing the new circle instance
var newCircle:Sprite = new Sprite();
//Basically the same code you had (you don't need to set the alpha value to 1, it's default value is 1 regardless)
newCircle.graphics.beginFill(Math.random() * 0xFFFFFF);
newCircle.graphics.drawCircle(0, 0, circleRadius);
newCircle.graphics.endFill();
//Since the circle's origin is the center, you want its outer edges to be bound to the gameStage's edges
var safeStageX:Number = Main.randomNumber(newCircle.width / 2, gameStage.width - newCircle.width / 2);
var safeStageY:Number = Main.randomNumber(newCircle.height / 2, gameStage.height - newCircle.height / 2);
//Adding the circle to the gameStage's display field
gameStage.addChild(newCircle);
//Only set the circle's x and y AFTER you add it to the gameStage's display list, otherwise it might not set properly
newCircle.x = safeStageX;
newCircle.y = safeStageY;
}
Now following up I will give the code I made for the creation of the gameStage. You probably already have something for it, but I'll provide mine just in case you want to use it instead:
//Initializing the gameStage instance
var gameStage:Sprite = new Sprite();
//Adding the gameStage to the Stage's display field
this.stage.addChild(gameStage);
//Setting the gameStage's width and height (using "gameStage.width = 550" and "gameStage.height = 350" WILL NOT WORK)
//Use the color of your main game's background so you don't see this fill (unless you want to)
//Either do this or add a background picture, you need to do one or the other in order to set the gameStage's dimensions
gameStage.graphics.beginFill(0x000000);
gameStage.graphics.drawRect(0, 0, 550, 350);
gameStage.graphics.endFill();
//This puts the gameStage on the bottom of the screen (since it's 50 pixels shorter in the y direction)
gameStage.y = 50;
Lastly I will give you the actual for loop to create your circles (this function is present in the same class/FLA that your gameStage is on, because the addCircle function needs to take in that gameStage instance:
//Now let's populate your gameStage
for (var i:uint = 0; i < [number of circles you want]; i++) {
Main.addCircle(gameStage, [radius of the circle]);
}
And you're done! I'll also include the entire Main class, just so you can see how all the functions work together.
package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite {
public function Main() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
var gameStage:Sprite = new Sprite();
this.stage.addChild(gameStage);
gameStage.graphics.beginFill(0x000000);
gameStage.graphics.drawRect(0, 0, 550, 350);
gameStage.graphics.endFill();
gameStage.y = 50;
for (var i:uint = 0; i < 150; i++) {
Main.addCircle(gameStage, Main.randomNumber(15, 25));
}
}
public static function addCircle(gameStage:Sprite, circleRadius:uint):void {
var newCircle:Sprite = new Sprite();
newCircle.graphics.beginFill(Math.random() * 0xFFFFFF);
newCircle.graphics.drawCircle(0, 0, circleRadius);
newCircle.graphics.endFill();
var safeStageX:Number = Main.randomNumber(newCircle.width / 2, gameStage.width - newCircle.width / 2);
var safeStageY:Number = Main.randomNumber(newCircle.height / 2, gameStage.height - newCircle.height / 2);
gameStage.addChild(newCircle);
newCircle.x = safeStageX;
newCircle.y = safeStageY;
}
public static function randomNumber(minValue:Number, maxValue:Number):uint {
return Math.floor(Math.random() * (1 + maxValue - minValue)) + minValue;
}
}
}

Actionscript 3.0 - tracing the path of a moving body ;

I'm learning AS3.0 currently. I am trying to design a simple two body planet simulation. I need to show the paths of the planets on the screen. So my question is, once I have the updated x and y coordinates for the planets at each Timer interval, how do I change the color of the pixel (x,y) of the stage so that it shows the path of the planets? Is there some command of the form stage.x = color?
Thanks!
I recommend using BitmapData's draw() method to render your planets as pixels each time you update them. It basically works like a 'screenshot' of the display object you pass it as n argument. If you pass the objects transformation, the position/rotation/scale will be visible (as opposed to drawing from 0,0). This way, you will only be updating pixels instead of continuously creating new display objects.
Here's a basic commented example:
import flash.display.Sprite;
import flash.events.Event;
var trails:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x00000000);//create a transparent bitmap to draw the trails into
var trailsFade:ColorTransform = new ColorTransform(1,1,1,0.025,0,0,0,1);//color transform: keep rgb the same(1,1,1), set alpha to 0.025 out of 1.0
var background:Bitmap = addChild(new Bitmap(trails,PixelSnapping.AUTO,true)) as Bitmap;//add the trails pixels/bitmap data into a Bitmap/display object at the bottom of the display list
var dot:Sprite = addChild(new Sprite()) as Sprite;
dot.graphics.lineStyle(3);
dot.graphics.drawCircle(-4, -4, 8);
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
dot.x = mouseX;
dot.y = mouseY;
//draw trails of the dot
trails.draw(dot,dot.transform.concatenatedMatrix,trailsFade);//draw the dot into the bitmap data using the dot's transformation (x,y, rotation, scale)
}
Notice the trails when you move the mouse and how they are affected by the (update) speed.
Here's a longer example using multiple objects:
import flash.display.*;
import flash.events.Event;
import flash.geom.ColorTransform;
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var trails:BitmapData = new BitmapData(w,h,true,0x00000000);//create a transparent bitmap to draw the trails into
var trailsFade:ColorTransform = new ColorTransform(1,1,1,0.025,0,0,0,0.1);//color transform: keep rgb the same(1,1,1), set alpha to 0.025 out of 1.0
var background:Bitmap = addChild(new Bitmap(trails,PixelSnapping.AUTO,true)) as Bitmap;//add the trails pixels/bitmap data into a Bitmap/display object at the bottom of the display list
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var mercuryPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var venusPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var mercury:Sprite = mercuryPivot.addChild(getCircleSprite(24.40 / 4,0xCECECE)) as Sprite;
var venus:Sprite = venusPivot.addChild(getCircleSprite(60.52 / 4,0xFF2200)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
mercury.x = 5791 / 40;
venus.x = 10820 / 40;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
mercuryPivot.rotation += 0.5;
venusPivot.rotation += 0.25;
earthPivot.rotation += 0.12;
//draw trails
trails.draw(spheres,spheres.transform.concatenatedMatrix,trailsFade);
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
Notice we call trails.draw(spheres,spheres.transform.concatenatedMatrix,trailsFade);
but it could be trails.draw(earth,earth.transform.concatenatedMatrix,trailsFade); if you only want to draw the trails of earth.
In the example above I'm just nesting sprites and using the rotation property to keep things simple. You might want to use a bit of trigonometry to update positions because planets will probably not have perfectly circular orbits and pass through the exact location every single time.
Update
Thinking about this more, using the old school Graphics API might be handy for you if you get started and haven't got used to playing with pixels yet.
It's easy to get started with: objects that can be displayed in flash player can have a graphics property (see the Shape/Sprite/MovieClip classes). (You can have display object that you can't draw into whether you can nest elements into (DisplayObjectContainer) or not(DisplayObject), but that's a whole other thing for you too look into).
This graphics property Sprites and MovieClip has allows you to draw programmatically using simply commands such as: setting a stroke(lineStyle()), a fill (beginFill()/endFill()), moving an imaginary 'pen' without drawing (moveTo), drawing a line (lineTo), a circle, a rectangle, a rounded rectangle, etc. It's all there.
So, a minimal drawing program would look a bit like this:
import flash.events.MouseEvent;
import flash.events.Event;
var mousePressed:Boolean = false;//keep track if the mouse is pressed or not
graphics.lineStyle(1);//set the stroke to have a thickness of 1 (and the other parameters are defaults(color: black, transparency: 100% / 1.0, etc.))
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseEventHandler);//listend for mouse down
stage.addEventListener(MouseEvent.MOUSE_UP,mouseEventHandler);//...and mouse up changes
stage.addEventListener(Event.ENTER_FRAME,update);//update continuously
function mouseEventHandler(e:MouseEvent):void{
mousePressed = (e.type == MouseEvent.MOUSE_DOWN);
graphics.moveTo(mouseX,mouseY);//place the graphics 'pen' at this new location
}
function update(e:Event):void{
if(mousePressed) graphics.lineTo(mouseX,mouseY);//if the mouse is pressed, keep drawing a line to the current mouse location
}
or a more complex version where you use the speed of the mouse movement to influence the stroke thickness and transparency:
import flash.events.MouseEvent;
import flash.events.Event;
import flash.geom.Point;
var prevPos:Point = new Point();//previous mouse position
var currPos:Point = new Point();//current mouse position
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var mousePressed:Boolean = false;//keep track if the mouse is pressed or not
graphics.lineStyle(1);//set the stroke to have a thickness of 1 (and the other parameters are defaults(color: black, transparency: 100% / 1.0, etc.))
stage.doubleClickEnabled = true;
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseEventHandler);//listend for mouse down
stage.addEventListener(MouseEvent.MOUSE_UP,mouseEventHandler);//...and mouse up changes
stage.addEventListener(MouseEvent.DOUBLE_CLICK,function(e:MouseEvent):void{graphics.clear()});//double click to clear
stage.addEventListener(Event.ENTER_FRAME,update);//update continuously
function mouseEventHandler(e:MouseEvent):void{
mousePressed = (e.type == MouseEvent.MOUSE_DOWN);
graphics.moveTo(mouseX,mouseY);
}
function update(e:Event):void{
//currPos.setTo(mouseX,mouseY);//this works for flash player 11 and above instead of setting x,y separately
currPos.x = mouseX;
currPos.y = mouseY;
var mappedValue: Number = Point.distance(currPos,prevPos) / (w+h);//map the distance between points
//prevPos.copyFrom(currPos);//this works for flash player 11 and above instead of setting x,y separately
prevPos.x = mouseX;
prevPos.y = mouseY;
graphics.lineStyle(mappedValue * 100,0,1.0-(0.25+mappedValue));
if(mousePressed) graphics.lineTo(mouseX,mouseY);//if the mouse is pressed, keep drawing a line to the current mouse location
}
So going back to the tracing of a planet path, using the graphics api, my previous example would look like so:
import flash.display.*;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Point;
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var hasMoved:Boolean = false;//has the graphics 'pen' been moved ?
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
earthPivot.rotation += 0.12;
//draw trails
drawTrail(earth,0x0000FF);
}
function drawTrail(s:Sprite,color:int) {
var globalPos:Point = s.localToGlobal(new Point());//convert the local position of the sprite (it might have been nested several times) to the global/stage coordinate system
if(!hasMoved){//if the graphics 'pen' wasn't moved (is still at 0,0), this will happen only once: the 1st time you draw the mouse position
graphics.moveTo(globalPos.x,globalPos.y);//move it to where we're about to draw first
hasMoved = true;//and make sure we've marked that the above was done
}
graphics.lineStyle(1,color);
graphics.lineTo(globalPos.x,globalPos.y);
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
From my experience, using this older drawing API can get slow if you have a lot of lines on stage. I say older because it might actually be 15 years old now. Flash Player 10 introduced a newer drawing API. You can read on it on the Adobe Devnet but I warmly recommend Senocular's Flash Player 10 Drawing API Tutorial and his slides and example code from Flash Camp
Back to pixels: it's not that hard. You use the BitmapData class to manipulate pixels and use a Bitmap instance so you can add those pixels on stage. Here's a minimal drawing program:
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0xFFFFFF);//setup pixels
addChild(new Bitmap(canvas));//add them to the stage
addEventListener(Event.ENTER_FRAME,update);//setup continuous updates
function update(e:Event):void{
canvas.setPixel(int(mouseX),int(mouseY),0x990000);//pretty easy, right ?
}
want to make trippy patterns, sure thing, have a play:
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0xFFFFFF);//setup pixels
addChild(new Bitmap(canvas));//add them to the stage
addEventListener(Event.ENTER_FRAME,update);//setup continuous updates
function update(e:Event):void{
canvas.lock();//when updating multiple pixels or making multiple pixel operations
canvas.perlinNoise(mouseX,mouseY,mouseX/stage.stageWidth * 8,getTimer(),false,true);
canvas.unlock();//when you're done changing pixels, commit the changes
}
So, back to the trails example:
var w:Number = stage.stageWidth;
var h:Number = stage.stageHeight;
var canvas:BitmapData = new BitmapData(w,h,false,0xFFFFFF);
addChild(new Bitmap(canvas));
var spheres:Sprite = addChild(new Sprite()) as Sprite;//add a container for all the spheres (planets/moons/sun/etc.)
var earthPivot:Sprite = spheres.addChild(new Sprite()) as Sprite;
var sun:Sprite = spheres.addChild(getCircleSprite(69.5500 /4,0xFF9900)) as Sprite;
var earth:Sprite = earthPivot.addChild(getCircleSprite(60.52 / 4,0x2233FE)) as Sprite;
earth.x = 14960 / 40;
spheres.x = (w-spheres.width) * 0.5;
spheres.y = (h-spheres.height) * 0.5;
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
earthPivot.rotation += 0.12;
//draw trails
drawTrail(earth,0x0000FF,canvas);
}
function drawTrail(s:Sprite,color:int,image:BitmapData) {
var globalPos:Point = s.localToGlobal(new Point());//convert the local position of the sprite (it might have been nested several times) to the global/stage coordinate system
image.setPixel(int(globalPos.x),int(globalPos.y),color);//colour a pixel at a set position
}
function getCircleSprite(radius:Number,color:int):Sprite{
var circle:Sprite = new Sprite();
circle.graphics.beginFill(color);
circle.graphics.drawCircle(-radius * .5,-radius * .5,radius);//draw from centre
circle.graphics.endFill();
return circle;
}
Which looks like this:
Not sure if it's what you want though, but pixels are fun to use and pretty fast too.
With a bit of math you can do some minimal 3D as well.
Also, for your inspiration on drawing in actionscript, you can have a look at some of Keith Peters', Erik Natzke, Joshua Davis, etc.
No, there isn't such a command, but you can always create a very simple Sprite object and add it to the stage at the corresponding position. Something like:
var dot:Sprite = new Sprite();
dot.graphics.beginFill(0xCCCCCC);
dot.graphics.drawRect(-1, -1, 2, 2);
dot.graphics.endFill();
dot.x = x;
dot.y = y;
addChild(dot);

over drawn the circle when input angle in actionscript?

var theTextField:TextField = new TextField();
var theText:TextField = new TextField();
theTextField.type = TextFieldType.INPUT;
theTextField.border = true;
theTextField.x = 50;
theTextField.y = 10;
theTextField.height = 20;
theTextField.multiline = true;
theTextField.wordWrap = true;
theText.border = false;
theText.x = 10;
theText.y = 10;
theText.text = "Angle";
addChild(theText);
addChild(theTextField);
submit.addEventListener(MouseEvent.CLICK, click_handler);
function click_handler(event:MouseEvent):void
{
var txt:String = theTextField.text;
ang = Number(txt);
if (ang<0)
{
angle = - ang;
}
else
{
angle = 360 - ang;
}
var circleSlider:CircleSlider=new CircleSlider(120,angle); //draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
circleSlider.x = stage.stageWidth / 2;
circleSlider.y = stage.stageHeight / 2;
circleSlider.addEventListener(CircleSliderEvent.CHANGE, circleSliderEventHandler);
addChild(circleSlider);
}
Can someone help me.
var circleSlider:CircleSlider=new CircleSlider(120,angle);//draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
this code is the problem. CircleSlider is a separate class.I tried like this
circleSlider.CircleSlider(120,angle);
but it gives an error "" Call to a possibly undefined method CircleSlider through a reference with static type CircleSlider.""
when i run the program and input value as 90.
then i enter another value as 180 then it becomes
how can i overcome this error
Every time your click handler is executed you're creating a new instance of your circle class and adding it to the stage without removing the old instance. I think the best way to resolve it would be to move the logic you have in the constructor of your CircleSlider class into a separate public method, say draw and call that in the click handler.
Your code would look something like this:
// Set up the circle once
var circleSlider = new CircleSlider();
circleSlider.x = stage.stageWidth / 2;
circleSlider.y = stage.stageHeight / 2;
circleSlider.addEventListener(CircleSliderEvent.CHANGE, circleSliderEventHandler);
// and add it to the stage once
addChild(circleSlider);
function click_handler(event:MouseEvent):void
{
var txt:String = theTextField.text;
ang = Number(txt);
if (ang<0)
{
angle = - ang;
}
else
{
angle = 360 - ang;
}
// Now simply redraw in the same circle instance
circleSlider.draw(120,angle); //draw Circle According to the angle i think here is problem becoz every time clicked it creates new circle and draw over the old circle.
}
Assuming you're using the drawing API to draw the graphic, you could draw the circle (which seems to be constant) in the constructor (once) and the line illustrating the angle in the draw method (repeatedly). You'll need to clear the old line each time like this:
// Assumes you're drawing in the graphics property of the class
this.graphics.clear();

Drawing random circles using a timer within stage boundaries in ActionScript

I need to use a Timer for time controlled animation, time the drawing to occur every 500 milliseconds & Draw 20 circles in total. I also need to make sure the circles are completely drawn inside the limits of the stage. This is the code I've been playing around with and I can't figure it out.
import flash.events.TimerEvent;
import flash.utils.Timer;
// creates a new hundred-second Timer, ticks every 250 milliseconds
var faster_minuteTimer:Timer = new Timer(250, 6);
// designates listeners for the interval and completion events
faster_minuteTimer.addEventListener(TimerEvent.TIMER, onTick);
faster_minuteTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
// starts the timer ticking
faster_minuteTimer.start();
function onTick(event:TimerEvent):void
{
// displays the tick count so far
trace("Count... " + event.target.currentCount);
}
function onTimerComplete(event:TimerEvent):void
{
trace("Play Done.");
}
var xCoord, yCoord, radius, Width, Height:uint; // declare variables
// not using any variables for the first one
xCoord = (Math.random()* stage.stageWidth); // somewhere on the stage
yCoord = (Math.random() * stage.stageHeight);
radius = Math.max(Math.random() * 85, 20); // radius between two numbers
graphics.beginFill(Math.random() * 0xffffff); // random color
graphics.drawCircle(xCoord,yCoord,radius); // coordinates x & y, radius
graphics.endFill(); // end color fill
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
var timer:Timer = new Timer(500, 20);
timer.addEventListener(TimerEvent.TIMER, timerTick);
timer.start();
function timerTick(e:TimerEvent):void {
var newCircle:Sprite = new Sprite();
var radius:Number = Math.max(Math.random() * 85, 20);
var safeX:Number = ((stage.stageWidth - radius) - radius) * Math.random() + radius;
var safeY:Number = ((stage.stageHeight - radius) - radius) * Math.random() + radius;
newCircle.graphics.beginFill(Math.random() * 0xFFFFFF, 1);
newCircle.graphics.drawCircle(0, 0, radius);
newCircle.graphics.endFill();
newCircle.x = safeX;
newCircle.y = safeY;
stage.addChild(newCircle);
}
Setup your timer
Create a circle sprite
Generate a random radius size per your question's parameters
Determine a safe value between max (stage.stageWidth - radius) and min (radius)
Draw a circle in the circle sprite such that its center point is on the Sprite's origin (0, 0)
Position the circle at its given, randomized safe coordinates
Add the circle to the stage
Hope this helps!
Tested and working :)
Edit: Here is a sample image of a distribution of 2000 circles: http://grab.by/6C1O
I don't have a compiler handy, but something like this should be close.
The global variables maxWidth, maxHeight, maxSize determine the largest circle that will fit and where it can be drawn.
The drawOne() function draws a circle of random radius. The center of the circle is randomly set to be at least the circle's radius away from each side of the stage.
Each tick of the timer calls drawOne().
import flash.events.TimerEvent;
import flash.utils.Timer;
// Timer ticks 20 times 500 msec apart
var circleTimer:Timer = new Timer(500, 20);
// designates listeners for the interval and completion events
circleTimer.addEventListener(TimerEvent.TIMER, onTick);
circleTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
// starts the timer ticking
circleTimer.start();
function onTick(event:TimerEvent):void
{
trace("Count... " + event.target.currentCount);
drawOne();
}
function onTimerComplete(event:TimerEvent):void
{
trace("Play Done.");
}
// globals for size of stage, circles
var maxWidth:uint = stage.stageWidth;
var maxHeight:uint = stage.stageHeight;
var maxSize:uint = Math.min(maxWidth, maxHeight);
var minSize:uint = Math.min(20, Math.floor(maxSize/2));
function drawOne():void
{
// to fit box, radius must be 1/2 shortest side or less
var radius:uint = Math.max(Math.floor(Math.random() * maxSize/2), minSize);
// center circle at least radius from any side
var xCoord:uint = Math.random()*(maxWidth - 2*radius) + radius;
var yCoord:uint = Math.random()*(maxHeight - 2*radius) + radius;
graphics.beginFill(Math.random() * 0xffffff); // random color
graphics.drawCircle(xCoord,yCoord,radius);
graphics.endFill();
}
Hope this helps...

Finding Something lighter than Sprites!

I am making a Sim City like game. There are lots of tiles. When I first started. I was just using a tilesheet. I was copying the necessary pieaces from the tilesheet. on to a blank bitMapData. I then took the bitMapData and put it into a bitMap which I then put into a DisplayObject. It worked great!
tileSheet:BitMapData <----- data is already in
loop { loop through and tiled
bg:bitMapData= new bitMapData();
bg.copyPixel(tileSheet,rect,point);
}
canvas.BitMap(bg);
addChild(canvas);
Only problem was I needed to make my tiles interactive. I needed to highlight them and change colors and stuff. So I used the Sprite object. It works great but I can only have so many on the stage at once. or else it moves slow when I scroll. I need something Lighter then a sprite, but yet I can still turn into a object to make interactive. Anyone have any ideas ???
If you have a lot of tiles, that will impact performance because Flash needs to update the transformations of a lot of display objects (which internally means a lot of matrix calculations, and subsequent redraws of big areas of the screen.)
There is another way to achieve interactivity, if you find that you must use a single bitmap data for performance. Keep an "abstract" (i.e. not graphical) data model in memory, that stores your game state. Make sure that you are able to read from your store where a certain element is positioned in the game world. Then you can use a flat bitmap data to render the game world, because the individual positions are stored elsewhere.
When the user clicks the DisplayObject containing the bitmap data (a Sprite in which the bitmap is drawn using a bitmap fill, or that wraps a Bitmap), look in your model which of your game elements was hit by that click.
// myTileSprite is a Sprite with a bitmap fill
myTileSprite.addEventListener(MouseEvent.CLICK, handleWorldClick);
function handleWorldClick(ev : MouseEvent) : void
{
var i : int;
// Loop through all game element data models
for (i=0; i<myGameElements.length; i++) {
// Test the mouse position against the element model
if (myGameElements[i].hitTest(myTileSprite.mouseX, myTileSprite.mouseY)) {
trace('this was the element that was clicked: '+myGameElements[i].toString());
}
}
}
Here, whenever the player clicks the world graphics, the loop tries to find that element which was directly under the mouse position. You will need to implement a hitTest() method on all your game element data models, of course. Such a method simply checks the supplied world space position against the tile's area:
// GameElement.hitTest():
/**
* Tests a world position against the position and area of this game
* element tile. Returns a boolean indicating whether this tile was hit.
*/
public function hitTest(mouseX : Number, mouseY : Number) : void
{
var rect : Rectangle = new Rectangle(this.worldX, this.worldY, this.width, this.height);
if (mouseX > rect.left && mouseX < rect.right
&& mouseY > rect.top && mouseY < rect.top) {
return true;
}
else return false;
}
The GameElement class is not an display object, but has worldX and worldY properties indicating where it is located in the world. It's width and height properties define it's dimensions.
The trick from hereon is to make sure that the rendered bitmap and your model storage is synchronized, so that a tile's position on the bitmap really corresponds to it's worldX/worldY properties in the data model.
I am one step ahead of you. And that is a great idea. Its alot easier to keep a data representation of the world when the tiles are squared. I therefore can take my mouseX/tileWidth, and thats hw many columns I moved from left to right. same with the Y axis.
Not only that but coordinates start at top left corner.
But issue I have is that my tiles are Isometric. So instead of the X axis start off like...
012345678
0
1
2
3
4
5
6
7
8
My tiles are aligned like...
00
1 1
2 2
3 3
4 4
5 6
its a little sloppy. but the right side represents the y axis and the left represents the x axis. and the center origin is in the center of the screen. not on the top left. I am trying to figure out how to measure where my mouse is from the center and out on both sides. This sounds extremely difficult. I am not sure if its possible. The game is suppose to be like a sim city like game. The first sim city was squares not isometric. I dont think they went isometric until they started using 3d. I wonder if its possible to create a illusion of isometric on a square tile.
Ive been reading this great book on isometrics. They show to calculate tiles in 3d space. and even calculate your mouse in 3d space as well. here is the code. Its alot, but I hope someone else understands it more then I. The book was written by jobe makar on building multiplayer worlds. I wanted to share it because the code it is pretty simple as far as amount of code put into it. only 2 classes needed. I am not that good with trigonometry. so I cant really interpret how the math is getting the results. hopefully someone can explain that for me :D.
Y coordinates are not given because the width is = to height. The coordinates method is just a custom made Point class which holds x, y and z.
package com.gamebook.grid {
import com.gamebook.utils.geom.Coordinate;
import com.gamebook.utils.Isometric;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/**
* ...
* #author Jobe Makar - jobe#electrotank.com
*/
public class Map extends MovieClip{
private var _grid:Array;
private var _iso:Isometric;
private var _tileWidthOnScreen:int;
private var _tileHeightOnScreen:int;
private var _tileWidth:Number;
private var _tileHeight:Number;
private var _cols:int;
private var _rows:int;
private var _lastTile:Tile;
public function Map() {
initialize();
}
private function initialize():void{
_iso = new Isometric();
//when mapped to the screen the tile makes a diamond of these dimensions
_tileWidthOnScreen = 64;
_tileHeightOnScreen = 32;
//figure out the width of the tile in 3D space
_tileWidth = _iso.mapToIsoWorld(64, 0).x;
//the tile is a square in 3D space so the height matches the width
_tileHeight = _tileWidth;
buildGrid();
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
}
private function mouseMoved(e:MouseEvent):void {
if (_lastTile != null) {
_lastTile.alpha = 1;
_lastTile = null;
}
var coord:Coordinate = _iso.mapToIsoWorld(mouseX, mouseY);
var col:int = Math.floor(coord.x / _tileWidth);
var row:int = Math.floor(Math.abs(coord.z / _tileHeight));
if (col < _cols && row < _rows) {
var tile:Tile = getTile(col, row);
tile.alpha = .5;
_lastTile = tile;
}
}
private function buildGrid():void{
_grid = [];
_cols = 10;
_rows = 10;
for (var i:int = 0; i < _cols;++i) {
_grid[i] = [];
for (var j:int = 0; j < _rows;++j) {
var t:Tile = new Tile();
var tx:Number = i * _tileWidth;
var tz:Number = -j * _tileHeight;
var coord:Coordinate = _iso.mapToScreen(tx, 0, tz);
t.x = coord.x;
t.y = coord.y;
_grid[i][j] = t;
addChild(t);
}
}
}
private function getTile(col:int, row:int):Tile {
return _grid[col][row];
}
}
}
Then we have the isometric class that calculates 3d space.
package com.gamebook.utils {
import com.gamebook.utils.geom.Coordinate;
/**
* #author Jobe Makar - jobe#electrotank.com
*/
public class Isometric {
//trigonometric values stored for later use
private var _sinTheta:Number;
private var _cosTheta:Number;
private var _sinAlpha:Number;
private var _cosAlpha:Number;
/**
* Isometric class contrustor.
* #param declination value. Defaults to the most common value, which is 30.
*/
public function Isometric() {
var theta:Number = 30;//even though the tiles are already isometric, you still have to put the degrees the tiles will be turned.
var alpha:Number = 45;//45 degrees on y axis, 30 dgrees on x axis
theta *= Math.PI/180; // then you translate to radians
alpha *= Math.PI/180;
_sinTheta = Math.sin(theta);
_cosTheta = Math.cos(theta);
_sinAlpha = Math.sin(alpha);
_cosAlpha = Math.cos(alpha);
}
/**
* Maps 3D coordinates to the 2D screen
* #param x coordinate
* #param y coordinate
* #param z coordinate
* #return Coordinate instance containig screen x and screen y
*/
public function mapToScreen(xpp:Number, ypp:Number, zpp:Number):Coordinate {
var yp:Number = ypp;
var xp:Number = xpp*_cosAlpha+zpp*_sinAlpha;
var zp:Number = zpp*_cosAlpha-xpp*_sinAlpha;
var x:Number = xp;
var y:Number = yp*_cosTheta-zp*_sinTheta;
return new Coordinate(x, y, 0);
}
/**
* Maps 2D screen coordinates into 3D coordinates. It is assumed that the target 3D y coordinate is 0.
* #param screen x coordinate
* #param screen y coordinate
* #return Coordinate instance containig 3D x, y, and z
*/
public function mapToIsoWorld(screenX:Number, screenY:Number):Coordinate {
var z:Number = (screenX/_cosAlpha-screenY/(_sinAlpha*_sinTheta))*(1/(_cosAlpha/_sinAlpha+_sinAlpha/_cosAlpha));
var x:Number = (1/_cosAlpha)*(screenX-z*_sinAlpha);
return new Coordinate(x, 0, z);
}
}
}