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.

simple app to focus my learning

Discussion in 'Getting Started' started by tylerlybb, Jul 30, 2016.

  1. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    I followed a bunch of beginner tutorials and I created the Roll A Ball game, but now I want to focus my learning on what I will need to know for the first actual project I'm creating. For learning purposes, I've thought up a simple app that I want to create that will teach me a few of the skills I will need for the this project. I want to create this app then build it so that I can play it on my Android phone. Does anyone know of any existing tutorials that would teach the simple concepts I need for this? Or, if anyone can just give me some high-level direction, that would be great. For example, which parts of this app could be created within Unity vs what would need to be created outside of Unity and imported (asking about camera animation in particular)? What would be handled through scripts? Etc. I'm just looking for someone to point me in the right direction on any of this. Thanks!

    Here's the simple app I'd like to create and then play on my phone:

    1. Run the app
    2. See a cube off in the distance
    3. Tap on the screen
    4. See the view gradually change till you're up close and looking down on the top of the cube (smooth camera animation with ease-in/ease-out)
    5. Tap on the screen
    6. See the view gradually change back to the previous view (reverse of the previous animation)
    7. (You could repeat from here or be done)
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    This is a great starter project! If you've done those tutorials you should be well on your way to doing this.

    You'll need a script to move that camera, but it's not a hard script. I believe the Standard Assets contain a "SmoothFollow" or some such script that will probably do it for you. You just need another (even simpler) script to change the target transform and/or follow distance in step 4 (and change it back it step 6). The only possible hitch here is that the various smooth-follow scripts don't usually provide easy control over the angle of the camera. So when you move back, you might end up facing the wrong way.

    So, experiment with different scripts you can find, or write your own. You just need to smoothly interpolate the camera position, while keeping the rotation constantly looking at the cube. Post here if you get stuck on this step.

    To detect the tap, you can just use a Canvas containing a giant invisible Button (that is, a Button whose image color is set to completely transparent). So, to sketch out the full solution, you will need:
    • a scene set up with your cube, and perhaps two empty GameObjects that define the two positions for the camera
    • a script on the camera that smoothly moves towards a target position, while staying pointed at a target object
    • another script (let's call it TylerAction) that has a public method "ToggleView" that changes the camera target position between the two empty GameObjects (put this script on the camera as well)
    • a Canvas containing a giant Button, which invokes TylerAction.ToggleView on the main camera.
    Give it a go, and let us know when you need help!
     
  3. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    Thank you so much Joe! That helps a ton. I'll see how far I can get with it now.
     
  4. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    First time importing something from Standard Assets. I assume I'm supposed to look in the Cameras for the script you mentioned? I don't see a SmoothFollow. Below is an image showing what I see in the camera's scripts folder:

    Screen Shot 2016-07-30 at 9.05.12 PM.png

    Am I looking in the right place? If so, do you see one in here that might be the one you were trying to remember?
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    Possibly AbstractTargetFollower. You should also study LookatTarget (you can use that to keep the camera's eye on the cube), and AutoCam, which could be who-knows-what but it never hurts to open it and see!
     
  6. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    I'm new to scripting so please bear with me here. I found the SmoothFollow script you mentioned before (in the Utility folder). However, I don't know which parts I'm going to need for what I'm trying to do. I will paste the SmoothFollow script below for reference. First off, do I even need to use the x-z plane distance and height distance? And I'm not sure what the 'damp around the y-axis' is and the 'damp height' is for. I apologize for asking too many questions. I understand the basic components I need which you listed in your first response (two game objects, two scripts for the camera) but because I'm so new to scripting and Unity I'm finding it difficult to write my own scripts for this.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace UnityStandardAssets.Utility
    4. {
    5.     public class SmoothFollow : MonoBehaviour
    6.     {
    7.  
    8.         // The target we are following
    9.         [SerializeField]
    10.         private Transform target;
    11.         // The distance in the x-z plane to the target
    12.         [SerializeField]
    13.         private float distance = 10.0f;
    14.         // the height we want the camera to be above the target
    15.         [SerializeField]
    16.         private float height = 5.0f;
    17.  
    18.         [SerializeField]
    19.         private float rotationDamping;
    20.         [SerializeField]
    21.         private float heightDamping;
    22.  
    23.         // Use this for initialization
    24.         void Start() { }
    25.  
    26.         // Update is called once per frame
    27.         void LateUpdate()
    28.         {
    29.             // Early out if we don't have a target
    30.             if (!target)
    31.                 return;
    32.  
    33.             // Calculate the current rotation angles
    34.             var wantedRotationAngle = target.eulerAngles.y;
    35.             var wantedHeight = target.position.y + height;
    36.  
    37.             var currentRotationAngle = transform.eulerAngles.y;
    38.             var currentHeight = transform.position.y;
    39.  
    40.             // Damp the rotation around the y-axis
    41.             currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
    42.  
    43.             // Damp the height
    44.             currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime);
    45.  
    46.             // Convert the angle into a rotation
    47.             var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);
    48.  
    49.             // Set the position of the camera on the x-z plane to:
    50.             // distance meters behind the target
    51.             transform.position = target.position;
    52.             transform.position -= currentRotation * Vector3.forward * distance;
    53.  
    54.             // Set the height of the camera
    55.             transform.position = new Vector3(transform.position.x ,currentHeight , transform.position.z);
    56.  
    57.             // Always look at the target
    58.             transform.LookAt(target);
    59.         }
    60.     }
    61. }
     
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    Then you need to study and experiment. Read the code — there are comments on everything; you won't understand all of it, but read it carefully and grasp as much as you can. Then experiment: put this script on one object (say, a cube), and set the target to another object (a sphere). Run. What does the follower object do? Now, using the scene editor tools (while the game is running!), drag your target object around. What does the follower do now? Now select the follower, and change those properties you're not sure about: distance, height, etc. Muck with them one at a time, while keeping a close eye on how the object reacts. Continue until it all makes sense.

    The damping properties are the only ones that might still be confusing at that point. These have to do with how the motion is smoothed. It's always a trade-off between reacting quickly, and avoiding jerky movements. Again, play with the values, and then see how the behavior changes as you move the target around.

    Once you understand all this, you can put the follower script on a camera instead of a cube, and see how it makes the camera follow a target. I notice that this script always makes the follower look at the target (see the last line of the Update method), so you don't need a separate LookAt script; you only need this one.

    At that point you should be able to basically set up your scene. You can manually make the camera go between the two positions by changing the distance and height properties on this script. Zoom the camera back and forth several times until you're certain you know exactly what values to apply.

    Then all that will be left is to write a button action that switches those two values for you, instead of doing it manually!
     
  8. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    I played around with this script and found that the height doesn't do anything. Why would the script offer a "height" value to be entered yet do nothing with it? Maybe I'm just not understanding this correctly. Do you see a way to modify this script to use the height?

    If I can figure out how to get the height to work, I like your idea of writing a button action that switches the values of the distance and height, while leaving the target the same. I don't know how to write a script for a button that changes the values inside a separate script on the camera, but I am going to dig around in the tutorials and the manual to see if I can figure it out. Any guidance there would be great.

    Again, thanks for all your help!
     
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    Ugh. I see they're abusing Lerp here... and worse, this script really won't work if heightDamping and rotationDamping are zero (which they are by default).

    I don't much care for this code, but I think it will work for your purposes as long as you set these properties to values > 0. Try 1 for starters, and then increase it to make it approach the goal position faster, or decrease it to make it approach slower.
     
  10. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    I see what you mean. Height is working great for me now. However, I don't think this script will work for me (tell me if I'm wrong) because there is no damping for distance. The two properties I will want the invisible button to change are distance and height. Height works great now but distance does an instant pop. Is there an easy way to add distance damping to this script?

    Or maybe I should look into doing this in a different way. If there was a way to interpolate the camera between two transforms (indicated with two empty GameObjects in the scene, like you suggested in your initial response to my post), maybe that would be better. What do you think?

    I really appreciate your patience with me.
     
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    OK, yes, I think it's probably time for a custom script. Let's break it down and see if we can make it simple.

    All we really need is to move the camera between two positions. As for rotation, that will be easy: we'll just call transform.LookAt(target), as in the script above. So the only tricky part is the position.

    For that, we want to smoothly move from one position to another. We can recast this as moving smoothly towards a target position (and then assume you have another script, or an extension of this one, that changes the target between the two positions). Many people (including whatever bozo wrote the script above) abuse Lerp for that, but that's a bad idea for a number of reasons. The right way to do it is to use Vector3.SmoothDamp. That's what it's for.

    Now, the sample code given in the manual page for that is almost what we want. But it's more complicated than needed, because rather than going to the position of the target, it's going to some point relative to the target (that's what that TransformPoint call is in there for). So let's take that out and see what we have.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ExampleClass : MonoBehaviour {
    4.     public Transform target;
    5.     public float smoothTime = 0.3f;
    6.     private Vector3 velocity = Vector3.zero;
    7.     void Update() {
    8.         transform.position = Vector3.SmoothDamp(transform.position, target.position, ref velocity, smoothTime);
    9.     }
    10. }
    OK, now, this is a useful script! Do some experiments with it like you did before: attach this to a cube, and use a sphere for your target (which you drag into the "target" property of this script on your cube). Now run, and move your sphere around. You should see the cube smoothly chase the sphere.

    Now stop and change your scene setup a little bit. Put in TWO spheres (let's call them Target 1 and Target 2). Now run, and instead of moving the targets, drag in a different target to the "target" property of the script on your cube. If it's at Target 1, drag in Target 2. If it's at Target 2, drag in Target 1. You should see the cube move smoothly between the two points.

    Once you've got that, all you need is to (a) add that transform.LookAt call to make it always point at the subject of interest, and (b) add a method (invoked from a button) to switch between two targets automatically.

    Give all that a try and let us know how it goes!
     
  12. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    Thank you! It's all working great now (including the LookAt) except for the button. I have added the button to my scene and I have it positioned correctly (I decided to just let the button be visible). Now I need to add an 'on click' event to cause the target on the other script to switch to target1 if target2 is in there, or switch to target2 if target1 is in there. I am searching around for guidance on this but haven't found enough to get me all the way there yet. I will keep looking but if you have any quick advice I (again) would appreciate it. You have helped me a ton. Thank you!
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    Right, I doubt you would find something for that specific need. But it's pretty simple once you get some basic C# skills. It would look something like this:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TargetSwitcher : MonoBehaviour {}
    4.     public Transform target1;
    5.     public Transform target2;
    6.    
    7.     public void Switch() {
    8.         ExampleClass follower = GetComponent<ExampleClass>();
    9.         if (follower == null) Debug.LogError("Hey, where's the ExampleClass?!?");
    10.  
    11.         if (follower.target == target1) follower.target = target2;
    12.         else follower.target = target1;
    13.     }
    14. }
    Attach this to the same object as your ExampleClass (follower) script. Then, hook up the button event to invoke this Switch() method.

    Read through that code and see if you can understand every line; ask questions where it's unclear. This really is fundamental stuff you should get a good grasp of before moving on.
     
  14. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    OK, I've got it all up and working now! And I was able to test it on my Android phone too. Awesome! THANK YOU!

    And although I obviously really need to learn C# in more of a textbook way, starting at the beginning, I think I basically understand what this code is doing. However, I will ask this:

    C#_question.png

    Thanks again!!
     
  15. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    The answer to the question in your image is "yes". That's exactly what that line says.

    And the first "ExampleClass" in that line is part of declaring a variable. C# is a strongly typed language, so the standard way to declare a variable is to first give the type, and then the name, and then optionally assign an initial value (as we have done here). In the line:

    Code (CSharp):
    1. Grep foo = bar;
    We are declaring a variable of type "Grep" called "foo" whose value is "bar".

    But now I have to tell you about an annoying (but sometimes convenient) little exception to this rule. In a case like this, where you are, in fact, assigning an initial value as you declare a local variable, you don't have to tell C# the type... you can instead use the magic word "var" which means "figure out the type from what I'm assigning." So I could have instead written

    Code (CSharp):
    1. var follower = GetComponent<ExampleClass>();
    and this would do exactly the same thing. But I tend to avoid "var", particularly when providing code examples, because it's not as clear and explicit as spelling out the type.
     
  16. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    Thanks. And thank you for all your help here! I really appreciate it.
     
    JoeStrout likes this.
  17. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    Joe, not sure if you'll see this but I have a follow-up question on this thread.

    You helped me write two scripts which work together to switch my camera's position. One updates the camera's position smoothly on every frame (CameraPosition). The other holds the different targets to switch between (CameraSwitcher). I now want to not only change the camera's position but also where it is aiming at. I added the aim targets to CameraSwitcher and it worked but the aim doesn't change gradually, it popped over one frame. I went and looked at the CameraPosition script and tried changing it so that the lookAt would blend smoothly just like the position, but it's giving me errors. I'm trying to figure out the right way to do this. If you have any suggestions I would greatly appreciate it, as always.

    The CameraPosition script before I tried changing it. (This is the one that causes the camera's aim to pop over one frame.)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CameraPosition : MonoBehaviour {
    5.     public Transform target;
    6.     public float smoothTime = 0.3f;
    7.     public Transform aimTarget;
    8.     private Vector3 velocity = Vector3.zero;
    9.  
    10.     void Update() {
    11.         transform.position = Vector3.SmoothDamp(transform.position, target.position, ref velocity, smoothTime);
    12.         transform.LookAt(aimTarget);
    13.     }
    14. }


    The CameraPosition script after I tried changing it. This one gives me errors. I'm obviously just not understanding how to deal with lookAts and vector3s.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CameraPosition : MonoBehaviour {
    5.     public Transform target;
    6.     public float smoothTime = 0.3f;
    7.     public Transform aimTarget;
    8.     private Vector3 velocity = Vector3.zero;
    9.  
    10.     void Update() {
    11.         transform.position = Vector3.SmoothDamp(transform.position, target.position, ref velocity, smoothTime);
    12.         transform.LookAt = Vector3.SmoothDamp(transform.LookAt, aimTarget.LookAt, ref velocity, smoothTime);
    13.     }
    14. }
     
  18. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,815
    Right... you're trying to assign to (and read from) LookAt. You can't do that, as it's a method, not a property. I can imagine the compiler is all kinds of confused about what the heck you are trying to say there.

    Actually, your original code is interesting to me because I didn't know (or had forgotten) that LookAt is overloaded ("has two versions") to take either a Transform or a Vector3. That seems like a weird thing for them to bother to include, since if you have a Transform, it's trivial to get its position... but they must have reasoned that wanting to LookAt the position of another object is the most common use case.

    But fortunately there is the other version, that takes a Vector3, because you're going to need that for one of the two solutions here. The options are:
    1. Smoothly move the point in space that the camera is looking at, from its old aim target to the new aim target. OR,
    2. Smoothly rotate the camera from the orientation that looks at the old target, to the orientation that looks at the new target.
    These are not quite the same thing. For option 1, imagine a little target marker that sits on your first aim target. (Go on, picture it, a little red and white bulls-eye.) This is what the camera always looks at. And now we switch to the second aim target, and this little target marker is going to move in a straight line, through the world, to its new position, passing through whatever points in the world are in between. The camera tracks it faithfully. So for example, if the target marker passes below the camera on its way to its new position, then the camera is going to look straight down.

    Option 2 would instead rotate in the shortest direction from target 1 to target 2, which might mean not looking straight down, but instead (more likely) panning over the horizon to focus on target 2.

    I don't know which you want, though if it were me, I'd probably prefer option 2. But let me sketch out how to do each of them.
    1. Keep a Vector3 which is your aimMarker, and use Vector3.SmoothDamp to move this from wherever it is (aimMarker) to where it belongs (aimTarget.position); and then just transform.LookAt(aimMarker). OR,
    2. Use Quaternion.LookRotation to calculate the rotation that looks at aimTarget, and then use Quaternion.RotateTowards to gradually move your transform rotation towards this proper rotation.
    So there you go... two solutions, no extra charge! :)
     
  19. tylerlybb

    tylerlybb

    Joined:
    Jul 21, 2016
    Posts:
    51
    I chose method 1 because it's similar to how I've set up cameras for animation in Maya before. And it worked perfectly! Again, thank you so much!

    Joe, you've helped me out so much. If you ever need any help with art or animation, etc. please let me know.
     
    JoeStrout likes this.