丸群
運動量保存を考慮した衝突判定のアレ
@author naoto koshikawa
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.events.Event;
[SWF(width = "465", height = "465", backgroundColor = "0xFFFFFF", frameRate = "24")]
/**
* 運動量保存を考慮した衝突判定のアレ
* @author naoto koshikawa
*/
public class PhysicalSomething4 extends MovieClip
{
// _____________________________________________________ Property
/** particle count */
private static const PARTICLE_COUNT:uint = 10;
/** particle min size */
private static const PARTICLE_SIZE_MIN:uint = 15;
/** particle max size */
private static const PARTICLE_SIZE_MAX:uint = 30;
/** particle list */
private var _particles:Array;
/** for index */
private var _i:uint;
private var _j:uint;
// _____________________________________________________ Method
/**
* constructor
*/
public function PhysicalSomething4()
{
stage.quality = StageQuality.HIGH;
_particles = [];
Physics.init(stage);
createParticles();
addEventListener(Event.ENTER_FRAME, enterFrameListener);
}
/**
* create particles
*/
private function createParticles():void
{
for (_i = 0; _i < PARTICLE_COUNT; _i++)
{
var size:Number = Math.floor(Math.random() * (PARTICLE_SIZE_MAX - PARTICLE_SIZE_MIN + 1 )) + PARTICLE_SIZE_MIN;
var particle:Particle = new Particle(size);
Physics.base.addChild(particle);
var position:Object;
do
{
position = Physics.getRandomPosition(particle, _particles);
}
while (!position);
particle.alpha = 0.7;
particle.x = position.x;
particle.y = position.y;
_particles.push(particle);
}
}
// _____________________________________________________ Listener
/**
* Event.ENTER_FRAME event listener
* @param event
*/
private function enterFrameListener(event:Event):void
{
for (_i = 0; _i < _particles.length - 1; _i++)
{
for (_j = _i + 1; _j < _particles.length; _j++)
{
Physics.checkCollision(_particles[_i], _particles[_j]);
}
}
for (_i = 0; _i < _particles.length; _i++)
{
_particles[_i].x += _particles[_i].vx * Physics.t + 0.5 * _particles[_i].ax * Physics.t * Physics.t;
_particles[_i].y += _particles[_i].vy * Physics.t + 0.5 * _particles[_i].ay * Physics.t * Physics.t;
_particles[_i].vx += _particles[_i].ax * Physics.t;
_particles[_i].vy += _particles[_i].ay * Physics.t;
// bound horizontally
if (Physics.right < _particles[_i].x + _particles[_i].width / 2)
{
_particles[_i].vx *= Physics.BOUNCE;
_particles[_i].x = Physics.right - _particles[_i].width / 2;
}
else if (_particles[_i].x - _particles[_i].width / 2 < Physics.left)
{
_particles[_i].vx *= Physics.BOUNCE;
_particles[_i].x = Physics.left + _particles[_i].width / 2;
}
// bound vertically
if (Physics.bottom < _particles[_i].y + _particles[_i].height / 2)
{
_particles[_i].vy *= Physics.BOUNCE;
_particles[_i].y = Physics.bottom - _particles[_i].height / 2;
}
else if (_particles[_i].y - _particles[_i].height / 2 < Physics.top)
{
_particles[_i].vy *= Physics.BOUNCE;
_particles[_i].y = Physics.top + _particles[_i].height / 2;
}
_particles[_i].ax = 0;
_particles[_i].ay = 0;
}
}
}
}
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.utils.getTimer;
class Physics {
// _____________________________________________________ Property
/** gravity accerator */
internal static const GRAVITY:Number = 980;
/** firiction */
internal static const FRICTIION:Number = 0.0;
/** bounce */
internal static const BOUNCE:Number = -0.9;
/** spring */
internal static const SPRING:Number = 30;
/** boundary of top position */
private static var _top:Number;
public static function get top():Number
{
if (!_init) throwError();
return _top;
}
/** boundary of left postion */
private static var _left:Number;
public static function get left():Number
{
if (!_init) throwError();
return _left;
}
/** boundary of bottom position */
private static var _bottom:Number;
public static function get bottom():Number
{
if (!_init) throwError();
return _bottom;
}
/** boundary of right postion */
private static var _right:Number;
public static function get right():Number
{
if (!_init) throwError();
return _right;
}
/** initialized flag */
private static var _init:Boolean;
/** real stage */
private static var _stage:Stage;
/** virtual stage */
private static var _base:Sprite;
public static function get base():Sprite
{
if (!_init) throwError();
return _base;
}
/** time */
private static var _prevtime:Number;
private static var _t:Number;
public static function get t():Number
{
if (!_init) throwError();
return _t;
}
// _____________________________________________________ Method
/**
* init
*/
public static function init(stage:Stage):void
{
_stage = stage;
_prevtime = flash.utils.getTimer();
createBase();
_init = true;
}
/**
* get random poisition
* @param target target object whicth you wanna get random position
* @param duplicatedChecks
* @return
*/
public static function getRandomPosition(target:Particle, duplicatedChecks:Array = null):Object
{
if (!_init) throwError();
var position:Object = { x:0, y:0 };
if (!duplicatedChecks || duplicatedChecks.length == 0)
{
position.x = Math.random() * (_base.width - target.width) + target.width / 2 - _base.x;
position.y = Math.random() * (_base.height - target.height) + target.width / 2 -_base.y;
}
else
{
for (var i:uint = 0; i < duplicatedChecks.length; i++)
{
position.x = Math.random() * (_base.width - target.width) + target.width / 2 - _base.x;
position.y = Math.random() * (_base.height - target.height) + target.width / 2 -_base.y;
if (Math.sqrt(position.x * duplicatedChecks[i].x + position.y * duplicatedChecks[i].y)
<= target.size + duplicatedChecks[i].size)
{
return null;
}
}
}
return position;
}
/**
* check collision
* @param particleA Particle
* @param particleB Particle
*/
public static function checkCollision(particleA:Particle, particleB:Particle):void
{
var dx:Number = particleB.x - particleA.x;
var dy:Number = particleB.y - particleA.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
if (dist < particleA.size + particleB.size)
{
// collision angle
var radius:Number = Math.atan2(dy, dx);
var sin:Number = dy / dist;// Math.sin(radius);
var cos:Number = dx / dist;// Math.cos(radius);
var pointA:Object = { x:0, y:0 };
var pointB:Object = rotateCoordinates(dx, dy, sin, cos, true);
var velocityA:Object = rotateCoordinates(particleA.vx, particleA.vy, sin, cos, true);
var velocityB:Object = rotateCoordinates(particleB.vx, particleB.vy, sin, cos, true);
// Conservation of momentum
var vxTotal:Number = velocityA.x - velocityB.x;
velocityA.x = ((particleA.mass - particleB.mass) * velocityA.x
+ 2 * particleB.mass * velocityB.x) / (particleA.mass + particleB.mass);
velocityB.x = vxTotal + velocityA.x;
// update x, y
// pointA.x += velocityA.x * _t;
// pointB.x += velocityB.x * _t;
var overlap:Number = (particleA.size + particleB.size) - Math.abs(pointA.x + pointB.x);
pointA.x += overlap * (velocityA.x) / (Math.abs(velocityA.x) + Math.abs(velocityB.x));
pointB.x += overlap * (velocityB.x) / (Math.abs(velocityA.x) + Math.abs(velocityB.x));
// rotate position
pointA = rotateCoordinates(pointA.x, pointA.y, sin, cos, false);
pointB = rotateCoordinates(pointB.x, pointB.y, sin, cos, false);
// rotate velocity
velocityA = rotateCoordinates(velocityA.x, velocityA.y, sin, cos, false);
velocityB = rotateCoordinates(velocityB.x, velocityB.y, sin, cos, false);
// update real x, y
particleB.x = particleA.x + pointB.x;
particleB.y = particleA.y + pointB.y;
particleA.x = particleA.x + pointA.x;
particleA.y = particleA.y + pointA.y;
// update real vx, vy
particleA.vx = velocityA.x;
particleA.vy = velocityA.y;
particleB.vx = velocityB.x;
particleB.vy = velocityB.y;
}
}
/**
* rotate coordinates
* @param x
* @param y
* @param sin
* @param cos
* @param reverse
* @return
*/
private static function rotateCoordinates(x:Number, y:Number, sin:Number, cos:Number, reverse:Boolean):Object
{
var result:Object = { x:x, y:y };
if (reverse)
{
result.x = x * cos + y * sin;
result.y = y * cos - x * sin;
}
else
{
result.x = x * cos - y * sin;
result.y = y * cos + x * sin;
}
return result;
}
/**
* throw error
*/
private static function throwError():void
{
throw(new Error("please invoke Physics.init, first"));
}
/**
* create base object
*/
private static function createBase():void
{
_base = new Sprite();
_base.x = _stage.stageWidth / 2;
_base.y = _stage.stageHeight / 2;
var dotData:BitmapData = new BitmapData(2, 2, true, 0x00000000);
dotData.setPixel32(0, 0, 0x33000000 | 0x00FFFFFF * Math.random());
var dot:Shape = new Shape();
dot.graphics.beginBitmapFill(dotData);
dot.graphics.drawRect(-_base.x, -_base.y, _stage.stageWidth, _stage.stageHeight);
_base.addChild(dot);
_stage.addChild(_base);
_top = -_base.height/2;
_left = - _base.width/2 ;
_bottom = _base.height / 2;
_right = _base.width / 2;
_base.addEventListener(Event.ENTER_FRAME, enterFrameListener, false, int.MIN_VALUE, true);
}
// _____________________________________________________ Listener
/**
* Event.ENTER_FRAME event listener
* @param event
*/
private static function enterFrameListener(event:Event):void
{
_t = (getTimer() - _prevtime) / 1000;
_prevtime = getTimer();
}
}
import flash.display.Graphics;
import flash.display.MovieClip;
class Particle extends MovieClip {
// _____________________________________________________ Property
public var vx:Number = 0;
public var vy:Number = 0;
public var ax:Number = 0;
public var ay:Number = 0;
public var size:Number;
public var mass:Number;
// _____________________________________________________ Method
/**
* constructor
*/
public function Particle(size:Number=20):void
{
this.size = size;
this.mass = size * 2;
var gr:Graphics = graphics;
gr.beginFill(0xEEEEEE * Math.random(), 0.9);
gr.drawCircle(0, 0, size);
var radian:Number = Math.random() * Math.PI * 2;
var radius:Number = Math.random() * size * 10 + 30; // temporary action ...
vx = Math.cos(radian) * radius;
vy = Math.sin(radian) * radius;
}
}