SpriteKit Advanced - كيفية بناء لعبة 2،5D (الجزء الثاني)

مقدمة

يوضح هذا المقال كيفية كتابة تظليل أساسي في SpriteKit. إنه منقسم إلى قسمين: أولاً نلعب ، ثم نتعلم.

يحتوي أيضًا على معلومات حول كيفية استخدام فئات SKAttribute و SKAttributeValue التي تمت إضافتها في iOS SDK 10.0.

إذا لم تكن قد قرأت ذلك بالفعل ، فإليك الجزء 1 من سلسلة المقالات هذه.

تحضير المشروع

هيا بنا نتسرع وقذرة.

  • افتح XCode 8 وقم بإنشاء مشروع جديد من القالب: iOS> Game.
  • افتح GameScene.sks وقم بإزالة الملصق في وسط الشاشة.
  • قم بتنزيل هذا ووضعه داخل Assets.xcassets
  • سمها "الأشجار"
  • افتح GameScene.m
  • إزالة جميع المتغيرات المثيل
  • إزالة جميع الطرق

شظية شادر

الآن نقوم بإنشاء تظليل جزء فارغ في XCode:

  • في Project Navigator ، حدد Supporting Files
  • اختر: ملف> جديد> ملف ...
  • اختر: أخرى> فارغة
  • قم بتسمية "myShader.fsh" واضغط على إنشاء.
  • ضع هذا في الداخل:
/ / حاليًا تمريرة مفتاح تظليل ممل رئيسي (باطل) {vec4 colour = texture2D (utexture، vtexcoord) ؛ // هنا سيظهر شيء يستحق glFragColor = color؛}

أعلاه شادر شادر لا يفعل شيئا ملموسا. شرح سريع:

  • الفراغ الرئيسي ()
     يتم استدعاء هذه الوظيفة لكل بكسل من العفريت ولون المخرجات لهذا البيكسل
    الحصول على بيانات الإدخال من globals المحيطة ويجب تعيين المتغير gl_FragColor
  • vec2 ، vec3and vec4 هي الأنواع المتشابهة مع C: صفيف float [2] ، صفيف float [3] و صفيف float [4]
  • u_texture هو معرف النسيج
    إتركه وحده :-)
  • v_tex_coord هو vec2 الذي يحتوي على موقعنا الحالي في الملمس
  • texture2D (tex ، p) هي وظيفة تقوم بإرجاع اللون من tex texture في النقطة p كـ vec4
    الذي يحتوي على rgba
  • gl_FragColor هو لون الإخراج
     يجب علينا تعيينه vec4

كود التحميل

ما تبقى هو رمز التحميل.

  • افتح GameScene.m
  • إضافة طريقة -didMoveToView:
- (باطل) didMoveToView: (SKView *) عرض {
// 1. قم بتحميل مصدر التظليل من myShaderFile.fsh
NSString * file = [[NSBundle mainBundle] pathForResource: @ "myShader" ofType: @ "fsh"]؛
NSString * sourceString = [NSString stringWithContentsOfFile: ترميز الملف: NSUTF8StringEncoding error: nil]؛

// 2. إنشاء تظليل
SKShader * shader = [SKShader shaderWithSource: sourceString]؛

/ / 3. تعيين التظليل إلى عقدة العفريت المنشأة حديثا
SKSpriteNode * spriteNode = [SKSpriteNode spriteNodeWithImageNamed: @ "Trees"]؛
spriteNode.shader = تظليل؛

// 4. أخيرًا أضف العفريت إلى المشهد
[self addChild: spriteNode]؛
}

تأكد من أن أرقام myShader.fsh في ProjectFile> الهدف> مراحل البناء> نسخ موارد الحزمة!

يمكنك الآن تشغيل المشروع على جهاز iOS. لن تكون هناك أية أخطاء في وحدة تحكم XCode ويجب أن تشاهد شاشة مشابهة لهذه أدناه:

هيا نلعب قليلا!

الآن، جزء المرح. سنستبدل الوظيفة الرئيسية للتظليل.

اللون مع الأحمر مع الحفاظ ألفا

الفراغ الرئيسي (الفراغ)
{
    لون vec4 = texture2D (u_texture ، v_tex_coord) ؛
    تعويم ألفا = اللون.
    gl_FragColor = vec4 (1،0،0، 1.0) * alpha؛ // google "alpultiplied alpha"
}

خفض بمقدار 2x

الفراغ الرئيسي (الفراغ)
{
    vec4 colour = texture2D (u_texture، v_tex_coord * 2.0)؛
    gl_FragColor = اللون ؛
}

مبادلة الألوان بعد 1 ثانية

الفراغ الرئيسي (الفراغ)
{
    لون vec4 = texture2D (u_texture ، v_tex_coord) ؛
    تعويم ألفا = اللون.
    مرحلة تعويم = وزارة الدفاع (u_time ، 3) ؛
    vec3 outputColor = color.rgb؛
    إذا (المرحلة <1.0) {
        outputColor = color.bgr؛
    } آخر إذا (المرحلة <2.0) {
        outputColor = color.brg؛
    }
    gl_FragColor = vec4 (outputColor ، 1.0) * ألفا ؛
}

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

الفراغ الرئيسي (الفراغ)
{
    لون vec4 = texture2D (u_texture ، v_tex_coord) ؛
    تعويم ألفا = اللون.
    float r = (sin (u_time + 3.14 * 0.00) +1.0) * 0.5؛
    float g = (sin (u_time + 3.14 * 0.33) +1.0) * 0.5؛
    float b = (sin (u_time + 3.14 * 0.66) +1.0) * 0.5؛
    gl_FragColor = vec4 (r، g، b، 1.0) * alpha؛
}

أمواج

الفراغ الرئيسي (الفراغ)
{
    float deltaX = sin (v_tex_coord.y * 3.14 * 10 + u_time * 4) * 0.01؛
    vec2 coord = v_tex_coord؛
    coord.x = coord.x + deltaX؛
    لون vec4 = texture2D (u_texture ، coord) ؛
    gl_FragColor = اللون ؛
}

سمات جديدة

في WWDC 2016 ، قدمت Apple تحديثًا مهمًا لـ SpriteKit - فئتي SKAttribute و SKAttributeValue.

قبل تحديث SDK هذا ، إذا أردنا تمرير المعلمات المخصصة إلى برنامج التظليل ، فعلينا تمرير البيانات من خلال قيمة موحدة.

كان لهذا عيبان خطيران:

  • كل تغيير موحد تسبب في إعادة تظليل التظليل
  • التعامل مع برنامج تظليل كل العفريت بالطريقة نفسها بالضبط

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

ثانياً ، نصنع حالة تظليل واحدة ونغير زيها الذي يسبب إعادة التجميع.

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

قدم SDK 10.0 فصول SKAttribute و SKAttributeValue. هذان يسمحان (أخيرًا!) بتمرير البيانات إلى برامج التظليل دون إعادة التجميع. خوارزمية الاستخدام بسيطة:

  • الجزء تظليل:
  1. إنشاء برنامج تظليل
    SKShader
  2. إنشاء مجموعة من SKAttributes
  3. تعيين مجموعة من السمات لبرنامج التظليل
  • الجزء العفريت:
  1. تعيين برنامج تظليل إلى العفريت
  2. تعيين قاموس SKAttributeValues

مثال مع السمات

في المثال الأخير ، سنعمل على إضافة شريطين آخرين. سيكون لكل واحد منهم برنامج تظليل واحد وسيختلف فقط في السمات. دعنا نعدل -didMoveToView: inGameScene.m:

- (باطل) didMoveToView: (SKView *) عرض {
    NSString * file = [[NSBundle mainBundle] pathForResource: @ "myShader" ofType: @ "fsh"]؛
    NSString * sourceString = [NSString stringWithContentsOfFile: ترميز الملف: NSUTF8StringEncoding error: nil]؛
    SKShader * shader = [SKShader shaderWithSource: sourceString]؛
    
    // 1. أضف سمة مخصصة لتظليل
    SKAttribute * attrProgress = [SKAttribute attributeWithName: @ "THE_MIGHTY_DARK_FACTOR" type: SKAttributeTypeFloat]؛
    shader.attributes = @ [attrProgress]؛

    // 2. إنشاء شجرة العفاريت
    أشجار NSArray * = @ [
                       [self createThWithShader: shader mightyFactor: 0.3f zPosition: 1] ،
                       [self createThWithShader: shader mightyFactor: 0.6f zPosition: 2] ،
                       [self createThWithShader: shader mightyFactor: 0.9f zPosition: 3] ،
                       ].
    لـ (شجرة SKSpriteNode * في الأشجار) {
        [النفس addChild: شجرة] ؛
    }
}

- (SKSpriteNode *) createTreeWithShader: (SKShader *) shader mightyFactor: (CGFloat) mightyFactor zPosition: (CGFloat) zPosition {
    SKSpriteNode * treeNode = [SKSpriteNode spriteNodeWithImageNamed: @ "Trees"]؛
    treeNode.shader = تظليل؛
    // 3. املأ السمة المخصصة على العفريت
    treeNode.attributeValues ​​= @ {@ "THE_MIGHTY_DARK_FACTOR": [SKAttributeValue valueWithFloat: mightyFactor]}؛
    treeNode.zPosition = zPosition؛
عودة شجرة عقدة؛
}

... وبرنامج التظليل:

الفراغ الرئيسي (الفراغ)
{
    vec4 colour = texture2D (u_texture، v_tex_coord * (2.5 * THE_MIGHTY_DARK_FACTOR)) ؛
    تعويم ألفا = اللون.
    vec3 baseColor = color.rgb * THE_MIGHTY_DARK_FACTOR؛
    gl_FragColor = vec4 (baseColor ، 1.0) * ألفا ؛
}

... وانظر النتيجة معلمات!

المحاذير

  • عادةً ما يتم تحميل شفرة مصدر shader من ملف .fsh إلى NSString عادي
    يجب ترجمة هذا الرمز على الجهاز المستهدف أثناء وقت التشغيل
    لا الشيكات وقت البناء!
  • قد تستخدم الأجهزة الأقدم إصدارًا مختلفًا من OpenGL ES ، لذلك احذر اختلافات بناء جملة GLSL!
    في حالة Raft Challenge ، كانت هناك حاجة لاستبدال __constant (صالح في OpenGL ES 3.0) للتشغيل في OpenGL ES 2.0.
  • من المستحسن الاحتفاظ بالإشارة إلى كائن SKShader في مكان ما وإعادة استخدامه بشكل متكرر حسب الحاجة لتجنب انخفاض معدل الإطار المرئي
    في حين يستغرق تجميع التخصيص والتظليل أقل من 1/60 ثانية ، فقد يصبح عبئًا كبيرًا في حلقة التجسيد
  • عند استخدام SpriteKit's Texture Atlases ، كن حذراً من vtexcoord
    قد يقوم XCode بتدوير بعض القوام التي تتبادل المحور X و Y
    تعديل اللون آمن ، والهندسة ليست كذلك

ملخص

لقد تعلمنا بأمثلة كيفية استخدام تظليل الأجزاء في Sprite Kit. لقد أضفنا معلمات إلى sprites حتى يتمكن برنامج التظليل الخاص بنا من تقديم كل مثيل بطريقة مختلفة دون أي خسارة في الأداء.

المشروع الكامل متاح للتنزيل.

يمكنك قراءة الجزء 3 من هذه السلسلة هنا.

نبذة عن الكاتب: كامل Ziktek هو مطور دائرة الرقابة الداخلية في www.allinmobile.co