Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by SebasSBM · Jul 28, 2016 at 01:28 PM · javascriptcamera followcollisionscamera rotation

How can I make cam move in a perfect circular orbit, but without diving into colliders?

I am making a moving-sphere game from scratch. For the camera, I decided to control it through transform instead of rigidbody. I've tried several kind of approaches, but using a circle function around the target has offered me the most accurate rotation and following.

Why I move cam through Transform rather than Rigidbody

The motive I decided to implement cam moving through transform is that no matter how fast the ball is moving, or which direction the cam is looking: operating cam coords directly makes sure the cam is always over a perfect circle around the sphere. Also, due to my approach sets the camera position relative to the target's position (the sphere) in every frame, this way the relative positions of both the camera and the sphere remain the same, independent from the sphere's rotation.

I just couldn't figure out how to achieve the same without overriding cam's physics (ALERT: I am assuming that any translation or rotation through transform overrides physics, but I am not sure if this assumption is completely true).

Other approaches I tried and failed (for the record)

  • Making cam a child of the sphere: epic fail, cam starts following the sphere rotation and I don't want that
  • The accepted answer in this question: It works, but it lacks accuracy. The orbit might be a perfect circle when the ball is not moving, but when moving(specially at high speeds), even through the cam follows the ball, the orbit gets unacceptable imperfections. || UPDATE: @whebert, who posted that answer, published in a comment an updated version of the snippet in his answer. I tried it and it works as fine as my approach, but it is based on transform as well.
  • My approach works well, but...

    ...it becomes a problem when avoiding that the cam goes through walls, though. I tried tracing rays out of the wall, but then the cam makes an annoying 'inside-out' alternative move.

    What can I do to prevent the cam going inside the walls, but at the same time mantaing this "perfect orbit" system? (for the record, I'd throw away my cam's beautiful code if necessary) In other words, i would like that the orbit's arc that is inside the wall, becomes a straight line stuck to the wall's collider. Any tips for a better approach are welcome.

    orbit_example

    The code

    Here is the script(in Javascript) I attached to the cam, for the sake of completeness:

     #pragma strict
     public var target : Transform;
     public var height: float = 1.0f;
     public var cam_distance: float = 4.0f;
     public var orbit_speed: float = 40.0f;
    
     private var trans: Transform;
     
     function Start () {
         trans = GetComponent.<Transform>();
     }
     
     function Update () {
         //Initial positioning: later we change it depending on cam's angle
         trans.position.x = target.position.x;
         trans.position.y = target.position.y + height;
         trans.position.z = target.position.z - cam_distance;
    
         //Rotate the cam with A and D keys
         if(Input.GetKey(KeyCode.A)) {
             trans.Rotate(Vector3.up, -orbit_speed * Time.deltaTime);
         }
         if(Input.GetKey(KeyCode.D)) {
             trans.Rotate(Vector3.up, orbit_speed * Time.deltaTime);
         }
         
         var base_angle : float;
         // First, we get the cam's angle and turn it 180º
         base_angle = (trans.rotation.eulerAngles.y + 180) % 360;
    
         // Using trigonometry to translate the cam to the corresponding orbit position depending on angle
         trans.position.x = cam_distance * Mathf.Sin(base_angle * Mathf.Deg2Rad) + target.position.x;
         trans.position.z = cam_distance * Mathf.Cos(base_angle * Mathf.Deg2Rad) + target.position.z;
     }
    
    
    orbit-example.png (10.2 kB)
    Comment
    Add comment
    10 |3000 characters needed characters left characters exceeded
    ▼
    • Viewable by all users
    • Viewable by moderators
    • Viewable by moderators and the original poster
    • Advanced visibility
    Viewable by all users

    2 Replies

    · Add your reply
    • Sort: 
    avatar image
    1
    Best Answer

    Answer by Glurth · Jul 31, 2016 at 04:48 PM

    Once you have computed the "DESIRED" position of the camera (your existing code), you can use a physics.RAYCAST from the center of rotation to the desired camera position (sounds like you did the opposite), to determine if a wall is in the way. If so, simply decrease your (cam_distance) distance to the center, to the value provided by the raycast result. (let me know if you need more details on that last part which can be done with vector subtraction, Vector magnitude and Vector scaling).

    https://docs.unity3d.com/ScriptReference/Physics.Raycast.html (scroll down to see version with hit-point as an out parameter)

    Edit: Collider.Raycast is useful to detect collisions with a particular object, as opposed to the first collider hit by the ray (like Physics.Raycast). https://docs.unity3d.com/ScriptReference/Collider.Raycast.html

    Comment
    Add comment · Show 6 · Share
    10 |3000 characters needed characters left characters exceeded
    ▼
    • Viewable by all users
    • Viewable by moderators
    • Viewable by moderators and the original poster
    • Advanced visibility
    Viewable by all users
    avatar image SebasSBM · Jul 31, 2016 at 08:52 PM 0
    Share

    So Rays can interact with collisions... I didn't realize before (I was using them just to move it towards the center, and depending on collision "cam vs wall"). Looks like your approach can avoid the "inside-out" issue if I implement it correctly. NOTE: I confused Ray with Raycast :P

    I have to use the args origin, direction and maxDistance on Physics.Raycast.

  • As you pointed, the origin should be the sphere position ins$$anonymous$$d of the cam's position.
  • I need a direction. I think I can obtain it lerping both sphere and cam's positions I confused what Vector3.Lerp actually does. I found in this question is as simple as substracting both coords. Amazing!
  • The maxDistance might be taken from camDistance itself, perhaps with a little tweak.
  • I will try to implement it this way and see what happens.

    PD: @Glurth, about the last part, I am imagining looping while Raycast returns true, decreasing distance in every iteration. Looks like a good idea, but you seem to have something different in $$anonymous$$d. The part I don't get is "decrease to the value provided by Raycast result". Raycast returns a boolean, so the only way I imagine doing it is with a while loop. If my guess is wrong, would you edit your answer with further information? Thank you.

    avatar image Glurth SebasSBM · Jul 31, 2016 at 09:39 PM 1
    Share

    The direction will simply be camera's position $$anonymous$$us the center position, normalized. (Normilizing a vector keeps the direction the same, but sets it's length to 1.0). https://docs.unity3d.com/ScriptReference/Vector3.Normalize.html

    The vector from the raycast hit point to the center (also a simple Vector subtraction), will have a length that you can get by using the Vector3.$$anonymous$$agnitude function. https://docs.unity3d.com/ScriptReference/Vector3-magnitude.html

    Since the normalized vector has a length of 1.0, you can simply $$anonymous$$ultipy that vector by the length or magnitude of the center-to-hitpoint vector. That will keep the direction the same, but change the length of the vector. https://docs.unity3d.com/ScriptReference/Vector3-operator_multiply.html

    Alternatively, you could just put the camera at the detected hit-point. ( Though this might need some adjustment (towards the center object) to make sure the camera does not see the wall.

    you are quite right about the physics raycast function, I suggest the wrong one! (yes having two does get confusing,) Collider.Raycast is what I should have suggested, though this DOES assume the wall has a collider component. https://docs.unity3d.com/ScriptReference/Collider.Raycast.html This function outputs a "hit-point" (via an OUT parameter), which is what I keep talking about... sorry bout that!

    avatar image SebasSBM Glurth · Jul 31, 2016 at 10:12 PM 0
    Share

    It's allright. All this information will keep me in the right way. Now I just must develope the implementation until it works. Thank you.

    BTW, the while loop idea was a bad call. Trying it I just achieved to freeze Unity. It looks quite dangerous. Do not try this at home hehe

    Show more comments
    avatar image
    0

    Answer by SebasSBM · Aug 01, 2016 at 05:53 AM

    I implemented @Glurth 's approach by the following snippet, and it worked perfectly.

    I made this fragment of code, and appended it to the script that I posted in the question:

     function LateUpdate() {
         // Correct radius if collider is in the way
         var direction : Vector3 = trans.position - target.position;
         direction.Normalize();
     
         var radius : float = cam_distance;
         var target_to_camera : Ray = new Ray(target.position, direction);
         var hit : RaycastHit;
    
         if (Physics.Raycast(target.position, direction, hit, cam_distance)) {
             // the sphere radius is 0.5f <- actually, this wasn't relevant at all
             // but you might need to tweak distance to avoid camera seeing the wall from behind
             radius = hit.distance - 0.5f;
             trans.position.x = radius * Mathf.Sin(base_angle * Mathf.Deg2Rad) + target.position.x;
             trans.position.z = radius * Mathf.Cos(base_angle * Mathf.Deg2Rad) + target.position.z;
         }
     }
    
    Comment
    Add comment · Share
    10 |3000 characters needed characters left characters exceeded
    ▼
    • Viewable by all users
    • Viewable by moderators
    • Viewable by moderators and the original poster
    • Advanced visibility
    Viewable by all users

    Your answer

    Hint: You can notify a user about this post by typing @username

    Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

    Follow this Question

    Answers Answers and Comments

    64 People are following this question.

    avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

    Related Questions

    How to rotate a camera around a spaceship freely 0 Answers

    Make camera follow and look at object at the same time 0 Answers

    Need help with camera in top down shooter 0 Answers

    Fixed Camera rotate to follow player 2 Answers

    Collision Problems 1 Answer


    Enterprise
    Social Q&A

    Social
    Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

    Footer

    • Purchase
      • Products
      • Subscription
      • Asset Store
      • Unity Gear
      • Resellers
    • Education
      • Students
      • Educators
      • Certification
      • Learn
      • Center of Excellence
    • Download
      • Unity
      • Beta Program
    • Unity Labs
      • Labs
      • Publications
    • Resources
      • Learn platform
      • Community
      • Documentation
      • Unity QA
      • FAQ
      • Services Status
      • Connect
    • About Unity
      • About Us
      • Blog
      • Events
      • Careers
      • Contact
      • Press
      • Partners
      • Affiliates
      • Security
    Copyright © 2020 Unity Technologies
    • Legal
    • Privacy Policy
    • Cookies
    • Do Not Sell My Personal Information
    • Cookies Settings
    "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
    • Anonymous
    • Sign in
    • Create
    • Ask a question
    • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Explore
    • Topics
    • Questions
    • Users
    • Badges