【发布时间】:2014-08-05 07:43:42
【问题描述】:
我目前正在学习 AS3.0。我正在尝试设计一个简单的两体行星模拟。我需要在屏幕上显示行星的路径。所以我的问题是,一旦我在每个 Timer 间隔更新了行星的 x 和 y 坐标,如何更改舞台像素 (x,y) 的颜色,以便显示行星的路径?有没有 stage.x = color 形式的命令?
谢谢!
【问题讨论】:
标签: actionscript-3 flash
我目前正在学习 AS3.0。我正在尝试设计一个简单的两体行星模拟。我需要在屏幕上显示行星的路径。所以我的问题是,一旦我在每个 Timer 间隔更新了行星的 x 和 y 坐标,如何更改舞台像素 (x,y) 的颜色,以便显示行星的路径?有没有 stage.x = color 形式的命令?
谢谢!
【问题讨论】:
标签: actionscript-3 flash
我建议在每次更新行星时使用BitmapData's draw() 方法将行星渲染为像素。它基本上就像您将其作为 n 参数传递的显示对象的“屏幕截图”。如果您通过对象转换,则位置/旋转/比例将可见(而不是从 0,0 绘制)。这样,您将只更新像素,而不是不断创建新的显示对象。
这是一个基本的注释示例:
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)
}
注意移动鼠标时的轨迹以及它们如何受到(更新)速度的影响。
这是一个使用多个对象的更长示例:
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;
}
注意我们打电话给trails.draw(spheres,spheres.transform.concatenatedMatrix,trailsFade);
但如果你只想绘制earth的轨迹,它可能是trails.draw(earth,earth.transform.concatenatedMatrix,trailsFade);。
在上面的示例中,我只是嵌套精灵并使用旋转属性来保持简单。您可能需要使用一些三角函数来更新位置,因为行星可能不会有完美的圆形轨道并且每次都通过确切的位置。
更新
想一想,如果您刚开始使用像素,但还没有习惯玩像素,使用老派的Graphics API 可能会很方便。
很容易上手:可以在 Flash Player 中显示的对象可以具有图形属性(请参阅 Shape/Sprite/MovieClip 类)。 (您可以拥有无法绘制的显示对象,是否可以将元素嵌套到(DisplayObjectContainer)或不(DisplayObject)中,但这对您来说也是另一回事)。
这个 graphics 属性 Sprites 和 MovieClip 允许您以编程方式使用简单的命令进行绘制,例如:设置描边 (lineStyle())、填充 (beginFill()/endFill())、移动假想的“笔”不画(moveTo)、画线(lineTo)、圆、长方形、圆角长方形等,都在那里。
所以,一个最小的绘图程序看起来有点像这样:
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
}
或更复杂的版本,您可以使用鼠标移动的速度来影响笔画粗细和透明度:
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
}
所以回到行星路径的追踪,使用图形 api,我之前的例子看起来像这样:
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;
}
根据我的经验,如果舞台上有很多线条,使用这个较旧的绘图 API 会变得很慢。我说年龄更大,因为它现在可能实际上已经 15 岁了。 Flash Player 10 引入了更新的绘图 API。你可以在Adobe Devnet 上阅读它,但我强烈推荐Senocular's Flash Player 10 Drawing API Tutorial 和他的slides and example code from Flash Camp
回到像素:这并不难。您使用 BitmapData 类来操作像素并使用 Bitmap 实例,以便您可以在舞台上添加这些像素。这是一个最小的绘图程序:
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 ?
}
想制作迷幻图案,当然,玩一玩:
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
}
那么,回到路径示例:
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;
}
看起来像这样:
虽然不确定它是否是您想要的,但像素使用起来很有趣,而且速度也很快。 通过一些数学运算,您也可以做一些minimal 3D。
此外,对于您在 actionscript 中绘图的灵感,您可以查看 Keith Peters'、Erik Natzke、Joshua Davis 等人的一些内容。
【讨论】:
不,没有这样的命令,但你总是可以创建一个非常简单的 Sprite 对象并将其添加到舞台的相应位置。比如:
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);
【讨论】:
draw() 方法传递行星的变换矩阵。或者,您甚至可以应用过滤器(例如颜色变换来为轨迹着色,或者根据需要使用微弱的模糊来随着时间的推移淡化轨迹)