as3 addChild 到生成的三角形中

as3 addChild into a generated triangle

大家好!我正在尝试在三角形内动态添加(然后删除)一些动画片段。 movieclip 内的简单 movieclip 不起作用(最后是一个正方形)。画三角形很简单,addChild方法也crystal明白了。困难的部分在后面。这是我正在尝试开发的代码:

btn_toys_2.confirm.addEventListener(MouseEvent.MOUSE_UP, confirmToys);
import flash.display.Graphics;

var point1:Point = new Point(466, 65);
var point2:Point = new Point(370, 540);
var point3:Point = new Point(570, 540);

var vertices:Vector.<Number> = Vector.<Number>([point1.x, point1.y, point2.x, point2.y, point3.x, point3.y]);

var triangle:Sprite = new Sprite();

triangle.graphics.beginFill(0x00ff00, 1);
triangle.graphics.drawTriangles(vertices);
triangle.graphics.endFill();
addChild(triangle);

function confirmToys(e:MouseEvent){
    var toy:MovieClip = new shar_001;
    triangle.addChild(toy);
    toy.x = Math.random()*30;
    toy.y = Math.random()*30;
}

"toy" 影片剪辑出于某种原因放置在三角形之外(0-30 x 轴和 0-30 y 轴)。 重要的部分是让 "toys" 出现在一个三角形中,不必是一个动画片段。解决这个问题的方法也很棒! 提前致谢!

之所以会这样,是因为您将三角形的锚点设置为零。你这样做的时候就这样做了

addChild(triangle);

这将始终将添加的 child 放在 (0,0) 处。三角形没有出现的唯一原因是因为您通过使 points 大于零来添加空像素缓冲。相反,您将使用

addChild(triangle);
triangle.x = 370;
triangle.y = 65;

您希望三角形左上角所在的点是 (370, 65)。你应该让你的三角形点为 (96, 0), (0, 475), (200, 475)。现在三角形的左上角位于舞台上的 (0,0) 处。现在将三角形添加到舞台后,将三角形设置为 (370, 65)。现在三角形的锚点仍然是三角形的左上角,而不是舞台,所以当你添加玩具时,它会参考你期望的点。

// let the minimum x and y be zero, and adjust the others relative to that.
var point1:Point = new Point(96, 0);
var point2:Point = new Point(0, 475);
var point3:Point = new Point(200, 475);
var toyArray:Array = new Array();

var vertices:Vector.<Number> = Vector.<Number>([point1.x, point1.y, point2.x, point2.y, point3.x, point3.y]);

var triangle:Sprite = new Sprite();

triangle.graphics.beginFill(0x00ff00, 1);
triangle.graphics.drawTriangles(vertices);
triangle.graphics.endFill();
addChild(triangle);
// position anchor point on stage
triangle.x = 370;
triangle.y = 65;


function confirmToys(e:MouseEvent){
    var p:Point = new Point(Math.random()*triangle.width,Math.random()*triangle.height);
    if (isInsideTriangle(Point1,Point2,Point3,p))
        {
            var toy:MovieClip = new shar_001;
            triangle.addChild(toy);
            toyArray.push(toy);
            toy.x = p.x;
            toy.y = p.y;
        }
    }

private function isInsideTriangle(A:Point,B:Point,C:Point,P:Point):Boolean {
        var planeAB:Number = (A.x-P.x)*(B.y-P.y)-(B.x-P.x)*(A.y-P.y);
        var planeBC:Number = (B.x-P.x)*(C.y-P.y)-(C.x - P.x)*(B.y-P.y);
        var planeCA:Number = (C.x-P.x)*(A.y-P.y)-(A.x - P.x)*(C.y-P.y);
        return sign(planeAB)==sign(planeBC) && sign(planeBC)==sign(planeCA);
    }
private function sign(n:Number):int {
        return Math.abs(n)/n;
    }

根据您要使用的方法,从三角形中取出玩具应该非常简单。我添加了一个 toyArray,您可以循环访问以删除它们。

检查一个位置是否在期望的边界内,如果不在则拒绝它当然是一个解决方案。但是,这会阻止程序确定性,因为您永远不知道在找到边界内的位置之前需要尝试多少次。

这是否意味着程序可以 运行 永远?可能是的,但这不太可能不会发生。根据三角形填充其边界框的多少,它仍然会产生相当多的未命中。必须检查、拒绝并重试的失误。

我不建议反对这种策略,因为它可能是一个性能问题(实际上可能是一个),而是因为它似乎没有抓住要点:如果应该找到三角形中的位置,让我们做到这一点。所有这些试验和错误以及测试和拒绝都是违反直觉的。


你只有一个内置的伪随机数生成器:Math.random()。 这会产生一个 均匀分布 介于 0 和 1 之间的随机数。(让我们忽略边界是否是可能的值)

要创建二维分布,只需使用其中的两个就非常容易。

现在均匀分布的问题是它是均匀的。要形成非矩形形状,必须应用变换。

把三角形的两条边看成两个向量。通过以随机方式线性组合这两个向量来找到三角形中的随机点。

