Those graphics are way better than the crap I do. Nice job.
I'm getting access violations when I run it (not crashing the program, but they're showing up in the debugger). I haven't pinpointed what's causing it, but I'd wager it has something to do with your bullets (you really shouldn't be using pointers for those ;P)
Other than that, really the only thing is organizational/stylistic stuff. Criticism to follow -- please don't be offended or anything, I'm not trying to be judgemental -- I just figured you posted this thread because you're looking for this kind of honest feedback.
0) Return 0 is right -- don't use #define unless you have to
1) Some of your functions are a bit haphazardly designed and overly complicated. Take for instance the Shoot function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
/*
The player shoots by pressing the Space key.
The shooting function needs to check for three main states. First, which bullet are we going to fire? That's the reason behind the for loop. Second,
does this bullet already exists? That's the reason behind the (!bulletExists[i]). And third, has the last bullet moved up enough for the next shot to
be fired (has moved beyond the 370 'y' value.
*/
void Shoot()
{
for (int i = 0; i < 3; i++)
{
if ( (i != 0) && (!bulletExists[i]) && (bulletExists[i-1]) )
{
if ( (window.GetInput().IsKeyDown(sf::Key::Space)) && (bullet[i-1]->y < 370) )
{
bullet[i] = new BulletStruct;
bulletExists[i] = true;
}
}
else if ( (i == 0) && (window.GetInput().IsKeyDown(sf::Key::Space)) && (bulletExists[i] == false) )
{
bullet[i] = new BulletStruct;
bulletExists[i] = true;
}
}
}
|
The first thing that has me scratching my head here is that you're checking the status of the space bar. Since the function name is "Shoot" I would expect it to do just that: shoot a bullet. Not check to see whether or not the user pressed the space bar and shoot only if they pressed it.
Basically your function name is misleading. Or you should have designed the function to do less.
Right now you're calling Shoot unconditionally and having it check user input. Instead, I would conditionally call it (based on user input) and have it unconditionally shoot. Something like this:
1 2
|
if(WasSpaceJustPressed())
Shoot();
|
Then all Shoot() has to do it what you'd expect: shoot a bullet.
The next thing that strikes me as odd about this function is.. why do you care about bullet[i-1]? You seem to be checking its state in the loop, but why?
Also, what does the Y position have to do with anything? Isn't the bullet removed once it goes offscreen?
A simpler approach would be this:
1 2 3 4 5 6 7 8 9
|
void Shoot()
{
int slot = FindOpenBulletSlot(); // returns -1 if no available slot
if(slot >= 0)
{
// shoot bullet[slot] here
}
}
|
continuing with that... FindOpenBulltSlot could be simplified:
1 2 3 4 5 6 7 8 9 10 11
|
int FindOpenBulletSlot()
{
for(int i = 0; i < 3; ++i)
{
if(!bulletExists[i]) // if it doesn't exist, it's an open slot
return i;
}
// if all bullets exist, there's no open slots
return -1;
}
|
(although even this is not ideal, see #3 below)
KISS. Your functions are trying to do too much. Break it up into smaller jobs. A function should do 1 clear thing.
A general rule of thumb is if you find yourself using && or ||, you're doing it wrong. (Of course that's not always the case, but && and || are very much overused by beginners).
Other functions have similar problems, but this was a good example one to show you what I mean.
2) Don't get cute with variable names. Example:
1 2
|
sf::Image Nme1;
sf::Image Nme2;
|
It took me a minute to figure out that NME wasn't an acronym for something, but actually was a leetspeak way to type 'enemy'. Don't do that.
Also don't name things foo1, foo2, foo3. If you find yourself doing that, you're doing it wrong. Use arrays.
|
sf::Image enemy[2]; // much better
|
3) Avoid globals / You could stand to objectify more. You seem to be doing the C-style faux-OOP where you make a bunch of structs, then make global functions that work with those structs.
A better approach is to use classes. This also helps get rid of all your globals (globals should be avoided!)
Take a look at your MoveBullet function:
Seems harmless for the most part, but it assumes too much. It assumes there's a global array of bullets and that they can be indexed. What if you decide to change that later? What if you get rid of the globals? Then this function needs to be rewritten. What if you decide to use a std::list of bullets instead of an array? Have to rewrite it. What if you decide to add another player and they both have their own set of bullets in two separate arrays? Have to rewrite it.
This is one of the big problems with globals is that they make [i]the entire program[/i] be aware of them. This means you're stuck with it. What's worse, code you write that uses globals can't be reused because it was written to work ONLY with globals.
A smarter approach would be to pass the
bullet to the function, rather than having the function find the bullet on its own:
|
void MoveBullet( BulletStruct& bullet )
|
This is much better. Then you can just modify 'bullet' as you see fit, without having to worry about accessing a global array. Then you can get rid of or change the global array at any time.
Of course if you're doing that, then you might as well make MoveBullet a member function, which would be the next step up:
|
void BulletStruct::Move()
|
That's about all I can think of for now. Keep up the good work =)