gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > The Kirby Revolution

#102563 - Kirby - Fri Sep 15, 2006 8:32 am

Hi! I'm Kirby! I am making a new platformer game called The Kirby Revolution. I am just making the engine for it right now, and I have to say, it's pretty good. If anybody could tell me how to upload a demo here, I would.

But the one problem I am having is creating enemies. I want to be able to create a base enemy with the code to randomly walk around and jump (a big problem), and scroll properly (another big problem).

I'm currently using thegamefreak0134's state code to put together the character (don't worry, I did put all of your copyright stuff in), but it's not really working that great.

If anybody knows how to add characters in the game in a EASY way, please tell me. (I'll add your name to the credits list!)
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!


Last edited by Kirby on Thu Sep 28, 2006 6:26 am; edited 1 time in total

#102585 - Kirby - Fri Sep 15, 2006 1:39 pm

The error with the character thing is that I have a base character. You asign CharaData to it. See thegamefreak0134's State code. Download it at http://thegamefreak0134.googlepages.com/SmashBrosAdvance.zip.

These are the errors:

Code:
1>main.c: In function `stateUpdate':
1>main.c(1063):subscripted value is neither array nor pointer
1>main.c(1068):subscripted value is neither array nor pointer
1>main.c(1069):subscripted value is neither array nor pointer
1>main.c(1075):subscripted value is neither array nor pointer
1>main.c(1076):subscripted value is neither array nor pointer
1>main.c(1078):subscripted value is neither array nor pointer
1>main.c(1079):subscripted value is neither array nor pointer
1>main.c(1081):subscripted value is neither array nor pointer
1>main.c(1082):subscripted value is neither array nor pointer
1>main.c(1084):subscripted value is neither array nor pointer
1>main.c(1085):subscripted value is neither array nor pointer


Once again, when someone tells me how to upload a demo to the site, I will post a link to it, then somebody can download it and check my source code. Thanx!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!


Last edited by Kirby on Sat Sep 16, 2006 12:20 pm; edited 1 time in total

#102596 - sgeos - Fri Sep 15, 2006 3:10 pm

Kirby wrote:
I don't really know whether or not this should go into Beginners or Coding

They blur. Do you think you are a beginner, or do you feel you can work out "simple" problems on your own? This is a language syntax problem. I think that you should probably be posting in beginners.

At any rate, you should be able to solve you own problem. Read the error messages. Figure out what they mean, and then fix the problems. Here is how:
"subscripted value is neither array nor pointer"

Do you know what a subscripted value is?
Do you know what an array is?
Do you know what a pointer is?
Do you know how/why/when pointers and arrays can be interchanged?
Do you know how/when to properly subscript pointers and arrays?

Go to lines 1063, 1068, etc. You are dereferencing something the wrong way... 11 times (11 errors). This is probably the same (human) logic error 11 times over, so if you figure out how to properly subscript your value (or whatever you really want to be do) once, then you can fix this problem everywhere.

If you want help, post line 1063. Above it, post the declaration of every variable that appears in in line 1063. Also mention what you have tried to do to fix the problem, and what you don't understand.

-Brendan

#102601 - Kirby - Fri Sep 15, 2006 3:33 pm

sgeos wrote:
If you want help, post line 1063. Above it, post the declaration of every variable that appears in in line 1063. Also mention what you have tried to do to fix the problem, and what you don't understand.


Code:
 for (i=0;i<NumCharas;i++) {
      Characters[i].laststate = Characters[i].state;
      Characters[i].frame = Characters[i].CharaFrames[Characters[i].CharaStateFrames[Characters[i].state] + Characters[i].stateframe]; //This is line 1063
      CPUPlayerPhysics(i);
      CPUCollideCheck(i,Characters[i].AI);
      Characters[i].stateframe = Characters[i].stateframe + 1;
      CPURunFunc(i,Characters[i].state,Characters[i].AI);
      if (Characters[i].stateframe == Characters[i].CharaStateLength[Characters[i].state]) {
         Characters[i].state = Characters[i].CharaStateJumps[Characters[i].state];
         Characters[i].stateframe = 0;
      }
      if (Characters[i].state != Characters[i].laststate)
      {
         //Update Physics
         if (Characters[i].CharaXspeedEnabled[Characters[i].state] == 1) {
            Characters[i].xspeed = Characters[i].CharaXspeed[Characters[i].state];
         }
         if (Characters[i].CharaYspeedEnabled[Characters[i].state] == 1) {
            Characters[i].yspeed = Characters[i].CharaYspeed[Characters[i].state];
         }
         if (Characters[i].CharaXaccelEnabled[Characters[i].state] == 1) {
            Characters[i].xaccel = Characters[i].CharaXaccel[Characters[i].state];
         }
         if (Characters[i].CharaYaccelEnabled[Characters[i].state] == 1) {
            Characters[i].yaccel = Characters[i].CharaYaccel[Characters[i].state];
         }
      }
   }


Characters is an array of character:

Code:
#define AI_DUMB 0
#define AI_BUMP 1
#define AI_FOLLOW 2
#define AI_AVOID 3
#define AI_BOSS 4
#define AI_RACE 5

typedef struct Character
{
   s32 x;
   s32 y;
   s32 drawX;
   s32 drawY;
   u8 state; //Doubt seriously we'll need bigger numbers here than
   u8 laststate;
   u8 stateframe;
   u8 frame; // like, 50 maybe. Course, you never know...
   u16 xblock;
   u16 yblock;
   u8 xblockpos;
   u8 yblockpos;
   s8 xspeed;
   s8 yspeed;
   s8 xaccel;
   s8 yaccel;
   u8 land;
   u8 bounce;
   u8 CharaData;
   u32 CharaFrames;
   u32 CharaStateFrames;
   u32 CharaStateLength;
   u32 CharaStateJumps;
   u32 CharaXspeed;
   u32 CharaXspeedEnabled;
   u32 CharaYspeed;
   u32 CharaYspeedEnabled;
   u32 CharaXaccel;
   u32 CharaXaccelEnabled;
   u32 CharaYaccel;
   u32 CharaYaccelEnabled;
   u8 visible;
   u32 memLoc;
   u8 teleCheck;
   u8 HP;
   u8 power;
   u16 map;
   u16 OAMnum;
   u16 attr2;
   u16 Anims;
   u16 AI;
   u8 *charaAnim;
}Character;


i is a number. (This is being done in a for loop, so that editing is done to all of the characters)

Code:
u32 waddledeeFrames[] = { 6 , 6 , 6 , 7 , 7 , 5 , 5 , 4 , 4, 2, 2 , 2, 3, 3, 1, 1, 0, 0, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8 ,8 ,8};
u32 waddledeeStateFrames[] = { 0 , 1 , 8 , 9, 17, 21, 25, 29};
u32 waddledeeStateLength[] = { 1 , 8 , 1 , 8,  5,  5,  5,  5};
u32 waddledeeStateJumps[] = { 0 , 1 , 2 , 3 , 5 , 5 , 7 , 7 };
u32 waddledeeXspeed[] = {0,4,0,-4,0,0,0,0};
u32 waddledeeXspeedEnabled[] = { 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 };
u32 waddledeeYspeed[] = {4,4,4,4,-16,0,-16,0};
u32 waddledeeYspeedEnabled[] = { 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 };
u32 waddledeeXaccel[] = {0,0,0,0,0,0,0,0};
u32 waddledeeXaccelEnabled[] = { 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 };
u32 waddledeeYaccel[] = {0,0,0,0,0,1,0,1};
u32 waddledeeYaccelEnabled[] = { 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 };


This is the character init code.

Code:
void InitCharas() {
   InitWaddleDee();

   //Near Kirby's House
   SetUpChara(0,WaddleDee,50 * 32,48 * 32,3,0x06010800,3,64,AI_BUMP);
}

void SetUpChara(u16 edit, Character base, u16 xa, u16 ya, u16 mapa, u16 memLoca, u16 OAMnuma, u16 attr2a, u16 AImode) {
   Characters[edit].attr2 = attr2a;
   Characters[edit].bounce = base.bounce;
   u16 i;
   /*for (i=0;i<base.Anims;i++) {
      Characters[edit].charaAnim[i] = base.charaAnim[i];
   }*/
   Characters[edit].charaAnim = base.charaAnim;
   Characters[edit].CharaData = base.CharaData;
   Characters[edit].CharaFrames = base.CharaFrames;
   Characters[edit].CharaStateFrames = base.CharaStateFrames;
   Characters[edit].CharaStateJumps = base.CharaStateJumps;
   Characters[edit].CharaStateLength = base.CharaStateLength;
   Characters[edit].CharaXaccel = base.CharaXaccel;
   Characters[edit].CharaXaccelEnabled = base.CharaXaccelEnabled;
   Characters[edit].CharaXspeed = base.CharaXspeed;
   Characters[edit].CharaXspeedEnabled = base.CharaXspeedEnabled;
   Characters[edit].CharaYaccel = base.CharaYaccel;
   Characters[edit].CharaYaccelEnabled = base.CharaYaccelEnabled;
   Characters[edit].HP = base.HP;
   Characters[edit].map = mapa;
   Characters[edit].memLoc = memLoca;
   Characters[edit].OAMnum = OAMnuma;
   Characters[edit].x = xa;
   Characters[edit].y = ya;
   Characters[edit].AI = AImode;
}


I don't see what is wrong with using an array as a subscript. I've tried adding & or (Character *) in front of Characters[i].????. I've also tried creating local variables with Characters[i].????[?] in it. If there is an easier way of doing this, then please tell me.

sgeos wrote:
Kirby wrote:
I don't know if this belongs in Beginners or Coding


This is my first platforming game, but I thought it was more advanced to be doing an AI animated character!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102604 - Kirby - Fri Sep 15, 2006 3:54 pm

Just to note...

This thread is for my Kirby game, not the "syntax error". I just added it here because it was convienent. The Kirby game is Coding. The "syntax error" could be Beginner...

EDIT: Sorry about the double-posting...
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102614 - poslundc - Fri Sep 15, 2006 5:04 pm

You use the array operator [] on CharaFrames and CharaStateFrames.

Now look at your definition of a Character struct. Is the data type of CharaFrames or CharaStateFrames an array data type?

Dan.

#102615 - poslundc - Fri Sep 15, 2006 5:07 pm

And while there are other things in your code that are going to prove difficult down the road, I will point out to you now that unless you put a const qualifier in front of each of those array declarations, they will go into fast RAM (which you only have 32K of) and you will quickly run out of it.

Dan.

#102641 - Kirby - Fri Sep 15, 2006 9:10 pm

poslundc wrote:
You use the array operator [] on CharaFrames and CharaStateFrames.

Now look at your definition of a Character struct. Is the data type of CharaFrames or CharaStateFrames an array data type?

Dan.


That's all very well, except that when I do that, I get about eleven errors saying "Flexible array not at end of structure".

poslundc wrote:
And while there are other things in your code that are going to prove difficult down the road, I will point out to you now that unless you put a const qualifier in front of each of those array declarations, they will go into fast RAM (which you only have 32K of) and you will quickly run out of it.

Dan.


Remember, this is to be a base characacter so that it has to be assigned a maximum, and any value at all.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102650 - tepples - Fri Sep 15, 2006 11:00 pm

Kirby wrote:
That's all very well, except that when I do that, I get about eleven errors saying "Flexible array not at end of structure".

"Flexible array" means one declared [] or [0]. You need a positive number of elements in an array that is a struct member, or you need to use a pointer.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#102669 - Kirby - Fri Sep 15, 2006 11:47 pm

tepples wrote:
"Flexible array" means one declared [] or [0]. You need a positive number of elements in an array that is a struct member, or you need to use a pointer.

The reason I'm using "Flexible arrays" is because
Kirby wrote:
Remember, this is to be a base characacter so that it has to be assigned a maximum, and any value at all.

That means that each item is an array with a number of items that depends on the character!
But the pointer thing sounds like a good idea... I might try that... And anyway, I seem to be doing that with charaAnim. Thanx. I'll tell you if it works.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102692 - Kirby - Sat Sep 16, 2006 6:40 am

Kirby wrote:
That means that each item is an array with a number of items that depends on the character!
But the pointer thing sounds like a good idea... I might try that... And anyway, I seem to be doing that with charaAnim. Thanx. I'll tell you if it works.

Guess what. It works! Tada! But... not entirly.
    First of all, the pointer things works!
    But there are still some errors such as:
    Waddle Dee loops through his animation faster than light. (This'd work better if I had access to a PROPER State Editor, but now I'm going to make my own)
    Waddle Dee floats at the top of the screen when he's supposed to be offscreen. But I know the solution to this problem.
    The base character currently only supports typing out each memcpy for every character to be copied to memory. I have to intergrate support for looping through EVERY character and copying them.

Thank you for everybody who made this possible. But first, a few requests:

    Does anybody know of a GOOD bmp2gba program? I've tried Img2Gba and Bmp2Spr which both return null data, and I've written my own, which worked once, but then made the second picture look a little off. This is the code for the program, which I call Bmp2Hex: (it's in BASIC)
Code:
Module Module1

    Sub Main()
        Dim tilesMode As Boolean
        Console.WriteLine("Bmp2Hex (c) 2006 by Kirby")
        Console.WriteLine("Transforms a bitmap file in a C file ready for programming use")
        Console.WriteLine()
        Try
            Select Case My.Application.CommandLineArgs(0)
                Case ""
                    ShowHelp()
                Case "-?"
                    ShowHelp()
                Case "-p"
                    If My.Application.CommandLineArgs.Count - 1 = 0 Then
                        ShowPrefHelp()
                    Else
                        Select Case My.Application.CommandLineArgs(1)
                            Case "-i"
                                If My.Application.CommandLineArgs.Count - 1 = 2 Then
                                    My.Settings.Input = My.Application.CommandLineArgs(2)
                                    End
                                Else
                                    IncorrectParas()
                                End If
                            Case "-o"
                                If My.Application.CommandLineArgs.Count - 1 = 2 Then
                                    My.Settings.Output = My.Application.CommandLineArgs(2)
                                    End
                                Else
                                    IncorrectParas()
                                End If
                        End Select
                    End If
                Case Else
                    If My.Application.CommandLineArgs.Count - 1 = 2 Then
                        If My.Application.CommandLineArgs(2).Trim = "-t" Then
                            tilesMode = True
                        End If
                    End If
                    Select Case My.Application.CommandLineArgs.Count - 1
                        Case Is >= 1
                            Transform(My.Application.CommandLineArgs(0), My.Application.CommandLineArgs(1), tilesMode)
                        Case 0
                            Transform(My.Application.CommandLineArgs(0), My.Settings.Output, tilesMode)
                    End Select
                    IncorrectParas()
            End Select
        Catch
            ShowHelp()
        End Try
    End Sub

    Public Sub ShowHelp()
        Console.WriteLine("Arguments:")
        Console.WriteLine("[Input] [Output] -- Creates a .h file from INPUT and saves at OUTPUT")
        Console.WriteLine("[Input] -- Creates a .h file from INPUT and saves it in the folder specified by -p")
        Console.WriteLine("[Input] [Output] -t -- Compile as a tileset")
        Console.WriteLine("-? -- Shows this screen")
        Console.WriteLine("-p -- Shows preference help")
        End
    End Sub

    Public Sub ShowPrefHelp()
        Console.WriteLine("-p [Switches]:")
        Console.WriteLine("-i -- Changes the default input folder. Used when no input folder is specified, and the file isn't in the current directory")
        Console.WriteLine("-o -- Changes the default output folder. Used when no output folder is specified")
        End
    End Sub

    Public Sub IncorrectParas()
        Console.WriteLine("Incorrect paramanters")
        End
    End Sub

    Public Sub Transform(ByVal input As String, ByVal output As String, ByVal doTiles As Boolean)
        If IO.File.Exists(input) = False Then
            If IO.File.Exists(".\" & input) = False Then
                If IO.File.Exists(My.Settings.Input & input) = False Then
                    IncorrectParas()
                Else
                    input = My.Settings.Input & input
                End If
            Else
                input = ".\" & input
            End If
        End If
        If output.EndsWith("\") Then
            output &= input.Remove(0, input.LastIndexOf("\") + 1)
        End If
        If output.EndsWith(".bmp") Then
            output.Replace(".bmp", ".h")
        ElseIf Not output.EndsWith(".h") Then
            output &= ".h"
        End If
        Dim result As String
        Dim temp As New Bitmap(input)
        Dim height As Integer = temp.Height
        Dim width As Integer = temp.Width
        Dim sprdata(height * width) As Byte
        Dim numcolors As Integer = 1
        Dim colors(255) As Long
        Dim b, c As Integer
        Dim x, y As Integer
        Dim g As Color
        Dim tSq As Integer = 8
        Dim tMX, tMY, tX, tY As Integer
        If doTiles = False Then tSq = 1
        tMX = (width / tSq)
        tMY = (height / tSq)
        colors(0) = ColorTranslator.ToOle(temp.GetPixel(x, y))
        'For y = 0 To tSq - 1
        'For x = 0 To tSq - 1
        'b = ColorTranslator.ToOle(temp.GetPixel(x, y))
        'g = ColorTranslator.FromOle(b)
        'For c = 0 To numcolors
        'If colors(c) = b Then GoTo inst
        'Next
        'colors(numcolors) = b
        'c = numcolors
        'numcolors += 1
        'inst:           sprdata(x + (y * width)) = c
        'Next
        'Next
        For tY = 0 To tMY - 1
            For tX = 0 To tMX - 1
                For y = (tY * tSq) To ((tY * tSq) + tSq) - 1
                    For x = (tX * tSq) To ((tX * tSq) + tSq) - 1
                        b = ColorTranslator.ToOle(temp.GetPixel(x, y))
                        g = ColorTranslator.FromOle(b)
                        For c = 0 To numcolors
                            If colors(c) = b Then GoTo ins
                        Next
                        colors(numcolors) = b
                        c = numcolors
                        numcolors += 1
ins:                    sprdata(x + (y * width)) = c
                    Next
                Next
            Next
        Next
        Dim fn As String
        If input.Contains("\") Then
            fn = input.Remove(0, input.LastIndexOf("\") + 1).Replace(".bmp", "")
        Else
            fn = input.Replace(".bmp", "")
        End If
        result = "//Output from Bmp2Hex for Kirby Platformer Advanced Project" & _
        vbCrLf & _
        vbCrLf & "#define " & fn & "_WIDTH " & width & _
        vbCrLf & "#define " & fn & "_HEIGHT " & height & _
        vbCrLf & _
        vbCrLf & "const u16 " & fn & "Data[" & width * height & "] = {"
        For tY = 0 To tMY - 1
            For tX = 0 To tMX - 1
                For y = (tY * tSq) To ((tY * tSq) + tSq) - 1
                    result &= vbCrLf
                    For x = (tX * tSq) To ((tX * tSq) + tSq) - 1 Step 2
                        result &= "0x" & IIf(Len(Hex(sprdata(x + (y * width)))) = 1, "0", "") & Hex(sprdata(x + (y * width))) & IIf(Len(Hex(sprdata(x + (y * width) + 1))) = 1, "0", "") & Hex(sprdata(x + (y * width) + 1)) & ", "
                    Next
                Next
                result &= vbCrLf
            Next
        Next
        result = result.Remove(result.LastIndexOf(", "), 2) & _
        vbCrLf & "};" & _
        vbCrLf & _
        vbCrLf & "const u16 " & fn & "Palette[256] = {" & _
        vbCrLf
        For c = 0 To numcolors - 1
            result &= "0x" & Hex(pc2gba(colors(c))) & ", "
        Next
        For c = numcolors To 255
            result &= "0x0" & ", "
        Next
        result = result.Remove(result.LastIndexOf(", "), 2) & _
        vbCrLf & "};" & _
        vbCrLf
        FileOpen(1, output, OpenMode.Output)
        WriteLine(1, result.ToCharArray)
        FileClose(1)
        Console.WriteLine("File transform complete")
        End
    End Sub

    Public Function pc2gba(ByRef color As Long) As Integer
        Dim g, r, b As Long
        r = color Mod 256
        g = (color \ 256) Mod 256
        b = (color \ 65536) Mod 256
        pc2gba = r \ 8 + (g \ 8) * 32 + (b \ 8) * 32 * 32
    End Function

End Module
As I said before, I used this program to translate a tileset to hex, and it worked, but the second tileset I tried had the 3'rd and 4'th pixels switched, and 5'th and 6'th pixels switched on every row. Does anybody know why?
Second, does anybody know where to get good Kirby tilesets? I'm currently drawing mine, and they look... quite frankly horrible.

_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102713 - sgeos - Sat Sep 16, 2006 11:07 am

Kirby wrote:
Does anybody know of a GOOD bmp2gba program?

This question really belongs in another thread. Probably under "graphics".

I think the gimp can export to C code. The gd library is fantastic for graphics manipulation, if you want to write your own tool.

-Brendan

#102718 - Kirby - Sat Sep 16, 2006 11:52 am

sgeos wrote:
This question really belongs in another thread. Probably under "graphics".

Where should this topic actually be? I want it to be an overall topic of the coding, design, gfx, sfx, and... everything else about The Kirby Revolution, sort of like thegamefreak0134's Smash Bros Advanced topic.
sgeos wrote:
I think the gimp can export to C code. The gd library is fantastic for graphics manipulation, if you want to write your own tool.

What type of library is this we're talking about? I'm using Visual Studio 2005 Express BASIC to write the conversion program. I think I can use .OCX and .DLL.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102721 - Kirby - Sat Sep 16, 2006 12:19 pm

This time I'm doing the right thing and posting stuff in the right place. I have made a new topic about an E-Reader-releated minigame I want to put in my game. You can access it here.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#102811 - Kirby - Sun Sep 17, 2006 4:58 am

OK, I have a problem (like always)...

So, as I said before, I'm trying to make a BASE character that works for all characters. So I wrote a little function that (is supposed to) update(s) the character's picture:
Code:
void frameUpdate() {
   u16 i;
   for (i=0;i<NumCharas;i++) {
      if (Characters[i].map == curmap) {
         memcpy( (u16 *)Characters[i].memLoc, &Characters[i].charaAnim[Characters[i].frame], 256 );
      }
   }
}

But, currently, it's not working, so I wrote a little thing to take it's place while I'm testing with Waddle Dee:
Code:
void frameUpdate() {
   memcpy( (u16 *)0x6010800, waddledeeAnim[Characters[0].frame], 256);
}

I don't see why it doesn't work. I have posted the definition of Character above. I've changed Characters.h and waddledeeStates.h:

Characters.h
Code:
#include "gba.h"
#include "Characters\Kirby.h"
#include "Characters\WaddleDee.h"
#include "Characters\blankSprite.h"
#include "Characters\AI_Bump.h"

#define NumCharas 1

void InitCharas() {
   InitWaddleDee();

   //Near Kirby's House
   SetUpChara(0,WaddleDee,48*32,46*32,3,0x06010800,3,64,AI_BUMP);
}

void SetUpChara(u16 edit, Character base, u16 xa, u16 ya, u16 mapa, u16 memLoca, u16 OAMnuma, u16 attr2a, u16 AImode) {
   Characters[edit].attr2 = attr2a;
   Characters[edit].bounce = base.bounce;
   u16 i;
   /*for (i=0;i<base.Anims;i++) {
      Characters[edit].charaAnim[i] = base.charaAnim[i];
   }*/
   Characters[edit].charaAnim = base.charaAnim;
   Characters[edit].CharaData = base.CharaData;
   Characters[edit].CharaFrames = base.CharaFrames;
   Characters[edit].CharaStateFrames = base.CharaStateFrames;
   Characters[edit].CharaStateJumps = base.CharaStateJumps;
   Characters[edit].CharaStateLength = base.CharaStateLength;
   Characters[edit].CharaXaccel = base.CharaXaccel;
   Characters[edit].CharaXaccelEnabled = base.CharaXaccelEnabled;
   Characters[edit].CharaXspeed = base.CharaXspeed;
   Characters[edit].CharaXspeedEnabled = base.CharaXspeedEnabled;
   Characters[edit].CharaYaccel = base.CharaYaccel;
   Characters[edit].CharaYaccelEnabled = base.CharaYaccelEnabled;
   Characters[edit].HP = base.HP;
   Characters[edit].map = mapa;
   Characters[edit].memLoc = memLoca;
   Characters[edit].OAMnum = OAMnuma;
   Characters[edit].x = xa;
   Characters[edit].y = ya;
   Characters[edit].AI = AImode;
   Characters[edit].animSpeed = base.animSpeed;
   /*if (AImode == AI_DUMB) {
      
   }
   if (AImode == AI_BUMP) {
      Characters[edit].CollideBlock(u16) = AI_BUMP_CollideBlock(u16);
      Characters[edit].RunFunc(u16) = AI_BUMP_RunFunc(u16);
   }*/
}

void KframeUpdate() {
   memcpy( (u16 *)Kirby.memLoc, kirbyAnim[Kirby.frame], 1024 );
}

void frameUpdate() {
   memcpy( (u16 *)0x6010800, waddledeeAnim[Characters[0].frame], 256);
}

waddledeeStates.h
Code:
#include "..\..\gba.h"

u32 waddledeeFrames[] = {4,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,5,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,1};
u32 waddledeeStateFrames[] = {0,1,16,17,32,41,50,51};
u32 waddledeeStateLength[] = {1,16,1,16,10,10,1,1};
u32 waddledeeStateJumps[] = {0,1,2,3,6,7,6,7};
u32 waddledeeXspeed[] = {0,4,0,-4,0,0,0,0};
u32 waddledeeXspeedEnabled[] = {1,1,1,1,0,0,0,0};
u32 waddledeeYspeed[] = {4,4,4,4,-16,-16,0,0};
u32 waddledeeYspeedEnabled[] = {1,1,1,1,1,1,0,0};
u32 waddledeeXaccel[] = {0,0,0,0,0,0,0,0};
u32 waddledeeXaccelEnabled[] = {1,1,1,1,0,0,1,0};
u32 waddledeeYaccel[] = {0,0,0,0,0,0,1,1};
u32 waddledeeYaccelEnabled[] = {1,1,1,1,1,0,1,1};

const unsigned char (*waddledeeAnim[14]) [] = {
&waddledeefall00_Bitmap,
&waddledeefallright00_Bitmap,
&waddledeejump00_Bitmap,
&waddledeejumpright00_Bitmap,
&waddledeestand00_Bitmap,
&waddledeestandright00_Bitmap,
&waddledeewalk01_Bitmap,
&waddledeewalk02_Bitmap,
&waddledeewalk03_Bitmap,
&waddledeewalk04_Bitmap,
&waddledeewalkright01_Bitmap,
&waddledeewalkright02_Bitmap,
&waddledeewalkright03_Bitmap,
&waddledeewalkright04_Bitmap
};

Which, by the way, I made in my HOMEMADE STATE EDITOR!!!!

So... if anybody know why it won't update (or, actually, spots any other little errors in the code), please tell me.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#103304 - ProblemBaby - Wed Sep 20, 2006 9:32 pm

I aint sure but I think...that you cant use memcpy to write to VRAM since it copies one byte a time.

#103324 - Cearn - Wed Sep 20, 2006 11:08 pm

ProblemBaby wrote:
I aint sure but I think...that you cant use memcpy to write to VRAM since it copies one byte a time.
memcpy will work fine as long as you obey the rules.

I am a little suspicous of this definition:
Kirby wrote:
Code:
const unsigned char (*waddledeeAnim[14]) [] = { ... };

If I'm parsing this correctly, This is an array of arrays of pointers. Or an array of pointers to arrays, I'm not sure. What exactly are you after here? And what is the exact error you're getting? Corrupted graphics? An erased frame?

EDIT:

One definite problem and one inefficiency here:
Code:
void SetUpChara(u16 edit, Character base, u16 xa, u16 ya, u16 mapa, u16 memLoca, u16 OAMnuma, u16 attr2a, u16 AImode)

memLoca is 16bit. Addresses generally aren't. Please limit your use of non-word datatypes to structs or globals, the one place where they don't cost you anything. Also, it's not a good idea to pass whole structs to functions. Use 'Character *base' and change the periods to '->', or in C++ use references: 'Character &base'.

#103518 - madmax - Fri Sep 22, 2006 4:26 pm

First of Cearn your right to be suspicious about the array with a * before it thats not allowed. An array is just a special way of accessing data that is sequentialy allocated in a block so when you declare an array your really just using a pointer each time you request a new index pointer arithmetic is done in the background to work out where the element you want is. You can actually even use the array [] on pointers that you have not declared as an array (altough they better have been allocated as one or you'll get memory thats either invalid or used for something else).

However back to talking about designing characters or entitys. I think OOP should be used for all character related coding. I don't know much about the state system your using, but I'd advise you to try creating your own Enemy classes instead of using someone else's code because you will learn more and have greater control over it. Nearly all characters on your system will share lots of similaritys which just make sense to program as objects.

In my games I start with a sprite that is an base class for any displayable object in the game. Basic directional movement, sprite animation is all done here and it gives me a usefull abstraction I can use for all other objects.

Then I would subclass sprite into the objects I want (I actually don't do it exactly like this because of how my sprite manager system works in that I use composition and a sprite interface) for instance if you have a soldier then you start with sprite then create creature then soldier thats how your inheritance hierarcy goes. The creature class is more advanced than sprite and knows how to do things like move in a line also it has a live state so we know if it has been killed another thing would be collision handeling creature knows what to do when he collides with basic things, also there is basic map navigation. Soldier is the next specialisation of creature, soldier has a weapon (which is actully another object) that he can fire at other enemys, knows what to do if he's hit by bullets and soldiers also have collision avoidance ai so they don't keep bumping into each other (altough you might argue a basic creature can do that too).

The diffrence from doing it all in once class is that the behaviour is compartmentalised (I'm sure thats spelt wrong). Also if I needed to make a fox I wouldn't have to just create a specilisation of creature and program the functionality in. Because all my sprite and basic movement stuff is already done :)

However there is a draw back to all this and that is that the behaviours are coupled to the classes of object that implement them. So if I wanted to give my fox the collision avoidance code from soldier I have to go into soldier and copy it to fox. I it this way only on GBA because C++ is slower than C and lots of objects eat up memory, however on other systems a better way of doing it is compartmentising ever behaviour in it's own class you need to have a common interface for every object the behaviour could be applied to. You could also use a more dynamic system to forward certain calls to other classes that know what to do with them if they exist in the object these special handeler classes get called when soldiers collide for instance to resolve the collision between soldiers without the actual soldier code knowing what to do, but this gets complex.

However you do it your aim is to have each diffrent behaviour in a unique class so you can easily add it to new entitys with minimum code repetition. This way you get fewer errors because all the behaviours are in their own class that have all been tested throughly. You can even make new behaviours by combining the base behaviours classes so that you end up with inheritance hierarcy between your behaviours. However there is one problem with this that must be garded agasn't and thats direct coupling between a behaviour and the classes that use it. For instance you want to make behaviours that can work for all entitys not just a few spesific entitys in these cases you should use a abstract handeler class that lays out what sort of actions need to be taken but leaves the implementation to it's sub classes. It's okay for these handlers to be coupled to behaviours and the entitiys because the handeler can always be replaced with new ones yet the entitys and behaviours will not need changing.

Well thats how I do things anyway
_________________
John Noon

#103539 - thegamefreak0134 - Fri Sep 22, 2006 6:51 pm

I can probably tell you what you\\\'re doing wrong, if you\\\'re still using my editor. My state system is designed to work (currently) with one character only, namely \\\"Player1\\\". I am actually in the process of allowing it to process multiple characters, but since this has not been done yet by me, you\\\'ll have to do it.

I might need to go ahead and give you my state editor (it\\\'s GUI and exports the offensive code you\\\'re having issues with for you). If you have the smallest trinket in the definitions defined wrong, the state engine will have issues. Then, I would recommend re-making the characters and modifying the state process function to take an input known as \\\"Player1\\\". Pass it the info for every character, including the proper pointers to all of the state info and such, and it can be used to process more than one chracter with more than one set of data.

If you\\\'d like however, I\\\'ll go ahead and mod it this weekend and send you the corrected code as well as the editor and instruction both for using it and for making the editor stay happy. (It was hand tailored ot my useage, and doesn\\\'t expect a lot of wierd user-firendly stuff) I really don\\\'t like the idea of you keeping it in there, but if you want to use it for now that\\\'s fine. It sholdn\\\'t be too hard to make your own once you have the main concept down though.

Let me know if this interests you.

-thegamefreak

PS: PM me, don\\\'t post, as I don\\\'t check this section too often.

EDIT: OH yeah, my code also includes a frame update function, which will need some explaining, but works for most purposes. I\'ll explain that if you need me to, it should get your pictures working.
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#103872 - Kirby - Mon Sep 25, 2006 2:48 am

I've actually changed around the state system now, so it supports multiple AI characters, but the one problem I'm having is having them scroll correctly around the screen. Kirby moves around fine, but other characters are a few pixels off, and because Kirby moves quickly, the difference is very noticable.

Second, waddledeeAnim is an array of pointers to arrays. I'm not actually having any problem with it, though. All character physics are working great, the only problem I'm having, is that I can't start Visual C++ 2005 Express Edition any more, and that one of the tilesets look weird. Imagine this:

Say one tile is supposed to be like this:

1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8

But it's coming out like this:

2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7
2,1,4,3,6,5,8,7

And I tried swapping bits in my code but it makes it even weirder.

Third, in a makefile (i'm new 2 makefiles), how do you get it to recompile a file when a header file it uses is changed, because I have to run make clean too much. I use DevKitAdv and Visual C++ 2005 Express Edition (which, as I said before, I cannot get to open)
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!


Last edited by Kirby on Thu Sep 28, 2006 6:34 am; edited 1 time in total

#103876 - tepples - Mon Sep 25, 2006 3:18 am

If you're getting pixels ordered 2, 1, 4, 3, 6, 5, 8, 7, within a tile, then you accidentally chose Sega Genesis (4-bit packed pixels, big-endian) mode instead of GBA/DS 16-color (4-bit packed pixels, little-endian) mode.

Here's how to specify multiple prereqs in a file:
Code:
gimmicks.o: gimmicks.c lj.h
<TAB>$(CC) $(CFLAGS) $< -o $@

_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#103892 - Kirby - Mon Sep 25, 2006 5:41 am

tepples wrote:
If you're getting pixels ordered 2, 1, 4, 3, 6, 5, 8, 7, within a tile, then you accidentally chose Sega Genesis (4-bit packed pixels, big-endian) mode instead of GBA/DS 16-color (4-bit packed pixels, little-endian) mode.

First, Sega/Gensis mode what? What on EARTH does that mean? I'm using my own tileset compiler here!
Second, I'm using one rule to compile all of my .c's (there are alot of them), and I want to know how to get a list of dependencies for that file.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#103924 - tepples - Mon Sep 25, 2006 1:55 pm

Kirby wrote:
tepples wrote:
If you're getting pixels ordered 2, 1, 4, 3, 6, 5, 8, 7, within a tile, then you accidentally chose Sega Genesis (4-bit packed pixels, big-endian) mode instead of GBA/DS 16-color (4-bit packed pixels, little-endian) mode.

First, Sega/Gensis mode what? What on EARTH does that mean? I'm using my own tileset compiler here!

Then you may have a defect in your own tileset compiler. I must have been thinking of a converter that I wrote that supports NES, Super NES, Game Boy, Virtual Boy, Genesis, GBA 4-bit, and GBA 8-bit, as well as several other formats.

Quote:
Second, I'm using one rule to compile all of my .c's (there are alot of them), and I want to know how to get a list of dependencies for that file.

The easy way is to switch to wintermute's makefile. If you want to do it yourself for understanding, try the same article that wintermute read.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#103993 - Mucca - Mon Sep 25, 2006 9:27 pm

madmax wrote:
Quote:
Then I would subclass sprite into the objects I want


What if your entities require usage of more than one sprite? Compositing entities just to have an extra sprite will duplicate logically singular data and behaviour. Also what if you need objects with no visual representation? Eg a zone in a level that triggers some behaviour when the player enters it. Its better to seperate logical and graphical elements. A sprite is a graphical element, a creature is logical.

And don't be too quick to dismiss C++ and full usage thereof as slow. My engine, currently being used for its fourth commercial gba game, is fully C++ with some quite deep inheritance trees, lots of virtual functions, templates, the lot. Proper organisation of data is the key. Contextualize and sort where appropriate (quadtrees and binary trees are king in 2D).

#104060 - Kirby - Tue Sep 26, 2006 8:02 am

tepples wrote:
Kirby wrote:
tepples wrote:
If you're getting pixels ordered 2, 1, 4, 3, 6, 5, 8, 7, within a tile, then you accidentally chose Sega Genesis (4-bit packed pixels, big-endian) mode instead of GBA/DS 16-color (4-bit packed pixels, little-endian) mode.

First, Sega/Gensis mode what? What on EARTH does that mean? I'm using my own tileset compiler here!

Then you may have a defect in your own tileset compiler. I must have been thinking of a converter that I wrote that supports NES, Super NES, Game Boy, Virtual Boy, Genesis, GBA 4-bit, and GBA 8-bit, as well as several other formats.

You're right on that one. I was converting 8-bit values to 16-bit without reversing them. I found that the other picture did also look weird.

EDIT: Which means that you were right about it being 2,1,4,3,6,5,8,7, not 1,2,4,3,6,5,7,8. I just thought that becuase the edges of the tiles I was using were pretty much symmetrical.

Second, VC++2k5EE now works again! *Party noises* Some weird yvbb01.dll was messing it up. Just had to download HaxFix.exe and *poof*! It works again!

Third, where do I get cp and sed for the autodependcy makefile? I've tried to download it from the site, and it gives me a huge folder of binaries I'm supposted to compile using Linux or something insane like that!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!


Last edited by Kirby on Thu Sep 28, 2006 6:36 am; edited 1 time in total

#104115 - tepples - Tue Sep 26, 2006 5:58 pm

You get them as part of MSYS, which is bundled with devkitPro Updater. Yes, much of MSYS consists of Windows ports of GNU text tools associated with UNIX (and Linux).
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#104168 - Kirby - Tue Sep 26, 2006 9:55 pm

Alrightly then, maybe I won't use cp, sed.

Second, I don't think I fully explained my scrolling problem before. I have a human-controlled character that stays at the exact same OAM coordinates at all times, (except for in side-scrolling levels, but I havn't implemented that yet). I have a system for animated other AI characters, that works great, with the exception of this:

    When Kirby is at the exact same X and Y as WaddleDee, WaddleDee looks fine (but is underneath Kirby). When the difference between Kirby's X and his X or Kirby's Y and his Y gets bigger, he may start floating off the ground and all of that whatnot.
    I have to memcpy each Character in turn, because this for-loop doesn't work:
Code:
for (i=0;i<NumCharas;i++) {
      if (Characters[i].map == curmap) {
         memcpy( (u16 *)Characters[i].memLoc, &Characters[i].charaAnim[Characters[i].frame], 256 );
      }
   }

Lastly, if anyone's noticed that I've havn't answered any posts in about a week, it's because my computer had a virus and Internet Explorer couldn't start. Some stupid yvbb01.dll. If anyone is interested, they should download HaxFix (Google is your friend), because yvbb01 can also ruin half the programs on your computer. Just run the program, type 3, type in yvbb and then type N. Reboot when asked. Voila, computer fixed.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104172 - thegamefreak0134 - Tue Sep 26, 2006 10:27 pm

You\'re not using my scaling routine too, are you? I had similar issues with it... This sounds very much like a scaling issue. Is waddledee moving on his own, or is he staying put?
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#104180 - Kirby - Tue Sep 26, 2006 11:10 pm

I'm not using any scaling. Kirby doesn't move at all in OAM, the background scrolls behind him, but I use this for WaddleDee:
Code:
u16 h;
   u16 top;
   u16 left;
   left = Kirby.x - (4 * 32);
   top = Kirby.y - (2 * 32);
   if(foih == 0) {
      for (h=0;h<NumCharas;h++) {
         if (Characters[h].map == curmap) {
            Characters[h].drawX = Characters[h].x - left;
            Characters[h].drawY = Characters[h].y - top;
            if(Characters[h].x < (0 - 109)) {
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }   
            if(Characters[h].y < (0 - 128)) {
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }
            if(Characters[h].x > (left + 960)) {
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }   
            if(Characters[h].y > (top + 640)) {
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }
            sprites[h+3].attribute0 = COLOR_256 | SQUARE | ROTATION_FLAG | SIZE_DOUBLE | ((Characters[h].drawY / 3 + 42) & 0x00FF);
            sprites[h+3].attribute1 = SIZE_16 | ROTDATA(0) | ((Characters[h].drawX / 3 + 64) & 0x01FF);
         }
      }
      CopyOAM();
      foih = 1;
   } else {
      if (foih == 1) {
         foih = 2;
      } else {
         foih = 0;
      }
   }

WaddleDee moves on his own, but doesn't scroll exactly in tune to the background in relation of Kirby's X and Y. I will send a demo to gbadev.org for anybody who wants to see my demo so far.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104233 - Cearn - Wed Sep 27, 2006 9:39 am

The division by three could be part of the problem. Integer division rounds towards 0, which may create some friction when the sign becomes negative. In the case of x/3 :
Code:
  x | x/3
----+----
 -3 | -1
 -2 |  0
 -1 |  0
  0 |  0
  1 |  0
  2 |  0
  3 |  1
  4 |  1
  5 |  1
  6 |  2
Why are dividing by 3 anyway? If it's for sub-pixel accuracy, couldn't you just use standard fixed point numbers here?

Another potential problem is this:
Kirby wrote:
Code:
   u16 top;
   u16 left;
   left = Kirby.x - (4 * 32);
   top = Kirby.y - (2 * 32);
   ...
   Characters[h].drawX = Characters[h].x - left;
   Characters[h].drawY = Characters[h].y - top;

I don't know what Kirby.x is, but if it's, say, 127 (anything below 128 will do) then 'left' is 65535, not -1, and might throw the rest of the calculations off. Once again, the problem is caused by using the u16 datatype. Do NOT use non-word sized datatypes for local variables! It'll produce longer and slower code and be more susceptible to overflow problems. Use 'int' or 'u32', please.

#104272 - Kirby - Wed Sep 27, 2006 5:17 pm

Kirby.x is the X coordinate of my Human-controlled Character. Left is supposed to be the coordinate of where the far left of the screen on the map (so where it is scrolling-wise). I'm divinding by 3 because if I don't divide, he scrolls much faster than the map behind him, and if I divide by 4, he scrolls too slowly. I need to know what to do to the number so that it appears correctly on the screen.
Code:
 u16 top;
   u16 left;
   left = Kirby.x - (4 * 32);
   top = Kirby.y - (2 * 32);
   ...
   Characters[h].drawX = Characters[h].x - left;
   Characters[h].drawY = Characters[h].y - top;

Cearn wrote:
I don't know what Kirby.x is, but if it's, say, 127 (anything below 128 will do) then 'left' is 65535, not -1, and might throw the rest of the calculations off. Once again, the problem is caused by using the u16 datatype.

I don't think so. If you didn't notice, left is UNSIGNED!!!!! Meaning that there are negative values!!!!
Also, / is the Integer Division?!?!? OOOHHHHH..... Because in VB... never mind... So how do I do regular division?
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104277 - Cearn - Wed Sep 27, 2006 5:42 pm

Kirby wrote:
Kirby.x is the X coordinate of my Human-controlled Character. Left is supposed to be the coordinate of where the far left of the screen on the map (so where it is scrolling-wise). I'm divinding by 3 because if I don't divide, he scrolls much faster than the map behind him, and if I divide by 4, he scrolls too slowly. I need to know what to do to the number so that it appears correctly on the screen.


This is why we have floating point values. What you need is something that can scroll with a speed of 1/3 = 0.333... . Or, in case of a platform without an FPU, use fixed point math. In .8 fixed point, 1/3 is about 0x56 (i.e., 0x56 256ths); division by 3 then becomes (x*0x56)>>8. Of course there would be a small problem with roundoff again. But the 'right' scrolling speed might not be exactly 1/3 anyway, so the roundoff problem wouldn't matter much.

Kirby wrote:

Code:
 u16 top;
   u16 left;
   left = Kirby.x - (4 * 32);
   top = Kirby.y - (2 * 32);
   ...
   Characters[h].drawX = Characters[h].x - left;
   Characters[h].drawY = Characters[h].y - top;

Cearn wrote:
I don't know what Kirby.x is, but if it's, say, 127 (anything below 128 will do) then 'left' is 65535, not -1, and might throw the rest of the calculations off. Once again, the problem is caused by using the u16 datatype.

I don't think so. If you didn't notice, left is UNSIGNED!!!!! Meaning that there are negative values!!!!

Signed means there are negative values. Unsigned means there aren't.

Kirby wrote:
Also, / is the Integer Division?!?!? OOOHHHHH..... Because in VB... never mind... So how do I do regular division?
Same way, it depends on the types of the operands. If either operand is floating point, then the result will also be floating point; if both are integer, integer division is used. And as floating point arithmetic is hella slow on the GBA and NDS, you'll usually be working with integers.

#104300 - thegamefreak0134 - Wed Sep 27, 2006 9:28 pm

I\'m confused as to why you are having to divide one and not the other. Shouldn\'t you have the map and waddledee (I assume that\'s who\'s \"scrolling\" because you said kirby doesn\'t move) be moving at the same speed? If (with no divide) you have a speed difference, something is amiss, it looks like. What is the psuedo-code that causes waddledee to be positioned on-screen? (I\'m having trouble deciphering your C code, sorry)

The only thing I could figure is that you are using a rotational background for the map, in which case you should be having to divide by 8, not three... But I don\'t see why that would be the case either.

-gamefreak
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#104315 - Kirby - Wed Sep 27, 2006 11:59 pm

thegamefreak0134 wrote:
The only thing I could figure is that you are using a rotational background for the map, in which case you should be having to divide by 8, not three... But I don\'t see why that would be the case either.

I AM using an affline map, because I can't seem to get regular ones to work. But dividing by 8 would just make him WAY too slow.
Cearn wrote:
Signed means there are negative values. Unsigned means there aren't.

No they're not! I'll prove it! I'll open my C++ book and!... oh... oh my gosh.... That's why WaddleDee appears at the top of the screen some times... oops... poop. Now I've changed lots of my signed numbers to unsigned... Thanx for telling me that.
thegamefreak0134 wrote:
I\'m confused as to why you are having to divide one and not the other. Shouldn\'t you have the map and waddledee (I assume that\'s who\'s \"scrolling\" because you said kirby doesn\'t move) be moving at the same speed? If (with no divide) you have a speed difference, something is amiss, it looks like. What is the psuedo-code that causes waddledee to be positioned on-screen? (I\'m having trouble deciphering your C code, sorry)

Yes, WaddleDee is supposed to be "scrolling", because for Kirby, the map just scrolls behind him. Yes, when I don't divide I have a speed diference. I'll comment my code to help you understand it more:
Code:
u16 h; //For-variable for looping through all characters
   u16 top; //Variable
   u16 left; //Variable
   left = Kirby.x - (4 * 32); //Represents the left of the screen
   top = Kirby.y - (2 * 32); //Represents the top of the screen
   if(foih == 0) {
      for (h=0;h<NumCharas;h++) { //For loop
         if (Characters[h].map == curmap) { //If character is on current map
            Characters[h].drawX = Characters[h].x - left; //Take away the left of the screen from WaddleDee's X value
            Characters[h].drawY = Characters[h].y - top; //Take away the top of the screen from WaddleDee's Y value
            if(Characters[h].x < (0 - 109)) { //If WaddleDee is too far to the left
               Characters[h].drawX = 624; //Hide Waddle Dee
               Characters[h].drawY = 384; // ""
            }   
            if(Characters[h].y < (0 - 128)) { //If WaddleDee is too far to the north
               Characters[h].drawX = 624; // Hide
               Characters[h].drawY = 384; // ""
            }
            if(Characters[h].x > (left + 960)) { //If he is to far to the right
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }   
            if(Characters[h].y > (top + 640)) {  //If he is too far to the south
               Characters[h].drawX = 624;
               Characters[h].drawY = 384;
            }
            sprites[h+3].attribute0 = COLOR_256 | SQUARE | ROTATION_FLAG | SIZE_DOUBLE | ((Characters[h].drawY / 3 + 42) & 0x00FF); //sprites is an array off OAMEntry. This code moves him
            sprites[h+3].attribute1 = SIZE_16 | ROTDATA(0) | ((Characters[h].drawX / 3 + 64) & 0x01FF); //So does this one.
         }
      }
      CopyOAM(); //Copy to OAM
      foih = 1;
   } else {
      if (foih == 1) {
         foih = 2;
      } else {
         foih = 0;
      }
   }


EDIT: For the scrolling, I'm not using speed as such. I'm not adding values to WaddleDee's X and Y, I'm setting them to approximate values.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!


Last edited by Kirby on Thu Sep 28, 2006 6:23 am; edited 1 time in total

#104342 - Kirby - Thu Sep 28, 2006 5:33 am

The closest I can get to correct scrolling is instead of diving X and Y by 3, to divide X by 3.6, and add 64, and to divide Y by 2.4, and add 37, and even then it's still a little bit off. Also, dividing these numbers are making the game go really slow when you're on the same map as WaddleDee.

EDIT: Also, I wonder if anyone has notice the HUGE bug in one of the pieces of code I've posted. I have fixed the error, but I want to know if anyone else has noticed it. HINT: It's on the first page. When this topic gets up to the 4'th page, I'll let you all know. (Another Hint: This error was causing WaddleDee move incorrectly)
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104387 - thegamefreak0134 - Thu Sep 28, 2006 4:24 pm

I have an idea. Why not (instead of worrying about the screen) take waddledee\'s position in the map as a value. (his position on the level, if you will, regardless of screen positioning.) Then, figure out how far the map is scrolled, and scroll waddledee that much as well. (In this case, the mapscrolled / 8 should do the trick) Boom. You have waddledee\'s position. Since the map is scrolled relative to the screen, you shouldnt have to adjust to the screen at all, just check if he is not on the screen and dont draw him in this case. By doing this, you should effectively eliminate all divides, since the compiler optimizes a divide by 8 to a shift anyway, which is much faster.

Is the map anchored to the top left corner? If not, you will need to account for this as well.

-gamefreak
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#104446 - sgeos - Fri Sep 29, 2006 6:34 am

I agree with thegamefreak0134. You should be able to position your viewport anywhere you want to- it should not be hard coded to any single critter. Then, if you want to tie it to a particular critter, you can do something like this:

Code:
Point pos = myCritter.getPos();

setCameraPos(pos);  // screen center
// ... later ...
redraw();

-Brendan

#104458 - Kirby - Fri Sep 29, 2006 8:33 am

Code:
Characters[h].drawX = (((Characters[h].drawX * 9) >> 5) + 64);
Characters[h].drawY = (((Characters[h].drawY * 27) >> 6) + 37);

Until I can find something better, I'm going to use this. I followed what
Cearn wrote:
This is why we have floating point values. What you need is something that can scroll with a speed of 1/3 = 0.333... . Or, in case of a platform without an FPU, use fixed point math. In .8 fixed point, 1/3 is about 0x56 (i.e., 0x56 256ths); division by 3 then becomes (x*0x56)>>8. Of course there would be a small problem with roundoff again. But the 'right' scrolling speed might not be exactly 1/3 anyway, so the roundoff problem wouldn't matter much.
, and used fixed point math. I found that the equivilent(I know that's spelt wrong) of (X / 3.6) + 64 was ((X * 9) >> 5) + 64, and that (Y / 2.4) + 37 was the same as ((Y * 27) >> 6) + 37. Basically, it's the same as before when it was
Code:
Characters[h].drawY = (((Characters[h].drawY * 1.0) / 2.4) + 37);
Characters[h].drawX = (((Characters[h].drawX * 1.0) / 3.6) + 64);
, but that way used floating points, and slowed down the game. It's actually pretty good, the error ratio is 1:32 (1 pixel off for every 32 pixels between you).
thegamefreak0134 wrote:
I have an idea. Why not (instead of worrying about the screen) take waddledee\'s position in the map as a value. (his position on the level, if you will, regardless of screen positioning.) Then, figure out how far the map is scrolled, and scroll waddledee that much as well. (In this case, the mapscrolled / 8 should do the trick) Boom. You have waddledee\'s position. Since the map is scrolled relative to the screen, you shouldnt have to adjust to the screen at all, just check if he is not on the screen and dont draw him in this case. By doing this, you should effectively eliminate all divides, since the compiler optimizes a divide by 8 to a shift anyway, which is much faster.
It didn't work. I tried every way I could think of to get this to work, but it didn't.

I wrote a small program that gets the Fixed Point expression "(x * y) >> z" from a Floating Point division "x / y", so if you have to do a decimal division, you could turn it in to a Fixed Point expression. Do you think I should send it to gbadev.org, or is FPM something easy to work out and nobody would use my program?

Aside from this post's main topic, I'm having another problem. I'm trying to add a bar at the bottom of the screen with Kirby's HP and lives on it. I'm putting it on BG 1, and I'm using Mode 1. I can get the data onto the map correctly, it's just the tileset I'm having a problem with. I'm trying to add the palette to 0x050001C0, and just add E0 to each of the values in the tile array. But when I test it out, one of two things happens:

    A. The bar looks very weird (palette errors)
    B. There are black stripes down every other column (I think the colors are inverted)
This is the code I'm using:

CharBg.h
Code:
#include "..\gba.h"
#include "CharData.h"
#include "CharDataMap.h"
#include "..\textsys.h"

void InitCharData(void);
void ShowCharData(u16 hp, u16 lives, u16 points);

Bg bg1;

void InitCharData()
{
   u16 tempg[2560];
   u16 tempp[22];
   bg1.charBaseBlock = 2;
   bg1.colorMode = BG_COLOR_256;
   bg1.size = TEXTBG_SIZE_256x256;
   u16 i;
   for(i=0;i<40*4*8;i++) VRAM[0x4000+i] = CharDataTiles[i] + 0xE0;
   for(i=0;i<22;i++) PAL[0xE0+i] = CharDataPalette[i];
   //memcpy((u16 *)0x06008000, &tempg, sizeof(CharDataData)); //1125
   //memcpy((u16 *)0x050001C0, &CharDataPalette,22);
   for(i=0;i<2048;i++) bg1.mapData[i] = CharDataMapMap[i];
   bg1.number = 1;
}

void ShowCharData(u16 hp, u16 lives, u16 points)
{
   InitCharData();
   PlotText(Int2Txt(lives),4,8);
   PlotText(Int2Txt(points),8,8);
   EnableBackground(&bg1);
}
If you spot any errors, please tell me.

EDIT: Above where it says ((X * 9) >> 5) + 64, the + 64 does count, because the FPV is different without it. My program says it's (X * 17) >> 6.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104460 - Cearn - Fri Sep 29, 2006 10:43 am

Kirby wrote:
Code:
Characters[h].drawX = (((Characters[h].drawX * 9) >> 5) + 64);
Characters[h].drawY = (((Characters[h].drawY * 27) >> 6) + 37);

Until I can find something better, I'm going to use this. I followed what
Cearn wrote:
This is why we have floating point values. What you need is something that can scroll with a speed of 1/3 = 0.333... . Or, in case of a platform without an FPU, use fixed point math. In .8 fixed point, 1/3 is about 0x56 (i.e., 0x56 256ths); division by 3 then becomes (x*0x56)>>8. Of course there would be a small problem with roundoff again. But the 'right' scrolling speed might not be exactly 1/3 anyway, so the roundoff problem wouldn't matter much.
, and used fixed point math. I found that the equivilent(I know that's spelt wrong) of (X / 3.6) + 64 was ((X * 9) >> 5) + 64, and that (Y / 2.4) + 37 was the same as ((Y * 27) >> 6) + 37. Basically, it's the same as before when it was
Code:
Characters[h].drawY = (((Characters[h].drawY * 1.0) / 2.4) + 37);
Characters[h].drawX = (((Characters[h].drawX * 1.0) / 3.6) + 64);
, but that way used floating points, and slowed down the game. It's actually pretty good, the error ratio is 1:32 (1 pixel off for every 32 pixels between you).

I wrote a small program that gets the Fixed Point expression "(x * y) >> z" from a Floating Point division "x / y", so if you have to do a decimal division, you could turn it in to a Fixed Point expression. Do you think I should send it to gbadev.org, or is FPM something easy to work out and nobody would use my program?

Depending on the details it might be useful, but really, fixed point math isn't difficult. It's just a scaled form of regular integer math.
Also, don't think in terms of (floating point) divisions for movement, just plain floats or fixeds will do. In the case of your movement scaling, what you want is not so much scaling down the numbers you have by 2.4, but movements in 1/2.4 = 0.41666 = 5/12 increments. Or 27/64th or 107/256th or even 27307/65536th. The point of fixed point math is that you always count in fractions, so that adding fractions simply becomes adding integers:
Code:
// Example of .8 fixed point math:
int fa= 0x1234;    // fa= 0x1234/256 ≅ 18.20
int fb= 0x4321;    // fb= 0x4321/256 ≅ 67.13
int fc;

fc= fa + fb;    // fc = (0x1234+0x4231)/256 = 0x5555/256 = 85.33

fc += 107;    // fc = fc + 107/256 = fc + 0.41666

fc= fc/256;   // Conversion to normal integer (.8 -> .0 fixeds)
More fractional bits means higher accuracy, but also more chance of overflow. It's usually a good idea to use a standard number of fractional bits for you FPM because you can't just add the numerators of fractions that have different denominators (1/3 + 1/4 ≠ 2/4, etc). You're likely to see .8, .12 or .16 fixed point in the GBA/NDS world, though possibly others as well. Fractional arithmetic is covered in primary school, so it should know this stuff already. For a little more on fixed point, especially for divisions, go to tonc:fixed.

Kirby wrote:
thegamefreak0134 wrote:
I have an idea. Why not (instead of worrying about the screen) take waddledee\'s position in the map as a value. (his position on the level, if you will, regardless of screen positioning.) Then, figure out how far the map is scrolled, and scroll waddledee that much as well. (In this case, the mapscrolled / 8 should do the trick) Boom. You have waddledee\'s position. Since the map is scrolled relative to the screen, you shouldnt have to adjust to the screen at all, just check if he is not on the screen and dont draw him in this case. By doing this, you should effectively eliminate all divides, since the compiler optimizes a divide by 8 to a shift anyway, which is much faster.

It didn't work. I tried every way I could think of to get this to work, but it didn't.
Then try again :P. The method thegamefreak0134 and sgeos hint at is probably the best bet.

Think of it this way. You have the game world, with a given coordinate set. This probably corresponds to the coordinates on the full map. Then you have sprites on the map, each with a pair of world coordinates. Then you have the hardware, which can only show a portion of the world. It can also only hold a small portion of the map (in screenblocks) and the positions of the sprites has to be relative to the screen as well. The viewport is the portion of the world you can actually see; it also has a pair of world coordinates. If the world coord-system corresponds to the map coord-system, then the position of the viewport will also be the position you enter into the background offset registers, so that makes that part easier. For the sprites, translate the world positions to screen positions by subtracting by the viewport coords. Then check whether the sprite is in view (check for overlap between sprite rect and viewport). If so, the sprite is visible and you place it accordingly. If not, hide the thing.

And that's basically it. This should require no division at any time. Scrolling is a matter of repositioning the viewport and translating the world coords to screen coords. If pixel speeds isn't accurate enough, use fixed point math for sub-pixel speeds. For tieing the viewport to a specific sprite, use a function like sgeos mentioned. Make a small, simple demo app to get the basics of a viewport system to work, then carry it over in the game.

Kirby wrote:
Aside from this post's main topic, I'm having another problem. I'm trying to add a bar at the bottom of the screen with Kirby's HP and lives on it. I'm putting it on BG 1, and I'm using Mode 1. I can get the data onto the map correctly, it's just the tileset I'm having a problem with. I'm trying to add the palette to 0x050001C0, and just add E0 to each of the values in the tile array. But when I test it out, one of two things happens:

    A. The bar looks very weird (palette errors)
    B. There are black stripes down every other column (I think the colors are inverted)
This is the code I'm using:

CharBg.h
Code:
#include "..\gba.h"
#include "CharData.h"
#include "CharDataMap.h"
#include "..\textsys.h"

void InitCharData(void);
void ShowCharData(u16 hp, u16 lives, u16 points);

Bg bg1;

void InitCharData()
{
   u16 tempg[2560];
   u16 tempp[22];
   bg1.charBaseBlock = 2;
   bg1.colorMode = BG_COLOR_256;
   bg1.size = TEXTBG_SIZE_256x256;
   u16 i;
   for(i=0;i<40*4*8;i++) VRAM[0x4000+i] = CharDataTiles[i] + 0xE0;
   for(i=0;i<22;i++) PAL[0xE0+i] = CharDataPalette[i];
   //memcpy((u16 *)0x06008000, &tempg, sizeof(CharDataData)); //1125
   //memcpy((u16 *)0x050001C0, &CharDataPalette,22);
   for(i=0;i<2048;i++) bg1.mapData[i] = CharDataMapMap[i];
   bg1.number = 1;
}

Probably a problem with datatypes. VRAM is in halfwords (u16), that I'm pretty sure of. CharDataTiles is probably either halfwords or bytes. If it is a byte array, you have a mismatch between source and destination types and you're only copying half the data you should. If it's a halfword array, remember you're copying two pixels at once and adding 0xE0 will only work on one of them. Try adding 0xE0E0.

Kirby wrote:
Code:
u16 i
For the third time, INTS! I know this sounds annoying and pedantic, but the standard datatype should be int, either signed or unsigned. You're probably only doing it out of habit, but that's why I keep saying this. The ARM cpu is a 32bit processor. The native datatype (the word size) is 32 bits long, and other datatypes such as u16 have to be forced to stay that way. Using non-word datatypes for local variables can makes the code longer, slower and is more prone to alignment and overflow problems. Doing the Right Thing in the first place is so ridiculously easy that doing otherwise can be considered a form of pessimisation. Unless you actually have a good reason for using non-ints, don't. Break the habit, you'll benefit from it in the long run.

#104468 - thegamefreak0134 - Fri Sep 29, 2006 1:49 pm

Quote:
Then try again :P. The method thegamefreak0134 and sgeos hint at is probably the best bet.


Whoa. I was actually right? Awesomeness! (Sorry, I just had to.)

I can probably give you some example code, but it will take me a day because I\'m at school and my compiler is at home. Something bothers me though...

Quote:
I wrote a small program that gets the Fixed Point expression \"(x * y) >> z\" from a Floating Point division \"x / y\", so if you have to do a decimal division, you could turn it in to a Fixed Point expression. Do you think I should send it to gbadev.org, or is FPM something easy to work out and nobody would use my program?


??? What kind of equasion are you supposed to be getting? If you are trying to get a non-power of two for division I can see the usefulness here, but for a map this should not be necessary... Oh wait. Ah, I see. Nevermind. Yes, to answer your question.

-gamefreak

Will post code tomorrow, if I can. Affine maps, single sprite in middle that doesn\'t move, various enemy sprites that are tied to the map. Should work out alright.
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#104513 - Kirby - Fri Sep 29, 2006 11:21 pm

Oh, shoot! My post was deleted! And it was a long one! Great.
Cearn wrote:
Kirby wrote:
thegamefreak0134 wrote:
I have an idea. Why not (instead of worrying about the screen) take waddledee\'s position in the map as a value. (his position on the level, if you will, regardless of screen positioning.) Then, figure out how far the map is scrolled, and scroll waddledee that much as well. (In this case, the mapscrolled / 8 should do the trick) Boom. You have waddledee\'s position. Since the map is scrolled relative to the screen, you shouldnt have to adjust to the screen at all, just check if he is not on the screen and dont draw him in this case. By doing this, you should effectively eliminate all divides, since the compiler optimizes a divide by 8 to a shift anyway, which is much faster.
It didn't work. I tried every way I could think of to get this to work, but it didn't.
Then try again :P. The method thegamefreak0134 and sgeos hint at is probably the best bet.

Think of it this way. You have the game world, with a given coordinate set. This probably corresponds to the coordinates on the full map. Then you have sprites on the map, each with a pair of world coordinates. Then you have the hardware, which can only show a portion of the world. It can also only hold a small portion of the map (in screenblocks) and the positions of the sprites has to be relative to the screen as well. The viewport is the portion of the world you can actually see; it also has a pair of world coordinates. If the world coord-system corresponds to the map coord-system, then the position of the viewport will also be the position you enter into the background offset registers, so that makes that part easier. For the sprites, translate the world positions to screen positions by subtracting by the viewport coords. Then check whether the sprite is in view (check for overlap between sprite rect and viewport). If so, the sprite is visible and you place it accordingly. If not, hide the thing.
I'll have you know that that was what I was doing the entire time since I started AI characters!!!!!!!! Just taking his X and Y, and subtracting the left and top of the screen (or as you say, viewpoint). IT DOESN'T WORK! There have always been speed problems. The closest I could get it to work is with FPM!!!!!!!
thegamefreak0134 wrote:
Whoa. I was actually right? Awesomeness! (Sorry, I just had to.)
It's OK. I know the feeling...
thegamefreak0134 wrote:
I can probably give you some example code, but it will take me a day because I\'m at school and my compiler is at home. Something bothers me though...
Thanx! I'd like to see what you are all thinking of, because to me, at least, it doesn't work. Also, thanx for the frame update code, I never did get back to thanking you.
Cearn wrote:
Try adding 0xE0E0.
Thanx for that one, too. It looks great now, but the back is now all blue, and blocking the rest of the backgrounds. Is there either:
    A. a way to add priority to the other backgrounds so they appear in front? or
    B. a way to change the blues so that they are transparent?
Cearn wrote:
Kirby wrote:
Code:
u16 i
For the third time, INTS! I know this sounds annoying and pedantic, but the standard datatype should be int, either signed or unsigned. You're probably only doing it out of habit, but that's why I keep saying this. The ARM cpu is a 32bit processor. The native datatype (the word size) is 32 bits long, and other datatypes such as u16 have to be forced to stay that way. Using non-word datatypes for local variables can makes the code longer, slower and is more prone to alignment and overflow problems. Doing the Right Thing in the first place is so ridiculously easy that doing otherwise can be considered a form of pessimisation. Unless you actually have a good reason for using non-ints, don't. Break the habit, you'll benefit from it in the long run.
Sorry about the halfwords! It's just, when I was just learning GBA Programming, the two tutorials I used both used u16, and then it grew into a habit.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104514 - Kirby - Fri Sep 29, 2006 11:22 pm

OK. More news on my program:
    A. It should be able to work out a #'s FP even if it is 5 digits long because I added a function that increases the work done when the number is longer.
    B. Smaller numbers will be done more quickly, for the reason above.
    C. The error ratio is about is about x:y. That means that the result of the equation will be y bigger or smaller than x, or equal to x. Y is the number of digits in X - 1. So a one or two digit number will only be 0 or 1 off, but a four digit number could be 3 off. This is because of the rounding errors the GBA has.
    D. I already have two people (actually, 1 and a half) people telling me to post it. I you think I should, please tell me. If 3 more people vouch for it, I'll post it.
To prove that it works, I'll give a few examples:
Code:
 x / y | (x * y) >> z
-------+---------------
 x / 1 | (x * 4) >> 2
 x / 2 | (x * 2) >> 2
x / 2.4| (x * 13) >> 5
Feel free to try these with a calculator. If your calulator doesn't have a Right-Shift, like mine, use this for the FPM: "((x * y) / 2) / x". After you round them off, and apply C above, you will see that it works good.

Also, if you are wondering why I a talking about this in this topic, instead of making another one, it is because I found that I had accidentally posted on of my posts twice, so I replaced the double with this.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104524 - Cearn - Sat Sep 30, 2006 2:18 am

Kirby wrote:
I'll have you know that that was what I was doing the entire time since I started AI characters!! Just taking his X and Y, and subtracting the left and top of the screen (or as you say, viewpoint). IT DOESN'T WORK! There have always been speed problems. The closest I could get it to work is with FPM!!

Ah! I thought you meant "didn't work" as in the technique didn't function properly. Whether or not it's fast enough is another matter. Well, cutting down on divisions should matter quite a bit. The GCC division routine isn't particularly fast and can cost as much as 100 times more as a multiplication, if I recall correctly (I seem to have misplaced the file here I put those timing tests :( ). Cutting those out should have helped. Seems a little odd that this would be such a bottleneck, though: all it calls for is adds/subtracts and a few checks. Have you tried timing the routines? If not, do so: it will give tell you where all those cycles are going and what you should work on to get them back. for a simple example of setting up something like that, go here.
Also, how many objects are we talking about here? A dozen? Hundreds? Even at 100 cycles for 100 objects there should be plenty of time left in the VBlank. If there anyway we can play with the code ourselves to find the problem?

Kirby wrote:
Cearn wrote:
Try adding 0xE0E0.
Thanx for that one, too. It looks great now, but the back is now all blue, and blocking the rest of the backgrounds. Is there either:
    A. a way to add priority to the other backgrounds so they appear in front? or
    B. a way to change the blues so that they are transparent?

The standard order from front to back is objects, bg0, bg1, bg2, bg3. If you've organized the backgrounds in that order, there should be no problem. You can also break the normal order by using bits 0 and 1 of REG_BGxCNT.
The blue thing is probably because by adding 0xE0 to all the pixels, there are no pixels with '0' anymore, so nothing will be transparent. Only a pixel value of 0 is transparent. Try this:
Code:
int ii;
u16 *dst= (u16*)&VRAM[0x4000], src= (u16*)CharDataTiles;
u32 in, out;

for(ii=0; ii<40*4*8; ii++)
{
    out= 0;
    in= src[ii];
    // Check 2 pixels for 0-ness; only apply the offset if they're not
    if( in & 255 )
        out = (in&255) + 0xE0;
    if( in &~255 )
        out += (in&~255)+0xE000;

    // write 2 pixels to the proper VRAM destination
    dst[ii]= out;
}
Untested, but should work. Faster version may be possible, but it's getting a little late here, so maybe another time. One thing to look out for here is that the pixel value + 0xE0 should not be over 255, or it'll bleed into the next pixel. Also note the use of pointers instead of VRAM and CharDataTiles directly. The use of pointers and temp variables can keep code cleaner and more maintainable. The code in this post would be a lot more readable if you'd use a pointer to keep track of 'Characters[i]' and a temp for the frequently used 'Characters[i].state'.

Kirby wrote:
Cearn wrote:
Kirby wrote:
Code:
u16 i
For the third time, INTS! I know this sounds annoying and pedantic, but the standard datatype should be int, either signed or unsigned. You're probably only doing it out of habit, but that's why I keep saying this. The ARM cpu is a 32bit processor. The native datatype (the word size) is 32 bits long, and other datatypes such as u16 have to be forced to stay that way. Using non-word datatypes for local variables can makes the code longer, slower and is more prone to alignment and overflow problems. Doing the Right Thing in the first place is so ridiculously easy that doing otherwise can be considered a form of pessimisation. Unless you actually have a good reason for using non-ints, don't. Break the habit, you'll benefit from it in the long run.
Sorry about the halfwords! It's just, when I was just learning GBA Programming, the two tutorials I used both used u16, and then it grew into a habit.
A lot of the older tutorials teach bad habits, of which this is one. The other major one is putting data (like graphics) in header files and then #including them, instead of compiling them and linking them or using a file system. In fact, if you're doing this as well, it may be the reason why memcpy() fails for graphic transfers. Both source and destination addresses need to be word aligned for memcpy() to work properly, which is likely not the case if you #include the data arrays (unless they happen to be u32 arrays, which are guaranteed word-aligned).

#104531 - Kirby - Sat Sep 30, 2006 5:29 am

Cearn wrote:
The other major one is putting data (like graphics) in header files and then #including them, instead of compiling them and linking them or using a file system. In fact, if you're doing this as well, it may be the reason why memcpy() fails for graphic transfers. Both source and destination addresses need to be word aligned for memcpy() to work properly, which is likely not the case if you #include the data arrays (unless they happen to be u32 arrays, which are guaranteed word-aligned).
Nice try, but that's not what I'm doing. My makefile uses gfx2gba to translate all of the pictures at the same time into .s files, and then I use as to assemble them all into one object file.
Cearn wrote:
Code:
int ii;
u16 *dst= (u16*)&VRAM[0x4000], src= (u16*)CharDataTiles;
u32 in, out;

for(ii=0; ii<40*4*8; ii++)
{
    out= 0;
    in= src[ii];
    // Check 2 pixels for 0-ness; only apply the offset if they're not
    if( in & 255 )
        out = (in&255) + 0xE0;
    if( in &~255 )
        out += (in&~255)+0xE000;

    // write 2 pixels to the proper VRAM destination
    dst[ii]= out;
}
Untested, but should work.
It does. Thanx. But now it's map looks weird. Do you know of any map editors that export u16 values? I was trying your Mirach, but it exports u32's, which don't copy into VRAM correctly using you're code above.
Kirby wrote:
If your calulator doesn't have a Right-Shift, like mine, use this for the FPM: "((x * y) / 2) / x". After you round them off, and apply C above, you will see that it works good.
Just a quick fix, but "((x * y) / 2) / x" is the incorrect way to be going about this. Instead, go "(x * y) << -z". Why the calculator has Left Shift and not right, I do not know.
Cearn wrote:
The GCC division routine isn't particularly fast and can cost as much as 100 times more as a multiplication, if I recall correctly (I seem to have misplaced the file here I put those timing tests :( ).
Now do you know why I wrote FPU2FPM?
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104561 - Cearn - Sat Sep 30, 2006 12:28 pm

Kirby wrote:
It does. Thanx. But now it's map looks weird. Do you know of any map editors that export u16 values? I was trying your Mirach, but it exports u32's, which don't copy into VRAM correctly using you're code above.
If you temporary pointers like I did here, the datatype of the original addresses becomes irrelevant. And the map looks weird how?

Oh, since you say the routine works, I take it you noticed that 'src' should have been a pointer too?

Kirby wrote:
If your calulator doesn't have a Right-Shift, like mine, use this for the FPM: "((x * y) / 2) / x". After you round them off, and apply C above, you will see that it works good.
Just a quick fix, but "((x * y) / 2) / x" is the incorrect way to be going about this. Instead, go "(x * y) << -z". Why the calculator has Left Shift and not right, I do not know.[/quote]
Calculator has a left-shift? *checks* Heh, Cool!. Well, the reason for not having a Rsh is probably for button parsimony. There's no Sqrt either, but you can make that with Inv-x^2. Inv-Lsh is Rsh.

About your program: consider x / 2.4 | (x * 13) >> 5, with x= 12
12/2.4 = 5.
(12*13)>>5 = 4.875 = 4

With only 5 bits, the division will degenerate rapidly, which will have a noticeable effect on your scrolling.

However, with (x*107)>>8 you're safe till 67. More fractional bits mean more higher accuracy, though at some point you'll have to start worrying about overflow again. Take the expected range of x into account when finding the number of bits. I did have safety guidelines for guaranteed equality between reciprocal multiplication and division, but it seems they don't work with fractional denominators :( Oh well.

#104565 - Mucca - Sat Sep 30, 2006 1:14 pm

Cearn, I commend your patience.

Kirby, forget everything you think you know about fixed point math as you've patently misunderstood the concept.

Fixed point is a way of storing a number as a number and a power-of-two friendly fraction. In decimal, we're used to writing fractions in tenths, hundreths, thousands. In fixed point we use eights, sixteenths, 1/256s, 1/65536s.

Now, while you may have chanced across a couple of numbers which seem to accurately represent a float, the goal should be to create a system which can handle any number automatically. Its really very simple. Addition and subtraction is the same on a fixed point number as on a normal integer. With division and multiplication an extra step is required.

For simplicity and clarity, use one type of fixed-point format. Lets use 16-16, ie 1 sign bit, 15 bits for integer, and 16 bits for the fraction. But dont worry, its not that difficult to change.

In C, where theres no classes and no operator overloading, your best bet is to write a few functions.

Code:

// define how many bits are used for fraction
#define FP_FRACTION_BITS    16

// for extra readability, make a typedef
typedef s32  FP;

// "construct" a fixed point from a normal integer
FP fix( s32 val )
{
    FP fp = val << FP_FRACTION_BITS ;
    return fp;
}

// "construct" from a float
FP fix( float f )
{
    FP fp = (FP)(f * ( 1 << FP_FRACTION_BITS));
    return fp;
}

// multiply two fixed point numbers
FP fix_mul( FP fp1, FP fp2 )
{
    s64 tmp = (s64)fp1 * (s64)fp2;
    FP prod  = (FP)(tmp >>  FP_FRACTION_BITS);
    return prod;
}

// divide one fixed point number by another
FP fix_div( FP dividend, FP divisor )
{
    s64 tmp = (s64)(dividend << 32);
    FP quotient = (FP)(( tmp / divisor ) >> (32 - FP_FRACTION_BITS));
    return quotient;
}


Usage looks like this:
Code:

FP a = fix(4);
FP b = fix(2.5f);

FP c = fix_mul( a, b );

c = fix_div( a, c );
a = a - b + c;


One downfall here is that, despite the typedef, the compiler wont do a good job of enforcing type correctness. You just have to be careful.

#104570 - Cearn - Sat Sep 30, 2006 1:42 pm

Two addenda to what Mucca just said:

Mucca wrote:
Lets use 16-16, ie 1 sign bit, 15 bits for integer, and 16 bits for the fraction.

Unlike floating point number, signed integers (and hence fixed point numbers) never have a sign bit, not in the truest sense of the word. For example, The 16bit representation of -1 is 0xFFFF, not 0x8001, which it would be with a real sign bit. Two's complement integers interpret the top half of their range as negative numbers, 'wrapped around' at 0:

Code:
0x8000 ... 0xFFFE 0xFFFF 0x0000 0x0001 0x0002 ... 0x7FFF
-2^15  ...   -2     -1     0      1      2    ... +2^15-1

Yes, this does mean that the most significant bit tells you whether the number is negative or not, but that is merely incidental to the whole scheme; for smaller negative numbers, later bits will indicate the sign just as well.

Also, inlining those functions might be a good idea. OTOH, 64bit math is slow in thumb code, so having fix_mul and fix_div as ARM functions will probably be faster than inlined THUMB versions.

#104579 - poslundc - Sat Sep 30, 2006 5:08 pm

Cearn wrote:
OTOH, 64bit math is slow in thumb code, so having fix_mul and fix_div as ARM functions will probably be faster than inlined THUMB versions.


I'm pretty sure a 64-bit multiply compiled in Thumb mode will just branch off to extra ARM code anyway, since Thumb doesn't provide a 64-bit multiply instruction.

Dan.

#104580 - gmiller - Sat Sep 30, 2006 5:19 pm

The only format for interger that actually HAD a sign bit was signed magnitude. For signed magmitude the range for integer was the same for positive and negative, not like the 2's complement system which goes one more negative than positive.

for example: (for an eight bit system)
Code:


Type           Max                    Min
-----------   -----------------    -----------------------
signed        +127                  -127
magnitude

2's comp.    +127                  -128


Just for "edification":

The switch beween signed magnitude and the complement systems (1's and 2's) came about to simplify the math hardware. The the switch from 1's to 2's complement came about to simplify the hardware even more. Sorry, I teach Machine Architecture so I can be pretty annal about this some times.

#104581 - tepples - Sat Sep 30, 2006 5:28 pm

poslundc wrote:
I'm pretty sure a 64-bit multiply compiled in Thumb mode will just branch off to extra ARM code anyway, since Thumb doesn't provide a 64-bit multiply instruction.

But will the automatic support for long long arithmetic be smart enough to put the ARM backends in fast 32-bit RAM (IWRAM on the GBA or ITCM on the DS)?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#104589 - Cearn - Sat Sep 30, 2006 6:15 pm

gmiller: good points.

poslundc wrote:
Cearn wrote:
OTOH, 64bit math is slow in thumb code, so having fix_mul and fix_div as ARM functions will probably be faster than inlined THUMB versions.


I'm pretty sure a 64-bit multiply compiled in Thumb mode will just branch off to extra ARM code anyway, since Thumb doesn't provide a 64-bit multiply instruction.

Dan.

Nope. At least not with standard DKP flags. It uses __aeabi_lmul for the 64bit multiply, which is a THUMB function. A fairly long thumb function to boot. Quick and dirty tests with various versions of fix_mul() doing 123456*123456:
Code:
pure THUMB  | 339 cycles
ARM/ROM     | 109 cycles
ARM/IWRAM   | 69 cycles

Even the ARM/ROM version is a good deal faster than THUMB with the __aeabi_lmul call. FYI: if the cycle counts for the ARM functions seem high: remember that there's a 30-60 cycle function overhead.

#104593 - tepples - Sat Sep 30, 2006 6:50 pm

Cearn wrote:
FYI: if the cycle counts for the ARM functions seem high: remember that there's a 30-60 cycle function overhead.

Where does this overhead come from? Last I checked, a call to a leaf function that doesn't need more than five registers involved loading its code address into a register (3 cycles), a bx into the function (3 cycles), and a bx out of the function (3 cycles).
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#104596 - Cearn - Sat Sep 30, 2006 7:07 pm

ROM/EWRAM code waitstates. 1S and 1N take 3 or more cycles so that your numbers need to be multiplied by 3 or so. For each argument and return value, add 1 mov or ldr. Jumping to IWRAM takes one or two ldr's and a bx more, and possible stack work takes up some more instructions as well. All with waitstates. It can add up pretty quickly.

#104608 - Mucca - Sat Sep 30, 2006 9:12 pm

Regarding the times posted by Cearn, that's an incredible performance gap on such a fundamental operation such as multiplying a number. Hmm, so far I've never bothered compiling to ARM and loading to IWRAM, its problematic with gcc 2.9.5 involving various shenanigans and I couldn't afford to spend the time getting it to work. Apart from that I've never really had performance problems. But such a yield is definitely tempting.

#104635 - Kirby - Sun Oct 01, 2006 12:22 am

    First, maybe I should have put this program into another topic, because of the amount of posts it seems to have accumalated.

    Second, I know what Fixed Point Math is. I know my program doesn't actually do fixed point math, it just converts a division problem into a shift problem. I call it FPU2FPM because the division IS floating-point, and the shift SEEMS like fixed point. I read all about fixed point at TONC. OK?

    Third, Cearn, I tested out the #'s with 64, not 12. As I said before, the margin of error is about the number of digits in x. It does work. In my game, when using the system, you can hardly tell that there is a scrolling problem, and that's in VBA at 4x Video mode!
I'm sorry that I made the mistake of calling it Fixed Point Math, because I can see the huge reaction it created. By calling it Fixed Point, I meant it SEEMED like Fixed Point. If I though that it was possible, I would ask a moderator to move these posts to a new topic called Floating Point to Fixed Point.

Now, more importantly,
Cearn wrote:
If you temporary pointers like I did here, the datatype of the original addresses becomes irrelevant. And the map looks weird how?
It looks weird, because the first map I made was made incorrectly, then I tried Mirach, which exported u32 data, so I had to change the code to this:
Code:
for(i=0;i<1024;i++) bg1.mapData[i*2] = CharDataMapData[i];
, but then only every other collumn is shown.

Now, if you all really want me to create a new topic on the FPM, I will, but I see no point. (Haha... fixed point... gettit?)
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104639 - Kirby - Sun Oct 01, 2006 2:02 am

Never mind about the map thing, I've fixed it. And the error I was talking about on page 3 is...
Code:
Characters[edit].CharaXaccel = base.CharaXaccel;
   Characters[edit].CharaXaccelEnabled = base.CharaXaccelEnabled;
   Characters[edit].CharaXspeed = base.CharaXspeed;
   Characters[edit].CharaXspeedEnabled = base.CharaXspeedEnabled;
   Characters[edit].CharaYaccel = base.CharaYaccel;
   Characters[edit].CharaYaccelEnabled = base.CharaYaccelEnabled;
Right in the definition of Characters.h, there was an error. If you've peeked at thegamefreak0134's State code before, you'd know what this is. I copied all of the base's data to the character... except for it's Y data. Because of this, WaddleDee could not fall to the ground and start walking, which left him floating in the air. Full points to anyone else who found this out!

Now, if anyone knows where I can get good Kirby tilesets, or good enemy sprites, please tell me.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104642 - tepples - Sun Oct 01, 2006 2:19 am

Kirby wrote:
Now, if anyone knows where I can get good Kirby tilesets, or good enemy sprites, please tell me.

From backups of your own Game Paks.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#104645 - Kirby - Sun Oct 01, 2006 2:48 am

tepples wrote:
Kirby wrote:
Now, if anyone knows where I can get good Kirby tilesets, or good enemy sprites, please tell me.

From backups of your own Game Paks.
Actually, that's not a bad idea! Thanx! I didn't think of that.

Now I'm up to getting Kirby to collide with WaddleDee. I know this is probably very simple, but I've had problems with it in all of my games. After enemy collision is complete, I'll be able to start working on the real game!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104659 - poslundc - Sun Oct 01, 2006 8:20 am

Cearn wrote:
Nope. At least not with standard DKP flags. It uses __aeabi_lmul for the 64bit multiply, which is a THUMB function. A fairly long thumb function to boot. Quick and dirty tests with various versions of fix_mul() doing 123456*123456:
Code:
pure THUMB  | 339 cycles
ARM/ROM     | 109 cycles
ARM/IWRAM   | 69 cycles

Even the ARM/ROM version is a good deal faster than THUMB with the __aeabi_lmul call. FYI: if the cycle counts for the ARM functions seem high: remember that there's a 30-60 cycle function overhead.


I've not used DKP, but I seem to remember it generating bx instructions into ARM code, back and forth, in DKA. That was a few years ago that I was looking into that stuff, though, so I may be remembering something else. (And no, tepples, it certainly wouldn't give any consideration to what memory section the generated code should be put into.)

I'm a little shocked by those ARM/IWRAM timings as well. But it's too late at night for me to hunt down my calculator, instruction timing sheet and list of memory region waitstates to try and figure it out for myself. But I instinctively think a good optimizing compiler should be able to do a somewhat better job than that (even having to set up the function call in Thumb), if you are using non-default ROM waitstates.

Dan.

#104674 - Cearn - Sun Oct 01, 2006 12:51 pm

Kirby wrote:
Second, I know what Fixed Point Math is. I know my program doesn't actually do fixed point math, it just converts a division problem into a shift problem. I call it FPU2FPM because the division IS floating-point, and the shift SEEMS like fixed point. I read all about fixed point at TONC. OK?
Well, yes, it is part of fixed point, but a very small part. When it comes to scrolling, there is no reason to use divisions at all, just use additive speeds. But I think you got that already :P

Kirby wrote:
Third, Cearn, I tested out the #'s with 64, not 12. As I said before, the margin of error is about the number of digits in x. It does work. In my game, when using the system, you can hardly tell that there is a scrolling problem, and that's in VBA at 4x Video mode!

64 just happens to be one of the numbers in which the results are still exact if you use 13/32. Most of the surrounding numbers aren't. It may be hardly noticeable right now, but it should be easy to find something where it isn't noticeable at all. For things like speeds being off by 1 may not be so significant because the motion blurs it away a little; it's to be expected that there's some variation in the last digit. But division is usually used for, well, dividing things. Like x/60 for a seconds to minute conversion, or number->decimal string (x/10). In these cases it is very important to get it exactly right. For example, an hour's worth of seconds, you need x*4370>>18, or (x>>2)*4370>>16). For more hours you'd need more bits, but at some point you'll run into multiplication overflow.
As an aside from that, because the scrolling speed should be a normal FP number, it is better to have it in the same precision as the rest of your FP numbers; a .5 FP is a little non-conventional.

Kirby wrote:
Now, more importantly,
Cearn wrote:
If you temporary pointers like I did here, the datatype of the original addresses becomes irrelevant. And the map looks weird how?
It looks weird, because the first map I made was made incorrectly, then I tried Mirach, which exported u32 data, so I had to change the code to this:
Code:
for(i=0;i<1024;i++) bg1.mapData[i*2] = CharDataMapData[i];
, but then only every other collumn is shown.

Yes, because the types are off and you're copying to every other column by the 2*i. Which is why you need the typecasts. Or just a dedicated copy routine that does the casts internally. That's why memcpy uses void pointers, it doesn't care what datatypes you give it. Two examples:

Code:
int ii;
u16 *dst= (u16*)bg1.mapData, *src= (u16*)CharDataMapData;
int size= size_in_bytes;

for(ii=0; ii< size/2; ii++)
    dst[ii]= src[ii];

Code:
int ii;
u32 *dst= (u32*)bg1.mapData, *src= (u32*)CharDataMapData;
int size= size_in_bytes;

for(ii=0; ii< size/4; ii++)
    dst[ii]= src[ii];

If you use the data directly you always have to check what the original types are, which will be different for every converter and an annoyance to check all the time. With temporaries you won't have to and you can generally choose any type you want to. I generally cast to u32 because copying will be twice as fast. Of course, it's still better to use a dedicated copier because they can be a good deal faster than manual loops.


poslundc wrote:
I've not used DKP, but I seem to remember it generating bx instructions into ARM code, back and forth, in DKA. That was a few years ago that I was looking into that stuff, though, so I may be remembering something else. (And no, tepples, it certainly wouldn't give any consideration to what memory section the generated code should be put into.)

DKA had four different types of libraries, so it's quite possible that it picked ARM code where applicable. As far as I've been able to make out, DKP's C libraries are thumb only.

poslundc wrote:
I'm a little shocked by those ARM/IWRAM timings as well. But it's too late at night for me to hunt down my calculator, instruction timing sheet and list of memory region waitstates to try and figure it out for myself. But I instinctively think a good optimizing compiler should be able to do a somewhat better job than that (even having to set up the function call in Thumb), if you are using non-default ROM waitstates.
Remember, guys, I said quick and dirty. Those results here were from a very simple test case, a carefully picked minimal test case could stand to lose 20 cycles (clock cycles) or so. But then, in real use you won't have minimal cases as well, so the results should give a decent indication.

Anyway, it's true that the absolute minimum function overhead is just bl+bx, which is 5S+2N. With default waitstates and ROM code that translates to 25. In EWRAM (multiboot / NDS main ram maybe too) it'd be 21, with 3/1 ROM waitstates it's 18. And in an ideal world (or cache I presume) it's only 7; however, most GBA code is still in ROM. Calling an IWRAM function from ROM and returning adds 1 ldr+1 bx -(iwram diff in bx) for 14 extra cycles.
And then there's setting up registers for the function's arguments, potential stackwork and possibly a reshuffle of registers in both caller and callee. If the function is called inside a loop, calling the function prohibits certain optimisations that would have been possible if the code was inlined. Of course, all of this highly depends on function called, so it's really difficult to give a one-size-fits-all figure. 30-60 will have to do for now.

#104723 - Kirby - Sun Oct 01, 2006 10:50 pm

Cearn wrote:
64 just happens to be one of the numbers in which the results are still exact if you use 13/32. Most of the surrounding numbers aren't.
My program tests numbers with 64, and if works, it then checks with every # between 2 and (9 x Len(y))
Cearn wrote:
Kirby wrote:
Now, more importantly,
Cearn wrote:
If you temporary pointers like I did here, the datatype of the original addresses becomes irrelevant. And the map looks weird how?
It looks weird, because the first map I made was made incorrectly, then I tried Mirach, which exported u32 data, so I had to change the code to this:
Code:
for(i=0;i<1024;i++) bg1.mapData[i*2] = CharDataMapData[i];
, but then only every other collumn is shown.
Yes, because the types are off and you're copying to every other column by the 2*i. Which is why you need the typecasts. Or just a dedicated copy routine that does the casts internally. That's why memcpy uses void pointers, it doesn't care what datatypes you give it.
Yeah, I fixed this by changing to mem cpy:
Code:
memcpy((u32 *)ScreenBaseBlock(24),&CharDataMapData, 2048);
I also added some nice code that makes an HP bar for Kirby:
Code:
void UpdateHP(u16 hp)
{
   u16 i;
   if (hp != hadHP) {
      if (hp > hadHP) {
         for (i=1;i<=hp;i++) {AddBar(i);}
      } else {
         for (i=5;i>hp;i--) {RemoveBar(i);}
      }
      hadHP = hp;
   }
}

void AddBar(u16 bar)
{
   switch (bar) {
      case 1: memcpy((u16 *)hpBars[8],5,1);
            memcpy((u16 *)hpBars[9],19,1);
            break;
      case 2: memcpy((u16 *)hpBars[6],5,1);
            memcpy((u16 *)hpBars[7],19,1);
            break;
      case 3: memcpy((u16 *)hpBars[4],5,1);
            memcpy((u16 *)hpBars[5],19,1);
            break;
      case 4: memcpy((u16 *)hpBars[2],5,1);
            memcpy((u16 *)hpBars[3],19,1);
            break;
      case 5: memcpy((u16 *)hpBars[0],5,1);
            memcpy((u16 *)hpBars[1],19,1);
   }
}

void RemoveBar(u16 bar)
{
   switch (bar) {
      case 1: memcpy((u16 *)hpBars[8],0,1);
            memcpy((u16 *)hpBars[9],0,1);
            break;
      case 2: memcpy((u16 *)hpBars[6],0,1);
            memcpy((u16 *)hpBars[7],0,1);
            break;
      case 3: memcpy((u16 *)hpBars[4],0,1);
            memcpy((u16 *)hpBars[5],0,1);
            break;
      case 4: memcpy((u16 *)hpBars[2],0,1);
            memcpy((u16 *)hpBars[3],0,1);
            break;
      case 5: memcpy((u16 *)hpBars[0],0,1);
            memcpy((u16 *)hpBars[1],0,1);
   }
}
But, when the bar is removed, there is a little blue mark on the map where the bar was. What should I type in for memcpy to have this blue thing removed. (hpBars is an array of addresses) (Oh yeah, by the way, hpBars is an INT array!!!!!)

And I don't think I made it clear before what I wanted to do. Character collision, I can do. RPG's are especially easy. But what I want to do is use a more advanced system that collides if the pixels overlap, not just if the bounderies overlap.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104737 - tepples - Mon Oct 02, 2006 6:08 am

Kirby wrote:
But what I want to do is use a more advanced system that collides if the pixels overlap, not just if the bounderies overlap.

Pixel boundaries are generally considered too rough to be effective collision boxes. They often result in characters getting stuck on walls and each other. This is why most side-scrollers even on GBA, which arguably has enough CPU power for, use separate geometry such as bounding circles or boxes for hit detection.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#104743 - Kirby - Mon Oct 02, 2006 7:31 am

Updates:
    I've added a pause menu with an inventory list and a nice feature called Kirby's Journal. It's not much, but it will be a thing which Kirby writes in every day, so every once in a while in the game, you can press START, press ->, and you will be in Kirby's Journal. Actually... (see Kirby's Journal)

    I've started creating the story (not putting it into the game yet, of course)

    I've collected more SFX.

Also, in my game I will not be using pixel collision for map collision, and when you collide with enemies, you would get hurt and start flashing for a few moments. While you are flashing, you cannot get hurt. (any self-respecting Kirby fan knows that)
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104748 - Cearn - Mon Oct 02, 2006 9:12 am

Kirby wrote:
I also added some nice code that makes an HP bar for Kirby:
Code:
void UpdateHP(u16 hp)
{
   u16 i;
   if (hp != hadHP) {
      if (hp > hadHP) {
         for (i=1;i<=hp;i++) {AddBar(i);}
      } else {
         for (i=5;i>hp;i--) {RemoveBar(i);}
      }
      hadHP = hp;
   }
}

void AddBar(u16 bar)
{
   switch (bar) {
      case 1: memcpy((u16 *)hpBars[8],5,1);
            memcpy((u16 *)hpBars[9],19,1);
            break;
      case 2: memcpy((u16 *)hpBars[6],5,1);
            memcpy((u16 *)hpBars[7],19,1);
            break;
      case 3: memcpy((u16 *)hpBars[4],5,1);
            memcpy((u16 *)hpBars[5],19,1);
            break;
      case 4: memcpy((u16 *)hpBars[2],5,1);
            memcpy((u16 *)hpBars[3],19,1);
            break;
      case 5: memcpy((u16 *)hpBars[0],5,1);
            memcpy((u16 *)hpBars[1],19,1);
   }
}

void RemoveBar(u16 bar)
{
   switch (bar) {
      case 1: memcpy((u16 *)hpBars[8],0,1);
            memcpy((u16 *)hpBars[9],0,1);
            break;
      case 2: memcpy((u16 *)hpBars[6],0,1);
            memcpy((u16 *)hpBars[7],0,1);
            break;
      case 3: memcpy((u16 *)hpBars[4],0,1);
            memcpy((u16 *)hpBars[5],0,1);
            break;
      case 4: memcpy((u16 *)hpBars[2],0,1);
            memcpy((u16 *)hpBars[3],0,1);
            break;
      case 5: memcpy((u16 *)hpBars[0],0,1);
            memcpy((u16 *)hpBars[1],0,1);
   }
}
But, when the bar is removed, there is a little blue mark on the map where the bar was. What should I type in for memcpy to have this blue thing removed. (hpBars is an array of addresses) (Oh yeah, by the way, hpBars is an INT array!!!!!)

You're using memcpy to set a single byte? In an integer array? Cast as a (u16*) ?

That's ... that's ... NO!! >_<

You don't use memcpy() for variables, you just use a single assignment ('='). memcpy() is for big blocks of transfers, and only becomes beneficial after 16 bytes or so. For single variables, by the time you reach the inside of the function you could have set the value 10 times already.
And, of course, if hpBars is an array of pointers to integers ('int *hpBars[]' ?), you're only setting the lower byte in the integer, not the whole integer. Also, if you find yourself writing the exact same code except for one or two values, you're probably doing something wrong. In this case, you can replace both functions with simple arithmetic.

Code:
void UpdateHP(int hp)
{
    int ii;

    if(hp == hadHP)
        return;
   
    if(hp>hadHP) {
        // Assuming hp has a max of 5?
        for(ii=0; ii<hp; ii++) {
            hpBars[ 8-2*ii][0]= 5;
            hpBars[ 9-2*ii][0]= 19;
        }
    } else {
        for(ii=hp; ii<5; ii++) {
            hpBars[ 8-2*ii][0]= 0;
            hpBars[ 9-2*ii][0]= 0;
        }
    }
    hadHP= hp;
}
Should be possible to clean it up further, but I'm not exactly sure how hpBars fits into all this and why it's an array of pointers rather than an array of integers. Notice that I changed the index from u16 to int and let it start at the usual 0 instead of 1. Any particular reason why you made it start at 1?

#104749 - Kirby - Mon Oct 02, 2006 10:03 am

Cearn wrote:
Notice that I changed the index from u16 to int and let it start at the usual 0 instead of 1. Any particular reason why you made it start at 1?
Particular reason why I made it start at 1:
    It's a HP bar! It starts at 1 because 0 represents death! Kirby can have 5 HP (yes, I know that is different than usual), and when it gets to 0, poof. ****....
Cearn wrote:
Should be possible to clean it up further, but I'm not exactly sure how hpBars fits into all this and why it's an array of pointers rather than an array of integers.
Maybe then, you'd like to see what's in hpBars:
Code:
const u32 hpBars[10] = {100713628,100713692, //Last Bar
100713626,100713690, //4'th Bar
100713624,100713688, //3'rd Bar
100713622,100713686, //2'nd Bar
100713620,100713684}; //1'st Bar
hpBars is an array of ADDRESSES. Sorry, but I don't think you exactly got what it's doing. It's displaying 5 bars that represents Kirby's HP! When he loses 1 hp, a bar vanishes. When he has an energy drink, two bars appear!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#104759 - Cearn - Mon Oct 02, 2006 12:39 pm

Kirby wrote:
Maybe then, you'd like to see what's in hpBars:
Code:
const u32 hpBars[10] = {100713628,100713692, //Last Bar
100713626,100713690, //4'th Bar
100713624,100713688, //3'rd Bar
100713622,100713686, //2'nd Bar
100713620,100713684}; //1'st Bar
hpBars is an array of ADDRESSES. Sorry, but I don't think you exactly got what it's doing. It's displaying 5 bars that represents Kirby's HP! When he loses 1 hp, a bar vanishes. When he has an energy drink, two bars appear!

I see now, it's just 5 hearts or something like that on a background. In that case you don't need the hpBars array at all, just a pointer to the screen entry where the hearts start, in this case 0x0600C494, which is screenblock 24, entry (10, 18) I think. Or better, use an offset from the start of the screenblock, since hardcoded addresses (especially in decimal) are generally a bad idea.

Assuming we're talking about a text background:
Code:

// Setup pointer to bar in the screenblock
// u16 *hpBar= &se_mem[24][18*32+10];

void UpdateHP(int hp)
{
    int ii;

    if(hp == hadHP)
        return;

    for(ii=0; ii<hp; ii++)
    {   hpBar[ii]= 5;   hpBar[ii+32]= 19;   }
   
    for(ii=hp; ii<5; ii++)
    {   hpBar[ii]= 0;   hpBar[ii+32]=  0;   }

    hadHP= hp;
}

#105024 - Kirby - Thu Oct 05, 2006 8:37 am

Thanx for the tip, Cearn. It really does work.

This post's news:
    I've completed Character collision (it works better than I expected it to)

    I've started the Kirby Journal contest. Check out it here.

    I'm still having problems with Chara frame update. (see below)
Thanx for the advice, thegamefreak0134, but it doesn't work for me. And anyway, that wasn't even my problem. My problem is that it won't do all of the characters at the same time, it wants me to write out each memcpy... I'm currently using this:
Code:
void frameUpdate() {
   u16 i;
   for (i=0;i<NumCharas;i++) {
      if (Characters[i].map == curmap) {
         if (Characters[i].base == 1) {
            memcpy( (u16 *)Characters[i].memLoc, waddledeeAnim[Characters[i].frame], 256 );
         }
      }
   }
   //memcpy( (u16 *)0x6010800, waddledeeAnim[Characters[0].frame], 256);
}
, but even then it goes too slow. Base is a system that updates each character of a certain kind. My two characters are waddledees, so they are both base 1. It works great, but as I said before, it makes it seem as if I'm still doing floating-point division. Does anybody know why this is happening or how to fix it, because it is practically the only think blocking me from starting to make the real game!
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#105026 - Cearn - Thu Oct 05, 2006 9:06 am

GBA-wise, memcpy runs at around 5.8 cycles/byte, so ~1500 for 256 bytes. While there are faster routines (lookup DMA and CpuFastSet), if these are the only two sprites right now, this is nowhere near enough to cause significant slowdown. However, it is possible that everything else is slow and that adding this code is the final straw; the real problem could very well be elsewhere. Set up some timers and check how long various parts take, that should point you to the parts you need to concentrate on.

Also, what are your compiler options? Are you using -mthumb and at least -O1?

#105265 - Kirby - Sat Oct 07, 2006 3:13 am

Cearn wrote:
Are you using -mthumb and at least -O1?
I'm using -mthumb-interwork, not -mthumb. And, I just found out that using -O2 makes it work. I read somewhere that too much optimization will ruin the game. Is -O2 dangerous, or can I use it?
Cearn wrote:
Set up some timers and check how long various parts take, that should point you to the parts you need to concentrate on.
Though I don't like to admit it, I'm not that good with timers. Plus, I can only use 3 now because I'm using AAS, which uses Timer 1.

EDIT: OK. New problem. With any kind of optimization, I'm getting a problem:

-O0 (No op)
    Map with AI characters goes too slow
-O1
    Game sticks on AAS splash screen and music is twice too fast
-O2
    SRAM is deleted on reload
-O3
    -O2 and BIOS calls freeze game (fixed this one: -O3 optimizeses away null functions, which includes ones with asm in them)

_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#105289 - Cearn - Sat Oct 07, 2006 11:22 am

What optimization mostly does is prune inefficient code; it isn't necessarily dangerous unless you rely on inefficient code. For example:
Code:

... Do stuff

for(ii=0; ii<40000; ii++)
    ;   // wait a while

... Do more stuff

In -O0 this would wait a while, but the optimizer will remove the loop because it doesn't actually do anything. But if this was how one timed the action, everything would now go too fast. The 'danger' part that is sometimes mentioned probably refers to checking registers like REG_VCOUNT:
Code:

while(REG_VCOUNT<160)
    ;   // Wait for the VBlank

because REG_VCOUNT isn't updated inside the loop, the optimizer knows that this would produce an infinite loop and halt. Something similar might be going on with the BIOS calls. The magic word for cases like these is volatile.

The timer business might not be necessary now; non-optimized code can be really, really slow, so that could very well have been the problem all along. If turning on optimizations breaks the code, chances are you were doing a number of things you weren't supposed to.

#105369 - Kirby - Sun Oct 08, 2006 6:10 am

Cearn wrote:
If turning on optimizations breaks the code, chances are you were doing a number of things you weren't supposed to.
It doesn't "break" the code. I have a function that runs at the start of the program that checks the SRAM for zeros. If it is zero-filled, it will reset the SRAM to 0xFF. I think the optimizer may be skipping the SRAM check, because when I commented out the function call, the save stays.

Also, I believe I have finally made enough of the game system to start making the game. The system currently includes:
  • Title Screen
  • Sound
  • One near-fully playable character
  • Scrolling maps
  • Intro scene
  • AI characters
  • HP and lives
  • HP and lives bar
  • Text System
  • Pause Menu
  • Inventory
  • Kirby's Journal
  • Kirby's Journal Cover Page

_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#105701 - Kirby - Wed Oct 11, 2006 7:50 am

Small Request here: Does anyone know where I can get GOOD Kirby tilesets? It's really the only thing blocking me from making the game. And, no. The tilesets in the GBA roms are messy and impossible to work with.

Also, please note that I have posted a previous version of the demo to www.gbadev.org. Sometime, I'll post the complete game system, but that'll be all. If you have read the Kirby's Journal topic, you would see that I'll be trying to sell this someday, so posting a full game onto the internet would be... how should I say this... stupid.
_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#105708 - Mucca - Wed Oct 11, 2006 10:24 am

No more stupid than trying to sell a game with stolen tilesets.

#105785 - tepples - Wed Oct 11, 2006 10:01 pm

Kirby wrote:
Small Request here: Does anyone know where I can get GOOD Kirby tilesets?

Sure. But first I'll have to see your Nintendo license number to make sure that you are authorized to use GOOD Kirby tilesets in a product that you plan to sell.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#105854 - Kirby - Thu Oct 12, 2006 5:31 pm

tepples wrote:
*Snap* *snap*
Mucca wrote:
*Snappy* *Snappy* *snap* *snap* *snap*
OK OK! Two things. First of all, I will end up getting a license, because otherwise I can't use Kirby. Second, I've decided to make my own tilesets after the remarks I'm getting.
Everybody else wrote:
Yes, Kirby must be right, shame on you tepples and Mucca!

_________________
Knock-knock.
Whose there?
Kirby.
Kirby-who?
I told you! Kirby!

#105863 - tepples - Thu Oct 12, 2006 6:51 pm

Kirby wrote:
Everybody else wrote:
Yes, Kirby must be right, shame on you tepples and Mucca!

Powergaming today?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#105869 - sgeos - Thu Oct 12, 2006 7:13 pm

Kirby wrote:
I will end up getting a license, because otherwise I can't use Kirby.

And how much money do you have? What is up with people cloning HAL's games and expecting to get licenses to do it? Are you even incorporated yet? How many people do you have on staff? What are your office facilities?

-Brendan