Godot: أضف نظام GDScript Trait.

تم إنشاؤها على ١٨ أكتوبر ٢٠١٨  ·  93تعليقات  ·  مصدر: godotengine/godot

(تعديل:
لتقليل مشكلات XY الإضافية:
المشكلة التي يتم تناولها هنا هي أن نظام / لغات البرمجة النصية في Godot's Node-Scene لا تدعم حتى الآن إنشاء تطبيقات مجمعة قابلة لإعادة الاستخدام تكون 1) خاصة بميزات عقدة الجذر و 2) يمكن تبديلها و / أو دمجها. يمكن استخدام البرامج النصية ذات الأساليب الثابتة أو العقد الفرعية مع البرامج النصية للبت الأخير ، وفي كثير من الحالات يعمل هذا. ومع ذلك ، يفضلك Godot عمومًا أن تحتفظ بمنطق السلوك العام للمشهد الخاص بك مخزّنًا في العقدة الجذرية بينما يستخدم البيانات المحسوبة بواسطة العقد الفرعية أو يفوض المهام الفرعية المنحرفة بشكل كبير لهم ، على سبيل المثال ، KinematicBody2D لا يدير الرسوم المتحركة لذلك يفوض ذلك إلى AnimationPlayer.

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

أفترض أن موضوع هذه المشكلة يدور حول كيفية التعامل مع المشكلة المذكورة أعلاه أكثر مما يتعلق بشكل خاص بـ GDScript ، ومع ذلك أعتقد أن سمات GDScript ستكون الطريقة الأبسط والأكثر مباشرة لحل المشكلة.
)

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

أتخيل شيئًا حيث يمكن استخدام أي ملف GDScript كسمة لملف GDScript آخر ، طالما أن النوع المتتبع يمتد إلى فئة موروثة بواسطة البرنامج النصي المدمج ، أي أن Sprite-Extended GDScript لا يمكنها استخدام Resource GDScript كصفة ، ولكن يمكنها استخدام Node2D GDScript. أتخيل بناء جملة مشابهًا لهذا:

# move_right_trait.gd
extends Node2D
class_name MoveRightTrait # not necessary, but just for clarity
func move_right():
    position.x += 1

# my_sprite.gd
extends Sprite
is MoveRightTrait # maybe add a 'use' or 'trait' keyword for this instead?
is "res://move_right_trait.gd" # alternative if class_name isn't used
func _physics_process():
    move_right() # MoveRightTrait's content has been merged into this script
    if MoveRightTrait in self:
        print("I have a MoveRightTrait")

يمكنني رؤية طريقتين للقيام بذلك:

  1. تحليل النص عبر RegEx مسبقًا لـ "^ سمة \عملية إعادة التحميل). سيتعين علينا عدم دعم تداخل السمات أو إعادة فحص شفرة المصدر التي تم إنشاؤها باستمرار بعد كل تكرار لمعرفة ما إذا كان قد تم إدخال المزيد من السمات.
  2. قم بتحليل البرنامج النصي بشكل طبيعي ، ولكن قم بتعليم المحلل اللغوي التعرف على الكلمة الأساسية ، وتحميل البرنامج النصي المشار إليه ، وتحليل هذا النص البرمجي ، ثم إلحاق محتوى ClassNode الخاص به إلى ClassNode الذي تم إنشاؤه من البرنامج النصي الحالي (أخذ النتائج التي تم تحليلها لأحد البرامج النصية وإضافتها بشكل فعال إلى النتائج التي تم تحليلها للنص الآخر). هذا من شأنه أن يدعم تداخل الأنواع المحددة تلقائيًا.

من ناحية أخرى ، قد يرغب الأشخاص في أن يكون للسمة GDScript اسم ولكن قد لا يرغبون في ظهور اسم فئة GDScript الخاص بـ GDScript في مربع الحوار CreateDialog (لأنه ليس من المفترض أن يتم إنشاؤه بمفرده). في هذه الحالة ، قد لا يكون من الجيد فعلاً جعل أي برنامج نصي يدعمه ؛ فقط تلك التي تم تمييزها خصيصًا (ربما عن طريق كتابة "سمة" في أعلى الملف؟). على أي حال ، أشياء للتفكير فيها.

أفكار؟

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

archived discussion feature proposal gdscript

التعليق الأكثر فائدة

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

السمات / Mixins موجودة في PHP ، و Ruby ، ​​و D ، و Rust ، و Haxe ، و Scala ، والعديد من اللغات الأخرى (كما هو مفصل في ويكي المرتبطة) ، لذلك يجب أن يكونوا بالفعل على دراية واسعة بالأشخاص الذين لديهم ذخيرة واسعة من الإلمام بلغة البرمجة.

إذا أردنا تنفيذ الواجهات (التي لا أعارضها أيضًا ، خاصة مع الطباعة الثابتة الاختيارية القادمة) ، فسيكون ذلك بشكل فعال مجرد وسيلة لتحديد توقيعات الوظائف ثم المطالبة بتنفيذ نصوص GDScript ذات الصلة هذه التوقيعات الوظيفية ، مع السمات متضمنة (إذا كانت موجودة في تلك النقطة).

ال 93 كومينتر

ما هي الفائدة بدلاً من: extends "res://move_right_trait.gd"

MrJustreborn لأنه يمكن أن يكون لديك سمات متعددة في الفصل ، لكن لا يمكنك أن ترث سوى نص برمجي واحد.

إذا فهمت بشكل صحيح ، فإن هذا هو أساسًا ما تسميه C # " واجهات " ، ولكن مع طرق غير مجردة؟ قد يكون من الأفضل استدعاء واجهات الميزات بدلاً من السمات لتكون مألوفة للمبرمجين.

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

السمات / Mixins موجودة في PHP ، و Ruby ، ​​و D ، و Rust ، و Haxe ، و Scala ، والعديد من اللغات الأخرى (كما هو مفصل في ويكي المرتبطة) ، لذلك يجب أن يكونوا بالفعل على دراية واسعة بالأشخاص الذين لديهم ذخيرة واسعة من الإلمام بلغة البرمجة.

إذا أردنا تنفيذ الواجهات (التي لا أعارضها أيضًا ، خاصة مع الطباعة الثابتة الاختيارية القادمة) ، فسيكون ذلك بشكل فعال مجرد وسيلة لتحديد توقيعات الوظائف ثم المطالبة بتنفيذ نصوص GDScript ذات الصلة هذه التوقيعات الوظيفية ، مع السمات متضمنة (إذا كانت موجودة في تلك النقطة).

ربما كلمة رئيسية مثل includes ؟

extends Node2D
includes TraitClass

على الرغم من أن الأسماء الأخرى مثل السمات ، و mixin ، و has ، وما إلى ذلك جيدة بالتأكيد أيضًا.

تعجبني أيضًا فكرة وجود بعض الخيارات لاستبعاد class_name type من قائمة الإضافة. يمكن أن تصبح مزدحمة للغاية مع الأنواع الصغيرة التي لا تعمل من تلقاء نفسها كعقد.

قد يكون مجرد موضوع خاص به.

(حذفت تعليقي بالصدفة ، يأس! أيضًا ، إلزامي "لماذا لا تسمح فقط بنصوص متعددة ، الوحدة تفعل ذلك" )

كيف سيعمل هذا في VisualScript ، على كل حال؟

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

تضمين التغريدة

كيف سيعمل هذا في VisualScript ، على كل حال؟

إذا تم ذلك بالطريقة التي اقترحتها ، فلن يحدث لـ VisualScript على الإطلاق. GDScript فقط. سيتم تصميم أي نظام سمة تم تنفيذه لـ VisualScript بشكل مختلف تمامًا لأن VisualScript ليس لغة محللة. لا يستبعد الاحتمال على الإطلاق (سيحتاج فقط إلى التنفيذ بشكل مختلف). بالإضافة إلى ذلك ، ربما يجب أن نفكر في الحصول على دعم وراثة VisualScript أولاً؟ مضحك جدا

أيضًا ، هل يمكن أن يكون من المفيد تضمين واجهة مفتش للسمات ، إذا تم تنفيذ السمات؟

لن يكون هناك الكثير من النقاط. تنقل السمات التفاصيل ببساطة إلى GDScript ، وتعطيها الخصائص والثوابت والإشارات والطرق التي تحددها السمة.

أتخيل أن بعض حالات الاستخدام للسمات قد تتضمن حالات استخدام حيث توجد سمات فقط ولا يوجد نص برمجي (على الأقل ، لا يوجد نص بجانب واحد يتضمن ملفات السمات).

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

أتساءل عما إذا كان الجهد المبذول في إنشاء مثل هذه الواجهة يستحق كل هذا العناء

إن إنشاء واجهة المفتش بطريقة ما لن يكون له معنى كبير لـ GDScript وحده. قد تتضمن إضافة سمة أو إزالتها تحرير التعليمات البرمجية المصدر مباشرةً لخاصية مورد البرنامج النصي source_code ، أي أنها ليست خاصية في البرنامج النصي نفسه. لذلك ، إما ...

  1. يجب تعليم المحرر كيفية التعامل على وجه التحديد مع التحرير المناسب للشفرة المصدر لملفات GDScript للقيام بذلك (عرضة للخطأ) ، أو ...
  2. يجب أن تدعم جميع النصوص البرمجية السمات بحيث يمكن لـ GDScriptLanguage توفير عمليتها الداخلية الخاصة لإضافة السمات وإزالتها (ولكن ليس كل اللغات تدعم السمات ، لذلك لن تكون الخاصية ذات معنى في جميع الحالات).

ما هي الحاجة لهذه الميزة؟ هل هناك أي شيء يسمح به هذا ولا يمكنك فعله الآن؟ أم أنه يجعل التعامل مع بعض المهام أسرع بشكل ملحوظ؟

أفضل الاحتفاظ بـ GDscript بلغة بسيطة بدلاً من إضافة ميزات معقدة تقريبًا لم يتم استخدامها مطلقًا.

إنه يحل مشكلة Child-Nodes-As-Script-Dependencies التي واجهها هذا الرجل ، لكنه لا يأتي مع أي من نفس النوع من الأمتعة التي كانت لدى MultiScript لأنها مقيدة بلغة واحدة. يمكن لوحدة GDScript عزل المنطق حول كيفية ارتباط السمات ببعضها البعض والنص الرئيسي بينما سيكون حل الاختلافات بين اللغات المختلفة أكثر تعقيدًا.

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

groud @ Zireael07 أعني ، سيكون الأسلوب الأكثر جذرية ومتعدد اللغات هو 1) إعادة تصميم الكائن بالكامل لاستخدام ScriptStack لدمج البرامج النصية المكدسة في تمثيل نصي واحد ، 2) إعادة تقديم MultiScript وإنشاء دعم محرر يحول تلقائيًا إضافة نصوص إلى نصوص متعددة (أو فقط جعل جميع البرامج النصية متعددة النصوص من أجل البساطة ، وفي هذه الحالة سيكون تنفيذ MultiScript في الأساس ScriptStack الخاص بنا) ، أو 3) تنفيذ نوع من نظام السمات عبر اللغات لنوع الكائن الذي يمكن أن يندمج في النصوص المرجعية الممتدة كسمات ، تتضمن محتواها تمامًا مثل النص النموذجي. كل هذه الخيارات هي وسيلة أكثر توغلًا للمحرك. هذا يبقي كل شيء أبسط.

لا أعتقد أن السمات ضرورية. أكثر ما نحتاجه هو دورة إطلاق المحرك السريعة. أعني أننا بحاجة إلى جعل المحرك أكثر مرونة لإضافة ميزة جديدة بسيطة ، ما عليك سوى إضافة ملفات dll جديدة أو نحو ذلك وتكامل المحرك تلقائيًا معه ، مثل نمط المكونات الإضافية في معظم IDE. على سبيل المثال ، أنا بحاجة ماسة حقًا إلى عمل مقبس الويب ، ولست بحاجة إلى الانتظار حتى الإصدار 3.1. 3.1 مكسورة للغاية الآن مع الكثير من الأخطاء. سيكون رائعًا إذا كانت لدينا هذه الميزة. يمكن للفئة الجديدة الحقن التلقائي إلى GDScript من .dll أو. العشوائي في بعض المسارات. لا أعرف مقدار الجهد الذي يجب القيام به في c ++ ولكن آمل ألا يكون هذا صعبًا جدًا

@ fian46 حسنًا ، إذا قام شخص ما بتنفيذ Websockets كمكوِّن إضافي لـ GDNative قابل للتنزيل ، إذن نعم ، ما وصفته سيكون سير العمل. بدلاً من ذلك ، اختاروا جعلها ميزة متكاملة متوفرة في محرك الفانيليا. لا يوجد شيء يمنع الأشخاص من إنشاء ميزات بهذه الطريقة ، لذا فإن وجهة نظرك لا علاقة لها بموضوع هذا العدد.

عفوًا ، لا أعرف أن GDNative موجود. السمة رائعة ولكن هل من الأسهل إنشاء فئة سمة مزيفة وتثبيتها ثم استدعاء الوظيفة لاحقًا مثل البرنامج النصي الأساسي؟

إذا كان نص جودو عبارة عن فصل دراسي غير مسمى ، فلماذا لا يتم إنشاء "move_right_trait.gd" في "my_sprite.gd"؟
آسف لجهلي إذا لم أفهم المشكلة.

أتفهم استخدام السمات في لغات مكتوبة بقوة أكبر مثل Rust أو (واجهات في) C ++ ، ولكن في لغة مكتوبة بطة ، أليس هذا غير ضروري بعض الشيء؟ يجب أن يتيح لك تنفيذ نفس الوظائف ببساطة تحقيق واجهة موحدة بين الأنواع الخاصة بك. أعتقد أنني غير متأكد بعض الشيء من المشكلة الدقيقة بالطريقة التي يتعامل بها GDScript مع الواجهات أو كيف يمكن لنظام السمات أن يساعد حقًا.

ألا يمكنك أيضًا استخدام التحميل المسبق ("Some-other-behavior.gd") وتخزين النتائج في متغير لتحقيق نفس التأثير بشكل أساسي؟

@ fian46DriNeo حسنًا ، نعم ولا. إن تحميل البرامج النصية واستخدام فئات البرامج النصية يعتني بذلك بالفعل ، لكن المشكلة تمتد إلى ما هو أبعد من ذلك.

تضمين التغريدة

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

لا تكمن المشكلة في تحقيق واجهة موحدة أنت على حق ، فالكتابة بالبط تحل بشكل جيد ، ولكن بدلاً من ذلك تنظيم (دمج / تبديل) مجموعات من التطبيقات ذات الصلة بكفاءة.


في الإصدار 3.1 ، باستخدام فئات البرامج النصية ، يمكنك تحديد الوظائف الثابتة في نص مرجعي (أو أي نوع بالفعل) ثم استخدام هذا البرنامج النصي كمساحة اسم للوصول إلى هذه الوظائف في GDScript عالميًا.

extends Reference
class_name Game
static func print_text(p_text):
    print(p_text)
# can even add inner classes for sub-namespaces

extends Node
func _ready():
    Game.print_text("Hello World!")

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

على سبيل المثال ، ماذا لو كان لدي KinematicBody2D وأريد أن يكون لدي سلوك "Jump" و "Run"؟ سيحتاج كل من هذه السلوكيات إلى الوصول إلى معالجة الإدخال وخصائص move_and_slide الخاصة بـ KinematicBody2D. من الناحية المثالية ، سأكون قادرًا على تبديل تنفيذ كل سلوك بشكل مستقل والاحتفاظ بكل التعليمات البرمجية لكل سلوك في نصوص منفصلة.

في الوقت الحالي ، جميع مهام سير العمل التي أعرفها لهذا الغرض ليست مثالية.

  1. إذا احتفظت بجميع التطبيقات في نفس البرنامج النصي وقمت فقط بتبديل الوظائف التي يتم استخدامها ...

    1. قد يتضمن تغيير "السلوكيات" تبديل العديد من الوظائف كمجموعة ، لذلك لا يمكنك تجميع تغييرات التنفيذ بشكل فعال.

    2. توجد جميع الوظائف لكل سلوك (X * Y) في النص الفردي الخاص بك ، لذلك يمكن أن ينتفخ بسرعة كبيرة.

  2. يمكنك فقط استبدال النص بأكمله ، ولكن هذا يعني أنه يجب عليك إنشاء نص جديد لكل مجموعة من السلوكيات بالإضافة إلى أي منطق يستخدم هذه السلوكيات.
  3. إذا كنت تستخدم العقد الفرعية كاعتماديات للبرنامج النصي ، فهذا يعني أن لديك هذه العقد الغريبة Node2D "المكونة" التي تلتقط الوالد وتستدعي طريقة move_and_slide لذلك ، وهو نوع غير طبيعي نسبيًا.

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

    • أنت أيضًا لا تحصل على إكمال التعليمات البرمجية المناسب للعمليات المقصودة للعقد نظرًا لأنها مشتقة من Node2D والمقصود هو قيادة سلوك أحد الوالدين KinematicBody2D.

الآن ، سأعترف بأن الخيار 3 هو سير العمل الأكثر فاعلية حاليًا ، وأنه يمكن حل مشكلاته إلى حد كبير باستخدام الكتابة الثابتة سهلة الاستخدام لـ GDScript في الإصدار 3.1. ومع ذلك ، هناك قضية أكثر جوهرية في اللعبة.

يشتمل نظام مشهد العقدة في Godot بشكل عام على شكل المستخدمين الذين ينشئون عُقدًا أو مشاهد تؤدي وظيفة معينة ، في نظامهم المغلق. يمكنك مثيل تلك العقد / المشاهد في مشهد آخر وجعلها تحسب البيانات التي يتم استخدامها بعد ذلك بواسطة المشهد الأصلي (هذا هو الحال مع علاقة Area2D و CollisionShape2D).

ومع ذلك ، فإن استخدام محرك الفانيليا والتوصية العامة بأفضل الممارسات هي الحفاظ على سلوك المشهد الخاص بك مغلقًا على عقدة الجذر و / أو البرنامج النصي الخاص به. نادرًا ما يكون لديك عقد "مكون سلوك" تخبر الجذر فعليًا بما يجب القيام به (وعندما يكون هناك ، فإنه يشعر بالضيق الشديد). تعتبر عُقد AnimationPlayer / Tween الاستثناءات الوحيدة القابلة للجدل التي يمكنني التفكير فيها ، ولكن حتى عملياتها يتم توجيهها بواسطة الجذر (إنه يفوضها بشكل فعال بالتحكم مؤقتًا). (تحرير: حتى في هذه الحالة ، فإن الرسوم المتحركة والحركة البينية ليست مهمة KinematicBody2D ، لذلك فمن المنطقي أن يتم تفويض هذه المهام. ومع ذلك ، فإن الحركة ، مثل الجري والقفز هي مسؤوليتها) من الأسهل والأكثر طبيعية السماح تطبيق سمة لتنظيم الكود لأنه يحافظ على العلاقات بين العقد بشكل صارم للبيانات / السلوك لأسفل ويحافظ على الكود أكثر عزلًا في ملفات البرنامج النصي الخاصة به.

إيه ، تحديد نفسك على أنك "تنفذ واجهة / سمة" يجب أن يفي أيضًا باختبار * is * رغم ذلك ، وهو أمر مناسب لاختبار وظائف شيء ما.

@ OvermindDL1 أعني ، لقد أعطيت مثالًا لإجراء اختبار كهذا ، لكنني استخدمت in بدلاً من ذلك لأنني أردت التمييز بين الميراث واستخدام السمات.

