ملاحظة: يمكن تحميل الملفات المصدرية لكامل هذه السلسلة عبر حساب أكاديمية حسوب على Github، يمكن أيضا تحميل ملف APK لتجريب اللعبة على أجهزة Android.
استقبال مدخلات شاشة اللمس
استقبال المدخلات من شاشة اللمس تختلف عن استقبال مدخلات الفأرة أو لوحة المفاتيح. فأنت هنا تتعامل مع شاشة تستقبل عدة لمسات، وتتعامل مع أحداث مثل وضع الإصبع وتحريكه ورفعه، أو وضع أصبعين وتقريبهما أو إبعادهما عن بعضهما. كل هذه الأنماط من الإدخال سيكون عليك التعرف عليها وتنفيذ ما يناسبها من أوامر.
لنبدأ أولا مع مدخلات عناصر واجهة المستخدم مثل الأزرار. ما الذي يجب علينا فعله لاستقبال المدخلات على هذه العناصر؟ لا شيء! فهذا الأمر يقوم به محرك Unity تلقائيا. أي أنك عندما تلمس زرا على الشاشة سيتعرف التطبيق على هذا الأمر كأنك ضغطت على الزر وينفذ الأمر أو الأوامر المرتبطة به.
لننتقل الآن للمدخلات الخاصة باللعبة. تذكر أننا قمنا بكتابة 3 بريمجات لاستقبال مدخلات الفأرة، وسنحتاج الآن لكتابة 3 مقابلها لاستقبال مدخلات شاشة اللمس. هذه البريمجات الثلاث هي:
- CameraMouseInput والذي أضفناه لقوالب عناصر الخلفية والأرضية من أجل تحريك الكاميرا والتحكم بتقريبها وإبعادها.
- LauncherMouseInput والذي أضفناه على قالب المقلاع بحيث يستقبل مدخلات اللاعب على المقذوفات ويحدد اتجاه التصويب وشدته ويطلق المقذوف حين إفلاته.
- SpecialAttackMouseInput والذي أضفناه على قوالب المقذوفات من أجل تنفيذ الهجوم الخاص عند الضغط على زر الفأرة بعد الإطلاق.
الموضوع ببساطة شديدة أننا مقابل كل بريمج إدخال من الفأرة سنكتب بريمج إدخال من شاشة اللمس ونضيفه على نفس القالب. القاسم المشترك بين قوالب استقبال مدخلات اللمس أنها ستقوم بالتحقق من بيئة تشغيل اللعبة، فإذا وجدت أن نظام التشغيل هو Android ستقوم بتدمير بريمجات استقبال مدخلات الفأرة الموجودة معها على نفس الكائن. أهمية هذه الخطوة تكمن في أن Unity يحاول محاكاة وجود فأرة على شاشات اللمس عن طريق تحويل اللمسات إلى مدخلات فأرة. هذا الأمر يؤدي إلى سلوك غير مرغوب لا يفيد في لعبتنا، لذا نتجنبه عن طريق حذف أي بريمج يقرأ من الفأرة.
لنبدأ مع بريمج التحكم بالكاميرا CameraTouchInput والذي سنضيفه على قالبي عنصري الخلفية والأرضية حيث يوجد CameraMouseInput أيضا. هذا البريمج موضح في السرد التالي:
using UnityEngine; using System.Collections; public class CameraTouchInput : MonoBehaviour { //سرعة تحريك الكاميرا public float movementSpeed = 0.0075f; //سرعة التقريب والإبعاد public float zoomingSpeed = 0.00075f; //هي يلمس اللاعب الشاشة حاليا بإصبع واحد؟ private bool singleTouch = false; //هل يلمس اللاعب الشاشة حاليا بأصبعين private bool doubleTouch = false; //مرجع لبريمج التحكم بالكاميرا CameraControl camControl; //تستدعى مرة عند بداية التشغيل void Start() { //قم بتدمير بريمج استقبال مدخلات الفأرة إن تم اكتشاف نظام تشغيل الهاتف الذكي if (Application.platform == RuntimePlatform.Android) { CameraMouseInput mouseInput = GetComponent<CameraMouseInput>(); Destroy(mouseInput); } camControl = FindObjectOfType<CameraControl>(); } //يتم استدعاؤها مرة عند تصيير كل إطار في وقت متأخر void LateUpdate() { UpdateSingleTouch(); UpdateDragging(); UpdateDoubleTouch(); UpdateZooming(); } //تتأكد من كون اللاعب يلمس الشاشة بإصبع واحد void UpdateSingleTouch() { if (Input.touchCount == 1) { Touch playerTouch = Input.touches[0]; if (playerTouch.phase == TouchPhase.Began) { //قام اللاعب للتو بوضع إصبعه على الشاشة //هل تم وضع الإصبع على هذا الكائن تحديدا؟ Vector2 touchPos = Camera.main.ScreenToWorldPoint(playerTouch.position); //اجلب مكوّن التصادم الخاص بهذا الكائن Collider2D myCollider = GetComponent<Collider2D>(); //قم بتوليد شعاع قصير جدا ابتداء من موقع اللمس وباتجاه الأعلى واليمين //ومن ثم تحقق من كون هذا الشعاع قد اصطدم بمكوّن التصادم الخاص بهذا الكائن RaycastHit2D hit = Physics2D.Raycast(touchPos, Vector2.one, 0.1f); if (hit.collider == myCollider) { //نعم لقد قام اللاعب بلمس هذا الكائن بإصبع واحد singleTouch = true; } } else if (playerTouch.phase == TouchPhase.Ended || playerTouch.phase == TouchPhase.Canceled) { //قام اللاعب للتو برفع إصبعه عن الشاشة singleTouch = false; } } else { //عدد الأصابع على الشاشة لا يساوي 1 أي أنه //لا يوجد هناك لمسة بإصبع واحد singleTouch = false; } } //تقوم بفحص تحريك اللاعب لإصبع واحد على الشاشة void UpdateDragging() { if (singleTouch) { Touch playerTouch = Input.touches[0]; camControl.Move(playerTouch.deltaPosition * -movementSpeed); } } //تقوم بفحص قيام اللاعب باللمس بأصبعين void UpdateDoubleTouch() { //تأكد من عدم وجود لمسة بإصبع واحد حاليا if (!singleTouch) { if (Input.touchCount == 2) { doubleTouch = true; } else { doubleTouch = false; } } } //تقوم بتحديث التقريب والإبعاد مستخدمة حركة الأصبعين على الشاشة void UpdateZooming() { if (doubleTouch) { //على الشاشة A, Bمواقع الأصبعين المسميين //في كل من الإطار الحالي 2 والإطار السابق 1 Vector2 posA1, posA2, posB1, posB2; Touch a, b; a = Input.touches[0]; b = Input.touches[1]; posA2 = a.position; posB2 = b.position; posA1 = a.position - a.deltaPosition; posB1 = b.position - b.deltaPosition; //تأكد من كون المسافة بين الأصابع قد زادت أو قلت منذ //الإطار السابق float currentDist = Vector2.Distance(posA2, posB2); float prevDist = Vector2.Distance(posA1, posB1); //طرح المسافة السابقة من الحالية سيعطينا //الإشارة الصحيحة للتقريب أو الإبعاد camControl.Zoom((currentDist - prevDist) * zoomingSpeed); } } }
آلية عمل هذا البريمج تعتمد على أربعة خطوات رئيسية:
- التحقق من لمس الشاشة بإصبع واحد.
- ثم التحقق من تحريك الكاميرا باستخدام الإصبع.
- ثم التحقق من لمس الشاشة بأصبعين.
- وأخيرا التأكد من تنفيذ التقريب والإبعاد باستخدام الأصبعين.
قراءة مدخلات شاشة اللمس تتم كالآتي: أولا نتعرف على عدد اللمسات على الشاشة عن طريق المتغير Input.touchCount، فإذا كان عدد اللمسات يساوي واحدا، فإن هذا يعني أن اللاعب يضع إصبعا على الشاشة واحتمال أن يقوم بتحريك الكاميرا وارد. نستخدم هنا المتغيرين singleTouch و doubleTouch من أجل تخزين عدد اللمسات التي اكتشفناها. هذه اللمسات تكون مخزنة في المصفوفة Input.touches وهي مصفوفة تحتوي على عناصر من نوع Touch. هذا النوع من المتغيرات يحتوي على معلومات عن كل لمسة على الشاشة. أول هذه المعلومات التي سنتعامل معها هي Touch.phase والتي تمثل المرحلة التي تمر بها اللمسة. فبمجرد أن يضع اللاعب إصبعه على الشاشة ستكون المرحلة هي TouchPhase.Began. اللحظة الأولى للمس الشاشة مهمة جدا، حيث أنها اللحظة التي يجب علينا أن نتحقق من كون اللاعب قد وضع إصبعه على عنصر الخلفية أو الأرضية، وبالتالي نحدد إذا ما كنا سنسمح له بتحريك الكاميرا. فنحن لا نرغب بتحريك الكاميرا إذا كان اللاعب قد وضع إصبعه على المقذوف أو على زر الخروج مثلا.
التحقق من كون اللاعب قد وضع يده على عنصر الخلفية أو الأرضية يتم عبر الدّالة ()UpdateSingleTouch، حيث علينا أن نقوم أولا بتحويل موقع الإصبع من إحداثيات الشاشة إلى إحداثيات المشهد تماما كما سبق وفعلنا مع مؤشر الفأرة. بعد ذلك نقوم باستخراج مكوّن التصادم الخاص بهذا العنصر ومن ثم نستخدم بث الأشعة Ray Casting. بث الأشعة طريقة يستخدمها محرك الفيزياء من أجل الكشف عن تقاطع خط مستقيم مع كائن ما في المشهد، وسنستخدمها هنا لنرسم خطا قصيرا جدا من موقع اللمس إلى نقطة شديدة القرب منه، ونرى إن كان هذا الخط يتقاطع مع مكوّن التصادم الذي استخرجناه. يتم تنفيذ بث الأشعة عن طريق الدّالة ()Physics2D.Raycast والتي تأخذ 3 متغيرات: المتغير الأول هو موقع بداية الشعاع، والمتغير الثاني هو الاتجاه الذي سيسير فيه هذا الشعاع، بينما يحدد المتغير الثالث وهو قيمة رقمية أقصى مسافة يمكن أن يقطعها هذا الشعاع. وهنا تلاحظ استخدامنا لمسافة قصيرة جدا، وهي كافية حين يكون موقع البداية موجودا أصلا داخل مكوّن التصادم الذي يتم فحص التصادم معه. القيمة التي ترجعها هذه الدّالة هي من نوع RaycastHit2D، وهي تحتوي على متغير لتخزين مكوّن التصادم الذي اصطدم به الشعاع.
بعد ذلك نفحص إذا ما كان المكوّن الذي اصطدم به الشعاع هو نفسه مكوّن التصادم الذي استخرجناه من العنصر الحالي، وإذا تحقق هذا الأمر فإننا نعتمد هذا اللمسة من قبل اللاعب على الخلفية أو الأرضية ونسمح له بالتالي بتحريك الكاميرا عن طريق تغيير قيمة singleTouch إلى true. في حالة كانت اللمسة في مرحلة أخرى مثل TouchPhase.Ended أو TouchPhase.Canceled، فإن هذا يعني أن اللاعب قد رفع إصبعه عن الشاشة، وبالتالي نعيد قيمة singleTouch إلى false. الأمر نفسه سيحدث في حال اكتشفنا أن عدد الأصابع الموجودة على الشاشة Input.touchCount لا يساوي واحدا، بالتالي لن نسمح للاعب في هذه الحالة بتحريك الكاميرا.
الدّالة ()UpdateDragging هي المسؤولة عن تحريك الكاميرا، لذا عليها أن تتأكد أولا من قيمة singleTouch وبعد ذلك تقوم بتحريك الكاميرا بالمقدار playerTouch.deltaPosition مضروبا في سرعة الحركة. لاحظ أننا هنا لا نحتاج لتخزين موقع اللمسة السابق حيث أن قيمة الإزاحة تأتينا مباشرة بعكس ما كان عليه الحال حين التعامل مع مؤشر الفأرة.
بعد ذلك تقوم الدّالة ()UpdateDoubleTouch بالتحقق من كون اللاعب يضع أصبعين على الشاشة، وفي هذه الحالة تقوم مباشرة بتغيير قيمة doubleTouch إلى true. لاحظ أن اللمس بأصبعين ليس له أي مدلول في اللعبة سوى التقريب والإبعاد بخلاف اللمسة الواحدة التي يمكن أن تستخدم لأكثر من غرض. ولهذا السبب لا نحتاج للتحقق من مواقع الأصبعين بل يكفي وجودهما لمنع أي مدخلات أخرى غير التقريب والإبعاد.
أخيرا تقوم الدّالة ()UpdateZooming بالتأكد من وجود أصبعين على الشاشة عبر المتغير doubleTouch وبناء عليها تحسب 4 مواقع وهي كالتالي:
- posA1: موقع الإصبع الأول خلال الإطار السابق.
- posA2: موقع الإصبع الأول خلال الإطار الحالي.
- posB1: موقع الإصبع الثاني خلال الإطار السابق.
- posB2: موقع الإصبع الثاني خلال الإطار الحالي.
لاحظ أننا قمنا بطرح الإزاحة من مواقع الأصابع الحالية حتى نحصل على مواقع الأصابع في الإطار السابق، وذلك لأننا لم نقم بتخزين هذه المواقع من الأساس. بعد ذلك نحسب المسافة بين الأصبعين في الإطار السابق prevDist والإطار الحالي currentDist. تذكر أن الدّالة ()Zoom في البريمج CameraControl تقوم بالتقريب إذا أعطيناها قيمة سالبة وبالإبعاد إذا أعطيناها قيمة موجبة. من أجل ذلك نقوم بطرح المسافة السابقة من المسافة الحالية وضرب الناتج في سرعة التقريب والإبعاد zoomingSpeed. بهذه الطريقة نضمن الحصول على قيمة موجبة إذا حرك اللاعب أصبعيه بعيدا عن بعضهما مما يؤدي للتقريب وقيمة سالبة إذا قرب اللاعب أصبعيه من بعضهما مما يؤدي للإبعاد، وهذا السلوك بطبيعة الحال هو المعتاد عند مستخدمي الهواتف الذكية والأجهزة اللوحية.
البريمج الثاني الذي سنتناوله في موضوع استقبال مدخلات شاشة اللمس هو الخاص بإطلاق المقذوفات. تذكر أننا قمنا بإضافة بريمج يسمى LauncherMouseInput على قالب مقلاع إطلاق المقذوفات. البريمج الجديد يسمى LauncherTouchInput وسنقوم بإضافته على نفس القالب من أجل تمكين اللاعب من إطلاق المقذوفات عن طريق اللمس. السرد التالي يوضح هذا البريمج:
using UnityEngine; using System.Collections; public class LauncherTouchInput : MonoBehaviour { //مرجع لبريمج إطلاق المقذوفات private Launcher launcher; //تستدعى مرة واحدة عند بداية التشغيل void Start () { //تقوم بتدمير بريمج قراءة مدخلات الفأرة في حال اكتشاف نظام تشغيل الهاتف if (Application.platform == RuntimePlatform.Android) { LauncherMouseInput mouseInput = GetComponent<LauncherMouseInput>(); Destroy(mouseInput); } launcher = GetComponent<Launcher>(); } //تستدعى مرة عند تصيير كل إطار void Update () { UpdateTouchStart(); UpdateDragging(); UpdateRelease(); } //تقوم بالتحقق من كون اللاعب قد وضع اصبعا واحدا //على المقذوف الحالي void UpdateTouchStart() { Projectile currentProj = launcher.currentProjectile; if (currentProj == null) { //لا يوجد ما يمكن فعله return; } if (Input.touchCount == 1) { Touch playerTouch = Input.touches[0]; if (playerTouch.phase == TouchPhase.Began) { //قام اللاعب للتو بلمس الشاشة // تحقق من كون عملية اللمس تمت داخل حدود المقذوف Vector2 touchPos = Camera.main.ScreenToWorldPoint(playerTouch.position); //استخرج مكوّن التصادم الخاص بالمقذوف الحالي Collider2D projectileCollider = currentProj.GetComponent<Collider2D>(); if (projectileCollider.bounds.Contains(touchPos)) { //تمت عملية اللمس داخل حدود المقذوف بالتالي يجب إمساكه launcher.HoldProjectile(); } } } } //تقوم بالتحقق من تحريك اللاعب لإصبعه على الشاشة أثناء إمساك المقذوف void UpdateDragging() { if (Input.touchCount == 1) { Vector2 touchWorldPos = Camera.main.ScreenToWorldPoint(Input.touches[0].position); launcher.DragProjectile(touchWorldPos); } } //تتحقق من رفع اللاعب إصبعه عن الشاشة void UpdateRelease() { if (Input.touchCount == 1) { Touch playerTouch = Input.touches[0]; if (playerTouch.phase == TouchPhase.Ended || playerTouch.phase == TouchPhase.Canceled) { launcher.ReleaseProjectile(); } } } }
ينفذ هذا البريمج ثلاث خطوات في كل عملية تحديث:
- الخطوة الأولى عبر الدّالة ()UpdateTouchStart والتي تتحقق من كون اللاعب قد لمس الشاشة للتو مستخدما إصبعا واحدا. في هذه الحالة تقوم الدّالة بحساب موقع اللمسة في فضاء المشهد ومن ثم فحص ما إذا كان هذا الموقع ضمن حدود مكوّن التصادم الخاص بالمقذوف الحالي المتواجد على مقلاع الإطلاق. إذا كانت هذه اللمسة فعلا داخل حدود المقذوف فهذا يعني أن اللاعب يريد إمساكه، وبالتالي تقوم الدّالة بإمساك المقذوف عبر استدعاء ()HoldProjectile من البريمج Launcher.
- الخطوة الثانية التي تنفذ عبر ()UpdateDragging تتعلق بوجود إصبع واحد على الشاشة، حيث تقوم بحساب موقع الإصبع في فضاء المشهد وتطلب من البريمج Launcher تحريك المقذوف الحالي إلى هذا الموقع. تذكر أن الأمور الأخرى مثل التأكد من وجود مقذوف أو إذا ما كان ممسوكا أو تم إطلاقه تتم من خلال البريمج Launcher نفسه بالتالي لا نحتاج لفحصها هنا، ولعلك تذكر أننا لم نفحصها أيضا في بريمج قراءة مدخلات الفأرة.
- أخيرا تقوم الدّالة ()UpdateRelease بالتحقق من كون اللاعب قد رفع إصبعا واحدا عن الشاشة، وذلك من خلال فحص مرحلة اللمس playerTouch.phase إذا ما كانت تساوي Ended أو Canceled وهما الحالتان المتوقعتان حال رفع الإصبع. عند التأكد من هذا الأمر تقوم باستدعاء دالّة إطلاق المقذوف ()ReleaseProjectile.
البريمج الأخير الذي يقرأ مدخلات شاشة اللمس هو البريمج الخاص بتنفيذ الهجوم الخاص للمقذوف بعد إطلاقه. هذا الهجوم يتم عبر لمس الشاشة مرة واحدة في أي موقع بعد إطلاق المقذوف. هذا البريمج يسمى SpecialAttackTouchInput ويجب أن تتم إضافته لجميع قوالب المقذوفات التي أنشأناها. هذا البريمج موضح في السرد التالي:
using UnityEngine; using System.Collections; public class SpecialAttackTouchInput : MonoBehaviour { //تستدعى مرة واحدة عند بداية التشغيل void Start () { //قم بتدمير بريمج قراءة مدخلات الفأرة في حال اكتشاف نظام تشغيل الهاتف if (Application.platform == RuntimePlatform.Android) { SpecialAttackMouseInput mouseInput = GetComponent<SpecialAttackMouseInput>(); Destroy(mouseInput); } } //تستدعى مرة واحدة عند تصيير كل إطار void Update () { if (Input.touchCount == 1) { Touch playerTouch = Input.touches[0]; if(playerTouch.phase == TouchPhase.Began) { SendMessage("PerformSpecialAttack"); } } } }
يمكنك ملاحظة مدى بساطة هذا البريمج، حيث أن كل ما يفعله هو إرسال الرسالة PerformSpecialAttack في حال قام اللاعب بلمس الشاشة مرة واحدة.
بهذا تكون اللعبة مكتملة لأجهزة الحاسب والهواتف الذكية، وبقي علينا تصديرها على شكل تطبيق مستقل.
تصدير اللعبة بعد إكمالها
يوفر محرك Unity إمكانية تصدير الألعاب على مجموعة كبيرة من المنصات، وما يهمنا منها الآن هو منصتا الحاسب الشخصي والهواتف الذكية وتحديدا الأجهزة التي تعمل بنظام Android. تصدير اللعبة يتم عن طريق النافذة التي حددنا منها ترتيب المشاهد وهي:
File > Build Settings
بالنسبة للتصدير للحواسيب وأجهزة Mac اختر من القائمة اليسرى PC, Max & Linux Standalone لتظهر أمامك الخيارات التالية:
أهم هذه الخيارات هو Target Platform والذي تحدد من خلاله نظام التشغيل المستهدف وخيار Architecture والذي يحدد نوع النظام التشغيل سواء كان 32 أو 64 بت. من المهم الانتباه إلى أن الخيار x86_64 يجعل اللعبة تعمل فقط على أنظمة 64 بت حيث أن وجود x86 في الاسم قد يضلل أحيانا فتظن أنه يشمل النظامين. إن كنت تستهدف النظامين 32 و 64 فابق على الخيار الأول x86. بعد ذلك يمكنك الضغط على Build ثم تختار مكان التخزين لتبدأ بعدها عملية التصدير.
بالنسبة للتصدير للهواتف الذكية الموضوع أصعب قليلا. سأتحدث هنا بالتفصيل عن طريقة التصدير لمنصة Android فقط لأنني للأسف لا أملك خبرة في التعامل مع أنظمة iOS. قبل التصدير لهذا النظام علينا القيام بتجهيز بعض الأدوات. الخطوة الأولى هو تحميل وتثبيت Android SDK وستحتاج هنا للإصدار الخامس حيث أن الإصدار الخامس من محرك Unity يحتاج لهذه النسخة. يمكنك تحميل المكتبات اللازمة من هذا الرابط https://developer.android.com/sdk/index.html#Other. عند التثبيت يفضل أن تختار موقعا سهلا مثل d:\AndroidSDK.
بعد تثبيت Android SDK على جهازك عليك أن تقوم بتنزيل مكتبة واحدة على الأقل عن طريق تشغيل SDK Manager وهو التطبيق المصاحب لـ Android SDK. كل ما تحتاج إليه هنا هو اختيار SDK Platform للإصدار الأخير (حتى كتابة هذه الدروس الإصدار الأخير هو API22 5.1.1). وذلك موضح في الصورة التالة:
بعد تنزيل وتثبيت المكتبة علينا أن نخبر Unity عن موقعها وذلك عبر الخيار:
Edit > Preferences
ومن ثم اختيار External Tools من القائمة اليسرى. بعدها يمكنك تحديد المجلد الذي قمت بتثبيت Android SDK فيه عن طريق الخيار Android SDK Location كما ترى هنا:
بما أن اللعبة ستعمل على شاشة الهاتف، يجب علينا أن نجبر النظام على تشغيلها في الوضع الأفقي وليس العمودي. هذا الخيار يمكن ضبطه من خلال القائمة:
Edit > Project Settings > Player
ومن ثم اختيار رمز نظام Android وفتح مجموعة الإعدادات المسماة Resolution and Presentation. بعدها يجب ضبط Default Orientation على الخيار Landscape Left كما هو موضح في الصورة التالية:
الخطوة الأخيرة قبل التصدير هي تحديد معرف الحزمة، وهو عبارة عن اسم على شكل عنوان موقع إنترنت معكوس يبدأ بعنوان موقع الناشر وينتهي باسم المنتج، مثل com.Hsoub.Academy.AngryAnimals. قد تبدو هذه الطريقة في التعريف غريبة بعض الشيء، لكنها موروثة من طريقة تعريف الحزم البرمجية بلغة Java والتي تعتبر اللغة الرئيسية لبرمجة التطبيقات على نظام Android. لا يشترط طبعا أن يكون موقع الإنترنت المستخدم حقيقيا، لكن المهم هو أن تتبع هذا التنسيق في تعريف الحزمة. لإضافة معرّف الحزمة ادخل إلى القائمة:
Edit > Project Settings > Player
ومن ثم اختر رمز نظام Android من نافذة الخصائص وافتح مجموعة الإعدادات المسماة Other Settings كما هو موضح في الصورة التالية:
يمكنك بعدها كتابة معرّف الحزمة في الخانة PlayerSettings.bundleIdentifier كما يمكنك تحديد الإصدار الأدنى الذي يدعمه التطبيق. يستحسن أن تضبط التوافق على الإصدار 4.0 من النظام حيث أنه لا زال شائعا في معظم الأجهزة المتوفرة.
بعدها يمكنك أخيرا العودة لشاشة التصدير:
File > Build Settings
واختيار Android من القائمة اليسرى ومن ثم الضغط على Build واختيار موقع التصدير. سيكون الملف الناتج من نوع APK والذي يمكنك نسخه لجهازك المحمول وتنزيله وتشغيله. قد تضطر لتغيير إعدادات الجهاز حتى يسمح بتنزيل تطبيقات مجهولة المصدر (أي من مصدر غير متجر Google Play) وهذه الإعدادات تختلف من جهاز لآخر. بالنسبة لأجهزة Samsung ستجد هذا الخيار في أعدادات الحماية تحت بند إدارة الجهاز كما هو موضح هنا:
تهانينا! لقد قمت ببناء وتصدير لعبة كاملة على هاتفك الذكي أو جهازك اللوحي، ويمكنك الاستمتاع بلعبها ومشاركتها مع أصدقائك.
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.