Opsive and Bonecracker’s Realistic Car Controller (RCC) – Version 1

Version 1 of Third Person Controller (Ultimate Character Controller) and Bonecracker’s Realistc Car Controlelr Integration. As I only have the Third Person piece, I do not know how this will work with the First Person piece. There may be issues, but this is the current state of where it stands.

NOTE: Edit Project Settings – Physics: Deselect SubCharacter on the RCC line. Otherwise there will be collission issues because RCC has a complex OnCollissionEnter() section which is not necessary in my opinion.

This integration allows the player to control vehicles and enter/exit smoothly. I would like to enhance this to allow for passengers, changing seats, and using weapons while in a passenger seat. Feel free to share suggested updates with code.

Below is a rough How To document for Edy’s but it should be the same process. I did not elaborate on all the presets I created which include ones for the CameraController, CharacterIK, MoveWithObject, UltimateCharacterLocomotion. I also did not talk about the Animator, so watch the prior video for that piece.

Only Tested with UCC Version 2.1.5 and Unity 2019.1.

Below is the Vehicle Manager script to be added to each vehicle.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RCCVehicleManager : MonoBehaviour
{
    #region Realistic Car Controller Variables
    [HideInInspector]
    public RCC_CarControllerV3 m_RCCCarControllerV3;
    #endregion

    #region Variables - Public to be used by the UCC Ability System
    [Tooltip("Create a new prefab, add the FollowVehiclePosition script, and the Kinematic Object Script.  Then set the Layer to MovingPlatform.")]
    public GameObject FollowPrefab;
    [HideInInspector]
    public GameObject newFollowPrefab;
    [Tooltip("This is the position that the UCC Character will be moved to upon entering.  Make sure it doesn't stick through the floor.")]
    public Transform drivePosition;
    [Tooltip("This is the position that the UCC Character will be moved to upon entering.  Make sure it doesn't stick through the floor.")]
    public Transform lookPosition;
    [Tooltip("This is the position of the Left Hand.  If there is a steering wheel, add it there.")]
    public GameObject LeftHandIKTarget;
    [Tooltip("This is the position of the Right Hand.  If there is a steering wheel, add it there.")]
    public GameObject RightHandIKTarget;
    [Tooltip("This is the position of the Left Elbow.")]
    public GameObject LeftElbowIKTarget;
    [Tooltip("This is the position of the Right Elbow..")]
    public GameObject RightElbowIKTarget;
    [Tooltip("This is the position of the Left Foot.  Make sure it doesn't stick through the floor.")]
    public GameObject LeftFootIKTarget;
    [Tooltip("This is the position of the Right Foot.  Make sure it doesn't stick through the floor.")]
    public GameObject RightFootIKTarget;
    [Tooltip("This is the position of the Left Knee.  Make sure it doesn't stick through the door.")]
    public GameObject LeftKneeIKTarget;
    [Tooltip("This is the position of the Right Knee.  Make sure it doesn't stick through the door.")]
    public GameObject RightKneeIKTarget;
    #endregion

    // Start is called before the first frame update
    void Start()
    {
        #region Assign Realistic Car Controller Variables
        m_RCCCarControllerV3 = this.GetComponent<RCC_CarControllerV3>();
        #endregion

        #region Prevent the RCC Car from moving
        m_RCCCarControllerV3.SetCanControl(false);
        #endregion

        if (drivePosition == null)
        {
            Debug.Log("Drive Position was not assigned", gameObject);
            drivePosition = transform;
        }
        if (lookPosition == null)
        {
            Debug.Log("Look Position was not assigned", gameObject);
            lookPosition = transform;
        }

        //General IK Target Warnings
        if (LeftHandIKTarget == null)
            Debug.Log("Left Hand IK Target is not set, so IK Left Hand won't work on this ", gameObject);
        if (RightHandIKTarget == null)
            Debug.Log("Right Hand IK Target is not set, so IK Right Hand won't work on this ", gameObject);
        if (LeftElbowIKTarget == null)
            Debug.Log("Left Elbow IK Target is not set, so IK Left Elbow won't work on this ", gameObject);
        if (RightElbowIKTarget == null)
            Debug.Log("Right Elbow IK Target is not set, so IK Right Elbow won't work on this ", gameObject);
        if (LeftFootIKTarget == null)
            Debug.Log("Left Foot IK Target is not set, so IK Left Foot won't work on this ", gameObject);
        if (RightFootIKTarget == null)
            Debug.Log("Right Foot IK Target is not set, so IK Right Foot won't work on this ", gameObject);
        if (LeftKneeIKTarget == null)
            Debug.Log("Left Knee IK Target is not set, so IK Left Knee won't work on this ", gameObject);
        if (RightKneeIKTarget == null)
            Debug.Log("Right Knee IK Target is not set, so IK Right Knee won't work on this ", gameObject);

        NewObject();
    }

    void NewObject()
    {
        newFollowPrefab = Instantiate(FollowPrefab, drivePosition.position, drivePosition.rotation);
        newFollowPrefab.GetComponent<FollowVehiclePosition>().positionToFollow = drivePosition;
        newFollowPrefab.GetComponent<FollowVehiclePosition>().lookPosition = lookPosition;
    }
}

