Guest User

Untitled

a guest
Feb 11th, 2019
94
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*************************************************************
  2. /* Created by @kingbladeDev ( on twitter - https://twitter.com/kingbladeDev )
  3. /* Handles Unity kinematic physics for objects that are able to push one another and are effected by eachother's weight
  4. /************************************************************/
  5.  
  6.  
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11.  
  12. public class PhysicsObject : MonoBehaviour
  13. {
  14.     private static Vector2 GRAVITY = new Vector2(0, -600);
  15.  
  16.     [Range(0.0001f, Mathf.Infinity)] public float weight = 1;
  17.  
  18.     public Vector2 velocity;
  19.     protected ContactFilter2D contactFilter;
  20.     protected RaycastHit2D[] hitBuffer = new RaycastHit2D[64];
  21.  
  22.     public float gravityModifier = 1;
  23.  
  24.     protected Rigidbody2D rb2d;
  25.     protected BoxCollider2D box;
  26.  
  27.     private Vector2 weightFromLastPush = Vector2.zero;
  28.  
  29.     void OnEnable()
  30.     {
  31.         rb2d = GetComponent<Rigidbody2D>();
  32.         box = GetComponent<BoxCollider2D>();
  33.     }
  34.  
  35.     void Start()
  36.     {
  37.         contactFilter.useTriggers = false;
  38.         contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer));
  39.         contactFilter.useLayerMask = true;
  40.     }
  41.  
  42.     Vector2 CalculateDeltaFrame()
  43.     {
  44.         // Apply gravity
  45.         velocity += gravityModifier * GRAVITY * Time.fixedDeltaTime;
  46.  
  47.         // calculate the delta position that should be moved this update
  48.         Vector2 deltaPosition = velocity * Time.fixedDeltaTime;
  49.  
  50.         return deltaPosition;
  51.     }
  52.  
  53.  
  54.     void FixedUpdate()
  55.     {
  56.  
  57.         // get how much you should move
  58.         Vector2 averageMovement = CalculateDeltaFrame() * weight;
  59.         Vector2 deltaPosition = averageMovement / (weight * Vector2.one + weightFromLastPush);
  60.         weightFromLastPush = Vector2.zero;
  61.  
  62.         // Handle X movement
  63.         Vector2 move = Vector2.right * deltaPosition.x;
  64.         Movement(move, false);
  65.  
  66.         // Handle Y movement
  67.         move = Vector2.up * deltaPosition.y;
  68.         Movement(move, true);
  69.     }
  70.  
  71.     class PushInfo
  72.     {
  73.         public PhysicsObject obj;
  74.         public Vector2 motionOriginalPosition;
  75.         public float distanceInsideWall = 0;
  76.     }
  77.  
  78.     PushInfo GetPushInfo(List<PushInfo> rootMotionInfluenced, PhysicsObject obj, bool createOnNotExist = false)
  79.     {
  80.         foreach (PushInfo info in rootMotionInfluenced)
  81.         {
  82.             if (info.obj == obj)
  83.             {
  84.                 return info;
  85.             }
  86.         }
  87.  
  88.         if (createOnNotExist)
  89.         {
  90.             PushInfo info = new PushInfo();
  91.             info.obj = obj;
  92.             info.motionOriginalPosition = obj.rb2d.position;
  93.             rootMotionInfluenced.Add(info);
  94.             return info;
  95.         }
  96.  
  97.         return null;
  98.     }
  99.    
  100.     void Movement(Vector2 move, bool yMove, List<PushInfo> rootMotionInfluenced = null)
  101.     {
  102.         float distance = move.magnitude;
  103.  
  104.         // check if we are the root of the movement
  105.         bool root = false;
  106.         if(rootMotionInfluenced == null) {
  107.             root = true;
  108.             rootMotionInfluenced = new List<PushInfo>();
  109.         }
  110.  
  111.         // calculate the data for box casting.
  112.         // it should shave a little bit of the box from the side to no collide with objects like a nearby wall when falling or the floor when moving to the side
  113.         Vector2 boxSize = box.size * transform.lossyScale - new Vector2(yMove ? 0.1f : 0, yMove ? 0 : 0.1f);
  114.  
  115.         // cast for collision
  116.         int count = Physics2D.BoxCastNonAlloc(rb2d.position, boxSize, 0, move.normalized, hitBuffer, move.magnitude, contactFilter.layerMask);
  117.         float distanceInWall = 0;
  118.  
  119.         // run on hits
  120.         for (int i = 0; i < count; i++)
  121.         {
  122.             // colliding with yourself is not a thing
  123.             if (hitBuffer[i].transform == transform)
  124.             {
  125.                 continue;
  126.             }
  127.  
  128.             // since we are calling this function once for x-movement and one for y-movement
  129.             // we want to make sure that the collision is in the right direction, otherwise we might get blocked
  130.             // by a collision that is to our left of us when we want to move to the right
  131.             if (hitBuffer[i].normal * -1 != move.normalized)
  132.             {
  133.                 continue;
  134.             }
  135.  
  136.             // calculate potential push distance (with shell distance)
  137.             float pushDistance = Mathf.Max(0, distance - hitBuffer[i].distance);
  138.  
  139.             // if possible, push the object
  140.             PhysicsObject other = hitBuffer[i].rigidbody.GetComponent<PhysicsObject>();
  141.             if (other != null)
  142.             {
  143.                 // if the object was already pushed by this root motion, skip it
  144.                 if (GetPushInfo(rootMotionInfluenced, other) == null)
  145.                 {
  146.                     // add to the 'moved' list
  147.                     GetPushInfo(rootMotionInfluenced, other, true);
  148.  
  149.                     // push instead of being pushed :D
  150.                     other.Push(pushDistance * move.normalized, yMove, rootMotionInfluenced);
  151.                 }
  152.             }
  153.             // if not possible, we are against a wall
  154.             else
  155.             {
  156.                 // calculate the maximum distance we are inside a wall
  157.                 distanceInWall = Mathf.Max(pushDistance, distanceInWall);
  158.             }
  159.         }
  160.  
  161.         // add to the pushing list
  162.         PushInfo pushedEntry = GetPushInfo(rootMotionInfluenced, this, true);
  163.         pushedEntry.distanceInsideWall = Mathf.Max(distanceInWall, pushedEntry.distanceInsideWall);
  164.  
  165.         // move the object
  166.         rb2d.position = Round(rb2d.position + move); //.normalized * distance;
  167.  
  168.         // if we are the root, fix the collisions
  169.         if (root)
  170.         {
  171.             float totalWeight = 0;
  172.             float maxDistanceInsideWall = 0;
  173.             foreach (PushInfo i in rootMotionInfluenced)
  174.             {
  175.                 totalWeight += i.obj.weight;
  176.                 maxDistanceInsideWall = Mathf.Max(maxDistanceInsideWall, i.distanceInsideWall);
  177.  
  178.                 // add wight we are pushing to the calculation for next frame
  179.                 if (i.obj != this)
  180.                 {
  181.                     weightFromLastPush += i.obj.weight * new Vector2(yMove ? 0 : 1, yMove ? 1 : 0);
  182.                 }
  183.             }
  184.  
  185.             // reposition the pushed objects
  186.             foreach (PushInfo i in rootMotionInfluenced)
  187.             {
  188.                 Vector2 objMoved = i.obj.rb2d.position - i.motionOriginalPosition;
  189.                 i.obj.rb2d.position = Round(i.motionOriginalPosition + objMoved);
  190.                 Vector2 setBackByWall = move.normalized * maxDistanceInsideWall;
  191.  
  192.                 // set the objects back as they should
  193.                 Vector2 positionAfterSetback = i.obj.rb2d.position - setBackByWall;
  194.                 if ((positionAfterSetback - i.motionOriginalPosition).normalized != move.normalized)
  195.                 {
  196.                     i.obj.rb2d.position = Round(i.motionOriginalPosition);
  197.                 }
  198.                 else
  199.                 {
  200.                     i.obj.rb2d.position = Round(positionAfterSetback);
  201.                 }
  202.  
  203.                 // set speed for the original object or it's connected objects
  204.                 if (i.obj.rb2d.position - i.motionOriginalPosition == Vector2.zero && i.obj == this)
  205.                 {
  206.                     if (yMove)
  207.                     {
  208.                         i.obj.velocity.y = 0;
  209.                     }
  210.                     else
  211.                     {
  212.                         i.obj.velocity.x = 0;
  213.                     }
  214.                 }
  215.             }
  216.         }
  217.     }
  218.  
  219.     private void Push(Vector2 movement, bool yMove, List<PushInfo> rootMotionInfluenced)
  220.     {
  221.         // Handle X movement
  222.         if (!yMove)
  223.         {
  224.             Movement(new Vector2(movement.x, 0), false, rootMotionInfluenced);
  225.         }
  226.         // Handle Y movement
  227.         else
  228.         {
  229.             Movement(new Vector2(0, movement.y), true, rootMotionInfluenced);
  230.         }
  231.     }
  232.  
  233.     Vector2 Round(Vector2 v, float digits = 2)
  234.     {
  235.         float mul = Mathf.Pow(10, digits);
  236.         return new Vector2(Mathf.Round(mul * v.x), Mathf.Round(mul * v.y)) / mul;
  237.     }
  238. }
RAW Paste Data