#2 تاثير كشف التسلل من لعبة Assassin’s Creed Unity

unity

تحدثنا في درس سابق عن احد التاثيرات في واحده من اشهر ايقونات عالم التخفي، وهي سلسلة العاب اساسنز كريد  “Assassin’s Creed” وتحديدا التأثير الذي اسميه كشف التسلل.

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

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

#1 تاثير كشف التسلل من لعبة Assassin’s Creed Unity

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

عمل الشيدر للنسخة الفراغية

ستبدا الان في انشاء الشيدر، الذي ستقوم بإلحاقه بالنسخة الفراغية، وسيكون من نوع Unlit عن طريق قائمة Create >> Shader Graph >> URP >> Unlit Shader Graph.

ثم من اعدادات الشيدر تقوم بتغيير نوع السطح – Surface Type الي الشفاف – Transparent.

وتحتاج الآن لإضافة عقدة تأثير الفرنيل – Fresnel Effect، وعمل متغيرين احدهما من نوع الرقم العشري – Float، ليكون مسؤولا عن قوة الفرنيل، مع جعل قيمته الافتراضية 1، والآخر من نوع اللون – Color مع جعله HDR.

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

بعد ذلك تقوم بتوصيل المتغير الخاص بقوة التأثير في خانة القوة الاسية – Power، ومن ثم تقوم بضرب عقده الفرنيل مع متغير اللون، ثم توصيلهم في مخرج اللون الأساسي – Base Color.

ولإضافة الشفافية تقوم بتوصيل عقدة الفرنيل مباشرة بمخرج قناة الشفافية – Alpha.

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

وما تبقى لعمله في الشيدر، الآن هو اضافة شيء يسمح بجعله يتلاشى تدريجيا، وسيكون هذا عن طريق متغير جديد من نوع الرقم العشري، مع تغيير نوعه الي منظم – Slider، وجعل القيمة الافتراضية و العظمى والصغرى 1، 1، 0 على الترتيب.

بعد ذلك تقوم باضافة عقدة الاستيفاء – Lerp، مع جعل قيمتها الاولي 0، وتوصيل عقدة الفرنيل في الثانية، ثم توصيل المتغير المسؤول عن التلاشي في مدخل الزمن – T، وتوصيل ناتج عقدة الاستيفاء في مخرج الشفافية.

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

تلاشي النسخة تلقائيا

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

public void GenerateBSobject(){
  AppearRating = AppearRate;
  GameObject Obj = new GameObject();
  
  Obj.transform.SetPositionAndRotation(transform.position،transform.rotation);
  
  MeshRenderer MeshR = Obj.AddComponent<MeshRenderer>();
  MeshFilter MeshF = Obj.AddComponent<MeshFilter>(); 
  //السطر المسئول عن إضافة النص البرمجي الجديد
  //AppearFading : اسم النص البرمجي الجديد
  Obj.AddComponent<AppearFading>();
  
  Mesh BSmesh = new Mesh();
  SMR.BakeMesh(BSmesh);
  
  MeshF.mesh = BSmesh;
  MeshR.material = BSMat;
  
  Destroy (Obj،5);
  }

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

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

واخيرا تحتاج متغيرا للخامه – Material الحامله الشيدر، حتي تستطيع التحكم بقيمة متغير التلاشي بداخلها، أي 5 متغيرات.

public class AppearFading : MonoBehaviour{
    // متغير الخامة
    private Material CurrentMat;
    // متغير التلاشي
    private float Fading=1;
    // متغير سرعة التلاشي
    public float FadingSpeed=3;
    // متغير وقت الانتظار والعداد الخاص به
    public float WaitTime = 4;
    private float WaitTimer;
   }

لاحظ هنا اني جعلت بعض المتغيرات من نوع العام – Public، وهذا لكي تتمكن استدعائها من النص البرمجي الاول، لانه كما ذكرت، هذا النص البرمجي – الثاني – لن تقوم بإلحاقه يدويا، والذي لن يسمح لك بتعديل قيم المتغيرات منه.

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

 بالاضافة لجعل قيمة متغير التلاشي – Fading يساوي الصفر، لكي تظهر النسخة تماما، حيث في هذا النص البرمجي، 0 تعني انها ظاهره و 1 تعني انها مختفيه، ثم تقوم بجعل مؤقت الانتظار يساوي وقت الانتظار.

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

    void Start(){
        CurrentMat = this.GetComponent<MeshRenderer>().material;
        Fading = 0;
        WaitTimer = WaitTime;
        // تعطيل إسقاط الظلال
        GetComponent<MeshRenderer>().shadowCastingMode= ShadowCastingMode.Off;
    }

عداد مؤقت الانتظار

الان في دالة التحديث – Update تقوم اولا بتشغيل مؤقت الانتظار ، ويكون هذا عن طريق التحقق من كونه أكبر من صفر أم لا، وفي حالة تحقق الشرط يبدأ بالنقصان حتي يصل للصفر.

وما يجعله أكبر من الصفر هو السطر الذي اضفته في دالة البدء، عندما جعلت المؤقت يساوي وقت الانتظار، وبهذه الطريقة كلما جعلت المتغير أكبر، كلما استغرق المؤقت وقتا أكبر ليصل للصفر.

وقت التلاشي

بعد ذلك تقوم باضافة سطر آخر لزيادة قيمة التلاشي – لتبدأ النسخة بالتلاشي – لحظة وصول مؤقت الانتظار للصفر، وفي نفس الوقت تتحقق من كون قيمة متغير التلاشي اقل من الـ 1، فكونها 1 يعني تلاشي النسخة تماما.

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

لكن حتى الآن ما يتغير هي قيم المتغيرات نفسها في النص البرمجي، وليست قيمة متغير التلاشي في الخامة.

تفعيل العدادات في الشيدر 

ولذلك تعين قيمتها الآن، لتساوي معادلة استيفاء – lerp بين 1 و 0، والزمن يساوي قيمة متغير التلاشي.

و لنتوقف قليلا هنا لنعرف ماذا حدث، في البدايه قمت باضافة متغير تلاشي في الشيدر بمدى من 0 إلى 1، حيث 0 تجعله متلاشي تماما، و 1 تجعله ظاهرا تماما.

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

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

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

void Update(){
// السطر المسئول عن بدأ عداد وقت الانتظار 
if(WaitTimer >0){WaitTimer-=Time.deltaTime; }

// السطر المسئول عن بدء تغير قيمة التلاشي لحظة وصول عداد وقت الانتظار للصفر
if(Fading<1 && WaitTimer<=0){Fading+=Time.deltaTime*FadingSpeed;}
    
// السطر المسئول عن جعل قيمة التلاشي في الخامة تساوي معادلة الاستيفاء
// *لاحظ انه يجب ان يكون ما بين علامتي "" مطابقةً لخانة الـ ريفرينس في متغير الشيدر
CurrentMat.SetFloat("_Fading"،Mathf.Lerp(1،0،Fading));
}

إضافة القدرة للتحكم بالمتغيرات

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

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

public SkinnedMeshRenderer SMR;
public Material BSMat;
public float FadingSpeed;
public float WaitTime;
public float AppearRate = 2;
private float AppearRating;
public Animator Anim;
public void GenerateBSobject(){

AppearRating = AppearRate;

GameObject Obj = new GameObject();
Obj.transform.SetPositionAndRotation(transform.position،transform.rotation);

MeshRenderer MeshR = Obj.AddComponent<MeshRenderer>();
MeshFilter MeshF = Obj.AddComponent<MeshFilter>();
Obj.AddComponent<AppearFading>();

Obj.GetComponent<AppearFading>().FadingSpeed = FadingSpeed;
Obj.GetComponent<AppearFading>().WaitTime = WaitTime;

Mesh BSmesh = new Mesh();
SMR.BakeMesh(BSmesh);

MeshF.mesh = BSmesh;
MeshR.material = BSMat;

Destroy (Obj،5);
}

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

0 0 votes
Article Rating
Subscribe
نبّهني عن
guest

0 تعليقات
Inline Feedbacks
View all comments