Clase de Coliciones en ActionScript 3 | Phoxer.com
Clase de Coliciones en ActionScript 3
05/11/2010

A la hora de programar un juego las coliciones de Graficos son muy importantes, por eso he desarrollado esta clase que me permite agrupar objetos y clasificarlos por grupos para detectar coliciones entre ellos.

Primero lo primero.. la clase que se encarga de detectar las colociones:

/**
By ::[PHOXER]::
http://www.phoxer.com
v 1.3;
*/
package phoxer.Games{
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
 
	public class HitTest {
 
		public static function complexHitTestObject(target1:DisplayObject,target2:DisplayObject,accurracy:Number=1,offSetPoint:Point=null):Boolean {
			return complexIntersectionRectangle(target1,target2,accurracy,offSetPoint).width != 0;
		}
 
		public static function intersectionRectangle( target1:DisplayObject, target2:DisplayObject ):Rectangle {
			if (! target1.root||! target2.root||! target1.hitTestObject(target2)) {
				return new Rectangle();
			}
			var bounds1:Rectangle=target1.getBounds(target1.root);
			var bounds2:Rectangle=target2.getBounds(target2.root);
			var intersection:Rectangle = new Rectangle();
			intersection.x=Math.max(bounds1.x,bounds2.x);
			intersection.y=Math.max(bounds1.y,bounds2.y);
			intersection.width= Math.min( ( bounds1.x + bounds1.width ) - intersection.x, ( bounds2.x + bounds2.width ) - intersection.x );
			intersection.height = Math.min( ( bounds1.y + bounds1.height ) - intersection.y, ( bounds2.y + bounds2.height ) - intersection.y );
			return intersection;
		}
 
		public static function complexIntersectionRectangle(target1:DisplayObject,target2:DisplayObject,accurracy:Number=1,offSetPoint:Point=null):Rectangle {
			if (accurracy<=0) {
				throw new Error("ArgumentError: Error #5001: Invalid value for accurracy",5001);
			}
			if (! target1.hitTestObject(target2)) {
				return new Rectangle();
			}
 
			var hitRectangle:Rectangle=intersectionRectangle(target1,target2);
			if (hitRectangle.width*accurracy<1||hitRectangle.height*accurracy<1) {
				return new Rectangle();
			}
 
			var bitmapData:BitmapData=new BitmapData(hitRectangle.width*accurracy,hitRectangle.height*accurracy,false,0x000000);
			bitmapData.draw( target1, HitTest.getDrawMatrix(target1,hitRectangle,accurracy,offSetPoint), new ColorTransform( 1, 1, 1, 1, 255, -255, -255, 255 ) );
			bitmapData.draw( target2, HitTest.getDrawMatrix(target2,hitRectangle,accurracy,offSetPoint), new ColorTransform( 1, 1, 1, 1, 255, 255, 255, 255 ), BlendMode.DIFFERENCE );
			var intersection:Rectangle=bitmapData.getColorBoundsRect(0xFFFFFFFF,0xFF00FFFF);
			bitmapData.dispose();
			if (accurracy!=1) {
				intersection.x/=accurracy;
				intersection.y/=accurracy;
				intersection.width/=accurracy;
				intersection.height/=accurracy;
			}
 
			intersection.x+=hitRectangle.x;
			intersection.y+=hitRectangle.y;
			return intersection;
		}
 
		protected static function getDrawMatrix( target:DisplayObject, hitRectangle:Rectangle,accurracy:Number,offSetPoint:Point=null):Matrix {
			var localToGlobal:Point;
			var matrix:Matrix;
			var rootConcatenatedMatrix:Matrix=target.root.transform.concatenatedMatrix;
			localToGlobal = target.localToGlobal( new Point( ) );
			matrix=target.transform.concatenatedMatrix;
			matrix.tx=localToGlobal.x-hitRectangle.x;
			matrix.ty=localToGlobal.y-hitRectangle.y;
			if(offSetPoint){
				matrix.tx = matrix.tx-(offSetPoint.x); 
				matrix.ty = matrix.ty-(offSetPoint.y); 
			}
			matrix.a=matrix.a/rootConcatenatedMatrix.a;
			matrix.d=matrix.d/rootConcatenatedMatrix.d;
			if (accurracy!=1) {
				matrix.scale( accurracy, accurracy );
			}
			return matrix;
		}
	}
}


y aquí mi clase HitObjectsGroup:

