إتقان الكاميرات ثنائية الأبعاد في الوحدة: برنامج تعليمي لمطوري الألعاب
نشرت: 2022-03-11بالنسبة للمطور ، تعد الكاميرا أحد الأركان الأساسية في عملية تطوير اللعبة. من مجرد عرض عرض لعبتك في تطبيق شطرنج إلى توجيه حركة الكاميرا ببراعة في لعبة AAA ثلاثية الأبعاد للحصول على تأثيرات سينمائية ، تُستخدم الكاميرات أساسًا في أي لعبة فيديو تم صنعها على الإطلاق ، حتى قبل أن يطلق عليها في الواقع اسم "الكاميرات".
سأشرح في هذا المقال كيفية تصميم نظام كاميرا للألعاب ثنائية الأبعاد ، وسأشرح أيضًا بعض النقاط حول كيفية البدء في تنفيذه في أحد أكثر محركات الألعاب شيوعًا ، وهو Unity.
من 2D إلى 2.5D: نظام كاميرا قابل للتوسيع
نظام الكاميرا الذي سنقوم بتصميمه معًا هو نظام معياري وقابل للتوسيع. يحتوي على نواة أساسية تتكون من عدة مكونات ستضمن الوظيفة الأساسية ، ثم المكونات / التأثيرات المختلفة التي يمكن استخدامها اختياريًا ، اعتمادًا على الموقف في متناول اليد.
يستهدف نظام الكاميرا الذي نبنيه هنا ألعاب المنصات ثنائية الأبعاد ، ولكن يمكن توسيعه بسهولة ليشمل أنواعًا أخرى من الألعاب ثنائية الأبعاد أو ألعاب 2.5D أو حتى الألعاب ثلاثية الأبعاد.
سأقوم بتقسيم وظائف الكاميرا إلى مجموعتين رئيسيتين: تتبع الكاميرا وتأثيرات الكاميرا.
تتبع
ستعتمد معظم حركة الكاميرا التي سنقوم بها هنا على التتبع. هذه هي قدرة الكائن ، في هذه الحالة الكاميرا ، على تتبع أشياء أخرى أثناء تحركها في مشهد اللعبة. ستعمل أنواع التتبع التي سننفذها على حل بعض السيناريوهات الشائعة التي تمت مواجهتها في ألعاب النظام الأساسي ثنائية الأبعاد ، ولكن يمكن توسيعها بأنواع جديدة من التتبع لسيناريوهات معينة أخرى قد تكون لديك.
تأثيرات
سنقوم بتنفيذ بعض التأثيرات الرائعة مثل اهتزاز الكاميرا وزوم الكاميرا وتلاشي الكاميرا وتراكب الألوان.
ابدء
أنشئ مشروعًا جديدًا ثنائي الأبعاد في Unity واستورد الأصول القياسية ، وخاصة شخصية RobotBoy. بعد ذلك ، قم بإنشاء مربع أرضي وأضف مثيلاً للحرف. يجب أن تكون قادرًا على المشي والقفز مع شخصيتك في المشهد الحالي. تأكد من ضبط الكاميرا على الوضع العمودي (يتم ضبطها على منظور افتراضيًا).
تتبع الهدف
سيضيف البرنامج النصي التالي سلوك التتبع الأساسي إلى الكاميرا الرئيسية لدينا. يجب إرفاق البرنامج النصي كمكون بالكاميرا الرئيسية في المشهد الخاص بك ويكشف حقلاً لتعيين كائن هدف لتتبعه. ثم يضمن البرنامج النصي أن إحداثيات الكاميرا x و y متطابقة مع الكائن الذي تتعقبه. تتم كل هذه المعالجة أثناء خطوة التحديث.
[SerializeField] protected Transform trackingTarget; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x, trackingTarget.position.y, transform.position.z); }
اسحب شخصية RobotBoy من التسلسل الهرمي للمشهد الخاص بك فوق حقل "هدف التتبع" المكشوف من خلال سلوكنا التالي من أجل تمكين تتبع الشخصية الرئيسية.
مضيفا الإزاحة
كل شيء جيد ، ولكن يمكننا أن نرى قيودًا مباشرة من الخفافيش: الشخصية دائمًا في مركز المشهد لدينا. يمكننا أن نرى الكثير خلف الشخصية ، والتي عادة ما تكون أشياء لا نهتم بها ، ونرى القليل جدًا مما هو أمام شخصيتنا ، والذي قد يكون ضارًا بطريقة اللعب.
لحل هذه المشكلة ، نضيف بعض الحقول الجديدة إلى البرنامج النصي الذي سيسمح بوضع الكاميرا في إزاحة عن هدفها.
[SerializeField] float xOffset; [SerializeField] float yOffset; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x + xOffset, trackingTarget.position.y + yOffset, transform.position.z); }
يمكنك أن ترى أدناه تكوينًا محتملاً للحقلين الجديدين:
تنعيم الأشياء
حركة الكاميرا قاسية جدًا وستنتج أيضًا دوارًا لدى بعض اللاعبين من الحركة المتصورة للبيئة. من أجل إصلاح هذا ، سنضيف بعض التأخير في تتبع الكاميرا باستخدام الاستيفاء الخطي ، ومجالًا جديدًا للتحكم في مدى سرعة الكاميرا في مكانها بعد أن تبدأ الشخصية في تغيير موضعها.
[SerializeField] protected float followSpeed; // ... protected override void Update() { float xTarget = trackingTarget.position.x + xOffset; float yTarget = trackingTarget.position.y + yOffset; float xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); float yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); transform.position = new Vector3(xNew, yNew, transform.position.z); }
وقف الدوخة: قفل المحور
نظرًا لأنه ليس من الجيد لعقلك أن يشاهد الكاميرا تتجه لأعلى ولأسفل طوال الوقت جنبًا إلى جنب مع الشخصية ، فإننا نقدم قفل المحور. هذا يعني أنه يمكننا قصر التتبع على محور واحد فقط. بعد ذلك ، سنقوم بفصل شفرة التتبع الخاصة بنا إلى تتبع مستقل عن المحور ، وسنأخذ علامات القفل الجديدة في الاعتبار.
[SerializeField] protected bool isXLocked = false; [SerializeField] protected bool isYLocked = false; // ... float xNew = transform.position.x; if (!isXLocked) { xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); } float yNew = transform.position.y; if (!isYLocked) { yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); }
نظام المسار
الآن بما أن الكاميرا تتعقب المشغل أفقيًا فقط ، فإننا مقيدون بارتفاع شاشة واحدة. إذا صعدت الشخصية بعض السلم أو قفزت أعلى من هذا ، فعلينا أن نتبعه. الطريقة التي نقوم بها هي استخدام نظام الممرات.
تخيل السيناريو التالي:
الشخصية في البداية في الممر السفلي. بينما تظل الشخصية داخل حدود هذا الممر ، ستتحرك الكاميرا أفقيًا فقط عند إزاحة ارتفاع معين للممر يمكننا ضبطه.
بمجرد أن تدخل الشخصية إلى حارة أخرى ، ستنتقل الكاميرا إلى ذلك الممر وتستمر في التحرك أفقيًا من هناك إلى أن يحدث تغيير المسار التالي.
يجب توخي الحذر عند تصميم الممر من أجل منع التبديل السريع للممر أثناء إجراءات مثل القفزات ، والتي يمكن أن تخلق ارتباكًا للاعب. يجب تغيير الحارة فقط إذا كانت شخصية اللاعب ستبقى عليها لفترة من الوقت.
يمكن أن تتغير مستويات Lanes طوال مستوى اللعبة بناءً على احتياجات المصمم المحددة ، أو يمكن مقاطعتها تمامًا ويمكن لنظام تتبع كاميرا آخر أن يحل محله. لذلك ، نحتاج إلى بعض المحددات لتحديد مناطق الممرات.
تطبيق
التنفيذ المحتمل هو إضافة الممرات كأشياء بسيطة في المشهد. سنستخدم إحداثيات الموضع Y المقترنة بإزاحة Y في نص التتبع أعلاه لتنفيذ النظام. لذلك ، فإن تحديد موقعهم على إحداثيات X و Z لا يهم.
أضف فئة LaneSystem إلى الكاميرا ، جنبًا إلى جنب مع فئة التتبع ، وقم بتعيين كائنات الممر للمصفوفة المتوفرة. قم أيضًا بتعيين شخصية اللاعب إلى الحقل المرجعي. نظرًا لوضع المرجع بين حارة وحارة أخرى ، فسيتم استخدام الحارة السفلية من الاثنين لوضع الكاميرا.
وتهتم فئة نظام LaneSystem بتحريك الكاميرا بين الممرات ، بناءً على الموضع المرجعي. يتم استخدام FollowSpeed هنا مرة أخرى لاستيفاء الموضع ، لمنع تبديل المسار من أن يكون مفاجئًا للغاية:
[SerializeField] Transform reference; [SerializeField] List<Transform> lanes; [SerializeField] float followSpeed = 5f; // ... void Update() { float targetYCoord = transform.position.y; if (lanes.Count > 1) { int i = 0; for (i = 0; i < lanes.Count - 1; ++i) { if ((reference.position.y > lanes[i].position.y) && (reference.position.y <= lanes[i + 1].position.y)) { targetYCoord = lanes[i].position.y; break; } } if (i == lanes.Count - 1) targetYCoord = lanes[lanes.Count - 1].position.y; } else { targetYCoord = lanes[0].position.y; } float yCoord = Mathf.Lerp(transform.position.y, targetYCoord, Time.deltaTime * followSpeed); transform.position = new Vector3(transform.position.x, yCoord, transform.position.z); }
هذا التطبيق ليس تطبيق WYSIWYG ، ويتم تركه كتمرين للقارئ.

نظام قفل العقدة
يعد تحريك الكاميرا على الممرات أمرًا رائعًا ، ولكن في بعض الأحيان نحتاج إلى قفل الكاميرا على شيء ما ، نقطة اهتمام (POI) في مشهد اللعبة.
يمكن تحقيق ذلك من خلال تكوين نقاط الاهتمام هذه في المشهد وإرفاق مصادم الزناد بها. عندما تدخل الشخصية في مصادم الزناد ، نحرك الكاميرا ونبقى في نقطة الاهتمام. عندما تتحرك الشخصية ثم تغادر مصادم الزناد POI ، نعود إلى نوع آخر من التتبع ، عادة ما يكون سلوك المتابعة القياسي.
يمكن أن يتم تبديل تتبع الكاميرا إلى عقدة القفل والظهر إما عن طريق مفتاح بسيط أو عن طريق نظام مكدس ، حيث يتم دفع أوضاع التعقب وفرقعها.
تطبيق
لتكوين عقدة قفل ، ما عليك سوى إنشاء كائن (يمكن أن يكون فارغًا أو كما في الصورة أدناه ، كائن) وأرفق مكون Circle Collider 2D كبير به بحيث يحدد المنطقة التي سيكون فيها المشغل عندما تكون الكاميرا ركز العقدة. يمكنك اختيار أي نوع من المصادم ، سأختار Circle كمثال هنا. يمكنك أيضًا إنشاء علامة يمكنك التحقق منها بسهولة ، مثل "CameraNode" وتخصيصها لهذا الكائن.
أضف الخاصية التالية إلى البرنامج النصي للتتبع على الكاميرا:
public Transform TrackingTarget { get { return trackingTarget; } set { trackingTarget = value; } }
ثم قم بإرفاق النص التالي بالمشغل ، والذي سيسمح له بتبديل هدف الكاميرا مؤقتًا إلى عقدة القفل التي قمت بتعيينها. سيتذكر البرنامج النصي أيضًا هدفه السابق حتى يتمكن من العودة إليه عندما يكون اللاعب خارج منطقة التشغيل. يمكنك المضي قدمًا وتحويل هذا إلى مكدس كامل إذا كنت بحاجة إلى ذلك ، ولكن لغرضنا لأننا لا نتداخل مع عدة عقد قفل ، فإن هذا سيفي بالغرض. يرجى أيضًا الانتباه إلى أنه يمكنك تعديل موضع مصادم الدائرة 2D ، أو إضافة أي نوع آخر من المصادم لتشغيل قفل الكاميرا ، وهذا مجرد مثال.
public class LockBehavior : MonoBehaviour { #region Public Fields [SerializeField] Camera camera; [SerializeField] string tag; #endregion #region Private private Transform previousTarget; private TrackingBehavior trackingBehavior; private bool isLocked = false; #endregion // Use this for initialization void Start() { trackingBehavior = camera.GetComponent<TrackingBehavior>(); } void OnTriggerEnter2D(Collider2D other) { if (other.tag == tag && !isLocked) { isLocked = true; PushTarget(other.transform); } } void OnTriggerExit2D(Collider2D other) { if (other.tag == tag && isLocked) { isLocked = false; PopTarget(); } } private void PushTarget(Transform newTarget) { previousTarget = trackingBehavior.TrackingTarget; trackingBehavior.TrackingTarget = newTarget; } private void PopTarget() { trackingBehavior.TrackingTarget = previousTarget; } }
تكبير الكاميرا
يمكن تنفيذ تكبير / تصغير الكاميرا إما على إدخال المستخدم أو كرسوم متحركة عندما نريد التركيز على شيء مثل POI أو منطقة أضيق داخل المستوى.
يمكن تحقيق تكبير الكاميرا ثنائية الأبعاد في Unity 3D من خلال معالجة حجم الكاميرا المتعامد. سيؤدي إرفاق النص التالي كمكون بالكاميرا واستخدام طريقة SetZoom لتغيير عامل التكبير / التصغير إلى التأثير المطلوب. 1.0 يعني عدم التكبير ، 0.5 يعني التكبير مرتين ، 2 يعني التصغير مرتين ، وهكذا.
[SerializeField] float zoomFactor = 1.0f; [SerializeField] float zoomSpeed = 5.0f; private float originalSize = 0f; private Camera thisCamera; // Use this for initialization void Start() { thisCamera = GetComponent<Camera>(); originalSize = thisCamera.orthographicSize; } // Update is called once per frame void Update() { float targetSize = originalSize * zoomFactor; if (targetSize != thisCamera.orthographicSize) { thisCamera.orthographicSize = Mathf.Lerp(thisCamera.orthographicSize, targetSize, Time.deltaTime * zoomSpeed); } } void SetZoom(float zoomFactor) { this.zoomFactor = zoomFactor; }
إهتزاز الشاشة
عندما نحتاج إلى إظهار زلزال أو انفجار أو أي تأثير آخر في لعبتنا ، يكون تأثير اهتزاز الكاميرا مفيدًا.
يتوفر مثال لتنفيذ كيفية القيام بذلك على GitHub: gist.github.com/ftvs/5822103. التنفيذ واضح ومباشر. على عكس التأثيرات الأخرى التي غطيناها حتى الآن ، فهي تعتمد على القليل من العشوائية.
تتلاشى وتراكب
عندما يبدأ مستوانا أو ينتهي ، يكون تأثير التلاشي أو التلاشي أمرًا رائعًا. يمكننا تنفيذ ذلك عن طريق إضافة مادة واجهة مستخدم غير قابلة للتفاعل في لوحة تمتد في جميع أنحاء شاشتنا. في البداية شفافة ، يمكننا ملء هذا بأي لون وشفافية ، أو تحريك ذلك لتحقيق التأثير الذي نريده.
فيما يلي مثال على هذا التكوين ، يرجى ملاحظة أن كائن لوحة واجهة المستخدم يتم تعيينه إلى "تراكب الكاميرا" التابع لكائن الكاميرا الرئيسي. يكشف Camera Overlay عن برنامج نصي يسمى Overlay يتميز بما يلي:
[SerializeField] Image overlay; // ... public void SetOverlayColor(Color color) { overlay.color = color; }
من أجل الحصول على تأثير التلاشي ، قم بتغيير نص التراكب الخاص بك عن طريق إضافة إقحام إلى اللون المستهدف الذي قمت بتعيينه باستخدام SetOverlayColor كما هو الحال في البرنامج النصي التالي ، وقم بتعيين اللون الأولي للوحة لدينا على الأسود (أو الأبيض) واللون الهدف إلى اللون النهائي للتراكب الخاص بك. يمكنك تغيير fadeSpeed إلى ما يناسب احتياجاتك ، أعتقد أن 0.8 هو خيار جيد للمبتدئين. تعمل قيمة fadeSpeed كمعدِّل للوقت. 1.0 يعني أنه سيحدث عبر إطارات متعددة ، ولكن في غضون إطار زمني يبلغ ثانية واحدة. 0.8 يعني أن الأمر سيستغرق فعليًا 1 / 0.8 = 1.25 ثانية حتى يكتمل.
public class Overlay : MonoBehaviour { #region Fields [SerializeField] Image overlay; [SerializeField] float fadeSpeed = 5f; [SerializeField] Color targetColor; #endregion void Update() { if (overlay.color != targetColor) { overlay.color = Color.Lerp(overlay.color, targetColor, Time.deltaTime * fadeSpeed); } } #region Public public void SetOverlayColor(Color color) { targetColor = color; } #endregion }
يتم إحتوائه
حاولت في هذه المقالة توضيح المكونات الأساسية اللازمة لامتلاك نظام كاميرا ثنائي الأبعاد معياري للعبتك ، وأيضًا ما هي العقلية المطلوبة لتصميمها. بطبيعة الحال ، كل الألعاب لها احتياجاتها الخاصة ، ولكن مع التتبع الأساسي والتأثيرات البسيطة الموضحة هنا ، يمكنك الحصول على شوط طويل والحصول أيضًا على مخطط لتنفيذ التأثيرات الخاصة بك. ثم يمكنك الذهاب إلى أبعد من ذلك وتعبئة كل شيء في حزمة Unity ثلاثية الأبعاد قابلة لإعادة الاستخدام والتي يمكنك نقلها إلى مشاريع أخرى أيضًا.
تعد أنظمة الكاميرا مهمة جدًا في نقل الجو المناسب للاعبين. المقارنة الجيدة التي أحب استخدامها هي عندما تفكر في الفرق بين المسرح الكلاسيكي والأفلام. جلبت الكاميرات والأفلام نفسها العديد من الاحتمالات للمشهد حتى أنها تطورت في النهاية إلى فن بمفردها ، لذلك إذا كنت لا تخطط لتنفيذ لعبة "بونج" أخرى ، فيجب أن تكون الكاميرات المتقدمة هي الأداة التي تختارها في أي مشروع لعبة تريده سوف نتعهد من الآن فصاعدا.