إنتقل إلى المحتوى الرئيسي

ValkarnTaskSettings

ValkarnTaskSettings هو Unity ScriptableObject يتحكم في سلوك وقت تشغيل Valkarn Tasks — بشكل رئيسي مجموعة الكائنات التي تُعيد تدوير آلات الحالة الغير المتزامنة وكائنات الوعد لتجنب القمامة أثناء اللعب.


إنشاء أصل الإعدادات

  1. في نافذة المشروع، انقر بالزر الأيمن على مجلد Resources (أنشئه إذا لم يكن لديك واحد).
  2. اختر Assets > Create > Valkarn Tasks > Task Settings.
  3. اسم الملف ValkarnTaskSettings وضعه داخل مجلد Resources.

يجب تسمية الملف ValkarnTaskSettings بالضبط ويجب أن يوجد في مجلد باسم Resources في أي مكان في مشروعك. يُحمَّل الأصل في وقت التشغيل بـResources.Load<ValkarnTaskSettings>("ValkarnTaskSettings").

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


الوصول إلى الإعدادات في وقت التشغيل

في بنيات Unity، تُقرأ الإعدادات من الأصل عبر singleton مخزَّن مؤقتًا:

ValkarnTaskSettings settings = ValkarnTaskSettings.Instance;

مُعاملات مجموعة الموارد الأكثر حاجةً تُعرض مباشرةً على ValkarnTask للراحة:

int max      = ValkarnTask.DefaultMaxPoolSize;   // يقرأ من ValkarnTaskSettings.Instance
int min = ValkarnTask.MinPoolSize;
int interval = ValkarnTask.TrimCheckInterval;

في بنيات غير Unity (الاختبارات، .NET مستقل)، ValkarnTaskSettings هو فئة ثابتة مع خصائص قابلة للتعديل بدلًا من ScriptableObject. تنطبق نفس أسماء الخصائص ويمكن كتابتها مباشرةً:

// بنيات غير Unity / الاختبار فقط
ValkarnTask.DefaultMaxPoolSize = 512;
ValkarnTask.TrimCheckInterval = 600;
ValkarnTask.MinPoolSize = 16;

الخصائص القابلة للتكوين

تهيئة مجموعة الموارد

DefaultMaxPoolSize

النوعint
الافتراضي256
النطاق الصالح81024
تلميح المفتش"الحد الأقصى للعناصر لكل نوع مجموعة موارد. تُزال العناصر الزائدة."

الحد الأقصى للكائنات المحتفظ بها في كل مجموعة موارد لكل نوع. كل تهيئة جنيريكية مميزة (مثل PooledPromise<int>، PooledPromise<string>) لها مجموعة موارد خاصة بها مُحدَّة بهذه القيمة.

عندما تكتمل مهمة ويُرجع كائنها الداخلي إلى مجموعة الموارد، إذا كانت مجموعة الموارد تحمل بالفعل عناصر بقدر DefaultMaxPoolSize، يُتجاهل الكائن المُرجَع (مؤهل لجامع النفايات). هذا يمنع نمو الذاكرة اللانهائي بعد موجة من النشاط الغير متزامن.

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

MinPoolSize

النوعint
الافتراضي8
النطاق الصالح164
تلميح المفتش"الحد الأدنى لمجموعة الموارد — لا تتقلص أدنى من هذا."

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

TrimCheckInterval

النوعint
الافتراضي300
النطاق الصالح301000
تلميح المفتش"الإطارات بين فحوصات التقليص. عند 60fps، 300 ≈ 5 ثوانٍ."

كم إطارًا يمر بين مرورات تقليص مجموعة الموارد. يسير مرور التقليص على جميع مجموعات الموارد المسجلة ويُطلق الكائنات الزائدة (تلك التي تجاوزت MinPoolSize) إذا كانت مجموعة الموارد متضخمة باستمرار.

عند 60 fps، القيمة الافتراضية 300 تعادل تقريبًا 5 ثوانٍ بين الفحوصات. قلّل هذه القيمة إذا أردت تقليصًا أكثر تكرارًا (بتكلفة عمل تقليص أكثر تكرارًا). ارفعها إذا كانت مرورات التقليص تظهر كطفرات في التنميط.

TrimHysteresisCount

النوعint
الافتراضي2
النطاق الصالح110
تلميح المفتش"عدد الفحوصات المتتالية فوق الحد قبل التقليص."

لا تُقلَّص مجموعة موارد إلا بعد ملاحظتها متضخمة لهذا العدد من دورات التقليص المتتالية. هذا يمنع التخبط — إذا كانت اللعبة لديها ارتفاع مفاجئ ثم فترة هادئة، يعني عدد التخلف 2 أن مجموعة الموارد تنجو من دورة هادئة واحدة قبل أن تبدأ في تحرير الكائنات.

TrimReleaseRatio

النوعfloat
الافتراضي0.25
النطاق الصالح0.11.0
تلميح المفتش"كسر الزيادة للتحرير لكل دورة تقليص (0.25 = 25%)."

عندما تُقلَّص مجموعة موارد، يُحرَّر هذا الكسر من الطاقة الزائدة (العناصر التي تجاوزت MinPoolSize) لكل دورة بدلًا من كلها مرة واحدة. القيمة 0.25 تعني أن كل مرور تقليص يُزيل 25% من الزيادة. هذا التحرير التدريجي يتجنب انخفاضًا مفاجئًا في حجم مجموعة الموارد قد يسبب موجة تخصيص إذا ارتفع الحمل مجددًا.

اضبطها على 1.0 إذا أردت تحرير كل الزيادة فورًا في كل دورة.


دورة الحياة

EnableAutoCancel

النوعbool
الافتراضيtrue
تلميح المفتش"ربط تلقائي لمهام MonoBehaviour بـ destroyCancellationToken."

عند التفعيل، تُربط المهام المبدأة من MonoBehaviour تلقائيًا بـdestroyCancellationToken لتلك MonoBehaviour. إذا دُمِّرت MonoBehaviour أثناء تشغيل مهمة، تُلغى المهمة بدلًا من الاستمرار في التنفيذ ضد كائن مدمر.

عطّل هذا فقط إذا كنت تُدير الإلغاء يدويًا ولا تريد الربط التلقائي.


معالجة الأخطاء

LogUnobservedCancellations

النوعbool
الافتراضيfalse
تلميح المفتش"تسجيل الإلغاءات غير الملاحظة كتحذيرات."

افتراضيًا، تُتجاهل المهمة التي أُلغيت لكن لم تُنتظَر (إلغاء غير ملاحظ) بصمت. فعّل هذا لتسجيل تحذير عند حدوث ذلك. مفيد أثناء التطوير للعثور على مهام fire-and-forget التي تُلغى بصمت.

تُبلَّغ عن الأخطاء غير الملاحظة دائمًا عبر حدث ValkarnTask.UnobservedException بغض النظر عن هذا الإعداد.

MaxExceptionLogsPerFrame

النوعint
الافتراضي10
النطاق الصالح1100
تلميح المفتش"الحد الأقصى لإدخالات سجل الاستثناءات لكل إطار لمنع البريد المزعج."

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


كيف يعمل تقليص مجموعة الموارد في وقت التشغيل

في كل تحديث لحلقة Unity player، يزيد PlayerLoopHelper عداد إطار. عندما يصل العداد إلى TrimCheckInterval، يستدعي PoolRegistry.TrimAll(MinPoolSize). كل مجموعة موارد مسجلة تفحص ما إذا كانت فوق الطاقة لعدد TrimHysteresisCount المتتالي من الفحوصات. إذا كان الأمر كذلك، تُطلق TrimReleaseRatio من زيادتها.

تسجّل مجموعات الموارد نفسها تلقائيًا عند أول استخدام. تُرجع طريقة ValkarnTask.GetPoolInfo() لقطة من جميع مجموعات الموارد المسجلة حاليًا مع نوعها وحجمها الحالي والحجم الأقصى — هذا ما تعرضه نافذة Task Tracker.

الإطار 0 ──────────────────────────────────────────────────────────
تعمل حلقة player
مجموعة الموارد أ: الحجم 200، الحد الأقصى 256 — طبيعي، لا حاجة للتقليص

الإطار 300 ────────────────────────────────────────────────────────
TrimAll يُطلَق
مجموعة الموارد أ: الحجم 18، الحد الأقصى 256 — فوق MinPoolSize(8)، لكن أول فحص زيادة
hysteresisCount لـأ = 1 (لم يصل بعد لحد 2)

الإطار 600 ────────────────────────────────────────────────────────
TrimAll يُطلَق
مجموعة الموارد أ: الحجم 18، الحد الأقصى 256 — فوق MinPoolSize(8)، ثاني فحص زيادة
hysteresisCount لـأ = 2 — وصل الحد!
الزيادة = 18 - 8 = 10؛ أطلق 10 * 0.25 = 2 كائنات
مجموعة الموارد أ: الحجم 16

نافذة Task Tracker

افتح عبر Window > Valkarn Tasks > Task Tracker.

Task Tracker نافذة محرر فقط تُظهر حالة مجموعة الموارد المباشرة أثناء وضع التشغيل. تتحدث بفاصل زمني قابل للتكوين (الافتراضي 0.5 ثانية، قابل للضبط من 0.1 إلى 5 ثوانٍ عبر شريط التمرير في شريط الأدوات).

تبويب Pools

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

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

إذا لم تُستخدم أي مجموعات موارد بعد، تقرأ رسالة "لا توجد مجموعات موارد نشطة. تُنشأ مجموعات الموارد عند أول استخدام."

تبويب Config

يُظهر مُعاملات مجموعة الموارد الثلاثة المباشرة كما تُقرأ من ValkarnTask.DefaultMaxPoolSize وValkarnTask.TrimCheckInterval وValkarnTask.MinPoolSize. تُظهَر القيم فقط في وضع التشغيل — في وضع التحرير تُنبّهك ملاحظة بالدخول إلى وضع التشغيل لرؤية القيم المباشرة.

يُظهر تبويب Config أيضًا مرجعًا لأصل ValkarnTaskSettings (إذا وُجد في Resources)، لذا يمكنك النقر للفحص أو التعديل. إذا لم يُوجد أصل، يُوجّهك تحذير لإنشاء واحد.


Result<T>

Result<T> هو بنية تمييزية تمثل نتيجة عملية بدون رمي. هو نوع الإرجاع الذي تستخدمه مُجمِّعات WhenAll للإبلاغ عن نتائج المهام الفردية، وهو أيضًا متاح كنمط نتيجة للأغراض العامة.

public readonly struct Result<T>
{
public bool IsSuccess { get; }
public bool IsFailure { get; } // صحيح إذا كانت معطوبة أو مُلغاة
public bool IsFaulted { get; }
public bool IsCanceled { get; }

public ValkarnTask.Status Status { get; }
public T Value { get; } // يرمي إذا لم تنجح
public Exception Error { get; } // null إذا لم تُخطئ

public static Result<T> Success(T value);
public static Result<T> Failure(string error); // يُغلّف في InvalidOperationException
public static Result<T> Faulted(Exception error);
public static Result<T> Canceled(OperationCanceledException oce = null);

public static implicit operator bool(Result<T> r); // صحيح إذا نجحت
}

يوجد Result غير جنيريكي للمهام الـvoid بنفس الشكل لكن بدون Value.

طرق امتداد AsResult

حوّل أي ValkarnTask أو ValkarnTask<T> إلى Result بدون try/catch في كودك الخاص:

public static ValkarnTask<Result<T>> AsResult<T>(this ValkarnTask<T> task);
public static ValkarnTask<Result> AsResult(this ValkarnTask task);

إذا كانت المهمة الأساسية مكتملة بشكل متزامن بالفعل (المسار السريع الخالي من التخصيص)، تُرجع AsResult فورًا بدون إنشاء آلة حالة غير متزامنة. وإلا تُغلّف في طريقة غير متزامنة تلتقط OperationCanceledException وجميع الاستثناءات الأخرى، مُترجِمةً إياها إلى نوع Result المناسب.

متى تستخدم Result<T>

استخدم Result<T> بدلًا من التقاط الاستثناءات عندما:

  • تستدعي عدة مهام بالتوازي وتريد نتائج لكل مهمة بدون تقصير الدُفعة بالكامل.
  • تريد التعبير عن العمليات القابلة للفشل بطريقة مضمونة النوع بدون تدفق تحكم بالاستثناءات.
  • تُرجع من طريقة قد لا يريد المُستدعي تغليفها في try/catch.
// أطلق مهام متعددة، احصل على جميع النتائج بغض النظر عن الإخفاقات الفردية
Result<int>[] results = await ValkarnTask.WhenAll(
FetchScoreAsync(playerA).AsResult(),
FetchScoreAsync(playerB).AsResult(),
FetchScoreAsync(playerC).AsResult()
);

foreach (var r in results)
{
if (r.IsSuccess)
Debug.Log($"النتيجة: {r.Value}");
else if (r.IsFaulted)
Debug.LogError($"فشل: {r.Error.Message}");
else
Debug.Log("مُلغى");
}

فحص IsSuccess أو استخدام المشغّل الضمني bool هما الطريقتان المفضّلتان للتفرع على نتيجة:

var result = await SomeOperationAsync().AsResult();

if (result)
{
Use(result.Value);
}

الوصول إلى result.Value عندما تكون IsSuccess هي false يرمي InvalidOperationException.

طرق المصنع

الطريقةالحالة المضبوطةالخطأ المضبوط
Result<T>.Success(value)Succeededلا شيء
Result<T>.Failure(message)Faultednew InvalidOperationException(message)
Result<T>.Faulted(exception)Faultedالاستثناء المُقدَّم
Result<T>.Canceled(oce?)CanceledOperationCanceledException المُقدَّم، أو null

خاصية Succeeded موجودة على كلٍّ من Result وResult<T> لكنها مُعلَّمة بـ[Obsolete] — استخدم IsSuccess بدلًا من ذلك للاتساق مع IsFailure وIsFaulted وIsCanceled.