"কনস্ট্যান্ট গেমের গতি সর্বাধিক এফপিএস" গেম লুপের উপর অত্যন্ত বিভ্রান্ত


12

আমি সম্প্রতি গেম লুপগুলিতে এই নিবন্ধটি পড়েছি: http://www.koonsolo.com/news/dewitters-gameloop/

এবং প্রস্তাবিত শেষ বাস্তবায়ন আমাকে গভীরভাবে বিভ্রান্ত করছে। এটি কীভাবে কাজ করে তা আমি বুঝতে পারি না এবং এটি একটি সম্পূর্ণ জগাখিচুড়ি মনে হচ্ছে।

আমি নীতিটি বুঝতে পেরেছি: যতটুকু সম্ভব খেলা যতবার সম্ভব রেন্ডার করে স্থির গতিতে গেমটি আপডেট করুন।

আমি এটি গ্রহণ করি আপনি একটি ব্যবহার করতে পারবেন না:

  • 25 টিকিটের জন্য ইনপুট পান
  • 975 টিক্সের জন্য গেমার রেন্ডার করুন

আপনি যেহেতু দ্বিতীয়টির প্রথম অংশের জন্য ইনপুট পাচ্ছেন এবং এটিকে কী অদ্ভুত লাগবে? বা নিবন্ধে কি চলছে?


মূলত:

while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP)

এটি কীভাবে বৈধ?

আসুন তার মানগুলি ধরে নিই।

MAX_FRAMESKIP = 5

আসুন পরবর্তী গেম_টিক ধরে নেওয়া যাক, মূল গেম লুপটি ... 500 বলার আগে, যা সূচনা হওয়ার কয়েক মুহুর্ত পরে বরাদ্দ করা হয়েছিল।

অবশেষে, আমি যেমন আমার গেমের জন্য এসডিএল এবং ওপেনজিএল ব্যবহার করছি, ওপেনজিএল কেবলমাত্র রেন্ডারিংয়ের জন্যই ব্যবহৃত হচ্ছে, ধরে নেওয়া যাক যে GetTickCount()এসডিএল_আইনিট বলা হওয়ার পরে যে সময়টি এসেছে, তা এটি করে।

SDL_GetTicks -- Get the number of milliseconds since the SDL library initialization.

সূত্র: http://www.libsdl.org/docs/html/sdlgetticks.html

লেখক এটিও ধরে নিয়েছেন:

DWORD next_game_tick = GetTickCount();
// GetTickCount() returns the current number of milliseconds
// that have elapsed since the system was started

আমরা যদি whileবিবৃতিটি প্রসারিত করি তবে :

while( ( 750 > 500 ) && ( 0 < 5 ) )

750 কারণ next_game_tickনির্ধারিত হওয়ার পরে সময় পেরিয়ে গেছে । loopsআপনি নিবন্ধে দেখতে পারেন শূন্য।

সুতরাং আমরা লুপটি প্রবেশ করেছি, আসুন কিছু যুক্তি করি এবং কিছু ইনপুট গ্রহণ করি।

Yadayadayada।

লুপটির শেষে, যা আমি আপনাকে মনে করিয়ে দিচ্ছি যে আমাদের মূল গেম লুপটির মধ্যে রয়েছে:

next_game_tick += SKIP_TICKS;
loops++;

আসুন কোডটির পরবর্তী পরবর্তী পুনরাবৃত্তিটি কী তা আপডেট করা যাক

while( ( 1000 > 540 ) && ( 1 < 5 ) )

1000 কারণ সময়টি লুপটির পরবর্তী ব্যর্থতায় পৌঁছানোর আগে ইনপুট পেতে এবং স্টাফ করার কাজটি অতিক্রান্ত হয়ে গেছে, যেখানে গেটটিকাউন্ট () পুনরায় কল করা হয়েছে।

540 কারণ, কোডটি 1000/25 = 40, সুতরাং, 500 + 40 = 540

1 কারণ আমাদের লুপটি একবারে পুনরাবৃত্তি হয়েছে

5 , আপনি জানেন কেন।


সুতরাং, যেহেতু লুপটি পরিষ্কারভাবে অবনমিত হয় MAX_FRAMESKIPএবং উদ্দেশ্যটি না হয় TICKS_PER_SECOND = 25;কীভাবে গেমটি এমনকি সঠিকভাবে চালানোর কথা রয়েছে?

আমার কাছে অবাক হওয়ার কিছু ছিল না যে আমি যখন এটিকে আমার কোডে প্রয়োগ করি তখন সঠিকভাবে আমি যুক্ত করতে পারি যেহেতু আমি আমার ফাংশনগুলির নাম পরিবর্তন করে ব্যবহারকারীর ইনপুট পরিচালনা করতে এবং নিবন্ধটির লেখক তার উদাহরণ কোডটিতে গেমটি আঁকেন, গেমটি কিছুই করেনি ।

আমি লুপটির fprintf( stderr, "Test\n" );ভিতরে একটি স্থাপন করেছি যা গেমটি শেষ না হওয়া অবধি মুদ্রণযোগ্য হবে না।

গেমটির লুপটি সেকেন্ডে 25 বার চলছে, গ্যারান্টিযুক্ত, যত দ্রুত গতিতে উপস্থাপন করতে পারে?

আমার কাছে, যদি আমি বিশাল কিছু মিস না করি তবে মনে হচ্ছে ... কিছুই নয়।

এবং এই লুপটি যখন এইরূপ নয়, অনুমিতভাবে 25 বার সেকেন্ডে চলছে এবং তারপরে নিবন্ধটির শুরুতে আমি ঠিক আগে উল্লিখিত গেমটি আপডেট করছি?

যদি তা হয় তবে কেন আমরা সাধারণ কিছু করতে পারি না:

while( loops < 25 )
{
    getInput();
    performLogic();

    loops++;
}

drawGame();

এবং অন্য কোনওভাবে অন্তরোলনের জন্য গণনা করুন।

আমার অত্যন্ত দীর্ঘ প্রশ্নটি ক্ষমা করুন, তবে এই নিবন্ধটি আমার ভালোর চেয়ে বেশি ক্ষতি করেছে। আমি এখন মারাত্মকভাবে বিভ্রান্ত হয়ে পড়েছি - এবং এই সমস্ত উত্থাপিত প্রশ্নের কারণে কীভাবে সঠিক গেম লুপটি প্রয়োগ করা যায় তা সম্পর্কে আমার কোনও ধারণা নেই।


1
ভাড়া নিবন্ধের লেখকের দিকে আরও ভালভাবে পরিচালিত হয়। আপনার উদ্দেশ্য প্রশ্নটি কোন অংশ ?
আনকো

3
এই গেম লুপটি কি বৈধ, কেউ ব্যাখ্যা করে? আমার পরীক্ষাগুলি থেকে এটিতে 25 বার সেকেন্ডে চালানোর সঠিক কাঠামো নেই। এটি কেন করে তা আমার ব্যাখ্যা করুন। এছাড়াও এটি কোনও কৌতুক নয়, এটি প্রশ্নের ধারাবাহিক। আমার কি ইমোটিকন ব্যবহার করা উচিত, আমি কি রাগান্বিত বলে মনে করি?
tsujp

2
যেহেতু আপনার প্রশ্নটি "এই গেমের লুপটি সম্পর্কে আমি কী বুঝতে পারি না" তাতে উত্থিত হয় এবং আপনার প্রচুর গা bold়-ফন্টের শব্দ রয়েছে তাই এটি কমপক্ষে হতাশার মতোই আসে comes
কির্বিনেটর

@ কিরবিনেটর আমি এটির প্রশংসা করতে পারি, তবে আমি এই নিবন্ধে যা অস্বাভাবিক মনে করি তার সবই ভিত্তি করে দেওয়ার চেষ্টা করছিলাম যাতে এটি কোনও অগভীর এবং ফাঁকা প্রশ্ন নয়। আমি মনে করি না যে এটি পঞ্চম। তবে যেভাবেই হোক, আমি ভাবতে চাই যে আমার কিছু বৈধ পয়েন্ট রয়েছে - সর্বোপরি এই নিবন্ধটি শেখানোর চেষ্টা করা হচ্ছে, তবে এত ভাল কাজ করছেন না।
tsujp

7
আমাকে ভুল করবেন না, এটি একটি ভাল প্রশ্ন, এটি কেবল ৮০% কম হতে পারে।
কির্বিনেটর

উত্তর:


8

আমি মনে করি লেখক একটি ক্ষুদ্র ত্রুটি করেছেন:

while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP)

হতে হবে

while( GetTickCount() < next_game_tick && loops < MAX_FRAMESKIP)

এটি হল: যতক্ষণ না আমাদের পরের ফ্রেমটি আঁকার সময় এখনও আসে নি এবং আমরা MAX_FRAMESKIP এর মতো ফ্রেমগুলি এড়াতে পারি নি আমাদের অপেক্ষা করা উচিত।

তিনি কেন next_game_tickলুপে আপডেট হন তা আমি বুঝতে পারি না এবং আমি ধরে নিয়েছি এটি অন্য একটি ত্রুটি। যেহেতু কোনও ফ্রেমের শুরুতে আপনি নির্ধারণ করতে পারবেন যে পরবর্তী ফ্রেমটি কখন হওয়া উচিত (যখন কোনও নির্দিষ্ট ফ্রেমের হার ব্যবহার করা হয়)। next game tickউপর নির্ভর করে না কত সময় আমরা আপডেট এবং রেন্ডারিং পর ওভার বাকি আছে।

লেখক আরও একটি সাধারণ ত্রুটি করেন

যতটুকু বাকি রয়েছে তার সাথে যতবার সম্ভব গেমটি রেন্ডার করুন।

এর অর্থ একই ফ্রেমের একাধিকবার রেন্ডারিং। লেখক এমনকি এটি সম্পর্কে অবগত:

গেমটি প্রতি সেকেন্ডে 50 বার স্থির সময়ে আপডেট করা হবে এবং যত দ্রুত সম্ভব রেন্ডারিং করা হয়। মন্তব্য করুন যে যখন প্রতি সেকেন্ডে 50 বারেরও বেশি সময় রেন্ডারিং করা হয়, তখন কিছু পরবর্তী ফ্রেম একই হয়, তাই প্রকৃত ভিজ্যুয়াল ফ্রেমগুলি প্রতি সেকেন্ডে সর্বোচ্চ 50 ফ্রেমে প্রদর্শিত হবে।

এটি কেবলমাত্র জিপিইউকে অপ্রয়োজনীয় কাজ করতে বাধ্য করে এবং যদি রেন্ডারিং প্রত্যাশার চেয়ে বেশি সময় নেয় তবে এটি আপনার পরবর্তী ফ্রেমে কাজ করার উদ্দেশ্যে তৈরি করার পরে তৈরি করতে পারে যার ফলে ওএসের কাছে উত্পন্ন হওয়া এবং অপেক্ষা করা ভাল।


+ + ভোট দিন। MMMM। এটি সত্যিই আমাকে তখন কী করতে হবে তা নিয়ে বিস্মিত করে। আমি আপনার অন্তর্দৃষ্টি জন্য আপনাকে ধন্যবাদ। আমার সম্ভবত চারপাশে একটি নাটক থাকবে, সমস্যাটি হ'ল FPS সীমাবদ্ধ করা বা এফপিএসকে গতিশীলভাবে সেট করার বিষয়ে জ্ঞান এবং এটি কীভাবে করা যায়। আমার মনে স্থির ইনপুট হ্যান্ডলিং প্রয়োজনীয় তাই গেমটি সবার জন্য একই গতিতে চলে। এটি কেবল একটি সাধারণ 2 ডি প্ল্যাটফর্মার এমএমও (খুব দীর্ঘ
tsujp

লুপটি সঠিক বলে মনে হচ্ছে এবং পরবর্তী_গেম_টিক বাড়ানোর জন্য রয়েছে। ধীর এবং দ্রুত হার্ডওয়্যারে ধ্রুব গতিতে সিমুলেশন চলমান রয়েছে। "যত দ্রুত সম্ভব" রেন্ডারিং কোড চালানো (বা যাইহোক পদার্থবিজ্ঞানের তুলনায় দ্রুত) তখনই বোঝা যায় যখন রেন্ডারিং কোডে দ্রুতি থাকে যখন দ্রুত বস্তু ইত্যাদির জন্য আরও সহজ করার জন্য (নিবন্ধের শেষাংশ), তবে কেবল শক্তিটি নষ্ট হয় যদি এটি আউটপুট ডিভাইস (স্ক্রীন) যা দেখতে সক্ষম তা তার চেয়ে বেশি রেন্ডার করছে।
দোতি

সুতরাং তার কোডটি একটি বৈধ বাস্তবায়ন, এখন তা। এবং আমরা এই বর্জ্য সঙ্গে বাস করতে হবে? বা আমি এর জন্য অনুসন্ধান করতে পারি এমন কোন পদ্ধতি রয়েছে।
tsujp

ওএসকে দেওয়া এবং অপেক্ষা করা কেবলমাত্র তখনই ভাল ধারণা যদি আপনি ওএস কখন আপনার কাছে নিয়ন্ত্রণ ফিরিয়ে আনতে পারে - আপনার পছন্দ হওয়ার সাথে সাথে তা নাও হতে পারে good
কাইলোটান

আমি এই উত্তরটি ভোট দিতে পারি না। তিনি পুরোপুরি ইন্টারপোলেশন সামগ্রী হারিয়েছেন, যা উত্তরের পুরো দ্বিতীয়ার্ধটিকে অবৈধ করে তুলেছে।
AlbeyAmakiir

4

আমি যদি এটিকে কিছুটা সহজ করি তবে সেরা:

while( game_is_running ) {

    current = GetTickCount();
    while(current > next_game_tick) {
        update_game();

        next_game_tick += SKIP_TICKS;
    }
    display_game();
}

whileমেনলুপের অভ্যন্তরে লুপটি আগে থেকে যেখানে ছিল এখন থেকে কোথায় হওয়া উচিত ছিল সিমুলেশন পদক্ষেপগুলি চালাতে ব্যবহৃত হয়। update_game()ফাংশনটি সর্বদা ধরে নেওয়া উচিত যে SKIP_TICKSশেষ কল করার পরে কেবলমাত্র পরিমাণের সময় কেটে গেছে। এটি গেম ফিজিক্সকে ধীর এবং দ্রুত হার্ডওয়্যারে ধ্রুব গতিতে চলতে থাকবে।

চলমান next_game_tickপরিমাণ দ্বারা SKIP_TICKSএটি বৃদ্ধি বর্তমান সময়ের কাছাকাছি। এটি বর্তমান সময়ের চেয়ে বড় হয়ে current > next_game_tickউঠলে এটি ভেঙে যায় ( ) এবং মেইনলুপ বর্তমান ফ্রেমকে রেন্ডার করতে থাকে।

রেন্ডারিংয়ের পরে, পরবর্তী কলটি GetTickCount()নতুন বর্তমান সময়ে ফিরে আসবে। যদি এই সময়টি এর চেয়ে বেশি হয় next_game_tickতবে এর অর্থ হ'ল আমরা ইতিমধ্যে সিমুলেশনে 1-N পদক্ষেপের পিছনে আছি এবং আমাদের ধীরে ধীরে একই ধ্রুবক গতিতে সিমুলেশনের প্রতিটি পদক্ষেপ চালানো উচিত। এই ক্ষেত্রে যদি এটি কম হয় তবে এটি আবার একই ফ্রেমটি পুনরায় রেন্ডার করবে (যদি না আন্তঃবিভাজন থাকে)।

মূল কোডটি যদি আমাদের খুব বেশি দূরে ( MAX_FRAMESKIP) ছেড়ে যায় তবে লুপের সংখ্যা সীমিত করে রেখেছিল । এটি কেবল এটিকে কিছু দেখায় এবং লক হওয়ার মতো না দেখায় যেমন উদাহরণস্বরূপ স্থগিত হওয়া বা গেমটি দীর্ঘ সময়ের জন্য ডিবাগারে বিরাম দেওয়া থেকে শুরু করা (ধরে নেওয়া GetTickCount()যায় যে সেই সময়ের মধ্যে থেমে থাকে না) যতক্ষণ না এটি সময়ের সাথে ধরা পড়ে।

আপনি যদি অভ্যন্তরীণ প্রবিধান ব্যবহার না করে থাকেন তবে অব্যবহৃত একই ফ্রেম রেন্ডারিং থেকে মুক্তি পেতে display_game()আপনি যদি বিবৃতিতে পছন্দ করেন তবে এটি ভিতরে আবদ্ধ করতে পারেন:

while (game_is_running) {
    current = GetTickCount();
    if (current > next_game_tick) {
        while(current > next_game_tick) {
            update_game();

            next_game_tick += SKIP_TICKS;
        }
    display_game();
    }
    else {
    // could even sleep here
    }
}

এটি সম্পর্কে এটি একটি ভাল নিবন্ধ: http://gafferongames.com/game-physics/fix-your-timestep/

এছাড়াও, কারণ fprintfআপনার খেলাগুলি শেষ হওয়ার পরে আপনার আউটপুটগুলি কেন এমন হতে পারে যে এটি ফ্লাশ হয় নি।

আমার ইংরেজি সম্পর্কে দুঃখিত।


4

তার কোডটি পুরোপুরি বৈধ বলে মনে হচ্ছে।

whileশেষ সেটটির লুপটি বিবেচনা করুন :

// JS / pseudocode
var current_time = function () { return Date.now(); }, // in ms
    framerate = 1000/30, // 30fps
    next_frame = current_time(),

    max_updates_per_draw = 5,

    iterations;

while (game_running) {

    iterations = 0;

    while (current_time() > next_frame && iterations < max_updates_per_draw) {
        update_game(); // input, physics, audio, etc

        next_frame += framerate;
        iterations += 1;
    }

    draw();
}

আমি জায়গায় একটি সিস্টেম পেয়েছি যা বলছে "গেমটি চলাকালীন, বর্তমান সময়টি পরীক্ষা করে দেখুন - এটি যদি আমাদের চলমান ফ্রেমরেট ট্যালির চেয়ে বেশি হয়, এবং আমরা 5 টি ফ্রেমের চেয়ে কম অঙ্কন এড়িয়ে চলেছি, তবে অঙ্কন এড়িয়ে চলুন এবং কেবল ইনপুট আপডেট করুন এবং পদার্থবিজ্ঞান: অন্যথায় দৃশ্যটি আঁকুন এবং পরবর্তী আপডেটের পুনরাবৃত্তিটি শুরু করুন "

যখন প্রতিটি আপডেট হয়, আপনি আপনার আদর্শ ফ্রেমরেট দ্বারা "পরের_ ফ্রেম" সময় বাড়িয়ে তোলেন। তারপরে আপনি আবার আপনার সময়টি পরীক্ষা করে দেখুন। যদি আপনার বর্তমান সময়টি পরবর্তী_ফ্রেম আপডেট হওয়ার চেয়ে কম হয় তবে আপনি আপডেটটি এড়িয়ে যা পেয়েছেন তা আঁকুন।

যদি আপনার বর্তমানের সময়টি আরও বেশি হয় (কল্পনা করুন যে শেষের ড্র প্রক্রিয়াটি বেশ দীর্ঘ সময় নিয়েছে, কারণ কোথাও কোথাও কিছু হিচকি ছিল, বা একটি পরিচালিত ভাষায় কিছুটা আবর্জনা-সংগ্রহ, অথবা সি ++ বা যে কোনও কিছুতে ম্যানেজড-মেমরি প্রয়োগ) ড্র এড়িয়ে যায় এবং সময়ের next_frameসাথে আরও একটি অতিরিক্ত ফ্রেমের সাথে আপডেট হয়, যতক্ষণ না হয় আপডেটগুলি যেখানে আমাদের ঘড়ির উপরে থাকা উচিত সেখানে ধরা পড়ে না, বা পর্যাপ্ত ফ্রেম আঁকতে আমরা এড়াতে পারি যে আমাদের একেবারে একটি আঁকতে হবে, যাতে প্লেয়ার দেখতে পাবে তারা কি করছে

যদি আপনার মেশিনটি দ্রুতগতিতে বা আপনার গেমটি অতি-সহজ হয় তবে current_timeএটি next_frameপ্রায়শই কম হতে পারে যার অর্থ আপনি এই পয়েন্টগুলির সময় আপডেট করছেন না।

এদিকেই দ্রাবনটি আসে Like একইভাবে, আপনি "নোংরা" স্থান ঘোষণা করার জন্য লুপগুলির বাইরে ঘোষিত একটি পৃথক বুল পেতে পারেন।

আপডেট লুপের অভ্যন্তরে আপনি সেট করেছিলেন dirty = true, এটি নির্দেশ করে যে আপনি আসলে একটি আপডেট করেছেন।

তারপরে, কেবল কল করার পরিবর্তে draw(), আপনি বলবেন:

if (is_dirty) {
    draw(); 
    is_dirty = false;
}

তারপরে আপনি মসৃণ গতির জন্য কোনও দ্বিধাবিভক্তি হারিয়ে ফেলছেন, তবে আপনি নিশ্চিত হচ্ছেন যে আপনি যখন আপডেট পেয়ে যাচ্ছেন তখনই আপনি আপডেট করছেন (রাজ্যগুলির মধ্যে বিভক্তির চেয়ে)।

আপনি যদি সাহসী হন, "আপনার টাইমস্টেপ ঠিক করুন!" নামে একটি নিবন্ধ আছে গ্যাফারঅনগেমস দ্বারা।
এটি সমস্যাটিকে কিছুটা ভিন্নভাবে পৌঁছে দেয় তবে আমি এটিকে একটি প্রাকৃতিক সমাধান হিসাবে বিবেচনা করি যা বেশিরভাগ ক্ষেত্রে একই জিনিস হয় (আপনার ভাষার বৈশিষ্ট্যগুলির উপর নির্ভর করে এবং আপনি আপনার গেমের পদার্থবিজ্ঞানের গণনা সম্পর্কে কতটা যত্নবান হন)।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.