موضوع: [Game Math] استخدام الجتا / الجا(Cos/Sin)

ردود: 7 | زيارات: 4619
  1. #1

    [Game Math] استخدام الجتا / الجا(Cos/Sin)


    السلام عليكم ^^




    الموضوع ببسطه بكونسبت أخف بإذن الله عشان يكون واضح للجميع وما يحتاج عصر دماغ XD



    أولاً نبدأ بإيش ممكن تفيدنا الجتا/الجا (Cos/Sin) في صناعة الالعاب

    الموضوع متعلق بشكل بحتي بالزوايا/ الدوائر / المنحنيات ، وبرياضات الألعاب المعقدة(مع إنها من جد مب معقدة لما تفهمها)



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

    الدرس بسيط وسهل ، وأتمنى يبين كيف الموضوع سهل ومب معقد مثل ما البعض معتقد ^^

    مع العلم الـCos/Sin لها استخدامات أخرى ، وهذي أبسط استخداماتها



    ندخل في الدرس xP



    تقدرون تشرحون لي الشكل التالي؟





    هاه؟ مين يعرف ؟ لا الشكل ما يشير لحمامة xD هذي زاوية (ما توقعتوا هاه XD )



    ندخل في الجد ^^

    الزاوية تتكون من ضلعين و"فتحة" بينهم

    لاحظوا الضلع الثابت (الأسود) والضلع الآخر المتحرك ( اللي لونته باللون الاحمر xP بتعرفون ليه الحين)



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

    وهذا بيفيدنا في إننا نحرك هذي الشخصية على حسب زاويتها ^^



    بالمناسبة ، الضلع الثابت عادة يرسم بس عشان يحدد مقدار فتحة الزاوية



    أحس إن شرحي معقد الأمور X"D على أي حال نكمل



    ناتج جتا الزاوية في الصورة هو احداثي (س/X ) النقطة الزرقاء ، وناتج جا الزاوية في الصورة هو احداثي (ص/Y ) النقطة الزرقاء في الصورة ، بإعتبار إن احداثي النقطة البنفسجية هو (0 ، 0)

    وبإعتبار إن المسافة بين النقطة الزرقاء هي 1 بكسل

    فهمتوا شئ؟ XD أنا نفسي مب فاهم اللي أكتبه

    عشان نوضح أكثر ، شوفوا الصورة التالية :





    هذا يشرح الجتا/جا بالطريقة التقليدية ^^ ..

    تخيلوا النقطة البنفسجية في إحداثي (0،0) يعني مكان نقطة الأصل

    في دائرة طول نصف قطرها 1 ، وهنا النقطة البنفسجية هي مركزها

    بالمناسبة ، هذا معناه إن المسافة بين مركز الدائرة ( النقطة البنفسجية) و أي نقطة على الدائرة ( أي نقطة زرقاء) هو 1 لكن هذا مب معناه إن لازم أي من الاحداثيين يكون 1 ، بس البعد بين المركز وأي نقطة على الدائرة = 1

    هنا ^^ لو قلنا جتا(Cos) الزاوية بيطلع احداثي X للنقطة الزرقاء اللي بيتلاقى فيها الضلع المتحرك مع الدائرة

    ولو قلنا جا (Sin ) الزاوية بيطلع احداثي Y للنقطة الزرقاء اللي بيتلاقى فيها الضلع المتحرك مع الدائرة

    يعني بإختصار ^^ بيكون إحداثي أي نقطة تقع "على" الدائرة هو (جتا الزاوية ، جا الزاوية )

    طيب نفترض الزاوية 90 ، يعني الخط طالع من المركز مباشرة على فوق xP .. من دون ما تستخدمون الآلة الحاسبة ، تقدرون تقولون كم جتا 90 وجا 90 في هاي الحالة (النقطة الزرقاء) ؟ ^^

    ببساطة بما إننا تحركنا لفوق ومب يمين ولا يسار بتكون جتا( 90 ) = 0 ،، جا ( 90 ) = 1 بما إنه خط طالع لفوق مباشرة ! ..يعني تحركنا 1 على الإحداثي الصادي (Y ) وما تحركنا شئ على الإحداثي السيني (X )

    طيب لو 0 ؟ ^^ بنكون تحركنا لليمين وما تحركنا شئ على إحداثي Y يعني جتا(0) = 1 و جا (0) =0



    طيب لو زاوية 45 ؟

    XD

    هذي بإذن الله (0.707 ، 0.707) ..بس طبعاً إحنا مالنا دخل =P بنسيب اللعبة تحسب الزوايا !



    في شئ نقدر نستنتجه ^^ .. دام الدائرة نصف قطرها =1 ..فهذا معناه إن الجا/الجتا بين الـ-1 والـ1 !

    يعني مستحيل دائرة نصف قطرها 1 وموجودة في نقطة الأصل وتكون نقطة تقاطع الضلع المتحرك معاها 2 مثلاً XD



    طيب كيف بنقدر نستفيد من هذا؟ ^^





    نرجع مرة ثانية لصورة الحمامة..أقصد الزاوية xD

    على اعتبار إن النقطة الزرقاء هنا تبعد عن النقطة البنفسجية 1 .. كيف نقدر نخلي النقطة البنفسجية تتحرك نحوها؟



    ببساطة نفس فكرة الدائرة اللي فوق ^^

    الاحداثي السيني للنقطة الزرقاء = الاحداثي السيني للنقطة البنفسجية + جتا(الزاوية)

    الاحداثي الصادي للنقطة الزرقاء = الاحداثي الصادي للنقطة البنفسجية + جا(الزاوية)



    في البرمجة بيصير الامر كالتالي ^^ :

    (على إعتبار إن blueX , blueY احداثيات النقطة الزرقاء..

    و purpleX , purpleY إحداثيات النقطة البنفسجية ...
    و angle الزاوية بين النقطتين)

    كود:
    blueX = purpleX+cos(angle);
    
    blueY = purpleY + sin(angle);
    ممتاز جداً ! >ن< الحين نقدر نحرك النقطة على حسب زاويتها ^_^ !!



    طيب بس ما تلاحظون إن السرعة بتكون بطيئة >_>" كيف نقدر نعدلها؟

    ببساطة ^^ .. بنقوم بضرب الجتا/الجا في السرعة اللي نبيها ^__^

    لو نبي ضعف السرعة(2) بنقوم بضربها في 2 ..لو نبي 3 بنقوم بضربها في 3 وهكذا..

    مثال :


    كود:
    
    Speed = 5;
    
    blueX = purpleX+cos(angle)*Speed;
    
    blueY=purpleY+sing(angle)*Speed;
      

    ببساطة بيتم مضاعفة المسافة ^^







    مثال أكثر استخداماً / تنظيماً للتحرك على حسب الزاوية :

    (في هذا المثال dX هي المسافة التي سيتم اضافتها على الاحداثي السيني ، و dY هي المسافة التي سيتم اضافتها على الاحداثي الصادي )

    كود:
    
    
    dX = cos(angle)*Speed;
    
    dY= sin(angle)*Speed;
    
    x=x+dX;
    
    y=y+dY;
    
    


    أتمنى يكون كل شئ واضح الحين ^_^ !

    المثال التالي كتبته بمحرك الـAllegro على السي++ .. بكتبه بإذن الله بعدين بالـXNA نظراً لوجود الكثير من مستخدمي الـXNA هنا ^^..

    --

    اعمل مشروع جديد ، وبعدها اعمل ملف هيدر جديد ..سميه اللي تبي xD بسميه gameBullet.h ، بما إن اللي ناوي أسويه قريب جداً من اللي يستخدموه في ألعاب الشوتر ^-^

    ببساطة بنحط فيه كلاس ، سميه اللي تبي xP بسميه gameBullet بعد عشان يكون على اسم الملف ^^ بس براحتك !




    كود:
    
    #include <allegro.h>
    
    class gameBullet
    
    {
    
      public:
    
       fixed  x , y;
    
       fixed  angle;
    
       gameBullet(fixed newX,fixed newY,fixed newAngle);
    
       void update();
    
    
    
    };
    
    


    الكلاس بيخزن معلومات عن احداثيات كل رصاصة ^^ وزاوية تحركها ، ايضاً بيحتوي كونستركتور (يتم استدعاؤه حينما يتم الإعلان عن الكائن) وفنكشن بسيطة بنسميها update بتتكفل بتحديث الرصاصة ^^ وإضافة الموقع الجديد عليها



    المهم ، لاحظوا هنا إننا استخدمنا نوع fixed ! نوع غريب للمتغيرات صح؟ متعودين عادة نستخدم انتيغر / فلوت (أعداد صحيحة / أعداد نسبية) .. السبب الرئيسي إن في الواقع في احداثيات على الشاشة مثلاً 2.5 ما لها معنى حقيقة xD رغم إن محركات ثانية تسمح بيها وتقرب لأقرب عدد صحيح لما تطلب منها الرسم في موقع غير صحيح ^^ ..لإن لما يكون عندنا شاشة مثلاً بعرض 850 بكسل ..فمعناها عندنا 850 بكسل صحيحة ومن المستحيل الرسم بينها لأنها أصغر وحدة !



    فهنا نوع fixed جاء مع الاليغرو عشان يهتم بالموضوع ^_^ .. بالإضافة لبعض الدوال الثانية للتحويل !





    نرجع لملف الـmain.cpp (لو ما غيرت اسمه xP ) فيه الكود المبدأي للأليغرو مثل وضع الـcolor depth و فتح الشاشة ، الخ



    المهم لا تنسى تضيف فوق جملة إنكلود لملف الهيدر ^^

    كود:
    #include "gameBullet.h"
    كود:
    
    
    


    انزل تحت ، بنعرف الكونستركتور اللي كتبنا الـprototype حقه في ملف الهيدر ^^ بيتم استدعائه في بداية تكوين الأوبجكت

    كود:
    
    
    gameBullet::gameBullet(fixed newX, fixed newY , fixed newAngle)
    
    {
    
     x = newX;
    
     y = newY;
    
     angle = newAngle;                          
    
    }
      



    ببساطة يتيح لك إنك تدخل القيم ، وبيقوم بمساواة القيم داخل الاوبجكت بالقيم اللي أدخلتها ^^



    بعدها عرف فنكشن الـupdate اللي كتبنا البروتوتايب حقها في الهيدر بعد ^_^
    كود:
    void gameBullet::update()
    
    {
    
     x += fcos(angle);
    
     y += fsin(angle);
    
    }
    ببساطة بيزود على الاحداثيات الحالية الجتا والجا للزاوية ^^



    لاحظ إننا استخدمنا fcos و fsin ، الدالتين بيتم استخدامهم مع نوع fixed ^^ وبيقوموا بإرجاع قيمة من نوع fixed


    كود:
    
    #include <vector>
    
    using namespace std;
    
    vector<gameBullet*> bullets;
    
    


    اطلع فوق مرة ثانية XD

    قوم بعمل إنكلود لهيدر الفيكتور

    اللي ما يعرف ايش هو الفيكتور ^^ فهو جزء من الـSTL ، ببساطة هو array او لائحة تقدر تضيف ليها كائنات من النوع اللي تختاره ^_^ وهذا بيسهل علينا كثير تطبيق نفس الشئ على عدد من الكائنات بوقت واحد !

    شئ ثاني ، مثل معظم مكتبات الـSTL لازم تستخدم using namespace std

    وفي السطر الثالث بنعمل فيكتور من نوع الكلاس اللي عملناه ..ونسمي الفيكتور بإسم bullets


    كود:
    
    BITMAP* buffer;
    
    


    وقوم بعمل بوينتر عشان بفر الشاشة ^^ ( بنقوم برسم كل شئ على هذا البفر ، بعدين في النهاية بنقوم برسم البفر على الشاشة ، وهذا أفضل للجرافكس كارد)



    إنزل تحت في بداية الـمين ^^ ..

    وضيف هذا السطر :

    كود:
    
    
    buffer = create_bitmap(screen->w , screen->h);
    
    


    ببساطة بنقوم بشغل مساحة البفر بصورة فارغة ^_^ .. عرضها عرض الشاشة وطولها طول الشاشة
    كود:
                for (int i=0; i != 10 ;i++)
    
                {
    
            bullets.push_back(new gameBullet(itofix(screen->w/2) , itofix(screen->h/2) ,itofix( i*30)));
    
        }
    بدأنا اللعب XDD

    بنستخدم جملة for عشان نكرر الكود 10 مرات ^_^ ..

    ببساطة بيقوم بإضافة 10 كائنات للفيكتور (بما إن شرط الخروج إن العداد I يساوي 10 xP )

    كل مرة في وسط الشاشة ^^ (العرض مقسوم على 2 ، والطول مقسوم على 2) ..

    وهنا دالة ثانية itofix لتحويل الرقم الناتج لـfixed ^^

    بالنسبة للقيمة الثالثة (الزاوية ) بتساوي قيمة العداد (0 إلى 10 ) * 30 .. عشان نتأكد إن الزاوية كبيرة بس ^^ وتكبر أكثر مع كل أوبجكت ^^



    اعثر على :
    كود:
                while (!key[KEY_ESC])
    
         {
    
    }
    وضع بداخلها ^^ :
    كود:
                for ( int i =0; i < bullets.size() ; i++ )
    
                            {
    
                bullets[i]->update();
    
                 textout_ex(buffer, font, "@",fixtoi( bullets[i]->x), fixtoi(bullets[i]->y)
    
                , makecol(255, 0, 0), -1);
    
            }
    ببساطة سنمر داخل كل عنصر في الفيكتور ونقوم باستدعاء فنكشن update

    ومن ثم سنقوم برسم حرف "@" مكانه XD يعني بدل السبرايت ^^ >>بما إن المثال ما فيه سبرايت
    كود:
    draw_sprite(screen,buffer,0,0); clear(buffer);

    قم برسم البفر على الشاشة ثم محوه للتجهيز للرسم عليه مجدداً ^^



    وأول ما تعمل كومبايل بإذن الله بتشوف هذي النتيجة ^^ :







    10 علامات "@" يتحركوا على شكل دائرة XD

    هذا ملف exe مع السورس ^^

    http://www.mediafire.com/?omkyuj3wm5jdjnw



    الواجب

    في محرككم المفضل اعملوا شخصية لما تضغط سهم أيمن أو أيسر تدور ولما تضغط للأمام أو للوراء تتحرك في إتجاه الزاوية أو عكسه ^_^


    ------------------------------------

    الاستخدام مع الثري دي :

    لن يختلف الأمر كثيرا ^^
    تذكروا فقط ، أننا في التودي الـY يشير للأمام/الخلف ^^ والـX لليمين/اليسار

    إذاً في الثري دي ^^ ، إذا كان الدوران حول الـY كما هو في الغالب ، فسيكون الـZ هو الذي يؤشر للأمام والـX كما هو ^_^
    إذاً في الثري دي .. الدوران سيكون حول الـY ..وسنستبدل الـY في التودي بالـZ في الثري دي ، حيث أن الـZ في الثري دي هو ما يشير للأمام ^^
    وهذه في حالة إن أردنا أن نحرك شخصية على الأرض على حسب زاويتها
    تحياتي،،


  2. #2
    عضو نشيط
    تاريخ التسجيل
    Mar 2006
    المنطقة
    Lake Oswego, Oregon, United States
    ردود
    474
    لم أقرأ النص بالكامل ولكن فعلاً علم المثلثات في برمجة الألعاب مهم جداً.

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

    فكان من الضروري الانتباه أن زاوية القوة المؤثرة على ذيل المروحية تتغير بدورانها شيئا فشيئا.

    وحل المشكلة كان بتطبيق قوتين تتغير قوتهما و زاويتهما باستعمال "Cos - Acos - Sin - Asin"

    فميزتها هي في القيمة التي تنتقل من ١ إلى -١ مروراً بالصفر ثم تعود القيمة بالصعود مرة أخرى على حسب الزاوية.

    ممتعة للغاية

  3. #3
    مشرف منبر Games Design
    صور رمزية general1
    تاريخ التسجيل
    Jun 2007
    ردود
    1,066
    ياااااااااااه

    انا كنت أنتظر في المنتدى مثل هذه المواضيع منذ الكثير من السنين و بالفعل نفس هذه المواضيع هي التي يتم وضعها في المنتديات الأجنبية

    و اتمنى ان تكون الاسئلة من نفس النوع

    على العموم موضوع رائع
    Egypt


    I Love Direct-X

    سبحان الله و بحمده سبحان الله العظيم
    اللهم صلي وسلم و بارك علي سيدنا محمد

  4. #4
    عضو نشيط
    صور رمزية ayoubsoft
    تاريخ التسجيل
    Jul 2007
    المنطقة
    Maroc
    ردود
    987
    موضوع مفيد مودي

    شكرآ و الظاهر الموضوع متعوب عليه

  5. #5
    عضو فعال
    تاريخ التسجيل
    Mar 2010
    المنطقة
    العراق
    ردود
    266
    مشكور عالشرح..

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

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

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



    function Update () {

    transform.RotateAround(Vector3(0,0,0) , Vector3.up * Input.GetAxis("Horizontal") , 30 * Time.deltaTime);


    transform.LookAt(Vector3(0,0,0));
    transform.position= transform.TransformPoint(Vector3.forward *Input.GetAxis("Vertical") * 0.1);


    }

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

    والكود في المرفقات نظراً لظهور النقاط في الكود

  6. #6
    ما شاء الله الشرح جميل
    وزى ما قال الاخ جنرال هى دى فعلا المواضيع اللى بتتشرح فى المنتديات الاجنبية

    جزاك الله خيرا

  7. #7
    عضو نشيط
    صور رمزية Virtools
    تاريخ التسجيل
    Jul 2008
    المنطقة
    الإمارات العربية المتحدة
    العمر
    27
    ردود
    702
    هذي المواضيع ولا بلااااش

    والله رووعة

    احسنت اخي العزيز

    قرأته واستفدت منه بشكل كبير

    شكرا لك
    Back to Game Developing

Bookmarks

قوانين الموضوعات

  • لا يمكنك اضافة موضوع جديد
  • لا يمكنك اضافة ردود
  • لا يمكنك اضافة مرفقات
  • لا يمكنك تعديل مشاركاتك
  •  
  • كود BB مفعّل
  • رموز الحالة مفعّل
  • كود [IMG] مفعّل
  • [VIDEO] code is مفعّل
  • كود HTML معطل