PowerFlappy - Canvas App Power Apps for c# and JavaScript Developers

As promised, I'm posting the code up for PowerFlappy! This post aims to give c# and JavaScript developers some tips on creating Canvas Apps.

To install, simply open web.powerapps.com, select Create an App, then select Open. It works really well on your mobile. I hope to see someone playing it on the tube on Monday!

Canvas Apps are function driven. Very much like Excel, they operate on values and tables of values using functions that accept these values as parameters.

You can bind controls to data so that it will change as the underlying data changes. It is like the data-binding principle you get with KnockoutJS and Angular etc.

Here are some common patterns that you'll need to use if you are writing Canvas Apps if you are a c# developer.

…semicolons and new lines

If you are a c# or JavaScript developer, then you are used to semicolons. It's practically subconscious; Canvas Apps follows suit but with one exception – the last line of a code segment!

This is rather troublesome when you add more lines because you'll need to remember to put a semicolon on the previous line before starting. To get around this by always adding 'true' to the end of a code segment:

This way, you can add new lines above 'true' and you'll not have to worry about not adding semicolons for the last line.

Oh, and you are going to find quickly that just like in Excel you need to use SHIFT-RETURN to get a new line – just like in Excel!

Variable

You can set global variables in any function. They don't have to be defined before hand - simply use:

Set(myVariable, "FooBar")

Collections

It's easy to add array style data using a JSON style format:

// Initialise a new collection
ClearCollect(collection, {i:1, x:100,y:100},{i:2, x:200,y:100},{i:3,x:300,y:100},{i:4,x:300,y:100});
// Add a new item to the collection
Collect(collection, {i:5,x:100});

Timers

Timers are a great way of making your code run at a specific interval. In a game, this would be typically be the main game loop. Since it's easy to have code spread all over your PowerApp – I find it useful to keep the majority of my code in one place.

Set your timer as follows:

You can then add the code to the Timer OnStartTimer event which will run every 100 milliseconds. Put it in the OnStart rather than the OnEnd to make it run immediately.

Think in Functions

If you had a collection of values, and you wanted to take an action on each of them based on their value, in c# you would commonly use something like:

foreach (var item in collection) {
item.x = item.x - 10;
if (item.x < 0) {

        item.x = item.x + 1000;
        }
} 

In Canvas Apps you can't do this kind of sequential logic, you have to think in functions like you would in a cell of Excel:

UpdateIf(collection, true, {x:x-10}); // Update all items

UpdateIf(collection, x<0, {x:x+1000}); // Update only those items where x<0 


This will filter the items that match the criteria and then update them accordingly. It kind of turns the foreach loop inside out!

Indexers

If you have a collection of items, it's quite normal to want to retrieve a value at a specific index. In C#:

var item = collection[4];

In Canvas Apps:

Set(item, Last(FirstN(collection,4)))

This clearly isn't optimal for large collections, so I hope there is a better option and will report if I find one. Canvas Apps compile into JavaScript - and collections are stored as simple object arrays with the Last and FirstN functions just operating on those array. You could use the Lookup function, but this ends up taking loner since it uses a delegate function to find the value rather than a simple array index.

Sprites

Canvas Apps has the concept of Galleries that allows you to create a template that is repeated for each data row. You might think that you could use this to create lots of sprites to use in your game – but unfortunately, the gallery control is limited in that you cannot move the controls outside of bounds of the screen and it's actually very slow at rendering.

To overcome this I used a set of 'generic' images that I then bound to a sprite collection. This allows me to set the values in the collections to whatever I need and the sprites will move and change size/image as required due to the data binding.

To create the side scroller, I simply scroll the sprites left using an UpdateIf as describe above. When it comes to graphics performance - it's a good idea to keep things a simple as possible.

Level Data

A trick that I've been using for years is to use Excel to write my code for me. If there is some data that you can use to generate code, then why write it manually?

For the levels, I created a simple Excel Spreadsheet that visualises the levels – and then created the collection data in the format {x:<top ground position>,y:<lower ground position>,l:<level up indicator>}

I guess I could create a level designer in a PowerApp too!

Performance

One of the challenges I had was keeping the scrolling smooth for the sprites. There were two things I found key to this:

  1. Only move sprites in increments of whole pixels. If you start to set sprites using fractions of pixels the rounding effect will create jitter
  2. Keep the timer interval higher than the time it takes to process each tick event.

Overall Maintainability

A trade off you get with Canvas Apps for the 'low-code' is that you don't get the easy to follow code files in traditional programming. Here are some tips to keep your App maintainable:

  1. Avoid having lots of code squirrelled away in many different events and property functions. For example, rather than having lots of timer events I have just a single game loop that does 90% of the functionality.
  2. Avoid functions in control property bindings, bind to a variable and set it to the value you need along with any calculations and conditions.
  3. Name your controls and variables consistently so that you can quickly identify what you need when using intellisense.

Well, there you have it. Happy Power Flapping - be sure to tweet me your highest score @ScottDurow.

Comments are closed