/**
By ::[PHOXER]::
http://www.phoxer.com
v 3.0;
*/
package phoxer.Games{
	import flash.display.DisplayObject;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.geom.Point;
 
	public class HitObjectsGroup{
		private var objects:Array;
		private var htmr:Timer;
		public var offSetPoint:Point=null;
		public function HitObjectsGroup(tm:int=10){
			objects=new Array();
			htmr= new Timer(tm,0);
		}
 
		/**
		@addObject
		clp : Clip principal
		grp : grupo de colicion al que pertenece
		hgrp: grupo con el que hace colicion
		ign : Array de grupos a ignorar
		bk  : funcion a la que devuelve datos cuando coliciona
		hit : hit area real, si es que esta dentro del clip principal
		rm  : si el clip puede ser borrado o no de la lista de colision
		nbk : funcion que llama cuando no se coliciona.
		rpt : boleano que indica si ametralla las llamadas a las funciones.
		*/
		public function addObject(clp:DisplayObject,grp:String,bk:Function=null,hit:DisplayObject=null,rm:Boolean=true,nbk:Function=null,rpt:Boolean=true,ign:Array=null):void{
			var obj:Object = new Object();
			obj.clip=clp;
			obj.hit=(hit!=null)? hit:clp;
			obj.grp=grp;
			obj.hgrp="";
			obj.ign=ign;
			obj.bk=bk;
			obj.nbk=nbk;
			obj.rm=rm;
			obj.rpt=rpt;
			obj.hrp=true;
			obj.nrp=true;
			objects.push(obj);
		}
 
		public function removeObject(obj:Object):void{
			for (var i:int=0;i<objects.length;i++){
				if(objects[i]===obj){
					objects[i]=null;
					objects.splice(i,1);
					break;
				}
			}
		}
 
		public function removeObjectByClip(clip:DisplayObject):void{
			for (var i:int=0;i<objects.length;i++){
				if(objects[i].clip===clip){
					objects[i]=null;
					objects.splice(i,1);
					break;
				}
			}
		}
 
		public function removeGroup(grp:String):void{
			for (var i:int=0;i<objects.length;i++){
				if(objects[i].grp===grp){
					objects[i]=null;
					objects.splice(i,1);
				}
			}
		}
 
		public function removeAllObjects():void{
			for (var i:int=0;i<objects.length;i++){
				objects[i]=null;
			}
			objects=null;
			objects=new Array();
		}
 
		public function startCheck():void{
			htmr.addEventListener(TimerEvent.TIMER,checkHits,false,0,true);
			htmr.start();
		}
		public function stopCheck():void{
			htmr.stop();
			htmr.removeEventListener(TimerEvent.TIMER,checkHits);
		}
 
		private function checkHits(e:TimerEvent):void{
			if(objects.length>1){
				for (var a:int=0;a<objects.length;a++){
					var t1:Object=objects[a];
					if(checkClip(t1.clip)){
						for (var b:int=0;b<objects.length;b++){
							var t2:Object=objects[b];
							if(checkClip(t2.clip)){
								if(t1.grp!=t2.grp){
									if(t1.clip.hitTestObject(t2.clip)){
										if(!ignoreGroup(t1.ign,t2.grp)){
											if(HitTest.complexHitTestObject(t1.hit,t2.hit,1,offSetPoint)){
												if(t1.bk!=null && t1.hrp){
													if(!t1.rpt){
														t1.hrp=false;
													}
													t1.hgrp=t2.grp;
													t1.bk(t1);
												}
												if(t2.bk!=null && t2.hrp){
													if(!t2.rpt){
														t2.hrp=false;
													}
													t2.hgrp=t1.grp;
													t2.bk(t2);
												}
												if(t1.rm){
													removeObject(t1);
												}
												if(t2.rm){
													removeObject(t2);
												}
												t1.nrp=true;
												t2.nrp=true;
												break;
											}else{
												onNoHit(t1);
												onNoHit(t2);
											}
										}
									}else{
										onNoHit(t1);
										onNoHit(t2);
									}
								}
							}
						}
					}
				}
			}
		}
 
		private function onNoHit(obj:Object):void{
			if(obj.nrp && obj.nbk!=null){
				if(!obj.rpt){
					obj.nrp=false;
				}
				obj.nbk(obj);
			}
			obj.hrp=true;
		}
 
		private function checkClip(clp:DisplayObject):Boolean{
			if(clp!=null){
				return clp.visible;
			}
			return false;
		}
 
		private function ignoreGroup(ign:Array,grp:String):Boolean{
			if(ign!=null){
				for(var i:int=0;i<ign.length;i++){
					if(ign[i]==grp){
						return true;
						break;
					}
				}
			}
			return false;
		}
	}
}


Mi clase utiliza grupos de Objetos para guardar información y funciona con cualquier otra clase de coliciones externas, asi sea como la Utils de Adobe:
y es muy facil de implementar:

import phoxer.Games.HitObjectsGroup;
 
var htsgrp:HitObjectsGroup = new HitObjectsGroup(100);
htsgrp.addObject(ball,"balls",onHitObject);
htsgrp.addObject(scuare,"scuares",onHitObject);
 
function onHitObject(dt:Object):void{
	//datos de las coliciones entre los objetos
}
htsgrp.startCheck();


Gracias a esta clase he desarrollado todos mis juegos facilitandome mucho la parte de las coliciones.

He desarrollado un juego parecido al Invaders con esta clase:


Aqui pueden ver mas juegos desarrollados por mi utilizando la clase de Colisiones:
www.BeatsGames.com -Mi pagina de juegos
BeatsGames -Mis Juegos en Facebook