void ApplyFrictionForce(CustomRigidbody objectA, CustomRigidbody objectB, Vector2 normal, float impValue, Vector2 impulse) { //assign new friction values float aInverseMass = 1 / objectA.m_Mass; float bInverseMass = 1 / objectB.m_Mass; // Get the recalculated relative velocity after the previous impulse has been applied Vector2 rv = objectB.m_Velocity - objectA.m_Velocity; // Retrieve the tangent vector, the vector that determines the direction of friction force to apply Vector2 tangent = rv - Vector2.Dot(rv, normal) * normal; tangent.Normalize(); // calculate the friction impulse value float jT = -Vector2.Dot(rv, tangent) / (aInverseMass + bInverseMass); // Coulombs law: force of friction <= force along the normal * mu float mu = (objectA.m_MaterialType.m_StaticFriction + objectB.m_MaterialType.m_StaticFriction) / 2; Vector2 frictionImpulse; // clamp the magnitude of the friction and create the friction impulse vector if (Mathf.Abs(jT) < impValue * mu) { frictionImpulse = jT * tangent; } else { float dynamicFriction = (objectA.m_MaterialType.m_DynamicFriction + objectB.m_MaterialType.m_DynamicFriction) / 2; impulse.Scale(tangent); Vector2 j = new Vector2(-impulse.x, -impulse.y); frictionImpulse = j * dynamicFriction; } objectA.m_Velocity -= (aInverseMass) * frictionImpulse; objectB.m_Velocity += (bInverseMass) * frictionImpulse; }
// all physics calculations are done within here // this is called from within MainManager public void PhysicsUpdate() { accum += deltaTime; //Calculate physics on the current frame while (accum > deltaTime) { colliderTest1 = null; colliderTest2 = null; currentCollisionPair = new List <CollisionPair>(); depthX = 0f; depthY = 0f; xDistance = 0f; yDistance = 0f; xDirection = 0f; yDirection = 0f; // if no path simulate normally //calculate rigidbodies for (int i = 0; i < rigidBodiesInScene.Count; i++) { // entity calc if (rigidBodiesInScene[i].tag == "Entity") { bool charging = false; recentHit = false; // check if player attacked // player charge attack if (Input.GetMouseButtonDown(0)) { chargeDmg = 0; chargeRate = 1000f; charging = true; if (charging == true) { chargeDmg += chargeRate * Time.deltaTime; } } if (Input.GetMouseButtonUp(0)) { charging = false; chargeDmg = Mathf.Clamp(chargeDmg, 0f, 100f); //create a player attack area and check if the enemy is inside it GameObject t = Instantiate(Resources.Load("EntityAttack")) as GameObject; GameObject graphic = Instantiate(Resources.Load("AttackGraphic")) as GameObject; EntityAttack et = t.GetComponent <EntityAttack>(); et.InitAttack(); CustomRigidbody player = GameObject.FindGameObjectWithTag("Player").GetComponent <CustomRigidbody>(); Vector2 direction = et.GetDirection(player).normalized; t.transform.position = (Vector2)player.transform.position + direction; graphic.transform.position = (Vector2)player.transform.position + direction; graphic.transform.SetParent(player.transform); Transform[] q = graphic.GetComponentsInChildren <Transform>(); foreach (Transform tr in q) { if (tr.tag == "pointer") { graphic.transform.rotation = tr.rotation; } } if (et.IsColliding(rigidBodiesInScene[i])) { // if hit, set enemy velocity to the direction of where the player pointed rigidBodiesInScene[i].m_IsIgnoringGravity = false; rigidBodiesInScene[i].m_Velocity = direction * chargeDmg; rigidBodiesInScene[i].m_IsEnemyDead = true; Destroy(et.gameObject); DestroyObject(graphic.gameObject, 0.5f); } else { DestroyObject(graphic.gameObject); Destroy(et.gameObject, 0.5f); } } // if entity has been attacked, dont path find AStarPathfind a = new AStarPathfind(); if (rigidBodiesInScene[i].m_IsEnemyDead != true) { // calculate entity positions for pathfinding playerPos = new Vector2(0, 0); enemyPos = new Vector2(0, 0); for (int l = 0; l < entityBoundingBoxes.Count; l++) { if (entityBoundingBoxes[l].tag == "Player") { playerPos = entityBoundingBoxes[l].transform.position; } } // generate path to player playerNode = grid.NodeFromWorldPoint(playerPos); enemyNode = grid.NodeFromWorldPoint(rigidBodiesInScene[i].transform.position); path = a.AStarSearch(grid, enemyNode, playerNode); dPath = path; //path = null; } else { Destroy(a); path = null; } // if path exists move through it if (path != null) { for (int j = 0; j < path.Count; j++) { rigidBodiesInScene[i].m_EnemyHasPath = false; Vector2 direction = (path[j] - (Vector2)rigidBodiesInScene[i].transform.position); direction.Normalize(); rigidBodiesInScene[i].m_EnemyMoveDirection = direction; rigidBodiesInScene[i].m_EnemyHasPath = true; rigidBodiesInScene[i].EnemyPhysicsLoop(); } } // if entity has been attacked, re enable gravity else if (path == null || rigidBodiesInScene[i].m_IsEnemyDead == true) { rigidBodiesInScene[i].m_EnemyHasPath = false; rigidBodiesInScene[i].m_IsIgnoringGravity = false; rigidBodiesInScene[i].EnemyPhysicsLoop(); } } // calc player and other rigidbodies if (rigidBodiesInScene[i].tag == "Player") { rigidBodiesInScene[i].PlayerPhysicsLoop(); } else if (rigidBodiesInScene[i].tag != "Player" || rigidBodiesInScene[i].tag != "Entity") { rigidBodiesInScene[i].PlayerPhysicsLoop(); } } //calculate collisions for (int j = 0; j < entityBoundingBoxes.Count; j++) { // this is the current entity we are checking colliders against // we dont need to check collisions between platforms. colliderTest1 = entityBoundingBoxes[j]; //collision between entity and ground for (int k = 0; k < floorBoundingBoxes.Count; k++) { colliderTest2 = floorBoundingBoxes[k]; if (TestAABBOverlap(colliderTest1, colliderTest2)) { if (colliderTest1.GetComponent <CustomRigidbody>().m_IsEnemyDead == true) { entityBoundingBoxes.Remove(colliderTest1); rigidBodiesInScene.Remove(colliderTest1.GetComponent <CustomRigidbody>()); Destroy(colliderTest1.gameObject); isEnemyActive = false; } colliderTest1.GetComponent <CustomRigidbody>().m_IsGrounded = true; //Debug.Log(string.Format("Ground Collision between: {0} and {1}", colliderTest1, colliderTest2)); PopulateCollisionData(colliderTest1, colliderTest2); } } //collision between entity and entity for (int l = 0; l < entityBoundingBoxes.Count; l++) { colliderTest2 = entityBoundingBoxes[l]; //Don't collide with itself if (colliderTest1 == colliderTest2) { continue; } if (TestAABBOverlap(colliderTest1, colliderTest2)) { //Debug.Log(string.Format("Entity Collision between: {0} and {1}", colliderTest1, colliderTest2)); PopulateCollisionData(colliderTest1, colliderTest2); float[] moveVals = { 12, -12 }; int chooseAxis = 0; // enemy 'attacks' // choose a random direction to set the players velocity in if (colliderTest1.tag == "Player") { CustomRigidbody rb = colliderTest1.GetComponent <CustomRigidbody>(); rb.m_PlayerHasBeenHit = true; float v = Random.Range(0, moveVals.Length); chooseAxis = Random.Range(0, 1); if (chooseAxis == 0) { rb.m_Velocity = new Vector2(moveVals[Random.Range(0, moveVals.Length)], 0); } if (chooseAxis == 1) { rb.m_Velocity = new Vector2(0, moveVals[Random.Range(0, moveVals.Length)]); } } if (colliderTest2.tag == "Player") { CustomRigidbody rb = colliderTest2.GetComponent <CustomRigidbody>(); rb.m_PlayerHasBeenHit = true; int v = Random.Range(0, moveVals.Length); chooseAxis = Random.Range(0, 1); if (chooseAxis == 0) { rb.m_Velocity = new Vector2(moveVals[Random.Range(0, moveVals.Length)], 0); } if (chooseAxis == 1) { rb.m_Velocity = new Vector2(0, moveVals[Random.Range(0, moveVals.Length)]); } } } } // Finally check whether an entity has hit the boundaries of the screen // and needs to be removed from scene for (int i = 0; i < boundaryBoundingBoxes.Count; i++) { colliderTest2 = boundaryBoundingBoxes[i]; if (TestAABBOverlap(colliderTest1, colliderTest2)) { if (colliderTest1.tag == "Player") { entityBoundingBoxes.Remove(colliderTest1); rigidBodiesInScene.Remove(colliderTest1.GetComponent <CustomRigidbody>()); Destroy(colliderTest1.gameObject); isPlayerActive = false; } if (colliderTest1.tag == "Entity") { entityBoundingBoxes.Remove(colliderTest1); rigidBodiesInScene.Remove(colliderTest1.GetComponent <CustomRigidbody>()); Destroy(colliderTest1.gameObject); isEnemyActive = false; } } } } accum -= deltaTime; } }
void ResolveCollisionPair(List <CollisionPair> pairs) { CustomRigidbody objectA = pairs[0].m_ObjectA.GetComponent <CustomRigidbody>(); CustomRigidbody objectB = pairs[0].m_ObjectB.GetComponent <CustomRigidbody>(); // get relative velocity of each object this is used to find out how far to move each object Vector2 objAVelocity = objectA.m_Velocity; Vector2 objBVelocity = objectB.m_Velocity; Vector2 relativeVelocity = objBVelocity - objAVelocity; // normalise the contact normal to make sure it only points in the right direction Vector2 normalMagnitude = pairs[0].m_CollisionNormal; normalMagnitude.Normalize(); // get the velocity along the normal float velocityAlongNormal = Vector2.Dot(relativeVelocity, normalMagnitude); // calculate the restitution (elasticity) which is between 0 and 1 // should change to a property of each rigidbody float coefficientOfRestitution = Mathf.Min(0.01f, 0.02f); // calculate the inverse mass to impulse (force) the objects away // do this to save from having to calculate it like 5 more times float aInverseMass; float bInverseMass; if (objectA.m_Mass == 0) { aInverseMass = 0; } else { aInverseMass = 1 / objectA.m_Mass; } if (objectB.m_Mass == 0) { bInverseMass = 0; } else { bInverseMass = 1 / objectB.m_Mass; } // calculate the impulse float impulse = -(1 + coefficientOfRestitution) * velocityAlongNormal / (aInverseMass + bInverseMass); // create and assign the new velocities Vector2 imp = impulse * normalMagnitude; objectA.m_Velocity -= aInverseMass * imp; objectB.m_Velocity += bInverseMass * imp; // stop sinking objects by offsetting the positions to above a certain threshold // depending on the penetration depth float percent = 0.2f; // the amount we want to move each colliding object float slop = 0.01f; // the amount we let the 2 objects sink into eachother to stop jittering float xAmount = 0f; float yAmount = 0f; // get the side that is being most penetrated xAmount = pairs[0].m_PenetrationDepth.x; yAmount = pairs[0].m_PenetrationDepth.y; Vector2 correction = Mathf.Max(-yAmount - slop, 0.0f) / (aInverseMass + bInverseMass) * percent * pairs[0].m_CollisionNormal; objectA.m_Velocity -= aInverseMass * correction; objectB.m_Velocity += bInverseMass * correction; }