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

Question Need help with LocalEulerRotations - implementation works great except for position 4.

Discussion in 'Scripting' started by Chasenuva1, Jul 16, 2023.

  1. Chasenuva1

    Chasenuva1

    Joined:
    Mar 4, 2023
    Posts:
    9
    EDIT: It is important to note that the joystick described below does not reflect the vehicle's movement. This game is a simulator-type of my day job, and the vehicle is a piece of heavy machinery with conveyor systems to move large boxes (in game these boxes are rigidbodies) across the conveying surface of the vehicle. The joystick needs to move smoothly into position to reflect the condition of the direction the conveying surface is moving boxes. I can't just set the joystick relative to the 'speed' of the conveyors because the conveying system is either moving or not moving, there is no speed control.

    ------
    Original Post:

    I'm probably just doing something stupid, but I don't see the error.... I am trying to make a gameobject that represents a joystick rotate properly based on what the rest of the Vehicle is being told to do. The player will have several options for controlling the vehicle, but the vehicle will always move the gameobject-joystick in the proper direction. I control the vehicle in other scripts, and when I need to change which direction this gameobject is facing, I call the MoveJoystick() function. This is mostly working, except in one case.

    The parent gameobject is locally rotated -20, 0, -20 so that the joystickObj only needs to be locally rotated positively. (I have had issues elsewhere trying to get negative local euler angles to behave, and this was the solution back then)

    The code below works great and as intended, unless I try to move the joystick to the right (position/state 4, X40 Z20). the first time I instruct the Right position, the joystick moves to the Left position. After the first time, all further instructions to move to the Right position results in the joystick moving to the Centered position. I have confirmed the targetAngles are being set and received properly. All other joystick positions move to the proper location/rotation. I have tried many different things, but they either have no effect or break the rest of the joystick positions. Anyone have any ideas?

    Here is a short recording of the problem if my description was a bit confusing:


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //All inputPosition scripts require the Controls script on the same GameObject
    6. [RequireComponent(typeof(Controls))]
    7.  
    8. public class JoystickPosition : MonoBehaviour {
    9.  
    10.     [SerializeField] private GameObject joystickObj;
    11.     [SerializeField] private float posCenteredXAngle;
    12.     [SerializeField] private float posCenteredZAngle;
    13.     [SerializeField] private float posUpXAngle;
    14.     [SerializeField] private float posUpZAngle;
    15.     [SerializeField] private float posDownXAngle;
    16.     [SerializeField] private float posDownZAngle;
    17.     [SerializeField] private float posLeftXAngle;
    18.     [SerializeField] private float posLeftZAngle;
    19.     [SerializeField] private float posRightXAngle;
    20.     [SerializeField] private float posRightZAngle;
    21.     [SerializeField] private float moveSpeed;
    22.     [SerializeField] private float bufferSafeZone;
    23.     private Controls ctrls;
    24.     private float curXAngle = 0f;
    25.     private float curZAngle = 0f;
    26.     private float tarXAngle = 0f;
    27.     private float tarZAngle = 0f;
    28.     private bool isTurning = false;
    29.  
    30.     public void Start() {
    31.         ctrls = GetComponent<Controls>();
    32.     }
    33.  
    34.     public void MoveJoystick() {
    35.         Debug.Log("Moving Joystick...");
    36.         int position = ctrls.currentState;
    37.         if (position == 0) {
    38.             tarXAngle = posCenteredXAngle;
    39.             tarZAngle = posCenteredZAngle;
    40.         }
    41.         else if (position == 1) {
    42.             tarXAngle = posUpXAngle;
    43.             tarZAngle = posUpZAngle;
    44.         }
    45.         else if (position == 2) {
    46.             tarXAngle = posDownXAngle;
    47.             tarZAngle = posDownZAngle;
    48.         }
    49.         else if (position == 3) {
    50.             tarXAngle = posLeftXAngle;
    51.             tarZAngle = posLeftZAngle;
    52.         }
    53.         else if (position == 4) {
    54.             Debug.Log("Setting target angle 4: " + posRightXAngle + ", " + posRightZAngle);
    55.             tarXAngle = posRightXAngle;
    56.             tarXAngle = posRightZAngle;
    57.         }
    58.         else {
    59.             Debug.Log("Invalid position sent to MoveJoystick(): " + position);
    60.             return;
    61.         }
    62.         curXAngle = joystickObj.transform.localEulerAngles.x;
    63.         curZAngle = joystickObj.transform.localEulerAngles.z;
    64.         isTurning = true;
    65.     }
    66.  
    67.     public void Update() {
    68.         if (isTurning) {
    69.             float turnXAmount = (tarXAngle - curXAngle);
    70.             float turnZAmount = (tarZAngle - curZAngle);
    71.             Debug.Log("Turning by amount: X:" + turnXAmount + ", Z:" + turnZAmount);
    72.             if (Mathf.Abs(turnXAmount) > moveSpeed * Time.deltaTime) { // checks if a full frame-length move is farther than we need
    73.                 if (turnXAmount < 0) {
    74.                     joystickObj.transform.localEulerAngles += new Vector3(-moveSpeed * Time.deltaTime, 0, 0);
    75.                 }
    76.                 else {
    77.                     joystickObj.transform.localEulerAngles += new Vector3(moveSpeed * Time.deltaTime, 0, 0);
    78.                 }
    79.             }
    80.             else {
    81.                 joystickObj.transform.localEulerAngles += new Vector3(turnXAmount, 0, 0);
    82.             }
    83.             if (Mathf.Abs(turnZAmount) > moveSpeed * Time.deltaTime) { // checks if a full frame-length move is farther than we need
    84.                 if (turnZAmount < 0) {
    85.                     joystickObj.transform.localEulerAngles += new Vector3(0, 0, -moveSpeed * Time.deltaTime);
    86.                 }
    87.                 else {
    88.                     joystickObj.transform.localEulerAngles += new Vector3(0, 0, moveSpeed * Time.deltaTime);
    89.                 }
    90.             }
    91.             else {
    92.                 joystickObj.transform.localEulerAngles += new Vector3(0, 0, turnZAmount);
    93.             }
    94.             curXAngle = joystickObj.transform.localEulerAngles.x;
    95.             curZAngle = joystickObj.transform.localEulerAngles.z;
    96.             Debug.Log("Turned Joystick to: X:" + curXAngle + ", Z:" + curZAngle);
    97.             // angles are weird, so we have an angle buffer to avoid permanent miniscule corrections to the joystick angles
    98.             if (tarXAngle <= curXAngle + bufferSafeZone && tarXAngle >= curXAngle - bufferSafeZone && tarZAngle <= curZAngle + bufferSafeZone && tarZAngle >= curZAngle - bufferSafeZone) {
    99.                 Debug.Log("Turn Complete.");
    100.                 isTurning = false;
    101.             }
    102.         }
    103.     }
    104. }
    105.  
     
    Last edited: Jul 17, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
  3. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I fear your script is unnecessarily complicated and so it's not really worth bothering trying to fix it. You should simply take the vehicles moving direction and use it to set the joysticks transform directly.

    joystick.transform.up=vehicle.transform.up+vehicleMoveDirection*0.5f;
     
    Last edited: Jul 16, 2023
  4. Chasenuva1

    Chasenuva1

    Joined:
    Mar 4, 2023
    Posts:
    9
    Interesting reads. I'll look more into some other options for angles, but the fact that the joystick works in the other positions leads me to believe it should work for the last position too.


    While I would agree with you in the case of the joystick controlling the movement of the vehicle, but it does not.
    This is a simulator-type game that includes a vehicle / piece of equipment I work with at my day job. IRL, the joystick controls a section of a conveyor system that moves very large boxes (in-game, these boxes are rigidbody gameobjects) across it's surface. The movement directions of the conveyor system are either on or off with no speed control, so it would look bad if I just set the position of the joystick to the state of that section of the conveyor system. The Joystick alse should move when the player inputs the controls, even if the conveyor surfaces are off and/or broken for some reason, which is why I have the script set up the way it is.

    Is there still a better way to do what I need? Probably. I readily acknowledge I am very new to game dev and I appreciate your feedback! If you have a simpler way to do what I need, I would love the tips!
     
    Last edited: Jul 17, 2023
  5. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    If you know what direction the vehicle is going in (with respect to its orientation), then that's all you need to rotate the joystick. You can flatten the direction, and then create a perpendicular direction with Vector.Cross, create a rotation around said axis with Quatenion.AngleAxis, and use that to rotate the joystick.

    Some code off the top of my head:
    Code (CSharp):
    1. Vector3 vehicleVel = vehicleRB.velocity;
    2. Vector3 localVel = rigibody.transform.InverseTransformDirection(vehicleVel);
    3.  
    4. Vector3 flatLocal = Vector3.ProjectOntoPlane(localVel, Vector3.up);
    5. Vector3 right = Vector3.Cross(Vector3.up, flatLocal);
    6.  
    7. float maxAngle = 20f // this can be an inspector field
    8.  
    9. float angle = vehicleVel.magnitude / maxAngle; // this calculation could be more elaborate
    10. angle = Mathf.Clamp(0f, maxAngle);
    11.  
    12. Quaternion angleRotation = Quaternion.AngleAxis(angle, right);
    13. Quaternion rotation = angleRotation * Quaternion.identity; // rotate default rotation by the angle
    14. transform.rotation = rotation;
    If you don't understand what each line does here, the docs will have an explanation; feel free to ask too.

    Notably this doesn't care about the direction of the vehicle. Most of this directional/rotational stuff usually doesn't need any form of branching logic. Just a series of calculations to get the result.
     
  6. Chasenuva1

    Chasenuva1

    Joined:
    Mar 4, 2023
    Posts:
    9
    I will update the first post to include that the joystick does not control the vehicle movement, but a conveyor system on the vehicle. Sorry for the confusion.

    I'll look into using those things like Vector.Cross and ProjectOntoPlane, I've never seen those before, and it seems like I might be able to use those to get the result I want with less branches. Thanks!

    And for the Quaternion stuff... I've been avoiding those because all the documentation goes too deep to fast and I feel like I get lost before I finish the first paragraph. :/ If you know of any resources that are good for explaining Quaternions, I'd love to know!
     
    Last edited: Jul 17, 2023
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Right, well the concept is the same in the end. Find a way to boil down the conveyor belts into some form of direction, and use that direction to create a rotation.

    There's probably good resources on youtube, or stuff other folks can link. I kinda... just got the hang of them through time.

    I see them as a rotation around a particular axis (this isn't strictly correct, it's just how I rationalise them in my math illiterate mind). You make these rotations with the provided methods, and combine them by multiplying them together (or subtracting by multiplying with the Quaternion.Inverse) of a rotation.

    So in my example, I make a rotation around an axis - which is perpendicular to the direction we worked out - to rotate the joystick in our direction. I combine said rotation with
    Quaternion.identity
    so we have a default rotation + the rotation around the axis, to get a combined rotation that should 'point' the joystick in the right direction.
     
  8. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I suggest you let the user move the joystick. In the real world there's a directional force being applied to the joystick and this determines its rotation. Unity's Input.GetAxis will return a direction that can also be applied to your virtual joystick in just a couple of lines of code.

    Perhaps this is a VR project and you're using a hand controller to move the joystick. The situation is still the same, you can take the relative position of the controller in relation to the joystick and create a direction vector to move the joystick.

    You can then use the state of virtual joystick to dictate the behavior of the conveyor. Otherwise it's going to look strange if your conveyor is moving before the joystick has fully moved into position.