Cannon and Bullet Behaviors
March 5, 2000
by Pat McClellan
I've made this game with a man holding a gun, and everytime he shoots a bullet comes out of the gun. How can I make a new bullet(sprite) fly out every time I hit a button?
Kenneth
P.S. I've written to you earlyer but you didn't respond, maybe I'm too eager... I don't know.
Dear Kenneth,
Sorry I can't respond to every message quickly, but I get a lot of questions and I can only write about one question a week. For more timely responses, I strongly recommend that you post your questions in our DOUGthreads forum, in our Help Central section. Zac Belado and many of our readers go through those questions daily and post answers and comments.
So you want to shoot some things? OK, I suppose that's a fairly common need in games, so let's think about what's involved. As you mention in your question, each bullet probably needs to be a sprite. That means that we'll need to have some practical limit regarding how many bullets are onscreen at once. The way we'll execute this is to have a place holder cast member in the sprite channel, then when a bullet is fired, the gun behavior will select a sprite and tell it to substitute the bullet cast member. The gun will also have to tell the bullet sprite which way to move.
If you want to make a "Space Invaders" or similar game where the cannon moves back and forth at the bottom and fires straight upward, read James Newton's excellent article Inter-sprite Communications. That article has a downloadable Space Invaders clone. In fact, it would be a great idea to read that article now because he has a great method of having the bullets notify the sprites they've hit. Since he covers it, I'm going to skip it in this article. Instead, I'll focus on having the bullets fire at various angles -- with no regard to when or if they hit anything. Play with this demo movie a bit so that you're clear on what we're talking about. In the sections of the demo where the "biter" monsters are chasing the gunner, you'll notice that the bullets are fun to fire, but they don't really affect the monsters. I'll leave that up to you (with James' article as a guide.)
A sample movie is available for download in Mac or PC format. This is a D7 movie.
The demo above is an expanded version of the demo for my Steering Control article a few weeks back. In that article, I detailed the code for moving at precise angles around the screen. The same geometry applies to firing bullets at various angles, so I've repurposed much of that code for creating the trajectory of the bullets -- expressed as a list, [x,y].
Let's look at the Lingo. We're going to have two behaviors, a Cannon Behavior and a Bullet Behavior. On the cannon, we'll want to let the author specify certain properties, as seen here:
Like our steering behavior, the Aiming Sensitivity is a measure of the smallest degree of rotation. Here, the default is 10 degrees. Also like the steering behavior, a starting rotation is specified. There will be some situations where we want to have the cannon rotate on its own (separate from a Steering Behavior). In these cases, check "Control Rotation". However, if you're using the Steering Behavior -- or any other behavior which controls the rotation of your cannon, leave it un-checked so that there aren't redundant commands being sent. The Sound Effect is simply a sound cast member for the firing sound.
When the Cannon "fires" it will need to send a message to a bullet sprite. But how does it know which sprite? You'll need to put some blank cast members as placeholders into a consecutive series of sprite channels. In this example, I've put placeholders into sprites 2 through 21. Every time the cannon fires, the behavior keeps track of which sprite the message was sent to and sends the next message to the next sprite. When it reaches the last spriteNum, it resets to the first one.
Here's the handler in the Cannon Behavior which takes care of sending out the fire message:
on fireBullet me -- convert angle to list index whichIndex = (pRotation/pAngle) + 1 thisShotXY = pTrigList[whichIndex] origin = pSprite.loc -- send the message to the bulletSprite sendSprite (pCurrentBulletSprite, #fire, thisShotXY, origin) puppetSound 3, pSFX -- "load" the next bulletSprite pCurrentBulletSprite = pCurrentBulletSprite + 1 if pCurrentBulletSprite > pLastBulletSprite then pCurrentBulletSprite = pFirstBulletSprite end if end fireBullet
(Note: To make my articles more readable, I've been trying not to post the entire code in my recent articles. I recommend you download the demo movies for the full Lingo.)
The first lines of this handler grab the trajectory information from the pTrigList which was generated in the beginSprite handler (identical code to the steering behavior.) Then a message is sent out to the pCurrentBulletSprite, along with the trajectory info and the origin of the shot (loc of the cannon). The sound effect is played. Then, pCurrentBullet is incremented and reset to pFirstBulletSprite if necessary. If you wanted to limit the ammunition that a player has, this is where you would keep track of it. I'll leave that bit to your ingenuity.
The Bullet Behavior is pretty simple by comparison. We'll need to tell it what cast members to use for the bullet and for the placeholder (blank). We'll also specify the speed of the bullet (in pixels per frame) and the range (defaulted to the stage rect.)
Here's the code for the behavior -- leaving out the getPropertiesDescriptionList handler and the property declarations:
on beginSprite me pSprite = sprite(me.spriteNum) pStatus = #waiting end beginSprite on fire me, trajectory, origin pTrajectory = trajectory pOrigin = origin pSprite.loc = pOrigin pSprite.member = pBulletMem pFrameCounter = 0 pStatus = #fired end fire on exitFrame me if pStatus = #fired then pFrameCounter = pFrameCounter + 1 deltaLoc = pSpeed * pFrameCounter * pTrajectory newLoc = pOrigin + deltaLoc if inside(newLoc, pRangeRect) then pSprite.loc = newLoc else pSprite.member = pBlankMem pStatus = #waiting end if end if end exitFrame
The beginSprite handler simply sets pSprite, as well as pStatus -- a flag which tells us what's happening with this sprite. It starts out #waiting.
When it receives the message from the cannon, the on fire me handler accepts the trajectory and origin data. It sets the location of the bullet sprite to the origin and swaps in the pBulletMem cast member. It sets a frame counter to 0 and sets pStatus to #fired. All the rest is handled in the exitFrame method.
The exitFrame handler does nothing if the pStatus is #waiting. But if pStatus is #fired, then it calculates the new position of the bullet. If the new position if inside the range of the bullet (pRangeRect) then it moves the bullet sprite to the new location. When it exceeds its range, the member of that sprite is reset to the blank member and pStatus is reset to #waiting.
That takes care of the bullet basics. A variation you might want to try is to have the bullet have a circular range (more realistic). Perhaps you could even have the bullet fade or slow as it reaches the limits of its range. Maybe it does less damage as it moves away from the cannon. There are dozens of variations on projectiles. I'm sure Raman Pfaff could write a dissertation on the angle of ricochet, the deflection of the target sprite, and the relative influences of gravity and air friction. But this should suffice for most games. Good luck with your project.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.