Birlik simulyatsiya muhitida - katta hisoblash amalga oshirilganda Update () ilovasini kechiktirishi

Men "Birlik" uchun juda yangiman va hozirda uni uchuvchi tadqiqot tadqiqotlari uchun sodda simulyatsiya muhiti yaratish uchun vosita sifatida foydalanmoqdaman. Bu erda soddalashtirilgan tushuntirish:

Asosiy belgilar (agent) muhitni ko'zdan kechiradi va TCP soket orqali davlatni (JSON obyekti sifatida) bir python serveriga yuboradi.

Python server tomonida davlat hisobiga katta miqdorda hisoblash amalga oshiriladi. Natijada, TCP orqali Unity orqali qaytarib yuborilgan xatti-harakatlar.

Belgilar ushbu amalni "Unity" da bajarishi kerak va keyin yana muhiti ko'rish kerak. Yangi davlat yana python serveriga yuboriladi.

Ushbu jarayon "abadiygacha" takrorlanmoqda (agar mijoz yoki server to'xtatilmasa), oxir-oqibat, agentga o'z bilimini o'rganishga asoslangan xulq-atvorni yetkazadi.

Men pitondagi ta'lim algoritmini dasturlash bilan bir qatorda atrof-muhitni yaratishni tugatdim. Bundan tashqari, ikkalasi o'rtasida TCP aloqasini o'rnatdim. Biroq, Unity-dagi asosiy Update() funktsiyasi muammosiga duch keldim.

Ya'ni, Ping (davlat bo'lgan) va python pong yuborish (jarayoni) yuborish Birlik jarayonini soddalashtirsam, takrorlash uchun quyidagi jarayonga muhtojman.

  • ramkani muzlatish
  • ping
  • hisoblash
  • pong
  • keyingi ramka

Shunday qilib, Unity Start() usulida rozetkalarni sozlash va dastlabki pingni yuborish. Asosiy Update() pastki qismida nimani xohlashim - Frityni yangilamasdan oldin Unity python pong javobini kutish. Men o'yinchoq misolini o'z ichiga olgan skriptni yaratdim va pongni hisobga olish vaqtini pythonda simulyatsiya qilishga urinib ko'rdim.

Unity skriptining kodi (faqat asosiy kameraga ulang):

 using UnityEngine;
 using System;
 using System.IO;
 using System.Net.Sockets;

 public class networkSocketPingPong : MonoBehaviour
 {
     public String host = "localhost";
     public Int32 port = 50000;

     internal Boolean socket_ready = false;
     internal String input_buffer = "";

     TcpClient tcp_socket;
     NetworkStream net_stream;

     StreamWriter socket_writer;
     StreamReader socket_reader;

     private void Start()
     {
         setupSocket();
         writeSocket("ping");
     }


      void Update()
      {

         string received_data = readSocket();

         switch (received_data)
              {
                  case "pong":
                      Debug.Log("Python controller sent: " + (string)received_data);
                      writeSocket("ping");
                      break;
                  default:
                      Debug.Log("Nothing received from Python");
                      break;
          }
     }

     void OnApplicationQuit()
     {
         closeSocket();
     }

    //Helper methods for:
     //...setting up the communication
     public void setupSocket()
     {
         try
         {
             tcp_socket = new TcpClient(host, port);
             net_stream = tcp_socket.GetStream();
             socket_writer = new StreamWriter(net_stream);
             socket_reader = new StreamReader(net_stream);
             socket_ready = true;
         }
         catch (Exception e)
         {
            //Something went wrong
             Debug.Log("Socket error: " + e);
         }
     }

     //... writing to a socket...
     public void writeSocket(string line)
     {
         if (!socket_ready)
             return;

         socket_writer.Write(line);
         socket_writer.Flush();
     }

     //... reading from a socket...
     public String readSocket()
     {
        if (!socket_ready)
         {
             Debug.Log("Socket is not ready");
             return "";
         }

         if (net_stream.DataAvailable)
             return socket_reader.ReadLine();

         return "";
     }

     //... closing a socket...
     public void closeSocket()
     {
         if (!socket_ready)
             return;

         socket_writer.Close();
         socket_reader.Close();
         tcp_socket.Close();
         socket_ready = false;
     }
 }

... va bu erda python server kodi:

 import socket
 import time

 host = 'localhost' 
 port = 50000
 backlog = 5 
 size = 1024 
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 s.bind((host,port)) 
 s.listen(backlog) 

 while 1:
     client, address = s.accept() 
     print "Client connected."
     while 1:
         data = client.recv(size)
         if data == "ping":
             time.sleep(2)
             print ("Unity Sent: " + str(data))
             client.send("pong\n")
         else:
             client.send("Bye!")
             print ("Unity Sent Something Else: " + str(data))
             client.close()
             break

