Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package example.portal
- {
- import flash.events.WeakFunctionClosure;
- import flash.printing.PrintJob;
- import nape.callbacks.CbEvent;
- import nape.callbacks.CbType;
- import nape.callbacks.InteractionCallback;
- import nape.callbacks.InteractionListener;
- import nape.callbacks.InteractionType;
- import nape.callbacks.PreCallback;
- import nape.callbacks.PreFlag;
- import nape.callbacks.PreListener;
- import nape.constraint.Constraint;
- import nape.constraint.ConstraintIterator;
- import nape.dynamics.Arbiter;
- import nape.dynamics.CollisionArbiter;
- import nape.dynamics.Contact;
- import nape.dynamics.ContactList;
- import nape.geom.Vec2;
- import nape.phys.Body;
- import nape.shape.Shape;
- import nape.shape.ShapeIterator;
- import nape.space.Space;
- /**
- * ...
- * @author liu wong
- *
- */
- public class PortalManager
- {
- public var PORTAL:CbType;
- // CbType for a Shape that is permitted to pass through portals.
- public var PORTABLE:CbType;
- // CbType for a Shape which is intersecting a portal sensor.
- public var PARTIAL:CbType;
- private var portalID:int = 0;
- public function PortalManager(space:Space):void
- {
- PORTAL = new CbType();
- PORTABLE = new CbType();
- PARTIAL = new CbType();
- space.listeners.add(new InteractionListener(
- CbEvent.BEGIN,
- InteractionType.SENSOR,
- PORTAL,
- PORTABLE,
- portalBegin
- ));
- space.listeners.add(new InteractionListener(
- CbEvent.END,
- InteractionType.SENSOR,
- PORTAL,
- PORTABLE,
- portalEnd
- ));
- space.listeners.add(new PreListener(
- InteractionType.COLLISION,
- PARTIAL,
- CbType.ANY_SHAPE,
- backCollision
- ));
- }
- private function portalBegin(cb:InteractionCallback):void
- {
- //---
- var portalSensor:Shape = cb.int1.castShape;
- var shape:Shape = cb.int2.castShape;
- var body:Body = shape.body;
- // Can happen in rare circumstances where an END is processed before the BEGIN occurs with CCD
- if (body == null) {
- return;
- }
- //trace(portalSensor.userData, shape.userData, body, body.userData);
- var portalData:* = portalSensor.userData;
- var shapeData:* = shape.userData;
- var bodyData:* = body.userData;
- var portal:Portal = portalData.portal;
- // Cases to consider:
- //
- // Either this shape is already interacting via this portal
- // (Still has a cloned shape on otherside) and has started to
- // overlap again on other side.
- //
- // This shape has just started interacting via the portal and:
- // This is the first shape of the body to interact
- // The body is already interacting via this portal.
- //
- // Also possible for shape which has passed through back of portal
- // sensor, to then intersect the target portal sensor before exiting
- // and must be ignored.
- //
- // A further fucking case occurs when we have portals very close together
- // and we can have portals arrange like || || with a body passing from left
- // to right, at the point where the body (partially passed through first two)
- // started intersecting the next portal in the line, then moves backwards.
- // the cloned body from second set of portals then enters 'back' into the first
- // set. We want to treat this as one continuous 'chain' and not permit such
- // back portalling. (#). We cannot however permit cyclic portalling.
- // Search for existing PortalPair for this (portal,body) pair
- var portalPair:PortalPair = null;
- if (bodyData.portal_pairs != null)
- {
- for each(var pair2:PortalPair in bodyData.portal_pairs)
- //for (pair in bodyData.portal_pairs)
- {
- if ((pair2.portalA == portal || pair2.portalB == portal)
- && (pair2.bodyA == body || pair2.bodyB == body)) {
- portalPair = pair2;
- break;
- }
- }
- }
- // Ensure we're not entering a portal that is behind one we're currently going through.
- if (portalPair == null && shapeData.portal_id != null && shape.cbTypes.has(PARTIAL)) {
- for each(var portalDt:PortalShapeData in shapeData.portals)
- //for (portalData in shapeData.portals)
- {
- if (behindPortal(portalDt.portal, portal.sensor.worldCOM))
- {
- return;
- }
- }
- }
- var targetPortal:Portal = portal.target;
- var scale:Number = targetPortal.width / portal.width;
- if (portalPair == null)
- {
- // Check we don't have case (#)
- if (bodyData.portal_pairs != null) {
- var stack:Array = [body];
- var visited:Array = [body];
- var longChain:Boolean = false;
- while (stack.length > 0 && !longChain)
- {
- //fixed zver
- //var body:Body = stack.pop();
- var bdy:Body = stack.pop();
- var bodyData2:* = bdy.userData;
- for each(var pair:PortalPair in bodyData2.portal_pairs)
- //for (pair in bodyData.__portal_pairs)
- {
- var otherBody:Body;
- if (pair.bodyA == bdy)
- otherBody = pair.bodyB
- else
- otherBody = pair.bodyA;
- if (visited.indexOf(otherBody) == -1)
- //if (Lambda.indexOf(visited, otherBody) == -1)
- {
- visited.push(otherBody);
- stack.push(otherBody);
- }
- if (pair.portalA == portal || pair.portalB == portal) {
- longChain = true;
- break;
- }
- }
- }
- if (longChain)
- {
- return;
- }
- }
- // This is a brand new interaction between this body and portal.
- // Create cloned body with initial cloned shape.
- var clone:Body = new Body();
- var cloneShape:Shape = shape.copy();
- cloneShape.scale(scale, scale);
- cloneShape.body = clone;
- clone.space = body.space;
- //trace("create portal joint");
- //trace(portal.position, portal.direction, targetPortal.position, targetPortal.direction, scale);
- // Create portal constraint.
- var pcon:PortalConstraint = new PortalConstraint(
- portal.body, portal.position, portal.direction,
- targetPortal.body, targetPortal.position, targetPortal.direction,
- scale, body, clone
- );
- pcon.space = clone.space;
- // Set properties of clone to satisfy constraint.
- pcon.setProperties(clone, body);
- //trace("info:", clone, body, clone.position, body.position);
- // Create portal pair.
- portalPair = new PortalPair(portal, targetPortal, body, clone);
- // Assign to pair lists.
- if (bodyData.portal_pairs == null)
- {
- bodyData.portal_pairs = [];
- }
- bodyData.portal_pairs.push(portalPair);
- var cloneData:* = clone.userData;
- cloneData.portal_pairs = [portalPair];
- // Assign id if not existing.
- var id:* = shapeData.portal_id;
- var portals:Array/*PortalShapeData*/ = shapeData.portals;
- //if (id == 0)
- if (id == null)
- {
- id = shapeData.portal_id = portalID++;
- portals = shapeData.portals = [];
- shapeData.portal_active = 0;
- }
- portals.push(
- new PortalShapeData(portal, false)
- );
- shapeData.portal_active++;
- var cloneShapeData:* = cloneShape.userData;
- cloneShapeData.portal_id = id;
- cloneShapeData.portals = [
- new PortalShapeData(portal.target, true)
- ];
- cloneShapeData.portal_active = 0;
- // Add PARTIAL CbTypes if it is not already present.
- if (!shape.cbTypes.has(PARTIAL))
- {
- shape.cbTypes.add(PARTIAL);
- cloneShape.cbTypes.add(PARTIAL);
- }
- }
- else {
- // Portal interaction exists for this body and portal.
- // Check shape, passed through front is not now just intersecting back
- // (Occurs if portals placed very close together into eachother)
- //
- // Also need to check that if shape pair exists, then at least one of
- // the portals is the same to avoid incrementing count. Can occur if
- // if the portals are not symmetric.
- var anyEqual:Boolean = false;
- if (shapeData.portals != null)
- {
- for each(var portalDt2:PortalShapeData in shapeData.portals)
- //for (portalData in shapeData.__portals)
- {
- if (portalDt2.portal.target == portal)
- {
- return;
- }
- if (portalDt2.portal == portal)
- {
- anyEqual = true;
- }
- }
- }
- // Check to see if Shape is already part of the interaction.
- var clone2:Body;
- if (portalPair.bodyA == body)
- clone2 = portalPair.bodyB
- else
- clone2 = portalPair.bodyA;
- var found:Boolean = false;
- //if (shapeData.portal_id != 0)
- if (shapeData.portal_id != null)
- {
- var si:ShapeIterator = clone2.shapes.iterator();
- while (si.hasNext())
- {
- var cloneShape2:Shape = si.next();
- var cloneShapeData2:* = cloneShape2.userData;
- if (cloneShapeData2.portal_id == shapeData.portal_id)
- {
- found = true;
- break;
- }
- }
- //for each(var cloneShape:Shape in clone.shapes)
- //for (cloneShape in clone.shapes)
- }
- if (!found)
- {
- // Shape is not part of the interaction, create its clone.
- var cloneShape3:Shape = shape.copy();
- cloneShape3.scale(scale, scale);
- cloneShape3.body = clone2;
- //if (shapeData.portal_id == 0)
- if (shapeData.portal_id == null)
- {
- shapeData.portal_id = portalID++;
- shapeData.portals = [];
- shapeData.portal_active = 0;
- }
- shapeData.portals.push(
- new PortalShapeData(portal, false)
- );
- shapeData.portal_active++;
- var cloneShapeData3:* = cloneShape3.userData;
- cloneShapeData3.portal_id = shapeData.portal_id;
- cloneShapeData3.portals = [
- new PortalShapeData(portal.target, true)
- ];
- cloneShapeData3.portal_active = 0;
- // Add PARTIAL CbTypes if it is not already present.
- if (!shape.cbTypes.has(PARTIAL))
- {
- shape.cbTypes.add(PARTIAL);
- cloneShape3.cbTypes.add(PARTIAL);
- }
- }
- else
- if (anyEqual)
- {
- shapeData.portal_active++;
- }
- }
- //---
- }
- private function portalEnd(cb:InteractionCallback):void
- {
- //---
- var portalSensor:Shape = cb.int1.castShape;
- var shape:Shape = cb.int2.castShape;
- var body:Body = shape.body;
- // Can occur when an object has entered portal, then intersects
- // target portal before exiting.
- //
- // The final clean up of shapes can leave some such dangling cases.
- if (body == null)
- {
- return;
- }
- var portalData2:* = portalSensor.userData;
- var shapeData:* = shape.userData;
- //var bodyData:* = body.userData;
- var portal:Portal = portalData2.portal;
- // Two main cases to consider:
- // The shape has exited the portal sensor from the front
- // and we keep it in its current body
- //
- // Or the shape has exited through the back and should be
- // completely teleported to other side.
- //
- // This is complicated with multiple portals as we must
- // ensure we do not introduce 'cuts' in the portal chains
- // and so we choose the simpler option: Remove the shape
- // only when this is the 'last' 'exit' in the chain for
- // the shape.
- //
- // We use the userData field __portal_active to track this
- // and for robustneed compute which side the shape should move
- // to at this point, storing it in __portal_target boolean
- // (true) if shape is moved to target side of portal pair.
- var portals:Array/*PortalShapeData*/ = shapeData.portals;
- var portalData:PortalShapeData = null;
- for each(var pData:PortalShapeData in portals)
- //for (pData in portals)
- {
- if (pData.portal == portal)
- {
- portalData = pData;
- break;
- }
- }
- if (portalData == null)
- {
- // can occur if object enters portal, then intersects target portal
- // too. We ignore this in begin interaction
- // and we ignore it in end too.
- return;
- }
- // Just exited portal sensor, no longer active in chain.
- shapeData.portal_active--;
- portalData.portal_target = behindPortal(portal, shape.worldCOM);
- // Get list of all shapes in the portal chain.
- var shapes:Array = [shape];
- var stack:Array = [body];
- while (stack.length > 0)
- {
- var stackBody:Body = stack.pop();
- var stackData:* = stackBody.userData;
- for each(var pair:PortalPair in stackData.portal_pairs)
- //for (pair in stackData.portal_pairs)
- {
- var clone:Body;
- if (pair.bodyA == stackBody)
- clone = pair.bodyB
- else
- clone = pair.bodyA;
- //for each(var cloneShape:Shape in clone.shapes)
- //for (cloneShape in clone.shapes)
- var si:ShapeIterator = clone.shapes.iterator();
- while(si.hasNext())
- {
- var cloneShape:Shape = si.next();
- //var cloneShapeData:* = cloneShape.userData;
- // Possible new shape in chain.
- var sData:* = cloneShape.userData;
- if (sData.portal_active != null)
- {
- if (shapes.indexOf(cloneShape) == -1)
- //if (Lambda.indexOf(shapes, cloneShape) == -1)
- {
- shapes.push(cloneShape);
- stack.push(cloneShape.body);
- }
- }
- }
- }
- }
- // Check whether all shapes in chain are inactive.
- var anyActive:Boolean = false;
- for each(var s:Shape in shapes)
- //for (s in shapes)
- {
- var sData2:* = s.userData;
- if (sData2.portal_active != 0)
- {
- anyActive = true;
- break;
- }
- }
- // Nothing to do yet.
- if (anyActive)
- {
- return;
- }
- // All shapes are inactive.
- // We now proceed to remove all but 1 of the shapes in the chain
- // The remaining shape being the final destination of the portal chain.
- var survivors:Array = [];
- for each(var shape2:Shape in shapes)
- //for (shape in shapes)
- {
- var sData3:* = shape2.userData;
- var anyTarget:Boolean = false;
- for each(var portalDt:PortalShapeData in sData3.portals)
- //for (portalData in sData.portals)
- {
- if (portalDt.portal_target)
- {
- anyTarget = true;
- break;
- }
- }
- if (anyTarget)
- {
- //fix zver
- //var body:Body = shape.body;
- var bdy:Body = shape2.body;
- shape2.body = null;
- if (bdy.shapes.empty())
- {
- // disable any constraint using body we're about to destroy.
- //for each (var c:Constraint in body.constraints)
- //for (c in body.constraints)
- var ci:ConstraintIterator = bdy.constraints.iterator();
- while (ci.hasNext())
- {
- var c:Constraint = ci.next();
- c.active = false;
- }
- bdy.space = null;
- //trace("portal joint destroy", body);
- }
- }
- else
- {
- // Shape survives to portal another data.
- survivors.push(shape2);
- }
- }
- for each(var survivor:Shape in survivors)
- //for (survivor in survivors)
- {
- var survivorData:* = survivor.userData;
- survivorData.portal_id = null;
- survivorData.portals = [];
- survivor.cbTypes.remove(PARTIAL);
- // Cull any dangling portal pairs.
- var survivorBody:Body = survivor.body;
- var bodyData:* = survivorBody.userData;
- var i:int = 0;
- var pk:int = bodyData.portal_pairs.length;
- //while (i < bodyData.portal_pairs.length)
- while (i < pk)
- {
- var pair2:PortalPair = bodyData.portal_pairs[i];
- var clone2:Body;
- if (pair2.bodyA == survivorBody)
- clone2 = pair.bodyB
- else
- clone2 = pair.bodyA;
- if (clone2.space == null)
- {
- // pair should be culled.
- bodyData.portal_pairs.splice(i, 1);
- pk--;
- }
- else
- {
- i++;
- }
- }
- }
- //---
- }
- private function behindPortal(portal:Portal, position:Vec2):Boolean
- {
- var u:Vec2 = position.sub(portal.body.localPointToWorld(portal.position, true));
- var v:Vec2 = portal.body.localVectorToWorld(portal.direction);
- var behind:Boolean = (u.dot(v) <= 0);
- u.dispose();
- v.dispose();
- return behind;
- }
- private function handlePartial(partial:Shape, carb:CollisionArbiter, ret:PreFlag):PreFlag
- {
- var partialData:* = partial.userData;
- var portals:Array/*PortalShapeData*/ = partialData.portals;
- for each(var portalData:PortalShapeData in portals)
- //for (portalData in portals)
- {
- var portal:Portal = portalData.portal;
- var anyBehind:Boolean = false;
- var i:int = 0;
- while (i < carb.contacts.length)
- {
- var contact:Contact = carb.contacts.at(i);
- var scale:Number = ((partial == carb.shape1) ? 0.5 : -0.5);
- var pos:Vec2 = contact.position.addMul(carb.normal, contact.penetration * scale);
- if (behindPortal(portal, pos))
- {
- carb.contacts.remove(contact);
- anyBehind = true;
- }
- else
- {
- i++;
- }
- pos.dispose();
- }
- // If there are any contact points behind,
- // we also cull any virtual ones that may cause issues.
- // We don't simply clear as we do want non-virtual ones.
- if (anyBehind)
- {
- i = 0;
- while (i < carb.contacts.length)
- {
- var contact2:Contact = carb.contacts.at(i);
- if (contact2.penetration < 0)
- {
- carb.contacts.remove(contact2);
- }
- else
- {
- i++;
- }
- }
- }
- if (carb.contacts.empty()) {
- return PreFlag.IGNORE_ONCE;
- }
- }
- return ret;
- }
- private function backCollision(cb:PreCallback):PreFlag
- {
- //---
- var partial:Shape = cb.int1.castShape;
- var other:Shape = cb.int2.castShape;
- var carb:CollisionArbiter = cb.arbiter.collisionArbiter;
- var partialData:* = partial.userData;
- var portals:* = partialData.portals;
- for each(var portalData:PortalShapeData in portals)
- //for (portalData in portals)
- {
- var portal:Portal = portalData.portal;
- // Special case for the portal sides and back.
- // We always permit collision with the portal sides
- // and never with the portal back.
- if (other.body == portal.body) {
- if (portal.collide.indexOf(other) != -1)
- //if (Lambda.indexOf(portal.collide, other) != -1)
- {
- return PreFlag.ACCEPT_ONCE;
- }
- else
- if (portal.ignore.indexOf(other) != -1)
- //if (Lambda.indexOf(portal.ignore, other) != -1)
- {
- return PreFlag.IGNORE_ONCE;
- }
- }
- }
- var ret:PreFlag = PreFlag.ACCEPT_ONCE;
- ret = handlePartial(partial, carb, ret);
- if (other.cbTypes.has(PARTIAL))
- {
- ret = handlePartial(other, carb, ret);
- }
- return ret;
- //---
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement