Search Unity

Asking for help with movement code

Discussion in 'Scripting' started by brandonschlosser101, Oct 28, 2019.

  1. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15
    Hi there!

    I'm still learning C# and was hoping someone could help me figure out a way to write this code I've been stuck on for a few weeks.

    I tried something a bit more complicated but eventually gave up and resorted to a simpler version. Ill include both ideas so any help is greatly appreciated!

    My original idea is for a 3d side-scrolling platformer. It takes place in a 3d space and the player only moves left and right, but "left and right" is relative to the path they are on. If the path curves around a building, "right" means forward on the path and "left" means back etc. It kind of looks like the level itself is rotating from the player perspective. I have the side scrolling movement down with jumping and double jumping, but how can I make the player move along a spline? The code seems very complicated so any help making this simpler is greatly appreciated.

    The thing I resorted to is to just have 90 degree turns where when the player gets to a corner the camera rotates around and lets the player continue on. (Imagine a square) However when the player goes backward, I want them to go back on the original side (not off into space) so basically just do the inverse of the angle. Ill include the code I have for this below. I don't think my "else if" statement is working correctly. the "If" makes the "else if" true so both execute. Is that supposed to happen?

    If anyone can help explain how to do the first thing, that would be amazing but if not, even the simpler workaround is great!
    Thank you again!


    using UnityEngine;
    public class Turning : MonoBehaviour
    {
    public int turningAngle;
    public bool hasGoneThrough;

    // This script is on an empty trigger placed on the corners

    void OnTriggerEnter(Collider other)
    {
    if (other.gameObject.tag == "Player" && hasGoneThrough == false)
    {
    Debug.Log(turningAngle);
    other.gameObject.transform.eulerAngles = new Vector3(0, turningAngle, 0);
    turningAngle = -turningAngle;
    hasGoneThrough = true;
    }
    else if(other.gameObject.tag == "Player" && hasGoneThrough == true)
    {
    Debug.Log(turningAngle);
    other.gameObject.transform.eulerAngles = new Vector3(0, turningAngle, 0);
    turningAngle = -turningAngle;
    hasGoneThrough = false;
    }
    }
    }
     

    Attached Files:

  2. ibbybn

    ibbybn

    Joined:
    Jan 6, 2017
    Posts:
    193
    Hm maybe you could just put empty transforms along the way rotated the correct way and then use the rotation of the nearest one on your character?
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Use code tags when you post code, it's the first sticky post on the forum.
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    As for the actual algorithm, I think the way you're approaching the problem is just about the hardest way you could do it. This is for something like Pandemonium's gameplay, right?

    The way I'd code this is as follows:
    First, you need to have a master "level progress" sort of object. This object will have an array of, let's call them LevelNodes, which represent the pivot points in your level (which I think your "Turning" script is intended to be?) Each one of these nodes has a "progress" variable, which represents how far horizontally it is along the level's course. (This could be set in Start by having the LevelProgress script run through its array, adding up the distance from one node to the next, and setting it to the correct value. This would make level design/iteration easier.)

    So now, how do you restrict your player's position to this path? Take that thang, flip it, and reverse it: when the player hits left or right, you never change the player's position, but rather, you change the progress, and then use that to change the player position. Essentially, use "progress" anytime you'd be tempted to use "transform.position.x" in a flat side-scroller. At the end of the whole thing, set the player's X and Z to values that you get from lerping between the appropriate nodes. (Leave Y unaffected)

    Does this approach sound like it'd work in your game?
     
  5. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15
    Thank you for such a quick reply! Im sorry about not tagging correctly. Im just starting to get into forums. And yes that link shows exactly the kind of thing I'm trying to do!

    So to make sure I understand, I would have a series of empty objects around my path and use a code to track the distance between node 0 and 1, 1 and 2, etc until the end of the level.

    If this is right, how do I update the transform of the player using this "progress?" Would each node lerp from one to the other so the transform values update as the "progress" updates? Or would the whole network be one big lerp from 0 to 1 doing the same thing?

    (I hope I'm using those terms correctly)

    Also, I'm assuming to make a more curved path, I'd just add more nodes right? Less nodes means more block turns?

    This is a much easier way than I was thinking so thank you. Here I am trying to calculate bezier curves and splines but I'm still learning the basics!
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You'll ultimately convert it to a 0-to-1 value between two nodes, but you'll want to do most of your logic in "units of distance" (e.g. "how many meters along the track")

    So here's roughly what that algorithm will look like on the level progress script:
    Code (csharp):
    1. public LevelNode[] nodes; //set these in the inspector
    2. public Vector3 GetPositionForProgress(float progress) {
    3. if (progress < 0f) return nodes[0].transform.position;
    4. for (int n=0;n<nodes.Length - 1;n++) {
    5. if (progress < nodes[n+1].progress) {
    6. float percentageBetweenTwoNodes = (progress - nodes[n].progress) / (nodes[n+1].progress - nodes[n].progress);
    7. return Vector3.Lerp(nodes[n].transform.position, nodes[n+1].transform.position, percentageBetweenTwoNodes);
    8. }
    9. }
    10. //if we've made it this far, "progress" must be past the last node's value
    11. return nodes[nodes.Length-1].transform.position;
    12. }
    Adding more nodes is definitely the simplest way to go about it. Adding bezier curves to it is an option but then you'd want to also add some editor tools to set the splines which is a whole other thing to learn, so probably best to stick to straight lines.
     
  7. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15
    Thanks so much for taking the time! I'll see how this all works. :)
     
  8. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15

    So I took some time to learn about for loops and I think I fully understand your code now. I switched the division around so that it becomes a decimal for the percentage. Is this right?

    It looks like I need to define what progress is. It errors when there is a progress after the "nodes[n].progress" stuff. You said before that I can define that by adding up the distances in the array during start. Am I adding the transform values? How do I do that?

    Below is how I have the code with my small tweak:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class LevelMapping : MonoBehaviour
    4. {
    5.     public GameObject[] nodes;
    6.  
    7.     void Start()
    8.     {
    9.      
    10.     }
    11.     public Vector3 GetPositionForProgress (float progress)
    12.     {
    13.      
    14.         if (progress < 0f) return nodes[0].transform.position;
    15.         for (int n = 0; n < nodes.Length - 1; n++)
    16.         {
    17.             if (progress < nodes[n + 1].progress)
    18.             {
    19.                 float percentageBetweenTwoNodes = (nodes[n + 1].progress - nodes[n].progress) / (progress - nodes[n].progress);
    20.                 return Vector3.Lerp(nodes[n].transform.position, nodes[n + 1].transform.position, percentageBetweenTwoNodes);
    21.             }
    22.         }
    23.         return nodes[nodes.Length - 1].transform.position;
    24.     }
    25. }
    26.  
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Let's say that your player's progress is 15, and there are nodes at 14 and 18. You want the result of this operation to be .25 (you're 25% of the way between those two nodes), and the way to get that is (15 - 14) / (18 - 14).

    You'll need to use a class for this, which I called LevelNode in my sample code - GameObject won't work here, precisely because it doesn't have this "progress" variable that we need.
    Code (csharp):
    1. public class LevelNode : MonoBehaviour {
    2. public float progress;
    3. }
    Attach this script to every one of your nodes, then drag them all into the array on the master object.
    To set the values, you'll want to do something like this in Start:
    Code (csharp):
    1. public float currentProgress = 0f;
    2. for (int n=0;n<nodes.Length;n++) {
    3. nodes[n].progress = currentProgress;
    4. if (n < nodes.Length - 1) {
    5. currentProgress += Vector3.Distance(nodes[n].transform.position, nodes[n+1].transform.position);
    6. }
    7. }
     
  10. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15

    Thanks again. You're really helping me learn things I needed to know about like arrays, loops and setting up lerps.

    Here is my code now. Ive changed a few words to make it clearer to me. Like progress to location. When running in the inspector, each additional node increases in value until the last node. So for example, node 0 is 0, 1 is 18, 2 is 27, 3 is 33, etc. and Im trying to update the players location in update on button press. But I can't seem to reference the node number in update. (the nodes[n] errors) I'm sorry to ask since I keep bothering you, but I've fiddled with this for 4 hours and can't figure out how to update position, let alone position and rotation.

    Any ideas?


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class LevelMapping : MonoBehaviour
    4. {
    5.     public LevelNode[] nodes;
    6.     public GameObject Player;
    7.     private float percentageBetweenTwoNodes;
    8.     public float speed;
    9.  
    10.  
    11.  
    12.     void Start()
    13.     {
    14.         float currentProgress = 0f;  //location while going through the loop each time
    15.  
    16.         for (int n = 0; n < nodes.Length; n++)  //Length meaning how many nodes are in the array
    17.         {
    18.             nodes[n].location = currentProgress;
    19.             if (n < nodes.Length - 1)
    20.             {
    21.                 currentProgress += Vector3.Distance(nodes[n].transform.position, nodes[n + 1].transform.position);
    22.             }
    23.         }
    24.  
    25.         Player.transform.position = nodes[0].transform.position;   //puts player at starting node
    26.     }
    27.     public Vector3 GetPositionForProgress (float location)
    28.     {
    29.      
    30.         if (location < 0f) return nodes[0].transform.position;
    31.         for (int n = 0; n < nodes.Length - 1; n++)
    32.         {
    33.             if (location < nodes[n + 1].location)
    34.             {
    35.                 percentageBetweenTwoNodes = (location - nodes[n].location) / (nodes[n + 1].location - nodes[n].location);
    36.                 return Vector3.Lerp(nodes[n].transform.position, nodes[n + 1].transform.position, percentageBetweenTwoNodes);
    37.              
    38.             }
    39.         }
    40.         return nodes[nodes.Length - 1].transform.position;
    41.      
    42.     }
    43.  
    44.     void update()
    45.     {
    46.         if(Input.GetKey("d"))
    47.         {
    48.             Player.transform.position = Vector3.Lerp(nodes[n].transform.position, nodes[n + 1].transform.position, speed);
    49.         }
    50.      
    51.         if(Input.GetKey("a"))
    52.         {
    53.             Player.transform.position = Vector3.Lerp(nodes[n].transform.position, nodes[n - 1].transform.position, speed);
    54.         }
    55.     }
    56. }
    57.  
    58.  
    59.  
     
  11. ibbybn

    ibbybn

    Joined:
    Jan 6, 2017
    Posts:
    193
    Not sure it's a good idea to link your movement code so directly to the nodes. You still want to jump, use physics etc. Why not just determine the rotation by the nodes and move the character via forces?
    Just find the nearest node and check on that transform if the player.position is nearer to node.transform.position + or -node.transform.forward. Then you got all the information you need to determine what node to look towards ( you can use Quaternion.LookRotation for example ).
     
    Last edited: Oct 30, 2019
  12. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15
    Thanks! Yeah that sounds like I can figure that out for the rotation. Like just compare the vector 3 if the player with the vector 3 of all nodes in update to determine which is closest and then make the rotation of the player the same as the node(assuming i rotate the notes accordingly) right?

    So you say that another way I can do this is to add force relative to the rotation of the player? How do I code that? So far I know how to code forces and velocities on a defined axis like AddForce(x,y,z) or velocity = new Vector 3( x,y,z).
     
  13. ibbybn

    ibbybn

    Joined:
    Jan 6, 2017
    Posts:
    193
    EDIT: Depends. If you use the node behind the player you can just use its rotation. If you use the one in front use lookrotation.

    You can use this to move in localspace: https://docs.unity3d.com/ScriptReference/Rigidbody.AddRelativeForce.html
     
    Last edited: Oct 30, 2019
  14. brandonschlosser101

    brandonschlosser101

    Joined:
    Apr 19, 2018
    Posts:
    15

    Ohhhh!! I'll try that out! I'm excited lol. I'll post how it goes using this method. Thank you so much for advice. Both of you are awesome!