【问题标题】:Actionscript 3.0 - tracing the path of a moving body ;Actionscript 3.0 - 追踪移动物体的路径;
【发布时间】:2014-08-05 07:43:42
【问题描述】:

我目前正在学习 AS3.0。我正在尝试设计一个简单的两体行星模拟。我需要在屏幕上显示行星的路径。所以我的问题是,一旦我在每个 Timer 间隔更新了行星的 x 和 y 坐标,如何更改舞台像素 (x,y) 的颜色,以便显示行星的路径?有没有 stage.x = color 形式的命令?

谢谢!

【问题讨论】:

    标签: actionscript-3 flash


    【解决方案1】:

    我建议在每次更新行星时使用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 等人的一些内容。

    【讨论】:

    • 等待详细的解释和出色的代码!应该更频繁地提供这种答案! :) 不幸的是,正如那家伙所说,这对他来说太复杂了,几乎可以肯定他不会使用它,但这是正确的方法。我很佩服你,因为我没有耐心,也不认为需要这种答案,因为我认为提出问题的人不会使用它-如果他问这种问题,他将不会理解。无论如何-竖起大拇指!如果有人真的想学习,他应该花时间阅读这样的东西!
    【解决方案2】:

    不,没有这样的命令,但你总是可以创建一个非常简单的 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);
    

    【讨论】:

    • 感谢您的回复。如果可能的话,能否请您简要介绍一下 beginFill、drawRect 和 endFill 的作用,以便我根据需要对其进行修改?
    • 它与Graphics Flash 类一起工作 - 开始用特定颜色填充某些东西,然后绘制一个矩形,最后结束填充它。查看规格。
    • 我刚刚试了一下 - 它似乎不起作用。我复制粘贴了您提供的代码,但屏幕上没有任何内容......我做错了什么?我已将您的倒数第二行和倒数第三行中的 x 和 y 设置为各自的行星坐标(工作得很好)。
    • @AndreyPopov 这是一种方法,但是您将添加一个新的 Sprite 并在每一个帧/时间绘制一个新框,以查看轨迹。我建议创建一个透明的 BitmapData 实例,将其添加到最底部图层/显示列表索引的位图中,并使用 draw() 方法传递行星的变换矩阵。或者,您甚至可以应用过滤器(例如颜色变换来为轨迹着色,或者根据需要使用微弱的模糊来随着时间的推移淡化轨迹)
    • @GeorgeProfenza,对我来说太复杂了,伙计,还在学习基础知识!你能分解一下吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 2012-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多