Microsoft MakeCode

Splash Screens and Options Panels (TS Library)

Update: I’ve released this extension; more information is available at Info Screens extension (beta)

I’ve started creating a TypeScript library that can create splash screens and options panels. The library is much more memory-friendly than the splash screens that I’ve used in my previously-shared games, and I will be going back to update those games once I’ve finished this library. There are a couple of bugs that I need to squash before I build this into an extension, but I thought I’d share an early release. :slight_smile:

I’m also sharing this preview because I need some help from my hardware experts. The demos run just fine in the browser, but they don’t work on hardware (at least, not on the BrainPad Arcade). No error is generated; just a blank screen. The file size is smaller than my Pong game, so it shouldn’t be having a problem with the size of the file. I’m not sure where to go from here. (FWIW, I’ve tested this in the beta build of Arcade; I have the same problem there.)

Below are two demos. The full demo shows some of the different screens that you can build. (You also get some hints about my next game. :smiley: ) The simplified demo only contains one splash screen. I figured that one would be easier to use for those of you who are helping me diagnose this library on the hardware devices.

More soon!

Full demo:

Simplified demo:

2 Likes

This is great!
If you’re able to get a reliable repro for the hardware issue, feel free to file a bug at: https://github.com/microsoft/pxt-arcade
I’ll try to repro this myself on an Adafruit PyBadge today.

1 Like

Thanks, @darzu! I’m trying to make it a pretty versatile library, at least for the TypeScript version. I’m hoping to bring a simplified version to the Blocks environment, as well.

Thanks for the encouragement regarding filing a bug report. I’d like to wrap my head more around these hardware devices, though. I find them terribly fascinating. How do all of you debug something like this, once you’re able to reproduce an error? I know Rick (@richard) and some others have written some documentation about the debug connector… Is that a good place to start debugging something like this, or is there a higher level in the debug stack where we begin?

@mmoskal is the hardware expert. I did the simulator debugger (not so useful for hardware stuff)

1 Like

Okay, took a bit but I figured out the bug; I ended up refactoring quite a bit while trying to identify the cause of the freeze / get a minimal repro (see https://github.com/microsoft/pxt-arcade/issues/968), so it’s currently structured quite a bit differently (https://makecode.com/_E2wb86766EXD, and I also noticed that the subtitles are changing too quickly on the last screen so I must have changed something by accident while uncommenting things)

The freeze was caused by an array access that was passing in undefined. Simple fix is just to add an | 0 when indexing on that value – the one that I noticed was accessing the headlines in drawCurrHeadline, but I might have fixed another issue without recognizing it as the issue while refactoring.

This behavior is similar to the one that is addressed in https://github.com/microsoft/pxt/pull/5408 - some values used to be special cased in some situations to not allow undefined, but that changed on hardware and some portions of the javascript simulator do not currently match.

Re: how to debug hardware; as richard mentioned, Michal typically does the true hardware debugging, but for simple cases the console.log approach (in this case, game.splash) works pretty well to bisect the issue; in this case, I trimmed out as much of the code as I could while still reproing the issue, and once I got it to be as minimal as I could easily get it, and narrowed down exactly where the issue was occurring by adding in some game.splashes before / after segments of code to see exactly where the crash occurred.

and one more side note, I also ran into https://github.com/microsoft/pxt-arcade/issues/959 on the options screen when running it on my pybadge, which made them hard to use - thats not a bug on you but just something to note

1 Like

@jwunderl: Joey, you’re incredible. Seriously. The fact that you spent your own time to break apart my code is… I’m speechless.

Thanks, too, for the reminder that the compiled code that runs on hardware is not, exactly, the same code that runs in the browser. Going the old-school console.log approach makes much more sense than trying to debug with a hardware debugger … especially since most of my errors can likely be fixed with a change to the TypeScript.

I’ll take a look at your modifications and update my source accordingly.

I’ve noticed #959, too, with the BrainPad Arcade, specifically when invoking the menu screen and then attempting to navigate within it. Does that seem to be some sort of debounce problem with the hardware, or do you think that’s just the button event handler being called when it shouldn’t be?

Thanks again, Joey. You’re the best!

P.S. Looking at your refactored code now. Interfaces. Duh. I’m an idiot. :slight_smile:

1 Like

Never a problem! I don’t think I deserve that much praise, though, as I’m just a new dev who happens to get to work on a cool project for a while :slight_smile:. Especially when you’re providing us with cool games and extensions (and identifying bugs we hadn’t seen yet so we can fix them!)

I haven’t actually looked into the cause for that behavior too much yet, but it is something to do with the implementation of any that isn’t behaving nicely; for example, in this game:

when downloaded to hardware, pressing a increases score by 2 and count by 4, pressing b increases life by 2 and count by 4, and any other button increases count by 3. But switching the any to down and everything works as expected. :man_shrugging:.

1 Like

I’ve been working on this library again today, taking the great ideas from @jwunderl’s refactoring and looking for ways to make the library as memory-efficient as possible. I’ve run into an interesting bug.

I’ve realized that there really is no point in each instance having its own canvas for drawing, so I moved the canvas to be a static member variable in the underlying RotatingScreens class. The canvas, then, is shared across all instances of the base class and any inherited classes. I’ve implemented that here:

I found it strange, though, that when I tried to run this on hardware, I could only create two instances. If I tried to create a third instance (even the simplest one, without any additional images or sprites), I’d get an out-of-memory error 21 on the device.

Finding that odd, I switched to using a global variable, moving the common canvas out of the RotatingScreens base class. That version is here:

Moving from a static variable to a global variable worked, and I could create all four instances on the hardware device with no problem.

That seems strange, right? The static variable should only be created once … yet, it seems like the static variable is being created for each instance. I also don’t know if this is a problem with the hardware compiler, or if this is an issue with the TypeScript transpiler. I’m thinking it’s the former, since the TS documentation indicates that the language supports static class variables.

Thoughts? File a bug report, or do some additional investigation first?

I’ve isolated the error … and it’s neither a bug in the compiler nor a bug in TypeScript. See below. :smiley:

OK … I’ve isolated my error. Yes, this is my error, and not an error in the compiler.

Head this way for a demonstration of this error. Press A to create instances of the first class, and press B to create instances of the second class. On a hardware device, you’ll be able to create many instances of the second class by pressing B. You’ll only be able to create two instances of the first class, though, before the device runs out of memory.

The faulty code is in the first class:

class StaticImage {
    protected static img: Image = image.create(screen.width, screen.height)

    constructor(id: number = 0) {
        StaticImage.img.fill(0)
        StaticImage.img.printCenter('Static Image ID: ' + id, screen.height / 2, 1, image.font8)
        scene.setBackgroundImage(StaticImage.img)
    }   // constructor()
}   // class StaticImage

The error is in the initialization of the static member variable. It appears that, each time an instance is created, that initialization call is made. I wrongly thought that the initializer would only be called once, when the static variable was first created.

A more correct way to initialize a static variable is in the second class. Do a null check and initialize the variable if needed as a part of the instance constructor.

class StaticImageNull {
    protected static img: Image

    constructor(id: number = 0) {
        if (!StaticImageNull.img) {
            StaticImageNull.img = image.create(screen.width, screen.height)
        }   // if (! StaticImageNull.img)
        StaticImageNull.img.fill(0)
        StaticImageNull.img.printCenter('Static Image Null ID: ' + id, screen.height / 2, 1, image.font8)
        scene.setBackgroundImage(StaticImageNull.img)
    }   // constructor()
}   // class StaticImageNull

Oops on my part. :slight_smile: I’m now wondering if I’ve made the same mistake in any other code that I’ve written in other languages…

I’m loving the fact that these hardware devices are helping us to write smarter, more efficient code!

The initializer should be only executed once in your original code. Also even if it’s executed multiple times, I don’t see why it wouldn’t get garbage collected. This looks like a bug on our side.

1 Like

Thanks for the feedback, @mmoskal. I’ll file a bug report in GitHub, but this might be more of a stylistic or existential issue. I’m with you, though, Michael: I think the initializer should only be called once, but I could see the other argument, as well. Whichever tack you take is fine with me, just as long as it’s consistent between the TS transpiler and the hardware compiler.

I don’t know why this program should overload the hardware (F401) on the “A” button only and gives the 021 error message. BUT, when pressing the “B” button repeatedly , no error. This occurs both on BrainPad Arcade and on Kittenbot’s Meowbit game consoles. OK, now I see that’s what AlexK said.

I’ve built a first pass at this extension and made it available on GitHub. It’s JavaScript-only for now. I’ll add simplified capabilities for Blocks later this month. When that is ready, I’ll start a new thread announcing the “official” release. :slight_smile:

Extension URL (for importing into your projects): https://github.com/robo-technical-group/pxt-arcade-info-screens.git

The README.md with some code samples can be found at the GitHub repository:

You can see a sample of the different screens here. It works on hardware, too!

For a simplified example, you can check out my JavaScript game template:

Happy Monday, and Happy Coding! Have fun!

1 Like

P.S. Thanks to @jwunderl and @mmoskal for correcting the bug in PXT mentioned earlier in this thread. Excellent detective work on that one! I look forward to testing it in beta once MakeCode Arcade is synchronized with upstream.

1 Like