أعتقد أنني دخلت في مشكلة XY هنا ، يا سيئة. كنت قد أتيت للتو من مشكلتين أخريين (# 23052 ، # 15996) تناولتا هذا الموضوع بطريقة أو بأخرى واعتقدت أنني سأقدم اقتراحًا ، لكنني لم أعطي كل السياق.

groud هذا الحل سيحل إحدى المشاكل التي أثيرت ضد # 19486.

willnationsdev فكرة عظيمة ، وأنا أتطلع إلى ذلك!

من خلال فهمي المحدود ، فإن الشيء الذي يريد نظام السمات هذا تحقيقه هو تمكين شيء مشابه لسير العمل الموضح في هذا الفيديو: https://www.youtube.com/watch؟v=raQ3iHhE_Kk
(ضع في الاعتبار ، أنا أتحدث عن _workflow_ المعروض ، وليس الميزة المستخدمة)

في الفيديو ، تتم مقارنتها بأنواع أخرى من مهام سير العمل ، مع مزاياها وعيوبها.

على الأقل على حد علمي ، هذا النوع من سير العمل مستحيل في GDScript في الوقت الحالي ، بسبب كيفية عمل الوراثة.

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

على الأقل على حد علمي ، هذا النوع من سير العمل مستحيل في GDScript في الوقت الحالي ، بسبب كيفية عمل الوراثة.

هذا الشيء ليس صحيحًا تمامًا ، لأن Godot في الواقع يقوم بهذا بشكل جيد جدًا فيما يتعلق بالتسلسلات الهرمية للعقدة وتصميم المشاهد. المشاهد معيارية بطبيعتها ، يمكن تصدير الخصائص (وحتى تحريكها) مباشرة من المحرر دون التعامل مع الكود ، ويمكن اختبار / تصحيح كل شيء بمعزل عن الآخرين لأنه يمكن تنفيذ المشاهد بشكل مستقل.

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

هذه ليست مشكلة في Unity لأن GameObject ليس لديه أي ميراث حقيقي يمكن للمستخدمين الاستفادة منه. في Unreal ، قد يكون الأمر قليلاً (؟) من المشكلة نظرًا لأن لديهم تسلسلات هرمية داخلية قائمة على العقدة / المكونات للممثلين.

حسنًا ، دعنا نلعب لعبة Devil's Advocate هنا قليلاً ( MysteryGM ، قد تحصل على طرد من هذا). قضيت بعض الوقت في التفكير في كيفية كتابة مثل هذا النظام في Unreal وهذا يعطيني منظورًا جديدًا عنه. آسف للأشخاص الذين كانوا يعتقدون أن هذه ستكون فكرة جيدة / كانوا متحمسين لها:

يضيف تقديم نظام السمات طبقة من التعقيد إلى GDScript كلغة قد تجعل الحفاظ عليها أكثر صعوبة.

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

يؤدي هذا في الواقع إلى إضعاف قابلية تصحيح أخطاء GDScript كلغة نظرًا لأن أي مشكلة معينة قد تتطلب منك تحديد موقعين أو ثلاثة مواقع مختلفة في قاعدة الكود. إذا كان من الصعب العثور على أي من هذه المواقع لأنهم يقولون إنهم موجودون في نص برمجي واحد ولكنهم موجودون بالفعل في مكان آخر - وإذا لم تحدد قابلية قراءة الكود بوضوح الشيء المسؤول عن البيانات / المنطق - فهذه 2 أو 3 يتم مضاعفة الخطوات في مجموعة كبيرة بشكل تعسفي ومرهقة للغاية من الخطوات.

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


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

حسنًا ، ما الذي سيفعله أي محرك غير ECS في هذه الحالة؟ لا يزال التكوين هو الحل ، ولكن في هذه الحالة ، تكون العقدة الكاملة غير معقولة عند أخذها على نطاق واسع / تعقد ديناميكية التسلسل الهرمي للملكية. بدلاً من ذلك ، يمكن للمرء فقط تحديد كائنات تنفيذ بخلاف Node التي تجرد التنفيذ الملموس للسلوك ، ولكنها لا تزال مملوكة للعقدة الجذرية. يمكن القيام بذلك باستخدام برنامج نصي Reference .

# root.gd
extends KinematicBody2D

export(Script) var jump_impl_script = null setget set_jump_impl_script
var jump_impl
func set_jump_impl_script(p_script):
    jump_impl = p_script.new() if p_script else null

export(Script) var move_impl_script = null setget set_move_impl_script
var move_impl
func set_move_impl_script(p_script):
    move_impl = p_script.new() if p_script else null

func _physics_process():
    # use logic involving these...
    move_impl.move(...)
    jump_impl.jump(...)

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

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

على أي حال ، نعم ، أعتقد أنني حددت نوعًا ما المشكلات الصارخة مع نظام السمات ونهج أفضل لحل المشكلة. الصيحة لقضايا مشكلة XY! /س

تعديل:

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

# root.gd
extends KinematicBody2D

# if you use a Resource script AND had a way of specifying that the assigned Resource 
# must extend that script, then the editor would automatically assign an instance of 
# that resource script to the var. No separate instancing or setter necessary.

export(Resource) var jump_impl = null # set jump duration, max height, tween easing via Inspector
export(Resource) var move_impl = null # similarly customize movement from Inspector

# can then create different Resources as different implementations. Because they are resources,
# one can edit them even outside of a scene!
func _physics_process():
    move_impl.move(...)
    jump_impl.jump(...)

ذات صلة: # 22660

تضمين التغريدة

ضع في الاعتبار ، أنا أتحدث عن سير العمل المعروض ، وليس الميزة المستخدمة

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

# root.gd
extends KinematicBody2D

export(Script) var jump_impl_script = null setget set_jump_impl_script
var jump_impl
func set_jump_impl_script(p_script):
jump_impl = p_script.new() if p_script else null
...

واو ، لم أكن أعرف أن التصدير كان بهذه القوة. توقعت أن تكون قادرة فقط على التفاعل مع الأوليات وهياكل البيانات الأخرى.

هذا يجعل تعليقي السابق غير صالح.
كما قلت ، إذا تم تنفيذ نوع من الماكرو لتسهيل تنفيذه ، فستكون أفضل طريقة لتنفيذ سير العمل هذا دون الحاجة إلى MultiScript. ربما لا تكون متعددة الاستخدامات مثل Unity ، لأنك ستظل بحاجة إلى إعلان كل نص ممكن مسبقًا ، ولكن لا يزال خيارًا جيدًا.

تضمين التغريدة

كما قلت ، إذا تم تنفيذ نوع من الماكرو لتسهيل تنفيذه ، فستكون أفضل طريقة لتنفيذ سير العمل هذا دون الحاجة إلى MultiScript.

حسنًا ، النهج القائم على Resource الذي ذكرته في نفس التعليق ، جنبًا إلى جنب مع بعض دعم المحرر الأفضل من # 22660 ، سيجعل الجودة قابلة للمقارنة مع ما يمكن أن تفعله Unity.

ربما لا تكون متعددة الاستخدامات مثل Unity ، لأنك ستظل بحاجة إلى إعلان كل نص ممكن مسبقًا ، ولكن لا يزال خيارًا جيدًا.

حسنًا ، إذا قاموا بإصلاح تلميحات نوع المصفوفة في الإصدار 3.2 ، فيمكنك تحديد مصفوفة تم تصديرها لمسارات الملفات التي يجب أن تمدد نصًا وتضيف بشكل فعال الخاص بك. يمكن القيام بذلك عبر مكون إضافي 3.1 باستخدام فئة EditorInspectorPlugin لإضافة محتوى مخصص إلى المفتش لموارد أو عقد معينة.

أعني ، إذا كنت تريد نظامًا مشابهًا لـ "Unity" ، فحينئذٍ سيكون لديك حقًا عقد فرعية تخبر الجذر بما يجب فعله وستحتاج فقط إلى جلبها من خلال الإشارة إلى أسمائها ، دون التصريح عنها يدويًا أو إضافتها من نص عقدة الجذر. تعد طريقة الموارد بشكل عام أكثر فاعلية وتحافظ على قاعدة كود أكثر نظافة.

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

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

لكنني منفتح على الاقتراحات لحل المشكلة الرئيسية ، وهي إعادة استخدام الكود في أنواع متعددة من العقد.

vnen الحل الذي توصلت إليه وهو العنصر الأخير هو الاستعانة بمصادر خارجية للأقسام القابلة لإعادة الاستخدام لنصوص الموارد.

  • لا يزال من الممكن كشفها وتحرير خصائصها في المفتش ، تمامًا كما لو كانت متغيرات عضو في عقدة.

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

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

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

willnationsdev أرى (على الرغم من أن اسم "Resource scripts" يبدو غريبًا ، نظرًا لأن جميع البرامج النصية هي موارد). تكمن المشكلة الرئيسية في هذا الحل في أنه لا يحل شيئًا يتوقعه الناس من خلال نهج الوراثة: إضافة المتغيرات والإشارات المصدرة إلى العقدة الجذرية للمشهد (خاصة عند تثبيت المشهد في مكان آخر). لا يزال بإمكانك تحرير المتغيرات التي تم تصديرها من المصادر الفرعية ، لكنها تصبح أقل عملية (لا يمكنك معرفة الخصائص التي يمكنك تحريرها في لمح البصر).

المشكلة الأخرى هي أنها تتطلب تكرار التعليمات البرمجية المعيارية كثيرًا. يجب عليك أيضًا التأكد من تعيين البرامج النصية للمورد بالفعل قبل استدعاء الوظائف.

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

تضمين التغريدة

لكنه يصبح أقل عملية (لا يمكنك معرفة الخصائص التي يمكنك تعديلها في لمحة سريعة).

هل يمكنك توضيح ما تعنيه هنا؟ لا أفهم كيف يمكن أن يكون هناك نقص كبير في الوضوح بشأن الخصائص التي يمكن الوصول إليها ، خاصة إذا تم دمج شيء مثل # 22660.

  1. أقوم بتجربة المشهد ، وأريد أن أعرف كيف يمكنني تحريره ، ولذا انظر إلى نص عقدة الجذر لهذا المشهد.
  2. داخل النص ، أرى ...

    • export(MoveImpl) var move_impl = FourWayMoveImpl.new()

    • use FourWayMoveTrait

  3. بافتراض أن لدينا وسيلة لتعقب المعرف بالنقر (والتي يجب أن تكون بالفعل ميزة 3.1.1!) لفتح البرنامج النصي ، فإننا ننتهي بعد ذلك بفتح البرنامج النصي المرتبط ويمكننا عرض خصائصه.

يبدو لي نفس عدد الخطوات ، إلا إذا فاتني شيء ما.

علاوة على ذلك ، فإن الخصائص القابلة للتحرير هي في الواقع أكثر وضوحًا بالموارد التي أود أن أقولها ، لأنه إذا كان لتطبيق ما خصائص خاصة به ، فإن حقيقة أن البيانات مرتبطة بالتنفيذ تكون أكثر وضوحًا إذا كان عليك تمهيد الوصول إليها باستخدام مثيل التنفيذ ، على سبيل المثال move_impl.<property> .

المشكلة الأخرى هي أنها تتطلب تكرار التعليمات البرمجية المعيارية كثيرًا. يجب عليك أيضًا التأكد من تعيين البرامج النصية للمورد بالفعل قبل استدعاء الوظائف.

هذا صحيح ، ومع ذلك ، ما زلت أعتقد أن فوائد كونه عملية تصدير مع تهيئة تفوق تكلفة النص المضاف:

("زميل فريق رفيع المستوى" ، HLT ، مثل المصممين والكتاب والفنانين ، إلخ.)

  • يمكن للمرء تعيين القيم مباشرة من المفتش ، بدلاً من الاضطرار إلى فتح البرنامج النصي ، وتحديد السطر الصحيح لتغييره ، ثم تغييره (سبق ذكره ، ولكنه يؤدي إلى ...).

  • يمكن للمرء أن يحدد أن المحتوى الذي تم تصديره له متطلبات النوع الأساسي. يمكن للمفتش بعد ذلك تقديم قائمة معدودة بالتطبيقات المسموح بها تلقائيًا. عندئذٍ يمكن لـ HLTs تعيين مشتقات من هذا النوع بأمان فقط. يساعد ذلك في عزلهم عن البديل المتمثل في الحاجة إلى معرفة تداعيات جميع نصوص السمات المختلفة التي تدور حولها. سيتعين علينا أيضًا تعديل الإكمال التلقائي في GDScript لدعم البحث عن ملفات السمات المسماة وغير المسماة استجابة لرؤية الكلمة الأساسية use .

  • يمكن للمرء إجراء تسلسل لتكوين تنفيذ كملف * .tres. يمكن لـ HLT بعد ذلك سحبها وإفلاتها من رصيف FileSystem أو حتى إنشاء حقها الخاص في المفتش. إذا رغب المرء في فعل الشيء نفسه مع السمات ، فسيتعين عليه إنشاء سمة مشتقة توفر مُنشئًا مخصصًا لتجاوز السمة الافتراضية. ثم يستخدمون هذه السمة بدلاً من ذلك كـ "تكوين مسبق" عبر مُنشئ مشفر حتميًا.

    1. اضعف لانها حتمية اكثر من كونها تصريحية.
    2. أضعف لأنه يجب تعريف المُنشئ صراحةً في البرنامج النصي.
    3. إذا كانت السمة غير مسماة ، فسيحتاج المستخدم إلى معرفة مكان السمة من أجل استخدامها بشكل صحيح بدلاً من السمة الأساسية الافتراضية. إذا تم تسمية السمة ، فإنها تسد مساحة الاسم العالمية دون داع.
    4. إذا قاموا بتغيير النص ليقول use FourWayMoveTrait بدلاً من use MoveTrait ، فلن يكون هناك أي مؤشر دائم على أن البرنامج النصي متوافق حتى مع MoveTrait الأساسي. إنه يمكّن من الارتباك HLT حول ما إذا كان FourWayMoveTrait يمكن أن يتغير إلى MoveTrait مختلف دون كسر الأشياء.
    5. إذا كان HLT ينشئ تطبيقًا جديدًا للسمات بهذه الطريقة ، فلن يعرف بالضرورة جميع الخصائص التي يمكن / تحتاج إلى تعيينها من السمة الأساسية. هذه ليست مشكلة مع الموارد التي تم إنشاؤها في المفتش.
  • يمكن للمرء حتى أن يكون لديه موارد متعددة من نفس النوع (إذا كان هناك سبب لذلك). لن تدعم السمة هذا ، ولكنها ستؤدي بدلاً من ذلك إلى إثارة تعارضات في التحليل.

  • يمكن للمرء تغيير التكوينات و / أو قيمها الفردية دون مغادرة منفذ العرض ثنائي الأبعاد / ثلاثي الأبعاد. هذا أكثر راحة بالنسبة لـ HLTs (أعرف الكثير ممن ينزعجون تمامًا عندما يتعين عليهم إلقاء نظرة على الكود على الإطلاق ).

مع كل ما قيل ، أوافق على أن النموذج المعياري مزعج. للتعامل مع ذلك ، أفضل إضافة نظام ماكرو أو مقتطف لتبسيطه. قد يكون نظام المقتطف فكرة جيدة لأنه يمكن أن يدعم أي لغة يمكن تحريرها في ScriptEditor ، بدلاً من GDScript وحده.

عن:

لكنه يصبح أقل عملية (لا يمكنك معرفة الخصائص التي يمكنك تعديلها في لمحة سريعة).

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

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

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

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

لا يمكنني إلا أن أتخيل ما يجب أن يحدث للخلف وللأمام لجعل النصوص الخارجية تتصرف كما لو كانت مكتوبة مباشرة في النص الرئيسي نفسه.

سيكون شيئًا لطيفًا إذا لم يكن من الضروري تحريك السماء والأرض لتحقيق ذلك. X)