显然,对于未转换的随机数,随机点会产生菱形边界。为了补偿向量在一个点相遇并在另一个方向上发散的事实,将平方根应用于一个随机数。这背后的数学原理并不复杂,但也不简单。这里我选择省略。有关更多信息,请提出一个新问题,math.se 可能是执行此操作的好地方。

这是一个完整的示例代码,用作文档 class,它将 1000 个圆放入三角形边界:

package 
{
    import flash.display.Shape;
    import flash.display.Sprite;

    import flash.geom.Point;

    public class Main extends Sprite 
    {
        public function Main() 
        {
            var distribution:EvenDistribution2DTriangleBoundary = new EvenDistribution2DTriangleBoundary(new Point(100, 100), new Point(400, 50), new Point(250, 350));

            for (var i:int = 0; i < 1000; ++i)
            {
                // create an object in each iteration of the loop
                var circle:Shape = new Shape();

                //add some graphics (this is unnecessary if you use a library symbol)
                circle.graphics.beginFill(0xff0000, .6);
                circle.graphics.drawCircle(0, 0, 3);
                circle.graphics.endFill();

                // add it to the display list
                addChild(circle);

                // reposition it with the help of the distribution object
                distribution.positionDisplayObject(circle);
            }
        }
    }
}

import flash.display.DisplayObject;

import flash.geom.Point;

internal class EvenDistribution2DTriangleBoundary
{
    private var u:Point;
    private var v:Point;
    private var position:Point;

    public function EvenDistribution2DTriangleBoundary(a:Point, b:Point, c:Point)
    {
        // consider corner "a" as the position of the triangle, this is arbitrary decision, but has to be consistent with the rest of this constructor
        position = a;

        // create two vectors from the corner that is the position to the other two corners respectively
        u = b.subtract(a);
        v = c.subtract(a);
    }

    public function getRandomPosition():Point
    {
        // random position formula with two random variables: position + (u + (v-u) * random1) * sqrt(random2)

        var r1:Number = Math.random();

        // the sqrt transforms the probability density function of the even distribution f(x) = 1 into a triangle g(y) = 2y
        var r2:Number = Math.sqrt(Math.random());

        // applying the above formula to create an evenly distributed random position within the triangle
        return position.add(new Point((u.x + (v.x - u.x) * r1) * r2, (u.y + (v.y - u.y) * r1) * r2));
    }

    // convenience function to position a display object at a random position in the triangle
    public function positionDisplayObject(object:DisplayObject):void
    {
        var position:Point = getRandomPosition();

        object.x = position.x;
        object.y = position.y;
    }
}

创建随机分布本身就是一个 class。为了简单测试,它是一个内部 class,因此整个示例是进入单个文件的单个 class。当然,在生产代码中,这应该更好地组织。

这是我得到的 4 个结果:


it seems I need to transfer all of my frames/timeline code (and there's a lot!) into the external class

这不是必需的,尽管是推荐的。您最终应该只使用基于 class 的代码,但当然在项目中进行这种转换并不是很实际。

在我上面的示例中,有两个 classes:MainEvenDistribution2DTriangleBoundary。你只对后一个感兴趣。 Main 只是为了使用另一个 class、创建和显示圆圈等:这是一个演示。

要在项目中使用 EvenDistribution2DTriangleBoundary,请在与名为 EvenDistribution2DTriangleBoundary.as 的 .fla 文件相同的目录中创建一个新的文本文件,内容如下:

package
{
    import flash.display.DisplayObject;

    import flash.geom.Point;

    public class EvenDistribution2DTriangleBoundary
    {
        private var u:Point;
        private var v:Point;
        private var position:Point;

        public function EvenDistribution2DTriangleBoundary(a:Point, b:Point, c:Point)
        {
            position = a;

            u = b.subtract(a);
            v = c.subtract(a);
        }

        public function getRandomPosition():Point
        {
            var r1:Number = Math.random();
            var r2:Number = Math.sqrt(Math.random());

            return position.add(new Point((u.x + (v.x - u.x) * r1) * r2, (u.y + (v.y - u.y) * r1) * r2));
        }

        public function positionDisplayObject(object:DisplayObject):void
        {
            var position:Point = getRandomPosition();

            object.x = position.x;
            object.y = position.y;
        }
    }
}

现在您可以在您的项目中像使用任何其他 class 一样使用 class。例如,您可以将 Main 的构造函数中的代码添加到您的时间线中,它应该可以工作:

var distribution:EvenDistribution2DTriangleBoundary = new EvenDistribution2DTriangleBoundary(new Point(100, 100), new Point(400, 50), new Point(250, 350));

for (var i:int = 0; i < 1000; ++i)
{
    // create an object in each iteration of the loop
    var circle:Shape = new Shape();

    //add some graphics (this is unnecessary if you use a library symbol)
    circle.graphics.beginFill(0xff0000, .6);
    circle.graphics.drawCircle(0, 0, 3);
    circle.graphics.endFill();

    // add it to the display list
    addChild(circle);

    // reposition it with the help of the distribution object
    distribution.positionDisplayObject(circle);
}

您应该会看到一个由红色圆圈组成的三角形,类似于我在上面的结果图片中发布的那些。有用吗?