برنامه نویسی

برنامه Flappy، چرا و چگونه در Power Apps یک بازی بسازیم

Power Apps به وضوح یک پلت فرم تمرکز سازمانی است که برای راه‌حل‌های کاربردی و تجاری طراحی شده است. پس چرا باید از آن برای ساختن یک بازی استفاده کنم، خوب یک کمی دیوانه هستم (حتی یاد گرفتم که چگونه Power Automate flows را کدنویسی کنم، اینجا بخوانید)، اما یک دلیل خوب وجود دارد. بهترین روشی که من یاد می‌گیرم انجام دادن است، و با توجه به تنوع ملزومات/راه‌حل‌های Power App، شما به مجموعه مهارت‌های متنوعی نیاز دارید. از ورود داده‌ها گرفته تا مدیریت پروژه گرفته تا سیستم‌های موجودی، Power Apps می‌تواند همه این کارها را انجام دهد، و به عنوان توسعه‌دهنده ممکن است تکرار زیادی برای تمرین نداشته باشید.
بنابراین من اغلب برای خودم پروژه‌های شخصی تنظیم می‌کنم تا مهارت‌هایم را یاد بگیرم/تمرین کنم، و در این مورد این بازی پرنده شل و ول بود. چه مهارت هایی… خوب در این مورد (کاملاً صادقانه برنامه ریزی شده، نه اینکه فقط می خواستم بازی دیگری بسازم) تایمرها و مختصات بود.

تایمرها فوق العاده قدرتمند هستند و نسبت به تایمرهای واقعی حلقه/انتظار بیشتری دارند. می‌توان از آن‌ها برای موارد اساسی مانند انیمیشن منوها تا به‌روزرسانی‌های وضعیت برنامه‌ریزی‌شده استفاده کرد.

مختصات اغلب توسط توسعه دهندگان جدید فراموش می شوند و به سادگی به کشیدن مؤلفه به مکان مناسب تکیه می کنند. اما استفاده از پارامترهای X و Y می‌تواند دقت را افزایش دهد و با به‌روزرسانی پویا همه مؤلفه‌ها در زمان صرفه‌جویی کند.

علاوه بر این، کار در LowCode اغلب به راه‌حل‌های نوآورانه نیاز دارد، و از آنجایی که Power Apps برای بازی‌ها ساخته نشده‌اند، قدرت نوآوری شما را به حداکثر می‌رساند.


بنابراین اکنون بخش های خسته کننده به پایان رسیده است، بیایید برنامه Flappy را بسازیم.

من در راهنماها بهترین نیستم، بنابراین در این وبلاگ فقط قصد دارم در مورد 3 حوزه اصلی پیچیدگی و آنچه به من کمک کرد یاد بگیرم صحبت کنم. مطمئنم هر چیز دیگری را می‌توانید بفهمید (نمرات، بازی تمام شده، راه‌اندازی مجدد)، و اگر می‌خواهید بدانید که چگونه آن را بدانید، یک کپی از برنامه را در Github خود آپلود می‌کنم، در پایین وبلاگ پیوند دهید.

1. اسکرول بی نهایت

چگونه می توانیم نه تنها انیمیشن، بلکه حرکت ادامه دار را بدون کدگذاری/ایجاد همه چیز ایجاد کنیم. حرکت خیلی بد نبود، با استفاده از تایمرها در یک حلقه می‌توانیم x و y لوله‌های خود را به‌روزرسانی کنیم.

برای اسکرول بی نهایت، من یک شی با 4 مقدار ایجاد کردم، اینها اسلات های ما هستند (آن را به عنوان یک تسمه با سطل/شاخه ها در نظر بگیرید). در شروع تایمر، هر یک از شکاف ها را با به روز رسانی مقدار x جابجا می کنیم. هنگامی که یک شکاف از 10- عبور کرد، اسلات به آخرین شکاف + شکاف تنظیم شده به روز می شود.

شکاف 1 = شکاف 4 + شکاف
شکاف 2 = شکاف 1 + شکاف
شکاف 3 = شکاف 2 + شکاف
شکاف 4 = شکاف 3 + شکاف

