Решил поигратся с АС3, до этого был только АС1-2 на таймлайне в FlashIDE в виде АктивХ оболочки на Делфи(опыта лет 8, всякие презентации, слайдшоу, автораны и тд в ассортименте)
Изучив с чем сейчас можно работать остановился на Haxe+FlashDevelop
Для первой задачи выбрал тему про сталкивающиеся 2д шары.
вроде все красиво... но есть 2 проблеммы и 1 вопрос:
1) мне кажется что шары "дергаются", перепрыгивают из 1 места поля в другое и тд, ведут себя странно вобщем. Пока их мало все красиво
это происходит именно в куске кода расчета столкновений
2) через некоторое время, иногда почти сразу, иногда секунд 15-30+ происходит ситуация, после которой VoloX, VeloY получает значение NaN, после чего все вычисляемые от них параметры также уходят в НаН

и шар залипает в 0,0 превращаяесь в "черную дыру"

((
Собственно вопрос, как в Нахе такие ситуации ловить?
т.е. я догадываюсь в чем проблемма в общем виде, но как-то привык что у меня рантайм-еррор выскакивает при неадекватных опперациях типа корня из отрицательного числа.
для удобства
весь проект архивом
собственно Код:
BallObject:

Код AS3:
package ;
import flash.display.GradientType;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.Lib;
class BallObject extends Sprite
{
public var Radius:Float;
public var Color:UInt;
public var VeloX:Float;
public var VeloY:Float;
public var minTop:UInt;
public var maxBottom:UInt;
public var minLeft:UInt;
public var maxRight:UInt;
public function MoveBall()
{
this.x += VeloX;
this.y += VeloY;
//wall collisions
//left
if (this.x < minLeft) {
VeloX = -VeloX;
this.x = minLeft;
} else
//right
if (this.x > maxRight) {
VeloX = -VeloX;
this.x = maxRight;
}
//top
if (this.y < minTop) {
VeloY = -VeloY;
this.y = minTop;
} else
//bottom
if (this.y > maxBottom) {
VeloY = -VeloY;
this.y = maxBottom;
}
}
private function Render()
{
var m = new Matrix();
m.createGradientBox(Radius, Radius, 0, -Radius, -Radius);
graphics.beginGradientFill(GradientType.RADIAL, [0xFFFFFF, Color], [1,1], [0, 0xFF], m);
graphics.drawCircle(0, 0, Radius);
graphics.endFill();
}
public function new(StartX,StartY,newRadius,VelX,VelY,newColor,stageHeight,stageWidth)
{
super();
this.x = StartX;
this.y = StartY;
Radius = newRadius;
minLeft = Math.round(Radius)+5;
maxRight = Math.round(stageWidth - Radius)-5;
minTop = Math.round(Radius)+5;
maxBottom = Math.round(stageHeight - Radius)-5;
Color = newColor;
VeloX = VelX;
VeloY = VelY;
Render();
}
}
и
BallManager:

Код AS3:
package ;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
import flash.Lib;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
class BallManager extends MovieClip
{
var myStage:MovieClip;
var balls:Array<BallObject>;
var wrongCollisions:UInt;
var rightCollisions:UInt;
var totalCollisions:UInt;
//var gui:MyGui;
public function BallCollideEx(ball1:BallObject,ball2:BallObject) {
totalCollisions++;
//localize
var ed:Float = 1;
var x1:Float = ball1.x; var y1:Float = ball1.y;
var x2:Float = ball2.x; var y2:Float = ball2.y;
var vX1:Float = ball1.VeloX; var vY1:Float = ball1.VeloY;
var vX2:Float = ball2.VeloX; var vY2:Float = ball2.VeloY;
var r1:Float = ball1.Radius; var r2:Float = ball2.Radius;
var mass1:Float = 1;
var mass2:Float = 1;
// flashback to collision time
var dx:Float = x2 - x1;
var dy:Float = y2 - y1;
var d:Float = Math.sqrt(dx * dx + dy * dy);
if (d < (r1 + r2)) {
// right collision
rightCollisions++;
var vp1:Float = vX1 * dx / d + vY1 * dy / d;
var vp2:Float = vX2 * dx / d + vY2 * dy / d;
var dt:Float = (r1 + r2 - d) / (vp1 - vp2);
// balls collision coordinates;
x1 -= vX1 * dt; y1 -= vY1 * dt;
x2 -= vX2 * dt; y2 -= vY2 * dt;
// collision
var dx:Float = x2 - x1;
var dy:Float = y2 - y1;
var d:Float = Math.sqrt(dx * dx + dy * dy);
// Unit vector in the direction of the collision
var aX:Float = dx / d;
var aY:Float = dy / d;
// Projection of the velocities in these axes
var vA1:Float = (vX1 * aX + vY1 * aY); var vB1:Float = ( -vX1 * aY + vY1 * aX);
var vA2:Float = (vX2 * aX + vY2 * aY); var vB2:Float = ( -vX2 * aY + vY2 * aX);
// New velocities in these axes (after collision): ed<=1, for elastic collision ed=1
var vAP1:Float = vA1 + (1 + ed) * (vA2 - vA1) / (1 + mass1 / mass2);
var vAP2:Float = vA2 + (1 + ed) * (vA1 - vA2) / (1 + mass2 / mass1);
// Undo the projections
vX1 = vAP1 * aX - vB1 * aY; vY1 = vAP1 * aY + vB1 * aX;
vX2 = vAP2 * aX - vB2 * aY; vY2 = vAP2 * aY + vB2 * aX;
// move time forward
x1 += vX1 * dt; y1 += vY1 * dt;
x2 += vX2 * dt; y2 += vY2 * dt;
// restore variables
ball1.x = x1; ball1.y = y1;
ball1.VeloX = vX1; ball1.VeloY = vY1;
ball2.x = x2; ball2.y = y2;
ball2.VeloX = vX2; ball2.VeloY = vY2;
} else {
//no collision detected
wrongCollisions++;
}
}
public function AddRandomBall(maxHeight,maxWidth):BallObject
{
var ColorArray:Array<Int> = [Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)];
//ColorArray[Math.round(Math.random() * 3)] = 255;
var Color = ColorArray[2] + ColorArray[1] * 0xff + ColorArray[2] * 0xffff;
var Radius = Math.random() * 5 + 5;
var Angle = Math.random() * 360;
var Speed = Math.random() * 5 + 1;
var VeloX = Math.sin(Angle) * Speed;
var VeloY = Math.cos(Angle) * Speed;
var BallX = Radius+ Math.random() * (maxWidth-Radius*2);
var BallY = Radius + Math.random() * (maxHeight-Radius*2);
return new BallObject(BallX,BallY,Radius,VeloX,VeloY,Color,maxHeight,maxWidth);
}
public function bruteCollide() {
for (i in 0...balls.length-1) {
for (j in 0...balls.length-1) {
if (i != j) { BallCollideEx(balls[i], balls[j]); }
}
}
}
public function xSortCollide() {
balls.sort(function (ball1:BallObject, ball2:BallObject):Int return Math.round(ball1.x - ball2.x) );
var rad:Float;
var xDist:Float;
for (i in 0...balls.length - 2) {
for (j in i + 1...balls.length - 1) {
rad = balls[j].Radius + balls[i].Radius;
xDist = Math.abs(balls[j].x - balls[i].x);
if (xDist < rad) { BallCollideEx(balls[i], balls[j]); }
else { break; }
}
}
}
public function moveBalls(e:Event) {
var mode:UInt;
//mode = gui.modeSwitch.mode;
mode = 1;
rightCollisions = 0;
wrongCollisions = 0;
totalCollisions = 0;
var cur:Float = Lib.getTimer();
for (i in 0...balls.length - 1) { balls[i].MoveBall(); }
switch (mode) {
case 0: bruteCollide();
case 1: xSortCollide();
}
var Elapsed = Lib.getTimer() - cur;
/*gui.fpsField.text = 'Elapsed: ' + Elapsed + 'ms';
gui.totalCollisionsField.text = 'Всего проверок: ' + totalCollisions;
gui.rightCollisionsField.text = 'Правильных столкновений: ' + rightCollisions;
gui.wrongCollisionsField.text = 'Ложных проверок: ' + wrongCollisions;
gui.wasteField.text = 'Эффективность: ' + Math.floor(10000*rightCollisions / wrongCollisions)/100 + '%';*/
}
public function new(dst:MovieClip)
{
super();
myStage = dst;
myStage.addChild(this);
myStage.stage.frameRate = 60;
//maxballs
var MaxBalls = 350;
balls = new Array<BallObject>();
for (i in 0...MaxBalls) {
balls.push(AddRandomBall(dst.stage.stageHeight, dst.stage.stageWidth));
myStage.addChild(balls[i]);
}
//gui = new MyGui();
//myStage.addChild(gui);
//gui.x = dst.stage.stageWidth - gui.width;
//gui.y = 0;
this.addEventListener(Event.ENTER_FRAME, moveBalls);
}
}
и Main

Код AS3:
package ;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.Lib;
class Main
{
private var Manager: BallManager;
public function new() {
Manager = new BallManager(Lib.current);
}
static function main()
{
new Main();
}
}