স্ট্যান্ডার্ড সি # / উইন্ডোজ ফর্মের গেম লুপটি কী?


32

সি # তে এমন কোনও গেম লেখার সময় যা সাধারণ-পুরানো উইন্ডোজ ফর্ম এবং স্লিমডিএক্স বা ওপেনটকে- র মতো কিছু গ্রাফিক্স এপিআই র‌্যাপার ব্যবহার করে , কীভাবে মূল গেমের লুপটি কাঠামোগত করা উচিত?

একটি ক্যানোনিকাল উইন্ডোজ ফর্ম অ্যাপ্লিকেশনটির মতো একটি এন্ট্রি পয়েন্ট রয়েছে

public static void Main () {
  Application.Run(new MainForm());
}

এবং ক্লাসের বিভিন্ন ইভেন্টকে হুক করে কেউ প্রয়োজনীয় কিছু অর্জন করতে পারে , সেই ইভেন্টগুলি গেম লজিক অবজেক্টগুলিতে নিয়মিত পর্যায়ক্রমিক আপডেটগুলি সম্পাদন করতে বা রেন্ডার শুরু করতে এবং শেষ করতে শেষ করার জন্য কোডের বিটগুলি রাখার জন্য একটি স্পষ্ট স্থান সরবরাহ করে না events ফ্রেম.Form

এই জাতীয় গেমটি ক্যানোনিকাল সদৃশ কিছু অর্জনের জন্য কী কৌশল ব্যবহার করা উচিত

while(!done) {
  update();
  render();
}

গেম লুপ, এবং ন্যূনতম পারফরম্যান্স এবং জিসি প্রভাব নিয়ে কাজ করতে চান?

উত্তর:


45

Application.Runকল ড্রাইভ আপনার উইন্ডোজ বার্তা পাম্প, যা পরিণামে কি ক্ষমতা সমস্ত ঘটনা আপনার উপর হুক করতে পারেন Formশ্রেণী (এবং অন্যদের)। এই বাস্তুতন্ত্রের একটি গেম লুপ তৈরি করতে, আপনি যখন অ্যাপ্লিকেশনটির বার্তা পাম্পটি খালি থাকে তখন শুনতে চান এবং এটি শূন্য থাকা অবস্থায় সাধারণত "প্রক্রিয়া ইনপুট স্টেটটি করুন, গেমের যুক্তি আপডেট করুন, দৃশ্যের রেন্ডার করুন" প্রোটোটাইপিক গেম লুপের পদক্ষেপগুলি ।

Application.Idleপ্রতিবার অ্যাপ্লিকেশনটির বার্তার সারিটি খালি হয়ে যাওয়ার পরে ইভেন্টটি জ্বলে ওঠে এবং অ্যাপ্লিকেশনটি নিষ্ক্রিয় অবস্থায় রূপান্তরিত হয়। আপনি আপনার মূল ফর্মটির নির্মাতায় ইভেন্টটি হুক করতে পারেন:

class MainForm : Form {
  public MainForm () {
    Application.Idle += HandleApplicationIdle;
  }

  void HandleApplicationIdle (object sender, EventArgs e) {
    //TODO: Implement me.
  }
}

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

[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
    public IntPtr Handle;
    public uint Message;
    public IntPtr WParameter;
    public IntPtr LParameter;
    public uint Time;
    public Point Location;
}

[DllImport("user32.dll")]
public static extern int PeekMessage(out NativeMessage message, IntPtr window, uint filterMin, uint filterMax, uint remove);

PeekMessageমূলত আপনাকে পরের বার্তাকে কাতারে দেখার অনুমতি দেয়; এটি উপস্থিত থাকলে সত্য ফিরে আসে, অন্যথায় মিথ্যা। এই সমস্যাটির উদ্দেশ্যে, প্যারামিটারগুলির মধ্যে কোনওটিই বিশেষভাবে প্রাসঙ্গিক নয়: এটি কেবল ফেরতের মূল্যের জন্য গুরুত্বপূর্ণ। এটি আপনাকে এমন একটি ফাংশন লিখতে দেয় যা আপনাকে জানায় যে অ্যাপ্লিকেশনটি এখনও নিষ্ক্রিয় রয়েছে (যা এখনও সারিতে কোনও বার্তা নেই):

bool IsApplicationIdle () {
    NativeMessage result;
    return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}

আপনার সম্পূর্ণ গেম লুপটি লেখার জন্য আপনার যা দরকার তা এখন রয়েছে:

class MainForm : Form {
  public MainForm () {
    Application.Idle += HandleApplicationIdle;
  }

  void HandleApplicationIdle (object sender, EventArgs e) {
    while(IsApplicationIdle()) {
      Update();
      Render();
    }
  }

  void Update () {
    // ...
  }

  void Render () {
    // ...
  }

  [StructLayout(LayoutKind.Sequential)]
  public struct NativeMessage
  {
      public IntPtr Handle;
      public uint Message;
      public IntPtr WParameter;
      public IntPtr LParameter;
      public uint Time;
      public Point Location;
  }

  [DllImport("user32.dll")]
  public static extern int PeekMessage(out NativeMessage message, IntPtr window, uint filterMin, uint filterMax, uint remove);
}

তদ্ব্যতীত, এই পদ্ধতির সাথে ক্যানোনিকাল নেটিভ উইন্ডোজ গেম লুপটি যতটা সম্ভব সম্ভব (পি / ইনভোকের উপর ন্যূনতম নির্ভরতার সাথে) মেলে যা দেখতে দেখতে:

while (!done) {
    if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
        TranslateMessage(&message);
        DispatchMessage(&message);
    }
    else {
        Update();
        Render();
    }
}

এই জাতীয় উইন্ডোজ এপিআইএস বৈশিষ্ট্যগুলি ব্যবহার করার দরকার কী? একটি নির্দিষ্ট স্টপ ওয়াচ দ্বারা নিয়ন্ত্রিত কিছুক্ষণ ব্লক করা (fps নিয়ন্ত্রণের জন্য) কি যথেষ্ট হবে না?
আমির লিমা

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

নেটিভ উইন্ডোজ গেম লুপগুলি একই কৌশলটি ব্যবহার করে (তুলনা করার জন্য আমি আমার উত্তরটি সংশোধন করেছিলাম একটি সহজ সমাধান অন্তর্ভুক্ত করার জন্য -style লুপ (ভাল স্পষ্টতা এবং তুলনায় জিসি প্রভাব সঙ্গে টাইমার ব্যবহার WM_TIMERভিত্তিক বেশী)।
জোশ

@ জোশপেট্রি পরিষ্কার হওয়ার জন্য, অলস জন্য উপরের চেকিংয়ে স্লিমডিএক্সের জন্য একটি ফাংশন ব্যবহার করা হয়েছে। এটি উত্তরে অন্তর্ভুক্ত করা কি আদর্শ হবে? অথবা এটি কি কেবলমাত্র আপনি 'ইসপ্লিকেশনআইডল' পড়ার জন্য কোডটি সম্পাদনা করেছেন যা স্লিমডিএক্স অংশ?
ভন হিলস

** দয়া করে আমাকে উপেক্ষা করুন, আমি বুঝতে পেরেছি আপনি এটি আরও নিচে সংজ্ঞায়িত করেছেন ... :)
ভন হিলস

2

জোশের উত্তরের সাথে সম্মত, কেবল আমার 5 সেন্ট যোগ করতে চাই। উইনফোর্ডস ডিফল্ট বার্তা লুপ (অ্যাপ্লিকেশন। রুন) নিম্নলিখিত (পি / অনুরোধ ছাড়াই) প্রতিস্থাপন করা যেতে পারে:

[STAThread]
static void Main()
{
    using (Form1 f = new Form1())
    {
        f.Show();
        while (true) // here should be some nice exit condition
        {
            Application.DoEvents(); // default message pump
        }
    }
}

এছাড়াও আপনি যদি বার্তা পাম্পে কিছু কোড ইনজেকশন করতে চান তবে এটি ব্যবহার করুন:

public partial class Form1 : Form
{
    protected override void WndProc(ref Message m)
    {
        // this code is invoked inside default message pump
        base.WndProc(ref m);
    }
}

2
আপনি যদি এই পদ্ধতিটি বেছে নেন তবে আপনাকে ডোইভেন্টস () আবর্জনা-উত্পাদনের ওভারহেড সম্পর্কে সচেতন হওয়া দরকার ।
জোশ

0

আমি বুঝতে পারি যে এটি একটি পুরানো থ্রেড, তবে আমি উপরে প্রস্তাবিত কৌশলগুলির দুটি বিকল্প সরবরাহ করতে চাই। আমি তাদের প্রবেশের আগে, এখন পর্যন্ত দেওয়া প্রস্তাবগুলি নিয়ে কিছু সমস্যা রয়েছে:

  1. পিকম্যাসেজ যথেষ্ট ওভারহেড বহন করে, লাইব্রেরি পদ্ধতিতে যেগুলি এটি বলে (স্লিমডিএক্স ইসপ্লিকেশনআইডল)।

  2. আপনি যদি রাফিনপুট বাফার নিয়োগ করতে চান তবে আপনাকে ইউআই থ্রেড বাদে অন্য থ্রেডে পিকম্যাসেজ সহ বার্তা পাম্পটি পোল করতে হবে, যাতে আপনি এটিতে দুবার কল করতে চান না।

  3. অ্যাপ্লিকেশন.ডাউনভেন্টস একটি শক্ত লুপে ডাকা ডিজাইন করা হয়নি, জিসির সমস্যাগুলি দ্রুত উদ্ভূত হবে।

  4. অ্যাপ্লিকেশন.আইডল বা পিকম্যাসেজ ব্যবহার করার সময়, কারণ আপনি কেবল তখনই কাজ করছেন যখন কোডের গন্ধ ছাড়াই আপনার উইন্ডোটি সরানো বা পুনরায় আকার দেওয়ার সময় আপনার গেম বা অ্যাপ্লিকেশন চলবে না।

এগুলির চারপাশে কাজ করার জন্য (আপনি যদি রাআইপুট রাস্তায় নিচে যান তবে 2 বাদে) আপনি যা করতে পারেন:

  1. একটি থ্রেডিং তৈরি করুন.এখানে আপনার গেম লুপটি পড়ুন এবং চালান।

  2. ইসলংরানিং পতাকা সহ একটি থ্রেডিং.টাস্ক.টাস্ক তৈরি করুন এবং এটি সেখানে চালান। মাইক্রোসফ্ট সুপারিশ করে যে এই দিনগুলিতে থ্রেডের জায়গায় টাস্কগুলি ব্যবহার করা উচিত এবং এটি কেন তা কঠিন নয়।

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

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