Copy Link
Add to Bookmark
Report

Bullets Handling

eZine's profile picture
Published in 
Techtutor
 · 29 Jul 2024

 ======================================================================== 
Just4Fun Productions presents:

Part 1 of: the Techtutor
(Topics never covered)


Bullets Handling
========================================================================

Introduction

Welcome to the first of these tech files... These files were originally only on the homepage of Just4Fun Productions, but I tought they deserved to have a more public audience, so here it is!

Since I already had these files completed, the first few are released on the same date, so if you downloaded this from some place, you should be able to get the next few at that same place (I uploaded them at least to x2ftp).

The topics I'll cover are not the normal demo tutors, or vga trainers (get the tutors from Telemachos and Denthor for that, they'r good!) I'll cover the game-programming topics that are never explained (at least not to my knowledge...), things I'll cover are for some simple things, but for a lot of other people very difficult to do. Most topics are useable in 2D games and 3D games, but since I still like the 2D games my code is also focused on those type of games.

Most of the code I talk about in these files will also be extracted from the SuperFX engine, a freeware engine I created for 2D games...you can download it from the Just4Fun homepage (http://people.zeelandnet.nl/rpb). Also I must tell you that the example code doesn't always work just like that, you might have to add things or change things...the hard stuff is usually working though..

---------------------------------------------------------------- 
For contacting Just4Fun or me (PeeBee) use the following ways:

Internet : http://people.zeelandnet.nl/rpb
Email : just4fun@zeelandnet.nl

SnailMail: P.Bestebroer
Anthony Edenlaan 28
4463 JC Goes (Zld.)
Holland

ICQ : 2309110 (probably fastest way to contact me)
----------------------------------------------------------------

Lets Start This!

Now let's talk about bullets, they'r very important in most games but how could we program them? First let's see what a bullet does in the game:

  • It's shot by the player or enemies
  • It flies a certain path until it hits something
  • If it hits an enemy then kill it, and erase the bullet
  • if it hits a wall erase the bullet.

This list shows that a bullet is only active for a short period so creating an array would not be that efficient.

We need a linked-list!

The best way for bullets would be to create a linked-list. For those of you who don't know what this is, here's some information:

A linked list is composed of pointers. You'll need at least two pointers one will be the first, pointing to the first item in the list, and the second will be the last, pointing to...exactly the last item.

Now the item it points to can be any variable you want, but using a RECORD would be best in the case of bullets. Here's an example of defining the linked-list:

                  TYPE PBullet = ^TBullet; 
TYPE TBullet = record
xposition : word;
yposition : word;
previous : PBullet;
next : PBullet;
END;

VAR FirstBullet : PBullet;
LastBullet : PBullet;

Let's explain this. The first type "PBULLET" is a pointer type of the Tbullet record, so that's used for the linked-list. The variables xposition/yposition explain them selves, but the previous/next variables are the main thing for the linked-list. They point to the previous/next item in the linked list! So let's say we've got 5 bullets flying around.

Our list would look like:

      FirstBullet = Bullet(1) 
Bullet(1).Previous = NIL { No bullet infront of bullet 1 ! }
Bullet(1).Next = Bullet(2) { pointer to next bullet }
Bullet(2).Previous = Bullet(1) { The previous bullet is bullet 1 }
Bullet(2).Next = Bullet(3) { pointer to next bullet = 3 }
....
LastBullet = Bullet(5) { the lastbullet is bullet(5) }
Bullet(5).Previous = Bullet(4) { the previous bullet is bullet 4 }
Bullet(5).Next = NIL { there are no more bullets }

That's all the information about "linked-list" I'll give here, just find some more and better examples if you still don't get it (there are enough books explaining it, and there should be some nice text files on the internet somewhere).

How do we create the bullets?

Bullet's will never be "there" all the time, so they have to be created as soon as the player hits FIRE, and erase as soon as they hit something. For this purpose we'll need a small procedure that will add a new bullet to the linked-list. The procedure that does this is called ADDBULLET, it's only purpose is to "initialise" a new bullet by adding it to the linked-list. Once the bullet is added it will be processed by the DOBULLETS procedure. The ADDBULLET procedure needs some information about the position of the bullet, the person who shot it (player/enemy) the BULLET-AI (ie. a rocket, shell, grenade, etc...). The speed of the bullet, the image-frame (sprite) the bullet uses, and maybe some specific things for you'r game, but that's up to you.

{=-=-= Example =-=-=} 
FUNCTION AddBullet( Xpos2,Ypos2,xspeed2,yspeed2 : integer;
F_Frame2,ai2,subai2 : byte):boolean;
VAR TempBull : PBullet; { this is used to create the new-bullet }
BEGIN
AddBullet:=false; { No bullet is added upto this point }

new(TempBull); { Get memory for new bullet }

if TempBull=nil then exit; { if TEMP=NIL, we don't have enough memory }

if FirstBullet=NIL then begin { is the list empty? }
LastBullet:=TempBull; { yes, so the NEW-BULLET is the first+last }
FirstBullet:=TempBull;
with FirstBullet^ do begin
next:=NIL; { next/previous are nothing, because there }
prev:=Nil; { is only one bullet in the list }
end;
end else begin { More bullets in the list! }
LastBullet^.next:=tempbull;{ Lastbullet points to new-bullet }
TempBull^.prev:=LastBullet;{ lastbullet is not lastbullet anymore }
LastBullet:=TempBull; { Lastbullet=NEWBULLET }
Tempbull^.next:=NIL; { no bullets after the new-bullet }
end;

With TempBull^ do begin { Set the new values }
xpos:=xpos2+xspeed2;
ypos:=ypos2+yspeed2;
xspeed:=xspeed2;
yspeed:=yspeed2;

ai :=ai2;
subai :=subai2;
f_Frame :=f_Frame2;
end;

AddBullet:=True; { the bullet was added }
END;
{=-=-= End of Example =-=-=-=}

What the ADDBULLET procedure does is this:

  • Create a new pointer to a bullet-type
  • Update LASTBULLET and FIRSTBULLET pointers if necessary.
  • set the new-bullet variables to the ones specified (xpos, ypos,speed...)

Now a new bullet is added to the linked-list...

I believe it can fly

Now we have a bullet in the list, but we still have to make it work.
To do this, we call the DOBULLETS procedure every frame. What the DOBULLETS procedure does is:

  • Get pointer of FIRSTBULLET.
  • Update Xposition, Yposition, animation, energy, etc...
  • Check if bullet hits something, and take action
  • Draw bullet on screen
  • Go to next bullet in the list, until LASTBULLET is reached.

{=-=-= Example =-=-=-=} 
PROCEDURE DoBullets;
VAR temp : PBullet; { temporary bullet }
next_Bull : PBullet; { next bullet in list }
done : boolean; { bullet done? }
BEGIN
Temp:=FirstBullet; { start with first bullet }

WHILE Temp<>Nil do begin { while not pointing to NIL, do bullets }
with Temp^ do begin
done:=false; { bullet not yet done }
next_Bull:=temp^.next; { pointer to Next-bullet in list }

Case AI of { check on bullet AI }
1 : BEGIN { normal bullet }
inc(xpos,xspeed); { increase X position }
inc(ypos,yspeed); { increase Y position }
{
....
Check boundaries of screen
and check for wall-background blocks
....
}
{ Bullet hits something, or is off-screen }
if not EraseBullet(temp) then begin
{ ERROR! bullet could not be disposed }
halt(1);
end;
Done:=True; { Bullet is done }
END;
END;{CASE ai }

{ if bullet was not "erased" continue the procedure }
If Not done then begin { see if we hit the player }
{
....
See if bullet is not shot by player
....
}
{
....
Check with player coordinates
....
}
{ IF HIT PLAYER THEN BEGIN }
{ Bullet hits player, and is erased }
if not EraseBullet(temp) then begin
{ ERROR! bullet could not be disposed }
halt(1);
end;
done:=true;
end;
{ if bullet was not "erased" continue the procedure }
if not done then
{
....
Draw the image on the screen!
....
}
end;
temp:=next_Bull; { get pointer to next bullet in the list }
end;
END;
{=-=-= End of Example =-=-=}

This should make the bullets fly around, BUT we DO check if the bullet hits something, but we should also erase it from the linked-list. For erasing it we need the third procedure called ERASEBULLET.

Kill, Kill, Kill the bullet

Question: Why do we need to erase the bullet, can't we just leave it, and not draw it?
Answer : No, because it stopped existing, and we need the memory it uses.
By erasing the bullet from the linked-list, you'll also free up the memory occupied by it and that is the power of linked-lists.

So how do we erase it? Well let's see what ERASEBULLET does:

  • When calling it, specify the pointer to the BULLET
  • Simply erase it from memory,
  • and update the list-pointers.

{=-=-= Example =-=-=} 
FUNCTION EraseBullet(Temp:PBullet):boolean;
VAR next_bull,
prev_bull : PBullet;
BEGIN
EraseBullet:=false; { bullet not yet erased }
if Temp=NIL then exit; { if bullet=NIL then just exit }

prev_BULL:=Temp^.prev; { Correct the bullet pointers }
next_BULL:=Temp^.next;

if prev_BULL=NIL then begin { if there is no previous bullet, then }
FirstBullet:=next_Bull; { we'r working with the firstbullet }
end else
Prev_bull^.next:=next_bull;

if next_bull=NIL then begin { if there is no next bullet , then }
LastBullet:=prev_Bull; { we'r working with the lastbullet }
end else
Next_Bull^.prev:=prev_Bull;

dispose(Temp); { dispose the bullet, freeing up memory }
END;
{=-=-= End of Example =-=-=}

That's it!

Well this was the bullet-code in a fast way, but just take a look at the supplied example code, and you should be able to do it your self. If you got some questions about the bullets, just email me and I'll try to answer you'r question.

The next Tech file is about handling aliens, objects and bonus things...

If you want to know about some other game-related code, mail me aswell, and I might create a new TECH file, trust me I'm really that nice!


PeeBee - September 10th '97

tech01.pas

{ 

TechTutor1
Bullets using linked-list technique

Coding by P.Bestebroer
FreeWare

This source will not work on it's own, it just shows how to
do bullet-code into you'r game...

Contacting:

HTTP://people.zeelandnet.nl/rpb/
EMAIL:just4fun@zeelandnet.nl

}
PROGRAM Tech01;

USES CRT;
{-----------------------------------------------------------------------------}
{
First create the variables

}
TYPE PBullet = ^TBullet;
TBullet = record
xpos,ypos : integer;
xspeed,yspeed : integer;
f_Frame : byte; { image frame }
ai : byte; { bullet AI identifier }
subai : byte; { person who shot it }
prev,next : Pbullet;{ link to next/previous bullet }
END;

VAR FirstBullet : PBullet; { first bullet in list }
LastBullet : PBullet; { last bullet in list }
{-----------------------------------------------------------------------------}
{
The addbullet procedure will add a bullet to the linked-list.

Expects: Xposition, Yposition, XSpeed, YSpeed
Image-Frame number, BULLET-AI, shot-by-ai
Returns: TRUE if bullet was added to the list.
}
FUNCTION AddBullet( Xpos2,Ypos2,xspeed2,yspeed2 : integer;
F_Frame2,ai2,subai2 : byte):boolean;
VAR TempBull : PBullet; { this is used to create the new-bullet }

BEGIN
AddBullet:=false; { No bullet is added upto this point }

new(TempBull); { Get memory for new bullet }

if TempBull=nil then exit; { if TEMP=NIL, we don't have enough memory }

if FirstBullet=NIL then begin { is the list empty? }
LastBullet:=TempBull; { yes, so the NEW-BULLET is the first+last }
FirstBullet:=TempBull;
with FirstBullet^ do begin
next:=NIL; { next/previous are nothing, because there }
prev:=Nil; { is only one bullet in the list }
end;
end else begin { More bullets in the list! }
LastBullet^.next:=tempbull;{ Lastbullet points to new-bullet }
TempBull^.prev:=LastBullet;{ lastbullet is not lastbullet anymore }
LastBullet:=TempBull; { Lastbullet=NEWBULLET }
Tempbull^.next:=NIL; { no bullets after the new-bullet }
end;

With TempBull^ do begin { Set the new values }
xpos:=xpos2+xspeed2;
ypos:=ypos2+yspeed2;
xspeed:=xspeed2;
yspeed:=yspeed2;

ai :=ai2;
subai :=subai2;
f_Frame :=f_Frame2;
end;

AddBullet:=True; { the bullet was added }
END;
{-----------------------------------------------------------------------------}
{

Erase a BULLET object from the linked-list

Expects: pointer to BULLET object that needs to be ERASED
Returns: True if succesfull

}
FUNCTION EraseBullet(Temp:PBullet):boolean;
VAR next_bull,
prev_bull : PBullet;
BEGIN
EraseBullet:=false; { bullet not yet erased }
if Temp=NIL then exit; { if bullet=NIL then just exit }

prev_BULL:=Temp^.prev; { Correct the bullet pointers }
next_BULL:=Temp^.next;

if prev_BULL=NIL then begin { if there is no previous bullet, then }
FirstBullet:=next_Bull; { we'r working with the firstbullet }
end else
Prev_bull^.next:=next_bull;

if next_bull=NIL then begin { if there is no next bullet , then }
LastBullet:=prev_Bull; { we'r working with the lastbullet }
end else
Next_Bull^.prev:=prev_Bull;

dispose(Temp); { dispose the bullet, freeing up memory }
END;

{-----------------------------------------------------------------------------}
{
The DOBULLETS procedure will process the linked-list

Expects: Nothing
Returns: Nothing
}
PROCEDURE DoBullets;
VAR temp : PBullet; { temporary bullet }
next_Bull : PBullet; { next bullet in list }
done : boolean; { bullet done? }
BEGIN
Temp:=FirstBullet; { start with first bullet }

WHILE Temp<>Nil do begin { while not pointing to NIL, do bullets }
with Temp^ do begin
done:=false; { bullet not yet done }
next_Bull:=temp^.next; { pointer to Next-bullet in list }

Case AI of { check on bullet AI }
1 : BEGIN { normal bullet }
inc(xpos,xspeed); { increase X position }
inc(ypos,yspeed); { increase Y position }
{
....
Check boundaries of screen
and check for wall-background blocks
....
}
{ Bullet hits something, or is off-screen }
if not EraseBullet(temp) then begin
{ ERROR! bullet could not be disposed }
halt(1);
end;
Done:=True; { Bullet is done }
END;
END;{CASE ai }

{ if bullet was not "erased" continue the procedure }
If Not done then begin { see if we hit the player }
{
....
See if bullet is not shot by player
....
}
{
....
Check with player coordinates
....
}
{ IF HIT PLAYER THEN BEGIN }
{ Bullet hits player, and is erased }
if not EraseBullet(temp) then begin
{ ERROR! bullet could not be disposed }
halt(1);
end;
done:=true;
end;
{ if bullet was not "erased" continue the procedure }
if not done then
{
....
Draw the image on the screen!
....
}
end;
temp:=next_Bull; { get pointer to next bullet in the list }
end;
END;
{-----------------------------------------------------------------------------}
BEGIN
FirstBullet:=Nil; { No first bullet in list }
LastBullet:=Nil; { and no last bullet in list... }

while port[$60]<>156 do ;
textcolor(7); textbackground(0); clrscr;
writeln('TechTutor #1');
writeln('written by P.Bestebroer, Just4Fun Productions');
writeln('');
writeln('This topic is for adding bullets to your games, since I don''t');
writeln('feel like writing a complete example of an implementation in a game');
writeln('this example wont work on it self.');
writeln;
writeln('The only major changes to get this working are in the DOBULLETS procedure');
writeln('You should use a great VGA unit (SuperFX engine for example ;) and ');
writeln('implement the things like drawing the bullets etc..');
writeln;
writeln('Watch out for the other techtutors...');
writeln;
writeln('Press a key');

writeln;
writeln;
writeln;
writeln('----------------------------------');
writeln('Contacting: just4fun@zeelandnet.nl');
writeln('http://people.zeelandnet.nl/rpb ');
writeln('----------------------------------');
repeat until port[$60]<>156;
END.

next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT