انتقل إلى المحتوى

تكامل بنية الكاميرا من شخصية ثالثة (الجزء العلوي)

أريد إنشاء كاميرا بزاوية ثالثة في Unity، يكون سلوك الكاميرا مستندًا إلى كاميرا الزاوية الثالثة في "عالم ووركرافت"، والمتطلبات المحددة هي:

يتم التحكم في تدوير الكاميرا حول الشخصية باستخدام زر الفأرة الأيسر، دون تدوير الشخصية. انقر بزر الماوس الأيمن: للتحكم في دوران الكاميرا حول الشخصية، حيث يتم دوران اتجاه الشخصية الأمامي (transform.forward في Unity) بشكل مقابل، دون تغيير في اتجاه الشخصية العلوي. 3. بعد دوران زر الماوس الأيسر، عند الدوران بالزر الأيمن، يقوم اتجاه الشخصية الأمامي بالتAdjust حسب دوران الزر الأيسر، ثم حسب دوران الزر الأيمن، في هذه الحالة يعادل الأمر دوران الزر الأيمن مرتين. 4. عجلة الماوس: التحكم في بُعد الكاميرا الكاميرا لا يمكن أن تخترق أي جسم صلب 6. الكاميرا تعود ببطء إلى المسافة الأصلية بعد الابتعاد عن جسم صلب تصادم. إذا اصطدمت الكاميرا بجسم، يجب استخدام عجلة الماوس لتقريب الكاميرا، ويجب أن تستجيب الكاميرا على الفور، وبعد ذلك لا تحدث النقطة السادسة مرة أخرى. الكاميرا صادفت الأرض أثناء الدوران، وتوقفت عن التدوير حول الشخص باتجاهيه العموديين لتدور حول نفسها باتجاهيه، ولا زالت تدور يمينًا ويسارًا حول الشخص.

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

موضع الكاميرا

在正式解决相机操作前,还有一个问题需要解决:相机位置的表示。这可以用多种方式:

قبل正式 حل مشكلة تشغيل الكاميرا، هناك مسألة أخرى بحاجة إلى حل: تمثيل موقع الكاميرا. يمكن القيام بذلك بعدة طرق:

  • إحداثيات العالم للكاميرا
  • الكاميرا بالنسبة لإحداثيات الشخص اتجاه الكاميرا والمسافة في نظام إحداثيات الشخصية

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

float curDistance = 5F;

تدوير الكاميرا

继续细分相机旋转的行为,可以分成左键旋转和右键旋转,下面我们来一步一步地完成这两个旋转。首先我把相机设为人物的子物体(children),这样人物的一些基本的移动相机都会自动的跟踪。

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

دورة بالزر الأيسر

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

في Unity، للحصول على حالة زر الفأرة الأيسر، نستخدم العبارة: Input.GetMouseButton(0) (ملحوظة: الأماكن التي تتضمن التعليمات البرمجية لاحقًا ستكون باستخدام C#). من الواضح أن الزر الأيمن هو Input.GetMouseButton(1). للحصول على معلومات حول موضع حركة مؤشر الفأرة (يمكن فهمها على أنها مقدار الإزاحة في محور X-Y بين الإطارات) هي: Input.GetAxis("Mouse X"); Input.GetAxis("Mouse Y"). لذا يمكننا أن نبدأ بالحصول على معلومات حركة المؤشر بعد الضغط على زر الفأرة الأيسر:

if (Input.GetMouseButton(0))
{
    float x = Input.GetAxis("Mouse X");
    float y = Input.GetAxis("Mouse Y");
}

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

另外还有一点要注意的是,四元数旋转轴只是一个向量,以原点为出发点,如果要以世界坐标系中的某点O为原点,以该点为出发点的向量V为旋转轴,就需要进行坐标系的变换,简单地说,就是把需要旋转的点P变换到,以O为原点的坐标系中,根据V旋转,再变换会世界坐标系。根据这些操作,可以写出一个功能函数:

هناك نقطة أخرى يجب الانتباه لها، وهي أن محور دوران الأعداد الرباعية هو مجرد متجه يبدأ من نقطة الأصل. إذا أردنا استخدام نقطة معينة O في نظام الإحداثيات العالمي كنقطة انطلاق، والمتجه V الذي يبدأ من تلك النقطة كمحور دوران، يجب إجراء تحويل لنظام الإحداثيات. ببساطة، يعني ذلك تحويل النقطة التي نريد تدويرها P إلى نظام الإحداثيات الذي تكون فيه O نقطة الأصل، ثم تدويرها وفقًا لـ V، وأخيرًا تحويلها مرة أخرى إلى نظام الإحداثيات العالمي. بناءً على هذه العمليات، يمكننا كتابة دالة وظيفية:

Vector3 MyRotate(Vector3 oldPosition, float angle, Vector3 axis, Vector3 axisPosition)
{
قم بإنشاء رباعي يأخذ محور الدوران اعتبارًا، وهذا الدوران يكون في نطاق نظام إحداثيات الشخصية.
    Quaternion rotation = Quaternion.AngleAxis(angle, axis);
// هنا ما نقوم به هو تحويل نظام الإحداثيات، حيث يتم تحويل إحداثيات الكاميرا من نظام الإحداثيات العالمي إلى نظام إحداثيات الشخص.
    Vector3 offset = oldPosition - axisPosition;
// احسب الدوران وقم بتحويله مرة أخرى إلى نظام الإحداثيات العالمي
    return axisPosition + (rotation * offset);
}
"Quaternion" هو نوع في Unity يُستخدم لتمثيل الرباعيات، بمجرد إضافة الكشف عن زر الفأرة الأيسر السابق، يمكنك التحكم بدوران الكاميرا يمينا ويسارا بالضغط على الزر الأيسر.

يمكن إعطاء الكود الذي يتحكم في دوران الكاميرا لليسار واليمين بتحريك الفأرة إلى اليسار واليمين مباشرة:

newForward = MyRotate(newForward, x, up, Vector3.zero);
لأن هنا يتم فقط تدوير المتجه الأمامي، دون التطرق إلى تحويل نظام الإحداثيات، فإن المعامل الرابع هو Vector3.zero.

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

newForward = MyRotate(newForward, -y, transform.right, Vector3.zero);

الرجاء وضع نص صالح

قم بعمل دوران باستخدام الزر الأيسر، سيكون دوران الزر الأيمن سهلًا جدًا، كل ما عليك هو ضبط اتجاه الشخص للأمام أثناء دوران يسارًا أو يمينًا:

player.forward = Vector3.Normalize(new Vector3(oldForward.x, 0, oldForward.z));

دوران لأعلى ولأسفل بنفس كود الزر الأيسر.

انقر أولاً بالزر الأيسر، ثم بالزر الأيمن

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

player.forward = Vector3.Normalize(new Vector3(oldForward.x, 0, oldForward.z));

الزوايا الأويلرية لقفل التوجيه العالمي

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

if ((Vector3.Dot(transform.forward, transform.parent.up) >= -0.95F || y > 0) &&
    (Vector3.Dot(transform.forward, transform.parent.up) <= 0.95F || y < 0))

الكود الكامل

// rotate oldPosition around a axis starting at axisPosition
Vector3 MyRotate(Vector3 oldPosition, float angle, Vector3 axis, Vector3 axisPosition)
{
    Quaternion rotation = Quaternion.AngleAxis(angle, axis);
    Vector3 offset = oldPosition - axisPosition;
    return axisPosition + (rotation * offset);
}

// rotate oldForward, player forward may change when use mouse RB
Vector3 RotateIt(Vector3 oldForward, Vector3 up, Vector3 right, Transform player)
{
    Vector3 newForward = -oldForward;
    // mouse LB RB rotate camera and character
    if (Input.GetMouseButton(0) ^ Input.GetMouseButton(1))
    {
        float x = Input.GetAxis("Mouse X") * rotateSpeed;
        float y = Input.GetAxis("Mouse Y") * rotateSpeed;

        if (x != 0F)
        {
            newForward = MyRotate(newForward, x, up, Vector3.zero);

            // mouse RB, character rotate together
            if (Input.GetMouseButton(1))
            {
                player.forward = Vector3.Normalize(new Vector3(oldForward.x, 0, 
                    oldForward.z));
            }
        }

        if (y != 0F)
        {

            if ((Vector3.Dot(transform.forward, up) >= -0.95F || y > 0)
                && (Vector3.Dot(transform.forward, up) <= 0.95F || y < 0))
            {
                newForward = MyRotate(newForward, -y, transform.right, Vector3.zero);

            }
        }
    }

    return -newForward;
}

Original: https://wiki.disenone.site/ar

This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.

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