multiple shots

Member
Posts: 196
Joined: 2002.04
Post: #1
Ok here's the code :

int shot;

- (void) shot {
// this is where the verticies go.
}

if (shot) {
[self shot]
}

if (keydown) {
shot = 1;
}

As you can see it creates one shot. How do I get it to create multiple shots without deleting the last one? Also what is "long" used for since there's already int and float?

Thanks,
Iceman
Quote this message in a reply
Lemming
Unregistered
 
Post: #2
I handled that while programming a small shooter by keeping an NSArray of shots and iterating through them for my shot calculations each frame.
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #3
Can you give some code examples? I'm not too familiar with arrays yet. Also I'm programming in Cocoa for anyone that was wondering. Grin

Iceman
Quote this message in a reply
Lemming
Unregistered
 
Post: #4
Soitanly. The actual shot is fired here:

Code:
if(keys[4] && (frame - lastShotFrame > kShotDelay)) {
        colour c = {1.0, 1.0, 0.0, 0.0, NO};
        
        lastShotFrame = frame;
        shot = [[ShotObject alloc]
                initWithX:[playerObject xPos] + 7.5
                y: [playerObject yPos] + 20.0
                z: [playerObject zLayer] - 0.1
                w: 5
                h: 5];
        [shot setColour: &c];
        [shot setVelocity: kShotSpeed
                damage: 4
                frame: frame
                duration: kShotFrames];
        
        [playerShotObjects addObject:shot];
    }

For calculating the trajectories each frame:
Code:
for(i = 0; i < [playerShotObjects count]; i++) {
        shot = [playerShotObjects objectAtIndex:i];
        // Has this shot run out of time or gone off the top of the screen?
        if (frame < [shot end_frame] || [shot yPos] > 256) {
            [playerShotObjects removeObjectAtIndex:i];
            [shot release];
        } else {
            [shot moveX:0 y:[shot velocity]];
            
            for(j = 0; j < [enemyObjects count]; j++) {
                baddie = [enemyObjects objectAtIndex:j];
                // Did we hit this bad guy?
                if([shot overlaps: baddie]) {
                    [baddie takeDamage:[shot damage]];
                    // See who dies
                    break;
                }
            }
        }
    }

When I want to reset the game, I just do [shotArray removeAllObjects] (in this case, it's an NSMutableArray because I didn't realize I could make it static easily). Hopefully that shines some light on the topic?
Quote this message in a reply
Jeff Binder
Unregistered
 
Post: #5
The type int refers to an integer type, as you know. But the size of int isn't defined, so it may be different on different platforms. Nowadays, it's usually 32 bits, but you can't be sure.

The type long (which is the same as long int) is officially at least 32 bits, but usually is exactly 32, the same as a plain int.

The type short (or short int) is smaller than long generally 16 bits.

Then, you can add unsigned (i.e. unsigned long x), which indicates that the numbers can't be negative, and the maximum value is twice what it normally would be.

So generally, a short can hold numbers from -32768 to 32767. An unsigned short can hold numbers from 0 to 65535. A long can hold much larger numbers, and an unsigned long can hold even larger numbers, although it can't hold negative ones.

Generally, it's best to specify long or short rather than just using int. This way, you're sure of how many bits you're getting if your code is ported to other platforms. Generally, you will use long, but if you're storing a very large number of values, you might want to use a short ints or even chars to save memory.

Usually, types like UInt32 (unsigned 32 bit integer), SInt16 (signed 16 bit integer) are defined as well. These will always give you the number of bits you specify, so they're good for cross-platform code.
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #6
int shooting;

- (void) shot {
// this is where the verticies go.
}

if (shooting) {
[self shot]
}

if (keydown) {
shooting = 1;
}

if (keyup) {
shooting = 0;
}

Thanks for all the help I've learned a lot, but I'm not understanding how I get the [self shot]; to produce 2 or more shots. All I can get with this is a shot that disappears when the key is up. :confused:

Thanks,
Iceman
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #7
I would do something like:

Make a class called 'SpriteBaseObject'. Give it methods called 'draw', 'update', and 'shouldThisBeDeleted' - this is an abstract class that you will never make an object out of.

Make a class called 'Shot' that inherits from SpriteBaseObject. Add a method 'setX:y:moveX:moveY:timeLeft:'

Make classes called 'Monster', 'Spaceship', 'Hero', etc.. for all your different sprite types and derive them all from SpriteBaseObject.

Then you keep a NSMutableArray called activeSpriteList which will hold all your active sprites. When the player hits the fire key, create a new shot object. Call setX:y:moveX:moveY:timeLeft: to set the shot's current location, its direction, and how long it should live for (in case it doesnt hit anything). Then add the shot object to your activeSpriteList.

In your game loop iterate through activeSpriteList calling 'update' for all the objects in it, the update should move the sprite to its next location, handle collisions, countdown its timeLeft counter, etc...

iterate through activeSpriteList calling 'shouldThisBeDeleted' for all the objects in it, and removing any objects that return YES. For your shot object - it returns YES if the timeLeft has run out - or if it collided with something else. Deleting objects in an array *while* still going through it can be a bit tricky. You might want to go through the list in reverse order - or make a list of all the indicies of objects that need to be removed and then call removeObjectsFromIndices:numIndices: to remove them in one fell swoop.

