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) 处。三角形没有出现的唯一原因是因为您通过使 point
s 大于零来添加空像素缓冲。相反,您将使用
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:Main
和 EvenDistribution2DTriangleBoundary
。你只对后一个感兴趣。 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);
}
您应该会看到一个由红色圆圈组成的三角形,类似于我在上面的结果图片中发布的那些。有用吗?
大家好!我正在尝试在三角形内动态添加(然后删除)一些动画片段。 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) 处。三角形没有出现的唯一原因是因为您通过使 point
s 大于零来添加空像素缓冲。相反,您将使用
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:Main
和 EvenDistribution2DTriangleBoundary
。你只对后一个感兴趣。 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);
}
您应该会看到一个由红色圆圈组成的三角形,类似于我在上面的结果图片中发布的那些。有用吗?