شیار جابجایی Power FX 1 به بعد از شکاف 4

Set(voSlots,{
            vi1:voSlots.vi4+viGap,
            vi2:voSlots.vi2, 
            vi3:voSlots.vi3,
            vi4:voSlots.vi4
            }
        );
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توضیحات تصویر

2. تشخیص برخورد

تشخیص برخورد همه چیز در مورد دانستن نشانه های اقلام شما و سپس ریاضی برای بررسی اینکه آیا آنها با هم تداخل دارند. نمودار زیر بهترین رویکرد من برای نشان دادن آن است (هنوز بهترین روشی که من می دانم نیست).

توضیحات تصویر

من از پارامترهای x و y برای بدست آوردن سمت چپ بالا استفاده می کنم، سپس عرض و ارتفاع را اضافه می کنم تا پایین سمت راست (و 2 گوشه دیگر با و بدون ارتفاع و عرض). اکنون مقادیری داریم که می‌توانیم آن‌ها را بررسی کنیم، بنابراین برای لوله پایین اگر:

  1. عرض اسپرایت X+ از لوله های X بیشتر است
  2. ارتفاع اسپرایت Y+ از لوله های Y بیشتر است
  3. sprites X کمتر از لوله X + عرض است

تاثیر داریم

در Power FX یعنی.

imFlap.X+imFlap.Width>pipe1.X&&imFlap.Y+imFlap.Height>pipe1.Y&&imFlap.X<pipe1.X+pipe1.Width

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای لوله بالایی Y به sprites Y و لوله Y+height تغییر می کند

تکرار برای تمام لوله ها

3. تایمر

در برنامه ای که من از 2 تایمر استفاده می کنم، هر دو به صورت پیوسته تنظیم شده اند، بنابراین در یک حلقه بی پایان قرار دارند. من 2 دارم زیرا 1 همیشه مدت زمان یکسانی خواهد داشت (1 میلی ثانیه) و دومی به تدریج از مدت زمان کاهش می یابد (سرعت حرکت لوله ها به صورت افقی).
با استفاده از OnTimerStart می توانیم متغیرها را به روز کنیم / تعاملات را بررسی کنیم.

تایمر 1 (tiSet)

//// if sprite above ground move down 10 px
If(viVertical<App.Height-viBase-imFlap.Height,
    Set(viVertical,viVertical+10);

    //impact check bottom pipe 1 - pipe past flapEnd & flap below pipe & pipe not past flap start  
If((imFlap.X+imFlap.Width>pipe1.X&&imFlap.Y+imFlap.Height>pipe1.Y&&imFlap.X<pipe1.X+pipe1.Width&&!vbCheat)
        || 
//impact check top pipe 1     (imFlap.X+imFlap.Width>pipeT1.X&&imFlap.Y<pipeT1.Y+pipeT1.Height&&imFlap.X<pipeT1.X+pipeT1.Width) ,
        Set(vbGameOver,true);
    );
//impact check pipe 2     If((imFlap.X+imFlap.Width>pipe2.X&&imFlap.Y+imFlap.Height>pipe2.Y&&imFlap.X<pipe2.X+pipe2.Width&&!vbCheat)
        ||       (imFlap.X+imFlap.Width>pipeT2.X&&imFlap.Y<pipeT2.Y+pipeT2.Height&&imFlap.X<pipeT2.X+pipeT2.Width) ,
        Set(vbGameOver,true);
    );
//impact check pipe 3       If((imFlap.X+imFlap.Width>pipe3.X&&imFlap.Y+imFlap.Height>pipe3.Y&&imFlap.X<pipe3.X+pipe3.Width&&!vbCheat)
        ||       (imFlap.X+imFlap.Width>pipeT3.X&&imFlap.Y<pipeT3.Y+pipeT3.Height&&imFlap.X<pipeT3.X+pipeT3.Width) ,
        Set(vbGameOver,true);
    );
//impact check pipe 4     If((imFlap.X+imFlap.Width>pipe4.X&&imFlap.Y+imFlap.Height>pipe4.Y&&imFlap.X<pipe4.X+pipe4.Width&&!vbCheat)
        ||       (imFlap.X+imFlap.Width>pipeT4.X&&imFlap.Y<pipeT4.Y+pipeT4.Height&&imFlap.X<pipeT4.X+pipeT4.Width) ,
        Set(vbGameOver,true);
    );
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تایمر 2 (tiMove)

If(Not(vbGameOver),
//// sets pipe position
    Set(voSlots,{
        vi1:voSlots.vi1-10,
        vi2:voSlots.vi2-10, 
        vi3:voSlots.vi3-10,
        vi4:voSlots.vi4-10
        }
    );
//// sets score
    Set(viScore,viScore+1);
//// if pipe 1 passes off side  
    If(voSlots.vi1<-10,
////move to after last pipe   
        Set(voSlots,{
            vi1:voSlots.vi4+viGap,
            vi2:voSlots.vi2, 
            vi3:voSlots.vi3,
            vi4:voSlots.vi4
            }
        );
//// randomly positions pipe gap
        Set(voPipes,{
            viPipe1:RandBetween(130,App.Height-100-(viGap+10)),
            viPipe2:voPipes.viPipe2, 
            viPipe3:voPipes.viPipe3,
            viPipe4:voPipes.viPipe4 
            }
        );
    );
//// if pipe 2 passes off side (same as pipe 1)
    If(voSlots.vi2<-10,
        Set(voSlots,{
            vi1:voSlots.vi1,
            vi2:voSlots.vi1+viGap, 
            vi3:voSlots.vi3,
            vi4:voSlots.vi4
            }
        );
        Set(voPipes,{
            viPipe1:voPipes.viPipe1,
            viPipe2:RandBetween(130,App.Height-100-(viGap+10)),
            viPipe3:voPipes.viPipe3,
            viPipe4:voPipes.viPipe4 
            }
        );
    );
//// if pipe 3 passes off side (same as pipe 1)
    If(voSlots.vi3<-10,
        Set(voSlots,{
            vi1:voSlots.vi1,
            vi2:voSlots.vi2, 
            vi3:voSlots.vi2+viGap,
            vi4:voSlots.vi4
            }
        );
        Set(voPipes,{
            viPipe1:voPipes.viPipe1,
            viPipe2:voPipes.viPipe2,
            viPipe3:RandBetween(130,App.Height-100-(viGap+10)),
            viPipe4:voPipes.viPipe4 
            }
        );
    );
//// if pipe 4 passes off side (same as pipe 1)
    If(voSlots.vi4<-10,
        Set(voSlots,{
            vi1:voSlots.vi1,
            vi2:voSlots.vi2, 
            vi3:voSlots.vi3,
            vi4:voSlots.vi3+viGap
            }
        );
        Set(voPipes,{
            viPipe1:voPipes.viPipe1,
            viPipe2:voPipes.viPipe2,
            viPipe3:voPipes.viPipe3,
            viPipe4:RandBetween(130,App.Height-100-(viGap+10))
            }
        );
    );
//// if score is block of 200 next level 
    If(Mod(viScore,200)=0,
        Set(viLevel,viLevel+1);
//// if level block of 2 increase speed
        If(Mod(viScore,2)=0 && viSpeed>0,Set(viSpeed,viSpeed-1));
//// decrease gap until 280 px 
        If(viGap>280,Set(viGap,viGap-10));
    );
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


همانطور که می بینید این احتمالا کارآمدترین راه برای انجام این کار نیست، اما کار می کند، و از منظر یادگیری، من تجربیات ارزشمندی در این زمینه به دست آوردم:

  • موقعیت نسبی اجزاء
  • انیمیشن (به عنوان مثال برای منوی کشویی)
  • تکرار اعتبارسنجی (مثلاً بررسی دوره ای برای به روز رسانی خارجی)
  • حلقه زدن عمومی (مثلاً کاری را 100 بار انجام دهید)

پیوند به صادرات: https://github.com/wyattdave/Power-Platform

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا