Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. The Burst compiler has its own forum section now.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Some physics help needed

Discussion in 'Scripting' started by mudloop, Nov 6, 2012.

  1. mudloop


    May 3, 2009

    I'm making a game where a character basically rotates around a vertical tube (and the camera stays behind him). Basically it's somewhat like the Amiga game Nebulus :

    I'm having some trouble constraining the player to a certain distance of the tube.

    I've tried several approaches :
    - Just rotate the tube instead of the character, and set x&z constraints to the player's rigidbody... This worked pretty well, but had a few shortcomings (the player could get stuck inside platforms, and it wouldn't work with multiple players/characters).
    - Use basic math to calculate the position of the player based on the desired rotation (you know, cos/sin stuff). This worked, but setting the position of the character manually would obviously cause problems with interpolation.

    So I'm guessing the proper way to do this is completely through physics, with joints and forces. I'm guessing a rigidbody in the center with a fixed (or hinge?) joint that connects it to the player, but I'm not entirely sure how to set it up. I've mostly been doing 2D games with Unity so I'm at a loss here. I've been at it for a while and am not getting the results I need.

    I'm open to other suggestions as well.

    Any advice here would be greatly appreciated. Thanks!
    Last edited: Nov 6, 2012
  2. Sock Puppet

    Sock Puppet

    Jan 9, 2011
    I did something very similar to this. I used sin, cos for positioning. You shouldn't have any problems with interpolation if you do it in Update(). I only needed a couple of ray casts for collision detection though. If you need anything more complicated you might run into problems but you can do a lot with just ray casts.

    Also getting a good feel with joints would be hard and joints tend to stretch at high speeds etc.
    Last edited: Nov 6, 2012
  3. renman3000


    Nov 7, 2011
    If the player is always at the same z position relative to the screen, you could just make a sprite with a lot of empty space. Place the spacecraft near the bottom (if 2D), then just rotate the sprite. The craft will appear to be sticking to the wall of the tube. Negates the use of physics.
  4. lordofduct


    Oct 3, 2011
    How do you control your characters?

    RigidBody - you'll need joints to clamp your player. Look into Configurable Joint:

    CharacterController - you can do it all in code...

    Either way what I'd do is as follows:

    Your world is a cylindrical world. So first things first is I'd have a custom transform component that wraps the existing transform that describes my values cylindrically.

    Cylindrical coordinates are a 3d planar projection of the radial coordinate direction. It's basically a position described on a right circular cylinder, just like your game:

    Your coordinates are:
    rho - radial distance from center of cylinder (in your case this will most often be constant)
    phi - angular distance of the origin plane that intersects the cylinder height wise (similar to theta in radial coordinates)
    z - height from the plane that intersects the plane side wise.

    you can call the coordinates whatever you want. Those are just the common characters used.

    Anyways, the conversion from cartesian to cylindrical is pretty simple.

    rho = sqrt(x^2 + y^2)
    phi = atan2(y, x)
    z = z

    note - this conversion is in the wiki. The phi one though looks like this:

    this is the actual algorithm for atan2

    Anyways, so now you have a new component that just remaps the coordinates. You can create define velocities based off this and the sort... instead of a velocity of 1 unit in the x or y, you might have a velocity of 1 unit in the phi or rho.

    Now if you use CharacterController you'd have another component, I usually call it the motor. This is what actually moves your player around (based on velocities and the sort). And instead of accessing 'this.transform', you'd access the cylinder transform component. Do all your movements around that.

    If you use RigidBody you'd have another component that requires the ConfigurableJoint. The ConfigurableJoint should constrain to a plane tangential to the surface of the cylinder at the point the RigidBody is at. The combined component you're writing on update event updates this plane based off the relative position to the cyldiner the RigidBody has moved to.

    Your basically doing the constraints the same way you would in a 2d planar world. Just instead of using the 'transform' object, you use your custom 'cylinder transform' object.
  5. mudloop


    May 3, 2009
    Thanks for the replies. I will evaluate them all. I do need some physics in there, so ideally everything is done through physics, but I will definitely consider alternatives like using raycasts if possible.
  6. ippdev


    Feb 7, 2010
    If I get your gist you want to walk around a cylinder and have the surface normal be your local up. It can be done simply without too much trig..which makes my head hurt sometimes.. Place an empty game object in the cylinder as a registration point for your local gravity. Say the cylinder is extruded along the z axis then the empty gameObject would be constrained to the cylinder's x and y axis with the z axis derived from the players position. Then place a LookAt script on the empty gameObject and have the -z axis of that object be the local gravity for your player. You would apply a relative force then on your character's rigidbody Y axis downwards...towards the empty gameObject..

  7. Bunzaga


    Jan 9, 2009
    I made a demo, but I'm using the beta version, so... :( I guess that was a bad idea LOL.

    Here's the code I put in the 'character', maybe it will help you get to your solution.

    Code (csharp):
    2. #pragma strict
    3. public var rb:Rigidbody;
    4. public var gravity:float = 30;
    5. public var jumpHeight:float = 2.0;
    6. public var maxSlope:float = 45;
    7. public var speed:float = 50;
    8. public var posOffset:Vector3 = Vector3(10, 0, 0); // offset from center...
    9. private var eulerY:float = 0.0; // this changes as the user rotates around the tower...
    10. private var jump:boolean = false;
    11. private var grounded:boolean = false;
    13. function Update(){
    14.     if(Input.GetAxis("Horizontal")){
    15.         eulerY-=Input.GetAxis("Horizontal")*Time.deltaTime*speed;
    16.     }
    17.     if(Input.GetButton("Jump")){
    18.         jump = true;
    19.     }
    21. }
    23. function FixedUpdate(){
    24.     posOffset.y = rb.position.y;
    25.     rb.MovePosition(Quaternion.Euler(0, eulerY, 0) * posOffset);
    26.     rb.rotation = Quaternion.LookRotation(Vector3(0,posOffset.y,0)-transform.position);
    27.     if(grounded){
    28.         if(jump){
    29.             rb.AddForce(Vector3.up*Mathf.Sqrt(2*gravity*jumpHe  ight), ForceMode.Impulse);
    30.         }
    31.     }
    32.     rb.AddForce(Vector3.up * (-gravity), ForceMode.Acceleration);
    34.     jump = false;
    35.     grounded = false;
    36. }
    38. function OnCollisionStay(col:Collision){
    39.     for(var contact:ContactPoint in col.contacts){
    40.         if(Vector3.Angle(contact.normal, Vector3.up) < maxSlope){
    41.             grounded = true;
    42.         }
    43.         else{
    44.             // on slope
    45.         }
    46.     }
    47. }
    I created an empty game object, which this code is attached to. I then added a rigid body to drag and drop into the proper slot.

    So basically, this object rotates around the cylinder, and always faces it. The 'real' character/mesh is a child of this game object, and is rotated 90 degrees, so it is facing like your image.

    The main thing for you to look at, are these three lines:
    Code (csharp):
    2. // this is to adjust for the current height of the character
    3. posOffset.y = rb.position.y;
    4. // we rotate around the center, with our positional offset
    5. rb.MovePosition(Quaternion.Euler(0, eulerY, 0) * posOffset);
    6. // we look at the center from our current position (add +90 or -90 here if you don't want to child the mesh)
    7. rb.rotation = Quaternion.LookRotation(Vector3(0,posOffset.y,0)-transform.position);
    Good luck man, even this little demo I made was pretty fun, I hope to be able to try out your game when it's done :D
    Last edited: Nov 8, 2012