DS Programming for Newbies!

Discussion in 'NDS - Tutorials & FAQs' started by Foxi4, Mar 3, 2012.

Mar 3, 2012

DS Programming for Newbies! by Foxi4 at 2:49 PM (30,620 Views / 25 Likes) 168 Comments

    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    As you can see, the while loop in main is right here:

    //Beginning of main function:
    int main(void) {
    scanKeys();
    consoleDemoInit();
    iprintf("Hold the A button to\ngenerate text.\n");
    if(KEY_A & keysHeld()) {
    iprintf("The A button is held.");
    }
    //While main function returns 1, aka, while main function is running:
    while(1) {
    swiWaitForVBlank();
    }
    }

    You're supposed to scan for input and check conditions every frame, otherwise it just won't work:

    //Beginning of main function:
    int main(void) {
    //Initialization of basic functionality:
    consoleDemoInit();
    //Will print once, upon initialization:
    iprintf("Hold the A button to\ngenerate text.\n");
    //While main returns 1, aka, while main function is running:
    while(1) {
    //Scan for input:
    scanKeys();
    //Check if Button is pressed:
    if(KEY_A & keysHeld()) {
    //Will print every time A is Held:
    iprintf("The A button is held.");
    }
    //Wait for next VBlank
    swiWaitForVBlank();
    }
    }

    You can of course remove the comments, I added them only for consistency's sake.
    1 people like this.


    • Member

    Kiaku New Member

    Member Since:
    Mar 22, 2010
    Message Count:
    272
    Country:
    United States
    Wow. Thank you so much! I didn't know that the while(1) actually loops each frame. I know you noted that it's required for that part to be at the end of each nds file all the time, but it was to loop each frame so the DS's video frame doesn't exit out?
    • Member

    Snailface My frothing demand for 3ds homebrew is increasing

    Member Since:
    Sep 20, 2010
    Message Count:
    4,090
    Location:
    Engine Room with Cyan, watching him learn.
    Country:
    United States
    Here, I added a couple of lines to make this example program behave a little nicer. ^_^
    Take note of the changes inside the /* */ comments and see what effects they have compared to the
    previous example. Small changes in a program can have a big effect. ;)
    Code:
    #include 
    //Beginning of main function:
    int main(void) {
    //Initialization of basic functionality:
    consoleDemoInit();
    
    //While main returns 1, aka, while main function is running:
    while(1) {
    //Scan for input:
    iprintf("Hold the A button to\ngenerate text.\n");   /*moved inside loop to prevent disappearance*/
    scanKeys();
    //Check if Button is pressed:
    if(KEY_A & keysHeld()) {
    //Will print every time A is Held:
    iprintf("The A button is held.");  
    }
    //Wait for next VBlank
    swiWaitForVBlank();
    consoleClear();						/*added to keep 'The A button is held.' from being persistent*/
    }
    }
    
    2 people like this.
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    @[member='Snailface']

    Well-done, clearing the console does make the program tidier. I didn't include it since it wasn't mentioned in the original question, but knowing that it's an option does improve the overall output. ;)

    Basically, the program launches main(){...} as its main function. It runs until it returns a 0 or you use exit(0); from the program, until then, everything that's before the while{...} is done during the initialization stage and everything that's in the while{...} is "the main flavour of the program", aka, what it actually does frame-by-frame. That's the easiest way I can explain it.
    2 people like this.
    • Member

    dicamarques Definetely not Bruce Wayne.

    Member Since:
    Jun 25, 2010
    Message Count:
    863
    Location:
    Your computer's Recycle Bin
    Country:
    Portugal
    When's the next chapter coming?
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    When I get spare time - right now I have exams on my head so nothing is certain.
    • Member

    Kiaku New Member

    Member Since:
    Mar 22, 2010
    Message Count:
    272
    Country:
    United States
    How would I code an nds file so that instead of having a button generate only one statement, I can generate different statements with the same button?
    For example, pressing A once would print "A button: 1" while pressing A twice would print "A button: 2", and so on?
    • Member

    DiscostewSM New Member

    Member Since:
    Feb 10, 2009
    Message Count:
    3,604
    Location:
    Sacramento, California
    Country:
    United States
    For such a case, where you want a "press this many times for a certain action", you need a time frame for the input, and a counter that holds the number of times you pressed the button. Here is a simple program I just made that will examine how many times you press the A button in a set amount of time.

    Code:
    #include 
    
    #include 
    
    
    // A few constant values
    
    #define TIME_FRAME 180					// (TIME_FRAME / 60) seconds -> 180 = 3 seconds
    #define RESULT_TIME_FRAME 240	// ((RESULT_TIME_FRAME / 60 ) seconds -> 240 = 4 seconds
    
    
    // The entry function
    
    int main(void) {
    
    consoleDemoInit();	// Generates the mode for text
    
    printf( "  Examine the number of times\nthe A button is pressed in\n%1.2f second(s).\n\n", (float)TIME_FRAME / 60.0 );
    
    int timerCountDown = 0;	// A counter that decrements to 0
    int timerState = 0;	// The current timer State ( 0 - Ready to start, 1 - Input in progress , 2 - Results )
    int buttonCounter = 0;	// The number of times the button was pressed
    
    while( 1 ) {
    scanKeys();	// Get the current state of the input
    
    
    if( timerState > 0 ) {		
    if( timerCountDown > 0 )		// Timer countdown
    timerCountDown--;
    else {
    if( timerState == 1 ) {		// Show results of test
    timerState = 2;
    timerCountDown = RESULT_TIME_FRAME;
    iprintf( "\x1b[5;0HButton A was pressed %d time(s).   ", buttonCounter );
    
    } else {			// Restart in ready mode
    timerState = 0;
    buttonCounter = 0;
    consoleClear();
    printf( "  Examine the number of times\nthe A button is pressed in\n%1.2f second(s).\n\n", (float)TIME_FRAME / 60.0 );				}
    }
    if( timerState == 2 ) {
    if( timerCountDown < 60 )
    printf( "\x1b[6;0HRestarting test in %.2f seconds.", (float)timerCountDown / 60 );
    else
    iprintf( "\x1b[6;0HRestarting test in %d seconds.", timerCountDown / 60 );
    }
    }
    
    
    if( timerState < 2 && KEY_A & keysDown() ) {	// Is the test set for input?
    buttonCounter++;
    
    if( timerState == 0 ) {		// Switch to input mode
    
    timerState = 1;
    timerCountDown = TIME_FRAME;
    }
    }
    if( timerState == 1 ) {
    if( timerCountDown < 60 )
    printf( "\x1b[5;0HIn progress...\n %.2f seconds remaining\n\n", (float)timerCountDown / 60 );
    else
    iprintf( "\x1b[5;0HIn progress...\n %d seconds remaining\n\n", timerCountDown / 60 );
    }
    
    swiWaitForVBlank();
    }
    
    return 0;
    }
    1 people like this.
    • Member

    Kiaku New Member

    Member Since:
    Mar 22, 2010
    Message Count:
    272
    Country:
    United States
    Is there a way to only count the buttons w/o the timer?
    • Member

    Snailface My frothing demand for 3ds homebrew is increasing

    Member Since:
    Sep 20, 2010
    Message Count:
    4,090
    Location:
    Engine Room with Cyan, watching him learn.
    Country:
    United States
    I sent you a PM a while ago. :P
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    Just so that the timer confusion is cleared up, libnds already has built-in key repeat functionality.

    keysHeld() checks whether the key is Held down since the last frame. You can use something different entirely though - keysDownRepeat().

    By using keysSetRepeat(u8 DelayInFrames, u8 NumberOfRepeats) and keysDownRepeat() you can in fact create a macro on exactly how many presses you need and at what intervals, nullifying the need for a timer altogether, at least that's my understanding of the documentation, which by the way is very vague on the subject and I've never found a particular use for such timers except for maybe combos in fighting games or for scrolling lists.
    • Member

    CannonFoddr Regular GBATemp Lurker

    Member Since:
    Sep 23, 2006
    Message Count:
    4,092
    Location:
    Sitting by computer
    Country:
    United Kingdom
    hmm, just been brought to my attention that some people been having trouble downloading the PDF file from filetrip...
    I've had a check & it seems that it doesn't work in Chrome, but does (at least) in IE8 and Firefox

    I found a workaround in Chrome if you have trouble (well - it works for me)

    In the popup box you see after clicking the 'Download selected version'
    If you RIGHT-Click on the 'Click Here' & then select 'Save Link As...' you should be able to download it...

    If that fails - here's a link to a mirror of the 1-6.1 version
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    An update to the guide is being written as we speak, I finally found some spare time.

    What you can expect is some theory about the memory layout of the DS, but laid out in Layman's terms so that everybody can understand the idea behind dividing VRAM into Banks and so on and so forth, next we'll introduce graphics while using NightFoxLib, conversion of graphics into tiled graphics or bitmaps using GRIT software and file management under Visual Studio 2008.

    The first part should be uploaded tonight, so stay tuned! ;)

    EDIT: Looks like it won't afterall. Forces beyond my control. Tomorrow though. ;)
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    It’s been a while, hasn’t it? Well, as I said, I was quite busy with my exams, but right now I have a little bit of spare time to write so I’ll dedicate it to moving forward with the guide. To re-cap, so-far we’ve learned everything we need to create conditions in our programs and to use both the buttons and the touchscreen as means of input. That’s all great, but other than console text, we still don’t know how to include graphics in our projects! The next few chapters will be dedicated to both the basic theory behind graphics on the DS and practical use of NightFoxLib, which will be our library of choice to facilitate the use of backgrounds and sprites. Before we get there though, we need to learn a little bit about the DS itself once more – to be exact, we have to review how exactly the memory of the DS works and how to use it properly. Without further ado, let us dig in to our next chapter, dedicated to RAM, VRAM and Screen Layers.

    Introduction to DS Hardware: Chapter 1 – RAM and VRAM

    Before we properly dive into the use of graphics and various other resources, first we need to learn a bit about how the DS manages memory. As you may or may not know, the DS has 4MB of Main RAM memory, shared between its two processors – the ARM9 and the ARM7. Main RAM is where your entire Binary will be stored during its execution and naturally it has to be used efficiently, as it isn’t a whole lot of space. For a long time, it was a huge bottleneck for homebrew programmers, but then came libraries such as FATlib, NitroFS and other file systems that facilitated streaming resources into RAM, nullifying this problem by the use of buffering. We won’t tackle that just yet, seeing that conveniently, NightFoxLib does it for us automatically, however we will talk about their use later-on when introducing Saving and Loading. Let’s move on to the next important part of the DS’s memory – the VRAM.

    The fact that your resources are stored in RAM isn’t always enough to utilize them. Graphics have to fit within Video RAM before they are displayed. The size of VRAM is only 656KiB altogether, which is quite a bottleneck, considering the way it is structured. The memory is divided into several banks, all of which have different functions assigned to them. Let’s have a look at this diagram:
    [IMG]
    This should help you imagine how the system works. Again, conveniently, NightFoxLib will deal with the VRAM banks for us, but I figured that it’s best if you have a look at it to understand why sometimes you run out of VRAM blocks when using graphics.

    To combat this inheritent problem of lack of space, Tiled graphics have been introduced back in the olden days. They take much less space than standard backgrounds, and these will be the first for us to tackle – specifically, tiled backgrounds converted with the GRIT (GBA Raster Image Transmogrifier) tool.
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    I specifically picked NightFox Lib as the library of choice for this guide, simply because its use is incredibly simple and because the library is still in active developed, not to mention that it's well-documented. I believe that we will all get the quickest and best results if we focus on it as our graphics library, so let's get to it.

    Practical Use of NightFox Lib – Chapter 1 – NightFox Lib Integration Part 1


    Integrating the library with your Projects is incredibly easy – all you really have to do is copy/paste the Template provided in the lib’s folder into Your project… and that’s it! There are further integration options that you may choose that will be discussed in the second part of this chapter. As you’ve already noticed, a few new folder have appeared in your Project Folder – those will be used to store various Resources. Visual Studio is capable of linking those folders to particular elements of your Solutions. By doing that, you will always see the contents of your folders which will make coding this one bit easier. Including those folders in your solution is pretty straight-forward, but we’ll leave that for later as I already know you all can’t possibly wait any longer to get to the juicy parts.

    For now, attempt to compile the Template. If everything goes well and without hiccups, you’ve just successfully included NightFox Lib in your Project – well done!

    Additional Utilities - Chapter 1 - GRIT


    Now that we’ve included NightFox Lib in our project and we’re all acustomed with how RAM and VRAM works, we can finally move on to what we’re really interested in – using Graphics in our games and applications. As I already mentioned, the DS mostly uses Tiled Graphics – they’re much easier to render and their size is lower, thus they don’t clutter the RAM and VRAM as much. To convert an image to that kind of a format, we will be using GRIT. The name stands for GBA RASTER IMAGE TRANSMOGRIFIER and it has been the tool of choice for both GBA and NDS development due to the variety of functions it offers. It’s been created and maintained by Jasper Vijn and it’s available at http://www.coranac.com/projects/grit/ however you already have it as it’s the default tool used by NightFox Lib – you can find it in the Tools directory at nflib/tools.

    There, you will find several Batch files that we will be using to convert our Bitmaps with. Those include:
    Convert_Affine - this Batch file will convert all the graphics in the BMP folder into Tiled Affine Backgrounds and copy them into the affine folder.
    Convert_Backgrounds - this Batch file will convert all the graphics in the BMP folder into Tiled Backgrounds and copy them into the backgrounds folder.
    Convert_bitmap8 - this Batch file will convert all the graphics in the BMP folder into 8-bit Bitmaps and copy them into the bitmap8 folder.
    Convert_bitmap8_shared - this Batch file will convert all the graphics in the BMP folder into 8-bit Bitmaps with a shared palette and copy them into the bitmap8 folder.
    Convert_bitmap16 - this Batch file will convert all the graphics in the BMP folder into 16-bit Bitmaps and copy them into the bitmap16 folder.
    Convert_CMaps - this Batch file will convert all the graphics in the BMP folder into Collision Maps and copy them into the cmaps folder.
    Convert_Fonts - this Batch file will convert fonts and copy them to the fonts folder.
    Convert_Sprites - this Batch file will convert all the graphics in the BMP folder into DS-Compatible Sprites and copy them into the sprites folder.
    Convert_Sprites_autopal - this Batch file will convert all the graphics in the BMP folder into DS-Compatible Sprites with a shared palette and copy them into the sprites folder.

    As you can see, with NightFox Lib’s pre-prepared Batch converter files, converting resources will be a breeze! Remember, by default, all Tiled Graphics have a transparent colour selected, and this colour is Magenta (RGB 255,0,255) – use it whenever you want parts of your graphics to be transparent as it will not appear on the hardware. Another thing worth noting is that our Backgrounds must have sizes divisible by 256 pixels and our 2D Sprites must have sizes divisible by 8 pixels, 64 pixels being the maximum width/heigh, so keep that in mind before converting to avoid errors.

    Practical Use of NightFox Lib – Chapter 2 – NightFox Lib 2D MODE-0 Part 1 - Tiled Backgrounds


    I didn’t quite know how to start off the subject of Graphics in NightFox Lib to make it approachable to anyone, so I figured that the best way to do so would be to do it methodically. In the following few chapters I’m going to introduce the idea behind MODE’s that the DS’s 2D Engine uses, what they do and how to take the best advantage of them. We’re going to start with MODE 0 and climb upwards until we’ve discussed them all. Just so that nobody’s bored during these chapters, I’m also going to introduce the types of graphics most commonly used in the MODE in question, so that we get a little bit of practical and theoretical knowledge. This chapter will be dedicated to MODE 0 of the 2D engine in NightFox. This mode offers 4 layers of Tiled Graphics on the screen in question, and it’s likely going to be the MODE you use most commonly. To initialize a MODE, we use the command:
    NF_Set2D(Screen, MODE);
    Simply substitute Screen with use 0 or 1 to select the Top or Bottom one, and substitute MODE with 0, 2 or 5 – the default MODE’s of NightFox Lib.

    Now, before we do anything else, we have to define the default Folder for our Resources, using this function:
    NF_SetRootFolder("ROOT");
    Now, to use the Internal filesystem, we substitute the ROOT with NITROFS, in all other cases, we simply input the folder name and the function will enable FAT instead.


    With that out of the way, we can start having fun with Tiled Graphics in our projects. We’ll start with Backgrounds. Before we can use any, we have to initialize the buffers for backgrounds, as NightFox streams them from NitroFS or FAT by default. To do so, we use these functions:
    NF_InitTiledBgBuffers();
    NF_InitTiledBgSys(Screen);
    And again, we substitute the word Screen with the screen we are interested in – 0 or 1.
    With that out of the way, we’re ready to select a background to load into VRAM. Our backgrounds have to be placed in the “nitrofiles” folder of our project, once they’re there, we can use this function:
    NF_LoadTiledBg("Path", "Name", Width, Height);
    It will open the background in question, load it into RAM, attach a name to it and store the parameters of its Width and Height (divisible by 256!) so that we don’t have to remember them later on as we code. It’s pretty convenient, but our Tiled Background is not ready to be displayed yet. As we learned from the previous chapter about RAM and VRAM, we still have to copy it over to VRAM to display it. To do so, we simply use this command:
    NF_CreateTiledBg(Screen, Layer, "Name");
    As you can see, NightFox will deal with RAM-VRAM communications for us and all we really have to do is to select the Screen and Layer on which we’d like to display our background. We won’t tackle the idea of Layers for now beyond the fact that there are 4 Layers on each Screen, numbered from 0 to 3, 0 being the top-most and 3 being the bottom-most Layer.

    Okay, so we have our Background ready to be displayed – it’s going to appear on our screen right after the next swiWaitForVBlank(); - perfect!

    Just to make sure, let's review our sample code, shall we?

    Code:
    /*
    #############################################
    ##DS Programming Guide - From Zero To Hero!##
    ####Example #2 - MODE 0 Tiled Backgrounds####
    #############################################
    */
     
    /*
    ############
    ##Includes##
    ############
    */
     
    // Include C
    #include <stdio.h>
     
    // Include Libnds
    #include <nds.h>
     
    // Include NFLib
    #include <nf_lib.h>
    /*
    ###############
    ##Main(){...}##
    ###############
    */
     
    int main(int argc, char **argv) {
     
    // Turn on MODE 0 on the Top Screen
    NF_Set2D(0, 0);
     
    // Set the Root Folder
    NF_SetRootFolder("NITROFS");
     
    // Initialize the Tiled Backgrounds System on the Top Screen
    NF_InitTiledBgBuffers();
    NF_InitTiledBgSys(0);
     
    // Load the Tiled Background
    NF_LoadTiledBg("Background", "Background", 256, 256);
    NF_CreateTiledBg(0, 3, "Background");
     
    while(1){
    swiWaitForVBlank();
    }
    return 0;
    }
    Let's compile this... and... SUCCESS! We've just learned how to create, load and display Tiled Backgrounds! Just to make sure that everything is understandable, I'll attach the Example compile folder so that everyone can review the source:

    http://www.mediafire...lg6tvxd78gw3kly

    I hope that everybody had fun and stay tuned for the next chapters, which will concern Sprites! Foxi4 over and out!
    Last edited by Foxi4, Nov 6, 2012
    1 people like this.
    • Member

    CannonFoddr Regular GBATemp Lurker

    Member Since:
    Sep 23, 2006
    Message Count:
    4,092
    Location:
    Sitting by computer
    Country:
    United Kingdom
    FYI - the PDF been updated to include the Above (download it here)

    Sorry, but I've changed the version number again - It now has the date instead...
    So that you know it contains everything up to the corresponding Foxi4 post

    EDIT: oopps - Just realised I've made a small mistake - the PDF is dated '11-Jun-2012', but the 'version' on filetrip is '30-Jun-2012'
    The PDF contains everything up to the 30th & not the 11th.... Sorry
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    Hey, no hurry! I'm already greatful that you're maintaining the offline version, take your time with tidying up things.
    • Member

    Skye07 New Member

    Member Since:
    Dec 24, 2006
    Message Count:
    450
    Location:
    Belgium
    Country:
    Belgium
    Could you elaborate a bit on how to add the library into a new DS project? Copy paste the template folder into the source folder of your project? Or paste into the solution explorer, ...

    I don't immediately see what I need to do.

    Never mind, found it (also in the documentation of the lib). Thanks for the tutorials!

    Also, how do you make a .map and a .pal file? When I run the Convert_Backgrounds.bat file it only makes an .img file. I checked the bat file and the parameters and looked them up and it really should make pal and map files, any idea why it doesn't?

    The bmp file has to have 8bit color depth, mine didn't, it works now.


    And another question: How would you go and write code to check if 2 buttons are pushed at the same time? (E.g. for diagonal movement)
    I gave it a go myself but I'm not sure if this is the best way to do it.
    • Reporter

    Foxi4 Gravity is for weaklings.

    Member Since:
    Sep 13, 2009
    Message Count:
    17,245
    Location:
    Gaming Grotto
    Country:
    Poland
    Have a look at the Operators chapter, you can connect two Conditions using the operator AND (&&). ;)

    Example:

    Code:
    //A quick wrapper over the two buttons, serves no real purpose other than clarifying the code and it can be omitted.
    //Declarations
    s16 Up;
    s16 Right;
    
    //Somewhere within while, after scanning for keys
    Up = keysHeld() & KEY_UP;
    Right = keysHeld() & KEY_RIGHT;
    
    
    if (Up && Right){
    ...Statements...
    }
    This is just an example though, I would do it slightly differently - all you really have to do to apply full diagonal movement is to make the D-Pad add or decrease the X and Y and the sprite or whatever else you'd want to move will move diagonally unless you restrict it:


    Code:
    //A quick wrapper over the two buttons, serves no real purpose other than clarifying the code and it can be omitted.
    //Declarations
    s16 Up;
    s16 Right;
    
    //Somewhere within while, after scanning for keys
    Up = keysHeld() & KEY_UP;
    Right = keysHeld() & KEY_RIGHT;
    
    if (Up){
    //Pre-set your speed or define it if it's static.
    Sprite.Y = Sprite.Y - Speed;
    }
    if (Right){
    //Pre-set your speed or define it if it's static.
    Sprite.X = Sprite.X + Speed;
    }
    If both are pressed, the movement will be diagonal as both conditions are returned true, thus both variables are updated.

    The solution you proposed is good as well - there are really as many solutions to your question as many there are programmers - simply pick the one you feel comfortable with. :)

    I personally like to declare the variables I will use with my keys in a separate header file and use shortened forms rather than the ones proposed by libnds and I keep them in a structure of bools just so that the names and outputs are clear, but hey! That's me.

    Also, sorry for not replying for such a long time - I was away on a business trip for the last week or so. Indeed, tiled backgrounds have to be 8-Bit since each map only supports 256 colours unless you use Extended Palettes Mode which will be covered later or if you use Raster backgrounds which are sort of wasteful and should be avoided unless they're used for splash screens and such.
    • Newcomer

    frezziii New Member

    Member Since:
    Aug 14, 2012
    Message Count:
    10
    Country:
    Germany
    Could you put in an example for the touchscreen use? Like:

    if(Touchscreen is touched on a location)
    {
    This will be happen
    }

    Your tutorial really good! Keep going ;)

Share This Page