Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Inverting Gravity to Walk on the Ceiling

Discussion in 'Scripting' started by SalemSage, Nov 23, 2009.

  1. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    Hi there, I'm working on a game in Unity. In one part of the game, the player finds himself in a unique area where he has control over his personal gravity.

    By pressing a certain key, the player can invert gravity so that the ground becomes the sky and vice versa.

    Here's my code so far:

    Code (csharp):
    1. var speed = 6.0;
    2. var jumpSpeed = 8.0;
    3. var gravity = 20.0;
    4. var inversiongravity = 0;
    5. private var moveDirection = Vector3.zero;
    6. private var grounded : boolean = false;
    7.  
    8. function FixedUpdate() {
    9.     if (grounded) {
    10.         // We are grounded, so recalculate movedirection directly from axes
    11.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    12.         moveDirection = transform.TransformDirection(moveDirection);
    13.         moveDirection *= speed;
    14.        
    15.         if (Input.GetButton ("Jump")) {
    16.             moveDirection.y = jumpSpeed;
    17.         }
    18.     }
    19. if (Input.GetButtonDown ("Fire1")) {
    20.    
    21.         if(inversiongravity == 0){
    22.         inversiongravity = 1;
    23.         Physics.gravity = Vector3(0, -1.0, 0);
    24.         }
    25.         else{
    26.             inversiongravity = 0;
    27.             Physics.gravity = Vector3(0, 1.0, 0);
    28.         }
    29.     }
    30.     // Apply gravity
    31.     if(inversiongravity == 0)
    32.     {
    33.         moveDirection.y -= gravity * Time.deltaTime;
    34.     }
    35.     else
    36.     {
    37.         moveDirection.y += gravity * Time.deltaTime;
    38.     }
    39.     // Move the controller
    40.     var controller : CharacterController = GetComponent(CharacterController);
    41.     var flags = controller.Move(moveDirection * Time.deltaTime);
    42.     grounded = (flags  CollisionFlags.CollidedBelow) != 0;
    43.    
    44. }
    45. @script RequireComponent(CharacterController)
    So far it works okay, but there's one or two things I'm not sure about:

    1) Despite the fact that I can change gravity, the character won't walk on the ceiling, instead being stuck in a constant 'falling' state. How do I make it so that the character is turned 180 degrees and treats the ceiling as the floor and vice versa?

    2) To prevent abuse, and to encourage players to look up to see where they will be landing, I want it so that the player cannot invert gravity unless they have touched the ground. For example, if the player inverts gravity, they'll fall upwards, and can't re-invert gravity until they touch the ground. However once they have touched it, they can invert it again, even if they're in the air. They just can't invert it more than once in the air. Hope that's clear.


    I'm not sure how to do either of these, yet I can't help but think it would be a simple matter. Can anyone help me out here?
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Hi, welcome to the forum!

    I think you will have to rotate the character from the script. Reversing gravity will make an object "fall" upward, but there's no particular reason why it would spin around so that its feet point towards the floor.

    As for the other question, you'll notice that the CharacterController has a variable called isGrounded. This can be used to detect if the character is touching the floor. Once the object has left the floor (when gravity is reversed), set a boolean value. This denotes that gravity can't be reversed again until the object has touched down. When the object lands (as detected by CharacterController.isGrounded), clear this boolean value.
     
  3. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    Hi, thanks for posting, that clears up some matters.

    I'm not entirely sure how to flip the player upside down, though - the scripting manual mentions something about a "Transform.Rotate", but it seems to be only by Time.deltaTime, as far as I know, instead of choosing a certain number of degrees to be rotated by. Ideally it should be 180, but my modifications aren't making a difference.

    How would I go about fixing this?
     
  4. Slem

    Slem

    Joined:
    Jan 28, 2009
    Posts:
    191
    You could set rotation manually and use Lerp or Slerp to make it look cool.
    Code (csharp):
    1.  
    2. transform.rotation = Quaternion.Lerp(transform.rotation, wantedRotation, Time.deltaTime); // or calculate lerptime to make a constant rotation
    3.  
     
  5. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    Thanks for replying, but I get an error message saying:
    I'm not even sure about what it means. :/
     
  6. Slem

    Slem

    Joined:
    Jan 28, 2009
    Posts:
    191
  7. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    I'm sorry to give you so much trouble, I'm entirely new to scripting >_<. I do appreciate the help though, I really do.

    I'm just not entirely sure what you mean by inputting a quaternion in the first and second parameter.
     
  8. Slem

    Slem

    Joined:
    Jan 28, 2009
    Posts:
    191
    No trouble at all.

    What you want do to is to make a boolean which you set to true when you invert gravity. At the same time you create a new rotation which will be the wanted rotation. I am sorry about my poor explanation, but maybe some code is easier.

    Code (csharp):
    1.  
    2. //global vars
    3. bool Rotate = false;
    4. Quaternion WantedRotation;
    5.  
    6. //inside your update
    7. if(Input.GetButtonDown("Fire1"))
    8. {
    9. Rotate = true;
    10. WantedRotation = transform.rotation;
    11. WantedRotation.eulerAngles = new Vector3(180, 0, 0);
    12.  
    13. }
    14.  
    15. if(Rotate)
    16. {
    17. transform.rotation = Quaternion.Lerp(transform.rotation, WantedRotation, Time.deltaTime);
    18.  
    19. }
    20.  
    You also need to set Rotate false when rotation is done. I have really little time now so I have to go. I will look in on this thread later.

    I might be wrong here, but this works for me.
     
  9. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    So I did as you said; I still have an error, so I'm going to post the code as it currently is:

    Code (csharp):
    1. var speed = 6.0;
    2. var jumpSpeed = 8.0;
    3. var gravity = 20.0;
    4. var wantedRotation = 180.0;
    5. var inversiongravity = 0;
    6. var from : Transform;
    7. var to : Transform;
    8. var HasInvertedGravity = 0;
    9. var boolean Rotate = false;
    10. Quaternion WantedRotation;
    11.  
    12.  
    13. private var moveDirection = Vector3.zero;
    14. private var grounded : boolean = false;
    15.  
    16. function FixedUpdate() {
    17.     if (grounded) {
    18.         HasInvertedGravity = 0;
    19.         // We are grounded, so recalculate movedirection directly from axes
    20.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    21.         moveDirection = transform.TransformDirection(moveDirection);
    22.         moveDirection *= speed;
    23.        
    24.         if (Input.GetButton ("Jump")) {
    25.             moveDirection.y = jumpSpeed;
    26.         }
    27.        
    28.     }
    29. if (Input.GetButtonDown ("Fire1")) {
    30.         if(HasInvertedGravity == 0){
    31.             if(inversiongravity == 0){
    32.                 HasInvertedGravity = 0;
    33.                 inversiongravity = 1;
    34.                 Physics.gravity = Vector3(0, -1.0, 0);
    35.                 Rotate = true;
    36.                 WantedRotation = transform.rotation;
    37.                 WantedRotation.eulerAngles = new Vector3(180, 0, 0);
    38.             }
    39.             else{
    40.                 HasInvertedGravity = 0;
    41.                 inversiongravity = 0;
    42.                 Physics.gravity = Vector3(0, 1.0, 0);
    43.                 Rotate = true;
    44.                 WantedRotation = transform.rotation;
    45.                 WantedRotation.eulerAngles = new Vector3(180, 0, 0);
    46.             }
    47.         }
    48.     }
    49.     // Apply gravity
    50.     if(inversiongravity == 0)
    51.     {
    52.         moveDirection.y -= gravity * Time.deltaTime;
    53.        
    54.        
    55.     }
    56.     else
    57.     {
    58.         moveDirection.y += gravity * Time.deltaTime;
    59.        
    60.     }
    61.     if(Rotate)
    62.     {
    63.         transform.rotation = Quaternion.Lerp(transform.rotation, WantedRotation, Time.deltaTime);
    64.  
    65.     }
    66.  
    67.    
    68.     // Move the controller
    69.     var controller : CharacterController = GetComponent(CharacterController);
    70.     var flags = controller.Move(moveDirection * Time.deltaTime);
    71.     grounded = (flags  CollisionFlags.CollidedBelow) != 0;
    72.    
    73. }
    74. @script RequireComponent(CharacterController)
    Unfortunately it's telling me it's missing a semicolon somewhere. Particularly for these two lines:

    Code (csharp):
    1. var boolean Rotate = false;
    2. Quaternion WantedRotation;
    Not entirely sure why I'm getting this error. Any help please?
     
  10. Maelstroms

    Maelstroms

    Joined:
    Nov 26, 2009
    Posts:
    11
    Just my 2 cents, wouldn't it be easier just to rotate the scene by 180 degrees instead of trying to re-simulate gravity on the player character ? It would mean that all the other objects in the scene would fall from the ground to the ceil, but that'd make sense if the gravity is really inverted :)
     
  11. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    I don't think it'd work quite like that; you're assuming that the level would rotate perfectly, but it would just dump the player in a different location rather than inverting gravity.

    Besides, people have managed to make gravity act like Super Mario Galaxy; I doubt that this is much harder.
     
  12. rocket5tim

    rocket5tim

    Joined:
    May 19, 2009
    Posts:
    242
    Change to:

    Code (csharp):
    1. var rotate : boolean = false;
    2. var wantedRotation : Quaternion;
    (I haven't tested this to see if it actually works, but it at least makes the errors go away :)
     
  13. SalemSage

    SalemSage

    Joined:
    Nov 23, 2009
    Posts:
    7
    Well, I don't get any errors now, but I still don't walk on the ceiling.

    Thank you for your attempts to make this code work, anyway.
     
  14. helioraanswer

    helioraanswer

    Joined:
    Apr 7, 2009
    Posts:
    412
    Hi,

    Slightly offtopic question, but how do you turn on Gravity of the rigidbody by javascript?
    I have airplanes that need to go down quick before getting destroyed. I know it's wrong down here :)

    Code (csharp):
    1.  
    2. function Kill () {
    3.         rigidbody.gravity= true;
    4.     // Destroy the projectile
    5.     Destroy(gameObject, 3);
    6. }
    7.  
     
  15. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    With a CharacterController, I don't think your solution will be workable. I couldn't get the CharacterController to actually flip due to lack of access to it's internals and I posted a week or so ago and it was deemed not possible.
     
  16. bode123

    bode123

    Joined:
    Nov 3, 2012
    Posts:
    3
    I'm yrying to do something similair, but can't really get it to work, can someone please confirm if this is doable with the standard character controller?
    I'm flipping the controller and inversing gravity but as stated above this isn't seen as isGrounded and is write only, am i missing something here? Does it matter how the rotation is done?

    For example i just use this
    player_01.transform.Rotate(Vector3.forward*-180);

    would greatly appreciate some help
     
  17. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    This is not doable with the standard CharacterController. Use a rigidbody and CapsuleCollider.

    HTH