vnen أرى ما تقوله الآن. حسنًا ، نظرًا لأنه يبدو أنه قد يكون هناك بعض الحياة في هذا العدد ، فسأعيد فتحه عندما تسنح لي الفرصة في المرة القادمة.

أي أخبار عن إعادة فتح هذا؟ الآن بعد أن تم الإعلان عن GodotCon 2019 ، و Godot Sprint شيء ، ربما هذا يستحق الحديث هناك.

AfterRebelion كنت قد نسيت للتو العودة وإعادة فتحه. شكرا لتذكيري. وجه ضاحك

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

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

willnationsdev لا أعرف ما إذا كان بإمكاني الاتصال بك عن طريق رسالة خاصة !! لكني أرغب في معرفة المزيد عن EditorInspectorPlugin (مثل بعض التعليمات البرمجية النموذجية) .. شيء ما في سطور نوع مورد مخصص (على سبيل المثال) MyResource التي لها خاصية تصدير "name" وزر مراقب يطبع "الاسم" متغير إذا ضغطت عليه (في المحرر أو أثناء التصحيح)! يفتقر التوثيق إلى وقت كبير في هذا الشأن ... سأكتب التوثيق بنفسي إذا كنت أعرف كيفية استخدام هذا! : D شكرا

أود أيضًا معرفة المزيد عنها. X)

فهل هذا هو نفس برنامج التحميل التلقائي مع الفئات الفرعية التي تحتوي على وظائف ثابتة؟

على سبيل المثال ، ستصبح قضيتك Traits.MoveRightTrait.move_right()

أو حتى أبسط من ذلك ، مع وجود نصوص مختلفة للتحميل التلقائي لفئات السمات المختلفة:

Movement.move_right()

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

لقد اكتشفت أنني أمتلك رمزًا مكررًا (وظائف أصغر ، مثل إنشاء قوس) أو عقد زائدة عن الحاجة لأنه لا يمكنني الحصول على وراثة متعددة.

سيكون هذا رائعًا ، أجد نفسي مضطرًا إلى إنشاء نص برمجي بنفس الوظيفة التي يتم استخدامها فقط على أنواع مختلفة من العقد تسبب extend s بشكل أساسي فقط لسطر واحد من التعليمات البرمجية. بالمناسبة ، إذا كان أي شخص يعرف كيفية القيام بذلك مع النظام الحالي ، فأعلمني بذلك.

إذا كانت لدي وظيفة لبرنامج نصي بـ extends Node ، فهل هناك طريقة لإرفاق نفس السلوك بنوع عقدة أخرى دون الحاجة إلى تكرار الملف المصدر واستبداله بـ extend المناسب؟

أي تقدم في ذلك؟ ما زلت أضطر إلى تكرار الكود أو أضطر إلى إضافة عقد ، كما قلت من قبل. أعلم أنه لن يتم ذلك في 3.1 ، ولكن ربما تهدف إلى 3.2؟

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

تحرير: إذا أراد شخص آخر اتخاذ قرار بشأن تنفيذ السمات ، فنحن نرحب به للانضمام إليها. فقط بحاجة للتنسيق مع المطورين.

"تنفيذ طريقة التمديد"؟

Zireael07 # 15586
يسمح للأشخاص بكتابة نصوص يمكن أن تضيف وظائف "مضمنة" جديدة لفئات المحرك. سيكون تفسيري للنحو شيئًا من هذا القبيل:

static Array func sum(p_self: Array):
    if not len(p_self):
        return 0
    var value = p_self[0]
    for i in range(1, len(p_self)):
        value += p_self[i]
    return value

ثم ، في مكان آخر ، سأتمكن من القيام بما يلي:

var arr = [1, 2, 3]
print(arr.sum()) # prints 6

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

لقد أجريت بعض المحادثات مع vnen و @ jahd2602 حول هذا الموضوع. الشيء الوحيد الذي يتبادر إلى الذهن هو حل Jai لتعدد الأشكال: استيراد مساحة اسم خاصية.

بهذه الطريقة ، يمكنك أن تفعل شيئًا كالتالي:

class A:
    var a_prop: String = "Hello"
    func foo():
        print("A's a_prop: ", a_prop)
    func bar():
        print("A's bar()")

