使用Unity的时候了解到Unity可通过射线碰撞检测来精确判断地形,从而实现不规则地形角色的运动模拟,那么away3d是否也有相同功能呢?通过样例学习,发现away3d中有鼠标拾取的功能。而这个功能的核心之一恰好就是Raycast方式;这个方式的原理就是从摄影机发射一条射线并判断焦点从而完成三维物件的拾取。那么,这条射线是否可人为定义从指定位置发射呢?答案是肯定的。away3d.core.pick.RaycastPicker就提供了此功能;RaycastPicker的getEntityCollision方法可获取射线所经过区域的交点;焦点是away3d.core.pick.PickingColliderVO类型的对象,包含了碰撞射线的属性;通过该对象的rayEntryDistance属性是射线起点到交点的长度;rayEntityCollision的参数有三个:第一是射线起点位置向量,第二参数是射线方向向量,第三参数是需要碰撞碰撞检查的Vector.<Entity>组;知道了这些,下面就动手看看是不是如自己所想吧;
import away3d.core.pick.RaycastPicker;
import away3d.core.pick.PickingCollisionVO;
import away3d.containers.View3D;
import away3d.primitives.CubeGeometry;
import away3d.materials.ColorMaterial;
import away3d.primitives.PlaneGeometry;
import away3d.entities.Mesh;
import away3d.entities.Entity;
import away3d.controllers.HoverController;
import away3d.primitives.SphereGeometry;
import away3d.entities.Sprite3D;
import away3d.materials.TextureMaterial;
import away3d.textures.BitmapTexture;
import away3d.core.pick.PickingColliderType;
var v3D:View3D=new View3D();
stage.addChild(v3D);
var box1:Mesh=new Mesh(new CubeGeometry(100,100),new ColorMaterial(0xFFFF00,0.4));
var box2:Mesh=new Mesh(new SphereGeometry(100),new ColorMaterial(0x00FFFF,0.8));
var pln:Mesh=new Mesh(new PlaneGeometry(1000,1000));
v3D.scene.addChild(pln);
v3D.scene.addChild(box1);
v3D.scene.addChild(box2);
box1.y=100;
box2.y=100;
box2.x=300;
var sp:Sprite3D=new Sprite3D(new TextureMaterial(new BitmapTexture(new MAPP())),10,10);
TextureMaterial(sp.material).alphaBlending=true;
v3D.scene.addChild(sp);
var hch:HoverControllerHelper=new HoverControllerHelper(stage,new HoverController(v3D.camera,pln));
通过以上代码搭建了测试场景,一个球体,一个方块和一个平面(作为地板);并设置了悬停控制器来观察场景的内容;增设了一个反应碰撞交点的Sprite3D作为指示器;而下面才是重头戏。
var rc:RaycastPicker=new RaycastPicker(true);
建立RaycastPicker 并设置其沿着最近的距离开始寻找位置(参数true)
var direct:Vector3D=new Vector3D(box2.x-box1.x,box2.y-box1.y,box2.z,-box1.z);
direct.normalize();
这里需要得到box1到box2向量的方向;所以通过上式计算得出;
var Vo:PickingCollisionVO=rc.getEntityCollision(box1.position,direct,Vector.<Entity>([box2]));
下面求得交点;如果没有getEntityCollision方法会返回null所以下面的判断还需要注意一下;
addEventListener(Event.ENTER_FRAME,run);
为了让程序运行,需要为view3d加入一个驱动,刷新屏幕;或许通过动态的判断检测效果会更直接,所以干脆把getEntityCollision放在驱动中;这里需要注意的是PickingCollisionVO仅仅包含了射线的属性(长度,起点,方向等)是不包含交点的。 不过可以以下方式计算出终点的位置;
var diro:Vector3D=new Vector3D(box1.x+direct.x*dis,box1.y+direct.y*dis,box1.z+direct.z*dis);
这样再持续将交点指示器位置更新就大功告成了; 为了直观,在每一帧给box2的rotationY加了一个增量;
function run(e:Event){
Vo=rc.getEntityCollision(box1.position,new Vector3D(1,0,0),Vector.<Entity>([box2]));
if(Vo!=null){
sp.visible=true;
var dis:Number=Vo.rayEntryDistance;
var diro:Vector3D=new Vector3D(box1.x+direct.x*dis,box1.y+direct.y*dis,box1.z+direct.z*dis);
sp.position=diro;
txt.text="distance="+dis;
}else{
if(sp.visible){
sp.visible=false;
}
}
box2.rotationY+=0.3;
if(box2.rotationY>360){
box2.rotationY-=360;
}
v3D.render();
}
经过测试发觉对于box物体可完美运行,而对于球体则不准确,忽然想起pickingCollider属性,设置其为PickingColliderType.AS3_FIRST_ENCOUNTERED或AS3_BEST_HIT之后,完美运行。
box2.pickingCollider=PickingColliderType.AS3_BEST_HIT;//AS3_FIRST_ENCOUNTERED;