Использование объекта EllipsoidCollider для определения столкновений

Материал из AlternativaPlatform Wiki

Перейти к: навигация, поиск

API Alternativa3D 7.7 предоставляет функционал для определения столкновений трехмерных объектов и обработки результатов этих столкновений. В данном уроке мы исследуем эти возможности. Для этого мы используем шаблон и нам потребуются некоторые знания из урока «Пересечение луча».

Итак, берем исходник
package {
import alternativa.engine3d.containers.ConflictContainer;
import alternativa.engine3d.controllers.SimpleObjectController;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.View;
 
import flash.display.Sprite;
import flash.events.Event;
 
[SWF(width="640", height="480", backgroundColor="#404060")]
public class Template extends Sprite {
    private var container:ConflictContainer;
    private var camera:Camera3D;
    private var controller:SimpleObjectController;
 
    public function Template() {
        super();
        container = new ConflictContainer();
        camera = new Camera3D();
        camera.z = 500;
        container.addChild(camera);
        controller = new SimpleObjectController(stage, camera, 200);
        controller.lookAtXYZ(200, 0, 0);
        camera.view = new View(640, 480);
        addChild(camera.view);
        addEventListener(Event.ENTER_FRAME, onEnterFrame);
 
    }
 
    private function onEnterFrame(event:Event):void {
        controller.update();
        camera.render();
    }
}
}

Добавим диаграмму для контроля состояния нашей системы

addChild(camera.diagram);

что, к слову, вовсе не обязательно. Но так мы сможем видеть насколько эффективно происходят все вычисления. Установим частоту кадров нашего ролика в 60

[SWF(width="640", height="480", frameRate="60", backgroundColor="#000000")]

что тоже весьма опционально. Изменим немного положение нашей камеры, чтобы сцену было видно полнее.

camera.y = 300;
camera.z = 300;
controller.lookAtXYZ(0, 0, 0);

Все подготовительные действия совершены. Давайте приступим к столкновениям. Перво-наперво определим общую переменную класса private var collider:EllipsoidCollider, в которой будет содержаться ссылка на объект вычисляющий столкновения и в конструкторе корневого класса создадим экземпляр, размером, совпадающий с нашими сферами:

collider = new EllipsoidCollider(RADIUS, RADIUS, RADIUS);

Зададим размер глобально

private const RADIUS:uint = 25;

Создадим необходимую нам геометрию: две сферы и плоскость

private var flat:Plane;
private var ball1:Sphere;
private var ball2:Sphere;

в определении корневого класса и

flat = new Plane(500, 500, 10, 10, false);
flat.setMaterialToAllFaces(new FillMaterial(0x111111, 1, 0, 0x666666));
flat.z = -RADIUS;
flat.addEventListener(MouseEvent3D.CLICK, onFlatClick);
container.addChild(flat);
 
ball1 = new Sphere(RADIUS, 16);
ball1.setMaterialToAllFaces(colors["blue"]);
container.addChild(ball1);
ball2 = new Sphere(RADIUS, 16);
ball2.setMaterialToAllFaces(colors["green"]);
ball2.visible = false;
container.addChild(ball2);

в конструкторе. Надо отметить, что плоскость flat, на которой располагаются наши сферы мы отпустим вниз на величину радиуса сфер flat.z = -RADIUS; - понятно для чего. Также назначим плоскости обработчик события клика по ней onFlatClick, в котором мы будем фиксировать координаты точки клика мыши по плоскости:

var data:RayIntersectionData = flat.intersectRay(e.localOrigin, e.localDirection);
if (!data) return;
clickPlace = data.point;

Как это работает и почему - смотрите в уроке «Пересечение луча».

Еще хочу отметить, что в конструкторе мы выключаем вторую сферу для отображения и в определении класса создаем заранее 3 простых материала типа FillMaterial для раскрашивания сфер в нужные цвета:

private var colors:Object = {
	"red": new FillMaterial(0xff0000), "green": new FillMaterial(0x00ff00), "blue": new FillMaterial(0x0000ff)
};

Наше приложение каждый кадр 60 раз в секунду в функции-обработчике события Event.ENTER_FRAME перерисовывает сцену и должно менять положение объектов, требующих анимации. Анимировать мы будем вторую сферу. Алгоритм будет прост. После клика пользователя на плоскости, где расположены сферы, вторая сфера появляется в точке клика (а её мы определили выше в обработчике onFlatClick) и начинает перемещаться в центр сцены, где расположена первая статичная сфера. Если вторая сфера сталкивается с первой, то она останавливается и окрашивается в красный цвет.

if (clickPlace) {
	ball2.x = clickPlace.x;
	ball2.y = clickPlace.y;
	ball2.setMaterialToAllFaces(colors["green"]);
	ball2.visible = true;
	clickPlace = null;
}
 
