/**
* Copyright kataribe ( http://wonderfl.net/user/kataribe )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/pUMl
*/
package{
import flash.text.TextField;
import flash.geom.Point;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.display.Sprite;
[SWF(width="465", height="465", backgroundColor="#000000", frameRate="60")]
public class _root extends Sprite{
public static const WIDTH:uint = 1920; //マップの横の長さ
public static const HEIGHT:uint = 1920; //マップの縦の長さ
public static var grid:_grid;
public static var camera:Point;
protected var key:Vector.<Boolean> = new Vector.<Boolean>(512, false);
protected var player:_player = new _player(WIDTH/2, HEIGHT/2, 0xFF0000);
//コンストラクタ
public function _root(){
grid = new _grid();
camera = new Point();
alpha = 0.5;
addChild(grid);
addChild(player);
addChild(new _player(WIDTH/2, 500, 0x0000FF));
//イベントリスナに登録
addEventListener(Event.EXIT_FRAME, main);
stage.addEventListener(KeyboardEvent.KEY_DOWN, on_keydown);
stage.addEventListener(KeyboardEvent.KEY_UP, on_keyup);
stage.addEventListener(MouseEvent.MOUSE_DOWN, on_mouse);
}
//Event.EXIT_FRAME
public function main(e:Event):void{
//プレイヤーをキー操作
if(key[65])player.move(_player.LEFT);
if(key[68])player.move(_player.RIGHT);
if(key[87])player.move(_player.UP);
if(key[83])player.move(_player.DOWN);
//プレイヤーが中心に映るようにカメラをセット
camera.x = player.x-232;
camera.y = player.y-232;
x = -camera.x;
y = -camera.y;
//プレイヤーにカーソルの方向を向かせる
const rad:Number = Math.atan2(camera.y+stage.mouseY-player.y, camera.x+stage.mouseX-player.x);
player.rotation = rad*180/Math.PI+90;
}
//KeyboardEvent.KEY_DOWN
public function on_keydown(e:KeyboardEvent):void{
key[e.keyCode] = true;
}
//KeyboardEvent.KEY_UP
public function on_keyup(e:KeyboardEvent):void{
key[e.keyCode] = false;
}
//MouseEvent.MOUSE_DOWN
public function on_mouse(e:MouseEvent):void{
player.shot(camera.x+e.stageX, camera.y+e.stageY);
}
}
}
import flash.geom.Rectangle;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.text.TextField;
import flash.display.Sprite;
import flash.events.Event;
import flash.display.Shape;
//双方向リスト
class _list{
public var head:_node = null;
public var tail:_node = null;
//ノードを追加
public function push(value:*):_node{
var tmp:_node = new _node();
tmp.list = this;
tmp.value = value;
if(tail){
tail.next = tmp;
tmp.prev = tail;
}else{
head = tmp;
}
tail = tmp;
return tmp;
}
}
//双方向リストのノード
class _node{
public var list:_list;
public var prev:_node;
public var next:_node;
public var value:*;
//リストから抜ける
public function leave():void{
if(list){
if(prev){
prev.next = next;
if(list.tail==this)list.tail = prev;
prev = null;
}else if(list.tail==this)list.tail = null;
if(next){
next.prev = prev;
if(list.head==this)list.head = next;
next = null;
}else if(list.head==this)list.head = null;
}
}
}
//線形四分木
class _grid extends Shape{
public static const LEVEL:uint = 4; //分割数
protected var cells:Vector.<_list>; //格子配列
protected var cell_num:Vector.<uint> = new Vector.<uint>(LEVEL, true); //階層ごとのルート空間からの要素数
//コンストラクタ
public function _grid(){
var i:uint;
//格子内にリストを準備
cell_num[0] = 1;
for(i=1; i<LEVEL; ++i)cell_num[i] = cell_num[i-1]*4;
cells = new Vector.<_list>(cell_num[LEVEL-1]*4, true);
for(i=0; i<cells.length; ++i)cells[i] = new _list();
//枠線を引く
const CELL_SIZE:uint = _root.WIDTH/(1<<LEVEL);
graphics.lineStyle(1, 0x000000, 0.5);
for(i=0; i<_root.WIDTH+1; i+=CELL_SIZE){
graphics.moveTo(i, 0);
graphics.lineTo(i, _root.HEIGHT);
}for(i=0; i<_root.HEIGHT+1; i+=CELL_SIZE){
graphics.moveTo(0, i);
graphics.lineTo(_root.WIDTH, i);
}
graphics.endFill();
//イベントリスナに登録
addEventListener(Event.ENTER_FRAME, on_enter);
}
//空間番号の算出用にビット値を切り離す
protected function separate(n:int):uint{
n = (n|(n<<8)) & 0x00ff00ff;
n = (n|(n<<4)) & 0x0f0f0f0f;
n = (n|(n<<2)) & 0x33333333;
return (n|(n<<1)) & 0x55555555;
}
//座標から空間番号を算出
protected function point_to(x:int, y:int):uint{
return separate(x / 1>>LEVEL) | (separate(y / 1>>LEVEL)<<1);
}
//矩形範囲から空間番号を算出
protected function rect_to(x1:int, y1:int, x2:int, y2:int):uint{
var tmp:uint = point_to(x2, y2);
var i:int;
//所属レベルを算出
var k:uint = point_to(x1, y1)^tmp;
for(i=LEVEL; i; --i){
if(k>>((i-1)*2))break;
}
tmp>>=i*2; //所属レベルからローカル番号を算出
tmp+=(cell_num[LEVEL-i] - 1) / 3 //ルート空間からの番号にする
return tmp;
}
//当たり判定を登録
public function join(hitmgr:_hitmgr):_node{
var i:uint = rect_to(
hitmgr.object.x + hitmgr.x,
hitmgr.object.y + hitmgr.y,
hitmgr.object.x + hitmgr.x + hitmgr.w,
hitmgr.object.y + hitmgr.y + hitmgr.h
);
hitmgr.morton = i;
return cells[i].push(hitmgr);
}
//Event.ENTER_FRAME
public function on_enter(e:Event):void{
var node:_node;
var node2:_node;
var i:uint;
var j:uint;
//衝突検知
//下から上に探査していけば再帰しなくても走査できるんじゃないだろうか
for(i=cells.length-1; 0<i; --i){
//現在の格子内のノードの衝突を判定
if((node=cells[i].head))for(; node.next; node=node.next){
node.value.collision(node.next.value);
}
for(node=cells[i].head; node; node=node.next){
//上空間内のノードとの衝突を判定
j=i;
while(j){
j=(j-1)/4;
for(node2=cells[j].head; node2; node2=node2.next){
node.value.collision(node2.value);
}
}
//ルート空間との衝突を判定
for(node2=cells[0].head; node2; node2=node2.next){
node.value.collision(node2.value);
}
}
}
//ルート空間内での衝突を判定
if((node=cells[0].head))for(; node.next; node=node.next){
node.value.collision(node.next.value);
}
}
}
//当たり判定の管理
class _hitmgr{
protected var node:_node;
public var hitareas:_list = new _list; //判定リスト
public var x:int, y:int; //全ての判定を矩形に収めた時の位置
public var w:uint, h:uint; //全ての判定を矩形に収めた時の長さ
public var object:_object; //親オブジェクト
public var morton:uint; //所属している空間番号
//コンストラクタ
public function _hitmgr(object:_object){
this.object = object;
}
//判定を追加
public function add(hitarea:_hitarea):void{
if(hitarea.x < x)x = hitarea.x;
if(hitarea.y < y)y = hitarea.y;
if(x+w < hitarea.x+hitarea.w)w = hitarea.w-x;
if(y+h < hitarea.y+hitarea.h)h = hitarea.h-y;
hitareas.push(hitarea);
hitarea.object = object;
}
//更新
public function update():void{
if(node)node.leave();
node = _root.grid.join(this);
}
//衝突判定
public function collision(tgt:_hitmgr):void{
for(var nd:_node=hitareas.head; nd; nd=nd.next){
for(var nd2:_node=tgt.hitareas.head; nd2; nd2=nd2.next){
if(nd.value.collision(nd2.value)){
object.on_hit(tgt.object);
tgt.object.on_hit(object);
}
}
}
}
}
//衝突範囲のもと
//モートン番号を出すために矩形としての範囲情報を持っていなければならない
class _hitarea{
public var flag:uint;
//コピペ用 : public static const F_:uint = ;
public static const F_CIRCLE:uint = 0; //円判定
public static const F_RECT:uint = 1; //矩形判定
public var object:_object; //親オブジェクト
public var x:int, y:int; //親オブジェクトとの相対距離
public var w:uint, h:uint; //矩形で表した時の長さ
//コンストラクタ
public function _hitarea(flag:uint, x:int, y:int, w:uint, h:uint){
this.flag = flag;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//衝突判定
public function collision(tgt:_hitarea):Boolean{
return false;
}
}
//矩形判定
class _rect extends _hitarea{
//コンストラクタ
public function _rect(x:int, y:int, w:uint, h:uint){
super(F_RECT, x, y, w, h);
}
//衝突判定
override public function collision(tgt:_hitarea):Boolean{
switch(tgt.flag){
case F_RECT:
return object.x+x <= tgt.object.x+tgt.x+tgt.w &&
tgt.object.x+tgt.x <= object.x+x+w &&
object.y+y <= tgt.object.y+tgt.y+tgt.h &&
tgt.object.y+tgt.y <= object.y+y+h;
case F_CIRCLE:
return false;
}
return false;
}
}
//円判定
class _circle extends _hitarea{
public var r:uint; //半径
//コンストラクタ
public function _circle(x:int, y:int, r:uint){
super(F_CIRCLE, x-r, y-r, r*2, r*2);
this.r = r;
}
//衝突判定
override public function collision(tgt:_hitarea):Boolean{
switch(tgt.flag){
case F_RECT:
return false;
case F_CIRCLE:
var dis_x:uint = tgt.object.x - object.x;
var dis_y:uint = tgt.object.y - object.y;
var dis:uint = _circle(tgt).r + r;
return dis_x*dis_x + dis_y*dis_y <= dis*dis;
}
return false;
}
}
//もの
class _object extends Sprite{
public var flag:uint = 0; //色んな属性の識別用
//コピペ用 : public static const F_:uint = 1 << ;
public static const F_UNIT:uint = 1 << 0; //生きもの
public var hitmgr:_hitmgr; //当たり判定
//矩形判定を登録
protected function set_rect(w:uint, h:uint, x:int=0, y:int=0):void{
hitmgr.add(new _rect(x, y, w, h));
}
//円判定を登録
protected function set_circle(r:uint, x:int=0, y:int=0):void{
hitmgr.add(new _circle(x, y, r));
}
//コンストラクタ
public function _object(x:Number, y:Number){
this.x = x;
this.y = y;
hitmgr = new _hitmgr(this);
//イベントリスナに登録
addEventListener(Event.ADDED, on_added);
addEventListener(Event.REMOVED, on_removed);
}
//Event.ADDED_TO_STAGE
public function on_added(e:Event):void{
removeEventListener(Event.ADDED, on_added);
addEventListener(Event.ENTER_FRAME, on_enter);
}
//Event.REMOVED_FROM_STAGE
public function on_removed(e:Event):void{
removeEventListener(Event.ENTER_FRAME, on_enter);
removeEventListener(Event.REMOVED, on_removed);
}
//Event.ENTER_FRAME
public function on_enter(e:Event):void{
hitmgr.update();
}
//衝突時に呼び出される
public function on_hit(tgt:_object):void{}
}
//指定オブジェクトの所属している空間番号を表示する
class _morton_info extends _object{
protected var text:TextField = new TextField();
protected var bmd:BitmapData = new BitmapData(32, 16);
protected var object:_object;
//コンストラクタ
public function _morton_info(object:_object, x:int=-16, y:int=-8){
super(x, y);
this.object = object;
addChild(new Bitmap(bmd));
}
//Event.ENTER_FRAME
override public function on_enter(e:Event):void{
bmd.fillRect(new Rectangle(0, 0, 32, 16), 0xFFFFFF)
text.text = String(object.hitmgr.morton);
bmd.draw(text);
}
}
//動かせるもの
class _physical extends _object{
public var vx:Number = .0; //速度x
public var vy:Number = .0; //速度y
public var friction:Number = 0.9; //摩擦
//コンストラクタ
public function _physical(x:Number, y:Number){
super(x, y);
}
//Event.ENTER_FRAME
override public function on_enter(e:Event):void{
x += vx;
y += vy;
//減速
if(friction){
if(vx){
vx *= friction;
if(Math.abs(vx)<0.01)vx = 0;
}if(vy){
vy *= friction;
if(Math.abs(vy)<0.01)vy = 0;
}
}
super.on_enter(e);
}
}
//生きもの
class _unit extends _physical{
public var hp:Number = 100.0; //HP
//コンストラクタ
public function _unit(x:Number, y:Number){
super(x, y);
flag |= F_UNIT;
}
//Event.ENTER_FRAME
override public function on_enter(e:Event):void{
//HPが尽きたら無効化
if(hp<=0){
parent.removeChild(this);
return;
}
super.on_enter(e);
}
}
//プレイヤー
class _player extends _unit{
//コンストラクタ
public function _player(x:Number, y:Number, color:uint){
super(x, y);
graphics.beginFill(color);
graphics.lineStyle(1);
graphics.drawCircle(0, 0, 16);
graphics.moveTo(0, -30);
graphics.lineTo(-7, -23);
graphics.lineTo(7, -23);
graphics.endFill();
//当たり判定を登録
set_circle(16);
addChild(new _morton_info(this));
}
//Event.ENTER_FRAME
override public function on_enter(e:Event):void{
super.on_enter(e);
//マップの端を越えたら戻す
if(_root.WIDTH < x)x = _root.WIDTH;
else if(x < 0)x = 0;
if(_root.HEIGHT < y)y = _root.HEIGHT;
else if(y < 0)y = 0;
}
//移動
internal static const LEFT:uint = 1 << 0;
internal static const RIGHT:uint = 1 << 1;
internal static const UP:uint = 1 << 2;
internal static const DOWN:uint = 1 << 3;
public function move(FLAG:uint):void{
if(FLAG&LEFT && -5<vx)vx-=0.5;
if(FLAG&RIGHT && vx<5)vx+=0.5;
if(FLAG&UP && -5<vy)vy-=0.5;
if(FLAG&DOWN && vy<5)vy+=0.5;
}
//衝突
override public function on_hit(tgt:_object):void{
parent.removeChild(this);
}
//ショット
public function shot(mouse_x:Number, mouse_y:Number):void{}
}