-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial
It follows our recommendation on how to develop games for MES.
Because uploading your game to a real console while debugging is really time consuming, we created a MES simulator called Virtual-MES. V-MES compiles your games source code with its own drivers, allowing MES games to run on PC (this is what makes it a simulator, not an emulator). You can set it up using the instructions over at its repository, but we recommend using our template repository. It incorporates simulation and uploading to real hardware and reduces the complexity of your workflow significantly. In this Tutorial, we will be using the template, so follow the instructions found in that repository. If you want to include MES/V-MES into your own build system, you're probably skilled enough to follow this tutorial anyway.
In terms of "Time to hello world", MES ranks quite high, considering it needs to render the characters and not simply send them through a data stream. This is what a "Hello World" program for MES looks like:
#include <mes.h>
#include <gpu.h>
uint8_t start(void) {
char* helloworld = "Hello, World!";
gpu_blank(FRONT_BUFFER, 0);
gpu_print_text(FRONT_BUFFER, 1, 1, 1, 0, helloworld);
return CODE_FREEZEFRAME;
}Since we will be drawing something on the screen, first include the GPU header. The main function, the one that will be executed when the user opens your game, is called start and has uint8_t as its return type. For a list of all return codes, see the documentation.
Inside the function we first declare our hello world string as a char*. The following two functions are provided by the GPU header and will clear the screen (with the color black, which is 0 in the standard palette), then render our text onto it at the location (1, 1), with foreground color 1 (white) and background color 0. All of this is applied to the FRONT_BUFFER, which is the buffer the user sees on screen.
Finally, by returning the freeze frame exit code, we leave our program but keep the last frame on screen. Among other things, the exit codes are defined inside mes.h, therefore it was included too.
MES image buffers are formatted in a very specific way to enable fast rendering on the GPU, who otherwise couldn't keep up with the demanding 60 FPS VGA standard. Not only to they have a bit depth of 3 (which means 8 simultaneous colors on screen), but are also grouped in 3-byte-chunks and scrambled among one another inside those chunks. This means the size of an image buffer is always divisible by three.
Especially of you're new to game development on MES, we recommend using additional headers like mesgraphics.h, which abstract the complex graphics specification to a higher level and provide functions for manipulating images easier. Take a look at the GPU Header documentation to learn about the exact specifications.
Because a depth of three bits drastically limits your inner artist, you can change the palette from the default one (black, white, red, green, blue, yellow, magenta, cyan), to any 9-bit color you want.
To keep it simple, lets limit ourselves to black and cyan. Since they have the indices 0 and 1 on the default palette, we can fill any graphics buffers with a those colors by filling them with ones or zeroes respectively.
This is what a 10x10 cyan rectangle at (50, 50) on a black screen looks like:
#include <mes.h>
#include <gpu.h>
uint8_t start(void) {
uint8_t* square = malloc(39);
memset(square, 0xFF, 39);
gpu_blank(FRONT_BUFFER, 0);
gpu_send_buf(FRONT_BUFFER, 10, 10, 50, 50, square);
return CODE_FREEZEFRAME;
}The buffer is 39 bytes big because 100 pixels at 3 bits per pixel make 300 bits, which would be 37,5 bytes, rounded up to the next multiple of three.
Right now, we can only display a single frozen frame. To continuously update our game, we'll need a loop. Inside the loop, we first update all game logic, then render it. Unless an exit code is returned at some point, this application will run forever.
#include <mes.h>
#include <gpu.h>
#include <timer.h>
uint8_t start(void) {
uint8_t* square = malloc(39);
memset(square, 0xFF, 39);
while (1) {
// todo: update
gpu_blank(BACK_BUFFER, 0);
gpu_send_buf(BACK_BUFFER, 10, 10, 50, 50, square);
gpu_swap_buf();
timer_block_ms(33);
}
}This should look exactly like the last example, but it is the base for every game. To prevent the cpu from running like crazy and flooding the gpu with calls, we simply wait a certain amount of time at the end of every loop. This is done using the timer_block_ms() function provided by the timer.h header. Waiting 33 milliseconds between updates results in about 30 FPS (1s / 0.033s ≈ 30Hz). This approach to timing is naive though, because the longer the game logic update takes, the lower your FPS will drop (e.g. lag will occur).
If you aim to create a somewhat big or competitive game, make sure to check out the timer documentation for multiple ways to accurately time your game loop.
Another thing you might have noticed: The FRONT_BUFFERs turned into BACK_BUFFERs and another GPU call was added. Since we are continuously updating the screen now, drawing directly to the front buffer (the one on screen) could result in artifacts. Fortunately, inside the GPU's RAM there is another buffer, the back buffer, which you can write to while the front buffer is being displayed, without any issues. Once your done, simply swap the buffers with gpu_swap_buf() and what was the back buffer turns into the front buffer and gets shown on screen.
Lets control our cube with the controller's D-pad. There is