衝突判定の学習
オライリー 衝突判定の学習
/**
* Copyright Nyarineko ( http://wonderfl.net/user/Nyarineko )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5rQT
*/
/*オライリー 衝突判定の学習*/
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display.DisplayObject;
import flash.events.Event;
public class GridCollision3 extends Sprite
{
private const GRID_SIZE:Number = 20;
private var _grid_size:Number = 20;
private const RADIUS:Number = 10;
private var _balls:Vector.<DisplayObject>;
private var _grid:CollisionGrid;
private var _numBalls:int = 200;
public function GridCollision3()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
/*
_grid = new CollisionGrid(stage.stageWidth,
stage.stageHeight,
GRID_SIZE);
_grid.drawGrid(graphics);
*/
//_grid_size
makeBalls();
_grid = new CollisionGrid(stage.stageWidth,
stage.stageHeight,
_grid_size);
_grid.drawGrid(graphics);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void
{
// 個々のボールについて速度を足す
updateBalls();
// チェックが必要なボールを調べる
_grid.check(_balls);
var numChecks:int = _grid.checks.length;
for (var j:int = 0; j < numChecks; j += 2)
{
// 2つずつ調べる
checkCollision(_grid.checks[j] as Ball,
_grid.checks[j + 1] as Ball);
}
}
private function makeBalls():void
{
var max:Number = 0;
// 全てのボールを生成する
_balls = new Vector.<DisplayObject>(_numBalls);
for (var i:int = 0; i < _numBalls; i++)
{
//var ball:Ball = new Ball(RADIUS);
var ball:Ball = new Ball(Math.random() * 10 + 5);
ball.x = Math.random() * stage.stageWidth;
ball.y = Math.random() * stage.stageHeight;
ball.vx = Math.random() * 4 - 2;
ball.vy = Math.random() * 4 - 2;
if(ball.radius > max) max = ball.radius;
addChild(ball);
_balls[i] = ball;
}
_grid_size = max*2;
}
private function updateBalls():void
{
for (var i:int = 0; i < _numBalls; i++)
{
// ボールを移動し、壁に跳ね返らせる
var ball:Ball = _balls[i] as Ball;
ball.update();
if (ball.x < RADIUS)
{
ball.x = RADIUS;
ball.vx *= -1;
}
else if (ball.x > stage.stageWidth - RADIUS)
{
ball.x = stage.stageWidth - RADIUS;
ball.vx *= -1;
}
if (ball.y < RADIUS)
{
ball.y = RADIUS;
ball.vy *= -1;
}
else if (ball.y > stage.stageHeight - RADIUS)
{
ball.y = stage.stageHeight - RADIUS;
ball.vy *= -1;
}
ball.color = 0xffffff;
}
}
private function checkCollision(ballA:Ball, ballB:Ball):void
{
// 2つのボールの衝突判定を行う
var dx:Number = ballB.x - ballA.x;
var dy:Number = ballB.y - ballA.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
if (dist < ballA.radius + ballB.radius)
{
ballA.color = 0xff0000;
ballB.color = 0xff0000;
}
}
}
}
import flash.display.Sprite;
class Ball extends Sprite
{
private var _color:uint;
private var _radius:Number;
private var _vx:Number = 0;
private var _vy:Number = 0;
public function Ball(radius:Number, color:uint = 0xffffff)
{
_radius = radius;
_color = color;
draw();
}
private function draw():void
{
// 中心に点のある円を描く
graphics.clear();
graphics.lineStyle(0);
graphics.beginFill(_color, .5);
graphics.drawCircle(0, 0, _radius);
graphics.endFill();
graphics.drawCircle(0, 0, 1);
}
public function update():void
{
// 位置に速度を足す
x += _vx;
y += _vy;
}
public function set color(value:uint):void
{
_color = value;
draw();
}
public function get color():uint
{
return _color;
}
public function set radius(value:Number):void
{
_radius = value;
draw();
}
public function get radius():Number
{
return _radius;
}
public function set vx(value:Number):void
{
_vx = value;
}
public function get vx():Number
{
return _vx;
}
public function set vy(value:Number):void
{
_vy = value;
}
public function get vy():Number
{
return _vy;
}
}
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.events.EventDispatcher;
class CollisionGrid extends EventDispatcher
{
private var _checks:Vector.<DisplayObject>;
private var _grid:Vector.<Vector.<DisplayObject>>;
private var _gridSize:Number;
private var _height:Number;
private var _numCells:int;
private var _numCols:int;
private var _numRows:int;
private var _width:Number;
public function CollisionGrid(width:Number,
height:Number,
gridSize:Number)
{
_width = width;
_height = height;
_gridSize = gridSize;
_numCols = Math.ceil(_width / _gridSize);
_numRows = Math.ceil(_height / _gridSize);
_numCells = _numCols * _numRows;
}
public function drawGrid(graphics:Graphics):void
{
// グリッドを表す線を描く
graphics.lineStyle(0, .5);
for (var i:int = 0; i <= _width; i += _gridSize)
{
graphics.moveTo(i, 0);
graphics.lineTo(i, _height);
}
for (i = 0; i <= _height; i += _gridSize)
{
graphics.moveTo(0, i);
graphics.lineTo(_width, i);
}
}
public function check(objects:Vector.<DisplayObject>):void
{
var numObjects:int = objects.length;
_grid = new Vector.<Vector.<DisplayObject>>(_numCells);
_checks = new Vector.<DisplayObject>();
// 全てのオブジェクトについてループする
for (var i:int = 0; i < numObjects; i++)
{
var obj:DisplayObject = objects[i];
// 1次元の格子中の位置を表す配列の添え字を
// 計算する
var index:int = Math.floor(obj.y / _gridSize) *
_numCols + Math.floor(obj.x / _gridSize);
// iにオブジェクトがなければ、格子を生成する
if (_grid[index] == null)
{
_grid[index] = new Vector.<DisplayObject>;
}
// 格子にオブジェクトを追加する
_grid[index].push(obj);
}
checkGrid();
}
private function checkGrid():void
{
// 各格子をループする
for (var i:int = 0; i < _numCols; i++)
{
for (var j:int = 0; j < _numRows; j++)
{
// 最初の格子の中のオブジェクトを互いに調べる
checkOneCell(i, j);
checkTwoCells(i, j, i + 1, j); // 右
checkTwoCells(i, j, i - 1, j + 1); // 左下
checkTwoCells(i, j, i, j + 1); // 下
checkTwoCells(i, j, i + 1, j + 1); // 右下
}
}
}
private function checkOneCell(x:int, y:int):void
{
// (x, y)で表される格子を得る
var cell:Vector.<DisplayObject> = _grid[y * _numCols + x];
if (cell == null) return;
// 格子中のオブジェクトの個数
var cellLength:int = cell.length;
// 全てのオブジェクトを互いに比較する
for (var i:int = 0; i < cellLength - 1; i++)
{
var objA:DisplayObject = cell[i];
for (var j:int = i + 1; j < cellLength; j++)
{
var objB:DisplayObject = cell[j];
_checks.push(objA, objB);
}
}
}
private function checkTwoCells(x1:int, y1:int,
x2:int, y2:int):void
{
// 格子が存在することを確認
if (x2 >= _numCols || x2 < 0 || y2 >= _numRows) return;
// 各格子にオブジェクトが存在することを確認
var cellA:Vector.<DisplayObject> =
_grid[y1 * _numCols + x1];
var cellB:Vector.<DisplayObject> =
_grid[y2 * _numCols + x2];
if (cellA == null || cellB == null) return;
var cellALength:int = cellA.length;
var cellBLength:int = cellB.length;
// 格子中の全てのオブジェクトを他の格子の全ての
// オブジェクトと比較する
for (var i:int = 0; i < cellALength; i++)
{
var objA:DisplayObject = cellA[i];
for (var j:int = 0; j < cellBLength; j++)
{
var objB:DisplayObject = cellB[j];
_checks.push(objA, objB);
}
}
}
public function get checks():Vector.<DisplayObject>
{
return _checks;
}
}