Below is the Vehicle Follow script to be added to a NEW prefab and assigned in the Vehicle Manager above in the Editor. (SAME AS EDY’s)

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
public class FollowVehiclePosition : MonoBehaviour
{
    public Transform positionToFollow = null;
    public Transform lookPosition = null;

    Rigidbody m_rigidbody = null;
    Vector3 m_rotateVector3 = new Vector3();
    
    void Start()
    {
        m_rigidbody = GetComponent<Rigidbody>();
        m_rigidbody.isKinematic = true;
        m_rigidbody.useGravity = false;
        m_rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
        m_rigidbody.angularDrag = 0f;
    }

    void FixedUpdate()
    {
        TransformRotation();
        TransformMove();
    }

    void TransformRotation()
    {
        m_rotateVector3 = new Vector3(lookPosition.position.x,
            transform.position.y,
            lookPosition.position.z);
        transform.LookAt(m_rotateVector3);
    }

    void TransformMove()
    {
        transform.position = positionToFollow.position;
    }
}

Below is the Ability which will automatically show up as an Ability Option on your character.

using UnityEngine;
using Opsive.UltimateCharacterController.Character.Abilities; //Used for DetectObjectAbilityBase
using Opsive.UltimateCharacterController.Character; //Used for the UCC Character IK
using Opsive.UltimateCharacterController.Objects.CharacterAssist; //Used for the UCC AbilityIKTarget
using Opsive.UltimateCharacterController.Character.Abilities.Items;

/// <summary>
/// Chris "RedHawk" Ferguson copyright 2019
/// </summary>

public class RCCDriveAbility : DetectObjectAbilityBase
{
    #region Realistic Car Controller Variables
    RCC_CarControllerV3 m_RCCCarControllerV3;
    RCCVehicleManager m_RCCVehicleManager;
    #endregion

    #region Variables - General
    Transform m_Vehicle;
    Collider[] m_OriginalColliders;
    GameObject InteractLocation;
    #endregion

    public override void Start()
    {
        base.Start();
    }

    //Prevent Ability from being prematurely stopped
    public override bool CanForceStopAbility()
    {
        return false;
    }

    /// <summary>
    /// The ability has started.
    /// </summary>
    protected override void AbilityStarted()
    {
        base.AbilityStarted();
        //DO GET IN STUFF
        #region Assign RCC's Vehicle Pieces
        m_Vehicle = m_DetectedObject.GetComponentInParent<RCC_CarControllerV3>().transform;
        m_RCCVehicleManager = m_Vehicle.GetComponentInChildren<RCCVehicleManager>();
        m_RCCCarControllerV3 = m_Vehicle.GetComponent<RCC_CarControllerV3>();
        #endregion

        #region UCC Colliders and Parenting
        //Locate the Colliders and make them IsTrigger TRUE
        m_OriginalColliders = m_CharacterLocomotion.Colliders;
        m_OriginalColliders[0].isTrigger = true;

        m_CharacterLocomotion.GetComponentInParent<UltimateCharacterLocomotion>().GetAbility<MoveWithObject>().Target = m_RCCVehicleManager.newFollowPrefab.transform;

        //Use the position contained in the Vehicle Manager
        if (m_RCCVehicleManager == null)
        {
            m_CharacterLocomotion.SetPosition(m_Vehicle.transform.position);
            m_CharacterLocomotion.SetRotation(m_Vehicle.transform.rotation);
            Debug.Log("There was no RCCVehicleManager on this Vehicle", m_GameObject);
        }
        else
        {
            m_CharacterLocomotion.SetPosition(m_RCCVehicleManager.newFollowPrefab.transform.position);
            m_CharacterLocomotion.SetRotation(m_RCCVehicleManager.newFollowPrefab.transform.rotation);
        }

        //IK Targets - TURN ON
        CharacterIK m_characterIK = m_CharacterLocomotion.gameObject.GetComponent<CharacterIK>();
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.LeftFootIKTarget.transform, m_RCCVehicleManager.LeftFootIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.RightFootIKTarget.transform, m_RCCVehicleManager.RightFootIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.LeftHandIKTarget.transform, m_RCCVehicleManager.LeftHandIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.RightHandIKTarget.transform, m_RCCVehicleManager.RightHandIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.LeftKneeIKTarget.transform, m_RCCVehicleManager.LeftKneeIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.RightKneeIKTarget.transform, m_RCCVehicleManager.RightKneeIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.LeftElbowIKTarget.transform, m_RCCVehicleManager.LeftElbowIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(m_RCCVehicleManager.RightElbowIKTarget.transform, m_RCCVehicleManager.RightElbowIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        #endregion

        #region RCC's Vehicle Stuff
        m_RCCCarControllerV3.SetCanControl(true);
        m_RCCCarControllerV3.StartEngine();
        #endregion
        InteractLocation = m_DetectedObject;
    }

    /// <summary>
    /// The Ability has ended
    /// </summary>
    /// <param name="force"></param>
    protected override void AbilityStopped(bool force)
    {
        base.AbilityStopped(force);
        //DO GET OUT STUFF

        #region RCC's Vehicle Stuff
        //Stop the vehicle and put the brakes on
        m_RCCCarControllerV3.KillEngine();
        m_RCCCarControllerV3.SetCanControl(false);
        #endregion

        #region UCC Colliders and Positioning
        m_CharacterLocomotion.GetComponentInParent<UltimateCharacterLocomotion>().GetAbility<MoveWithObject>().Target = null;

        m_CharacterLocomotion.SetPosition(InteractLocation.transform.position);
        m_CharacterLocomotion.SetRotation(InteractLocation.transform.rotation);

        //Turn Colliders back to Trigger On
        m_OriginalColliders[0].isTrigger = false;

        //IK Targets - TURN OFF
        CharacterIK m_characterIK = m_CharacterLocomotion.gameObject.GetComponent<CharacterIK>();
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.LeftFootIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.RightFootIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.LeftHandIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.RightHandIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.LeftKneeIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.RightKneeIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.LeftElbowIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        m_characterIK.SetAbilityIKTarget(null, m_RCCVehicleManager.RightElbowIKTarget.GetComponent<AbilityIKTarget>().Goal, 0f);
        #endregion
    }

    public override bool CanStopAbility()
    {
        return ExitDriverBlocked(InteractLocation.GetComponent<Collider>());
    }

    private bool ExitDriverBlocked(Collider exitCollider)
    {
        Collider[] overlap = new Collider[1];
        int hitCount = 0;
        if (exitCollider is BoxCollider)
        {
            var boxCollider = exitCollider as BoxCollider;
            hitCount = Physics.OverlapBoxNonAlloc(exitCollider.transform.TransformPoint(boxCollider.center), Vector3.Scale(boxCollider.size, boxCollider.transform.lossyScale) / 2,
                                overlap, exitCollider.transform.rotation, m_CharacterLayerManager.SolidObjectLayers, QueryTriggerInteraction.Ignore);
        }
        if (hitCount > 0)
            return false;
        else
            return true;
    }

    public override bool ShouldBlockAbilityStart(Ability startingAbility)
    {
        return startingAbility is ItemAbility;
    }
}

As always, feel free to make suggestions on how I can improve this.  Please make those suggestions on the Opsive Forum under my RCC integrations post. https://opsive.com/forum/index.php?threads/realistic-car-controller-by-bonecracker-games.799/