Men birinchi navbatda python serverini va simulyatsiyani ("Unity" muharriri orqali) ishlataman. Buning natijasida quyidagi konsol ekran tasvirlari paydo bo'ladi:

enter image description here

Bu shuni ko'rsatadiki, asosiy yangilash() döngüsü ishlayotgan paytda men uni "pong" serverdan qabul qilinganda to'xtatib turish va yangilashni xohlayman. Bunga qanday erishish mumkin?

Javoblar uchun forumni izladim, ammo har doim boshqa tomondan so'raydigan savollarga - Unity-ni muzlatib qo'ymaslik uchun qanday savollar berishga to'g'ri keldi - va Coroutines yoki Threads dan foydalanishni taklif qilgan javoblar.

Har qanday yordam juda qadrlanadi.

Oldindan rahmat!!!

1
Serverni kutib turganda Unity butunlay bloklanadi, u sizning UI-ni ham qamrab oladi - bu butunlay tushadi (ba'zi platformalarda "bu dastur javob bermayapti" xabari bilan ham keltiriladi). Loyihangizning "kutish" qismini, ya'ni NPC-larning ushbu xabarni serverdan qabul qilmaguncha hech narsa qilmaydigan qilib qo'ying. Hech qachon asosiy Unity ipini bloklamang.
qo'shib qo'ydi muallif Luke Briggs, manba
Serverni kutib turganda Unity butunlay bloklanadi, u sizning UI-ni ham qamrab oladi - bu butunlay tushadi (ba'zi platformalarda "bu dastur javob bermayapti" xabari bilan ham keltiriladi). Loyihangizning "kutish" qismini, ya'ni NPC-larning ushbu xabarni serverdan qabul qilmaguncha hech narsa qilmaydigan qilib qo'ying. Hech qachon asosiy Unity ipini bloklamang.
qo'shib qo'ydi muallif Luke Briggs, manba
IEnumerator Update() kodini yangilash uchun Update() kodini o'zgartirishingiz mumkin va yield return WWW dan foydalaning. Davom etishdan oldin yuklab olish tugaguncha kutib turadi.
qo'shib qo'ydi muallif Maakep, manba
IEnumerator Update() kodini yangilash uchun Update() kodini o'zgartirishingiz mumkin va yield return WWW dan foydalaning. Davom etishdan oldin yuklab olish tugaguncha kutib turadi.
qo'shib qo'ydi muallif Maakep, manba

6 javoblar

Forumga javoblarni izladim, lekin har doim savollarga qoqildi   Buning aksini so'rash - birlikni qanday qilib muzlatib qo'ymaslik kerak - va   Coroutines yoki Threads dan foydalanishni tavsiya qiladi.

Bu to'g'ri. Bu sizning dastlabki xabaringizdan keyin ham sizning e'loningiz bo'ldi:

  • ramkani muzlatish
  • ping
  • hisoblash
  • pong
  • keyingi ramka

Kodga tarjima qilingan:

void Start()
{
    StartCoroutine(doForever());
}


IEnumerator doForever()
{

    while (true)
    {
        //freeze frame
        Time.timeScale = 0f;

        //ping
        writeSocket("ping");

        //calculation

        //pong
        string pongValue = socket_reader.ReadLine();

        //unfeeze frame
        Time.timeScale = 1f;

        //next frame
        yield return null;
    }
}

This is so wrong in many ways. This will freeze Unity when waiting or receiving data from python. This may be something you want but it's not good in any way.

You must do this in a Thread. The reason for this is because you are using the blocking version of the receive function.

Thread socketThread;

void Start()
{
    socketThread = new Thread(doForever);
    socketThread.IsBackground = true;
    socketThread.Start();
}

void doForever()
{

    while (true)
    {
        //freeze frame
        gameStatus = GameStatus.PAUSED;

        //ping
        writeSocket("ping");

        gameStatus = GameStatus.RUNNING;
        //calculation

        //pong
        string pongValue = socket_reader.ReadLine();
    }
}

Agar Unity ning API-ni boshqa mavzudan yoki uning kodidan foydalanmoqchi bo'lsangiz, ushbu yuborishingiz kerak.

Do not use read from socket from the main Thread like you are currently doing. One exception is when using the async functions but you are not in this case.

Buning ko'p sabablari bor va sizning python kodingizdan biror narsa olmaguningizcha, sizning UI sizni javob bermasligingizning eng yomoni.


O'yinni to'xtatib turish uchun, siz faqatgina enum-ni ishlatishingiz mumkin va buni bajarish uchun iborani ishlatishingiz mumkin.

public enum GameStatus
{
    RUNNING, PAUSED
}

So'ngra siz o'zingizning har bir funktsiyangizda yangi narsa paydo qilsangiz, shunday bir narsa qilishingiz mumkin:

GameStatus gameStatus;

void moveCharacterEveryFrame()
{
    if (gameStatus == GameStatus.PAUSED)
    {
        return;
    }

    //Move Character code below
}

O'yinni yuqoridagi "Mavzu" da to'xtatishni xohlasangiz, buni quyidagini amalga oshirasiz:

gameStatus = GameStatus.PAUSED;

Pulni ishlatmaslik uchun:

gameStatus = GameStatus.RUNNING;

Endi siz O'yiningizni yoki UIni muzlatmaysiz.

2
qo'shib qo'ydi
Muammoning birligi bilan aloqasi yo'q. O'yinni rejalashtirishda odatda pauza/rezyume mexanizmini yaratadigan maxsus guruh mavjud. Garchi uni bir kishi amalga oshirsa. davlat mashinasini toping . Enum buni Birlikda qilishning eng yaxshi yo'li. Birlik bilan ishlashdan oldin C# haqida ko'proq ma'lumotga ega bo'lishingiz kerak deb o'ylayman. Ba'zi odamlar C #-ga asoslanib, Unity-ga o'tishadi va ular og'ir vaqtlargacha boradi. C# ma'lumotini oling. Asosiydan oldinga (Multi-Thread va Networking) boshlang va Unity ga qayting. Birlikdagi davlat mashinasi haqida ham ma'lumotga ega bo'ling. Bu sizning muammoingizni keltirib chiqaradi.
qo'shib qo'ydi muallif Programmer, manba
Men sizni pauza qilishning to'g'ri yo'lini ko'rsatdim, siz javobimda GameStatus holatini o'qiyotganda animatsiyaning yoki fizikani to'xtata olasiz. Agar siz hali ham noto'g'ri usulini pauza qilishni xohlasangiz, to'xtatish uchun Time.timeScale = 0f va Time.timeScale = 1.0f; dan foydalaning. Menga birinchi suyuqlik javobiga qo'shilgan. Eslatma: qilmang ni tavsiya eting. Tarmoq va Multi-Threading haqida C #-da bilib olganingizdan so'ng, mening javobimning ikkinchi qismiga qaytib kelishingiz mumkin. Baxtli kodlash!
qo'shib qo'ydi muallif Programmer, manba
Juda soz. Men sizni faqatgina "Unity" va "C #" ga o'xshagan deb bilaman. Birlikda ko'p narsalar juda oson. U soketni o'z ichiga olgandan so'ng, u murakkab/oldingi mavzuga aylanadi. Siz uchun Time.timeScale ishi bormi?
qo'shib qo'ydi muallif Programmer, manba
"Time.timeScale" ping-pong misolida ishlaydi. Endi uni haqiqiy simulyatsiya bilan sinab ko'rishim kerak. Hujjatlarga ko'ra, bu ishlashi kerak va global vaqtni to'xtatish kerak. Men hozirda ishlash haqida tashvishlanmayman. Men birligimga chuqurroq sho'ng'iyapman va "asosiy C #" ni o'qish bo'yicha maslahatingizga amal qila olaman. Katta rahmat! Yaqin orada qaytarib olinadi.
qo'shib qo'ydi muallif thecritter, manba
Aytmoqchi ... meni noto'g'ri qilmang. Menga yordam berish uchun sizning (va har bir insonni) chin dildan minnatdorman. Birlikning oldindan bashorat qilmaydigan bir jihatini (haqiqiy dunyo muammosi) aniqlayman.
qo'shib qo'ydi muallif thecritter, manba
Men serverdan olingan avtomat (aqlli) nazoratni ishlatish uchun "qutidan tashqarida" yondoshuvi yo'qligini qanday tushunish mumkinligini tushunmayapman. Ya'ni men erishmoqchi bo'lgan narsa juda oddiy - tahrirlovchini buzmasdan ma'lum server javobini kutayotib, butun fizika jarayonini to'xtatib turish. Nima uchun Threads yoki Coroutines ishtirok etishi kerak? GameStatus yondashuvi mavjudligini topa olmaydigan haqiqiy, murakkab o'yin haqida o'ylayman (qo'lda har qanday o'yin moslamasiga qo'shib qo'yilgan barcha skriptlarga kod qo'shish).
qo'shib qo'ydi muallif thecritter, manba
Sizning ikkinchi taklif qilingan echimingiz bilan o'ynadim. Bu mening maqsadlarim uchun uni amalga oshirish uchun juda murakkab (va mumkin emas) kabi ko'rinadi. So'ngra, sizning "sintez" tavsiyangizga qaytib keldim. Bu menga kerak bo'lgan narsalarga juda yaqin. Shuni unutmangki, men 3 haftalik Birlik foydalanuvchisiman va uni A.I.ga kontseptsiyaning isboti sifatida kerak. Bundan tashqari, server va mijoz ham bir xil kompyuterda, shuning uchun men kechikish bilan yashay olaman. Ammo, qanday qilib fizikani to'xtatib turishim mumkin (upd() loop) ichidagi ichakdan? Buning bir yo'li bormi?
qo'shib qo'ydi muallif thecritter, manba
Bu juda ko'p ma'no beradi. Buni imkon qadar qisqa vaqt ichida sinab ko'rasiz. Biroq, ba'zi narsalar mening GameObjects (dushman) larning bir qismi Corutine yordamida dinamik tarzda yaratilganligi sababli murakkablashadi. Men bu yondashuv dushman emas, balki asosiy xarakterimni buzishidan qo'rqaman. Yaqin orada qaytarib olinadi. Ko'p samimiy minnatdorchilik !!!
qo'shib qo'ydi muallif thecritter, manba

Update() funktsiyangizni o'chiring va WriteSocket() funktsiyangizning kodingizni qo'ying.

1
qo'shib qo'ydi
TimeScale Update usuli bilan hech qanday aloqasi yo'q yoki uni ko'rsatish.
qo'shib qo'ydi muallif m.rogalski, manba
qo'shib qo'ydi muallif maximelian1986, manba
Siz haqsiz. Ammo global vaqtni to'xtatish mening maqsadim uchun etarli bo'lib tuyuladi, chunki u vaqtinchalik to'xtaydi va serverlardan hisoblagandan so'ng qayta tiklanishi mumkin bo'ladi.
qo'shib qo'ydi muallif thecritter, manba

Update() funktsiyangizni o'chiring va WriteSocket() funktsiyangizning kodingizni qo'ying.

1
qo'shib qo'ydi
TimeScale Update usuli bilan hech qanday aloqasi yo'q yoki uni ko'rsatish.
qo'shib qo'ydi muallif m.rogalski, manba
qo'shib qo'ydi muallif maximelian1986, manba
Siz haqsiz. Ammo global vaqtni to'xtatish mening maqsadim uchun etarli bo'lib tuyuladi, chunki u vaqtinchalik to'xtaydi va serverlardan hisoblagandan so'ng qayta tiklanishi mumkin bo'ladi.
qo'shib qo'ydi muallif thecritter, manba

Mavzu yaratish uchun Coroutine'dan foydalanishingiz mumkin:

public Action OnReceiveMessage; //event trigger when receive message.

private Thread m_ReceiveThread;
private List m_ReceiveStrings;
private int m_ReceiveStrCount;

private void Start() {
    m_ReceiveStrings = new List ();
    m_ReceiveStrCount = m_ReceiveStrings.Count;
    StartCoroutine (CreateReceiveListener());
}

private IEnumerator CreateReceiveListener() {
    m_ReceiveThread = new Thread (ReceiveListener);
    m_ReceiveThread.IsBackground = true;
    m_ReceiveThread.Start ();
    yield return null;
}

Xabarni ro'yxatga qo'shish:

private void ReceiveListener() {
    while (m_Connected) {
        string receiveData = ReadSocket ();
        if (string.IsNullOrEmpty (receiveData) == false) {
            m_ReceiveStrings.Add (receiveData);
            Debug.Log (receiveData);
        }
    }
}

Keyinchalik, agar biron bir o'zgarish mavjud bo'lsa, oldindan vaqtincha tekshiring:

private void LateUpdate() {
    if (m_ReceiveStrCount != m_ReceiveStrings.Count) {
        if (OnReceiveMessage != null) {
            OnReceiveMessage (m_ReceiveStrings[m_ReceiveStrCount]);
            m_ReceiveStrCount = m_ReceiveStrings.Count;
        }
    }
}
1
qo'shib qo'ydi

Agar siz to'g'ri tushunsangiz, serverdan yangi ma'lumotlar olgunga qadar Unity ning ichki chizish mexanizmini osib qo'yishni xohlaysizmi?

Agar shunday bo'lsa, uni Update yoki LateUpdate usulida kuting.

void Update()//or void LateUpdate()
{
    while(!received && Application.isRunning){
       //do nothing
    }

   //receive
    string msg = readSocket();
   //log
    Debug.Log(msg);
}

Lekin Unity domenini ishlatishingiz kerak emas.

Agar siz istagan narsa emas bo'lsa, unda sharh qo'ying va men bu javobni o'chirib tashlayman :)

0
qo'shib qo'ydi
Buning uchun siz python serveridan "Unity" loyihasiga ba'zi xabarlarni yuborishingiz va keyinchalik "playmode" ni to'xtatishingiz yoki faqat "UI" ish zarrachasini bloklagan qismini olib tashlashingiz kerak bo'ladi.
qo'shib qo'ydi muallif m.rogalski, manba
Ha, ushbu rasmni serverdan yangi xabar kelguncha ushlab turish mo'ljallangan. Fizikani yangilash ( FixedUpdate usuli) ni kutish va kutish uchun sizning loyihangiz uchun fizika parametrlarini o'zgartirish kerak bo'ladi. Men buni bajarish vaqtida kod orqali amalga oshirish mumkinligini bilib olaman, keyin sizga javobni tahrirlash yoki izohlash bilan yangilayman.
qo'shib qo'ydi muallif m.rogalski, manba
Sizning takliflaringiz faqat fizikani emas, balki chizma mexanizmini qoldiradi. Bu hisoblash jarayonida atrof-muhitning o'zgarishiga olib keladi. Menga kerak bo'lgan narsa - bu fizikani to'xtatib qo'yishdir. Baribir rahmat!
qo'shib qo'ydi muallif thecritter, manba
Katta rahmat. Fikrni o'chirishga hojat yo'q, chunki bu juda foydali. Python-serverni to'xtatganimdan so'ng, tahrirlovchini muzlatishdan qochishning usulini topishim kerak. Juda istiqbolli !!!
qo'shib qo'ydi muallif thecritter, manba

Agar siz to'g'ri tushunsangiz, serverdan yangi ma'lumotlar olgunga qadar Unity ning ichki chizish mexanizmini osib qo'yishni xohlaysizmi?

Agar shunday bo'lsa, uni Update yoki LateUpdate usulida kuting.

void Update()//or void LateUpdate()
{
    while(!received && Application.isRunning){
       //do nothing
    }

   //receive
    string msg = readSocket();
   //log
    Debug.Log(msg);
}

Lekin Unity domenini ishlatishingiz kerak emas.

Agar siz istagan narsa emas bo'lsa, unda sharh qo'ying va men bu javobni o'chirib tashlayman :)

0
qo'shib qo'ydi
Ha, ushbu rasmni serverdan yangi xabar kelguncha ushlab turish mo'ljallangan. Fizikani yangilash ( FixedUpdate usuli) ni kutish va kutish uchun sizning loyihangiz uchun fizika parametrlarini o'zgartirish kerak bo'ladi. Men buni bajarish vaqtida kod orqali amalga oshirish mumkinligini bilib olaman, keyin sizga javobni tahrirlash yoki izohlash bilan yangilayman.
qo'shib qo'ydi muallif m.rogalski, manba
Buning uchun siz python serveridan "Unity" loyihasiga ba'zi xabarlarni yuborishingiz va keyinchalik "playmode" ni to'xtatishingiz yoki faqat "UI" ish zarrachasini bloklagan qismini olib tashlashingiz kerak bo'ladi.
qo'shib qo'ydi muallif m.rogalski, manba
Sizning takliflaringiz faqat fizikani emas, balki chizma mexanizmini qoldiradi. Bu hisoblash jarayonida atrof-muhitning o'zgarishiga olib keladi. Menga kerak bo'lgan narsa - bu fizikani to'xtatib qo'yishdir. Baribir rahmat!
qo'shib qo'ydi muallif thecritter, manba
Katta rahmat. Fikrni o'chirishga hojat yo'q, chunki bu juda foydali. Python-serverni to'xtatganimdan so'ng, tahrirlovchini muzlatishdan qochishning usulini topishim kerak. Juda istiqbolli !!!
qo'shib qo'ydi muallif thecritter, manba
Python
Python
372 ishtirokchilar

Bu guruh python dasturlash tilini muhokama qilish uchun. Iltimos, o'zingizni hurmat qiling va faqat dasturlash bo'yicha yozing. Botlar mavzusini @botlarhaqida guruhida muhokama qling! FAQ: @PyFAQ Offtopic: @python_uz_offtopic

Python offtopic group !
Python offtopic group !
150 ishtirokchilar

@python_uz gruppasining offtop gruppasi. offtop bo'lsa ham reklama mumkin emas ) Boshqa dasturlash tiliga oid gruppalar @languages_programming