Как сделать чтобы объект повторял движение другого объекта

Программирование на Юнити.

Как сделать чтобы объект повторял движение другого объекта

Сообщение Merfik933 21 окт 2022, 21:32

Здравствуйте, мастера кодинга!
У меня к Вам довольно массивный вопрос. Я делаю что-то наподобие 3д змейки. Змейка - набор кубиков которые двигаются один за одним. Змейка двигается вперед, а при нажатии 'a' и 'd' - влево и вправо. Проблема в том что я не могу сделать движение кубов тела за кубом головы. Если через MoveTorwards то части тела передвигаются напрямую к голове не проходя весь ее путь. Я пробовал сделать как-то через переменную-вектор LastTurn которая сохраняет последний поворот головы и отдельных частей тела, для головы и первой части тела это работает, но для всех других - нет. Если у Вас есть идеи как реализовать это по-другому или как исправить мой скрипт, то посоветуйте пожалуйста.
Вот код. (Простите за этот говнокод)))
Код головы: (висит на snake)
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class PlayerController : MonoBehaviour
{
    public float speed;
    public bool testModePC = false; //переменная для теста на пк (игра на андроид)
    public Transform player; //голова + камера
    public Transform playerHead; //голова
    public Transform playerBodyParent; //объект с кубиками тела

    private Vector3 beginCooldown;                       //
    private Sides cooldownSide = Sides.zero;          //
    private bool canIturnLeft;                                // переменные для кулдауна - чтобы игрок не мог поворачивать слишком часто
    private bool canIturnRight;                              //
    private bool canIturnFoward;                           //

    public Vector3 lastTurn; //сохраняет позицию последнего поворота

    private void FixedUpdate() {
        player.position += playerHead.forward * Time.deltaTime * speed;

        CheckCooldown();

        if(Input.touchCount == 1){
            if(Input.GetTouch(0).position.x <= Screen.width / 2 && canIturnLeft){
                if(playerHead.rotation != Quaternion.Euler(0, -90, 0)) {SetCooldown(-90); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
                playerHead.rotation = Quaternion.Euler(0, -90, 0);
            }
            else if(Input.GetTouch(0).position.x >= Screen.width / 2 && canIturnRight) {
                if(playerHead.rotation != Quaternion.Euler(0, 90, 0)) {SetCooldown(90); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
                playerHead.rotation = Quaternion.Euler(0, 90, 0);
            }
        }
        else if(testModePC == false && canIturnFoward){
            if(playerHead.rotation != Quaternion.Euler(0, 0, 0)) {SetCooldown(0); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
            playerHead.rotation = Quaternion.Euler(0, 0, 0); //fix in release
        }
       
        if(testModePC) //delete in release  //from
            if(Input.GetKey("a") && canIturnLeft) {
                if(playerHead.rotation != Quaternion.Euler(0, -90, 0)) {SetCooldown(-90); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
                playerHead.rotation = Quaternion.Euler(0, -90, 0);
                canIturnRight = false;
            }
            else if(Input.GetKey("d") && canIturnRight) {
                if(playerHead.rotation != Quaternion.Euler(0, 90, 0)) {SetCooldown(90); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
                playerHead.rotation = Quaternion.Euler(0, 90, 0);
                canIturnLeft = false;
            }
            else if (canIturnFoward) {
                if(playerHead.rotation != Quaternion.Euler(0, 0, 0)) {SetCooldown(0); lastTurn = player.position; transform.parent.GetChild(1).GetChild(0).GetComponent<BodyController>().isIFolow = true;}
                playerHead.rotation = Quaternion.Euler(0, 0, 0);
            }                               //to
    }

    public void SetCooldown(int turnAngle){
        beginCooldown = player.position;
        switch(turnAngle){
            case -90: cooldownSide = Sides.left; break;
            case 90: cooldownSide = Sides.right; break;
            case 0: cooldownSide = Sides.forward; break;
        }
    }

    private void CheckCooldown(){
        if(cooldownSide != Sides.zero){
            switch(cooldownSide){
                case Sides.left: if(beginCooldown.x - player.position.x >= 1) canIturnFoward = true; else canIturnFoward = false; break;
                case Sides.right: if(beginCooldown.x - player.position.x <= -1) canIturnFoward = true; else canIturnFoward = false; break;
                case Sides.forward:
                if(beginCooldown.z - player.position.z <= -1){
                    canIturnLeft = true;
                    canIturnRight = true;
                }
                else{
                    canIturnLeft = false;
                    canIturnRight = false;
                }
                break;
            }
        }
        else{
            canIturnLeft = true;
            canIturnRight = true;
            canIturnFoward = true;
        }
    }

    public enum Sides{
        left, right, forward, zero
    }
}


код с всех частей тела: висит на всех part
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BodyController : MonoBehaviour
{
    public Vector3 targetVector;
    public bool isIFolow = true;
    private float speed;
    public Vector3 lastTurn;

    private void Awake() {
        speed = transform.parent.parent.GetChild(0).GetComponent<PlayerController>().speed;
        lastTurn = transform.position;
    }

    private void FixedUpdate() {
        Transform Target;
        if(transform.GetSiblingIndex() == 0){
            Target = transform.parent.parent.GetChild(0);
            if(transform.position != Target.GetComponent<PlayerController>().lastTurn && isIFolow){
                transform.position = Vector3.MoveTowards(transform.position, Target.GetComponent<PlayerController>().lastTurn, speed * Time.deltaTime);
            }
            else{
                if(transform.position == Target.GetComponent<PlayerController>().lastTurn) {
                    lastTurn = transform.position;
                    transform.parent.GetChild(1).GetComponent<BodyController>().isIFolow = true;
                    isIFolow = false;
                }
                transform.position = Vector3.MoveTowards(transform.position, Target.position, speed * Time.deltaTime);
            }
        }
        else{
            Target = transform.parent.GetChild(transform.GetSiblingIndex() - 1);
            if(transform.position != Target.GetComponent<BodyController>().lastTurn && isIFolow){
                transform.position = Vector3.MoveTowards(transform.position, Target.GetComponent<BodyController>().lastTurn, speed * Time.deltaTime);
            }
            else{
                if(transform.position == Target.GetComponent<BodyController>().lastTurn){
                    lastTurn = transform.position;
                    isIFolow = false;
                    if(transform.parent.childCount != transform.GetSiblingIndex() + 1) transform.parent.GetChild(transform.GetSiblingIndex() + 1).GetComponent<BodyController>().isIFolow = true;
                }    
                transform.position = Vector3.MoveTowards(transform.position, Target.position, speed * Time.deltaTime);
            }
        }
    }
}


https://drive.google.com/file/d/1swNGiy3hfwdNLeK7sTiBGEw-Q1s0X6Iy/view?usp=sharing
Аватара пользователя
Merfik933
UNец
 
Сообщения: 35
Зарегистрирован: 30 сен 2021, 16:10
Откуда: Украина

Re: Как сделать чтобы объект повторял движение другого объекта

Сообщение 1max1 22 окт 2022, 14:28

Нужно повторять прошлые позиции части, которая идет после текущей.

Создаем один управляющий скрипт, который будет управлять всеми частями змейки и её головой. У него будет ссылка на голову и список всех частей змеи. Можно сделать скрипт-компонент и вешать его на каждую часть отдельно, но мне кажется так будет в разы сложнее.

Теперь представим что голова/часть делает шаг вперед + вправо. Что должна сделать предыдущая часть? Можно сделать простое следование:

Синтаксис:
Используется csharp
part.position = nextPart.position + (part.position - nextPart.position).normalized * offset;


Но это вызовет эффект цепочки как в реальной жизни. Если взять цепочку и поводить ею по столу, то все предыдущие звенья не будут идеально следовать за своим следующим звеном. Поэтому нужно именно повторять позиции, а не просто следовать за следующей частью.

Получается у каждой части и у головы должен быть список своих прошлых позиций, история. Наверное тут больше подойдет словарь, где ключ это объект, а значение - список всех его последних позиций.

Дальше циклом по каждой части двигаем ее к первой позиции из истории позиций следующей части. Если достигаем этой позиции, то удаляем ее и едем к следующей. Важно учитывать дистанцию между частями чтобы они не упирались друг в друга. Если следующая часть находится слишком близко к текущей, то никуда не едем, стоим ждем :D
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Как сделать чтобы объект повторял движение другого объекта

Сообщение Merfik933 22 окт 2022, 15:20

1max1 писал(а):Нужно повторять прошлые позиции части, которая идет после текущей.

Создаем один управляющий скрипт, который будет управлять всеми частями змейки и её головой. У него будет ссылка на голову и список всех частей змеи. Можно сделать скрипт-компонент и вешать его на каждую часть отдельно, но мне кажется так будет в разы сложнее.

Теперь представим что голова/часть делает шаг вперед + вправо. Что должна сделать предыдущая часть? Можно сделать простое следование:

Синтаксис:
Используется csharp
part.position = nextPart.position + (part.position - nextPart.position).normalized * offset;


Но это вызовет эффект цепочки как в реальной жизни. Если взять цепочку и поводить ею по столу, то все предыдущие звенья не будут идеально следовать за своим следующим звеном. Поэтому нужно именно повторять позиции, а не просто следовать за следующей частью.

Получается у каждой части и у головы должен быть список своих прошлых позиций, история. Наверное тут больше подойдет словарь, где ключ это объект, а значение - список всех его последних позиций.

Дальше циклом по каждой части двигаем ее к первой позиции из истории позиций следующей части. Если достигаем этой позиции, то удаляем ее и едем к следующей. Важно учитывать дистанцию между частями чтобы они не упирались друг в друга. Если следующая часть находится слишком близко к текущей, то никуда не едем, стоим ждем :D

Спасибо, огромное! Я понял как можно сделать это ^:)^
Аватара пользователя
Merfik933
UNец
 
Сообщения: 35
Зарегистрирован: 30 сен 2021, 16:10
Откуда: Украина


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6