public Vector2 Move(Vector2 deltaMovement) { prevCollisionState = new CollisionState(collisionState); collisionState.Reset(); if (!ignoreCollisions) { center = position + offset; center.x += Horizontal(ref deltaMovement); center.y += Vertical(ref deltaMovement); } transform.Translate(deltaMovement, Space.World); if (collisionState.slope) { deltaMovement.y = 0; } velocity = deltaMovement / Time.deltaTime; if (collisionState.down && !prevCollisionState.down) { onLanded(); } return(velocity); }
private void OnCollisionExit2D(Collision2D collision) { if (collision.gameObject.GetComponent <WallCollision>() != null) { currentCollisionState = CollisionState.NONE; } }
protected virtual void Awake() { rb = GetComponent <Rigidbody>(); player = GetComponent <Player>(); collisionState = GetComponent <CollisionState>(); Wake(); }
protected virtual void Awake() { inputState = GetComponent <InputState>(); rbdy2D = GetComponent <Rigidbody2D>(); collState = GetComponent <CollisionState>(); animator = GetComponent <Animator>(); }
protected virtual void Awake() { // inputState = GetComponent<InputState>(); abductBody2d = GetComponent<Rigidbody2D>(); abductCollisionState = GetComponent<CollisionState>(); boxCollider = GetComponent<BoxCollider2D>(); }
public void IgnoreCollisionWith(PhysicsComponent other, CollisionState state) { var otherNative = other.NativeCollisionObject; switch (state) { // Note that we're calling 'SetIgnoreCollisionCheck' on both objects as bullet doesn't // do it itself ; One of the object in the pair will report that it doesn't ignore // collision with the other even though you set the other as ignoring the former. case CollisionState.Ignore: { // Bullet uses an array per collision object to store all of the objects to ignore, // when calling this method it adds the referenced object without checking for duplicates, // so if a user where to call 'Ignore' of this function on this object n-times he'll have to call it // that same amount of time to re-detect them instead of just once. // We're calling false here to remove a previous ignore if there was any and re-ignoring // to force it to have only a single instance. otherNative.SetIgnoreCollisionCheck(NativeCollisionObject, false); NativeCollisionObject.SetIgnoreCollisionCheck(otherNative, false); otherNative.SetIgnoreCollisionCheck(NativeCollisionObject, true); NativeCollisionObject.SetIgnoreCollisionCheck(otherNative, true); break; } case CollisionState.Detect: { otherNative.SetIgnoreCollisionCheck(NativeCollisionObject, false); NativeCollisionObject.SetIgnoreCollisionCheck(otherNative, false); break; } } }
protected virtual void ForwardSense(List <GameObject> objectList, CollisionState collisionState) { var newListeners = new HashSet <HashedWeakReference <SomeSensorListener> >(); foreach (HashedWeakReference <SomeSensorListener> listener in sensorListeners) { if (listener.Reference.TryGetTarget(out SomeSensorListener target)) { // Keep listeners that were not disposed newListeners.Add(listener); switch (collisionState) { case CollisionState.Enter: target.OnSense(objectList, SenseState.Enter); break; case CollisionState.Stay: target.OnSense(objectList, SenseState.Stay); break; case CollisionState.Exit: target.OnSense(objectList, SenseState.Exit); break; } } } // Clean up any old weak references with null targets sensorListeners = newListeners; }
private void OnCollisionEnter2D(Collision2D collision) { WallCollision wall = collision.gameObject.GetComponent <WallCollision>(); if (wall != null) { // Determine the direction of the collision to know what our wall collision is. if (collision.transform.position.x > transform.position.x) { currentCollisionState = CollisionState.RIGHT; Vector3 updatedPosition = transform.position; updatedPosition.x = wall.transform.position.x - wall.boxCollider.bounds.extents.x - _boxCollider.bounds.extents.x; transform.position = updatedPosition; } else { currentCollisionState = CollisionState.LEFT; Vector3 updatedPosition = transform.position; updatedPosition.x = wall.transform.position.x + wall.boxCollider.bounds.extents.x + _boxCollider.bounds.extents.x; transform.position = updatedPosition; } } }
/// <summary> /// Internal quick constructor /// </summary> public CollisionHit4(CollisionState state, Contact c, bool isForA) { this.state = state; sensor = c.sensor; manifold = c.manifold; other = (Collider4)(isForA ? c.B.Tag : c.A.Tag); }
public void CallOnUpdate(CollisionState collisionState) { if (m_ladderState == LadderState.JumpOff) { return; } if (m_ladderState != LadderState.None) { float climb = Input.GetAxis("Vertical"); Offset = (climb * 0.02f); } else { Offset = 0.0f; } if (m_ladderState == LadderState.AtTop && Offset > 0.0f) { Offset = 0.0f; } if (collisionState == CollisionState.OnFloor && Offset < 0.0f) { Offset = 0.0f; } if (Input.GetButton("Jump") && m_ladderState != LadderState.None) { m_ladderState = LadderState.JumpOff; Offset = 0.0f; } }
public void CallOnUpdate(CollisionState collisionState) { if (m_ladderState == LadderState.JumpOff) return; if (m_ladderState != LadderState.None) { float climb = Input.GetAxis("Vertical"); Offset = (climb * 0.02f); } else { Offset = 0.0f; } if (m_ladderState == LadderState.AtTop && Offset > 0.0f) { Offset = 0.0f; } if (collisionState == CollisionState.OnFloor && Offset < 0.0f) { Offset = 0.0f; } if (Input.GetButton("Jump") && m_ladderState != LadderState.None) { m_ladderState = LadderState.JumpOff; Offset = 0.0f; } }
private void Awake() { _inputState = GetComponent <InputState> (); _walkBehavior = GetComponent <WalkBehavior> (); _animator = GetComponent <Animator> (); _collisionState = GetComponent <CollisionState> (); }
/* * \brief Works out if a value is almost another value (for floating point accuracy) */ public void CallOnCollisionEnter(Collision collision) { m_collisionState = CollisionState.None; foreach (ContactPoint contact in collision) { if (isNearly(contact.normal.y, 1.0f, 0.2f)) { m_collisionState = CollisionState.OnFloor; } else if (isNearly(contact.normal.y, -1.0f, 0.2f)) { m_collisionState = CollisionState.OnRoof; } else { m_collisionState = CollisionState.OnWall; } } if (m_collisionState == CollisionState.OnFloor) { m_jumpState = JumpState.Landed; } }
// private Duck duckBehavior; void Awake() { inputState = GetComponent<InputState>(); flyBehavior = GetComponent<Fly>(); animator = GetComponent<Animator>(); collisionState = GetComponent<CollisionState>(); // duckBehavior = GetComponent<Duck>(); }
private void Start() { inputState = GetComponent <InputState>(); walkBehaviour = GetComponent <Walk>(); animator = GetComponent <Animator>(); collisionState = GetComponent <CollisionState>(); }
// Unity continual collision callback protected virtual void OnCollisionStay(Collision collision) { // if this method has been called, we're in some kind of collision state collisionState = CollisionState.Grounded; float max = WaistPosition.y; float min = WaistPosition.y; // check collision points foreach (ContactPoint m in collision.contacts) { // how steep is the point with which we collided? float slope = Mathf.Acos(Mathf.Clamp(m.normal.y, -1f, 1f)) * Mathf.Rad2Deg; // if it's fairly steep, flag stuck and we may be trying to jump stuck = (slope > maxSlope / 2f); // max collision extrema if (m.point.y >= max) { maxCollisionPoint = m; max = m.point.y; // if greater than our max grade AND the point's height is higher than the actor's waist if (slope > maxSlope) { collisionState = CollisionState.Blocked; } } // min collision extrema if (m.point.y <= min) { minCollisionPoint = m; min = m.point.y; } } }
public void UpdateProjectile(Projectile projectile, Game1 game) { projectile.life++; if (projectile.life == GetLifeSpan()) { DestroyProjectile(projectile); } projectile.worldX += projectile.GetData().GetXVel(projectile); projectile.worldY += projectile.GetData().GetYVel(projectile); projectile.UpdateSprite(game.worldMap[game.currentRoom]); CollisionState result = CollisionDetector.PerPixelSprite(projectile, game.player, game.graphics); if ((result == CollisionState.Hurtbox || result == CollisionState.Standard) && game.worldMap[game.currentRoom].enemyProjectiles.Contains(projectile)) { game.player.TakeDamage(projectile); DestroyProjectile(projectile); } if (CollisionDetector.CheckMapCollision(0, 0, projectile, game.worldMap[game.currentRoom]) == false) { if (projectile.onScreen && projectile.nearScreen) { game.AddObjectToDraw(projectile); } else { projectile.Delete(); } } else { DestroyProjectile(projectile); } }
protected override void OnSense(GameObject target, CollisionState collisionState) { if (!this.sensorDelegate.TryGetTarget(out SensorDelegate sensorDelegate)) { return; } if (!sensorDelegate.IsSenseTarget(target)) { return; } var orientAngle = 360.0f - transform.eulerAngles.z; var minAngle = orientAngle - degreeAngleScan / 2.0f; var maxAngle = orientAngle + degreeAngleScan / 2.0f; //if (collisionState == CollisionState.Enter) { // Debug.Log("Scan angle to Target: " + angleToTarget.ToString() + " orientAngle: " + orientAngle.ToString()); //} List <GameObject> objectList = new List <GameObject>() { target }; // FUTURE: this can be optimized further by using a PolygonCollider2D, which reduces the number of false positives if (CheckOccluders(orientAngle, degreeAngleScan / 2.0f, target, false)) { return; } ForwardSense(objectList, collisionState); }
void Awake () { inputState = GetComponent<InputState> (); walkBehaviour = GetComponent<Walk> (); duckBehaviour = GetComponent<Duck> (); animator = GetComponent<Animator> (); collisionState = GetComponent<CollisionState> (); }
public void CheckCollsion(GameObject obj, out CollisionState _state) { _state = new CollisionState(false); foreach (GameObject obj2 in blocks) { if (obj2 != obj) { if (obj.HitBox.Rect.Intersects(obj2.HitBox.Rect)) { ObjectsCollided(obj, obj2, out _state); } } } foreach (GameObject obj2 in entities) { if (obj2 != obj) { if (obj.HitBox.Rect.Intersects(obj2.HitBox.Rect)) { ObjectsCollided(obj, obj2, out _state); } } } }
private void OnUpdate() { if (Object.op_Equality((Object)this.animal, (Object)null)) { return; } ((Component)this.animal).get_transform(); foreach (AnimalActionPoint searchPoint in this.SearchPoints) { if (!Object.op_Equality((Object)searchPoint, (Object)null)) { int instanceId = searchPoint.InstanceID; CollisionState collisionState1; if (!this.collisionStateTable.TryGetValue(instanceId, out collisionState1)) { CollisionState collisionState2 = CollisionState.None; this.collisionStateTable[instanceId] = collisionState2; collisionState1 = collisionState2; } if (((Component)searchPoint).get_gameObject().get_activeSelf() && this.animal.CheckTargetOnArea(searchPoint.Destination, this._visibleDistance, this._visibleHeight, this._visibleAngle)) { switch (collisionState1) { case CollisionState.None: case CollisionState.Exit: this.collisionStateTable[instanceId] = CollisionState.Enter; this.OnEnter(searchPoint); continue; case CollisionState.Enter: case CollisionState.Stay: this.collisionStateTable[instanceId] = CollisionState.Stay; continue; default: continue; } } else { switch (collisionState1) { case CollisionState.None: case CollisionState.Exit: this.collisionStateTable[instanceId] = CollisionState.None; continue; case CollisionState.Enter: case CollisionState.Stay: this.collisionStateTable[instanceId] = CollisionState.Exit; this.OnExit(searchPoint); continue; default: continue; } } } } }
protected virtual void Awake() { inputState = GetComponent <InputState>(); body2d = GetComponent <Rigidbody2D>(); collisionState = GetComponent <CollisionState>(); AManager = GetComponent <AudioManager>(); }
/// <summary> /// Assumptions: Either On the ground and beginning to jump with no wall collision, or on the ground and beginning to jump with wall collision, or no collision /// </summary> /// <param name="collision"></param> /// <param name="isEntering"></param> public void CollisionChange(Collision2D collision, CollisionState collisionState) { if (collisionState == CollisionState.ENTERING) { if (collision.contacts[0].normal.y > 0.4f) { manager.ChangeState(MovementState.STOPPED); } else if (collision.contacts[0].normal.y < -0.4f) { manager.ChangeState(MovementState.FALLING); } else if (collision.contacts[0].normal.x > 0.4f) { manager.ChangeState(MovementState.HUGGING_WALL); } } else if (collisionState == CollisionState.EXITING) { if (manager.CurrentCollisions.Count == 1 && Mathf.Abs(manager.WallHugDirection) > 0.4f) { transitionToHugging = true; //manager.ChangeState( MovementState.HUGGING_WALL ); } else { transitionToHugging = false; } } }
public void CallOnTriggerStay(Collider collider, ref PlayerState playerState) { CSceneObject obj = collider.gameObject.GetComponent <CSceneObject>(); if (obj == null && collider.gameObject != null && collider.gameObject.transform.parent != null) { GameObject parent = collider.gameObject.transform.parent.gameObject; if (parent != null) { obj = parent.GetComponent <CSceneObject>(); } } if (obj != null && obj.IsLadder && (m_ladderClimb.State != LadderState.JumpOff)) { m_ladderClimb.CallOnTriggerStay(collider, ref playerState); if (m_ladderClimb.State != LadderState.None) { m_body.constraints = RigidbodyConstraints.FreezeAll; } else { m_collisionState = CollisionState.OnFloor; } return; } }
public void OnCollisionEvent(CollisionState state) { if (cState == state) { return; } cState = state; // On surface? switch (cState) { case CollisionState.AIR: break; case CollisionState.GROUND: case CollisionState.WALL: case CollisionState.LEDGE: ResetJump(); break; case CollisionState.CEILING: ResetVelocity(); break; } //Debug.Log($"Collision State: {cState}"); }
void Awake() { inputState = GetComponent <InputState> (); walkBehavior = GetComponent <Walk> (); animator = GetComponent <Animator> (); collisionState = GetComponent <CollisionState> (); }
protected virtual void Awake() { inputState = GetComponent <InputState> (); body2d = GetComponent <Rigidbody2D> (); collisionState = GetComponent <CollisionState> (); //objectPooler = GameObject.Find("PlayerBulletObjectPool").GetComponent<ObjectPooler> (); }
public void OnUpdate(ref GruntState playerState) { if (playerState == GruntState.Standing) { m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; } m_isJumpDown = false; //= Input.GetButton("Jump"); //if (Application.platform == RuntimePlatform.Android) // m_isJumpDown = Input.touchCount != 0; if (m_isJumpDown && m_jumpState == JumpState.Landed && CanJump(playerState)) { if ((Time.time * 1000.0f) - m_jumpTimer > JumpDelayMS) { m_jumpTimer = (Time.time * 1000.0f); m_body.AddForce(new Vector3(0, PlayerJumpHeight, 0), ForceMode.Impulse); m_jumpState = JumpState.Jumping; playerState = GruntState.Jumping; m_collisionState = CollisionState.None; } } if (m_jumpState == JumpState.Jumping && playerState == GruntState.Jumping) { if ((Time.time * 1000.0f) - m_jumpTimer > 2000.0f) { playerState = GruntState.FallJumping; } } }
protected virtual void Awake() { inputState = GetComponent<InputState> (); body2d = GetComponent<Rigidbody2D> (); collisionState = GetComponent<CollisionState> (); spriteRenderer = GetComponent<SpriteRenderer> (); }
bool TestMapCollision(Rectangle collisionRect, Edge direction, CollisionState collisionState, out int collisionResponse) { collisionResponse = 0; var side = direction.OppositeEdge(); var perpindicularPosition = side.IsVertical() ? collisionRect.Center.X : collisionRect.Center.Y; var leadingPosition = collisionRect.GetSide(direction); var shouldTestSlopes = side.IsVertical(); PopulateCollidingTiles(collisionRect, direction); for (var i = 0; i < _collidingTiles.Count; i++) { if (_collidingTiles[i] == null) { continue; } // disregard horizontal collisions with tiles on the same row as a slope if the last tile we were grounded on was a slope. // the y collision response will push us up on the slope. if (direction.IsHorizontal() && collisionState._lastGroundTile != null && collisionState._lastGroundTile.IsSlope() && IsSlopeCollisionRow(_collidingTiles[i].Y)) { continue; } if (TestTileCollision(_collidingTiles[i], side, perpindicularPosition, leadingPosition, shouldTestSlopes, out collisionResponse)) { // store off our last ground tile if we collided below if (direction == Edge.Bottom) { collisionState._lastGroundTile = _collidingTiles[i]; collisionState.IsGroundedOnOneWayPlatform = collisionState._lastGroundTile.IsOneWayPlatform(); } return(true); } // special case for sloped ground tiles if (collisionState._lastGroundTile != null && direction == Edge.Bottom) { // if grounded on a slope and intersecting a slope or if grounded on a wall and intersecting a tall slope we go sticky. // tall slope here means one where the the slopeTopLeft/Right is 0, i.e. it connects to a wall var isHighSlopeNearest = _collidingTiles[i].IsSlope() && _collidingTiles[i].GetNearestEdge(perpindicularPosition) == _collidingTiles[i].GetHighestSlopeEdge(); if ((collisionState._lastGroundTile.IsSlope() && _collidingTiles[i].IsSlope()) || (!collisionState._lastGroundTile.IsSlope() && isHighSlopeNearest)) { // store off our last ground tile if we collided below collisionState._lastGroundTile = _collidingTiles[i]; return(true); } } } return(false); }
private void OnUpdate() { if (Object.op_Equality((Object)this._agent, (Object)null)) { return; } foreach (ActionPoint searchPoint in this._searchPoints) { if (!Object.op_Equality((Object)searchPoint, (Object)null)) { int instanceId = searchPoint.InstanceID; CollisionState collisionState1; if (!this._collisionStateTable.TryGetValue(instanceId, out collisionState1)) { CollisionState collisionState2 = CollisionState.None; this._collisionStateTable[instanceId] = collisionState2; collisionState1 = collisionState2; } if ((double)Vector3.Distance(((Component)this).get_transform().get_position(), searchPoint.CommandCenter) < (double)this._radius + (double)searchPoint.Radius) { switch (collisionState1) { case CollisionState.None: case CollisionState.Exit: this._collisionStateTable[instanceId] = CollisionState.Enter; this.OnEnter(searchPoint); continue; case CollisionState.Enter: case CollisionState.Stay: this._collisionStateTable[instanceId] = CollisionState.Stay; continue; default: continue; } } else { switch (collisionState1) { case CollisionState.None: case CollisionState.Exit: this._collisionStateTable[instanceId] = CollisionState.None; continue; case CollisionState.Enter: case CollisionState.Stay: this._collisionStateTable[instanceId] = CollisionState.Exit; this.OnExit(searchPoint); continue; default: continue; } } } } }
private void TryToEscape() { if (++escapeAttempts == EscapeAttemptPeriod) { escapeAttempts = 0; state = CollisionState.Plane; } }
void Awake() { inputState = GetComponent<InputState> (); walkBehavior = GetComponent<Walk> (); animator = GetComponent<Animator> (); collisionState = GetComponent<CollisionState> (); shootBehavior = GetComponent<Shoot> (); }
void TryToEscape(TriangleShape triangle, ref Vector3 position) { if (++escapeAttempts == EscapeAttemptPeriod && GetVoronoiRegion(triangle, ref position) == VoronoiRegion.ABC) { escapeAttempts = 0; state = CollisionState.Plane; } }
// Comcast New Self Department: 1-877-405-2298 //void AirControl() //{ // float xInput = Input.GetAxis("Horizontal"); // float tarSpeed = xInput * Speed; // float airAcc = AirControlSpeed * Time.deltaTime; // float tarX = Mathf.MoveTowards(MoveDir.x, tarSpeed, airAcc); // bool dif = Mathf.Sign(xInput) != Mathf.Sign(MoveDir.x); // bool areZeroes = MoveDir.x == 0 || xInput == 0; // bool isSpeedUP = Mathf.Abs(tarX) > Mathf.Abs(MoveDir.x); // if (areZeroes || dif || isSpeedUP) { MoveDir.x = tarX; } //} public void OnCollisionEvent(CollisionState state) { if (cState == state) { return; } cState = state; }
/// <summary> /// Cleans up the pair tester. /// </summary> public override void CleanUp() { convex = null; state = CollisionState.Plane; escapeAttempts = 0; localSeparatingAxis = new System.Numerics.Vector3(); Updated = false; }
/// <summary> /// Cleans up the pair tester. /// </summary> public override void CleanUp() { convex = null; state = CollisionState.Plane; escapeAttempts = 0; localSeparatingAxis = new Vector3(); Updated = false; }
protected virtual void Awake() { health = GetComponent<Health> (); inputState = GetComponent<InputState> (); body2d = GetComponent<Rigidbody2D> (); collisionState = GetComponent<CollisionState> (); objectPooler = GameObject.Find("PlayerBulletObjectPool").GetComponent<ObjectPooler> (); }
/// <summary> /// Cleans up the pair tester. /// </summary> public override void CleanUp() { triangle = null; convex = null; state = CollisionState.Plane; escapeAttempts = 0; localSeparatingAxis = new Vector3(); Updated = false; }
///<summary> /// Cleans up the pair tester. ///</summary> public void CleanUp() { state = CollisionState.Separated; previousState = CollisionState.Separated; cachedSimplex = new CachedSimplex(); localSeparatingAxis = new Vector3(); collidableA = null; collidableB = null; }
///<summary> /// Generates a contact between the objects, if possible. ///</summary> ///<param name="contact">Contact created between the pair, if possible.</param> ///<returns>Whether or not the objects were colliding.</returns> public bool GenerateContactCandidate(out ContactData contact) { //Generate contacts. This will just find one closest point using general supportmapping based systems like MPR and GJK. //The collision system moves through a state machine depending on the latest collision generation result. //At first, assume that the pair is completely separating. This is almost always the correct guess for new pairs. //An extremely fast, warm-startable boolean GJK test can be performed. If it returns with nonintersection, we can quit and do nothing. //If the initial boolean GJK test finds intersection, move onto a shallow contact test. //The shallow contact test is a different kind of GJK test that finds the closest points between the shape pair. It's not as speedy as the boolean version. //The algorithm is run between the marginless versions of the shapes, so that the closest points will form a contact somewhere in the space separating the cores. //If the closest point system finds no intersection and returns the closest points, the state is changed to ShallowContact. //If the closest point system finds intersection of the core shapes, then the state is changed to DeepContact, and MPR is run to determine contact information. //The system tries to escape from deep contact to shallow contact, and from shallow contact to separated whenever possible. //Here's the state flow: //On Separated: BooleanGJK // -Intersecting -> Go to ShallowContact. // -Nonintersecting -> Do nothing. //On ShallowContact: ClosestPointsGJK // -Intersecting -> Go to DeepContact. // -Nonintersecting: Go to Separated (without test) if squared distance > margin squared, otherwise use closest points to make contact. //On DeepContact: MPR // -Intersecting -> Go to ShallowContact if penetration depth < margin // -Nonintersecting -> This case is rare, but not impossible. Go to Separated (without test). previousState = state; switch (state) { case CollisionState.Separated: if (GJKToolbox.AreShapesIntersecting(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, ref localSeparatingAxis)) { state = CollisionState.ShallowContact; return DoShallowContact(out contact); } contact = new ContactData(); return false; case CollisionState.ShallowContact: return DoShallowContact(out contact); case CollisionState.DeepContact: return DoDeepContact(out contact); } contact = new ContactData(); return false; }
protected Matrix transformation; // initial transformation #endregion Fields #region Constructors public GameObject(Game1 game, Vector3 pos) { this.game = game; Type = GameObjectType.None; HitPoints = 0; AttackSpeed = 0; Damage = 0; model = null; modelName = ""; Scale = 1.0f; transformation = Matrix.Identity; color = Color.Transparent; isCollidable = true; isAlive = true; collisionState = CollisionState.None; attackCounter = 0; this.pos = pos; game.Add(this); }
void TryToEscape(ref Vector3 position) { if (++escapeAttempts == EscapeAttemptPeriod && GetVoronoiRegion(ref position) == VoronoiRegion.ABC) { escapeAttempts = 0; state = CollisionState.Plane; } }
void TryToEscape() { if (++escapeAttempts == EscapeAttemptPeriod) { escapeAttempts = 0; state = CollisionState.Plane; } }
private bool DoPlaneTest(out TinyStructList<ContactData> contactList) { //Find closest point between object and plane. Vector3 reverseNormal; Vector3 ab, ac; Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab); Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac); Vector3.Cross(ref ac, ref ab, out reverseNormal); //Convex position dot normal is ALWAYS zero. The thing to look at is the plane's 'd'. //If the distance along the normal is positive, then the convex is 'behind' that normal. float dotA; Vector3.Dot(ref triangle.vA, ref reverseNormal, out dotA); contactList = new TinyStructList<ContactData>(); switch (triangle.sidedness) { case TriangleSidedness.DoubleSided: if (dotA < 0) { //The reverse normal is pointing towards the convex. //It needs to point away from the convex so that the direction //will get the proper extreme point. Vector3.Negate(ref reverseNormal, out reverseNormal); dotA = -dotA; } break; case TriangleSidedness.Clockwise: //if (dotA < 0) //{ // //The reverse normal is pointing towards the convex. // return false; //} break; case TriangleSidedness.Counterclockwise: //if (dotA > 0) //{ // //The reverse normal is pointing away from the convex. // return false; //} //The reverse normal is pointing towards the convex. //It needs to point away from the convex so that the direction //will get the proper extreme point. Vector3.Negate(ref reverseNormal, out reverseNormal); dotA = -dotA; break; } Vector3 extremePoint; convex.GetLocalExtremePointWithoutMargin(ref reverseNormal, out extremePoint); //See if the extreme point is within the face or not. //It might seem like the easy "depth" test should come first, since a barycentric //calculation takes a bit more time. However, transferring from plane to depth is 'rare' //(like all transitions), and putting this test here is logically closer to its requirements' //computation. if (GetVoronoiRegion(ref extremePoint) != VoronoiRegion.ABC) { state = CollisionState.ExternalSeparated; return DoExternalSeparated(out contactList); } float dotE; Vector3.Dot(ref extremePoint, ref reverseNormal, out dotE); float t = (dotA - dotE) / reverseNormal.LengthSquared(); Vector3 offset; Vector3.Multiply(ref reverseNormal, t, out offset); //Compare the distance from the plane to the convex object. float distanceSquared = offset.LengthSquared(); float marginSum = triangle.collisionMargin + convex.collisionMargin; //TODO: Could just normalize early and avoid computing point plane before it's necessary. //Exposes a sqrt but... if (t <= 0 || distanceSquared < marginSum * marginSum) { //The convex object is in the margin of the plane. //All that's left is to create the contact. var contact = new ContactData(); //Displacement is from A to B. point = A + t * AB, where t = marginA / margin. if (marginSum > Toolbox.Epsilon) //This can be zero! It would cause a NaN is unprotected. Vector3.Multiply(ref offset, convex.collisionMargin / marginSum, out contact.Position); //t * AB else contact.Position = new Vector3(); Vector3.Add(ref extremePoint, ref contact.Position, out contact.Position); //A + t * AB. float normalLength = reverseNormal.Length(); Vector3.Divide(ref reverseNormal, normalLength, out contact.Normal); float distance = normalLength * t; contact.PenetrationDepth = marginSum - distance; if (contact.PenetrationDepth > marginSum) { //Check to see if the inner sphere is touching the plane. //This does not override other tests; there can be more than one contact from a single triangle. ContactData alternateContact; if (TryInnerSphereContact(out alternateContact))// && alternateContact.PenetrationDepth > contact.PenetrationDepth) { contactList.Add(ref alternateContact); } //The convex object is stuck deep in the plane! //The most problematic case for this is when //an object is right on top of a cliff. //The lower, vertical triangle may occasionally detect //a contact with the object, but would compute an extremely //deep depth if the normal plane test was used. //Verify that the depth is correct by trying another approach. CollisionState previousState = state; state = CollisionState.ExternalNear; TinyStructList<ContactData> alternateContacts; if (DoExternalNear(out alternateContacts)) { alternateContacts.Get(0, out alternateContact); if (alternateContact.PenetrationDepth + .01f < contact.PenetrationDepth) //Bias against the subtest's result, since the plane version will probably have a better position. { //It WAS a bad contact. contactList.Add(ref alternateContact); //DoDeepContact (which can be called from within DoExternalNear) can generate two contacts, but the second contact would just be an inner sphere (which we already generated). //DoExternalNear can only generate one contact. So we only need the first contact! //TODO: This is a fairly fragile connection between the two stages. Consider robustifying. (Also, the TryInnerSphereContact is done twice! This process is very rare for marginful pairs, though) } else { //Well, it really is just that deep. contactList.Add(ref contact); state = previousState; } } else { //If the external near test finds that there was no collision at all, //just return to plane testing. If the point turns up outside the face region //next time, the system will adapt. state = previousState; return false; } } else { contactList.Add(ref contact); } return true; } return false; }
private bool DoDeepContact(out TinyStructList<ContactData> contactList) { //Find the origin to triangle center offset. Vector3 center; Vector3.Add(ref triangle.vA, ref triangle.vB, out center); Vector3.Add(ref center, ref triangle.vC, out center); Vector3.Multiply(ref center, 1f / 3f, out center); ContactData contact; contactList = new TinyStructList<ContactData>(); if (MPRToolbox.AreLocalShapesOverlapping(convex, triangle, ref center, ref Toolbox.RigidIdentity)) { float dot; Vector3 triangleNormal, ab, ac; Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab); Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac); Vector3.Cross(ref ab, ref ac, out triangleNormal); float lengthSquared = triangleNormal.LengthSquared(); if (lengthSquared < Toolbox.Epsilon * .01f) { //Degenerate triangle! That's no good. //Just use the direction pointing from A to B, "B" being the triangle. That direction is center - origin, or just center. MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref center, out contact.PenetrationDepth, out contact.Normal, out contact.Position); } else { //Normalize the normal. Vector3.Divide(ref triangleNormal, (float)Math.Sqrt(lengthSquared), out triangleNormal); ////The first direction to check is one of the triangle's edge normals. Choose the one that is most aligned with the offset from A to B. ////Project the direction onto the triangle plane. //Vector3.Dot(ref triangleNormal, ref center, out dot); //Vector3 trianglePlaneDirection; //Vector3.Multiply(ref triangleNormal, dot, out trianglePlaneDirection); //Vector3.Subtract(ref trianglePlaneDirection, ref center, out trianglePlaneDirection); ////To find out which edge to use, compute which region the direction is in. ////This is done by constructing three planes which segment the triangle into three sub-triangles. ////These planes are defined by A, origin, center; B, origin, center; C, origin, center. ////The plane tests against the direction can be reordered to: ////(center x direction) * A ////(center x direction) * B ////(center x direction) * C //Vector3 OxD; //Vector3.Cross(ref trianglePlaneDirection, ref center, out OxD); //Vector3 p; //float dotA, dotB, dotC; //Vector3.Dot(ref triangle.vA, ref OxD, out dotA); //Vector3.Dot(ref triangle.vB, ref OxD, out dotB); //Vector3.Dot(ref triangle.vC, ref OxD, out dotC); //if (dotA >= 0 && dotB <= 0) //{ // //Direction is in the AB edge zone. // //Compute the edge normal using AB x (AO x AB). // Vector3 AB, AO; // Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB); // Vector3.Subtract(ref center, ref triangle.vA, out AO); // Vector3.Cross(ref AO, ref AB, out p); // Vector3.Cross(ref AB, ref p, out trianglePlaneDirection); //} //else if (dotB >= 0 && dotC <= 0) //{ // //Direction is in the BC edge zone. // //Compute the edge normal using BC x (BO x BC). // Vector3 BC, BO; // Vector3.Subtract(ref triangle.vC, ref triangle.vB, out BC); // Vector3.Subtract(ref center, ref triangle.vB, out BO); // Vector3.Cross(ref BO, ref BC, out p); // Vector3.Cross(ref BC, ref p, out trianglePlaneDirection); //} //else // dotC > 0 && dotA < 0 //{ // //Direction is in the CA edge zone. // //Compute the edge normal using CA x (CO x CA). // Vector3 CA, CO; // Vector3.Subtract(ref triangle.vA, ref triangle.vC, out CA); // Vector3.Subtract(ref center, ref triangle.vC, out CO); // Vector3.Cross(ref CO, ref CA, out p); // Vector3.Cross(ref CA, ref p, out trianglePlaneDirection); //} //dot = trianglePlaneDirection.LengthSquared(); //if (dot > Toolbox.Epsilon) //{ // Vector3.Divide(ref trianglePlaneDirection, (float)Math.Sqrt(dot), out trianglePlaneDirection); // MPRTesting.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref trianglePlaneDirection, out contact.PenetrationDepth, out contact.Normal); // //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle. // Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot); // if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)) // { // //Normal was facing the wrong way. // //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal. // Vector3 previousNormal = contact.Normal; // Vector3.Dot(ref contact.Normal, ref triangleNormal, out dot); // Vector3.Multiply(ref contact.Normal, dot, out p); // Vector3.Subtract(ref contact.Normal, ref p, out contact.Normal); // float length = contact.Normal.LengthSquared(); // if (length > Toolbox.Epsilon) // { // //Renormalize the corrected normal. // Vector3.Divide(ref contact.Normal, (float)Math.Sqrt(length), out contact.Normal); // Vector3.Dot(ref contact.Normal, ref previousNormal, out dot); // contact.PenetrationDepth *= dot; // } // else // { // contact.PenetrationDepth = float.MaxValue; // contact.Normal = new Vector3(); // } // } //} //else //{ // contact.PenetrationDepth = float.MaxValue; // contact.Normal = new Vector3(); //} //TODO: This tests all three edge axes with a full MPR raycast. That's not really necessary; the correct edge normal should be discoverable, resulting in a single MPR raycast. //Find the edge directions that will be tested with MPR. Vector3 AO, BO, CO; Vector3 AB, BC, CA; Vector3.Subtract(ref center, ref triangle.vA, out AO); Vector3.Subtract(ref center, ref triangle.vB, out BO); Vector3.Subtract(ref center, ref triangle.vC, out CO); Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB); Vector3.Subtract(ref triangle.vC, ref triangle.vB, out BC); Vector3.Subtract(ref triangle.vA, ref triangle.vC, out CA); //We don't have to worry about degenerate triangles here because we've already handled that possibility above. Vector3 ABnormal, BCnormal, CAnormal; //Project the center onto the edge to find the direction from the center to the edge AB. Vector3.Dot(ref AO, ref AB, out dot); Vector3.Multiply(ref AB, dot / AB.LengthSquared(), out ABnormal); Vector3.Subtract(ref AO, ref ABnormal, out ABnormal); ABnormal.Normalize(); //Project the center onto the edge to find the direction from the center to the edge BC. Vector3.Dot(ref BO, ref BC, out dot); Vector3.Multiply(ref BC, dot / BC.LengthSquared(), out BCnormal); Vector3.Subtract(ref BO, ref BCnormal, out BCnormal); BCnormal.Normalize(); //Project the center onto the edge to find the direction from the center to the edge BC. Vector3.Dot(ref CO, ref CA, out dot); Vector3.Multiply(ref CA, dot / CA.LengthSquared(), out CAnormal); Vector3.Subtract(ref CO, ref CAnormal, out CAnormal); CAnormal.Normalize(); MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref ABnormal, out contact.PenetrationDepth, out contact.Normal); //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle. Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot); if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)) { //Normal was facing the wrong way. //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal. Vector3 previousNormal = contact.Normal; Vector3.Dot(ref contact.Normal, ref triangleNormal, out dot); Vector3 p; Vector3.Multiply(ref contact.Normal, dot, out p); Vector3.Subtract(ref contact.Normal, ref p, out contact.Normal); float length = contact.Normal.LengthSquared(); if (length > Toolbox.Epsilon) { //Renormalize the corrected normal. Vector3.Divide(ref contact.Normal, (float)Math.Sqrt(length), out contact.Normal); Vector3.Dot(ref contact.Normal, ref previousNormal, out dot); contact.PenetrationDepth *= dot; } else { contact.PenetrationDepth = float.MaxValue; contact.Normal = new Vector3(); } } Vector3 candidateNormal; float candidateDepth; MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref BCnormal, out candidateDepth, out candidateNormal); //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle. Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot); if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)) { //Normal was facing the wrong way. //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal. Vector3 previousNormal = candidateNormal; Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot); Vector3 p; Vector3.Multiply(ref candidateNormal, dot, out p); Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal); float length = candidateNormal.LengthSquared(); if (length > Toolbox.Epsilon) { //Renormalize the corrected normal. Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal); Vector3.Dot(ref candidateNormal, ref previousNormal, out dot); candidateDepth *= dot; } else { contact.PenetrationDepth = float.MaxValue; contact.Normal = new Vector3(); } } if (candidateDepth < contact.PenetrationDepth) { contact.Normal = candidateNormal; contact.PenetrationDepth = candidateDepth; } MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref CAnormal, out candidateDepth, out candidateNormal); //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle. Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot); if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)) { //Normal was facing the wrong way. //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal. Vector3 previousNormal = candidateNormal; Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot); Vector3 p; Vector3.Multiply(ref candidateNormal, dot, out p); Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal); float length = candidateNormal.LengthSquared(); if (length > Toolbox.Epsilon) { //Renormalize the corrected normal. Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal); Vector3.Dot(ref candidateNormal, ref previousNormal, out dot); candidateDepth *= dot; } else { contact.PenetrationDepth = float.MaxValue; contact.Normal = new Vector3(); } } if (candidateDepth < contact.PenetrationDepth) { contact.Normal = candidateNormal; contact.PenetrationDepth = candidateDepth; } //Try the depth along the positive triangle normal. //If it's clockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle). if (triangle.sidedness != TriangleSidedness.Clockwise) { MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal, out candidateDepth, out candidateNormal); if (candidateDepth < contact.PenetrationDepth) { contact.Normal = candidateNormal; contact.PenetrationDepth = candidateDepth; } } //Try the depth along the negative triangle normal. //If it's counterclockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle). if (triangle.sidedness != TriangleSidedness.Counterclockwise) { Vector3.Negate(ref triangleNormal, out triangleNormal); MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal, out candidateDepth, out candidateNormal); if (candidateDepth < contact.PenetrationDepth) { contact.Normal = candidateNormal; contact.PenetrationDepth = candidateDepth; } } } MPRToolbox.RefinePenetration(convex, triangle, ref Toolbox.RigidIdentity, contact.PenetrationDepth, ref contact.Normal, out contact.PenetrationDepth, out contact.Normal, out contact.Position); //It's possible for the normal to still face the 'wrong' direction according to one sided triangles. if (triangle.sidedness != TriangleSidedness.DoubleSided) { Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot); if (dot < 0) { //Skip the add process. goto InnerSphere; } } contact.Id = -1; if (contact.PenetrationDepth < convex.collisionMargin + triangle.collisionMargin) { state = CollisionState.ExternalNear; //If it's emerged from the deep contact, we can go back to using the preferred GJK method. } contactList.Add(ref contact); } InnerSphere: if (TryInnerSphereContact(out contact)) { contactList.Add(ref contact); } if (contactList.Count > 0) return true; state = CollisionState.ExternalSeparated; return false; }
private bool DoExternalNear(out TinyStructList<ContactData> contactList) { Vector3 closestA, closestB; //Don't bother trying to do any clever caching. The continually transforming simplex makes it very rarely useful. //TODO: Initialize the simplex of the GJK method using the 'true' center of the triangle. //If left unmodified, the simplex that is used in GJK will just be a point at 0,0,0, which of course is at the origin. //This causes an instant-out, always. Not good! //By giving the contributing simplex the average centroid, it has a better guess. Vector3 triangleCentroid; Vector3.Add(ref triangle.vA, ref triangle.vB, out triangleCentroid); Vector3.Add(ref triangleCentroid, ref triangle.vC, out triangleCentroid); Vector3.Multiply(ref triangleCentroid, .33333333f, out triangleCentroid); var initialSimplex = new CachedSimplex { State = SimplexState.Point, LocalSimplexB = { A = triangleCentroid } }; if (GJKToolbox.GetClosestPoints(convex, triangle, ref Toolbox.RigidIdentity, ref Toolbox.RigidIdentity, ref initialSimplex, out closestA, out closestB)) { state = CollisionState.Deep; return DoDeepContact(out contactList); } Vector3 displacement; Vector3.Subtract(ref closestB, ref closestA, out displacement); float distanceSquared = displacement.LengthSquared(); float margin = convex.collisionMargin + triangle.collisionMargin; contactList = new TinyStructList<ContactData>(); if (distanceSquared < margin * margin) { //Try to generate a contact. var contact = new ContactData(); //Determine if the normal points in the appropriate direction given the sidedness of the triangle. if (triangle.sidedness != TriangleSidedness.DoubleSided) { Vector3 triangleNormal, ab, ac; Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab); Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac); Vector3.Cross(ref ab, ref ac, out triangleNormal); float dot; Vector3.Dot(ref triangleNormal, ref displacement, out dot); if (triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) return false; if (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0) return false; } //Displacement is from A to B. point = A + t * AB, where t = marginA / margin. if (margin > Toolbox.Epsilon) //This can be zero! It would cause a NaN if unprotected. Vector3.Multiply(ref displacement, convex.collisionMargin / margin, out contact.Position); //t * AB else contact.Position = new Vector3(); Vector3.Add(ref closestA, ref contact.Position, out contact.Position); //A + t * AB. contact.Normal = displacement; float distance = (float)Math.Sqrt(distanceSquared); Vector3.Divide(ref contact.Normal, distance, out contact.Normal); contact.PenetrationDepth = margin - distance; contactList.Add(ref contact); TryToEscape(ref contact.Position); return true; } //Too far to make a contact- move back to separation. state = CollisionState.ExternalSeparated; return false; }
public void OnUpdate(ref PlayerState playerState) { if (playerState == PlayerState.Standing) m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_isJumpDown = Input.GetButton("Jump"); if (Application.platform == RuntimePlatform.Android) m_isJumpDown = Input.touchCount != 0; if (m_isJumpDown && m_jumpState == JumpState.Landed && CanJump(playerState)) { if ((Time.time * 1000.0f) - m_jumpTimer > JumpDelayMS) { m_jumpTimer = (Time.time * 1000.0f); m_body.AddForce(new Vector3(0, PlayerJumpHeight, 0), ForceMode.Impulse); m_jumpState = JumpState.Jumping; playerState = PlayerState.Jumping; m_collisionState = CollisionState.None; } } if (m_jumpState == JumpState.Jumping && playerState == PlayerState.Jumping) { if ((Time.time * 1000.0f) - m_jumpTimer > 2000.0f) { playerState = PlayerState.FallJumping; } } if (m_ladderClimb.State != LadderState.None) { if (m_ladderClimb.State != LadderState.JumpOff) { playerState = PlayerState.UpALadder; m_collisionState = CollisionState.None; m_ladderClimb.CallOnUpdate(m_collisionState); } else playerState = PlayerState.Jumping; if (m_ladderClimb.State == LadderState.JumpOff && m_jumpState != JumpState.Jumping) { if (Input.GetAxis("Horizontal") == 0.0f) { m_ladderClimb.State = LadderState.AtMiddle; m_collisionState = CollisionState.None; playerState = PlayerState.UpALadder; return; } m_jumpTimer = (Time.time * 1000.0f); m_body.AddForce(new Vector3(0, PlayerJumpHeight, 0), ForceMode.Impulse); m_jumpState = JumpState.Jumping; playerState = PlayerState.Jumping; m_velocity = Input.GetAxis("Horizontal") * -2.0f; m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_velocityLockTimer = (Time.time * 1000.0f); } } }
private bool DoDeepContact(out ContactData contact) { #region Informed search if (previousState == CollisionState.Separated) //If it was shallow before, then its closest points will be used to find the normal. { //It's overlapping! Find the relative velocity at the point relative to the two objects. The point is still in local space! //Vector3 velocityA; //Vector3.Cross(ref contact.Position, ref collidableA.entity.angularVelocity, out velocityA); //Vector3.Add(ref velocityA, ref collidableA.entity.linearVelocity, out velocityA); //Vector3 velocityB; //Vector3.Subtract(ref contact.Position, ref localTransformB.Position, out velocityB); //Vector3.Cross(ref velocityB, ref collidableB.entity.angularVelocity, out velocityB); //Vector3.Add(ref velocityB, ref collidableB.entity.linearVelocity, out velocityB); ////The velocity is negated because the direction so point backwards along the velocity. //Vector3.Subtract(ref velocityA, ref velocityB, out localDirection); //The above takes into account angular velocity, but linear velocity alone is a lot more stable and does the job just fine. if (collidableA.entity != null && collidableB.entity != null) Vector3.Subtract(ref collidableA.entity.linearVelocity, ref collidableB.entity.linearVelocity, out localDirection); else localDirection = localSeparatingAxis; if (localDirection.LengthSquared() < Toolbox.Epsilon) { localDirection = Vector3.Up; } } if (MPRToolbox.GetContact(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, ref localDirection, out contact)) { if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin) state = CollisionState.ShallowContact; return true; } //This is rare, but could happen. state = CollisionState.Separated; return false; //if (MPRTesting.GetLocalOverlapPosition(collidableA.Shape, collidableB.Shape, ref localTransformB, out contact.Position)) //{ // //First, try to use the heuristically found direction. This comes from either the GJK shallow contact separating axis or from the relative velocity. // Vector3 rayCastDirection; // float lengthSquared = localDirection.LengthSquared(); // if (lengthSquared > Toolbox.Epsilon) // { // Vector3.Divide(ref localDirection, (float)Math.Sqrt(lengthSquared), out rayCastDirection);// (Vector3.Normalize(localDirection) + Vector3.Normalize(collidableB.worldTransform.Position - collidableA.worldTransform.Position)) / 2; // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out contact.PenetrationDepth, out contact.Normal); // } // else // { // contact.PenetrationDepth = float.MaxValue; // contact.Normal = Toolbox.UpVector; // } // //Try the offset between the origins as a second option. Sometimes this is a better choice than the relative velocity. // //TODO: Could use the position-finding MPR iteration to find the A-B direction hit by continuing even after the origin has been found (optimization). // Vector3 normalCandidate; // float depthCandidate; // lengthSquared = localTransformB.Position.LengthSquared(); // if (lengthSquared > Toolbox.Epsilon) // { // Vector3.Divide(ref localTransformB.Position, (float)Math.Sqrt(lengthSquared), out rayCastDirection); // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out depthCandidate, out normalCandidate); // if (depthCandidate < contact.PenetrationDepth) // { // contact.Normal = normalCandidate; // } // } // //Correct the penetration depth. // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out contact.PenetrationDepth, out rayCastDirection); // ////The local casting can optionally continue. Eventually, it will converge to the local minimum. // //while (true) // //{ // // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out depthCandidate, out normalCandidate); // // if (contact.PenetrationDepth - depthCandidate <= Toolbox.BigEpsilon) // // break; // // contact.PenetrationDepth = depthCandidate; // // contact.Normal = normalCandidate; // //} // contact.Id = -1; // //we're still in local space! transform it all back. // Matrix3X3 orientation; // Matrix3X3.CreateFromQuaternion(ref collidableA.worldTransform.Orientation, out orientation); // Matrix3X3.Transform(ref contact.Normal, ref orientation, out contact.Normal); // //Vector3.Negate(ref contact.Normal, out contact.Normal); // Matrix3X3.Transform(ref contact.Position, ref orientation, out contact.Position); // Vector3.Add(ref contact.Position, ref collidableA.worldTransform.Position, out contact.Position); // if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin) // state = CollisionState.ShallowContact; // return true; //} ////This is rare, but could happen. //state = CollisionState.Separated; //contact = new ContactData(); //return false; #endregion #region Testing //RigidTransform localTransformB; //MinkowskiToolbox.GetLocalTransform(ref collidableA.worldTransform, ref collidableB.worldTransform, out localTransformB); //contact.Id = -1; //if (MPRTesting.GetLocalOverlapPosition(collidableA.Shape, collidableB.Shape, ref localTransformB, out contact.Position)) //{ // Vector3 rayCastDirection = localTransformB.Position; // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out contact.PenetrationDepth, out contact.Normal); // MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out contact.PenetrationDepth, out rayCastDirection); // RigidTransform.Transform(ref contact.Position, ref collidableA.worldTransform, out contact.Position); // Vector3.Transform(ref contact.Normal, ref collidableA.worldTransform.Orientation, out contact.Normal); // return true; //} //contact.Normal = new Vector3(); //contact.PenetrationDepth = 0; //return false; #endregion #region v0.15.2 and before //if (MPRToolbox.AreObjectsColliding(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, out contact)) //{ // if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin) // state = CollisionState.ShallowContact; //If it's emerged from the deep contact, we can go back to using the preferred GJK method. // return true; //} ////This is rare, but could happen. //state = CollisionState.Separated; //return false; #endregion }
// to retain start cycle events protected virtual void Awake() { inputState = GetComponent<InputState>(); rb = GetComponent<Rigidbody2D>(); collisionState = GetComponent<CollisionState>(); ; }
public void testCollisions( ref Vector2 motion, Rectangle boxColliderBounds, CollisionState collisionState ) { _boxColliderBounds = boxColliderBounds; // save off our current grounded state which we will use for wasGroundedLastFrame and becameGroundedThisFrame collisionState.wasGroundedLastFrame = collisionState.below; // reset our collisions state collisionState.reset( ref motion ); // reset rounded motion for us while dealing with subpixel movement so fetch the rounded values to use for our actual detection var motionX = (int)motion.X; var motionY = (int)motion.Y; // first, check movement in the horizontal dir if( motionX != 0 ) { var direction = motionX > 0 ? Edge.Right : Edge.Left; var sweptBounds = collisionRectForSide( direction, motionX ); int collisionResponse; if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) ) { // react to collision. get the distance between our leading edge and what we collided with motion.X = collisionResponse - boxColliderBounds.getSide( direction ); collisionState.left = direction == Edge.Left; collisionState.right = direction == Edge.Right; collisionState._movementRemainderX.reset(); } else { collisionState.left = false; collisionState.right = false; } } // next, check movement in the vertical dir { var direction = motionY >= 0 ? Edge.Bottom : Edge.Top; var sweptBounds = collisionRectForSide( direction, motionY ); sweptBounds.X += (int)motion.X; int collisionResponse; if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) ) { // react to collision. get the distance between our leading edge and what we collided with motion.Y = collisionResponse - boxColliderBounds.getSide( direction ); collisionState.above = direction == Edge.Top; collisionState.below = direction == Edge.Bottom; collisionState._movementRemainderY.reset(); if( collisionState.below && collisionState._lastGroundTile != null && collisionState._lastGroundTile.isSlope() ) collisionState.slopeAngle = MathHelper.ToDegrees( (float)Math.Atan( collisionState._lastGroundTile.getSlope() ) ); } else { collisionState.above = false; collisionState.below = false; collisionState._lastGroundTile = null; } // when moving down we also check for collisions in the opposite direction. this needs to be done so that ledge bumps work when // a jump is made but misses by the colliderVerticalInset if( direction == Edge.Bottom ) { direction = direction.oppositeEdge(); sweptBounds = collisionRectForSide( direction, 0 ); sweptBounds.X += (int)motion.X; sweptBounds.Y += (int)motion.Y; if( testMapCollision( sweptBounds, direction, collisionState, out collisionResponse ) ) { // react to collision. get the distance between our leading edge and what we collided with motion.Y = collisionResponse - boxColliderBounds.getSide( direction ); // if we collide here this is an overlap of a slope above us. this small bump down will prevent hitches when hitting // our head on a slope that connects to a solid tile. It puts us below the slope when the normal response would put us // above it motion.Y += 2; collisionState.above = true; } } } // set our becameGrounded state based on the previous and current collision state if( !collisionState.wasGroundedLastFrame && collisionState.below ) collisionState.becameGroundedThisFrame = true; }
bool testMapCollision( Rectangle collisionRect, Edge direction, CollisionState collisionState, out int collisionResponse ) { collisionResponse = 0; var side = direction.oppositeEdge(); var perpindicularPosition = side.isVertical() ? collisionRect.Center.X : collisionRect.Center.Y; var leadingPosition = collisionRect.getSide( direction ); var shouldTestSlopes = side.isVertical(); populateCollidingTiles( collisionRect, direction ); for( var i = 0; i < _collidingTiles.Count; i++ ) { if( _collidingTiles[i] == null ) continue; // disregard horizontal collisions if the last tile we were grounded on was a slope. Our y movement will push us up on the slope. // this is not a fantastic solution if( direction.isHorizontal() && collisionState._lastGroundTile != null && collisionState._lastGroundTile.isSlope() && _collidingTiles[i].isSlope() ) return false; if( testTileCollision( _collidingTiles[i], side, perpindicularPosition, leadingPosition, shouldTestSlopes, out collisionResponse ) ) { // store off our last ground tile if we collided below if( direction == Edge.Bottom ) { collisionState._lastGroundTile = _collidingTiles[i]; collisionState.isGroundedOnOneWayPlatform = collisionState._lastGroundTile.isOneWayPlatform(); } return true; } // special case for sloped ground tiles if( collisionState._lastGroundTile != null && direction == Edge.Bottom ) { // if grounded on a slope and intersecting a slope or if grounded on a wall and intersecting a tall slope we go sticky. // tall slope here means one where the the slopeTopLeft/Right is 0, i.e. it connects to a wall var isHighSlopeNearest = _collidingTiles[i].getNearestEdge( perpindicularPosition ) == _collidingTiles[i].getHighestSlopeEdge(); if( ( collisionState._lastGroundTile.isSlope() && _collidingTiles[i].isSlope() ) || ( !collisionState._lastGroundTile.isSlope() && isHighSlopeNearest ) ) { // store off our last ground tile if we collided below collisionState._lastGroundTile = _collidingTiles[i]; return true; } } } return false; }
/* * \brief Works out if a value is almost another value (for floating point accuracy) */ public void CallOnCollisionEnter(Collision collision) { m_collisionState = CollisionState.None; foreach (ContactPoint contact in collision) { m_platform = contact.otherCollider.gameObject.GetComponent<CSceneObjectPlatform>(); if (m_platform != null) { m_platform.resetDeltaA(); } if (isNearly(contact.normal.y, 1.0f, 0.2f)) { m_collisionState = CollisionState.OnFloor; // are we on a special material? m_footMaterial = FootMaterial.Stone; if (contact.otherCollider.tag == "Wood Object") m_footMaterial = FootMaterial.Wood; else if (contact.otherCollider.tag == "Metal Object") m_footMaterial = FootMaterial.Metal; } else if (isNearly(contact.normal.y, -1.0f, 0.2f)) { m_collisionState = CollisionState.OnRoof; } else { m_collisionState = CollisionState.OnWall; m_ladderClimb.State = LadderState.None; } } if (m_collisionState == CollisionState.OnFloor) { if (m_jumpState != JumpState.Landed) m_player.GetPlayerAnimation().PlayFootstepAudio(m_footMaterial); m_jumpState = JumpState.Landed; m_ladderClimb.State = LadderState.None; if (m_player.GetPlayerState() != PlayerState.Turning) m_player.SetPlayerState(PlayerState.Standing); //m_ledgeGrabBox.collider.enabled = true; } }
/* * \brief Called whilst a collision is taking place */ public void CallOnCollisionStay(Collision collision, ref PlayerState playerState, ref float playerAlpha) { m_collisionState = CollisionState.None; foreach (ContactPoint contact in collision) { Debug.DrawRay(contact.point, contact.normal); // if (contact.otherCollider != null && contact.otherCollider.gameObject != null) { CSceneObjectPlatform platform = contact.otherCollider.gameObject.GetComponent<CSceneObjectPlatform>(); if (platform != null && m_platform == null) { m_platform = platform; m_platform.resetDeltaA(); } } // CSceneObject obj = null; if (contact.otherCollider) { obj = contact.otherCollider.gameObject.GetComponent<CSceneObject>(); if (obj == null && contact.otherCollider.gameObject.transform.parent != null) { GameObject parent = contact.otherCollider.gameObject.transform.parent.gameObject; if (parent != null) { obj = parent.GetComponent<CSceneObject>(); } } } if (contact.thisCollider != null && contact.thisCollider.gameObject != null && contact.thisCollider.gameObject.name == "Ledge_Grab_Detection" && (obj == null || obj.CanLedgeGrab)) { if(CSceneObject.CheckLedgeGrab(collision)) continue; } if (contact.otherCollider != null && contact.otherCollider.gameObject != null && contact.otherCollider.gameObject.name == "Ledge_Grab_Detection" && (obj == null || obj.CanLedgeGrab)) { if (CSceneObject.CheckLedgeGrab(collision)) continue; } // wall jumping if (obj != null && obj.CanWallJump == true && m_jumpState != JumpState.Landed && !isNearly(contact.normal.y, 1.0f, 0.2f) && !isNearly(contact.normal.y, -1.0f, 0.1f)) { m_collisionState = CollisionState.OnWall; playerState = PlayerState.WallJumpStart; m_jumpTimer = (Time.time * 1000.0f); m_body.constraints = RigidbodyConstraints.FreezeAll; m_velocity = 0.0f; m_wallJump.StartHangTime = Time.time * 1000.0f; } // floor check else if (isNearly(contact.normal.y, 1.0f, 0.8f)) { m_collisionState = CollisionState.OnFloor; if (!isNearly(contact.normal.y, 1.0f, 0.15f) && collision.contacts.Length == 1) { m_velocity = (m_movingDirection * 0.15f); m_velocityLockTimer = (Time.time * 1000.0f); } // are we on a special material? m_footMaterial = FootMaterial.Stone; if (contact.otherCollider.tag == "Wood Object") m_footMaterial = FootMaterial.Wood; else if (contact.otherCollider.tag == "Metal Object") m_footMaterial = FootMaterial.Metal; } // head check else if (isNearly(contact.normal.y, -1.0f, 0.1f)) { m_collisionState = CollisionState.OnRoof; } // wall check else { if (isFacingCollision(m_movingDirection, m_body.transform.position, contact.point, playerAlpha)) { m_collisionState = CollisionState.OnWall; break; } } } if (m_collisionState == CollisionState.OnWall && m_jumpState == JumpState.Jumping && playerState != PlayerState.WallJumpStart) { m_velocity = -(m_movingDirection * 0.15f); } if (m_collisionState == CollisionState.OnFloor && ((Time.time * 1000.0f) - m_jumpTimer > 200.0f)) { m_jumpState = JumpState.Landed; if (m_ladderClimb.State != LadderState.AtBase) { m_ladderClimb.State = LadderState.None; } else { playerState = PlayerState.UpALadder; } if (m_player.GetPlayerState() != PlayerState.Turning) m_player.SetPlayerState(PlayerState.Standing); //m_ledgeGrabBox.collider.enabled = true; } }
public void CallOnTriggerStay(Collider collider, ref PlayerState playerState) { CSceneObject obj = collider.gameObject.GetComponent<CSceneObject>(); if (obj == null && collider.gameObject != null && collider.gameObject.transform.parent != null) { GameObject parent = collider.gameObject.transform.parent.gameObject; if (parent != null) { obj = parent.GetComponent<CSceneObject>(); } } if (obj != null && obj.IsLadder && (m_ladderClimb.State != LadderState.JumpOff)) { m_ladderClimb.CallOnTriggerStay(collider, ref playerState); if (m_ladderClimb.State != LadderState.None) { m_body.constraints = RigidbodyConstraints.FreezeAll; } else { m_collisionState = CollisionState.OnFloor; } return; } }
public void OnUpdate(ref PlayerState playerState) { if (playerState == PlayerState.Standing) { m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_fakeJump = false; if (GetLadder.state != LadderState.AtBase) { // standing, we can't be on a ladder GetLadder.state = LadderState.None; GetLadder.offset = 0.0f; GetLadder.moving = false; GetLadder.direction = 0.0f; } } bool wasJump = m_isJumpDown; m_isJumpDown = Input.GetButtonDown("Jump"); if (Application.platform == RuntimePlatform.Android) m_isJumpDown = Input.touchCount != 0; if (m_isJumpDown && !wasJump) { m_canJumpFromLedge = true; } else { m_canJumpFromLedge = false; } if (m_isJumpDown && m_jumpState == JumpState.Landed && CanJump(playerState)) { if ((Time.time * 1000.0f) - m_jumpTimer > JumpDelayMS) { m_jumpTimer = (Time.time * 1000.0f); m_body.AddForce(new Vector3(0, PlayerJumpHeight, 0), ForceMode.Impulse); m_jumpState = JumpState.Jumping; playerState = PlayerState.Jumping; m_collisionState = CollisionState.None; } } if (m_jumpState == JumpState.Jumping && playerState == PlayerState.Jumping) { if ((Time.time * 1000.0f) - m_jumpTimer > 2000.0f) { playerState = PlayerState.FallJumping; if (!m_fakeJump && ((Time.time * 1000.0f) - m_jumpTimer > 3000.0f)) { m_player.PushPlayerFromTower(); } } } if (m_isJumpDown == true && (GetLadder.state == LadderState.OnMiddle || GetLadder.state == LadderState.OnTop)) { GetLadder.state = LadderState.JumpingOff; m_jumpTimer = (Time.time * 1000.0f); m_body.velocity = Vector3.zero; m_body.AddForce(new Vector3(0, PlayerJumpHeight, 0), ForceMode.Impulse); m_jumpState = JumpState.Jumping; playerState = PlayerState.Jumping; m_collisionState = CollisionState.None; rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; } // LADDER CODE float updown = (Input.GetAxis("Vertical") * 0.001f); if (updown > 0.0f) { if (GetLadder.state == LadderState.AtBase) { if (m_jumpState == JumpState.Jumping) { GetLadder.state = LadderState.OnMiddle; } else { GetLadder.state = LadderState.OnBase; } playerState = PlayerState.OnLadder; m_collisionState = CollisionState.None; m_jumpState = JumpState.Landed; } else if (GetLadder.state == LadderState.OnBase || GetLadder.state == LadderState.OnMiddle) { GetLadder.offset = (updown * 10.0f); GetLadder.moving = true; GetLadder.direction = 1.0f; } else if (GetLadder.state == LadderState.OnTop) { GetLadder.moving = false; GetLadder.direction = 0.0f; GetLadder.offset = 0.0f; } } else if (updown < 0.0f) { if (GetLadder.state == LadderState.OnBase) { GetLadder.state = LadderState.AtBase; playerState = PlayerState.Standing; rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; rigidbody.AddForce(0, 0.1f, 0); } else if (GetLadder.state == LadderState.OnBase || GetLadder.state == LadderState.OnMiddle || GetLadder.state == LadderState.OnTop) { GetLadder.offset = (updown * 10.0f); GetLadder.moving = true; GetLadder.direction = -1.0f; } } if (updown == 0.0f && GetLadder.state != LadderState.None) { GetLadder.moving = false; GetLadder.direction = 0.0f; GetLadder.offset = 0.0f; } }
private bool DoShallowContact(out ContactData contact) { Vector3 closestA, closestB; //RigidTransform transform = RigidTransform.Identity; //Vector3 closestAnew, closestBnew; //CachedSimplex cachedTest = cachedSimplex; //bool intersecting = GJKToolbox.GetClosestPoints(informationA.Shape, informationB.Shape, ref informationA.worldTransform, ref informationB.worldTransform, ref cachedTest, out closestAnew, out closestBnew); ////bool otherIntersecting = OldGJKVerifier.GetClosestPointsBetweenObjects(informationA.Shape, informationB.Shape, ref informationA.worldTransform, ref informationB.worldTransform, 0, 0, out closestA, out closestB); //bool otherIntersecting = GJKToolbox.GetClosestPoints(informationA.Shape, informationB.Shape, ref informationA.worldTransform, ref informationB.worldTransform, out closestA, out closestB); //Vector3 closestAold, closestBold; //bool oldIntersecting = OldGJKVerifier.GetClosestPointsBetweenObjects(informationA.Shape, informationB.Shape, ref informationA.worldTransform, ref informationB.worldTransform, 0, 0, out closestAold, out closestBold); //if (otherIntersecting != intersecting || (!otherIntersecting && !intersecting && // Vector3.DistanceSquared(closestAnew, closestBnew) - Vector3.DistanceSquared(closestA, closestB) > .0001f && // (Vector3.DistanceSquared(closestA, closestAnew) > .0001f || // Vector3.DistanceSquared(closestB, closestBnew) > .0001f)))// || // //Math.Abs(Vector3.Dot(closestB - closestA, closestBnew - closestAnew) - Vector3.Dot(closestB - closestA, closestB - closestA)) > Toolbox.Epsilon))) // Debug.WriteLine("Break."); //Vector3 sub; //Vector3.Subtract(ref closestA, ref closestB, out sub); //if (sub.LengthSquared() < Toolbox.Epsilon) bool intersecting; if (UseSimplexCaching) intersecting = GJKToolbox.GetClosestPoints(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, ref cachedSimplex, out closestA, out closestB); else { //The initialization of the pair creates a pretty decent simplex to start from. //Just don't try to update it. CachedSimplex preInitializedSimplex = cachedSimplex; intersecting = GJKToolbox.GetClosestPoints(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, ref preInitializedSimplex, out closestA, out closestB); } Vector3 displacement; Vector3.Subtract(ref closestB, ref closestA, out displacement); if (intersecting) //if (OldGJKVerifier.GetClosestPointsBetweenObjects(informationA.Shape, informationB.Shape, ref informationA.worldTransform, ref informationB.worldTransform, 0, 0, out closestA, out closestB)) { state = CollisionState.DeepContact; return DoDeepContact(out contact); } localDirection = displacement; //Use this as the direction for future deep contacts. float distanceSquared = displacement.LengthSquared(); float margin = collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin; if (distanceSquared < margin * margin) { //Generate a contact. contact = new ContactData(); //Displacement is from A to B. point = A + t * AB, where t = marginA / margin. if (margin > Toolbox.Epsilon) //Avoid a NaN! Vector3.Multiply(ref displacement, collidableA.Shape.collisionMargin / margin, out contact.Position); //t * AB else contact.Position = new Vector3(); Vector3.Add(ref closestA, ref contact.Position, out contact.Position); //A + t * AB. contact.Normal = displacement; float distance = (float)Math.Sqrt(distanceSquared); Vector3.Divide(ref contact.Normal, distance, out contact.Normal); contact.PenetrationDepth = margin - distance; return true; } //Too shallow to make a contact- move back to separation. state = CollisionState.Separated; contact = new ContactData(); return false; }
private bool DoExternalSeparated(out TinyStructList<ContactData> contactList) { if (GJKToolbox.AreShapesIntersecting(convex, triangle, ref Toolbox.RigidIdentity, ref Toolbox.RigidIdentity, ref localSeparatingAxis)) { state = CollisionState.ExternalNear; return DoExternalNear(out contactList); } TryToEscape(); contactList = new TinyStructList<ContactData>(); return false; }
/// <summary> /// moves the Entity taking into account the tiled map /// </summary> /// <param name="motion">Motion.</param> /// <param name="boxCollider">Box collider.</param> public void move( Vector2 motion, BoxCollider boxCollider, CollisionState collisionState ) { // test for collisions then move the Entity testCollisions( ref motion, boxCollider.bounds, collisionState ); boxCollider.unregisterColliderWithPhysicsSystem(); boxCollider.entity.transform.position += motion; boxCollider.registerColliderWithPhysicsSystem(); }
/* * \brief Called on player update */ public void OnFixedUpdate(ref PlayerState playerState) { if (playerState == PlayerState.FallingFromTower) return; float velocity = (Input.GetAxis("Horizontal") * MaxSpeed) * m_invert; if (Application.platform == RuntimePlatform.Android) velocity = Input.acceleration.y; if ((Time.time * 1000.0f) - m_velocityLockTimer < 100) { velocity = m_velocity; } if (m_ladderClimb.State == LadderState.AtMiddle || m_ladderClimb.State == LadderState.AtTop) velocity = 0; int direction = isNearly(velocity, 0.0f, 0.1f) ? 0 : velocity > 0 ? 1 : -1; //platform update if (m_platform && m_collisionState == CollisionState.OnFloor) { m_platformVelocity += m_platform.DeltaA; m_platform.resetDeltaA(); } // Ledge hanging code start if (playerState == PlayerState.LedgeHang || playerState == PlayerState.LedgeClimb || playerState == PlayerState.LedgeClimbComplete) { m_velocity = 0.0f; // If we are trying to move in the opposite way of the wall, fall off the wall if (direction != 0 && direction != m_movingDirection) { m_velocity = velocity * 4.0f; playerState = PlayerState.Walking; m_jumpState = JumpState.Landed; m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; // m_ledgeGrabBox.collider.enabled = true; m_movingDirection *= -1; m_direction *= -1; m_collisionState = CollisionState.None; // Transform t = m_ledgeGrabBox.transform; // t.localPosition = new Vector3(m_direction > 0 ? -0.18f : 0.18f, t.localPosition.y, t.localPosition.z); return; } // if the player is not already climbing the wall, check keys if (playerState != PlayerState.LedgeClimb) { // if the user pressed up, climb the wall if (Input.GetAxis("Vertical") > 0 && playerState != PlayerState.LedgeClimbComplete) { playerState = PlayerState.LedgeClimb; m_jumpState = JumpState.Landed; // m_ledgeGrabBox.collider.enabled = false; } // if the user pressed space, jump off the wall else if (m_isJumpDown) { m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_velocity = -(m_movingDirection) * 4.0f; // kick it away from the wall m_body.AddForce(new Vector3(0, PlayerJumpHeight * 0.25f, 0), ForceMode.Impulse); playerState = PlayerState.Jumping; m_collisionState = CollisionState.None; m_movingDirection *= -1; m_direction *= -1; // m_ledgeGrabBox.collider.enabled = true; // Transform t = m_ledgeGrabBox.transform; // t.localPosition = new Vector3(m_direction > 0 ? -0.18f : 0.18f, t.localPosition.y, t.localPosition.z); } return; } return; } // Ledge hanging code end if (playerState == PlayerState.WallJumpStart) { if ((Time.time * 1000.0f) - m_wallJump.StartHangTime > m_wallJump.WallHangTime) { m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_velocity = -m_movingDirection; playerState = PlayerState.Walking; m_jumpState = JumpState.Landed; } else if (m_isJumpDown && direction != 0 && direction != m_movingDirection) { m_body.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; m_velocity = -(m_movingDirection); // kick it away from the wall m_body.AddForce(new Vector3(0, PlayerJumpHeight * 1.1f, 0), ForceMode.Impulse); playerState = PlayerState.Jumping; m_jumpTimer = (Time.time * 1000.0f); m_collisionState = CollisionState.None; m_movingDirection *= -1; m_direction *= -1; } return; } if (!(m_collisionState == CollisionState.OnWall && m_jumpState == JumpState.Jumping)) m_velocity = velocity; int lastDirection = m_direction; int lastMovingDirection = m_movingDirection; if (playerState != PlayerState.Turning) { m_direction = direction; if (m_direction != 0) m_movingDirection = m_direction; } if (m_collisionState != CollisionState.None && m_jumpState != JumpState.Jumping) { if (m_direction == 0 && playerState != PlayerState.Turning) { playerState = PlayerState.Standing; } else { if (playerState == PlayerState.Turning) { m_velocity = 0.0f; return; } // are we tuning round? if (lastDirection != direction && ((Time.time * 1000.0f) - m_turnLockTimer > 1000.0f)) { playerState = PlayerState.Walking; if (m_velocity != 0.0f && lastMovingDirection != m_direction) { playerState = PlayerState.Turning; m_velocity = 0.0f; m_turnLockTimer = Time.time * 1000.0f; } } else playerState = PlayerState.Walking; } } }