var angle:Number = Math.atan2(ball2.y, ball2.x);
var delta:Vector3D = new Vector3D(SPEED * Math.cos(angle), SPEED * Math.sin(angle));
var oldPos:Vector3D = new Vector3D(ball2.x, ball2.y, ball2.z);
var newPos:Vector3D = collider.calculateDestination(oldPos, delta, ball1);
ball2.x = newPos.x;
ball2.y = newPos.y;
if (collider.getCollision(oldPos, delta, new Vector3D(), new Vector3D(), ball1))
	ball2.setMaterialToAllFaces(colors["red"]);

Как видите, если зарегистрирована точка клика clickPlace, то сфера помещается в эту точку, становится видимой и окрашивается в зеленый цвет. Далее мы вычисляем все необходимые нам для перемещения второй сферы параметры: угол сферы относительно центра сферы angle, приращение координат delta при перемещении, старое местоположение oldPos и новое newPos. Причем, новое местоположение сферы вычисляет нам объект collider методом calculateDestination. Этот же объект следит за столкновением геометрии ball1 и ball2 в методе getCollision.

Вот код всего урока:

package {
 
	import alternativa.engine3d.containers.ConflictContainer;
	import alternativa.engine3d.controllers.SimpleObjectController;
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.View;
	import alternativa.engine3d.core.EllipsoidCollider;
	import alternativa.engine3d.core.MouseEvent3D;
	import alternativa.engine3d.core.RayIntersectionData;
	import alternativa.engine3d.primitives.Plane;
	import alternativa.engine3d.primitives.Sphere;
	import alternativa.engine3d.materials.FillMaterial;
 
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Vector3D;
 
	[SWF(width="640", height="480", frameRate="60", backgroundColor="#000000")]
	public class EllipsoidColliderTest extends Sprite {
 
		private var container:ConflictContainer;
		private var camera:Camera3D;
		private var controller:SimpleObjectController;
 
		private const SPEED:Number = -2;
		private const RADIUS:uint = 25;
		private var flat:Plane;
		private var ball1:Sphere;
		private var ball2:Sphere;
		private var clickPlace:Vector3D;
		private var collider:EllipsoidCollider;
		private var colors:Object = {
			"red": new FillMaterial(0xff0000), "green": new FillMaterial(0x00ff00), "blue": new FillMaterial(0x0000ff)
		};
 
		public function EllipsoidColliderTest () {
			container = new ConflictContainer();
			camera = new Camera3D();
			camera.y = 300;
			camera.z = 300;
			camera.view = new View(640, 480);
			addChild(camera.view);
			addChild(camera.diagram);
			container.addChild(camera);
 
			collider = new EllipsoidCollider(RADIUS, RADIUS, RADIUS);
 
			controller = new SimpleObjectController(stage, camera, 200);
			controller.lookAtXYZ(0, 0, 0);
 
			flat = new Plane(500, 500, 10, 10, false);
			flat.setMaterialToAllFaces(new FillMaterial(0x111111, 1, 0, 0x666666));
			flat.z = -RADIUS;
			flat.addEventListener(MouseEvent3D.CLICK, onFlatClick);
			container.addChild(flat);
 
			ball1 = new Sphere(RADIUS, 16);
			ball1.setMaterialToAllFaces(colors["blue"]);
			container.addChild(ball1);
			ball2 = new Sphere(RADIUS, 16);
			ball2.setMaterialToAllFaces(colors["green"]);
			ball2.visible = false;
			container.addChild(ball2);
 
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
 
		}
 
		private function onEnterFrame(event:Event):void {
			if (clickPlace) {
				ball2.x = clickPlace.x;
				ball2.y = clickPlace.y;
				ball2.setMaterialToAllFaces(colors["green"]);
				ball2.visible = true;
				clickPlace = null;
			}
 
			var angle:Number = Math.atan2(ball2.y, ball2.x);
			var delta:Vector3D = new Vector3D(SPEED * Math.cos(angle), SPEED * Math.sin(angle));
			var oldPos:Vector3D = new Vector3D(ball2.x, ball2.y, ball2.z);
			var newPos:Vector3D = collider.calculateDestination(oldPos, delta, ball1);
			ball2.x = newPos.x;
			ball2.y = newPos.y;
			if (collider.getCollision(oldPos, delta, new Vector3D(), new Vector3D(), ball1))
				ball2.setMaterialToAllFaces(colors["red"]);
 
			controller.update();
			camera.render();
		}
 
		private function onFlatClick (e:MouseEvent3D):void {
			var data:RayIntersectionData = flat.intersectRay(e.localOrigin, e.localDirection);
			if (!data) return;
			clickPlace = data.point;
		}
	}
}
Личные инструменты
Пространства имён
Варианты
Действия
Навигация
Category
Инструменты
На других языках