Omnimaga
Calculator Community => TI Calculators => Calculator C => Topic started by: Kiligolo on February 02, 2011, 08:11:32 am
-
Hello,
I would like to program in C on my TI-89 Titanium and make one (tile) map. I looked at the code a bit of Zelda (http://ti.bank.free.fr/index.php?mod=archives&ac=voir&id=90), but it was terrible...
Does someone could tell me specifically how to make a map and sprites in C?
I already downloaded TIGCC.
Thank you!
-
What is your experience level with the C language?
-
I know how to make a sprite now, but not maps.
unsigned char homme[] = {
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
};
-
I know how to make a sprite now, but not maps.
unsigned char homme[] = {
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
};
First off... Here are some great resources for programming in C with TIGCC:
* TIGCC Documentation (http://tigcc.ticalc.org/doc/)
* Technoplaza Tutorials (http://www.technoplaza.net/programming/)
* The C Book (http://publications.gbdirect.co.uk/c_book/)
A tilemap is essentially an array of sprites. So, if this is one sprite:
unsigned char homme[8] =
{
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
};
Then an array of sprites (tilemap) would look like this:
unsigned char homme[4][8] =
{
{
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
},
{
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
},
{
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
},
{
0b01111110,
0b11111111,
0b10100101,
0b10000001,
0b01111110,
0b10100101,
0b10111101,
0b01111110
},
};
Keep in mind... in your example as well as my example, the sprite is just a simple black & white sprite without a corresponding mask. If you intend on using grayscale along with masks, then to completely define each sprite you will need a sprite for each video plane as well as a sprite mask (total of 3 sprites).
Do you understand the purpose of a sprite mask? How about the idea of multiple planes for grayscale? If not... the TIGCC Documentation is fairly helpful.
I'll be around if you have any more questions.
-
What is the routine to call a tile map?
-
What is the routine to call a tile map?
I may have been a little misleading with my previous post...
A traditional tilemap is essentially a 2 dimensional array of sprites that represents a visual 2D map for use in some types of games.
Example of an 8x8 tilemap:
unsigned char game_tilemap[8][8] =
{
{0,0,1,1,1,1,0,0},
{0,1,1,1,0,0,0,0},
{1,1,1,0,0,0,0,3},
{1,0,0,3,0,4,0,3},
{0,0,0,0,0,0,0,0},
{3,0,3,0,0,2,2,2},
{3,0,0,0,2,2,2,2},
};
legend: 0 = grass, 1 = mountain, 2 = water, 3 = tree, 4 = house
Notice that each item in the game_tilemap uniquely represents the index of the sprite that will be drawn.
Next we will need to define a sprite for each item used in the tilemap. From the above example, there will be 5 sprites and each will visually represent the items defined in legend (0 = grass, 1 = mountain, 2 = water, 3 = tree, 4 = house).
Now it is time to actually define the array of sprites utilized by the tilemap (note: 5 sprites, each sprite 8 pixels tall):
unsigned char game_sprites[5][8] =
{
{ // 0 = grass
0b10001000,
0b00000000,
0b00100010,
0b00000000,
0b10001000,
0b00000000,
0b00100010,
0b00000000,
},
{ // 1 = mountain
0b00000000,
0b00010000,
0b00111000,
0b01111100,
0b11011110,
0b10101111,
0b01110111,
0b11111011,
},
{ // 2 = water
0b11110000,
0b00001111,
0b11110000,
0b00001111,
0b11110000,
0b00001111,
0b11110000,
0b00001111,
},
{ // 3 = tree
0b00000000,
0b00111110,
0b01111111,
0b01111111,
0b01111111,
0b00111110,
0b00001000,
0b00001000,
},
{ // 4 = building
0b00000000,
0b00011000,
0b00111100,
0b01111110,
0b11111111,
0b01100110,
0b01100110,
0b01100110,
},
};
A pointer (or address) to the beginning of an 8bit wide sprite can be defined like this:
unsigned char* pSprite;
Example: if we need to get the address of the tree sprite, then we can do this:
pSprite = &game_sprites[3][0];
or
pSprite = game_sprites[3];
Next we need to draw the tilemap to the screen
int tilemap_x;
int tilemap_y;
int lcd_x;
int lcd_y;
unsigned char* pSprite;
unsigned char sprite_index;
// initialize the lcd Y value since we starting the first row
lcd_y = 0;
for (tilemap_y = 0; tilemap_y < 8; tilemap_y++)
{
// initialize the lcd X value since we are starting a new collumn
lcd_x = 0;
for (tilemap_x = 0; tilemap_x < 8; tilemap_x++)
{
// first, get the sprite index from the tilemap
sprite_index = game_tilemap[tilemap_y][tilemap_x];
// second, get the pointer (address) to the sprite
pSprite = &game_sprites[sprite_index][0];
// draw the sprite to the screen
Sprite8 (lcd_x, lcd_y, 8, pSprite, LCD_MEM, SPRT_RPLC); // refer to TIGCC docs for Sprite8 details
// increment the lcd X position in preparation for next sprite
lcd_x = lcd_x + 8;
}
// increment the lcd Y position in preparation for next row of sprites
lcd_y = lcd_y + 8;
}
Obviously, this is not the most optimized method for drawing a tilemap to the screen; but I think it illustrates each of the main ideas well.
Does this help any?
-
I think that this does help, because you're explaining the principles behind the Genlib and ExtGraph tilemap engines that I mentioned to Kiligolo on TI-Bank :)
-
Thanks.
-
up! ;D
I would like to know, with Sprite16() routine, how do the equivalent of Pt-Off() in Axe. I want all the pixels in the area where the sprite will be turn off until another sprite is displayed.
Is it possible?
-
I replied to you on TI-Bank ;)
-
I know it is off topic but how can I do a pause with a determinate time? (Bad english... ^^')
-
You can:
* use the AMS timers (OSRegisterTimer & friends), based on AUTO_INT_5 whose rate is normally ~19.32 Hz (on HW2 calculators);
* use the programmable rate generator (AUTO_INT_5) directly, and set its rate mostly as you wish through the PRG_* functions;
* use WaitForMillis-type functions, busy wait loops calibrated for waiting approximately the number of seconds passed to the function. Between others, TI-Chess uses such a function for debouncing keypresses.
-
Thanks.
-
Hi there!
I have a problem with grayscales and ams planes. I made a tilemap but when i start the programm there is nothing displayed but the TI isn't frozen i can press ESC key to see a big bug...
int tilemap_x;
int tilemap_y;
int lcd_x;
int lcd_y;
unsigned char* pSprite;
unsigned char sprite_index;
int key = 0, pUserx = 1, pUsery = 1;
unsigned char map1[MAP_Y][MAP_X] =
{
{01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01},
{01,02,02,02,02,02,02,02,00,00,00,00,00,00,00,00,00,00,00,01},
{01,02,02,02,02,02,02,00,00,00,00,05,06,07,00,00,00,00,00,01},
{01,02,02,02,00,00,00,00,00,00,00,10,11,12,00,00,00,00,00,01},
{01,02,00,00,00,00,15,16,00,00,00,13,14,13,00,00,00,00,00,01},
{01,00,00,00,00,00,17,20,00,00,00,21,21,21,03,04,04,03,00,01},
{00,00,00,00,00,00,00,00,00,00,00,00,21,00,03,04,04,03,00,01},
{21,21,21,05,06,07,00,00,00,00,21,21,21,00,03,03,03,03,02,01},
{00,00,21,10,11,12,00,00,21,21,21,00,00,00,00,00,02,02,02,01},
{01,00,21,13,14,13,00,21,21,00,00,00,00,00,02,02,02,02,02,01},
{01,00,21,21,21,21,21,21,00,00,00,00,02,02,02,02,02,02,02,01},
{01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01},
};
ClrScr();
GrayOn();
while (key != KEY_ESC)
{
while (map1[pUsery][pUserx] != 14)
{
//Affichage
//Gris clair
GraySetAMSPlane(LIGHT_PLANE);
lcd_y = 0;
for (tilemap_y = 0; tilemap_y < MAP_Y; tilemap_y++)
{
lcd_x = 0;
for (tilemap_x = 0; tilemap_x < MAP_X; tilemap_x++)
{
sprite_index = map1[tilemap_y][tilemap_x];
pSprite = &game_sprites_light[sprite_index][0];
Sprite8 (lcd_x, lcd_y, 8, vide, LCD_MEM, SPRT_AND);
Sprite8 (lcd_x, lcd_y, 8, pSprite, LCD_MEM, SPRT_OR);
lcd_x += 8;
}
lcd_y += 8;
}
//Gris foncé
GraySetAMSPlane(DARK_PLANE);
lcd_y = 0;
for (tilemap_y = 0; tilemap_y < MAP_Y; tilemap_y++)
{
lcd_x = 0;
for (tilemap_x = 0; tilemap_x < MAP_X; tilemap_x++)
{
sprite_index = map1[tilemap_y][tilemap_x];
pSprite = &game_sprites_dark[sprite_index][0];
Sprite8 (lcd_x, lcd_y, 8, pSprite, LCD_MEM, SPRT_OR);
lcd_x += 8;
}
lcd_y += 8;
}
Sprite8 (pUserx * 8, pUsery * 8, 8, vide, LCD_MEM, SPRT_AND);
Sprite8 (pUserx * 8, pUsery * 8, 8, mec, LCD_MEM, SPRT_OR);
GraySetAMSPlane(LIGHT_PLANE);
Sprite8 (pUserx * 8, pUsery * 8, 8, mec, LCD_MEM, SPRT_OR);
//Demande de touche
key = ngetchx();
if (key)
{
//Actualisation de la position de l'utilisateur
pUserx += (key == KEY_RIGHT) - (key == KEY_LEFT);
pUsery += (key == KEY_DOWN) - (key == KEY_UP);
//Verification de sa position
if (map1[pUsery][pUserx] == 1 || map1[pUsery][pUserx] == 3 || (map1[pUsery][pUserx] > 4 && map1[pUsery][pUserx] != 14 && map1[pUsery][pUserx] < 21) || pUserx == 21 || pUserx == -1 || pUsery == -1 || pUsery == 13)
{
pUserx -= (key == KEY_RIGHT) - (key == KEY_LEFT);
pUsery -= (key == KEY_DOWN) - (key == KEY_UP);
}
if (key == KEY_ESC)
return 0;
}
}
}
GrayOff();
return 0;
}
Thanks.
-
Sorry, I do not know an answer to this, but I am also learning C for the 68K calcs, so I am here just to say thank you ^-^ The code is very helpful for me to study :)
-
Don't write to LCD_MEM in grayscale mode :)
-
It's ranman's code: http://ourl.ca/9035/170957 :p
-
Ranman's code was meant for B/W, but grayscale is another story ;)
See http://debrouxl.github.com/gcc4ti/gray.html#GrayGetPlane :
GrayGetPlane returns a pointer to the grayscale plane plane. Valid values for plane are LIGHT_PLANE and DARK_PLANE. To draw in black, draw in both planes.
Note: Do not assume that any plane is on 0x4C00 when in grayscale mode, due to hardware version 2 support. Also do not assume that the 2 grayscale planes are consecutive, this is not the case on hardware version 1.
Some software grayscale implementations have two consecutive grayscale planes, but the one in TIGCC and GCC4TI doesn't.
-
Thanks it's works!
But now i have a problem when i stop the program. The light plane is displayed in black and there is white rectangles which appear and disapear on the screen, i can't do anything. (Fortunatly i am on tiEmu)...
-
When exiting by ESC, you immediately "return 0;" instead of falling out of the outer while(), which calls GrayOff() ;)
If if still doesn't work properly after that, make sure you aren't drawing past the beginning or the end of the screen, and try using ClipSprite8 instead of Sprite8 :)
And if your map is only read from, but not written to, you really should declare it "static const", so that it ends up as a constant in your program's code, instead of being constructed onto the stack: it will be both smaller and faster.
-
sorry to interrupt again .___.
I tried modifying the code to suite my purposes and I keep getting these errors and I don't know why D:
'MAP_Y' undeclared (first use in this function).
'MAP_X' undeclared (first use in this function).
Unused variable 'map1'.
EDIT: also, KERNELBASE.dll seems to have a lot of issues with me, so ever time I get an error, it freezes and uses up tons of system resources and I have to press F10 a bazillion times and click okay on a bunch of popup error messages
-
You have an error because MAP_X and MAP_Y are not defined in the code you copied.
You have to put before the code :
#define MAP_X 20
#define MAP_Y 12
I guess it should be 20 and 12 according to the table map1, but I'm not sure.
-
Lionel > Yes
Xeda >
#include <tigcclib.h>
#define MAP_X 20
#define MAP_Y 12
It's the map size.
For map1 error:
unsigned char game_sprites[2][8] = //array of sprites
{ //Grass
0b00000000,
0b00101000,
0b00010000,
0b00000000,
0b00000101,
0b00000010,
0b01010000,
0b00100000,
},
{ //tree
0b00000000,
0b00000000,
0b00111110,
0b01111111,
0b01111111,
0b01111111,
0b00111110,
0b00001000,
}
unsigned char map[8][8] =
{
{1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1},
};
//0 = Grass and 1 = Tree
//Drawing:
int tilemap_x, tilemap_y;
int lcd_x, lcd_y;
unsigned char* pSprite;
unsigned char sprite_index;
// initialize the lcd Y value since we starting the first row
lcd_y = 0;
for (tilemap_y = 0; tilemap_y < 8; tilemap_y++)
{
// initialize the lcd X value since we are starting a new collumn
lcd_x = 0;
for (tilemap_x = 0; tilemap_x < 8; tilemap_x++)
{
// first, get the sprite index from the tilemap
sprite_index = game_tilemap[tilemap_y][tilemap_x];
// second, get the pointer (address) to the sprite
pSprite = &game_sprites[sprite_index][0];
// draw the sprite to the screen
Sprite8 (lcd_x, lcd_y, 8, pSprite, LCD_MEM, SPRT_RPLC); // --> TIGCC lib
// increment the lcd X position in preparation for next sprite
lcd_x = lcd_x + 8;
}
// increment the lcd Y position in preparation for next row of sprites
lcd_y = lcd_y + 8;
}
-
Thanks lionel im very tired today i go to sleep! :p
-
Thank you much! I wasn't sure how to define MAP_X and MAP_Y, but now I know! Thanks!
-
Hi! I have a big problem with dynamic allocation...
Here is the code:
int i, j;
unsigned char ** map = malloc(MAP_Y * sizeof(unsigned char));
for (i = 0; i < MAP_Y; i++)
{
map[i] = malloc(MAP_X * sizeof(unsigned char));
}
and at the end :
for (i = 0; i < MAP_Y ; i++)
{
free(map[i]);
}
free(map);
When i start the program, the Ti89 display BUSY icon and freeze but when i add ngetchx(); in this part of code:
int i, j;
unsigned char ** map = malloc(MAP_Y * sizeof(unsigned char));
for (i = 0; i < MAP_Y; i++)
{
printf("%d",i);
ngetchx(); //Here
map[i] = malloc(MAP_X * sizeof(unsigned char));
}
The 89 don't freeze and the program start but when i stop the program and i go to Var-Link menu, my asm program is now and expression (64000bytes).
I'm lost
-
I'm not sure why you need dynamic allocation here ?
(and by they way, two-dimensional arrays suck for efficiency)
The symptoms (BUSY on exit, program size changed, program behaviour changing by adding unrelated lines) point out to severe memory corruption, by overflowing buffers on the heap. Check your code :)
-
Ok i'm going to try without it.
-
I'm not sure but I think you have a memory corruption because when you call malloc for the first time, you will allocate unsigned char for *map.
But in the for loop, the array will contain not an unsigned char, but a pointer so it is a long int.
So the adress to which *map points to will be wrong.
That will cause a corruption when freeing the array.
What you should do is :
unsigned char *map = malloc(MAP_X * MAP_Y * sizeof(unsigned char));
-
Thanks for your help! My program is okay now :p
-
You have the entire rights to make any program for learning purposes - but if you're not making this program for learning purposes, maybe you could join forces with Torio ? :)
-
If he want, why not! Even if I learn C with this program, it can be a serious project if it works but now it's still a test game.
-
OK, it's good if you're still learning. I just wanted to make sure that you were not both, in the exact same time frame, aiming at making a release-quality Pokemon port ;)
-
It's not pokémon but zelda (at the beginning i wanted to make a pokemon game but now it's more a zelda game, i am too tired to rename files... ^^')
-
I'm busy enough with Pokemon for now, so I'm afraid I won't be able to start another project.
However you can still ask me questions if you have problems with your game !
-
I didn't look at his archive further than the name, so I tried to make sure that you two weren't making the same thing; but they're projects with little in common, so you wouldn't be able to work much on each others' project anyway :)
-
I studied your code and spotted another error, which could be also naughty for your calculator :
//Actualisation de la position de l'utilisateur
pUserx += (key == KEY_RIGHT) - (key == KEY_LEFT);
pUsery += (key == KEY_DOWN) - (key == KEY_UP);
//Verification de sa position
if (map1[pUsery][pUserx] == 1 || map1[pUsery][pUserx] == 3 || (map1[pUsery][pUserx] > 4 && map1[pUsery][pUserx] != 14 && map1[pUsery][pUserx] < 21) || pUserx == 21 || pUserx == -1 || pUsery == -1 || pUsery == 13)
{
pUserx -= (key == KEY_RIGHT) - (key == KEY_LEFT);
pUsery -= (key == KEY_DOWN) - (key == KEY_UP);
}
When you check the position of the user and that he tries to go out of the limits, his position will be changed but you will also call map1[pUsery][pUserx] in the if condition...
If pUserx is 21 for example, you will go to a memory place you shouldn't have access to...
-
Yes youre right but im not changing value of memory adress so its ok isnt it?
-
You're not writing out of the bounds of the buffer, but you're nevertheless reading outside the bounds... which is unlikely to yield correct behaviour down the road ;)
-
It's an OR condition : if pUserx = 21 it's true and map[pUsery][pUserx] is useless so it's ok.
-
map[pUserX][pUserY] is useless but it will read the data out of the bounds, though. The simple fact of trying to read out of the bonds of the buffer can be naughty for the calculator.
-
Note, if you decide to take on a Zelda project, you should probably make a small game as your first project to start with, since a larger Zelda would be a huge project if you are still beginning with C. My Zelda clone only had 3 dungeons and I had 3 years of experience in TI-BASIC when I made it.
It's nice to see you around again by the way :D (just make sure to not do the grammar police in the French section like in 2010, though :P). I thought you lost interest in calc-related stuff since you were not on TI-Planet/bank either.
-
If he want, why not! Even if I learn C with this program, it can be a serious project if it works but now it's still a test game.
Zelda is a goal.
-
Ok that's good I guess.