Ever wondered how SHMUP (SHoot-theM-UP) could handle so many bullets while remaining so smooth ? No ? Well, I'll explain it anyway Razz

Let's say we have a 100-bytes free RAM area ; you want to make a bullet table out of it. A bullet/entry is 5 bytes long, and defined as follows :

- Byte 0 : is the bullet active ? Understand : has it been shot / does it need to be drawn ?
- Byte 1 : X coordinate of the bullet
- Byte 2 : Y coordinate of the bullet
- Byte 3 : X speed of the bullet
- Byte 4 : Y speed of the bullet

Let's write a small SHMUP simulator. Nothing too fancy, only a ship shooting bullets, to demonstrate how it can be done using plain free RAM and with as few code as possible.

First, we need a ship. Take the sprite you want, just make sure it's 8*8 (or you'll have to handle the drawing yourself, which is not a big deal anyway). I'll go with [1028448200000000]. Let's say the coordinates of our ship is (X, Y). Let's write the necessary to move our ship around (don't bother about the ship going off-screen, it's not important here).


Code:
:[1028448200000000]→Pic1
:0→X→Y
:
:While 1
:Pt-On(X,Y,Pic1)
:
:DispGraphClrDraw
:getKey(3)-getKey(2)+X→X
:getKey(1)-getKey(4)+Y→Y
:EndIf getKey(15)


Now, we need to actually shoot the bullets. Since we have a 100-bytes free RAM area and each bullet/entry is 5 bytes long, we can have at most 20 bullets at once on the screen. Let's initialize the bullet table (put this code before the "While 1" block), using L1 as a free RAM area (it's 768-bytes long, but let's say it's 100) :


Code:
:Fill(L1,100,0)


This code sets the 100 first bytes of L1 to 0. This is to make sure no bullet is shot before we actually want to shoot. Why will doing that change anything ? Remember we defined our first byte as a flag to say whether or not a bullet is active. This line sets this byte to 0 for each and every possible entry, so that no bullet is being active.

Now, all the following code will go in the "While 1" block, this is where we will actually handle _all bullet movement_. Yes, all. You'll see that it's not complicated at all.

First, we go through each and every bullet of the table. Since a bullet entry is 5-bytes long and we have 100 free bytes, we need to advance 5 bytes by 5 bytes through 20 bullets.


Code:
:For(A,0,19)
:A*5+L1→B
:
:End


Now that we selected our current bullet (B now holds the address of the first byte of the Ath bullet of the table, starting at 0), we can start working on it. Actually wait ! We have to test the first byte to see if the bullet is being active or not. We don't want to waste any CPU time on an inactive bullet !


Code:
:For(A,0,19)
:A*5+L1→B
: If {B}
:
: End
:End


Now, we can apply things on our bullets while being sure they are active. But what do we want to do to these bullets ? The answer is simple : make them go forward. Actually no, not forward, but in the direction given by the X speed and Y speed of every bullet. So what we do is simply adding the X speed to the X coordinate and the Y speed to the Y coordinate. By doing that, we actually move our bullet like we wanted to.


Code:
:For(A,0,19)
: A*5+L1→B
: If {B}
:  {B+1}+sign{B+3}→{B+1}
:  {B+2}+sign{B+4}→{B+2}
:
: End
:End


Now that our bullet is moved, we need to display it on the screen. But wait a second ! Since it has moved, we must make sure it still is on the screen ! Let's add some tests to that code :


Code:
:For(A,0,19)
: A*5+L1→B
: If {B}
:  {B+1}+sign{B+3}→{B+1}
:  {B+2}+sign{B+4}→{B+2}
:  If {B+1}<96 and ({B+2}<64)
:   
:  Else
:   
:  End
: End
:End


What do we want to do depending on the result of this test ? Simple enough : if the bullet is on-screen, display it with a Pt-On, else simply make it inactive by setting its first byte to 0, so it will be ignored on the next game loop iteration.