iterate through activeSpriteList calling 'draw' for all the objects in it.

do it all again!

hope this helps,
Codemattic
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #8
Thanks for all the code examples. I think I know what I'm doing wrong; I need an array for shot (all it's doing now is producing one shot Rolleyes )
What I want to do is this:

for (i = 0; i < 5; i++) {
[self shot[i]]
}

of course this doesn't work but it's the same idea.

Thanks again for the help,
Iceman
Quote this message in a reply
Lemming
Unregistered
 
Post: #9
I'd do something like [self shots: 5] and have the for loop in the shots: method.
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #10
Ok I got it figured out I'll post the code when I'm done working on it.

Thanks,
Iceman
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #11
Ok the alloc thing was too beyond me is there any easier way to write it?

Sorry,
Iceman
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #12
Grin Yes I finally got it with some of the C code in the GLUT asteroid game. I'll post with some useable code when I get the kinks worked out.

Iceman
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #13
You can look in /Developer/Examples/GLUTExamples - look at the target 'GameGLUT' and asteroids.c (funny there are no asteroids!) - but it has multiple shots - and is in straight c, not c++ or obj-c.

However - I still suggest you create a more general solution like what I outlined before. The extra time you put in to it upfront - will save you much later on. I mean - after multiple shots you are going to want multiple bad-guy ships, or to use the code with a new project that has multiple penguins, or whatever. You dont want to find yourself constantly copying and pasting whole blocks of code each time you want to add a new type of sprite. For instance - in the asteroids.c code if you wanted to add multiple asteroids, you would have to duplicate much of the code and find and replace 'bullet' with 'asteroid' etc... Of course then if you found a bug or wanted to make an improvement you would have to do it in two different places.
Just a suggestion.

good luck,

Codemattic
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #14
Thanks for the advice. Ok here's the problem (kink) I ran into:

// My code that I'm using from the asteriod.c
- (void)shotBullet {
if (allocBullet >= 0) {
[self initBullet];
}
}

- (int)allocBullet {
int j;
for (j=0; j<MAX_BULLETS; j++) {
if (!bullet[j].inuse) {
return j;
}
}
return -1;
}

// here's the problem that I know of
- (void)initBullet {
int j;
for (j=0; j<MAX_BULLETS; j++) {
bullet[j].inuse = 1;
bullet[j].x = 0.0; // Make sure one is odd and the other is even.
bullet[j].y = 0.0;
bullet[j].z = -5.0;

}
}
// end of the problem that I know of

//real asteroid.c code
void
initBullet(int i, int time)
{
float c = cos(angle*M_PI/180.0);
float s = sin(angle*M_PI/180.0);

bullet[i].inuse = 1;
bullet[i].x = x + 2 * c;
bullet[i].y = y + 2 * s;
}

As you can see initBullet(); has an (int i). Is this why the bullet won't loop on my code? If so how do I get -(void)initBullet to loop from 1 to 2 to 3...etc. intstead of jumping to 5. I've tried it without the for(j=0; j<MAX_BULLETS; j++) but it would only produce one bullet and stop.

Here's a link where you can download my game with the problem:
http://homepage.mac.com/jonwhite/example.sit

the keyboard commands are: 0 which fires the bullet , 7, 8, 9

Thanks again,
Iceman
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #15
lets look at the original asteroids code. There is a bullet struct and it holds information about a bullet (location, velocity, lifetime, etc...). It also holds a 'inuse' variable. We have an array of bullet structs. Some are active bullets that are currently moving on screen, and some are inactive. The inuse variable is 0 if that bullet is inactive or 1 if its active.

So when we start the game all of the bullet structs in our array have an inuse equal to 0 since they all start out inactive. Unfortunately the code doesnt explicitly set this at the beginning - it just assumes the compiler will put 0 in the variables to begin with - which it does - but for clarity and safety you might want to zero all that out explicitly on startup.

So whenever we loop through the array to draw or update or collide or anything - the first thing you want to do is check that inuse!=0, because if inuse==0 then you should ignore that bullet because its inactive.

OK - so the user hits the 'fire' key - what happens? First you call allocBullet which finds the first bullet where inuse == 0 and returns the index to it (or -1 if it loops through all the bullets without finding any that are inactive). So allocBullet returns the index of an inactive bullet (or -1 if there arent any).

Then we call initBullet with the index that allocBullet returned. initBullet takes that inactive bullet and makes it active by setting inuse=1 and then setting any initial variables (start location, velocity, lifetime, etc...).

>>
// here's the problem that I know of
<<

However your version of initBullet doesnt take the index that allocBullet returned. Instead in initBullet you are looping through and making *all* the bullets active. So when you fire you get a whole bunch of bullets.

>>
Here's a link where you can download my game with the problem:
http://homepage.mac.com/jonwhite/example.sit
<<

You have only provided the executable so its hard to tell what else is wrong here. If you dont want to give away all your code (which is understandable) - consider writing a tiny program that only shoots something. Many times by writing a small program just with the code that (you think) is the trouble you can see the problem more clearly and the solution comes to you. Sometimes that tiny program works fine and (surprise) the error wasnt even where you were looking. And if not - then you can post that code and ask why it doesnt work.

Does this make whats going on here any clearer?

cheers,
Codemattic
Quote this message in a reply
Post Reply