Advertisement
Ajes

Untitled

Sep 3rd, 2014
3,078
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
BOO 12.72 KB | None | 0 0
  1. These scripts are designed for quickly setting up basic rigidbody-driven 2D orbital physics.
  2.  
  3. Included are two Monobehaviours (Orbiter and OrbitRenderer) and two supporting classes (OrbitalEllipse and OrbitState). Attach Orbiter.js to the object that you would like to put into orbit. Optionally add the OrbitRenderer component for visual feedback while adjusting Orbiter properties. All objects must lie on the same x/y plane.
  4.  
  5.  
  6. == Orbiter.js ==
  7. <syntaxhighlight lang="javascript">
  8. #pragma strict
  9. @script RequireComponent(Rigidbody)
  10.  
  11. //==============================//
  12. //===        Orbiter         ===//
  13. //==============================//
  14.  
  15. /*
  16.   Required component. Add Orbiter.js to the object that you would like to put into orbit.
  17.  
  18.   Dependencies:
  19.     OrbitalEllipse.js - calculates the shape, orientation, and offset of an orbit
  20.     OrbitState.js - calculates the initial state of the orbiter
  21. */
  22.  
  23. var orbitAround : Transform;
  24. var orbitSpeed : float = 10.0; // In the original orbital equations this is gravity, not speed
  25. var apsisDistance : float; // By default, this is the periapsis (closest point in its orbit)
  26. var startingAngle : float = 0; // 0 = starting apsis, 90 = minor axis, 180 = ending apsis
  27. var circularOrbit : boolean = false;
  28. var counterclockwise : boolean = false;
  29.  
  30. private var gravityConstant : float = 100;
  31. private var rb : Rigidbody;
  32. private var trans : Transform;
  33. private var ellipse : OrbitalEllipse;
  34. private var orbitState : OrbitState;
  35.  
  36. // Accessor
  37. function Ellipse () : OrbitalEllipse {
  38.     return ellipse;
  39. }
  40.  
  41. function Transform() : Transform {
  42.     return trans;
  43. }
  44. function GravityConstant () : float {
  45.     return gravityConstant;
  46. }
  47.  
  48.  
  49. // Setup the orbit when the is added
  50. function Reset () {
  51.     if (!orbitAround)
  52.         return;
  53.     ellipse = new OrbitalEllipse(orbitAround.position, transform.position, apsisDistance, circularOrbit);
  54.     apsisDistance = ellipse.endingApsis; // Default to a circular orbit by setting both apses to the same value
  55. }
  56. function OnApplicationQuit () {
  57.     ellipse = new OrbitalEllipse(orbitAround.position, transform.position, apsisDistance, circularOrbit);
  58. }
  59.  
  60. function OnDrawGizmosSelected () {
  61.     if (!orbitAround)
  62.         return;
  63.     // This is required for the OrbitRenderer. For some reason the ellipse var is always null
  64.     // if it's set anywhere else, even including OnApplicationQuit;
  65.     if (!ellipse)
  66.         ellipse = new OrbitalEllipse(orbitAround.position, transform.position, apsisDistance, circularOrbit);
  67.     // Never allow 0 apsis. Start with a circular orbit.
  68.     if (apsisDistance == 0) {
  69.         apsisDistance = ellipse.startingApsis;
  70.     }
  71. }
  72.  
  73.  
  74. function Start () {
  75.     // Cache transform
  76.     trans = transform; 
  77.     // Cache & set up rigidbody
  78.     rb = rigidbody;
  79.     rb.drag = 0;
  80.     rb.useGravity = false;
  81.     rb.isKinematic = false;
  82.    
  83.     // Bail out if we don't have an object to orbit around
  84.     if (!orbitAround) {
  85.         Debug.LogWarning("Satellite has no object to orbit around");
  86.         return;
  87.     }
  88.    
  89.     // Update the ellipse with initial value
  90.     if (!ellipse)
  91.         Reset();
  92.     ellipse.Update(orbitAround.position, transform.position, apsisDistance, circularOrbit);
  93.    
  94.     // Calculate starting orbit state
  95.     orbitState = new OrbitState(startingAngle, this, ellipse);
  96.    
  97.     // Position the orbiter
  98.     trans.position = ellipse.GetPosition(startingAngle, orbitAround.position);
  99.    
  100.     // Add starting velocity
  101.     rb.AddForce(orbitState.velocity, ForceMode.VelocityChange);
  102.     StartCoroutine("Orbit");
  103. }
  104.  
  105. // Coroutine to apply gravitational forces on each fixed update to keep the object in orbit
  106. function Orbit () {
  107.     while (true) {
  108.         // Debug.DrawLine(orbitState.position - orbitState.tangent*4, orbitState.position + orbitState.tangent*4);
  109.         var difference = trans.position - orbitAround.position;
  110.         rb.AddForce(-difference.normalized * orbitSpeed * gravityConstant * Time.fixedDeltaTime / difference.sqrMagnitude, ForceMode.VelocityChange);
  111.         yield WaitForFixedUpdate();
  112.     }
  113. }
  114. </syntaxhighlight>
  115.  
  116.  
  117. == OrbitRenderer.js ==
  118. <syntaxhighlight lang="javascript">
  119. #pragma strict
  120. @script RequireComponent(Orbiter)
  121.  
  122. //===============================//
  123. //===     Orbit Renderer      ===//
  124. //===============================//
  125.  
  126. /*
  127.   Optional component. Display the Orbiter component's properties in the editor. Does nothing in-game.
  128. */
  129.  
  130. var orbitPointsColor : Color = Color(1,1,0,0.5); // Yellow
  131. var orbitPointsSize : float = 0.5;
  132. var ellipseResolution : float = 24;
  133. //var renderAsLines : boolean = false;
  134.  
  135. var startPointColor : Color = Color(1,0,0,0.7); // Red
  136. var startPointSize : float = 1.0;
  137.  
  138. private var orbiter : Orbiter;
  139. private var ellipse : OrbitalEllipse;
  140.  
  141. function Awake () {
  142.     // Remove the component in the compiled game. Likely not a noticeable optimization, just an experiment.
  143.     if (!Application.isEditor)
  144.         Destroy(this);
  145. }
  146.  
  147. function Reset () {
  148.     orbiter = GetComponent(Orbiter);
  149. }
  150. function OnApplicationQuit () {
  151.     orbiter = GetComponent(Orbiter);
  152. }
  153.  
  154.  
  155. function OnDrawGizmosSelected () {
  156.     if (!orbiter)
  157.         orbiter = GetComponent(Orbiter);
  158.        
  159.     // Bail out if there is no object to orbit around
  160.     if (!orbiter.orbitAround)
  161.         return;
  162.    
  163.     // Recalculate the ellipse only when in the editor
  164.     if (!Application.isPlaying) {
  165.         if (!orbiter.Ellipse())
  166.             return;
  167.         orbiter.Ellipse().Update(orbiter.orbitAround.position, transform.position, orbiter.apsisDistance, orbiter.circularOrbit);
  168.     }
  169.    
  170.     DrawEllipse();
  171.     DrawStartingPosition();
  172. }
  173.  
  174. function DrawEllipse () {
  175.     for (var angle = 0; angle < 360; angle += 360 / ellipseResolution) {
  176.         Gizmos.color = orbitPointsColor;
  177.         Gizmos.DrawSphere(orbiter.Ellipse().GetPosition(angle, orbiter.orbitAround.position), orbitPointsSize);
  178.     }
  179. }
  180.  
  181. function DrawStartingPosition () { 
  182.     Gizmos.color = startPointColor;
  183.     Gizmos.DrawSphere(orbiter.Ellipse().GetPosition(orbiter.startingAngle, orbiter.orbitAround.position), startPointSize);
  184. }
  185. </syntaxhighlight>
  186.  
  187.  
  188. == OrbitalEllipse.js ==
  189. <syntaxhighlight lang="javascript">
  190. #pragma strict
  191.  
  192. //===================================//
  193. //===  Elliptical orbit datatype  ===//
  194. //===================================//
  195.  
  196. /*
  197.   Calculates an ellipse to use as an orbital path
  198. */
  199.  
  200. class OrbitalEllipse extends Object {
  201.  
  202.     // "Starting" apsis is the position of the transform.position of the orbiter.
  203.     // "Ending" apsis is the distance that we've defined in the inspector.
  204.     // Each apsis defines the distance from the object we're orbiting to the orbiter
  205.     var startingApsis : float;
  206.     var endingApsis : float;
  207.    
  208.     var semiMajorAxis : float;
  209.     var semiMinorAxis : float;
  210.     var focalDistance : float;
  211.     var difference : Vector3; // difference between the object we're orbiting and the orbiter
  212.    
  213.    
  214.     //==== Instance Methods ====//
  215.    
  216.     // Constructor
  217.     function OrbitalEllipse (orbitAroundPos : Vector3, orbiterPos : Vector3, endingApsis : float, circular : boolean) {
  218.         Update(orbitAroundPos, orbiterPos, endingApsis, circular);
  219.     }
  220.    
  221.     // Update ellipse when orbiter properties change
  222.     function Update (orbitAroundPos : Vector3, orbiterPos : Vector3, endingApsis : float, circular : boolean) {
  223.         this.difference = orbiterPos - orbitAroundPos;
  224.         this.startingApsis = difference.magnitude;
  225.         if (endingApsis == 0 || circular)
  226.             this.endingApsis = this.startingApsis;
  227.         else
  228.             this.endingApsis = endingApsis;
  229.         this.semiMajorAxis = CalcSemiMajorAxis(this.startingApsis, this.endingApsis);
  230.         this.focalDistance = CalcFocalDistance(this.semiMajorAxis, this.endingApsis);
  231.         this.semiMinorAxis = CalcSemiMinorAxis(this.semiMajorAxis, this.focalDistance);
  232.     }
  233.    
  234.     // The global position
  235.     function GetPosition (degrees : float, orbitAroundPos : Vector3) : Vector3 {
  236.         // Use the difference between the orbiter and the object it's orbiting around to determine the direction
  237.         // that the ellipse is aimed
  238.         // Angle is given in degrees
  239.         var ellipseDirection : float = Vector3.Angle(Vector3.left, difference); // the direction the ellipse is rotated
  240.         if (difference.y < 0) {
  241.             ellipseDirection = 360-ellipseDirection; // Full 360 degrees, rather than doubling back after 180 degrees
  242.         }
  243.        
  244.         var beta : float = ellipseDirection * Mathf.Deg2Rad;
  245.         var sinBeta : float = Mathf.Sin(beta);
  246.         var cosBeta : float = Mathf.Cos(beta);
  247.        
  248.         var alpha = degrees * Mathf.Deg2Rad;
  249.         var sinalpha = Mathf.Sin(alpha);
  250.         var cosalpha = Mathf.Cos(alpha);
  251.        
  252.         // Position the ellipse relative to the "orbit around" transform
  253.         var ellipseOffset : Vector3 = difference.normalized * (semiMajorAxis - endingApsis);
  254.        
  255.         var finalPosition : Vector3 = new Vector3();
  256.         finalPosition.x = ellipseOffset.x + (semiMajorAxis * cosalpha * cosBeta - semiMinorAxis * sinalpha * sinBeta) * -1;
  257.         finalPosition.y = ellipseOffset.y + (semiMajorAxis * cosalpha * sinBeta + semiMinorAxis * sinalpha * cosBeta);
  258.        
  259.         // Offset entire ellipse proportional to the position of the object we're orbiting around
  260.         finalPosition += orbitAroundPos;
  261.        
  262.         return finalPosition;
  263.     }
  264.    
  265.    
  266.     //==== Private Methods ====//
  267.    
  268.     private function CalcSemiMajorAxis (startingApsis : float, endingApsis : float) : float {
  269.         return (startingApsis + endingApsis) * 0.5;
  270.     }
  271.     private function CalcSemiMinorAxis (semiMajorAxis : float, focalDistance : float) : float {
  272.         var distA : float = semiMajorAxis + focalDistance*0.5;
  273.         var distB : float = semiMajorAxis - focalDistance*0.5;
  274.         return Mathf.Sqrt( Mathf.Pow(distA+distB,2) - focalDistance*focalDistance ) * 0.5;
  275.     }
  276.     // private function CalcEccentricity (semiMajorAxis : float, focalDistance : float) : float {
  277.     //  return focalDistance / (semiMajorAxis * 2);
  278.     // }
  279.     private function CalcFocalDistance (semiMajorAxis : float, endingApsis : float) : float {
  280.         return (semiMajorAxis - endingApsis) * 2;
  281.     }          
  282. }
  283. </syntaxhighlight>
  284.  
  285.  
  286. == OrbitState.js ==
  287. <syntaxhighlight lang="javascript">
  288. #pragma strict
  289.  
  290. //================================//
  291. //===   Orbit State datatype   ===//
  292. //================================//
  293.  
  294. /*
  295.  The OrbitState is the initial state of the orbiter at a particular point along the ellipse
  296.  The state contains all of the information necessary to apply a force to get the orbiter moving along the ellipse
  297. */
  298.  
  299. class OrbitState extends Object {
  300.     var position : Vector3; // local position relative to the object we're orbiting around
  301.     var normal : Vector3;
  302.     var tangent : Vector3;
  303.     var velocity : Vector3;
  304.     private var orbiter : Orbiter;
  305.     private var ellipse : OrbitalEllipse;  
  306.    
  307.     //==== Instance Methods ====//
  308.    
  309.     // Constructor
  310.     function OrbitState (angle : float, orbiter : Orbiter, ellipse : OrbitalEllipse) {
  311.         Update(angle, orbiter, ellipse);
  312.     }
  313.    
  314.     // Update the state of the orbiter when its position along the ellipse changes
  315.     // Note: Make sure the ellipse is up to date before updating the orbit state
  316.     function Update (orbiterAngle : float, orbiter : Orbiter, ellipse : OrbitalEllipse) {
  317.         this.orbiter = orbiter;
  318.         this.ellipse = ellipse;
  319.         this.normal = CalcNormal(orbiterAngle);
  320.         this.tangent = CalcTangent(normal);
  321.         this.position = ellipse.GetPosition(orbiterAngle, orbiter.orbitAround.position);
  322.         this.velocity = CalcVelocity(orbiter.orbitSpeed * orbiter.GravityConstant(), position, orbiter.orbitAround.position);
  323.     }
  324.    
  325.    
  326.     //==== Private Methods ====//
  327.    
  328.     // Returns the normal on the ellipse at the given angle
  329.     // Assumes a vertical semi-major axis, and a rotation of 0 at the top of the ellipse, going clockwise
  330.     private function CalcNormal (rotationAngle : float) : Vector3 {
  331.         // Part 1: Find the normal for the orbiter at its starting angle
  332.         // Rotate an upward vector by the given starting angle around the ellipse. Gives us the normal for a circle.
  333.         var localNormal : Vector3 = Quaternion.AngleAxis(rotationAngle, Vector3.forward*-1) * Vector3.up;
  334.         // Sqash the normal into the shape of the ellipse
  335.         localNormal.x *= ellipse.semiMajorAxis/ellipse.semiMinorAxis;
  336.        
  337.         // Part 2: Find the global rotation of the ellipse
  338.         var ellipseAngle : float = Vector3.Angle(Vector3.up, ellipse.difference);
  339.         if (ellipse.difference.x < 0)
  340.             ellipseAngle = 360-ellipseAngle; // Full 360 degrees, rather than doubling back after 180 degrees
  341.  
  342.         // Part 3: Rotate our normal to match the rotation of the ellipse
  343.         var globalNormal : Vector3 = Quaternion.AngleAxis(ellipseAngle, Vector3.forward*-1) * localNormal;
  344.         return globalNormal.normalized;
  345.     }
  346.    
  347.     private function CalcTangent (normal : Vector3) : Vector3 {
  348.         var angle : float = 90;
  349.         var direction : int = orbiter.counterclockwise ? -1 : 1;
  350.         var tangent = Quaternion.AngleAxis(angle*direction, Vector3.forward*-1) * normal;
  351.         return tangent;
  352.     }
  353.    
  354.     private function CalcVelocity (gravity : float, orbiterPos : Vector3, orbitAroundPos : Vector3) : Vector3 {
  355.         // Vis Viva equation
  356.         var speed : float = Mathf.Sqrt( gravity * (2/Vector3.Distance(orbiterPos, orbitAroundPos) - 1/ellipse.semiMajorAxis ) );
  357.         var velocityVec : Vector3 = tangent * speed;
  358.         return velocityVec;
  359.     }  
  360. }
  361. </syntaxhighlight>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement