Power Apps is clearly an enterprise focus platform, designed for utilities and business solutions. So why would I use it to make a game you ask, well one I'm a little bit crazy (I even learned how to code Power Automate flows, read here), but there is one good reason. The best way I learn is by doing, and with how diverse Power App requirements/solutions are you need a diverse skill set. From data entry to project management to inventory systems, Power Apps can do it all, and as developer you may not get a lot of repetition to practice.
So I often set myself personal projects to learn/practice my skills, and in this case it was the flappy bird game. What skills... well in this case (totally planned honest, not that I just wanted to make another game) it was timers and co-ordinates.
Timers are incredibly powerful, and are more loops/waits then actual timers. They can be used for basic things like slide out menus animation to scheduled status updates.
Co-ordinates are often forgotten about by new developers, relying on simply dragging component to right place. But using the X and Y parameter can add that fineness of precision and save time by dynamically updating all components at once.
Additionally working in LowCode often requires innovate solutions, and as Power Apps is clearly not made for games, it will push your powers of innovation to the max.
So now the boring bits over, let's make Flappy App.
I'm not the best at walk through guides, so in this blog I'm just going to talk about the 3 main areas of complexity and what helped me learn. Everything else I'm sure you can figure it out (scores, game over, restarts), and I will upload a copy of the app to my Github if you do want to know how to, link at the bottom of the blog.
1. Infinite Scroll
How do we create not only animation but continued movement without hardcoding/creating everything. The movement wasn't too bad, leveraging timers on a loop we are able to update the x and y of our pipes.
For the infinite scroll I created an object with 4 values, these are our slots (think of it as a convayabelt with buckets/slots). On the timer start we move each of the slots by updating the x value. Once a slot passess -10 then the slot is updated to the last slot + the set gap.
slot 1 = slot 4 + gap
slot 2 = slot 1 + gap
slot 3 = slot 2 + gap
slot 4 = slot 3 + gap
Power FX movinng slot 1 to after slot 4
Set(voSlots,{
vi1:voSlots.vi4+viGap,
vi2:voSlots.vi2,
vi3:voSlots.vi3,
vi4:voSlots.vi4
}
);
2. Collision Detection
Collision detection is all about knowing corodiantes of your items and then math to check if they overlap.This is probalby the most complex/hard to get your head around part of the game. The diagram below is my best approach at showing it (still not the best I know).
I use the x and y parameters to get the top left, then add width and height to get the bottom right (and the other 2 corners with and without height and width). Now we have a values that we can check against, so for bottom pipe if:
- the sprites X+width is greater then the pipes X
- the sprites Y+height is greater then the pipes Y
- the sprites X is less then the pipes X+width
we have impact.
In Power FX that is.
imFlap.X+imFlap.Width>pipe1.X&&imFlap.Y+imFlap.Height>pipe1.Y&&imFlap.X<pipe1.X+pipe1.Width
For the top pipe the Y changes to the sprites Y adn pipes Y+height
The repeat for all the pipes
3. Timers
In the app I use 2 timers, both are set as contineous, so they are on a never ending loop. I have 2 because 1 will always have the same duration (1 millisecond) and the 2nd will gradually decrease in duration (speed of the pipes moving horizontally).
Using the OnTimerStart we are able to update variables / check interactions.
Timer 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);
);
)
Timer 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));
);
)
As you can see this probably isnt the most efficient way to do this, but it works, and from a learning perspective I gained valuable experience in:
- Relative positioning of components
- Animation (e.g. for slide out menu)
- Repeating validations (e.g. perodically checking for external update)
- General Looping (e.g do something 100 times)
Link to Export: https://github.com/wyattdave/Power-Platform
Top comments (2)
You have created an interesting blog post! I'm glad I found this blog! I read your article carefully. Diamond exchange id create
Greetings! I'm particularly interested in mancala gaming online casinos and would love to gather more information about them. If any of you have firsthand experience or knowledge about this platform, I kindly request your reviews and recommendations. Your insights would be greatly appreciated. Thank you!