Advertisement
  1. Game Development
  2. Unity 3D

كيفية بناء نظام ترجيع الوقت مثل Prince-Of-Persia، الجزء 1

Scroll to top
Read Time: 9 min
This post is part of a series called How to Build a Prince-Of-Persia-Style Time-Rewind System.
How to Build a Prince-of-Persia-Style Time-Rewind System, Part 2

Arabic (العربية/عربي) translation by Ayman Amar (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

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

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

A demonstation of the basic functionalityA demonstation of the basic functionalityA demonstation of the basic functionality
تمثيل للوظائف الأساسية

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

مستعد؟ هيا بنا!

كيف تم استخدام هذا قبل؟

Prince of Persia: The Sands of Time واحدة من أولى الألعاب التي حقاً دمجت ميكانيكية ترجيع الوقت في اللعب. عندما تموت ليس عليك فقط إعادة اللعبة، ولكن يمكنك بدلاً من ذلك إرجاع اللعبة لبضع ثوان حيث كنت على قيد الحياة مرة أخرى، وفورا حاول مرة أخرى.

Prince of Persia The Forgotten Sands The Sands Of Time Trilogy integrates time-rewinding beautifully into its gameplay and avoids immersion-breaking quick-reloadingPrince of Persia The Forgotten Sands The Sands Of Time Trilogy integrates time-rewinding beautifully into its gameplay and avoids immersion-breaking quick-reloadingPrince of Persia The Forgotten Sands The Sands Of Time Trilogy integrates time-rewinding beautifully into its gameplay and avoids immersion-breaking quick-reloading
Prince of Persia: The Forgotten Sands. ثلاثية Sands Of Time تدمج ترجيع الوقت بشكل جميل في اللعب وتتجنب كسر الغمر وسريعة إعادة الشحن.

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

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

When you have a fatal crash in GRID you get the chance to rewind the game to a point before the crashWhen you have a fatal crash in GRID you get the chance to rewind the game to a point before the crashWhen you have a fatal crash in GRID you get the chance to rewind the game to a point before the crash
عندما يحصل لك حادث مميت في GRID تحصل على فرصة لإرجاع اللعبة للنقطة قبل الحادث.

استخدامات أخرى

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

الإعادة

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

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

SUPERHOT أيضا تسجل حركاتك. عند الانتهاء من اللعب حول التقدم المحرز الخاص بك بالكامل تتم إعادته، لتعرض بضع ثوان من الحركة الفعلية التي حدثت.

Super Meat Boy تستخدم هذا بطريقة ممتعة. عند الانتهاء من مستوى ستشاهد إعادة لجميع محاولاتك السابقة وضعت فوق بعضها البعض، تنتهي مع ركضتك النهائية إبتداءا باخر وقفة لليسار.

The end-of-level replay in Super Meat Boy Every one of your previous attempts is recorded and then played back at the same timeThe end-of-level replay in Super Meat Boy Every one of your previous attempts is recorded and then played back at the same timeThe end-of-level replay in Super Meat Boy Every one of your previous attempts is recorded and then played back at the same time
إعادة نهاية المستوى في Super Meat Boy. سجلت كل واحدة من المحاولاتك السابقة وبعد ذلك بدأت مرة أخرى في نفس الوقت.

Time-Trial Ghosts

Race-Ghosting هي تقنية حيث تسابق لأفضل وقت في مسار فارغ. ولكن في الوقت نفسه، تسابق ضد شبح، التي هي سيارة شبحية، وشفافة، التي تقود بالضبط على الطريقة التي تسابقت بها من قبل في محاولتك الأفضل. لا يمكنك أن تصطدم معها، مما يعني يمكنك التركيز للحصول على أفضل وقت.

بدلاً من القيادة وحيدا تحصل على منافسة ضد نفسك، الأمر الذي يجعل time-trials أكثر متعة. تظهر هذه الميزة في معظم العاب السباقات، من سلسلة Need for Speed إلى Diddy Kong Racing.

Racing a ghost in Trackmania Nations This one has the silver difficulty meaning I will get the silver medal if I beat them Note the overlap in car-models showing the ghost isnt corporeal and can be driven throughRacing a ghost in Trackmania Nations This one has the silver difficulty meaning I will get the silver medal if I beat them Note the overlap in car-models showing the ghost isnt corporeal and can be driven throughRacing a ghost in Trackmania Nations This one has the silver difficulty meaning I will get the silver medal if I beat them Note the overlap in car-models showing the ghost isnt corporeal and can be driven through
سباق شبح في Trackmania Nations. هذا لديه صعوبة فضية، مما يعني سوف أحصل على الميدالية الفضية إذا هزمتهم. ملاحظة التداخل في نماذج السيارات، يظهر الشبح ليس ماديا ويمكن القيادة من خلاله.

Multiplayer-Ghosts

Asynchronous Multiplayer-Ghosting هي طريقة أخرى لاستخدام هذا الإعداد. في هذه الميزة التي نادراً ما تستخدم، المباريات المتعددة اللاعبين تنجز عن طريق تسجيل بيانات لاعب واحد، ثم يرسل ركضه إلى لاعب آخر، الذي يمكن بعد ذلك العراك ضد أول لاعب. يتم تطبيق البيانات بنفس طريقة time-trial-ghost التي ستكون، إلا أنه يمكنك السباق ضد لاعب آخر.

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

تحرير الفيلم

توفر بعض الألعاب هذا من get-go لكن اتخدامها بحق يمكن أن يكون أداة ممتعة. تقدم Team Fortress 2 محرر إعادة مدمج، الذي يمكنك من إنشاء مقاطعك الخاصة.

The replay-editor from Team Fortress 2 Once recorded a match can be seen from any perspective not just the playersThe replay-editor from Team Fortress 2 Once recorded a match can be seen from any perspective not just the playersThe replay-editor from Team Fortress 2 Once recorded a match can be seen from any perspective not just the players
محرر الإعادة من Team Fortress 2. حين تسجل مباراة ما يمكن رأيته من أي منظور، ليس فقط الخاص باللاعب.

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

كيفية بنائه

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

اللاعب

أنشئ مكعب في المشهد الخاص بك، وسيكون هذا شخصيتنا اللاعبة. بعد ذلك أنشئ نص # C جديد وقم بتسميته Player.cs واضبط دالة Update() لتظهر على هذا الشكل:

1
void Update()
2
{
3
    transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
4
    transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
5
}

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

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

Angle the camera so it views the cube from aboveAngle the camera so it views the cube from aboveAngle the camera so it views the cube from above

جربها، ويجب أن تكون قادرا على تحريك المكعب الخاص بك باستخدام WSAD ومفاتيح الأسهم.

متحكم الوقت

الآن أنشئ نص C# جديد وقم بتسميته TimeController.cs وأضفه إلى GameObject فارغ جديد. هذا سيقوم بالتعامل مع التسجيل الفعلي والإرجاع اللاحق للالعبة.

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

1
public GameObject player;

وأدرج كائن اللاعب للفتحة الناتجة على TimeController، بحيث أنه يمكن الوصول إلى اللاعب والبيانات الخاصة به.

Assign the player-object to the resulting slot on the TimeControllerAssign the player-object to the resulting slot on the TimeControllerAssign the player-object to the resulting slot on the TimeController

ثم نحن بحاجة إلى إنشاء مصفوفة للاحتفاظ ببيانات اللاعب:

1
public ArrayList playerPositions;
2
3
void Start()
4
{
5
    playerPositions = new ArrayList();
6
}

الذي سوف نفعله تاليا هو تسجيل موقع اللاعب باستمرار. سيكون لدينا الموقع المخزن حيث كان اللاعب في الإطار الأخير، الموقع المخزن حيث كان اللاعب قبل 6 إطارات، والموقع المخزن حيث كان اللاعب قبل 8 ثواني (أو أي طول قمت بتعينه للتسجيل). عندما نضغط في وقت لاحق على زر سنقوم بالرجوع إلى الخلف من خلال مصفوفة المواقع الخاصة بنا وإدراجها إطارا بإطار، مما يؤدي إلى ميزة إرجاع الوقت.

أولاً، دعونا نحفظ البيانات:

1
void FixedUpdate()
2
{
3
    playerPositions.Add (player.transform.position);
4
}

في دالة FixedUpdate() نقوم بتسجيل البيانات. FixedUpdate() تستخدم لتعمل بشكل ثابت على 50 دورة في الثانية (أو أي شيء قمت بتعيينه عليه)، الذي يسمح لفصل زمني ثابت بتسجيل وتعيين البيانات. دالة Update() تعمل في الوقت نفسه اعتمادا على كم من الإطارات يقوم المعالج بإدارتها، مما سيجعل الأمور أكثر صعوبة.

الكود سوف يخزن موقع اللاعب لكل إطار في المصفوفة. الآن نحن بحاجة لتطبيقه!

سنقوم بإضافة تحقق لمعرفة إذا تم الضغط على زر الترجيع. من أجل هذا، نحن بحاجة إلى متغير boolean:

1
public bool isReversing = false;

ونحقق في دالة Update() لتعيينه وفقا لما إذا كنا نريد ترجيع اللعب:

1
void Update()
2
{
3
    if(Input.GetKey(KeyCode.Space))
4
    {
5
        isReversing = true;
6
    }
7
    else
8
    {
9
        isReversing = false;
10
    }
11
}

لتشغيل اللعبة إلى الخلف، سوف نطبق البيانات بدلاً من التسجيل. يجب أن تبدو التعليمات البرمجية الجديدة لتسجيل وتطبيق موقع اللاعب كما يلي:

1
void FixedUpdate()
2
{
3
    if(!isReversing)
4
    {
5
        playerPositions.Add (player.transform.position);
6
    }
7
    else
8
    {
9
        player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
10
        playerPositions.RemoveAt(playerPositions.Count - 1);
11
    }
12
}

والنص البرمجي TimeController بالكامل هكذا:

1
using UnityEngine;
2
using System.Collections;
3
4
public class TimeController: MonoBehaviour
5
{
6
    public GameObject player;
7
    public ArrayList playerPositions;
8
    public bool isReversing = false;
9
10
    void Start()
11
    {
12
        playerPositions = new ArrayList();
13
    }
14
15
    void Update()
16
    {
17
        if(Input.GetKey(KeyCode.Space))
18
        {
19
            isReversing = true;
20
        }
21
        else
22
        {
23
            isReversing = false;
24
        }
25
    }
26
  
27
    void FixedUpdate()
28
    {
29
        if(!isReversing)
30
        {
31
            playerPositions.Add (player.transform.position);
32
        }
33
        else
34
        {
35
            player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
36
            playerPositions.RemoveAt(playerPositions.Count - 1);
37
        }
38
    }
39
}

أيضا، لا تنسى إضافة التحقق إلى كلاس player لمعرفة ما إذا كان TimeController حاليا يقوم بالترجيع أم لا، والتحرك فقط عندما لا يكون قيد العكس. وبخلاف ذلك، قد تنشئ سلوك غير سار:

1
using UnityEngine;
2
using System.Collections;
3
4
public class Player: MonoBehaviour
5
{
6
    private TimeController timeController;
7
8
    void Start()
9
    {
10
        timeController = FindObjectOfType(typeof(TimeController)) as TimeController;
11
    }
12
    
13
    void Update()
14
    {
15
        if(!timeController.isReversing)
16
        {
17
    	    transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
18
    	    transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
19
        }
20
    }
21
}

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

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

ولكن انتظر، لماذا لاعبنا المكعب البسيط يبحث عن الإتجاه الأخير الذي تركناه فيه؟ لأننا لم نقم بفعل ذلك لتسجيل دورانه أيضا!

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

1
using UnityEngine;
2
using System.Collections;
3
4
public class TimeController: MonoBehaviour
5
{
6
    public GameObject player;
7
    public ArrayList playerPositions;
8
    public ArrayList playerRotations;
9
    public bool isReversing = false;
10
    
11
    void Start()
12
    {
13
        playerPositions = new ArrayList();
14
        playerRotations = new ArrayList();
15
    }
16
    
17
    void Update()
18
    {
19
        if(Input.GetKey(KeyCode.Space))
20
        {
21
            isReversing = true;
22
        }
23
        else
24
        {
25
            isReversing = false;
26
        }
27
    }
28
    
29
    void FixedUpdate()
30
    {
31
        if(!isReversing)
32
        {
33
            playerPositions.Add (player.transform.position);
34
            playerRotations.Add (player.transform.localEulerAngles);
35
        }
36
        else
37
        {
38
            player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
39
            playerPositions.RemoveAt(playerPositions.Count - 1);
40
    
41
            player.transform.localEulerAngles = (Vector3) playerRotations[playerRotations.Count - 1];
42
            playerRotations.RemoveAt(playerRotations.Count - 1);
43
        }
44
    }
45
}

قم بتجربتها! TimeRewindingFunctionality02 هو النسخة المحسنة. الآن لدينا اللاعب المكعب يمكن الانتقال بالخلف في الوقت المناسب، وسوف ننظر بنفس الطريقة التي فعلت عندما كان في تلك اللحظة.

خاتمة

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

هنا ما علينا القيام به:

  • فقط تسجيل كل ~ 12 الإطار والاقتحام بين تلك المسجلة للحفاظ على تحميل البيانات الضخمة
  • فقط تسجيل المواقع ~ 75 الأخيرة للاعب والدوران للتأكد من أن لا تصبح المصفوفة غير عملية جداً واللعبة لا يحدث لها إنهيار

كما أننا سوف نلقي نظرة على كيفية توسيع هذا النظام السابق فقط شخصية اللاعب:

  • سجل أكثر من مجرد اللاعب
  • إضافة تأثير للدلالة على أن الترجيع يحدث (مثل تمويه VHS)
  • استخدام كلاس مخصص لحفظ موقع ودوران اللاعب بدلاً من المصفوفات

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.