سؤال

أقوم بتطوير استنساخ لعبة استراتيجية في الوقت الفعلي على منصة Java ولدي بعض الأسئلة المشكورة حول مكان وضع وكيفية إدارة حالة اللعبة. تستخدم اللعبة Swing / Java2D كما التقديم. في مرحلة التطوير الحالية، لا توجد محاكاة ولا توجد محاكاة لا يوجد بها، والمستخدم فقط قادر على تغيير حالة اللعبة (على سبيل المثال، بناء / هدم مبنى وإزالة خطوط الإنتاج، والأساطيل والمعدات). لذلك، يمكن إجراء معالجة حالة اللعبة في مؤشر ترابط الأحداث دون أي بحث في تقديم. تستخدم حالة اللعبة أيضا لعرض المعلومات المجمعة المختلفة للمستخدم.

ومع ذلك، فأنا بحاجة إلى إدخال المحاكاة (على سبيل المثال، بناء التقدم، والتغيرات السكانية، وحركات الأسطول، وعملية التصنيع، وما إلى ذلك)، فإن تغيير حالة اللعبة في جهاز توقيت و EDT سيتبطئ بالتأكيد التقديم.

دعونا نقول إن عملية المحاكاة / AI يتم تنفيذها في كل 500 ساعة وأستعبها SwingWorker لحساب حوالي 250 دورة في الطول. كيف يمكنني التأكد من أن عدم وجود شرط سباق فيما يتعلق بحكومة اللعبة يقرأ بين المحاكاة وتفاعل المستخدم المحتمل؟

أعلم أن نتيجة المحاكاة (وهي كمية صغيرة من البيانات) يمكن أن تنتقل بكفاءة مرة أخرى إلى إصدار EDT عبر SwentUtilities.invokelater ().

يبدو أن نموذج حالة اللعبة معقدا للغاية لتكون غير ممكنة لاستخدام فئات قيمة ثابتة في كل مكان.

هل هناك نهج صحيح نسبيا للقضاء على حالة القراءة هذه؟ ربما تقوم بتناسل الدولة لعبة كاملة / جزئية في كل توقيت أو تغيير مساحة المعيشة في حالة اللعبة من EDT إلى بعض الخيط الآخر؟

تحديث: (من التعليقات التي أعطيتها) تعمل اللعبة مع اللاعبين الذين يتم التحكم فيه عن 13 منظمة العفو الدولية، 1 لاعب بشريين ولديه حوالي 10000 كائنات لعبة (الكواكب والمباني والمعدات والبحوث، إلخ). كائن لعبة على سبيل المثال لديه السمات التالية:

العالم (الكواكب واللاعبين والأساطيل، ...) Planet (الموقع، المالك، السكان، نوع، الخريطة، المباني، الضرائب، التخصيص، ... Building (موقع، مكنت، طاقة، عامل، صحة، ...)

في سيناريو، يبني المستخدم مبنى جديد على هذا الكوكب. يتم تنفيذ ذلك في EDT حيث يجب تغيير مجموعة الخريطة والمباني. بالتوازي مع هذا، يتم تشغيل المحاكاة في كل 500 مللي ثانية لحساب تخصيص الطاقة للمباني على جميع كواكب اللعبة، والتي تحتاج إلى اجتياز مجموعة المباني لجمع الإحصاءات. إذا تم حساب التخصيص، فسيتم تقديمه إلى EDT ويتم تعيين حقل طاقة لكل مبنى.

تفاعلات لاعب الإنسان فقط لديها هذه الخاصية هذه الخاصية، لأن نتائج حساب AI يتم تطبيقها على الهياكل في EDT على أي حال.

بشكل عام، 75٪ من سمات الكائن ثابتة ويستخدم فقط للعرض. ما تبقى منه قابلة للتغيير إما عن طريق تفاعل المستخدم أو محاكاة / قرار AI. كما تم ضمانه، أنه لا يتم تشغيل خطوة محاكاة جديدة / منظمة العفو الدولية حتى كتابه السابق مرة أخرى.

أهدافي هي:

  • تجنب تأخير تفاعل المستخدم، مثل المستخدم يضع المبنى على الكوكب وفقط بعد 0.5S يحصل على ردود الفعل المرئية
  • تجنب حظر EDT مع حساب، قفل الانتظار، إلخ.
  • تجنب قضايا التزامن مع جمل اجتياز وتعديل، تغييرات السمة

خيارات:

  • غرامة كائن حبيبات قفل
  • مجموعات ثابتة
  • الحقول المتقلبة
  • لقطة جزئية

كل هذه لها مزايا وعيوب وتأثير النموذج واللعبة.

تحديث 2: أنا أتكلم عن هذه لعبه. استنساخ بلدي هو هنا. وبعد قد تساعد Screenshots في تخيل تفاعلات التقديم ونموذج البيانات.

تحديث 3:

سأحاول إعطاء عينة رمز صغيرة لتوضيح مشكلتي كما يبدو من التعليقات التي يساء فهمها:

List<GameObject> largeListOfGameObjects = ...
List<Building> preFilteredListOfBuildings = ...
// In EDT
public void onAddBuildingClicked() {
    Building b = new Building(100 /* kW */);
    largeListOfGameObjects.add(b);
    preFilteredListOfBuildings.add(b);
}
// In EDT
public void paint(Graphics g) {
    int y = 0;
    for (Building b : preFilteredListOfBuildings) {
        g.drawString(Integer.toString(b.powerAssigned), 0, y);
        y += 20;
    }
}
// In EDT
public void assignPowerTo(Building b, int amount) {
    b.powerAssigned = amount;
}
// In simulation thread
public void distributePower() {
    int sum = 0;
    for (Building b : preFilteredListOfBuildings) {
        sum += b.powerRequired;
    }
    final int alloc = sum / (preFilteredListOfBuildings.size() + 1);
    for (final Building b : preFilteredListOfBuildings) {
        SwingUtilities.invokeLater(=> assignPowerTo(b, alloc));            
    }
}

وبالتالي فإن التداخل بين onaddbuildingclicked () وتاجر (). الآن تخيل القضية حيث لديك 50 من هذا النوع من التداخل بين أجزاء مختلفة من نموذج اللعبة.

هل كانت مفيدة؟

المحلول

هذا يبدو أنه يمكن أن يستفيد من نهج العميل / الخادم:

اللاعب هو عميل - تفاعل وتقديم يحدث في هذه الغاية. لذا يضغط اللاعب على زر، يذهب الطلب إلى الخادم. يعود الرد من الخادم، ويتم تحديث حالة اللاعب. عند أي نقطة بين هذه الأشياء التي تحدث، يمكن إعادة رسم الشاشة، وتعكس حالة اللعبة حيث يعرف ذلك العميل حاليا.

AI هو كذلك العميل - إنه ما يعادل بوت.

المحاكاة هي الخادم. يحصل على تحديثات من عملائها في أوقات مختلفة وتحديث حالة العالم، ثم يرسل هذه التحديثات للجميع حسب الاقتضاء. إليك أين تتوافق مع وضعك: تتطلب المحاكاة / AI عالم ثابت، والعديد من الأشياء تحدث مرة واحدة. يمكن للخادم ببساطة طابور طلب طلبات تغيير وتطبيقها قبل إرسال التحديثات مرة أخرى إلى العميل (العملاء). إلى حد بعيد المعني بالخادم، فإن عالم اللعبة لا يتغير فعليا في الوقت الفعلي، فهو يتغير كلما قرر الخادم قرر جيدا.

أخيرا، على جانب العميل، يمكنك منع التأخير بين الضغط على الزر ورؤية نتيجة عن طريق القيام ببعض الحسابات التقريبية السريعة وعرض النتيجة (لذلك يتم استيفاء الحاجة العاجلة) ثم عرض النتيجة الأكثر صحة عندما يحصل الخادم للتحدث معك.

لاحظ أن هذا لا يجب أن ينفذ في الواقع في نوع من TCP / IP على شبكة الإنترنت، فقط أنه يساعد على التفكير في الأمر بهذه المصطلحات.

بالتناوب، يمكنك وضع المسؤولية عن إبقاء البيانات متماسكة أثناء المحاكاة في قاعدة بيانات، لأنها مبنية بالفعل مع قفل وتماسك في الاعتبار. شيء مثل SQLite يمكن أن يعمل كجزء من حل غير متصل بالشبكة.

نصائح أخرى

لست متأكدا من أنني أفهم تماما السلوك الذي تبحث عنه، ولكن يبدو وكأنه بحاجة إلى شيء مثل موضوع تغيير الحالة / قائمة الانتظار حتى يتم التعامل مع كل تغييرات الحالة بواسطة مؤشر ترابط واحد.

قم بإنشاء واجهة برمجة تطبيقات، وربما مثل FlowUTIONITION.INVOKELATER () و / أو flowudities.invokeandwait () لحالة انتظار تغيير حالتك للتعامل مع طلبات تغيير الحالة الخاصة بك.

كيف تنعكس في واجهة المستخدم الرسومية أعتقد أن تعتمد على السلوك الذي تبحث عنه. أي لا يمكن سحب الأموال لأن الحالة الحالية هي 0 دولار، أو العودة إلى المستخدم أن الحساب كان فارغا عند معالجة طلب سحب. (ربما ليس مع هذا المصطلحات ؛-))

أسهل النهج هو جعل المحاكاة بسرعة كافية للتشغيل في EDT. تفضل البرامج التي تعمل!

بالنسبة لنموذج الخيط، فإن ما أقترحه هو مزامنة نموذج المجال مع نموذج التقديم. يجب على نموذج Render الاحتفاظ بالبيانات حول ما جاء من نموذج المجال.

للحصول على تحديث: في مؤشر ترابط المحاكاة قفل طراز Render. اجتياز تحديث نموذج التجسيد حيث تختلف الأمور عن ما هو متوقع تحديث نموذج التجسيد. عند الانتهاء من عبور، قم بإلغاء تأمين طراز Render وجدولة إعادة رسم. لاحظ أنه في هذا النهج لا تحتاج إلى مستمعين Bazillion.

يمكن أن يكون لنموذج التجسيد أعماق مختلفة. في One Extrement قد تكون صورة وتشغيل التحديث هو فقط لاستبدال مرجع واحد مع كائن الصورة الجديد (هذا المقبض، على سبيل المثال، تغيير حجم أو غيره من التفاعل السطحي جيدا). قد لا تغني عناء التحقق مما إذا كان العنصر يحتوي على تغيير ويوم ما عليك سوى تحديث Eveything.

إذا كانت تغيير حالة اللعبة سريعة (بمجرد أن تعرف ما لتغييره)، يمكنك التعامل مع حالة اللعبة مثل نماذج التأرجح الأخرى وتغيير الحالة أو عرض الحالة فقط في EDT. إذا كان تغيير حالة اللعبة ليست سريعة، فيمكنك إما مزامنة تغيير الحالة وأفعلها في عامل التأرجح / الموقت (ولكن ليس EDT) أو يمكنك القيام بذلك في مؤشر ترابط منفصل تراجعه بشكل مشابه إلى EDT (عند النقطة انظر إلى استخدام BlockingQueue للتعامل مع طلبات التغيير). آخر مفيد أكثر مما إذا كان لا يتعين على واجهة المستخدم استرداد المعلومات من حالة اللعبة ولكن بدلا من ذلك، فإن تغييرات التقديم المرسلة عبر المستمعين أو المراقبين.

هل من الممكن تحديث حالة اللعبة بشكل تدريجي ولا يزال لديك نموذج ثابت؟ على سبيل المثال إعادة حساب مجموعة فرعية من كوكب / كائنات اللاعب / الأسطول بين تحديثات المستخدمين / المستخدمين.

إذا كان الأمر كذلك، فيمكنك تشغيل تحديثات تدريجية في EDT تقوم بحساب جزء صغير فقط من الحالة قبل السماح ب EDT لمعالجة مدخلات المستخدم وتقديمها.

بعد التحديث الإضافي في EDT، ستحتاج إلى تذكر مقدار النموذج الذي يظل محدثا وجدولة جدول أعمال تأرجح جديد على EDT لمواصلة هذه المعالجة بعد أي مدخلات مستخدم في انتظار وتم إجراء تقديم.

يجب أن يسمح لك ذلك بتجنب نسخ أو قفل نموذج اللعبة مع الاستفادة من تفاعلات المستخدم استجابة.

أعتقد أنه لا ينبغي أن يكون لديك متجر عالمي أو إجراء أي بيانات أو إجراء تغييرات على أي كائنات نفسها، ويجب استخدامها فقط للحفاظ على مرجع إلى كائن وعندما يجب تغيير هذا الكائن، فإن اللاعب يجعل التغيير تغييره مباشرة. في هذا الحدث، فإن الشيء الوحيد الذي تحتاج إلى القيام به هو مزامنة كل كائن في عالم اللعبة بحيث يكون عندما يقوم اللاعب بتغيير، لا يمكن لأي مشغل آخر القيام بذلك. إليك مثال على ما أفكر فيه:

لاعب يحتاج إلى معرفة كوكب، لذلك يسأل العالم عن هذا الكوكب (كيف يعتمد على تنفيذك). يعيد العالم مرجعا إلى Planet Object Player أطلبه. لاعب يقرر إجراء تغيير، لذلك يفعل ذلك. دعنا نقول أنه يضيف مبنى. تتم مزامنة الطريقة لإضافة مبنى إلى الكوكب بحيث يمكن لاعب واحد فقط القيام بذلك في وقت واحد. سيستمر المبنى وقت البناء الخاص به (إن وجد) حتى يتم تحرير طريقة بناء كوكب الوكالة على الفور تقريبا. وبهذه الطريقة يمكن للاعبين المتعددين طلب معلومات عن نفس الكوكب في نفس الوقت دون التأثير على بعضهم البعض واللاعبين يمكنهم إضافة المباني في وقت واحد تقريبا دون ظهور الكثير من التأخر. إذا كان لاعبان يبحثان عن مكان لوضع المبنى (إذا كان هذا جزءا من لعبتك)، فسيكون التحقق من ملاءمة الموقع سيكون استعلام وليس تغييرا.

أنا آسف إذا لم يرد هذا سؤالا، فأنا لست متأكدا مما إذا كنت مفهوما بشكل صحيح.

ماذا عن تطبيق بريزر وأنابيب المرشحات. يقوم الأنابيب بتوصيل المرشحات معا وطلبات قائمة الانتظار إذا كان المرشح ليس سريعا بما فيه الكفاية. يحدث المعالجة داخل المرشحات. المرشح الأول هو محرك AI أثناء تنفيذ محرك التقديم بواسطة مجموعة من المرشحات اللاحقة.

في كل علامة توقيت، يتم حساب الحالة العالمية الديناميكية الجديدة بناء على جميع المدخلات (الوقت أيضا مدخلات) و ينسخ إدراجها في الأنبوب الأول.

في أبسط حالة يتم تنفيذ محرك التقديم الخاص بك كمرشح واحد. يتطلب الأمر فقط لقطات الدولة من أنبوب الإدخال ويشعر ذلك جنبا إلى جنب مع الحالة الثابتة. في لعبة LIVE، قد يرغب محرك العرض في تخطي الدول إذا كان هناك أكثر من واحد في الأنبوب أثناء قيامك بقياس معيار أو إخراج مقطع فيديو، فأنت تريد تقديم كل واحد.

كلما زاد عدد المرشحات التي يمكنك تحلل محرك التقديم الخاص بك في ذلك، كلما كان التوازي الأفضل. ربما يكون من الممكن حتى تحلل محرك AI، على سبيل المثال، قد ترغب في فصل الحالة الديناميكية إلى حالة تغيير سريعة وبطيئة.

تمنحك هذه العمارة توازنا جيدا دون مزامنة.

مشكلة في هذه الهندسة المعمارية هي أن مجموعة القمامة سوف تعمل بشكل متكرر على تجميد جميع المواضيع في كل مرة، مما يجعل مقتل أي ميزة مكتسبة من متعدد الخيوط.

يبدو أنك بحاجة إلى PRIMITEQUEUE لوضع التحديثات على النموذج على، حيث التحديثات FRMO، يكون لدى المستخدم أولوية عبر التحديثات من المحاكاة وغيرها من المدخلات. ما أسمعه تقوله هو أن المستخدم يحتاج دائما إلى ملاحظات فورية على أفعاله، حيث يمكن أن يكون للمدخلات الأخرى (المحاكاة، وإلا) من العمال قد يستغرق وقتا أطول من خطوة محاكاة واحدة. ثم مزامنة على priorityqueue.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top