2011-04-04 13 views
5

तो, मैं एक गेम इंजन पर काम कर रहा हूं, और मैंने बहुत अच्छी प्रगति की है। हालांकि, मेरा इंजन सिंगल-थ्रेडेड है, और अलग-अलग धागे में अद्यतन और प्रतिपादन के फायदे बहुत अच्छे विचार की तरह लगता है।मल्टी थ्रेडेड गेम - अपडेटिंग, रेंडरिंग, और उन्हें कैसे विभाजित करें

मुझे यह कैसे करना चाहिए? सिंगल थ्रेडेड गेम इंजन (अवधारणात्मक) बनाने के लिए बहुत आसान हैं, आपके पास एक लूप है जहां आप अपडेट करते हैं -> प्रस्तुत करें -> नींद -> दोहराना। हालांकि, मैं अलग-अलग अपडेट करने और अलग करने के लिए एक अच्छा तरीका नहीं सोच सकता, खासकर अगर मैं अपनी अपडेट दरों को बदलता हूं (कहता हूं कि मैं अपडेट लूप 25x सेकेंड के माध्यम से जाता हूं, और प्रतिपादन के लिए 60 एफपीएस है) - अगर मैं आधा रास्ते अपडेट करना शुरू करता हूं तो क्या होगा रेंडर लूप के माध्यम से, या इसके विपरीत?

+1

http://gamedev.stackexchange.com/ इस तरह की सामग्री के लिए एक बेहतर जगह हो सकता है। – Kevin

उत्तर

6

किसी भी प्रकार के Updater कार्यकर्ता वर्ग (Runnable लागू करने) में अपने अद्यतन तर्क रखें, और अलग-अलग कार्यकर्ता वर्ग में प्रस्तुतकर्ता रखें। जब आपको डेटा अपडेट करने की आवश्यकता होती है, तो Updater को उस अद्यतन को अद्यतनकर्ता और निर्माता दोनों द्वारा साझा कतार में डाल दें। सबसे सुविधाजनक कतार का उपयोग करना होगा जिसमें पहले से ही अंतर्निहित बहु-थ्रेडेड समर्थन है, जैसे BlockingQueue के उप-वर्ग। उदाहरण कोड के लिए, BlockingQueue के लिए javadoc देखें।

कतार का उपयोग प्राकृतिक है यदि आपको सभी परिवर्तनों को प्रस्तुत करने की आवश्यकता है, यहां तक ​​कि अप्रचलित भी। यदि आप केवल नवीनतम परिवर्तन प्रस्तुत करना चाहते हैं, तो कतार के बजाय ConcurrentHashMap का उपयोग करें।

अपने अपडेट अपरिवर्तनीय ऑब्जेक्ट्स को न भूलें, इसलिए जब आप इसे प्रस्तुत करते हैं तो कोई अपडेट अपडेट नहीं हो सकता है।

जैसा कि निर्मल ने बताया, आप थ्रेड की संख्या सीमित करने और धागे की शुरुआत/रोक को सरल बनाने के लिए किसी प्रकार के थ्रेड पूल का उपयोग कर सकते हैं। उपलब्ध विकल्पों को देखने के लिए जेडीके में Executor इंटरफेस और Executors उपयोगिता वर्ग का संदर्भ लें।

+0

क्या उन्हें अपरिवर्तनीय होने की आवश्यकता है?ऐसा लगता है कि मुझे कुछ प्रकार की "फ़्रेम" ऑब्जेक्ट में प्रस्तुत करने के लिए प्रगतिशील रूप से प्रतिलिपि बनाने की आवश्यकता है। क्या यह सबसे कुशल तरीका है? – CodeBunny

+1

यह निश्चित रूप से स्मृति-वार का सबसे प्रभावी तरीका नहीं है। लेकिन बहु-थ्रेडेड वातावरण में विकसित करना और समझना सबसे आसान है। यदि आपकी ऑब्जेक्ट्स नहीं बदली जा सकती हैं, तो आपको समवर्ती धागे में उन्हें ठीक से बदलने के बारे में चिंता करने की आवश्यकता नहीं होगी - यह आपके कोड की सादगी और रखरखाव को काफी बढ़ाता है। –

0

प्रत्येक श्रेणी के लिए pojo बनाएं, एक रननेबल ऑब्जेक्ट में एफपीएस दर, यूआई स्क्रीन क्लास एन जैसे सभी आवश्यक जानकारी शामिल हैं, आप सामान्य जानकारी सिंगलटन बना सकते हैं, इसलिए प्रत्येक बार अपडेट करने के लिए थ्रेड शुरू करने के लिए, मैं स्मृति रखने के लिए थ्रेडपूल की सलाह देता हूं खपत सीमित

1

मैं इस वास्तुकला के साथ पाइपलाइन जा रहा है, जिसका अर्थ है कि मंच सभी तत्वों को पिछले फ्रेम पर अद्यतन प्रदान करेगा प्रस्तुत करना सुझाव है, तो वह ऐसा जाना होगा:

अद्यतन 0

अद्यतन 1 प्रस्तुत 0

अद्यतन 2 प्रस्तुत 1

अद्यतन 3 प्रस्तुत 2

....

इसका मतलब होगा कि अपने खेल अधिक स्मृति और सभी वस्तुओं का उपयोग करेगा/डेटा फ्रेम राज्यों प्रति के लिए

यदि आप और अधिक परतों इस पाइप लाइन में अपने खेल होगा इनपुट अंतराल से भुगतना होगा (अर्थ है कि उपयोगकर्ता को पेश होगा बाद में सामान्य पर स्क्रीन पर उसकी कार्रवाई देखें), इसलिए मैं केवल 2 चरण पाइपलाइन

0

का उपयोग करने का सुझाव देता हूं यह भी ध्यान दें कि आपके ड्रा थ्रेड को आपके अपडेट थ्रेड के बाद कभी तेज़ी से नहीं चलना चाहिए। चूंकि आपका अपडेट थ्रेड वर्तमान चरण के साथ अभी तक नहीं किया गया है, इसलिए आप पहले की तरह ही एक ही चीज़ खींचेगे। ऐसा करने के दौरान आप अपडेट चरण की समाप्ति को याद कर सकते हैं, जो अंत में इष्टतम फ़्रेमेट से कम होता है।

(ठीक उसी चित्र को चित्रित करना याद रखें जैसा पहले किसी को लाभ नहीं होता है)।

+0

यह वास्तव में गलत है। आप क्या करते हैं, आप प्रत्येक रेंडर के दौरान अंतिम अद्यतन के बाद से उस समय का ट्रैक रखते हैं, और वेग वाले किसी ऑब्जेक्ट को रैखिक प्रगति से रखा जाता है। यह आपको अद्यतन में अधिक श्वास कक्ष देने की अनुमति देता है (कहें, यदि आपके पास जटिल एआई है) और अपनी बहुमूल्य 60 एफपीएस को क्रैम्प न करें। – CodeBunny

+1

@CodeBunny, उस स्थिति में आपके पास प्रतिपादन और अद्यतन करने के बीच स्पष्ट अलगाव नहीं है, क्योंकि रेंडरर को वास्तविक प्रतिपादन के बगल में सभी ऑब्जेक्ट्स के लिए (अस्थायी) लॉजिकल अगली 'स्थिति' की गणना करने की आवश्यकता होती है। कॉम्प्लेक्स एआई एक पूरी तरह से अलग समस्या है, यहां आप एआई को काम करने के लिए एक अलग थ्रेड का उपयोग करते हैं, लेकिन अपडेट थ्रेड (रेंडर थ्रेड नहीं) प्रत्येक अद्यतन लाइनर प्रगति का उपयोग करते हैं, जब तक कि वास्तविक स्थिति स्थिति को बहु-फ्रेम- अलग धागा एआई कार्य। –

0

मैंने अपना प्रोग्राम तीन धागे का उपयोग करके किया है (हालांकि अधिक इस्तेमाल किया जा सकता है)।

  1. अद्यतन तर्क (डेटा एकत्र करने और पूर्व प्रसंस्करण करता है)

  2. हेल्पर धागा (समय अनंत नींद 1ms पाश में लेने वाली कैश पूर्व गणना आदि ... तो इस सूत्र की परवाह नहीं करता की गणना करता है, जहां अद्यतन तर्क जा रहा है, या कितनी तेज़ है। यह सिर्फ यह जांचता है कि यह कहां जा रहा है और गणना करता है अगर इसे नए आइटम कैश करने की आवश्यकता है)

  3. रेंडर थ्रेड (केवल प्रतिपादन करता है, इसे प्रस्तुत करने के लिए आवश्यक सब कुछ पूर्व-संसाधित होता है केवल कार्य खींचें और स्क्रीन की स्थिति की गणना करें)

ऐसा करना बहुत आसान है यदि आपके पास अभी "थ्रेड सुरक्षित" आइटम हैं जो आप चित्रित कर रहे हैं। लेकिन खेल में, मैं व्यक्तिगत रूप से सोचता हूं कि यदि आप प्लेयर 2 को खिलाड़ी 2 से आगे टिकते हैं तो यह बुरा नहीं है ... क्योंकि आप अभी भी तेजी से आकर्षित करना चाहते हैं, आप गेम पर कर सकते हैं। खेल तर्क धागा सुनिश्चित करता है कि कोई तार्किक अपवाद नहीं है ... इसलिए आम तौर पर मुझे लगता है कि इससे कोई फर्क नहीं पड़ता कि आप क्या आकर्षित करते हैं और कब, आप इसे जितना तेज़ कर सकते हैं उतना तेज़ कर सकते हैं जितना आप बिना किसी "सिंक्रनाइज़ेशन" के सोच सकते हैं।

मैं थ्रेड के बीच डेटा साझा करने के लिए प्राथमिक रूप से public static volatile item पसंद करता हूं। और AtomicIntegerArray इसके लिए एक उपयोगी कक्षा भी है।

4

एक सभ्य कार्यान्वयन वह है जहां आपको अपडेट थ्रेड के बीच सिंक्रनाइज़ेशन की आवश्यकता नहीं होगी और थ्रेड प्रस्तुत करना होगा। कठिनाई उस परिस्थिति में निहित है जहां एक धागा एक अलग गति से चलता है (जो बहुत संभव है)। की जाँच करें

http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part1/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part2/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part3/ http://blog.slapware.eu/game-engine/programming/multithreaded-renderloop-part4/

यह कैसे प्राप्त किया जा सकता। साइट एक स्पष्टीकरण और कार्यान्वयन (स्रोत + बाइनरी) देता है।

0

मैं कहूंगा कि एक फ़ील्ड जोड़ें जो चलाने और प्रस्तुत करने के लिए आवश्यक थ्रेड निर्दिष्ट करता है, और धागे की संख्या == थ्रेड आवश्यक होने पर थ्रेड्स को संख्या दें, फिर इसे चलाने और प्रस्तुत करने की अनुमति है, और आवश्यक थ्रेड फ़ील्ड को बढ़ाएं , जब तक यह अधिकतम हिट नहीं हो जाता है, तो लूप 0 पर वापस ले जाएं। वैकल्पिक रूप से, आप टिक के लिए एक थ्रेड का उपयोग कर सकते हैं और दूसरा प्रस्तुत करने के लिए, यह आसान हो सकता है। यहाँ एक उदाहरण कोड है:

public Game() { 
    this.tickThread=new Thread(this::tickLoop()); 
    this.renderThread=new Thread(this::renderLoop()); 
} 
public void tickLoop() { 
    //code for timing... 
    while(running) { 
     //more code for timing... 
     tick(); 
    } 
} 
public void renderLoop() { 
    //code for timing or syncing frames... 
    while(running) { 
     //more code for timing... 
     render(); 
    } 
} 

वैकल्पिक रूप से, आप कह सकते हैं:


| MyRunnable.java |

public interface MyRunnable 
{ 
    public abstract void run(boolean toRender); 
} 

| MyThread.java |

public class MyThread extends Thread 
{ 
    private boolean isRender; 
    private MyRunnable runnable 
    public MyThread(boolean isRender,MyRunnable runnable) 
    { 
     this.isRender=isRender; 
     this.runnable=runnable; 
    } 
    public void run() 
    { 
     this.runnable.run(this.isRender); 
    } 
} 

| Game.java |

public class Game extends /*JPanel/Canvas/JFrame/Some other component*/ 
{ 
    private MyThread tickThread; 
    private MyThread renderThread; 
    private boolean running; 
    public Game() 
    { 
     super(); 
     tickThread=new MyThread(this::run); 
     renderThread=new MyThread(this::run); 
     //other constructor code 
    } 
    public void tick() 
    { 
     //tick code here 
    } 
    public void render() 
    { 
     //render code here 
    } 
    public void run(boolean isRender) 
    { 
     //timing variables 
     while(running) 
     { 
      //timing code 
      if(isRender) 
      { 
       this.render(); 
      } 
      else 
      { 
       this.tick(); 
      } 
     } 
    } 
} 
संबंधित मुद्दे