Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Camera smooth zoom over set time (Project Included)

Discussion in 'Scripting' started by frogwise, Jan 3, 2010.

  1. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    UPDATE: JAN 7 2010
    PROJECT LINK : http://www.bartekn.com/SolarSystem/Solar_System.html

    Hey all,

    I am looking for a way to script two similar effects into my project. I'm just learning JavaScript so my knowledge is still limited.

    I have a player controlled camera that can "swap" look at targets to view different planets. The way it works now is that by hitting 1,2,3 or 4 keys, the camera simply snaps to the corresponding planet instantly. What I'm hoping to do are two things :

    - smoothly transition between planets based on a time variable
    - allow for mouse wheel zooming on selected planets between a
    min and max range. I already have rotation around the planet working.

    Any help would be much appreciated.

    Cheers!
     
  2. fallingbrickwork

    fallingbrickwork

    Joined:
    Mar 16, 2009
    Posts:
    1,072
    Have a look at Mathf.Lerp...

    you can blend between 2 positions (or any two amounts) using that... ie...

    Code (csharp):
    1.  
    2. //Calculate Current Position
    3.  
    4. // At the start of the update loop grab the current (last frame camera position)
    5. PositionOld = PositionCurrent;
    6.  
    7. //Calculate New Lerp'd Position
    8. PositionCurrent = Mathf.Lerp(PositionOld, PositionNew, Time.deltaTime * SpeedAdjuster);
    9.  
    you can then use the PositionCurrent to create your position vector.

    The SpeedAdjuster is there so you can alter how long it takes for the transition to take place. the PositionNew can be the new planet position.

    Please note the above is off the top of my head but it should work (it's c# but should get you heading in the right direction for Javascript). All the variables are floats in this case so you may have to Lerp the X,Y and Z portions of the Vector3 and then feed those back into a Vector3 for your new position (i'm not sure what you are doing).

    I hope that helps a bit, i'm not the best at explaining.

    Kind Regards,
    Matt.
     
  3. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Or just use Vector3.Lerp.

    Actually it is Javascript just the way it is. :)

    In addition to the above, I'd suggest looking into coroutines if you haven't already (i.e., don't try to do this in Update).

    --Eric
     
  4. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Thanks fellas. I finally have some time today to sit down and try your suggestions.

    Here is a rough version of my project. Use the 1,2,3,4 keys to switch planets.

    http://www.bartekn.com/SolarSystem/SSB.html

    I'm going to have to look into coroutines I guess. :)

    I'll keep you updated.
     
  5. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Hmm, cant seem to figure this out with my limited scripting knowledge.

    I am trying to wrap my head around how to set the new newPlanet position while remembering the oldPlanet position.

    Here is how I am trying to do it:

    Code (csharp):
    1.  
    2. var defaultDistance = 0;
    3. var smooth = 5.0;
    4. var oldPlanet : transform;
    5. var newPlanet : transform;
    6.  
    7.  
    8. function Update () {
    9.  
    10.     if (Input.GetButtonDown("Sun"))
    11.     {
    12.         defaultDistance = 50;
    13.        
    14.         //Calculate New Lerp'd Position
    15.         newPlanet = GameObject.Find("MESH_sun").transform;
    16.         newPlanet = Vector3.Lerp(oldPlanet, newPlanet, Time.deltaTime * smooth);
    17.         GetComponent(Main_Camera).target = newPlanet;
    18.         GetComponent(Main_Camera).distance = defaultDistance;
    19.     }
    20.     if (Input.GetButtonDown("Mercury"))
    21.     {
    22.         defaultDistance = 8;
    23.        
    24.         //Calculate New Lerp'd Position
    25.         newPlanet = GameObject.Find("MESH_planet_mercury").transform;
    26.         newPlanet = Vector3.Lerp(oldPlanet, newPlanet, Time.deltaTime * smooth);
    27.         GetComponent(Main_Camera).target = newPlanet;
    28.         GetComponent(Main_Camera).distance = defaultDistance;
    29.     }
    30. }
    31.  
    I know I am trying to do this in the Update function even though I was told I shouldnt, but right now im just trying to wrap my head around the logic before I try the co-routine method.

    Any ideas?
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    The problem is that Update runs every frame no matter what. It's just not good for scripting a series of events.

    I do have a script I wrote for a project which does something similar to what you're attempting. First of all, though, you can't lerp between transforms themselves. Transforms are, well, just things, not numbers. ;) But transforms contain a position, and you can lerp between two positions.

    I'm assuming your "Main_Camera" script is a modified SmoothFollow script. So, remove this line from your Main_Camera script:

    Code (csharp):
    1. var target : Transform;
    And replace it with this:

    Code (csharp):
    1. var targetPosition : Vector3;
    In the rest of the script, replace all instances of "target.position" with "targetPosition". Also remove this section:

    Code (csharp):
    1.     // Early out if we don't have a target
    2.     if (!target)
    3.         return;
    Because now the script will have a reference to the target's position, rather than the target itself, and the position will be set by the other script (see below). I've modified the script I had somewhat and added comments, which hopefully explain things:

    Code (csharp):
    1. // Drag your planets into this array
    2. var planets : Transform[];
    3. // Add entries in the same order as the planets array above
    4. var buttons = ["Sun", "Mercury", "Venus", "Earth"];
    5. // All three arrays should have the same number of entries
    6. // And remember that values exposed in the inspector override values in this script
    7. var defaultDistance = [50.0, 8.0, 10.0, 20.0];
    8. // The index of the planet to start with
    9. var currentPlanet = 0;
    10. // Time it takes to move from one planet to another, in seconds
    11. var cameraMoveTime = 1.0;
    12.  
    13. function Start () {
    14.     // Store script reference, because it's faster and easier
    15.     var camScript = GetComponent(Main_Camera);
    16.     // Set up initial position
    17.     camScript.targetPosition = planets[currentPlanet].position;
    18.     camScript.distance = defaultDistance[currentPlanet];
    19.     // Do this forever
    20.     while (true) {
    21.         // Wait for input indefinitely
    22.         var inputReceived = false;
    23.         while (!inputReceived) {
    24.             // Check all available buttons from the buttons array
    25.             for (thisButton = 0; thisButton < buttons.Length; thisButton++) {
    26.                 if (Input.GetButtonDown(buttons[thisButton])) {
    27.                     // Don't do anything if we're already at the desired planet
    28.                     if (thisButton == currentPlanet) continue;
    29.                     // Otherwise stop the loop; "thisButton" will be the index of the desired planet
    30.                     inputReceived = true;
    31.                     break;
    32.                 }
    33.             }
    34.             // If there was no input, wait a frame then loop back to checking input again
    35.             yield;
    36.         }
    37.         // Lerp from current position to new position over time
    38.         var timeFactor = 1.0 / cameraMoveTime;
    39.         for (i = 0.0; i < 1.0; i += Time.deltaTime * timeFactor) {
    40.             // Make an index for ease in/ease out instead of the plain linear motion of "i"
    41.             var t = Mathf.SmoothStep(0.0, 1.0, i);
    42.             camScript.targetPosition = Vector3.Lerp(planets[currentPlanet].position, planets[thisButton].position, t);
    43.             camScript.distance = Mathf.Lerp(defaultDistance[currentPlanet], defaultDistance[thisButton], t);
    44.             yield;
    45.         }
    46.         currentPlanet = thisButton;
    47.         // Now the loop is done so we'll go back to waiting for input again
    48.     }
    49. }
    --Eric
     
  7. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Wow thanks Eric, this helped so much. I'm actually using a modified "Mouse Orbit" script, but was able to fix it to work like you said. The script works as I hoped but there are a few issues -

    • 1. My solar system has the planets animated in orbit around the sun. What the script seems to do is fly to the new planet location but not actually continue to follow the planet. It will get to its destination and as soon as its there, stays there, while the planet continues to go about its orbit. Although now that I think of it, maybe it has something to do with the fact that I'm using a modified Mouse Orbit script rather than a Smooth Follow. I will try the Smooth Follow example.

      2. There seems to be a bit of a stutter as the camera travels to the new planet. I think this may also have something to do with the fact that the planets are always orbiting, so the position of the planet is changing each frame. Is there any way around this?

    Thanks for the immense help :) I will post an updated version of the project for you to view in a few minutes.
     
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Hmm, yeah, having a moving target would make things kind of different; hadn't really thought about that. One way I can think of offhand, though I haven't tested it...in the loop where it's waiting for input, add a line before the yield like this:

    Code (csharp):
    1.             // Keep updating the target position with the current planet's position
    2.             camScript.targetPosition = planets[currentPlanet].position;
    3.             // If there was no input, wait a frame then loop back to checking input again
    4.             yield;
    Since you mentioned the mouse orbit script, I expect there would also have to be a similar modification to deal with the fact that "camScript.distance" isn't necessarily the distance that the camera should actually at be at, since the user would be able to zoom in and out. Right now it just uses the same default distance all the time, which may account for that stutter you mentioned.

    --Eric
     
  9. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Brilliant, that did the trick. They travel smoothly now, and keep following the planet.

    However I have one more issue - the camera likes to "reset" itself to point at something in the scene when you let go of the mouse button while rotating. This looks awkward. Here is the latest project file to show you what I mean.

    http://www.bartekn.com/SolarSystem/Solar_System.html

    SWITCH PLANETS : 1,2,3,4
    ROTATE by holding Left Mouse Button



    I think this problem has to do with something that I noticed while I was updating the scripts you mentioned. Here is the new Main_Camera_Smooth_Follow script:

    Code (csharp):
    1. // The target we are following
    2. var targetPosition : Vector3;
    3. var target : Transform;
    4.  
    5. // The distance in the x-z plane to the target
    6. var distance = 10.0;
    7. // the height we want the camera to be above the target
    8. var height = 5.0;
    9. // How much we
    10. var heightDamping = 2.0;
    11. var rotationDamping = 3.0;
    12.  
    13. //
    14. var xSpeed = 250.0;
    15. var ySpeed = 120.0;
    16.  
    17. var yMinLimit = -20;
    18. var yMaxLimit = 80;
    19.  
    20. private var x = 0.0;
    21. private var y = 0.0;
    22. //
    23.  
    24. function Start () {
    25.  
    26.     var angles = transform.eulerAngles;
    27.     x = angles.y;
    28.     y = angles.x;
    29.  
    30.     // Make the rigid body not change rotation
    31.     if (rigidbody)
    32.         rigidbody.freezeRotation = true;
    33. }
    34.  
    35. function LateUpdate () {
    36.  
    37. if(Input.GetMouseButton(0))  
    38. {
    39.         x += Input.GetAxis("Mouse X") * xSpeed * 0.03;
    40.         y -= Input.GetAxis("Mouse Y") * ySpeed * 0.03;
    41.        
    42.         y = ClampAngle(y, yMinLimit, yMaxLimit);
    43.                
    44.         var rotation = Quaternion.Euler(y, x, 0);
    45.         var position = rotation * Vector3(0.0, 0.0, -distance) + targetPosition;
    46.        
    47.         transform.rotation = rotation;
    48.         transform.position = position;
    49. }
    50.  
    51.     // Calculate the current rotation angles
    52.     wantedRotationAngle = target.eulerAngles.y;
    53.     wantedHeight = targetPosition.y + height;
    54.        
    55.     currentRotationAngle = target.eulerAngles.y;
    56.     currentHeight = transform.position.y;
    57.    
    58.     // Damp the rotation around the y-axis
    59.     currentRotationAngle = Mathf.LerpAngle (currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
    60.  
    61.     // Damp the height
    62.     currentHeight = Mathf.Lerp (currentHeight, wantedHeight, heightDamping * Time.deltaTime);
    63.  
    64.     // Convert the angle into a rotation
    65.     currentRotation = Quaternion.Euler (0, currentRotationAngle, 0);
    66.    
    67.     // Set the position of the camera on the x-z plane to:
    68.     // distance meters behind the target
    69.     transform.position = targetPosition;
    70.     transform.position -= currentRotation * Vector3.forward * distance;
    71.  
    72.     // Set the height of the camera
    73.     transform.position.y = currentHeight;
    74.    
    75.     // Always look at the target
    76.     transform.LookAt (targetPosition);
    77. }
    78.  
    79. static function ClampAngle (angle : float, min : float, max : float) {
    80.     if (angle < -360)
    81.         angle += 360;
    82.     if (angle > 360)
    83.         angle -= 360;
    84.     return Mathf.Clamp (angle, min, max);
    85. }
    If you notice, I had to keep var target : Transform; in the mix because there is a line later in the code that says:

    Code (csharp):
    1.  
    2.     // Calculate the current rotation angles
    3.     wantedRotationAngle = TARGET.eulerAngles.y;
    4.     wantedHeight = targetPosition.y + height;
    5.  
    I tried replacing the target there with the new targetPosition instead but it gives me an error saying

    Assets/Scripts/Main_Camera_Smooth_Follow.js(63,46): BCE0019: 'eulerAngles' is not a member of 'UnityEngine.Vector3'.

    Because of that, I had to keep "target" and therefor it is constantly asking me to give it one, so whatever I give it (one of the game objects in the scene) it seems to always want to reset itself to look at it.




    On another note... I really appreciate all this help. I am just learning scripting, and although I have signed up for an intro to Java and object oriented programming class, it doesn't start until Spring. Are there any good books you recommend in the mean time that I could use as a resource for some of this stuff? Where I get caught up is what purpose different types of functions have (Update, LastUpdate, etc) as well as some of the math that is going on.

    Anyways, thanks again. :)
     
  10. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Yeah, the problem is that there's stuff in the Main_Camera script which is overriding other stuff, and some redundancy, so it doesn't quite work for this sort of thing. I'd get rid of most of that code in LateUpdate, and replace it with this:

    Code (csharp):
    1. var mouseSpeed = .03;
    2.  
    3. function LateUpdate () {   
    4.     if (Input.GetMouseButton(0)) {
    5.         x += Input.GetAxis("Mouse X") * xSpeed * mouseSpeed;
    6.         y -= Input.GetAxis("Mouse Y") * ySpeed * mouseSpeed;
    7.         y = ClampAngle(y, yMinLimit, yMaxLimit);
    8.     }
    9.  
    10.     var rotation = Quaternion.Euler(y, x, 0.0);
    11.     transform.rotation = rotation;
    12.     transform.position = rotation * Vector3.forward * -distance + targetPosition;
    13. }
    Then you can get rid of the "target" variable once and for all. ;) Depending on what you want exactly, maybe you want to add the height stuff back in at least. Also you probably want to put this:

    Code (csharp):
    1.     transform.LookAt (targetPosition);
    as the very first line in the Start function. If you do that, however, then you have an issue where the Start function in this script and the Start function in the other script execute in an undefined order. So I'd set things up in Awake in the other script instead, like this:

    Code (csharp):
    1. private var camScript : Main_Camera;
    2.  
    3. function Awake () {
    4.     // Store reference to script, because it's faster and easier
    5.     camScript = GetComponent(Main_Camera);
    6.     camScript.distance = defaultDistance[currentPlanet];
    7. }
    8.  
    9. function Start () {
    10.     // Do this forever
    11.     while (true) {....rest of script here
    Then the order of script execution will always be correct.

    I haven't read any programming books in years, so I can't really give any recommendations, I'm afraid. There is a Unity-specific book that came out quite recently; can't remember the name at the moment, but there are a few topics about it if you do a search. Basically I get programming info off the web, plus the Unity docs, some of which I've read several times (and they have good info on Update and so on). Also I'd suggest doing some Unity tutorials if you haven't already. Then just practice a lot, because there's no way you can understand it all at once. The stuff I wrote early on is pretty lousy compared to what I can do now, which is probably pretty lousy compared to what I'll do in a few more years (I hope...).

    --Eric
     
  11. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Again thanks so much for the help. Ive spent most of my time just experimenting with your code and trying to understand what is happening. I find it a great learning resource.

    The project is updated again so check it out here:

    http://www.bartekn.com/SolarSystem/Solar_System.html

    I've been trying to figure out a way to lerp smoothly between distances as I use the mouse wheel to zoom in and out. From my research, it seems that mouse wheel "clicks" are hard to make smooth. What I wouldnt mind doing instead is, having 3 zoomable distances that I could zoom between:

    defaultDistance - > the closest zoom available.
    medium zoom - > 25% zoomed out from planet.
    far zoom - > 50% zoomed out from planet.

    I should be able to easily lerp between those three distances based on mouse wheel input. This is how I initially tried doing it (betweeon only 2 distances, min and max) :

    Code (csharp):
    1. if (Input.GetAxis("Mouse ScrollWheel") != 0) {
    2.            
    3.             minDistance = distance;
    4.             maxDistance = 50.0;
    5.    
    6.             Debug.Log(Input.GetAxis("Mouse ScrollWheel"));
    7.             distance -= Input.GetAxis("Mouse ScrollWheel") * mouseWheelDistanceRate;
    8.             distance = Mathf.Clamp(distance, minDistance, maxDistance);
    9.             distance = Mathf.Lerp(minDistance, maxDistance, Time.deltaTime * 100.0);
    10.     }
    11.  
    The problem I am finding is that "distance" is being constantly updated to where I have zoomed out to already, rather than the original "defaultDistance" I am setting for each planet. Therefore, I can only zoom out and not in, because the "minDistance" is always being updated to where I currently am.

    I'm going to find that Unity book you mentioned. I have to stop asking about every little issue I encounter. A little embarrassing. :(
     
  12. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Oh by the way, regarding distance:

    There is a bug with the code that makes the camera always reset itself quickly to the "defaultDistance" provided initially, regardless of where I have zoomed out to. For example, if I view Mercury and zoom out from the default distance, and then try to transition to a new planet location, the camera quickly snaps back to the defaulPosition before it travels.

    I think if I can solve that, I might be able to solve the smooth lerp zooming challenge, too.
     
  13. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Well, that depends on the mouse you're using...gaming mice intentionally make the wheel go in chunks for weapon-switching and whatnot, whereas other mice have smooth wheels. In this case I don't think it really matters much, because you'd lerp the zooming regardless.

    I'd suggest using a variable called "userDistance" or something, which holds the amount of zooming the user does, and add that to the usual default distance.

    --Eric
     
  14. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Would it make sense for me to merge both SwitchPlanets and Main_camera scripts into one? I'm having trouble talking from one to the other, such as sampling those original defualt distances.
     
  15. dpentecost

    dpentecost

    Joined:
    Apr 3, 2009
    Posts:
    136
  16. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    I think it's better for design if you keep components as separated as possible; monolithic scripts tend to get messy pretty quickly. You can just use GetComponent, and make sure you're not trying to access private variables.

    --Eric
     
  17. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Its great to see Unity being used for experimental stuff like this :D Looks fantastic.
     
  18. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    I've managed to get a clamp on the mouse wheel zooming implement I wanted, but I can't for the life of me figure out how to lerp it smoothly! The functionality should be :

    Anytime you mouse wheel scroll "up", camera zooms in from max distance to min distance smoothly. Anytime you mouse scroll wheel "down", the camera zooms from min distant to max distance. Here is the code:

    Code (csharp):
    1.  
    2. // The target we are following
    3. var targetPosition : Vector3;
    4. var distance = 10.0;
    5.  
    6. var mouseWheelDistanceRate = 5.0;
    7. var planetDistance = 0.0;
    8. var minZoomDistance = planetDistance;
    9. var maxZoomDistance = planetDistance * 2.5;
    10. var zoomSpeed = 0.5;
    11.  
    12. function LateUpdate () {
    13.  
    14. minZoomDistance = planetDistance;
    15. maxZoomDistance = planetDistance * 2.5;
    16.    
    17.    
    18.    
    19.    
    20.     if (Input.GetAxis("Mouse ScrollWheel") < 0) {
    21.            
    22.            
    23.            
    24. newDistance = Mathf.Lerp(minZoomDistance, maxZoomDistance, 2.0 * Time.deltaTime);
    25. Debug.Log("Current Distance = " + distance);
    26. Debug.Log("Default Planet Distance = " + planetDistance);
    27. Debug.Log("minDistance = " + minZoomDistance);
    28. Debug.Log("maxDistance = " + maxZoomDistance);
    29.            
    30. distance = newDistance;
    31.            
    32.     }
    33.    
    34.     if (Input.GetAxis("Mouse ScrollWheel") > 0) {
    35.            
    36.            
    37. newDistance = Mathf.Lerp(maxZoomDistance, minZoomDistance, 2.0 * Time.deltaTime);
    38.  
    39. Debug.Log("Current Distance = " + distance);
    40. Debug.Log("Default Planet Distance = " + planetDistance);
    41. Debug.Log("minDistance = " + minZoomDistance);
    42. Debug.Log("maxDistance = " + maxZoomDistance);
    43.            
    44. distance = newDistance;
    45.            
    46. }
    47.    
    48. var rotation = Quaternion.Euler(y, x, 0.0);
    49. transform.rotation = rotation;
    50. transform.position = rotation *
    51. Vector3.forward * -distance + targetPosition;
    52.    
    53. }
    54.  
    Im grabbing "planetDistance" from another script that has an array of planet distances. Whatever the current planetDistance is, make that the minimum you can ever zoom, and the maximum will be that * 2.5.

    Any ideas as to what I'm doing wrong? It seems straight forward to me but I cant smooth it out!
     
  19. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Remember that Update runs every frame..."if (Input.GetAxis("Mouse ScrollWheel") < 0)", for example, will only run when it's actually true, which may just be for a few frames.

    --Eric
     
  20. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Hmm. Shouldn't it still work though? It's only checking for the input every frame, but then I tell it to move over a specified time.
     
  21. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    No, because this:

    Code (csharp):
    1. newDistance = Mathf.Lerp(minZoomDistance, maxZoomDistance, 2.0 * Time.deltaTime);
    only runs when input occurs. Also, the third parameter in Lerp is a number between 0 and 1. If the number is 0, the function returns the first parameter, and if it's 1, it returns the second parameter. Since Time.deltaTime is probably going to be a very small number, it will always return something pretty close to minZoomDistance.

    --Eric
     
  22. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    I think you're suggesting I look into while and for loops :)
     
  23. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    I'm away from my PC right now, but would it be something like this?

    Code (csharp):
    1.  
    2. var zooming = true;
    3.  
    4. while (zooming) {
    5.  
    6.    if (Input.GetAxis("Mouse ScrollWheel") < 0) {  
    7.          
    8.       newDistance = Mathf.Lerp(minZoomDistance, maxZoomDistance, Time.time);
    9.          
    10.       distance = newDistance;
    11.          
    12.    }
    13.  
    14. if (newDistance = maxZoomDistance) zooming == false;
    15.  
    16. }
    17.  
    I believe I tried something similar to this the other night and it would freeze the game in an endless loop :(
     
  24. Theformand

    Theformand

    Joined:
    Jan 2, 2010
    Posts:
    269
    Very nice work there. It looks really really good. I didnt read the entire thread, but is this for educational purposes? Im very interested in projects employing Unity as a teaching tool, which is why im asking :)

    Also, the Skybox, while a bit crude in the seams, looks really nice. Did you just photoshop 6 large textures yourself and slapped them in there?
     
  25. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Yeah the skybox I created from a large image painted on the skybox "cross" template. I then seperated the 6 sides and stitched them together using ATI's CubeMapGen. The reason there are seems at the edges is because unity refers to "front/side" and "left/right" differently than CubeMapGen. I think its just a matter of flipping some of the textures and they should line back up. Just haven't got around to it ;)

    For now, this project is my learning project, but I do hope to use what I learn to develop some interactive web-based educational games. Before I get there, I need to get fluent in scripting! :D
     
  26. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Not quite; check out the for loop in the Start function of the script I posted on the previous page. Indeed Unity will freeze if you have an infinite loop with no yield in it.

    --Eric
     
  27. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    I've spent a while trying to wrap my head around creating this for loop based on your original one. I think I may be over complicating it, but, maybe not.

    Code (csharp):
    1.  
    2. minZoomDistance = planetDistance;
    3. maxZoomDistance = planetDistance * 2.5;
    4.    
    5. // Do this forever
    6. while (true) {
    7.    
    8. // Wait for input indefinitely
    9. var inputReceived = false;
    10.        
    11.    while (!inputReceived) {
    12.        
    13.    // Check to see what the distance is and stop if  
    14.    it reaches maxZoomDistance
    15.    for (distanceCheck = 0; distanceCheck <= maxZoomDistance; distanceCheck++) {
    16.            
    17.    if (Input.GetAxis("Mouse ScrollWheel") < 0) {
    18.                    
    19.    Debug.Log("ZOOMING OUT. DistanceCheck = " +
    20.    distanceCheck);
    21.                    
    22.    // Don't do anything if we're already zoomed out.
    23.    if (distanceCheck == maxZoomDistance) continue;
    24.                    
    25.    // Otherwise stop the loop.
    26.                     inputReceived = true;
    27.                    
    28. break;
    29. }
    30. }
    31.  
    32. // Keep updating the target position with the current planet's position
    33. distanceCheck = distance;
    34.  
    35. // If there was no input, wait a frame then loop back to checking input again
    36. yield;
    37. }
    38.  
    39. // Lerp from current position to new position over time
    40. var timeFactor = 1.0 / zoomSpeed;
    41. for (i = 0.0; i < 1.0; i += Time.deltaTime * timeFactor) {
    42.  
    43. // Make an index for ease in/ease out instead of the plain linear motion of "i"
    44. var t = Mathf.SmoothStep(0.0, 1.0, i);
    45. newDistance = Mathf.Lerp(minZoomDistance, maxZoomDistance, t);
    46. Debug.Log("newDistance =" + newDistance);
    47. yield;
    48. }
    49. distance = newDistance;
    50. // Now the loop is done so we'll go back to waiting for input again
    51.    }
    52.  
    I understand exactly what I need to do, but I just cant "write it" in code. I need to :

    (User always starts on a planet in its "minZoomDistance")

    Listen for input from mouse wheel.
    If user scrolls back on mouse wheel >
    Keep zooming out from minZoomDistance to maxZoomDistance and dont stop until maxZoomDistance is reached.

    If user scrolls forward on mouse wheel >
    Keep zooming in from maxZoomDistance to minZoomDistance and dont stop until maxZoomDistance is reached.

    I guess my biggest confusion is regarding what I should be checking for in those brackets of the for loop. Right now im creating a temp variable called distanceCheck, and am trying to iterate it by 1 and stop when it reaches maxZoomDistance. It keeps returning the initial value I set, 0 :(

    I'm doing this in the Start function because I found out you can't use coroutines in any Update function.
     
  28. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    I made a couple of additions, and broke out the routines into their own functions, so that should make what's going on more clear.

    The main changes are adding a check for the mouse wheel in the input loop, and the zooming routine. I also had to change the loop for moving from one planet to another slightly, to make the end position come out exactly right (by using "while" instead of "for")...that's because the zoom code checks the actual distance, so "close enough" doesn't work. Also it has to account for the fact that you might change planets while either zoomed or not zoomed, hence the addition of "startDistance".

    Code (csharp):
    1. // Drag your planets into this array
    2. var planets : Transform[];
    3. // Add entries in the same order as the planets array above
    4. var buttons = ["Sun", "Mercury", "Venus", "Earth"];
    5. // All three arrays should have the same number of entries
    6. var defaultDistance = [5.0, 8.0, 10.0, 20.0];
    7. // The index of the planet to start with
    8. var currentPlanet = 0;
    9. // Time it takes to move from one planet to another, in seconds
    10. var cameraMoveTime = 1.0;
    11. // Number of times the far zoom is compared to standard zoom
    12. var maxZoom = 2.5;
    13. // How fast zooming is
    14. var zoomSpeed = 2.0;
    15.  
    16. private var camScript : Main_Camera;
    17.  
    18. function Awake () {
    19.     // Store reference to script, because it's faster and easier
    20.     camScript = GetComponent(Main_Camera);
    21.     camScript.distance = defaultDistance[currentPlanet];
    22. }
    23.  
    24. function Start () {
    25.     // Do this forever
    26.     while (true) {
    27.         // Wait for input indefinitely
    28.         var buttonPressed = -1;
    29.         while (buttonPressed == -1) {
    30.             var scrollWheel = Input.GetAxis("Mouse ScrollWheel");
    31.             if (scrollWheel != 0.0) {
    32.                 yield CheckWheelDirection(scrollWheel);
    33.             }
    34.             buttonPressed = CheckInput();
    35.             // Keep updating the target position with the current planet's position
    36.             camScript.targetPosition = planets[currentPlanet].position;
    37.             // If there was no input, wait a frame then loop back to checking input again
    38.             yield;
    39.         }
    40.         // There was input, so lerp to the indicated planet
    41.         yield MoveToPlanet(buttonPressed);
    42.         // Now the loop is done so we'll just go back to waiting for input again
    43.     }
    44. }
    45.  
    46. function CheckInput () : int {
    47.     // Cycle through all available buttons
    48.     for (thisButton = 0; thisButton < buttons.Length; thisButton++) {
    49.         if (Input.GetButtonDown(buttons[thisButton])) {
    50.             // Don't do anything if we're already at the desired planet
    51.             if (thisButton == currentPlanet) continue;
    52.             // Otherwise stop the loop and return the index of the desired planet
    53.             return thisButton;
    54.         }
    55.     }
    56.     return -1;
    57. }
    58.  
    59. // Check scroll wheel, and zoom in/out as appropriate
    60. function CheckWheelDirection (scrollWheel) {
    61.     // This variable isn't really necessary, but it makes the code easier to read
    62.     var defaultDist = defaultDistance[currentPlanet];
    63.     if (scrollWheel > 0.0  camScript.distance == defaultDist*maxZoom) {
    64.         yield Zoom(defaultDist*maxZoom, defaultDist);
    65.     }
    66.     else if (scrollWheel < 0.0  camScript.distance == defaultDist) {
    67.         yield Zoom(defaultDist, defaultDist*maxZoom);
    68.     }
    69. }
    70.  
    71. // Lerp camScript.distance from one number to another, while continuing to update the targetPosition
    72. function Zoom (start : float, end : float) {
    73.     var t = 0.0;
    74.     while (t < 1.0) {
    75.         t += Time.deltaTime * zoomSpeed;
    76.         camScript.distance = Mathf.Lerp(start, end, Mathf.SmoothStep(0.0, 1.0, t));
    77.         camScript.targetPosition = planets[currentPlanet].position;
    78.         yield;
    79.     }
    80. }
    81.  
    82. // Lerp from current position to new position over time
    83. function MoveToPlanet (newPlanet : int) {
    84.     var timeFactor = 1.0 / cameraMoveTime;
    85.     var startDistance = camScript.distance;
    86.     var i = 0.0;
    87.     while (i < 1.0) {
    88.         i += Time.deltaTime * timeFactor;
    89.         // Make an index for ease in/ease out instead of plain linear motion
    90.         var t = Mathf.SmoothStep(0.0, 1.0, i);
    91.         camScript.targetPosition = Vector3.Lerp(planets[currentPlanet].position, planets[newPlanet].position, t);
    92.         camScript.distance = Mathf.Lerp(startDistance, defaultDistance[newPlanet], t);
    93.         yield;
    94.     }
    95.     currentPlanet = newPlanet;
    96. }
    --Eric
     
  29. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    This worked brilliantly. I'm going to spend a few days dissecting your code until I fully understand whats happening. Then I will move to finishing the solar system setup :)

    Thanks so much for the help.
     
  30. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    By the way, when I was attempting my own zoom in/out code, I was doing it in the actual Main_Camera script rather than in the Switch_Planets script. Would that have made a difference?
     
  31. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Oops, noticed a small flaw...use this for the input loop instead:

    Code (csharp):
    1.         while (buttonPressed == -1) {
    2.             // Keep updating the target position with the current planet's position
    3.             camScript.targetPosition = planets[currentPlanet].position;
    4.             // Check scroll wheel, and wait for zoom if there is wheel input
    5.             var scrollWheel = Input.GetAxis("Mouse ScrollWheel");
    6.             if (scrollWheel != 0.0) {
    7.                 yield CheckWheelDirection(scrollWheel);
    8.             }
    9.             else {
    10.                 // If no scroll wheel input, check for button input every frame
    11.                 buttonPressed = CheckInput();
    12.                 yield;
    13.             }
    14.             // Loop back to checking input again
    15.         }
    Otherwise, using the original routine, attempting to zoom in or out when it's not possible to zoom further causes some slight jittering. That's because you get two yields in a row and only one update of targetPosition, which does need to be updated every frame when it's not being lerped in the MoveToPlanet function.

    Another way to fix it would be to leave the routine the way it was, and add a duplicate "camScript.targetPosition..." line before or after the "yield CheckWheelDirection..." line, but that's kind of ugly, and if we wanted ugly we'd just do all this in Update with a huge mess of if/then hacks. ;)

    --Eric
     
  32. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    I noticed the jiggling once I updated my code and ran it. I wish I was at your level by now - able to recognize that there will be a jiggle, just by looking at the code. :D

    Updated and uploading the new project now.
     
  33. frogwise

    frogwise

    Joined:
    Nov 27, 2009
    Posts:
    103
    Here it is :

    http://www.bartekn.com/SolarSystem/Solar_System.html

    (no new planets yet!)


    By the way, when I was attempting my own zoom in/out code, I was doing it in the actual Main_Camera script rather than in the Switch_Planets script like you did. Would that have made a difference? I felt it was somehow necessary (or just looked cleaner to me) to have the mouse zoom stuff in the same section as the mouse orbit stuff (which is in Main_Camera).
     
  34. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,369
    Well, I saw the two yields, and thought that would result in something bad, but I still had to actually run it to determine the exact form of the badness. ;)

    There's a typo in that URL, but it's easy to fix. Cool planets! As far as the scripts go, there's no one way to do things. However, it seems to me that the zooming is more related to the planet switching, since you're lerping from one point to another. Whereas the mouse orbit stuff is free-form.

    --Eric
     
  35. pretender

    pretender

    Joined:
    Mar 6, 2010
    Posts:
    862
    this is similiar to what i need, there is a lot of info here, i dont know where to start. what scripts to modify, can i get i short recap?

    thanks!