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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Making one object 'dodge' another

Discussion in 'Scripting' started by benjrose89, Jun 21, 2020.

  1. benjrose89

    benjrose89

    Joined:
    Jun 17, 2020
    Posts:
    8
    I'm pretty new to Unity, coming from GameMaker so thinking in 3D is slightly blowing my tiny mind...

    I'm trying to make a ship turn to dodge rocks in water if they come close. I thought the easiest way to do this would be to attach a collider to the front of the ship and have it turn when a rock entered that collider. I've coloured it red for visibility:

    upload_2020-6-21_23-57-41.png

    Giant boat-dongs aside, code below:

    Code (CSharp):
    1. public class ShipBehaviour : MonoBehaviour
    2. {
    3.     public float shipMovementSpeed;
    4.     Rigidbody myRigidBody;
    5.     public bool hasCollidedWithRock;
    6.     public float shipMaxTurnSpeed;
    7.     public float turnAccelerationPerSecond;
    8.     private float shipStandardTurnSpeed = 3;
    9.     private float shipTurnSpeed;
    10.     Vector3 turnRotation;
    11.     public float soulsAboard;
    12.  
    13.  
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.         myRigidBody = GetComponent<Rigidbody>();
    19.         soulsAboard = Random.Range(20, 60);
    20.         shipTurnSpeed = shipStandardTurnSpeed;
    21.     }
    22.  
    23.     // Update is called once per frame
    24.     void Update()
    25.     {
    26.  
    27.     }
    28.  
    29.     void FixedUpdate()
    30.     {
    31.         float amountToMove = shipMovementSpeed * Time.fixedDeltaTime;
    32.         if (hasCollidedWithRock == false)
    33.         {
    34.             myRigidBody.velocity = transform.forward * amountToMove;
    35.         }
    36.     }
    37.  
    38.     private void OnCollisionEnter(Collision collision)
    39.     {
    40.         if (collision.gameObject.tag.Equals("Rock"))
    41.         {
    42.             hasCollidedWithRock = true;
    43.             myRigidBody.constraints = RigidbodyConstraints.None;
    44.             shipMovementSpeed = 0;
    45.             myRigidBody.velocity = Vector3.zero;
    46.             //gameManager.soulsLost += soulsAboard;
    47.         }
    48.     }
    49.  
    50.     private void OnTriggerStay(Collider other)
    51.     {
    52.         if (other.gameObject.tag.Equals("Rock") || other.gameObject.tag.Equals("Cliff"))
    53.         {
    54.             RockBehaviour rockSeen = other.GetComponent<RockBehaviour>();
    55.             //Calculate direction to rock
    56.             Vector3 rockPosition = other.transform.position;
    57.             Vector3 shipPosition = myRigidBody.transform.position;
    58.             var rockDirection = rockPosition - shipPosition;
    59.             //Determine if the object is to the right or left
    60.             var projectionOnRight = Vector3.Dot(rockDirection, myRigidBody.transform.right);
    61.             if (shipTurnSpeed < shipMaxTurnSpeed)
    62.             {
    63.                 shipTurnSpeed += turnAccelerationPerSecond * Time.deltaTime;
    64.             }
    65.             Debug.Log(shipTurnSpeed);
    66.  
    67.             //Turn accordingly
    68.             if (projectionOnRight < 0 && rockSeen.isLit == true)
    69.             {
    70.                 //Debug.Log("Rock Left!");
    71.                 turnRotation = new Vector3(0, rockDirection.z + shipTurnSpeed, 0);
    72.                 Quaternion deltaRotation = Quaternion.Euler(turnRotation * Time.fixedDeltaTime);
    73.                 myRigidBody.MoveRotation(myRigidBody.rotation * deltaRotation);
    74.             }
    75.             if (projectionOnRight > 0 && rockSeen.isLit == true)
    76.             {
    77.                 //Debug.Log("Rock Right!");
    78.                 turnRotation = new Vector3(0, rockDirection.z - shipTurnSpeed, 0);
    79.                 //Debug.Log("Turn Rotation" + turnRotation);
    80.                 Quaternion deltaRotation = Quaternion.Euler(turnRotation * Time.fixedDeltaTime);
    81.                 myRigidBody.MoveRotation(myRigidBody.rotation * deltaRotation);
    82.             }
    83.         }
    84.     }
    85.  
    86.     private void OnTriggerExit(Collider other)
    87.     {
    88.         if (other.gameObject.tag.Equals("Rock") || other.gameObject.tag.Equals("Cliff"))
    89.         {
    90.             shipTurnSpeed = shipStandardTurnSpeed;
    91.         }
    92.  
    93.     }
    94. }
    This works great as long as only one rock is visible at a time, but there are a few problems:

    Problem 1: If more than one rock enters the collider, the ship wiggles furiously and crashes into both of them.
    Problem 2: If the ship turns to the point at which it's no longer travelling in the Z axis, it has no idea what to do and probably crashes. I'm sure this is because it's measuring against the other object's Z position, but my brain is stupid and can't figure out what to do about this.
    Problem 3: I'm not sure if my giant collider is in any way a good idea.

    I'd really appreciate any tips on this!
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    Pathfinding is "hard" in the sense that unless it's easy for a single agent to turn and avoid something, there's always the chance that it would run into another obstacle.

    You could put a plane at your water level, then put colliders on the rocks and mark them as not-travelable, build a NavMesh and then move your boat with a NavMeshAgent.

    This might make the boat move in a very non-boat way, so to fix that, you could make an invisible proxy agent that follows the path, while the boat has a separate script that tries to keep up with the agent, which would let the boat do whatever boaty-heave-in-water type behavior you want, and also not look like it's on a rail, if you give it some slop to deviate away from the invisible agent.

    Go play with NavMesh a bit... mark stuff you want "baked" into the navmesh as "STATIC" (upper corner of inspector) otherwise the baking would fail, and that is usually the step I forget when I play with navigation.

    I hope you go on to be rich and famous with the first game that has a giant boat dong. If you end up using pathfinding, you won't need a giant boat dong, but why not add one anyway? What have you got to lose?
     
  3. benjrose89

    benjrose89

    Joined:
    Jun 17, 2020
    Posts:
    8
    That sounds like a good solution, I'll look into that. Is it possible to generate a navmesh through randomly-placed objects? I'd also considered maybe switching to raytracing, with the rays stopping at the closest collision.

    I'll keep you posted about the dongs.
     
    Last edited: Jun 22, 2020
  4. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Attach the navmesh obstacle component to randomly spawned objects.Should do the trick
     
    PraetorBlue likes this.
  5. benjrose89

    benjrose89

    Joined:
    Jun 17, 2020
    Posts:
    8
    Thanks for the advice! Works great.
     
    Kurt-Dekker likes this.