class B:
    using var a: A = A.new()
    var a_prop: String = "World" # Overriding A's a_prop

    func bar():  # Overriding A's bar()
        print("B's bar()")

func main():
    var b: B = B.new()
    b.foo() # output: "A's a_prop: World"
    b.bar() # output: "B's bar()"

النقطة المهمة هي أن الكلمة الرئيسية using تستورد مساحة اسم الخاصية ، مثل أن b.foo() هو في الحقيقة سكر نحوي فقط لـ b.a.foo() .

وبعد ذلك ، تأكد من أنه يمكن استخدام b is A == true و B في المواقف المكتوبة التي تقبل A أيضًا.

هذا أيضًا له فائدة أنه لا يلزم الإعلان عن الأشياء كسمات ، وهذا من شأنه أن يعمل مع أي شيء ليس له أسماء جودة شائعة.

مشكلة واحدة هي أن هذا لا يتوافق بشكل جيد مع نظام الوراثة الحالي. إذا كان كل من A و B عبارة عن Node2D وقمنا بعمل دالة في A: func baz(): print(self.position) ، فأي مركز سيتم طباعته عندما نطلب b.baz() ؟
قد يكون أحد الحلول هو جعل المتصل يحدد self . سيؤدي استدعاء b.foo () إلى استدعاء foo () مع b كـ self و bafoo () باستدعاء foo () مع a as self.

إذا كانت لدينا طرق قائمة بذاتها مثل Python (حيث x.f(y) هو السكر مقابل f(x,y) ) ، فقد يكون هذا سهل التنفيذ حقًا.

فكرة أخرى غير ذات صلة:

ركز فقط على الوظائف القائمة بذاتها ، أسلوب JavaScript.

إذا اعتمدنا اصطلاح x.f(y) == f(x,y) للوظائف الثابتة ، فيمكننا بسهولة الحصول على ما يلي:

class Jumper:
    static func jump(_self: KinematicBody2D):
        # jump implementation

class Runner:
    static func run(_self: KinematicBody2D, direction: Vector2):
        # run implementation

class Character:
    extends KinematicBody2D
    func run = Runner.run       # Example syntax
    func jump = Jumper.jump

func main():
    var character = Character.new()
    character.jump()
    character.run(Vector2(1,0))

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

jabcross يبدو جيدًا ، فأنا أحب مفهوم نوع من تباعد الأسماء الاختياري ، وفكرة الوظيفة مثيرة للاهتمام.

فيما يتعلق بمعرف الهيكلة واحد ، أتساءل لماذا ليس مجرد using A ، الأشياء التوضيحية الأخرى تبدو غريبة.

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

الخيار ب ، ربما كلمات أساسية إضافية لـ GDScript لتحديد فئة السمات ، والفئة التي ترغب في الحصول على تلميحات منها. ستكون نفس الفكرة ، ولكن فقط خطوات إضافية لتظهر بشكل أكثر وضوحًا.

في الجزء العلوي من A.gd:

extends Trait as Node2D
is Trait as Node2D
is Trait extends B
extends B as Trait

أوه ، أنا حقًا أحب مفهوم استيراد مساحة الاسم. فهو لا يحل مشكلة السمات فحسب ، بل يحتمل أيضًا مفهوم "طرق الامتداد" لإضافة محتوى إلى أنواع المحركات.

class_name ArrayExt
static func sum(_self: Array) -> int:
    var sum: int = 0
    for a_value in _self:
        sum += a_value
    return sum

using ArrayExt
func _ready():
    var a = [1, 2, 3]
    print(a.sum())

jabcross إذا أضفنا أيضًا Lambas و / أو الكائنات المسموح بها لتنفيذ مشغل استدعاء (وكان لدينا نوع callable للقيم المتوافقة) ، فيمكننا البدء في إضافة نهج وظيفي أكثر إلى كود GDScript (والذي أعتقد أنها ستكون فكرة رائعة). منحت ، المشي أكثر في منطقة @ vnen # 18698 في تلك المرحلة ، ولكن ...

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

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

vnen Ahhh ، لم يدرك ذلك بخصوص تكلفة وقت التشغيل. وإذا كان ذلك ينطبق على أي تطبيق لطريقة الامتداد ، فأعتقد أن ذلك لن يكون مثاليًا أيضًا.

إذا قمنا بعمل نظام سمات خالصة ، فهل كنت تفكر في جعل trait TraitName بدلاً من extends مقترنًا بـ using TraitName تحت تمتد في نصوص أخرى؟ وهل ستنفذ هذا بنفسك ، أم سيتم تفويضه؟

إذا قمنا بعمل نظام سمات خالصة ، فهل كنت تفكر في جعل trait TraitName بدلاً من extends مقترنًا بـ using TraitName تحت تمتد في نصوص أخرى؟

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

وهل ستنفذ هذا بنفسك ، أم سيتم تفويضه؟

لا أمانع في تكليف شخص آخر بهذه المهمة. أنا في وقت قصير على أي حال. لكن يجب أن نتفق على التصميم مسبقًا. أنا مرن تمامًا فيما يتعلق بالتفاصيل ولكن يجب أن 1) لا تتحمل عمليات فحص وقت التشغيل (اعتقد أن GDScript يفسح المجال جيدًا للأشياء التي يستحيل اكتشافها في التجميع) ، 2) تكون بسيطة نسبيًا ، و 3) لا تضيف الكثير إلى وقت التجميع.

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

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

ستكون السمة أساسًا "استيراد" من وجهة نظري. يجب أن يكون تافها إظهار الإنجاز ، بافتراض أن اكتمال عمل الأعضاء.

vnen أتخيل أنك ستحلل بشكل أساسي إلى ClassNode مع وضع علامة trait عليها. ثم إذا قمت بإجراء عبارة using ، فستحاول دمج جميع الخصائص / الأساليب / الإشارات / الثوابت / الفئات الفرعية في النص الحالي.

  1. إذا تعارضت إحدى الطرق ، فإن تنفيذ البرنامج النصي الحالي سيتجاوز الطريقة الأساسية ، تمامًا كما لو كان يتجاوز طريقة موروثة.

    • ولكن ماذا تفعل إذا كانت الفئة الأساسية تحتوي بالفعل على الطريقة "المدمجة"؟

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

فكرة سيئة؟ أم يجب علينا منع التعارضات غير المنهجية بشكل صريح؟ ماذا عن الفئات الفرعية؟ لدي شعور بأننا يجب أن نجعل هذه الصراعات.

willnationsdev يبدو وكأنه "مشكلة الماس" (المعروفة أيضًا باسم "الماس القاتل") ، وهي عبارة عن غموض موثق جيدًا مع حلول مختلفة مطبقة بالفعل في العديد من لغات البرمجة الشائعة.

الذي يذكرني:
vnen هل الصفات ستكون قادرة على توسيع سمات أخرى؟

@ jahd2602 لقد اقترح بالفعل ذلك كاحتمال

يمكن للسمات أيضًا أن توسع سمات أخرى.

@ jahd2602 استنادًا إلى حلول Perl / Python ، يبدو أنها تشكل أساسًا "مكدسًا" من الطبقات يحتوي على محتوى كل فئة بحيث تبقى التعارضات من آخر سمة مستخدمة فوق الإصدارات الأخرى وتكتب فوقها. يبدو أن هذا حل جيد جدًا لهذا السيناريو. ما لم تكن لديك أو لدى vnen أفكار بديلة. شكرا على النظرة العامة المرتبطة بالحلول جاهد.

بعض الأسئلة.

أولاً: ما هي الطرق التي يجب أن ندعم بها بيان الاستخدام؟

أعتقد أن بيان using يجب أن يتطلب بعض GDScript ذي القيمة الثابتة.

using preload("res://my_trait.gd") # a preloaded expression
using ScriptClass.MyTrait # a const resource
using Autoload.MyTrait # a const resource
using MyTrait # a regular script class

أفكر في كل ما سبق.

ثانيًا: ماذا ينبغي أن يكون النحو المسموح به لتعريف صفة و / أو اسمها؟

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

لذلك ، إذا كان برنامجًا نصيًا لأداة ، فيجب بالطبع أن يحتوي على أداة في الأعلى. ثم ، إذا كانت سمة ، فيجب أن تحدد ما إذا كانت سمة في السطر التالي. اختياريًا ، اسمح لشخص ما أن يذكر اسم فئة البرنامج النصي بعد إعلان السمات في نفس السطر وإذا فعل ذلك ، فلا تسمح له أيضًا باستخدام class_name . إذا حذفوا اسم السمة ، فلا بأس من class_name <name> . بعد ذلك ، عند تمديد نوع آخر ، يمكننا إدخال extends بعد إعلان السمة و / أو في سطر خاص به بعد إعلان السمة. لذلك ، سأعتبر كل من هذه صحيحة:

# Global name from trait keyword.
trait MyTrait extends BaseTrait

# Global name from class_name keyword, but is still a trait and also happens to be a tool script.
tool
trait
extends BaseTrait
class_name MyTrait

# A trait with no global name associated with it. Does not extend anything.
trait

ثالثًا: هل ينبغي لنا ، لأغراض الإكمال التلقائي و / أو إعلان النية / المتطلبات ، السماح للسمة بتحديد نوع أساسي يجب أن تمتد إليه؟

لقد ناقشنا بالفعل أن السمات يجب أن تدعم وراثة سمات أخرى. ولكن إذا سمحنا لـ TraitA بـ extend Node ، فسنسمح للبرنامج النصي TraitA بالحصول على الإكمال التلقائي للعقدة ، ولكن بعد ذلك يؤدي أيضًا إلى حدوث خطأ تحليل إذا قمنا بعمل عبارة using TraitA عندما لا تقوم الفئة الحالية بتوسيع Node أو أي من أنواع مشتقاتها؟

رابعًا: بدلاً من امتلاك سمات تمتد لسمات أخرى ، لا يمكننا بدلاً من ذلك الاحتفاظ بعبارة extends محجوزة لامتدادات الفئات ، والسماح للسمة بعدم الحاجة إلى هذه العبارة على الإطلاق ، ولكن بدلاً من توسيع سمة أساسية ، اسمح السمات ببساطة للحصول على بيانات using الخاصة بهم والتي تقوم باستيراد هذه الصفات بشكل ثانوي؟

# base_trait.gd
trait
func my_method():
    print("Hello")

# derived_trait.gd
trait
using preload("base_trait.gd")
func my_method():
   print("World") # overrides previous method, will only print "World".

بالطبع ، ستكون الفائدة هنا هي أنك ستكون قادرًا على تجميع سمات متعددة تحت اسم سمة واحدة باستخدام عدة عبارات using ، على غرار C ++ تتضمن ملفات تتضمن عدة فئات أخرى.

خامساً: إذا كانت لدينا سمة ، ولديها using أو extends لطريقة ما ، ثم نفذت صفة خاصة بها ، ماذا نفعل عندما تستدعي ، ضمن هذه الوظيفة .<method_name> لتنفيذ تنفيذ القاعدة؟ هل نفترض أن هذه الدعوات تُنفَّذ دائمًا في سياق وراثة الصنف ، وأن التسلسل الهرمي للسمات ليس له أي تأثير هنا؟

سي سي vnen

أولاً: ما هي الطرق التي يجب أن ندعم بها بيان الاستخدام؟

أعتقد أن بيان using يجب أن يتطلب بعض GDScript ذي القيمة الثابتة.

using preload("res://my_trait.gd") # a preloaded expression
using ScriptClass.MyTrait # a const resource
using Autoload.MyTrait # a const resource
using MyTrait # a regular script class

أنا بخير مع كل هؤلاء. لكن بالنسبة للمسار ، أستخدم سلسلة مباشرة: using "res://my_trait.gd"

ثانيًا: ماذا ينبغي أن يكون النحو المسموح به لتعريف صفة و / أو اسمها؟

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

لذلك ، إذا كان برنامجًا نصيًا لأداة ، فيجب بالطبع أن يحتوي على أداة في الأعلى. ثم ، إذا كانت سمة ، فيجب أن تحدد ما إذا كانت سمة في السطر التالي. اختياريًا ، اسمح لشخص ما أن يذكر اسم فئة البرنامج النصي بعد إعلان السمات في نفس السطر وإذا فعل ذلك ، فلا تسمح له أيضًا باستخدام class_name . إذا حذفوا اسم السمة ، فلا بأس من class_name <name> . بعد ذلك ، عند تمديد نوع آخر ، يمكننا إدخال extends بعد إعلان السمة و / أو في سطر خاص به بعد إعلان السمة. لذلك ، سأعتبر كل من هذه صحيحة:

# Global name from trait keyword.
trait MyTrait extends BaseTrait

# Global name from class_name keyword, but is still a trait and also happens to be a tool script.
tool
trait
extends BaseTrait
class_name MyTrait

# A trait with no global name associated with it. Does not extend anything.
trait

لا ينبغي أن يحدث أي اختلاف في tool على سمة ، حيث لا يتم تنفيذها بشكل مباشر.

أوافق على أن السمة ليس لها بالضرورة اسم عالمي. سأستخدم trait بطريقة مشابهة لـ tool . يجب أن يكون أول شيء في ملف البرنامج النصي (باستثناء التعليقات). يجب أن يتبع الكلمة الرئيسية بشكل اختياري اسم السمة. لن أستخدم class_name لهم ، لأنهم ليسوا فصولاً.

ثالثًا: هل ينبغي لنا ، لأغراض الإكمال التلقائي و / أو إعلان النية / المتطلبات ، السماح للسمة بتحديد نوع أساسي يجب أن تمتد إليه؟

أنا بصراحة لا أحب إضافة ميزات في اللغة من أجل المحرر. هذا هو المكان الذي ستكون فيه التعليقات التوضيحية مفيدة.

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

رابعًا: بدلاً من امتلاك سمات تمتد لسمات أخرى ، لا يمكننا بدلاً من ذلك الاحتفاظ بعبارة extends محجوزة لامتدادات الفئات ، والسماح للسمة بعدم الحاجة إلى هذه العبارة على الإطلاق ، ولكن بدلاً من توسيع سمة أساسية ، اسمح السمات ببساطة للحصول على بيانات using الخاصة بها والتي تقوم باستيراد _those_ الصفات الفرعية؟

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

خامسًا: إذا كانت لدينا سمة ، ولديها using أو extends لطريقة ما ، ثم نفذت خاصيتها ، ماذا نفعل عندما تستدعي ، ضمن هذه الوظيفة .<method_name> لتنفيذ تنفيذ القاعدة؟ هل نفترض أن هذه الدعوات تُنفَّذ دائمًا في سياق وراثة الصنف ، وأن التسلسل الهرمي للسمات ليس له أي تأثير هنا؟

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

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

ماذا عن السمة التي تتطلب أن يمتلك الفصل سمة أخرى أو أكثر؟ على سبيل المثال ، السمة DoubleJumper تتطلب كلاً من السمة Jumper ، سمة Upgradable وفئة ترث KinematicBody2D .

يتيح لك Rust ، على سبيل المثال ، استخدام توقيعات كتابة مثل تلك. شيء من هذا القبيل KinematicBody2D: Jumper, Upgradable . ولكن نظرًا لأننا نستخدم : للتعليق على الكتابة ، يمكننا فقط استخدام KinematicBody2D & Jumper & Upgradable أو شيء من هذا القبيل.

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

على سبيل المثال ، نريد طريقة kill() في السمة Jumper ، والتي يتم استخدامها بواسطة كل من Enemy و Player . نريد تطبيقات مختلفة لكل حالة ، مع الحفاظ على توافق كلاهما مع نفس توقيع النوع Jumper . كيف نفعل ذلك؟

بالنسبة لتعدد الأشكال ، ستقوم فقط بإنشاء سمة منفصلة تتضمن السمة بـ kill() ثم تنفذ نسختها الخاصة من الطريقة. إن استخدام السمات التي تتجاوز طرق السمات المدرجة سابقًا هو كيفية التعامل معها.

أيضًا ، لا أعتقد أنه كانت هناك أي خطط (حتى الآن) لعمل تلميح نوع له متطلبات سمات. هل هذا شيء نود أن نفعله؟

خلق سمة منفصلة

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

سأقدر حقًا نظام توقيع قوي من النوع (ربما مع تكوين منطقي واختياري / غير فارغ). الصفات سوف تتناسب مع.

لست متأكدًا مما إذا كانت قد تمت مناقشتها ، لكنني أعتقد أنه ينبغي أن يكون من الممكن استدعاء إصدار محدد من إحدى السمات. فمثلا:

trait A
func m():
  print("A")

trait B
func m():
  print("B")

class C
using A
using B

func c():
  A.m()
  B.m()
  m()

الذي يطبع: A ، B ، B .


كما أنني لست متأكدًا تمامًا من "عدم تكلفة وقت التشغيل". كيف سيتم التعامل مع البرامج النصية المحملة ديناميكيًا (غير متوفرة أثناء التصدير) مع الفئات التي تستخدم السمات التي تم تحديدها قبل التصدير؟ هل أنا أسيء فهم شيء ما؟ أم أن هذه الحالة لا تعتبر "وقت التشغيل"؟

لست متأكدًا مما إذا كانت قد تمت مناقشتها ، لكنني أعتقد أنه ينبغي أن يكون من الممكن استدعاء إصدار محدد من إحدى السمات.

كنت أفكر بالفعل في ذلك ، لكنني لست متأكدًا من السماح للفصل باستخدام سمات متضاربة (أي السمات التي تحدد الطريقة بنفس الاسم). يجب ألا يُحدث ترتيب كشوف الحساب using أي فرق.

كما أنني لست متأكدًا تمامًا من "عدم تكلفة وقت التشغيل". كيف سيتم التعامل مع البرامج النصية المحملة ديناميكيًا (غير متوفرة أثناء التصدير) مع الفئات التي تستخدم السمات التي تم تحديدها قبل التصدير؟ هل أنا أسيء فهم شيء ما؟ أم أن هذه الحالة لا تعتبر "وقت التشغيل"؟

لا يتعلق الأمر بالتصدير. سيؤثر ذلك بالتأكيد على وقت التحميل ، نظرًا لأن التجميع يحدث عند التحميل (على الرغم من أنني لا أعتقد أنه سيكون مهمًا للغاية) ، ولكن لا ينبغي أن يؤثر عند تنفيذ البرنامج النصي. من الناحية المثالية ، يجب تجميع النصوص البرمجية عند التصدير ، لكن هذه مناقشة أخرى.

أهلا بالجميع.

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

لإنجاز هذا العمل ، يجب على عقدة الجذر "تصدير" الخصائص ثم نسخ القيم إلى الطفل المناسب في _الفعل. لذلك ، على سبيل المثال ، تخيل عقدة قنبلة مع مؤقت طفل. عقدة القنبلة الجذرية في المشهد الفرعي ستصدر "detonation_time" وبعد ذلك ستعمل $Timer.wait_time = detonation_time في _جاهز. يتيح لنا ذلك ضبطه بشكل جيد في واجهة مستخدم Godot كلما قمنا بتثبيته دون الحاجة إلى جعل الأطفال قابلين للتعديل والانتقال إلى Timer.

ومع ذلك
1) إنه تحول ميكانيكي للغاية لذا يبدو أن شيئًا مشابهًا يمكن أن يدعمه النظام
2) ربما يضيف عدم كفاءة طفيفًا في تحديد القيمة المناسبة مباشرة في العقدة الفرعية.

قبل أن أكمل ، قد يبدو هذا عرضيًا لما تتم مناقشته لأنه لا يتضمن السماح بنوع من الميراث "الخاص" (في لغة C ++). ومع ذلك ، فأنا في الواقع أحب نظام Godot لبناء السلوكيات من خلال تكوين عناصر المشهد بدلاً من الهندسة الشبيهة بالميراث. تلك العلاقات "المكتوبة" ثابتة ولا تتغير. OTOH ، هيكل المشهد ديناميكي ، يمكنك حتى تغييره في وقت التشغيل. منطق اللعبة عرضة للتغيير أثناء التطوير لدرجة أنني أعتقد أن تصميم Godot يناسب حالة الاستخدام جيدًا.

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

ومع ذلك ، أعتقد أن العقد الجذرية يجب أن تعمل أيضًا كواجهة أولية لوظيفة الفرع المعتمد بأكمله. يجب أن تكون جميع الخصائص التي يمكن تعديلها في المثيل "قابلة للتعيين" في العقدة الجذرية للفرع حتى لو كان المالك النهائي للخاصية هو عقدة فرعية. ما لم أفقد شيئًا ما ، يجب ترتيب ذلك يدويًا في الإصدار الحالي من Godot. سيكون من الجيد أن تتم أتمتة هذا بطريقة ما للجمع بين فوائد النظام الديناميكي مع سهولة البرمجة النصية.

شيء واحد أفكر فيه هو نظام "الميراث الديناميكي" ، إذا صح التعبير ، المتاح للفئات الفرعية من Node. سيكون هناك مصدران للخصائص / الأساليب في مثل هذا السيناريو ، تلك الخاصة بالسيناريو الذي يمتد إليه وتلك "التي ظهرت" من الأطفال داخل بنية المشهد. لذا فإن المثال الخاص بي في القنبلة سيصبح شيئًا مثل export lifted var $Timer.wait_time [= value?] as detonation_time داخل قسم متغيرات الأعضاء في البرنامج النصي bomb.gd. سيقوم النظام بشكل أساسي بإنشاء $Timer.wait_time = detonation_time في رد الاتصال _الفعل وإنشاء أداة الحصول / المُعيِّن التي ستسمح لـ $Bomb.detonation_time = 5 من أصل عقدة Bomb بإحداث تعيين $Timer.wait_time = 5 .

في مثال OP مع MoveRightTrait ، سيكون لدينا العقدة التي تم إرفاق mysprite.gd بها ، والتي تحتوي على MoveRightTrait كعقدة فرعية. ثم في mysprite.gd سيكون لدينا شيء مثل lifted func $MoveRightTrait.move_right [as move_right] (ربما تكون "as" اختيارية عندما يكون الاسم هو نفسه). الآن استدعاء move_right على كائن نصي تم إنشاؤه من mysprite.gd سيؤدي تلقائيًا إلى التفويض إلى العقدة الفرعية المناسبة. ربما يمكن إطلاق فقاعات للإشارات بحيث يمكن ربطها بعقدة فرعية من الجذر؟ ربما يمكن وضع فقاعات للعقد بأكملها بـ lifted $MoveRightTrait [as MvR] فقط بدون func أو signal أو var. في هذه الحالة ، يمكن الوصول إلى جميع الوظائف والخصائص الموجودة على MoveRightTrait من mysprite مباشرةً مثل mysprite.move_right أو من خلال mysprite.MvR.move_right إذا تم استخدام "as MvR".

هذه فكرة واحدة عن كيفية تبسيط إنشاء واجهة إلى هيكل مشهد في جذر فرع متطور ، وزيادة خصائص "الصندوق الأسود" والحصول على ملاءمة البرمجة جنبًا إلى جنب مع قوة نظام المشهد الديناميكي في Godot. بالطبع ، سيكون هناك العديد من التفاصيل الجانبية للنظر فيها. على سبيل المثال ، على عكس الفئات الأساسية ، يمكن إزالة العقد الفرعية في وقت التشغيل. كيف يجب أن تتصرف الوظائف والخصائص الفقاعية / المرفوعة إذا تم استدعاؤها / الوصول إليها في حالة الخطأ هذه؟ إذا تمت إضافة عقدة مع NodePath الصحيح مرة أخرى ، فهل تبدأ الخصائص التي تم رفعها في العمل مرة أخرى؟ [YES، IMO] سيكون من الخطأ أيضًا استخدام كلمة "lifted" في الفئات غير المشتقة من Node نظرًا لأنه لن يكون هناك أبدًا أطفال للفقاعة / الرفع منها في هذه الحالة. بالإضافة إلى ذلك ، من الممكن وجود تضارب في الأسماء مع تكرار "مثل {name}" أو "رفع $ Timer1 رفع $ Timer2" حيث تحتوي العقد على خصائص / طرق بنفس الاسم. من الأفضل أن يكتشف مترجم البرنامج النصي مثل هذه المشكلات المنطقية.

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

على أي حال ، إذا كنت قد حصلت على هذا الحد ، شكرًا على القراءة!

لقد استخدمت كلمة "مرفوع" في كل مكان ولكن هذا مجرد توضيح.
شيء مثل using var $Timer.wait_time as detonation_time أو using $Timer من الواضح أنه بنفس الجودة. في أي حال ، يمكنك الحصول على وراثة زائفة بشكل ملائم من العقد الفرعية ، مما يؤدي إلى إنشاء نقطة واحدة متماسكة للوصول إلى الوظيفة المطلوبة في جذر الفرع المراد تثبيته. الشرط على الأجزاء القابلة لإعادة الاستخدام للوظائف هو أنها توسع العقدة أو فئة فرعية منها بحيث يمكن إضافتها كأطفال إلى المكون الأكبر.

هناك طريقة أخرى للنظر إليها وهي أن الكلمة الرئيسية "الممتدة" في البرنامج النصي الذي يرث من العقدة تمنحك علاقتك "is-a" أثناء استخدام الكلمة الرئيسية "باستخدام" أو "رفع" في نص برمجي "لإنشاء فقاعة تفسيرية" يمنحك أعضاء العقدة الفرعية شيئًا مشابهًا لـ "تنفيذ" [مرحبًا ، كلمة رئيسية محتملة] الموجودة في اللغات ذات الميراث الفردي ولكن "الواجهات" المتعددة (مثل Java). في الوراثة المتعددة غير المقيدة (مثل c ++) تشكل الفئات الأساسية شجرة [ثابتة ، مكتوبة]. عن طريق القياس ، أقترح نوعًا ما وضع طبقات من التركيب المريح والقضاء المعياري على أشجار العُقد الموجودة في Godot.

إذا تم تحديد أن هذا شيء يستحق الاستكشاف ، فهناك جوانب من مساحة التصميم يجب مراعاتها:
1) يجب أن نسمح فقط للأطفال المباشرين في "استخدام". IOW using $Timer لكن ليس using $Bomb/Timer'? This would be simpler but would force us to write boilerplate in some cases. I say that a full NodePath ROOTED in the Node to which the script is attached should be legal [but NO references to parents/siblings allowed]. 2) Should there be an option that find_node's the "using"-ed node instead of following a written in NodePath? For example باستخدام "Timer" with a string for the pattern would be slower but the forwarding architecture would continue to work if a referenced node's position in the sub-tree changes at run time. This could be used selectively for child nodes that we expect to move around beneath the root. Of course syntax would have to be worked out especially when using a particular member (eg. باستخدام var "Timer" .wait_time as detonation_time is icky). 3) Should there be a way query for certain functionality [equivalent to asking if an interface is implemented or a child node is present]? Perhaps "using" entire nodes with aliases should allow testing the alias to be a query. So باستخدام MoveRightTrait as DirectionalMover in a script would result in node.DirectionalMover returning the child MoveRightTrait. This is logical because node.DirectionalMover.move_right () calls the method on the child MoveRightTrait. Other nodes without that statement would return null. So the statement if node.DirectionalMover: `سيصبح اختبارًا للوظيفة حسب الاصطلاح.
4) يجب أن يكون نمط الحالة قابلاً للتنفيذ عن طريق استبدال عقدة "استخدام" بأخرى لها سلوك متغير ولكن نفس الواجهة [كتابة البط] ونفس NodePath كما هو مشار إليه في عبارة "استخدام". بالطريقة التي تعمل بها شجرة المشهد ، سيعمل هذا مجانًا تقريبًا. ومع ذلك ، سيتعين على النظام تتبع الإشارات المتصلة من خلال أحد الوالدين واستعادة الاتصالات في الطفل الذي تم استبداله.

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

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

إن رمي C ++ (GDNative) في المزيج يجعل الأمور أسوأ ، لأن _ready و _init يتصرفان بشكل مختلف هناك (اقرأ: التهيئة بالقيم الافتراضية نصف تعمل أو لا تعمل على الإطلاق).

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

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

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

