Создание гонок. Глава 2 (1)

0 2

Всем привет! Вот и 2 глава, в этой части мы с вами сделаем место прохождения гонок, и если мы въедем в определенную точку, то откроется окно, с настройками гонки. И так давайте начнем!

Создание места прохождения гонок

Итак давайте добавим гараж, и если мы к нему приблизимся, то откроется окно. Скачайте и импортируйте ассет Brick garages. И поставьте гараж, куда хотите. Возле гаража создайте куб, и увеличьте его так:

Теперь, зайдите в наш куб, найдите компонент Mesh Renderer и удалите его (3 точки — Remove Component). Найдите у нашего куба Box Collider и поставьте галочку Is Trigger (это будет нашим триггером, если мы войдем в него, то откроется окно). Теперь добавьте ему новый тэг:

Нажмите Tag — Add Tag — плюс- напишите: Race_Trigger — Save. Нажмите еще раз на наш куб и нажмите Tag — Race_Trigger. Таким образом вы применили тэг.

Написание кода

Зайдите в наш скрипт автомобиля, и измените наш метод Start, также создайте новую переменную, и в конце создайте новый метод, впишите это:

[SerializeField] private GameObject racePanel; // Добавьте переменную типа GameObject
private void Start()
    {
        _rigidbody = GetComponent<Rigidbody>();
        racePanel.SetActive(false); // Добавьте это
    }
...
private void OnTriggerEnter(Collider collider) // Этот метод, должен быть в конце.
    {
        if(collider.tag == "Race_Trigger")
        {
            racePanel.SetActive(true);
        }
    }

Итак теперь вернитесь в юнити, и вы заметите, что у нашего автомобиля появилась новая переменная. Создайте UI — Panel

Примерно вот так. Теперь отключите ее, убрав галочку возле Tag. Вот, уберите галочку:

Укажите ссылку на нашу панель. И запускайте. Теперь если мы въехали в наш триггер, у нас открывается панель. Добавим несколько UI:

Давайте создадим новый скрипт под название RacePanelManager. Добавьте следующие строки:

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

public class RacePanelManager : MonoBehaviour
{
    public void EasyRace()
    {
        SceneManager.LoadScene(2);
    }

    public void NormallyRace()
    {
        SceneManager.LoadScene(3);
    }

    public void DifficultRace()
    {
        SceneManager.LoadScene(4);
    }
}

Теперь надо создать пустой объект с название RacePanelManager и туда добавим наш скрипт. Теперь нажимаем на кнопку с легкой сложность в On Click() нажимаем плюс, и перетаскиваем туда наш RacePanelManager (пустой объект) и нажимаем на No Function — RacePanelManager — EasyRace(). Для нормальной сложности — NormallyRace(). Для сложной сложности — DifficultRace(). Теперь создайте пустую сцену с названием MainMenu. И продублируйте нашу сцену 3 раза (Ctrl + D).

Теперь нажимаем — File — Build Settings — добавляем наши сцены. 0 — MainMenu. 1 — Word. 2 — EasyRace. 3 — NormallyRace. 4 — DifficultRace. Вот:

Кстати сцена Word (эта наша сцена с автомобилем). Теперь можно запускать.

Фикс багов.

Теперь у нас при появлении панели, у нас блокируется курсор, для этого впишем следующие строчки:

[System.Serializable]
        public enum UpdateMode
        {
 Update,
 FixedUpdate,
 LateUpdate,
 FixedLateUpdate
        }
 
        public Transform target; // The target Transform to follow
        public Transform rotationSpace; // If assigned, will use this Transform's rotation as the rotation space instead of the world space. Useful with spherical planets.
        public UpdateMode updateMode = UpdateMode.LateUpdate; // When to update the camera?
        public bool lockCursor = true; // If true, the mouse will be locked to screen center and hidden
 
        [Header("Position")]
        public bool smoothFollow; // If > 0, camera will smoothly interpolate towards the target
        public Vector3 offset = new Vector3(0, 1.5f, 0.5f); // The offset from target relative to camera rotation
        public float followSpeed = 10f; // Smooth follow speed
 
        [Header("Rotation")]
        public float rotationSensitivity = 3.5f; // The sensitivity of rotation
        public float yMinLimit = -20; // Min vertical angle
        public float yMaxLimit = 80; // Max vertical angle
        public bool rotateAlways = true; // Always rotate to mouse?
        public bool rotateOnLeftButton; // Rotate to mouse when left button is pressed?
        public bool rotateOnRightButton; // Rotate to mouse when right button is pressed?
        public bool rotateOnMiddleButton; // Rotate to mouse when middle button is pressed?
 
        [Header("Distance")]
        public float distance = 10.0f; // The current distance to target
        public float minDistance = 4; // The minimum distance to target
        public float maxDistance = 10; // The maximum distance to target
        public float zoomSpeed = 10f; // The speed of interpolating the distance
        public float zoomSensitivity = 1f; // The sensitivity of mouse zoom
 
        [Header("Blocking")]
        public LayerMask blockingLayers;
        public float blockingRadius = 1f;
        public float blockingSmoothTime = 0.1f;
        public float blockingOriginOffset;
        [Range(0f, 1f)] public float blockedOffset = 0.5f;
 
        public float x { get; private set; } // The current x rotation of the camera
        public float y { get; private set; } // The current y rotation of the camera
        public float distanceTarget { get; private set; } // Get/set distance
 
        private Vector3 targetDistance, position;
        private Quaternion rotation = Quaternion.identity;
        private Vector3 smoothPosition;
        private Camera cam;
        private bool fixedFrame;
        private float fixedDeltaTime;
        private Quaternion r = Quaternion.identity;
        private Vector3 lastUp;
        private float blockedDistance = 10f, blockedDistanceV;
 
        public void SetAngles(Quaternion rotation)
        {
            Vector3 euler = rotation.eulerAngles;
            this.x = euler.y;
            this.y = euler.x;
        }
 
        public void SetAngles(float yaw, float pitch)
        {
            this.x = yaw;
            this.y = pitch;
        }
 
        // Initiate, set the params to the current transformation of the camera relative to the target
        protected virtual void Awake()
        {
            Vector3 angles = transform.eulerAngles;
            x = angles.y;
            y = angles.x;
 
            distanceTarget = distance;
            smoothPosition = transform.position;
 
            cam = GetComponent<Camera>();
 
            lastUp = rotationSpace != null ? rotationSpace.up : Vector3.up;

            Cursor.lockState = CursorLockMode.Locked;
        }
 
        protected virtual void Update()
        {
            if (updateMode == UpdateMode.Update) UpdateTransform();
        }
 
        protected virtual void FixedUpdate()
        {
            fixedFrame = true;
            fixedDeltaTime += Time.deltaTime;
            if (updateMode == UpdateMode.FixedUpdate) UpdateTransform();
        }
 
        protected virtual void LateUpdate()
        {
            UpdateInput();
 
            if (updateMode == UpdateMode.LateUpdate) UpdateTransform();
 
            if (updateMode == UpdateMode.FixedLateUpdate && fixedFrame)
            {
                UpdateTransform(fixedDeltaTime);
                fixedDeltaTime = 0f;
                fixedFrame = false;
            }
        }
 
        // Read the user input
        public void UpdateInput()
        {
            if (!cam.enabled) return;
 
 
            // Should we rotate the camera?
            bool rotate = rotateAlways || (rotateOnLeftButton && Input.GetMouseButton(0)) || (rotateOnRightButton && Input.GetMouseButton(1)) || (rotateOnMiddleButton && Input.GetMouseButton(2));
 
            // delta rotation
            if (rotate)
            {
                x += Input.GetAxis("Mouse X") * rotationSensitivity;
                y = ClampAngle(y - Input.GetAxis("Mouse Y") * rotationSensitivity, yMinLimit, yMaxLimit);
            }
 
            // Distance
            distanceTarget = Mathf.Clamp(distanceTarget + zoomAdd, minDistance, maxDistance);
        }
 
        // Update the camera transform
        public void UpdateTransform()
        {
            UpdateTransform(Time.deltaTime);
        }
 
        public void UpdateTransform(float deltaTime)
        {
            if (!cam.enabled) return;
 
            // Rotation
            rotation = Quaternion.AngleAxis(x, Vector3.up) * Quaternion.AngleAxis(y, Vector3.right);
 
            if (rotationSpace != null)
            {
                r = Quaternion.FromToRotation(lastUp, rotationSpace.up) * r;
                rotation = r * rotation;
 
                lastUp = rotationSpace.up;
 
            }
 
            if (target != null)
            {
                // Distance
                distance += (distanceTarget - distance) * zoomSpeed * deltaTime;
 
                // Smooth follow
                if (!smoothFollow) smoothPosition = target.position;
                else smoothPosition = Vector3.Lerp(smoothPosition, target.position, deltaTime * followSpeed);
 
                // Position
                Vector3 t = smoothPosition + rotation * offset;
                Vector3 f = rotation * -Vector3.forward;
 
                if (blockingLayers != -1)
                {
                    RaycastHit hit;
                    if (Physics.SphereCast(t - f * blockingOriginOffset, blockingRadius, f, out hit, blockingOriginOffset + distanceTarget - blockingRadius, blockingLayers))
                    {
                        blockedDistance = Mathf.SmoothDamp(blockedDistance, hit.distance + blockingRadius * (1f - blockedOffset) - blockingOriginOffset, ref blockedDistanceV, blockingSmoothTime);
                    }
                    else blockedDistance = distanceTarget;
 
                    distance = Mathf.Min(distance, blockedDistance);
                }
 
                position = t + f * distance;
 
                // Translating the camera
                transform.position = position;
            }
 
            transform.rotation = rotation;
        }
 
        // Zoom input
        private float zoomAdd
        {
            get
            {
                float scrollAxis = Input.GetAxis("Mouse ScrollWheel");
                if (scrollAxis > 0) return -zoomSensitivity;
                if (scrollAxis < 0) return zoomSensitivity;
                return 0;
            }
        }
 
        // Clamping Euler angles
        private float ClampAngle(float angle, float min, float max)
        {
            if (angle < -360) angle += 360;
            if (angle > 360) angle -= 360;
            return Mathf.Clamp(angle, min, max);
        }

Измените код CameraController на это (здесь немного переделано). И зайдите в скрипт CarController, и измените:

private void OnTriggerEnter(Collider collider)
    {
        if(collider.tag == "Race_Trigger")
        {
            racePanel.SetActive(true);
            Cursor.lockState = CursorLockMode.None;
        }
    }

Все теперь при столкновении с коллайдером, у нас откроется панель, и разблокируется курсор, что позволит нам зайти на гонку.

Карта

Теперь нам надо скачать трек, вот: Racetrack — Karting Microgame Add-Ons скачайте и импортируйте его в наш проект. Теперь найдите папку Scenes в папке AddOns и выберите любой трек, скопируйте его, и перенесите его на каждую сцену сложности. При этом удалив террейн, дороги, триггер с гаражом. А теперь зайдите в канвас и удалите там RacePanel. Также вам придется сделать другой скрипт для автомобиля, вот он: ВНИМАНИЕ! Это второй скрипт по управлению автомобиля. Первый скрипт не удаляйте! На каждую сцену сложности замените первый скрипт на вот этот:

[SerializeField] private Transform _transformFL;
    [SerializeField] private Transform _transformFR;
    [SerializeField] private Transform _transformBL;
    [SerializeField] private Transform _transformBR;
 
    [SerializeField] private WheelCollider _colliderFL;
    [SerializeField] private WheelCollider _colliderFR;
    [SerializeField] private WheelCollider _colliderBL;
    [SerializeField] private WheelCollider _colliderBR;
 
    [SerializeField] private float _force;
    [SerializeField] private float _maxAngle;
    [SerializeField] private float speed;
    [SerializeField] private Text speedText;

    private Rigidbody _rigidbody;
    // Start is called before the first frame update

    private void Start()
    {
        _rigidbody = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        speed = Mathf.RoundToInt(_rigidbody.velocity.magnitude * 3.45f);
        speedText.text = speed + "KM/H";
    }
 
    private void FixedUpdate()
    {
        _colliderFL.motorTorque = Input.GetAxis("Vertical") * _force;
        _colliderFR.motorTorque = Input.GetAxis("Vertical") * _force;
 
        if (Input.GetKey(KeyCode.Space))
        {
            _colliderFL.brakeTorque = 3000f;
            _colliderFR.brakeTorque = 3000f;
            _colliderBL.brakeTorque = 3000f;
            _colliderBR.brakeTorque = 3000f;
        }
        else
        {
            _colliderFL.brakeTorque = 0f;
            _colliderFR.brakeTorque = 0f;
            _colliderBL.brakeTorque = 0f;
            _colliderBR.brakeTorque = 0f;
        }
 
        _colliderFL.steerAngle = _maxAngle * Input.GetAxis("Horizontal");
        _colliderFR.steerAngle = _maxAngle * Input.GetAxis("Horizontal");
 
        RotateWheel(_colliderFL, _transformFL);
        RotateWheel(_colliderFR, _transformFR);
        RotateWheel(_colliderBL, _transformBL);
        RotateWheel(_colliderBR, _transformBR);

        _rigidbody.centerOfMass = new Vector3(0f, -0.3f, 0f); // Adjust the Y value as needed
    }
 
    private void RotateWheel(WheelCollider collider, Transform transform)
    {
        Vector3 position;
        Quaternion rotation;
 
        collider.GetWorldPose(out position, out rotation);
 
        transform.rotation = rotation;
        transform.position = position;
    }

Конечно, мы могли бы просто сделать отдельный скрипт для столкновения машины с коллайдером, но лучше сделать второй скрипт, так как в этом скрипте будут добавлены новые механики, которых не будет в первом скрипте, к примеру: нитро. Первый скрипт должен быть применен только в сцене word. Также необходимо поставить слой Ground на каждую дорогу. Можно добавить к камере режим Solid Color, для этого перейдите к камере, и поменяйте SkyBox на SolidColor и измените на любой другой цвет, тем самым вы получите такой эффект:

Очень красиво!

Оставьте ответ

Ваш электронный адрес не будет опубликован.