Actionscript 3.0 - tracing the path of a moving body ;

回眸只為那壹抹淺笑 提交于 2020-01-03 05:33:19

问题


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!


回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/24227493/actionscript-3-0-tracing-the-path-of-a-moving-body

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!