Code:
:For(A,0,19)
: A*5+L1→B
: If {B}
:  {B+1}+sign{B+3}→{B+1}                      ← note the use of sign{ : we must handle negative speeds as well
:  {B+2}+sign{B+4}→{B+2}
:  If {B+1}<96 and ({B+2}<64)
:   Pt-On({B+1},{B+2},[40A0A0A0E0000000])     ← nice little bullet sprite
:  Else
:   0→{B}
:  End
: End
:End


Now, we have the bullet handling code done. Yeah, that was really all ! But hang on a second, the whole program is not done yet. We still have to make the ship shoot, or we'll just stay with a movable ship and a bullet handler that handles nothing.

So what we want to do is : each time the user presses [2nd], one bullet is fired from the ship. The bullet must go up (Y speed will be -2) and at the left or right (X speed will be either -1, 0 or 1, chosen randomly). To do so, we must keep track of which bullet/entry has been used in the table, and which are free.

So we'll use another variable which will vary between 0 and 19 inclusive and which will be the current entry in the bullet table - the bullet that can be shot. Each time we will shoot, we will increment that variable, and when it reaches 20, we set it back to 0 (because we can shoot 20 bullets). We will use that variable as an index in the bullet table. What we also want is a timer, to prevent the player to shoot too fast, because then the game becomes too easy. Each time we shoot, we set that timer to 8, and only shoot if that timer is 0, so that the player can only shoot every 8 frames.

First, we must set our variables to 0 (outside the game loop). Let's use C as the bullet counter and T as the timer.


Code:
:0→C→T


Now, we can write our shooting code. Add it in the game loop, just before the "EndIf getKey(15)" line.


Code:
:If getKey(54)     ← the code for the [2nd] key
: !If T
:  C*5+L1→B         ← select the correct bullet in the bullet table
:  1→{B}            ← make the bullet active !
:  X+2→{B+1}        ← make the bullet start at the top-center of the ship sprite
:  Y→{B+2}
:  rand^3-1→{B+3}   ← an X speed of either -1, 0 or 1 (rand^3 returns either 0, 1 or 2).
:  -2→{B+4}         ← the bullet will always go up
:  C+1^20→C         ← increment C and set it back to 0 if it is 20 using modulo
:  8→T
: Else
:  T--
: End
:End


And now we're done ! Yeah, already ! Here's the full code :


Code:
:.SHMUPSIM
:[1028448200000000]→Pic1
:0→C→T→X→Y
:
:Fill(L1,100,0)
:
:While 1
:
:For(A,0,19)
:A*5+L1→B
:If {B}
:{B+1}+sign{B+3}→{B+1}
:{B+2}+sign{B+4}→{B+2}
:If {B+1}<96 and ({B+2}<64)
:Pt-On({B+1},{B+2},[40A0A0A0E0000000])
:Else
:0→{B}
:End
:End
:End
:
:Pt-On(X,Y,Pic1)
:
:DispGraphClrDraw
:getKey(3)-getKey(2)+X→X
:getKey(1)-getKey(4)+Y→Y
:
:If getKey(54)
:!If T
:C*5+L1→B
:1→{B}
:X+2→{B+1}
:Y→{B+2}
:rand^3-1→{B+3}
:-2→{B+4}
:C+1^20→C
:8→T
:Else
:T--
:End
:End
:
:EndIf getKey(15)


If you try the code, you'll see that this is going a little too fast, so I put a Pause 20 in that screenshot, right after the DispGraphClrDraw command :



Have fun with that technique, and don't hesitate to ask questions if you don't understand something Smile

EDIT : fixed the issue Charty pointed out
I undERSTAND THIGNS
But i'm having an error that i'm still trying to fix. With all of your code (positively) entered correctly, the bullets either fly to the right, left, or stay frozen still.
(I'm still looking for what's wrong, but I can't find it so far.)
Thanks again, matref, for all the amazing help ^_^
I copy-pasted the code from the tutorial to my calc and it worked, so I can't really help you with that ... just make sure you wrote everything correctly another time Razz
HAH, I found the error!


Code:

{B+1}+sign{B+3}→{B+1}
{B+2}+sign{B+4}→{B+4}


In the second line, it's supposed to be:
{B+2}+sign{B+4}->{B+2}

It was saving into the wrong byte, so the speed/direction wasn't updated correctly Very Happy
Oh yeah I didn't take care of that !

Well, that proves that you understood the tutorial x) nice work !
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement