#176380 - MisterLogan - Tue Jul 19, 2011 1:59 am
Hey, I'm not sure this forum is still even alive, although I see posts every couple days so I assume there's a few people around.
I took up coding for the GBA a few weeks ago as a way to learn c++ (although right now it's the only reason I want to know c++), I know most people say to learn a bunch of c++ before you jump into the GBA but that's not really the kind of person I am.
So here's why I'm posting here, I'm trying to keep npcs from walking through the main sprite, which is fixed on the screen, so I wrote in something to reserve tiles in whatever direction you move as soon as it checks that that tile isn't already in use by the background or an npc or whatever, then releases the previous tile you were on when you're one pixel away from the tile you're moving towards.
The tiles that are used/unused are stored in a 16*16 1d array (map size / 16) with 0x0 being used and 0x1 being unused.
Instead of doing what I want there is a small slurry of bugs. First, it doesn't reserve the right tiles, much less free the right ones. Also, if you go towards the bottom of the map, right as the wrapped around tiles gets to the screen, the main sprite vanishes and the npc I have walking around changes into the main sprite.
Here's the function, the commented code is what's causing problems, specifically the lower 4 comments.
(sorry if anything is terrible looking, I don't really know what good code looks like due to just starting, also, i know using division is bad, there's an easy way to take out most of it, plus the modulo, that I haven't put in yet, but I couldn't think of a way to not divide bgX by 16 and get the right value.)
Code: |
void acceptUserInput() {
if (InpVar2 > 0) {
if(InpLoop == 0)
InpLoop = 16;
if (InpLoop == 16){
sprites[0].attribute2 = PRIORITY(1) | (InpVar2 * 2 - 1) * 16;
sprites[1].attribute2 = PRIORITY(2) | ((InpVar2 * 2 - 1) * 16) + 8;
}
if(InpLoop == 8){
sprites[0].attribute2 = PRIORITY(1) |(InpVar2 - 1) * 32;
sprites[1].attribute2 = PRIORITY(2) |((InpVar2 - 1) * 32) + 8;
}
InpLoop -= 1;
if(InpLoop == 0)
InpVar2 = 0;
}
else if (InpVar > 0) {
if(InpLoop == 0)
InpLoop = 16;
if (InpLoop == 16){
sprites[0].attribute2 = PRIORITY(1) | (InpVar * 2 - 1) * 16;
sprites[1].attribute2 = PRIORITY(2) | (InpVar * 2 - 1) * 16 + 8;
}
if(InpVar == 1){
bgY += 1;
a -= 1;
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80 - 15)] = 0x1;
}
else if(InpVar == 2){
bgY -= 1;
a += 1;
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80 + 15)] = 0x1;
}
else if(InpVar == 3){
bgX -= 1;
b += 1;
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16 + 1) + (bgY + 80)] = 0x1;
}
else if(InpVar == 4){
bgX += 1;
b -= 1;
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80)] = 0x1;
}
REG_BG3VOFS = bgY;
REG_BG3HOFS = bgX;
if(InpLoop == 8){
sprites[0].attribute2 = PRIORITY(1) | (InpVar - 1) * 32;
sprites[1].attribute2 = PRIORITY(2) | (InpVar - 1) * 32 + 8;
}
InpLoop -= 1;
if(InpLoop == 0)
InpVar = 0;
}
else {
if(keyDown(KEY_RIGHT)) {
//shhh, ignore this division.
if otmMovable(bgX%MapX/16+1, bgY%MapY/16) {
InpVar = 4;
//the 112 and 80 are the sprites offset in pixels from the map start.
//city_otm[(bgX + 112 / 16 + 1) + (bgY + 80)] = 0x0;
}
else
InpVar2 = 4;
} else if(keyDown(KEY_LEFT)) {
if otmMovable(bgX%MapX/16-1, bgY%MapY/16) {
InpVar = 3;
//city_otm[(bgX + 112 / 16 - 1) + (bgY + 80)] = 0x0;
}
else
InpVar2 = 3;
} else if(keyDown(KEY_UP)) {
if otmMovable(bgX%MapX/16, bgY%MapY/16-1) {
InpVar = 2;
//city_otm[(bgX + 112 / 16) + (bgY + 80) - 16] = 0x0;
}
else
InpVar2 = 2;
} else if(keyDown(KEY_DOWN)) {
if otmMovable(bgX%MapX/16, bgY%MapY/16+1) {
InpVar = 1;
//city_otm[(bgX + 112 / 16) + (bgY + 80) + 16] = 0x0;
}
else
InpVar2 = 1;
}
}
}
|
I tried setting all the tiles in the city_otm array to 0x1 to figure out what it was doing, but it seems random to me.
Anyway, if someone happens to see what I'm doing wrong here at a glance, I would be very grateful.
Thanks
#176383 - gauauu - Tue Jul 19, 2011 3:23 pm
Welcome! Yeah, the forum is at least a little bit alive. There's a lot of us that are still lurking around reading the one or two posts per day.
Anyway, I looked over your code, and I'm really having trouble following it. Without more descriptive variables names (InpVar2? a? b?), I'm really having trouble understanding what each piece of code is trying to do. It's better to name your variables something that really describes what they are.
I'd recommend changing your variable names, adding a few comments, then we can take another look.
#176384 - MisterLogan - Tue Jul 19, 2011 10:35 pm
Alright, awesome to hear this place still has some people.
I couldn't really think of a good name for inpvar without it being really long, but i'll try again. I guess I just forgot to rename a and b, although they don't have much to do with this bit, they're used to hold the other sprites onto the map when it moves.
Code: |
void acceptUserInput() {
///this section is just to animate the sprite when you walk into walls
if (ForwardBlockUsed > 0) {
if(InpLoop == 0)
InpLoop = 16;
if (InpLoop == 16){
//every 32x16 sprite is split into two 16x16 sprites to give them different priorities.
sprites[0].attribute2 = PRIORITY(1) | (ForwardBlockUsed * 2 - 1) * 16;
sprites[1].attribute2 = PRIORITY(2) | ((ForwardBlockUsed * 2 - 1) * 16) + 8;
}
if(InpLoop == 8){
sprites[0].attribute2 = PRIORITY(1) |(ForwardBlockUsed - 1) * 32;
sprites[1].attribute2 = PRIORITY(2) |(ForwardBlockUsed - 1) * 32) + 8;
}
InpLoop -= 1;
if(InpLoop == 0)
ForwardBlockUsed = 0;
}
///ends here
///this moves and animates the main sprite when you walk towards an open block.
else if ( ForwardBlockUnused > 0) {
if(InpLoop == 0)
///always loops things 16 times, moving one pixel every time, to keep everything within 16x16 squares.
InpLoop = 16;
if (InpLoop == 16){
//at the start, changes to the first of the two animation frames in the direction you're going (main sprite goes from 0 to 128 in sprite data.)
sprites[0].attribute2 = PRIORITY(1) | ( ForwardBlockUnused * 2 - 1) * 16;
sprites[1].attribute2 = PRIORITY(2) | ( ForwardBlockUnused * 2 - 1) * 16 + 8;
}
if(ForwardBlockUnused == 1){
bgY += 1;
SpriteHold1 -= 1;
///the + and - 15 on these are to account for the loop being 15 out of 16 steps from completion in either direction.
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80 - 15)] = 0x1;
}
else if(ForwardBlockUnused == 2){
bgY -= 1;
SpriteHold1 += 1;
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80 + 15)] = 0x1;
}
else if(ForwardBlockUnused == 3){
bgX -= 1;
SpriteHold2 += 1;
///should only have to add one here since it's divided by sixteen.
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16 + 1) + (bgY + 80)] = 0x1;
}
else if(ForwardBlockUnused == 4){
bgX += 1;
SpriteHold2 -= 1;
///and nothing on this since you won't have another full count of sixteen on the fifteenth step.
// if(InpLoop == 1)
// city_otm[(bgX + 112 / 16) + (bgY + 80)] = 0x1;
}
REG_BG3VOFS = bgY;
REG_BG3HOFS = bgX;
if(InpLoop == 8){
sprites[0].attribute2 = PRIORITY(1) | (ForwardBlockUnused - 1) * 32;
sprites[1].attribute2 = PRIORITY(2) | (ForwardBlockUnused - 1) * 32 + 8;
}
InpLoop -= 1;
if(InpLoop == 0)
ForwardBlockUnused = 0;
}
///and it ends over here.
///and if neither one of those two code blocks get executed, no key has been pressed, so it loops through this to check keys and decide which code to start running
else {
if(keyDown(KEY_RIGHT)) {
///shhh, ignore this division. (this checks if the block in the direction you pressed is unused)
if otmMovable(bgX%MapX/16+1, bgY%MapY/16) {
//if it's unused it flips this variable to 1, 2, 3, or 4, depending on the direction you pressed. it later uses this number to get the right sprite from memory.
ForwardBlockUnused = 4;
//the 112 and 80 are the sprites offset in pixels from the map start.
//this code should do the same thing as the otmMovable bit without using as much division, i just haven't switched the other bit to this yet
//city_otm[(bgX + 112 / 16 + 1) + (bgY + 80)] = 0x0;
}
else
//if otmMovable doesn't check out, you have a used block in front of you, triggering this instead. just animates you moving against the wall.
ForwardBlockUsed = 4;
} else if(keyDown(KEY_LEFT)) {
if otmMovable(bgX%MapX/16-1, bgY%MapY/16) {
ForwardBlockUnused = 3;
//city_otm[(bgX + 112 / 16 - 1) + (bgY + 80)] = 0x0;
}
else
ForwardBlockUsed = 3;
} else if(keyDown(KEY_UP)) {
if otmMovable(bgX%MapX/16, bgY%MapY/16-1) {
ForwardBlockUnused = 2;
//city_otm[(bgX + 112 / 16) + (bgY + 80) - 16] = 0x0;
}
else
ForwardBlockUsed = 2;
} else if(keyDown(KEY_DOWN)) {
if otmMovable(bgX%MapX/16, bgY%MapY/16+1) {
ForwardBlockUnused = 1;
//city_otm[(bgX + 112 / 16) + (bgY + 80) + 16] = 0x0;
}
else
ForwardBlockUsed = 1;
}
}
///and that ends that.
}
///I hope this explains what's going on here better. To give some context as to where this code goes, it's grouped with a bunch of other functions in the main loop after vblank.
|
Thanks again for the help.
#176385 - Miked0801 - Wed Jul 20, 2011 5:37 pm
Ok, I'm also going to ask for another code cleanup here. Your code is so full of "Magic Numbers", it's very hard to understand what is going on still.
Consider this a code review for your own growth and take nothing I say below personally:
InpLoop = InputLoop?
16 = ?
(ForwardBlockUsed * 2) - 1 == some odd number? * 16 and + 8 ? These powers of 2 don't make sense to someone coming in from the outside. Use constants.
InpLoop itself is a global I'm guessing, another source of confusion and poor maintainability.
Ok, it looks like 16 quite often is your sprite width. Make a constant for that and use kSpriteWidth so we (and you!) know what that number means a week from now.
Oh my. Is every single one of your variables global? That will make this nearly impossible to debug - too many points of failure. Those globals can be changed at any point in the program making debugging nearly impossible.
112 == ?
80 + 15 == ?
112 / 16 = ?
0x1 = ?
Ok, reading your comments, 112 and 80 are offsets. Const them please. Good code can almost be read in english.
If forwardBlockUsed is being checked on nearly every value, consider using a switch statement to make this clearer.
And then at the bottom * 32 , * 32 + 8 ???
Also, the amount of ifs and elses hurts my head as well. Strongly consider breaking up this function into a few smaller functions to help partition it against other points of failure. The idea being, if you get each individual function to work correctly, you don't need to worry about it when debugging other things.
otmMovable(bgX%MapX/16-1, bgY%MapY/16)
Are you really, really sure of your order of operations here? I'm not and I've been coding for many, many years. Drop these into a local var and use parenthesis. My guess is there is actually a bug on this line with mod being called when you don't expect it.
---------------------------------------
Anyways, what makes you so certain that your bug is in this module/function? Since you are using globals for everything and are willy/nilly accessing arrays via these global values at will, the error could be anywhere. You have quite the mess here to clean up before you will be able to effectively debug this. May I also strongly (very strongly) suggest that you drop asserts all over the place here? The kind of errors you are describing feel like array overwrite/underwrite issues where you are feeding bogus indexes into your arrays and trashing memory everywhere. An assert before accessing an array can go a long ways in verifying and debugging this portion of your code.
Code: |
void acceptUserInput(int passInVarsToNotUseGlobalsPlease)
{
if (ForwardBlockUsed > 0)
{
handle0Block();
return;
}
// Easy to visually mixup ForwardBlockUsed and Unused. Consider using different var names...
if(ForwardBlockUnused > 0)
{
DescriptiveFunctionNameForThisCaseBecauseIOnlyTypeItOnce(LocalVar1, LocaVar2, AsManyVarsAsItTakesToGetRidOfGlobalDeath);
}
else
{
DescriptiveFunctionNameForThisCaseAsWellBecauseIOnlyTypeItOnce();
}
}
// And we've removed 1/2 the indentation and isolated some stuff.
// Now some sample functions:
void DescriptiveFunctionNameForThisCaseAsWellBecauseIOnlyTypeItOnce()
{
int keystate = keyDown(ALL_KEYS);
switch(keyState)
{
case KEY_RIGHT:
handleKeyRightState();
break;
...
default:
// Bad key state found
assert(false);
}
}
void handleKeyRightState()
{
// This allows ease of debugging with watches
int MovableX = ((bgX % MapX) / kSpriteWidth) + 1;
int MovableY = ((bgY % MapY) / kSpriteWidth) + 0;
// Actually, there's a syntax error here as otmMovable() isn't surrounded by parenthesis for the if. Is this supposed to be a comment?
...
}
|
#176386 - MisterLogan - Wed Jul 20, 2011 9:20 pm
Haha, I won't take anything personally, just realize nobody has shown me what things are good and bad, so of course I used global variables, it seemed like the best way to do stuff.
As far as otm_moveable, that from a friend who was playing with the code at the start of my learning c++, I think he defines it in a header, but there's even more division and such so i planned to remove it (hence the "ignore this").
I don't know anything about asserts but I'll look up how to use them.
Thanks again for the help, I'll try and implement your advice later tonight and post what I get.
#176387 - MisterLogan - Thu Jul 21, 2011 7:54 am
I almost have everything back to normal with new code, but I'm guessing I have to write my own macro to use asserts?
#176391 - gauauu - Thu Jul 21, 2011 3:23 pm
If I remember correctly, libnds comes with an assert function.
http://libnds.devkitpro.org/a00141.html#_details
Code: |
sassert(somethingThatShouldBeTrue, "That thing wasn't true!");
|
You may or may not need to include sassert.h, not sure if that always gets automatically included with the other libnds headers.[/code]
#176392 - MisterLogan - Fri Jul 22, 2011 1:40 am
Will that work for the GBA? I found an assert.h buried in devkitadv and included that, I can't seem to get it working though...
Miked, at one point in you example code you used this here:
Code: |
int keystate = keyDown(ALL_KEYS);
|
I don't have any define for ALL_KEYS in the headers I'm using (the set I'm using is just called "gba.h") but I couldn't find ALL_KEYS in the TONC headers either, so what exactly is ALL_KEYS?
#176394 - GLaDOS - Fri Jul 22, 2011 5:45 am
I'd recommend coding it for the PC first and doing debugging there. It's much easier than trying to debug directly on the GBA.
That's what I did for the game I'm working on right now.
#176406 - Miked0801 - Fri Jul 22, 2011 6:24 pm
The ALL_KEYS was just an idea for you. I don't have access nor use the community libs. I'm forced to work with Nintendo stuff for good or bad, though I haven;t done GBA stuff now for years :)
If you don't have access to an all keys thingy, then by all means use a if/else chain to check.
#176426 - gauauu - Mon Jul 25, 2011 2:53 pm
MisterLogan wrote: |
Will that work for the GBA? I found an assert.h buried in devkitadv and included that, I can't seem to get it working though...
|
Oh, sorry, my brain was in DS mode. No, I don't believe that will work for GBA. You may have to make your own.
#176431 - wintermute - Wed Jul 27, 2011 12:18 am
MisterLogan wrote: |
Will that work for the GBA? I found an assert.h buried in devkitadv and included that, I can't seem to get it working though...
|
Please tell me you're not using devkitadv
with devkitARM and libgba the standard assert will work just fine, as it will with libnds for DS code.
Code: |
#include <gba.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//---------------------------------------------------------------------------------
// Program entry point
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
// the vblank interrupt must be enabled for VBlankIntrWait() to work
// since the default dispatcher handles the bios flags no vblank handler
// is required
irqInit();
irqEnable(IRQ_VBLANK);
consoleDemoInit();
iprintf("Hello World!\n");
assert(1==0);
while (1) {
VBlankIntrWait();
}
}
|
Basically you just need to make sure that stdio is hooked up to enable printing. I think libtonc has stdout/stderr output features somewhere but you'll need to look.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog
#176443 - MisterLogan - Thu Jul 28, 2011 2:59 am
I stopped getting email alerts so I thought this thread didn't have any replies, but I guess it has had a few.
My original question has become irrelevant due to switching how a lot of things work, mostly to get infinite scrolling maps working and taking miked's advice, but people are telling my about handy stuff in here so I'm going to keep asking a few more questions. In a funny way it was sort of fixed by letting go of the problem for a while and working on something else.
Quote: |
Please tell me you're not using devkitadv |
I am using devkitadv, I didn't know there was anything wrong with it (or that there was another optio ) and so far it's worked well enough. As they say, if it isn't broken, don't fix it. What are the main differences between them, I'm guessing an actual complete library for various registers is part of it? I ended up mixing the awful headers i was using with the tonc headers, but I would switch again if libgba is better.
Tonight I'm starting some research into audio, but it seems like a terribly daunting task, anything basic someone new to this should know?
edit: Noticing a suspicious amount of "WinterMute: Admin" on everything GBA related... :1 guess I'll just go ahead and grab devkitARM.
Thanks everyone.
Last edited by MisterLogan on Thu Jul 28, 2011 4:36 am; edited 2 times in total
#176444 - Dwedit - Thu Jul 28, 2011 3:33 am
For audio, there's the easy way, and the hard way.
The easy way: Use Maxmod. It comes included with the current versions of DevkitArm.
The hard way: Write a software music synthesizer, and have a vblank or timer interrupt periodically call a function to fill an audio buffer.
edit: yay, post #1337
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."
#176445 - Miked0801 - Thu Jul 28, 2011 5:11 am
Lol - leet you are :)
#176448 - MisterLogan - Fri Jul 29, 2011 1:23 am
One more short question on top of all the others.
I'm using the collision maps that MapED exports now, but I wanted to have NPCs walking around so I figured, "Oh, well I'll just have them change values in the collisions array when they move". So I wrote that up, got a compile error (well, many, but one that matters) telling me that the array was const-ed. So I changed that and compiled, and now the background doesn't show up. I have no idea why it affects the background, I don't see them interacting in any way in the code, but sure enough, black background.
Does the const change where the array is stored or something? If not a collision array like this, what do people do for object collisions?
The array is written as "const u16 bgCollision[256][256]ALIGN4 =" if that makes any difference.
#176449 - GLaDOS - Fri Jul 29, 2011 5:37 am
Your array is to big to easily fit in memory. It's 128kb. It won't fit in IWRAM at all and it would take up half of EWRAM just by itself.
If you make it const, it will probably be placed in ROM where there's much more space available.
Also, the way you handle collisions depends on your exact needs. My game takes an unconventional approach to handling collision data due to the requirements of the advanced physics I'm using.
#176451 - MisterLogan - Fri Jul 29, 2011 5:46 am
I know it needs to be in ROM, that's not what I was asking. I would just love to make it const and have it put in ROM but then I can't edit the array, which is the point.
Quote: |
My game takes an unconventional approach to handling collision data due to the requirements of the advanced physics I'm using. |
Okay...
#176452 - GLaDOS - Fri Jul 29, 2011 5:49 am
Well I can't really tell you what you should do without knowing anything about what you are trying to do.
One option is to manually check the locations of all NPCs instead of storing them in a collision map.
#176453 - MisterLogan - Fri Jul 29, 2011 5:59 am
That might work, I'm going to have a lot of NPCs on each map but I'll be loading/unloading them so they're only active when they get near the actual screen.
What kind of game are you working on? (if you're free to say that is)
#176455 - GLaDOS - Fri Jul 29, 2011 7:16 am
Portal
#176457 - MisterLogan - Fri Jul 29, 2011 8:49 am
:1
>>GLaDOS
>>Portal
Probably should have taken a guess at that one.
EDIT:
I'm off to bed for tonight, thanks for the help on stuff.
(And then one more question here in the parentheses. I'm switching to devkitARM and libgba and am almost done finding all the register defines and such, but when I try to compile I get errors from any graphics array headers (yeah, I'll stop using headers eventually...) using "ALIGN4". "Expected initializer before 'ALIGN4'" is the error. I'm guessing I just have some syntax that I need to change, or maybe I need to be using .o files or something, I don't really know. Syntax is just "const u16 arrayname[256][256]ALIGN4 = {lots, of, numbers, here};")
#176458 - wintermute - Fri Jul 29, 2011 9:47 am
fwiw, libtonc should work fine with devkitARM, it was written and tested with it. libtonc has many more features than libgba does as well as much more detailed documentation so you might be better off sticking with that.
For libgba you want ALIGN(4), I guess the 2 extra characters were too much typing for Jasper :p
Putting graphics in headers is a bad idea, tonc has a section on this - I will not put data in header files. You should probably be using grit directly in your build process (it's included with devkitARM).
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog
#176459 - MisterLogan - Fri Jul 29, 2011 8:59 pm
Alright, I already know where everything is in libtonc so that's good to hear.
Ah, thanks.
Hehe, yeah, I keep seeing people talk about how its bad and I'm bad for doing it. I'll get it hooked up soon, I promise! I'm mostly having a hard time doing stuff like this because I have pretty much no knowledge of makefiles, but I guess that just comes with experience.
#176472 - MisterLogan - Wed Aug 03, 2011 1:26 am
Another small question for this thread, when doing background transparency, does it use the level of transparency you set in the actual bmp, or do you set it manually by the tile?
I was hoping to have a gradually more transparent edge to have a sort of nighttime effect but if it works by the tile I guess that won't work.
#176476 - gauauu - Wed Aug 03, 2011 4:12 pm
Once again, Tonc is your best resource on learning this, but a basic answer:
Transparency in backgrounds is done by taking whatever color in your palette is color 0, and using that as the transparent color. It does NOT use transparency information directly from the source image. And there's no straightforward simple way (without more advanced tricks) of doing a gradual transparent edge to a background tile.
My recommendation is to use 16-color .png files for your source images, and use the ugly pinkish #FF00FF as your transparent color -- it's somewhat of a standard to use that, as it's not really often found naturally in background or sprite images. Then use Cearn's Usenti to make sure your image's palette is arranged so that #FF00FF is the first color in the palette. Then export/convert using grit.
#176477 - MisterLogan - Wed Aug 03, 2011 8:30 pm
I read through tonc but I didn't see anything referring to gradual transparency so I figured I would make sure here before throwing out the idea.
I should have waited until I was more well rested to post that question, I didn't really say what I meant to say. I was wondering about alpha blending at gradual intervals over one tile, but I'm guessing that's not easily done.
I know normal transparency stuff pretty well. I actually chose FF00FF without knowing it was any sort of standard, I just thought it was easy to remember and I wouldn't ever need it.
#176479 - Miked0801 - Wed Aug 03, 2011 11:23 pm
You get 1 alpha channel. You can use the hardware registers to say how much alpha is applied to that channel. You can then overlay multiple textures to "darken" the alpha to get further alpha.
Or you HBlank change the alpha per scanline.
Or you ficker the image on and off at fixed rates per vblank.
Or you dither.
Or you use the DS with A3I5 or A5I3 in 3D mode :)
#176480 - MisterLogan - Thu Aug 04, 2011 12:10 am
What do you mean by multiple textures? Backgrounds at different priorities or several sprites? I've read up on HBlank stuff but I don't understand how I could get the effect I'm looking for with that (unless I missed something majorly). The effect I'm trying to get is darkest in the corners and getting lighter as it moves out, so there's a sort of oval of light extending from the middle to just outside of the screens sides.
Flickering is a cool idea, do you know for sure that it works and isn't obnoxiously flickery?
I thought about dithering, it might work, but I don't want to effect a very large area for fear of it being annoying and dithering isn't great for things that small.
Maybe someday =) I rather like the older hardware right now, I would probably try the gameboy first if there was better documentation.
#176481 - sverx - Thu Aug 04, 2011 11:00 am
Using Hblank you could darken the top (and/or bottom) part of the screen, but I guess it won't be easy to darken the left/right borders.
You could use a circular flickering window, even if I don't know if the effect will be good enough or it could result annoying for the eye.
#176485 - Miked0801 - Thu Aug 04, 2011 7:18 pm
Sorry - sprites. 5 (different shaded) sprites overlayed with 10% alpha should come out to about opaque, but YMMV.
Flickering works, as long as you keep it to 30hz or perhaps 2:1 or 1:2 for 66%/33%. More than that and the effect breaks down and becomes annoying.
For HBlank, you could change the window and alpha blend level per line to do a darker to lighter effect, though this will be very CPU expensive.
Dithering also works, as long as you don't overlay 2 different dithers on top of each other. We used this on GBC and it will work on later platforms as well.
#176487 - MisterLogan - Thu Aug 04, 2011 11:59 pm
I think I'll try out a combination of flickering and dithering and see how it looks. =) Yay.
You wouldn't happen to know how many 16x16 256 color sprites you can hold in VRAM of the top of your head would you?
#176488 - Miked0801 - Fri Aug 05, 2011 4:48 pm
GBA has 32K of Obj RAM. 32K / (16*16*1) = 128 sprites.