كيف تختار بين المشاهد وملفات الشيفرة البرمجية

من موسوعة حسوب

تكلمنا مسبقًا أن المشاهد scenes والسكربتات scripts أمران مختلفان، فالسكربتات تعرّف صنفًا إضافيًا ضمن المحرك باستخدام التعليمات البرمجية، بينما تستخدم المشاهد البرمجة التصريحية.

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

الأنواع المجهولة

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

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

const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn")
var node = Node.new()
var my_node = MyNode.new() # استدعاء للتابع نفسه
var my_scene = MyScene.instantiate() # استدعاء تابع مختلف
var my_inherited_scene = MyScene.instantiate(PackedScene.GEN_EDIT_STATE_MAIN) # إنشاء مشهد يرث من‫ MyScene

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

الأنواع المسمّاة

يمكن للسكربتات أن تُسجّل كنوع جديد ضمن المحرر، وهذا يجعلها متاحة كنوع جديد ضمن قائمة إنشاء مورد أو عقدة جديدة مع خيار إضافة أيقونة مناسبة للنوع، وبهذه الطريقة يمكن للمستخدم أن يستخدم السكربت في أكثر من موضع دون أن:

  1. يعرف النوع الأساس للسكربت الذي يريد أن يستخدمه.
  2. يُنشئ نسخة من النوع الأساس.
  3. يُضيف السكربت إلى العقدة.

يصبح نوع السكربت المسجّل خيارًا عند إنشاء العُقد والموارد في النظام، وتحتوي القائمة على صندوق بحث يمكنك استخدامه لكتابة اسمه والبحث عنه.

هناك نوعان من الأنظمة لتسجيل الأنواع:

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

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

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

أداء ملفات الشيفرة البرمجية مقارنةً بعقدة PackedScene

أمرٌ آخر عليك أخذه بالحسبان هو سرعة التنفيذ عند الاختيار بين المشاهد والسكربتات.

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

نُنشئ في المثال أدناه عقدة جديدة Node ونغيّر من اسمها ومن ثم نُسند سكربت إليها، ونضبط أباها المستقبلي كمالكها بحيث تُحفظ على قرص التخزين معه، وأخيرًا، نضيفها كعقدة ابن للعقدة Main:

# main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("child.gd")
    child.owner = self
    add_child(child)

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

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

خلاصة

السلوك الأفضل للتحديد بين الطريقتين هو النظر إلى ما يلي:

  • إذا أردت إضافة أداة بسيطة ستعيد استخدامها مرات عدّة في مختلف المشاريع، وتعتقد أن الناس من جميع المستويات سيُعجبون بها (بما فيهم الأشخاص الذين لا يدعون أنفسهم بالمبرمجين)، فهذا يعني أنه يجب عليك غالبًا أن تستخدم سكربت مع إضافة اسم وأيقونة مخصصتين له.
  • إذا أردت إنشاء مفهوم له علاقة بلعبة معينة فقط، فلربما الخيار الأفضل هنا هو إنشاء مشهد، فالمشاهد أسهل في تتبعها وتعديلها وتقدّم أمانًا أكبر من السكربتات.
  • إذا أردت تسمية مشهد فيمكنك فعل ذلك من خلال التصريح عن صنف سكربت وتمرير المشهد له كثابت، مما يجعل السكربت يتصرف كفضاء أسماء namespace:
# game.gd
class_name Game # يوسّع الصنف‫ RefCounted, بحيث لا يظهر في قائمة اختيار العقد
extends RefCounted

const MyScene = preload("my_scene.tscn")

# main.gd
extends Node
func _ready():
    add_child(Game.MyScene.instantiate())