الطريقة التي أفهم بها سمات الصدأ (https://doc.rust-lang.org/1.8.0/book/traits.html) ، هي أنها تشبه فئات Haskell ، حيث تحتاج إلى تحديد بعض الوظائف ذات المعلمات للنوع أنت تضيف سمة إليها ، ثم يمكنك استخدام بعض الوظائف العامة المحددة على أي أنواع تنفذ سمة. هل تختلف سمات الصدأ عما هو مقترح هنا؟

من المحتمل أن يتم ترحيل هذا الشخص بالجملة ، حيث تمت مناقشة مستفيضة هنا.

من المحتمل أن يتم ترحيل هذا الشخص بالجملة ، حيث تمت مناقشة مستفيضة هنا.

_ أجد أن "تحريك" الاقتراحات لا طائل من ورائه ، فمن الأفضل إغلاقها ويطلب منها إعادة فتحها في مقترحات جيدة إذا أبدى الناس اهتمامًا ، والسماح بتنفيذ المقترحات الأخرى فعليًا إذا لزم الأمر. على أي حال..._

لقد عثرت على هذه المشكلة بالصدفة قبل عام ، لكنني الآن فقط بدأت أفهم الفائدة المحتملة لنظام السمات.

مشاركة سير العمل الحالي الخاص بي على أمل إلهام شخص ما لفهم المشكلة بشكل أفضل (وربما اقتراح بديل أفضل بخلاف تطبيق نظام السمات).

1. قم بإنشاء أداة لإنشاء قوالب مكونات لكل نوع من أنواع العقدة المستخدمة في المشروع:

willnationsdev https://github.com/godotengine/godot/issues/23101#issuecomment -431468744

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

ماشي على خطىك ... 😅

tool
extends EditorScript

const TYPES = [
    'Node',
    'Node2D',
]
const TYPES_PATH = 'types'
const TYPE_BASENAME_TEMPLATE = 'component_%s.gd'

const TEMPLATE = \
"""class_name Component{TYPE} extends {TYPE}

signal host_assigned(node)

export(bool) var enabled = true

export(NodePath) var host_path
var host

func _ready():
    ComponentCommon.init(self, host_path)"""

func _run():
    _update_scripts()


func _update_scripts():

    var base_dir = get_script().resource_path.get_base_dir()
    var dest = base_dir.plus_file(TYPES_PATH)

    for type in TYPES:
        var filename = TYPE_BASENAME_TEMPLATE % [type.to_lower()]
        var code = TEMPLATE.format({"TYPE" : type})
        var path = dest.plus_file(filename)

        print_debug("Writing component code for: " + path)

        var file = File.new()
        file.open(path, File.WRITE)
        file.store_line(code)
        file.close()

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

class_name ComponentCommon

static func init(p_component, p_host_path = NodePath()):

    assert(p_component is Node)

    # Try to assign
    if not p_host_path.is_empty():
        p_component.host = p_component.get_node(p_host_path)

    elif is_instance_valid(p_component.owner):
        p_component.host = p_component.owner

    elif is_instance_valid(p_component.get_parent()):
        p_component.host = p_component.get_parent()

    # Check
    if not is_instance_valid(p_component.host):
        push_warning(p_component.name.capitalize() + ": couldn't find a host, disabling.")
        p_component.enabled = false
    else:
        p_component.emit_signal('host_assigned')

هذه هي الطريقة التي يبدو بها المكون (السمة) مرة واحدة مع النص الأول:

class_name ComponentNode2D extends Node2D

signal host_assigned(node)

export(bool) var enabled = true

export(NodePath) var host_path
var host

func _ready():
    ComponentCommon.init(self, host_path)

(اختياري) 3. توسيع المكون (سمة)

vnen https://github.com/godotengine/godot/issues/23101#issuecomment -471816901

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

ماشي على خطىك ... 😅

class_name ComponentMotion2D extends ComponentNode2D

const MAX_SPEED = 100.0

var linear_velocity = Vector2()
var collision

export(Script) var impl
...

في الواقع ، يتم استخدام Script s المُصدَّر في هذه المكونات لقيادة سلوك أنواع محددة من العقدة المضيفة / الجذر لكل مكون. هنا ، سيحتوي ComponentMotion2D بشكل أساسي على نصين:

  • motion_kinematic_body_2d.gd
  • motion_rigid_body_2d.gd

لذلك لا يزال الأطفال يقودون سلوك host / root هنا. تأتي المصطلحات host مني باستخدام آلات الحالة ، وهنا حيث قد لا تكون السمات مناسبة تمامًا ، لأن الدول منظمة بشكل أفضل كعقد imo.

المكونات نفسها "مثبتة" في الجذر من خلال جعلها أعضاء onready ، مما يقلل بشكل فعال من الشفرة المعيارية (مع حساب الحاجة إلى الإشارة إليها على أنها object.motion )

extends KinematicBody2D

onready var motion = $motion # ComponentMotion2D

لست متأكدًا مما إذا كان هذا سيساعد في حل المشكلة ، ولكن C # لديها شيء يسمى طرق الامتداد التي تعمل على توسيع وظائف نوع الفئة.

يجب أن تكون الوظيفة ثابتة بشكل أساسي ، ويجب أن تكون المعلمة الأولى مطلوبة ويجب أن تكون self . سيبدو هذا كتعريف:

extension.gd

# any script that uses this method must be an instance of `Node2D`
static func distance(self source: Node2D, target: Node2D):
    return source.global_position.distance_to(target.global_position)

# any script that uses this method must be an instance of `Rigidbody2D`
# a `Sprite` instance cannot use this method
static func distance(self source: Rigidbody2D, target: Node2D):
    return source.global_position.distance_to(target.global_position)

ثم عندما تريد استخدام طريقة distance ، فما عليك سوى القيام بما يلي:

player.gd

func _ready() -> void:
    print(self.distance($Enemy))
    print($BulletPoint.distance($Enemy))

أنا على دراية به ، لكن هذا لا يساعد في حل المشكلة. هههه ، شكرا على الرغم من ذلك.

تم اقتراح طرق الامتداد TheColorRed بالفعل ، لكنني لا أعتقد أنها مجدية بلغة ديناميكية. لا أعتقد أيضًا أنهم يحلون المشكلة الجذرية التي بدأت هذه المناقشة في البداية.


في ملاحظة أخرى ، من المحتمل أن أفتح العديد من المقترحات الخاصة بـ GDScript كـ GIPs (بما في ذلك ، إذا كان @ willnationsdev لا يمانع).

ما زلت أعتقد أن السمات الأكثر منطقية لمشاركة الكود أفقيًا بلغة OO (بدون وراثة متعددة ، لكنني لا أريد أن أذهب بهذه الطريقة).

لا أعتقد أنها مجدية بلغة ديناميكية

هل GDS ديناميكية بالرغم من ذلك؟ يمكن أن تقتصر طرق الامتداد على الحالات المكتوبة فقط وستعمل تمامًا كما هو الحال في اللغات الأخرى - فقط سكر نحوي أثناء التجميع يستبدل استدعاء الأسلوب باستدعاء الطريقة الثابتة (الوظيفة). بصراحة ، أفضل طرق القوادين (الطرق الخارجية) قبل النماذج الأولية ala JS أو الطرق الديناميكية الأخرى لربط الطرق بالفئات أو حتى الحالات فقط.

مهما قررنا القيام به ، آمل ألا نقرر تسميته "القوادين".

هل GDS ديناميكية بالرغم من ذلك؟

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

يمكن أن تقتصر طرق الامتداد على المثيلات المكتوبة فقط

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

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

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

بصراحة ، أفضل طرق القوادين (الطرق الخارجية) قبل النماذج الأولية ala JS أو الطرق الديناميكية الأخرى لربط الطرق بالفئات أو حتى الحالات فقط.

لم يقترح أحد (حتى الآن) طرق إرفاق ديناميكيًا (في وقت التشغيل) وأنا لا أريد ذلك أيضًا. سيتم تطبيق السمات بشكل ثابت في وقت التجميع.

لقد قدمت تعليقًا في الأصل حول Haxe ومكتبة الماكرو الخاصة به ، لكنني أدركت بعد ذلك أن معظم المستخدمين لن يستخدموا لغة تابعة لجهة خارجية على أي حال.

لقد واجهت مؤخرًا حاجة لذلك.

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

على سبيل المثال ، لدي بعض الفئات التي لا يمكن أن ترث من نفس الوالد ، ولكن استخدم مجموعة مماثلة من API:
المستودع: المالية ، الحذف ، MouseInteraction + الآخرين
السيارة: المالية ، الحذف ، MouseInteraction + أخرى
VehicleTerminal: الشؤون المالية ، والحذف ، MouseInteraction + أخرى

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

الاخرون:
MouseInteraction and Delection لقد اضطررت للتو إلى نسخها ولصقها لأنها تحتاج إلى معرفة كائنات اللعبة ، وبعض التركيبات لا تعمل هنا إلا إذا قمت ببعض التفويض الغريب:

Warehouse:
  func delete():
      get_delete_component().delete(self);

لكن هذا لا يسمح لي حقًا بتجاوز كيفية عمل الحذف حيث إذا كانت فئة موروثة ، فقد يكون لدي القدرة على إعادة كتابة بعض كود الحذف إذا لزم الأمر.

MouseInteraction and Delection اضطررت للتو إلى نسخها ولصقها لأنها تحتاج إلى معرفة كائنات اللعبة ، وبعض التركيبات لا تعمل هنا إلا إذا قمت ببعض التفويض الغريب

يمكنني الوصول إلى المكونات عبر عقده onready حاليًا. أفعل شيئًا مشابهًا:

# character.gd

var input = $input # input component

func _set(property, value):
    if property == "focused": # override
        input.enabled = value
    return true

إذا هذا:

character.input.enabled = true

يصبح هذا:

character.focused = true

كما أشار Calinou ، تفضلت بالإشارة إلى أن مشكلتي ، https://github.com/godotengine/godot-proposals/issues/758 مرتبطة ارتباطًا وثيقًا بهذا الأمر. ما رأيك في الاقتراح لتتمكن من إضافة سمة إلى مجموعة؟ قد يكون هذا بشكل كبير الحاجة إلى البرامج النصية وغيرها من النفقات العامة.

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

تتم الآن مناقشة مقترحات الميزات والتحسينات لمحرك Godot ومراجعتها في أداة تعقب المشكلات الخاصة بمقترحات تحسين Godot (GIP) ( godotengine / godot- مقترحات ). يحتوي متتبع GIP على نموذج مشكلة مفصل مصمم بحيث تتضمن المقترحات جميع المعلومات ذات الصلة لبدء مناقشة مثمرة ومساعدة المجتمع على تقييم صحة الاقتراح الخاص بالمحرك.

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

إذا كنت مهتمًا بمقترح الميزة هذا ، فيرجى فتح اقتراح جديد على متتبع GIP باتباع نموذج المشكلة المحدد (بعد التحقق من عدم وجوده بالفعل). تأكد من الإشارة إلى هذه المشكلة المغلقة إذا كانت تتضمن أي مناقشة ذات صلة (والتي نشجعك أيضًا على تلخيصها في الاقتراح الجديد). شكرا لك مقدما!

ملاحظة: هذا اقتراح شائع ، إذا نقله أحد الأشخاص إلى مقترحات Godot ، يرجى محاولة تلخيص المناقشة أيضًا.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات