Помогите доработать скрипт синхронизации игровых объектов

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

Помогите доработать скрипт синхронизации игровых объектов

Сообщение skDYLAN 13 фев 2018, 14:34

Всем привет. Я работаю над скриптом синхронизации объектов в unet и никак не могу решить проблему гладкого передвижения объекта, данный скрипт получает от сервера местоположение объекта, все хорошо, интерполяция работает вроде как надо, но наблюдаются рывки, вместо гладкого передвижения. Может кто-то посмотрим свежим взглядом и найдет изъян в коде.

Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class SyncObj : NetworkBehaviour {

        public NetGM_Player netGM_Player = null;
        float maxRate = 0.06f; // частота отправки кадров

        Vector3 lastPosition = new Vector3 (0, 100, 0);

        Vector3 endPosition = new Vector3();
        Quaternion endRotation = new Quaternion();
        Vector3 startMarker_Position = new Vector3();
        Quaternion startMarker_Rotation = new Quaternion();
        float endTime = 0;
        float prec=0;

        float numscr = 0;

        float lastSendScren = 0;

        public float delta =0;

        Vector3 oldPosServ = new Vector3();
        Quaternion oldRotServ = new Quaternion();

        Vector3 newPosServ = new Vector3();
        Quaternion newRotServ = new Quaternion();

        // client
        List<ScreenTransform> _scrinsTransformPlayer = new List<ScreenTransform>();
        int numScreen = 0;

        // Use this for initialization
        void Start () {
                //запросить текущее положение объекта
                if (isClient)
                        CmdSyncStartPos (transform.position, transform.rotation);
        }

        // Update is called once per frame
        void Update () {
                Client ();
        }

        void FixedUpdate()
        {
                ClientUpdate ();
                ServerFixedUpdate ();
        }

        //запросить текущую позицию объекта у сервера
        [Command]
        void CmdSyncStartPos(Vector3 pos, Quaternion rot)
        {
                if (transform.position != pos || transform.rotation != rot) {
                        RpcSendAllNewTransform (transform.position, transform.rotation, Time.time);
                }
        }
        [ClientRpc]
        void RpcSendAllNewTransform(Vector3 newPosition, Quaternion newRotation, float time)
        {
                // добавить новый кадр в очередь
                numScreen++;
                //Debug.Log ("Номер: " + numScreen + " Время: " + Time.fixedTime);
                _scrinsTransformPlayer.Add (new ScreenTransform(gameObject.GetInstanceID(), newPosition, newRotation, time, numScreen));
        }

        //GM_local_main объект к которому игрок имеет доступ, в данном объекте содержется синхронизированное с сервером время,
        //получаемое через метод GetServerTime();
        void Client()
        {
                if (!isClient)
                        return;

                transform.position = newPosServ;
        }

        void ClientUpdate()
        {
                if (!isClient)
                        return;

                if (netGM_Player == null) {
                        GameObject obj = GameObject.Find ("GM_local_main");
                        if (obj != null) {
                                netGM_Player = obj.GetComponent<NetGM_Player> ();
                        }
                }


                if (netGM_Player != null) {

                        //интерполяция
                        //Если скрины есть в очереди
                        if (_scrinsTransformPlayer.Count > 0)
                        {

                                if (SerchScreen ()) {

                                        startMarker_Position = transform.position;

                                        //конечная позиция берется из кадра
                                        endPosition = _scrinsTransformPlayer [0].position;

                                        numscr = _scrinsTransformPlayer [0].number;

                                        startMarker_Rotation = transform.rotation;
                                        endRotation = _scrinsTransformPlayer [0].rotation;
                                        //время к которому объект должен достичь своей точки
                                        endTime = _scrinsTransformPlayer [0].time;
                                        //удалить кадр из учереди
                                                _scrinsTransformPlayer.RemoveAt (0);

                                }

                        }
                        //рассчитываем какой процент от всего пути объект должен был сделать к текущему времени
                        float test = netGM_Player.GetServerTime () + Time.fixedTime;
                        prec = ((netGM_Player.GetServerTime () + Time.fixedTime - maxRate*2) - (endTime)) / (maxRate);

                        Debug.Log ("процент: " + prec + " Текущее время: " + (netGM_Player.GetServerTime() + Time.fixedTime - maxRate*2) + " endTime " + endTime + " номер " + numscr);

                        //интерполируем положение объекта
                        newPosServ = Vector3.Lerp (startMarker_Position, endPosition, prec);
                        transform.rotation = Quaternion.Slerp (startMarker_Rotation, endRotation, prec);
                }

        }

        void ServerFixedUpdate()
        {
                if (!isServer)
                        return;

                if (lastSendScren == 0) {

                        oldPosServ = transform.position;
                        oldRotServ = transform.rotation;
                        // 3 цикла: 3 * 0.2 = 0.6
                        lastSendScren = 2;
                        RpcSendAllNewTransform (transform.position, transform.rotation, Time.fixedTime);
                } else
                        lastSendScren--;
        }

        // поиск подходящего кадра
        bool SerchScreen()
        {
                bool find = false;
                int k = 0;
                for (int i = 0; i < _scrinsTransformPlayer.Count; i++) {

                        // текущее серверное время - задержка в 2 maxRate
                        if (netGM_Player.GetServerTime () + Time.fixedTime - maxRate*2 >= _scrinsTransformPlayer [i].time
                        ){
                                        k = i;
                                        find = true;
                        }
                }
                if (find)
                        for (int j = 0; j < k - 1; j++) {
                                _scrinsTransformPlayer.RemoveAt (0);
                        }
                return find;
        }
}


Складывается впечатление, что проблема не в этом скрипте, а в какой то особой механики Unity. Если посмотреть начало данного видео https://www.youtube.com/watch?v=co_ACWIu4XU&t то в нем как раз можно увидеть мою проблему, объект синхронизирован, но движется рывками, хотя интерполяция работает хорошо.
skDYLAN
UNец
 
Сообщения: 30
Зарегистрирован: 04 окт 2017, 13:30

Re: Помогите доработать скрипт синхронизации игровых объектов

Сообщение skDYLAN 15 фев 2018, 00:29

Я решил данную проблему заменив Vector3.lerp на Vector3.SmoothDamp и использованием UDP, правда пришлось чутка доработать обработку кадров. При использовании Смуза управление становится менее приятным, она становится более тугим, но если добавить в скрипт опережающий ввод, то смуз можно сделать меньше и управление будет нормальным.
skDYLAN
UNец
 
Сообщения: 30
Зарегистрирован: 04 окт 2017, 13:30


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

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

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