Omnimaga

Calculator Community => TI Calculators => Axe => Topic started by: Deep Toaster on February 28, 2011, 08:14:35 pm

Title: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on February 28, 2011, 08:14:35 pm
This is a more specific tutorial on how to make SHMUP games. Some of the info overlaps with my Arrays in Axe tutorial, though, so I might just delete that someday.

After I finish this tutorial I'll probably work on one to make platformers :D

How to make a shoot-em-up (http://clrhome.org/tutorials/shmup/)

Space Invaders, Phoenix, Phantom Star—you've seen shoot-em-up games before. They can come in a variety of styles, but in general they're games where you play as a single ship with unlimited firepower firing recklessly and indiscriminately on groups of "enemies" that are only trying to defend their home planet. Cruelty is fun.

This tutorial will guide you step by step in making a simple shoot-em-up. First, as with any game, we need to decide what our game should look like. Let's just stick with one level for the purposes of this tutorial, but we can have a lot of fun with how enemies and bullets move. Let's get creative; maybe something like this?

[epic screenshot goes here]

Looks good? Like with all my programs, the code's split into sections, because it makes debugging that much easier. We start my making the main program ASHMUP that sets everything up:

(http://clrhome.org/homer/PROGRAM%3AASHMUP%0D%0A%3A.SHMUP+An+awesome+Game.%0D%0A%3AprgmASHMUPD%0D%0A%3AprgmASHMUPI%0D%0A%3ARepeat+getKey%2815%29%0D%0A%3AprgmASHMUPK.png)

(http://clrhome.org/homer/%3AprgmASHMUPM%0D%0A%3AEnd%0D%0A%3AReturn%0D%0A%3AprgmASHMUPR.png)

prgmASHMUPD will hold our data, prgmASHMUPI will set up (initiate) the variables, and prgmASHMUPR will hold all our subroutines. The actual game goes in the loop, so the user can quit at any time by pressing CLEAR. prgmASHMUPK handles user keypresses (and moves the ship accordingly), while prgmASHMUPM moves and tests the enemies and bullets.

The home ship (http://clrhome.org/tutorials/shmup/01/)

First let's make our ship move. Create program ASHMUPD and put this in:

(http://clrhome.org/homer/PROGRAM%3AASHMUPD%0D%0A%3A..DATA%0D%0A%3A%C3%8118183C7E66FF9981%5D%1CGDB0.png)

That's going to be our character.

Now let's code the actual movement. Since our ship's just one ship, we won't put it in an array. We need to keep track of its X- and Y-positions; for simplicity, how about just X and Y? Here's prgmASHMUPK:

(http://clrhome.org/homer/PROGRAM%3AASHMUPK%0D%0A%3A..KEYS%0D%0A%3AIf+getKey%281%29%0D%0A%3AY%2B2%1CY%0D%0A%3AEnd%0D%0A%3AIf+getKey%282%29%0D%0A%3AX-2%1CX%0D%0A%3AEnd.png)

(http://clrhome.org/homer/%3AIf+getKey%283%29%0D%0A%3AX%2B2%1CX%0D%0A%3AEnd%0D%0A%3AIf+getKey%284%29%0D%0A%3AY-2%1CY%0D%0A%3AEnd.png)

We move two pixels each time.

We haven't given it an initial position yet, so put that in prgmASHMUPI. The ship should start in the center of the screen, maybe a closer to the bottom edge. (44, 40) should work.

(http://clrhome.org/homer/PROGRAM%3AASHMUPI%0D%0A%3A..INIT%0D%0A%3A44%1CX%0D%0A%3A40%1CY.png)

But how will we display the ship and all the other stuff in the actual game? For such a simple game, let's just clear the screen and redraw everything every time. It's actually faster than the alternatives in this case because everything on the screen is dynamic. So change the main program to this:

(http://clrhome.org/homer/PROGRAM%3AASHMUP%0D%0A%3A.SHMUP+An+awesome+Game.%0D%0A%3AprgmASHMUPD%0D%0A%3AprgmASHMUPI%0D%0A%3ARepeat+getKey%2815%29%0D%0A%3AprgmASHMUPK.png)

(http://clrhome.org/homer/%3AClrDraw%0D%0A%3A.prgmASHMUPM%0D%0A%3APt-On%28X%2CY%2CGDB0%29%0D%0A%3ADispGraph%0D%0A%3AEnd%0D%0A%3AReturn%0D%0A%3A.prgmASHMUPR.png)

I've also commented the lines "prgmASHMUPM" and "prgmASHMUPR" since we haven't actually made them yet.

Well, how does it look? Compile it and see for yourself.

(http://clrhome.org/tutorials/shmup/shmup1.gif)

It's a bit fast, but it works well enough for now. It'll slow down after we add the enemies and bullets anyway. That's what we'll do next.

Chargin' laz0rz (http://clrhome.org/tutorials/shmup/02/)

All right, now for the fun stuff: bullets! Since there can be more than one bullet at a time, we need to store it in an array. Arrays are basically lists of elements with a similar structure; see our guide to arrays (http://ourl.ca/9288/176368) for more information.

Before we make the subroutines, though, we need to decide what the array's going to look like. An array is just a list of elements, so we need to figure out the most efficient format to store a bullet in. What information do you need about a bullet?


So that's it. Four bytes per element. We can store the bullets in L4, which gives us 323 bytes (80 elements) of memory to work with. First, like with all arrays, we need to set a variable up to store the number of elements. It should be initialized in the init code.

(http://clrhome.org/homer/PROGRAM%3AASHMUPI%0D%0A%3A..INIT%0D%0A%3A44%1CX%0D%0A%3A40%1CY%0D%0A%3A0%1CB.png)

Now you can add the bullet-adding subroutine to put in prgmASHMUPR:

(http://clrhome.org/homer/PROGRAM%3AASHMUPR%0D%0A%3A..ROUTINES%0D%0A%3ALbl+ASB%0D%0A%3AIf+B%3C64%0D%0A%3Ar%84%1C{r%83%1C{r%82%1C{r%81%1C{B%2B1%1CB*4%2BL%84-4}%2B1}%2B1}%2B1}%0D%0A%3AEnd.png)

(http://clrhome.org/homer/%3AReturn.png)

Again, see http://ourl.ca/9288/176368 (http://ourl.ca/9288/176368) for a full-length explanation on how this works.

Of course, bullets should only be added when the user presses a button to fire. One option could be 2ND. But we don't want the ship to fire too rapidly, so first set up a counter in the main program. We'll be using this counter for lots of things, here to make sure the fire button doesn't repeat too quickly.

(http://clrhome.org/homer/PROGRAM%3AASHMUP%0D%0A%3A.SHMUP+An+awesome+Game.%0D%0A%3AprgmASHMUPD%0D%0A%3AprgmASHMUPI%0D%0A%3ARepeat+getKey%2815%29%0D%0AC%2B1%1CC.png)

(http://clrhome.org/homer/%3AprgmASHMUPK%0D%0A%3A.prgmASHMUPM%0D%0A%3AClrDraw%0D%0A%3APt-Change%28X%2CY%2CGDB0%29%0D%0A%3ADispGraph%0D%0A%3AEnd%0D%0A%3A.prgmASHMUPR.png)

The only new line of code is a "C+1?C" right after the main loop starts. We don't really have to initiate C because since we're not using the whole number anyway, it won't make much of a difference.

What does matter though is how C affects firing. Add this code to the end of prgmASHMUPK:

(http://clrhome.org/homer/PROGRAM%3AASHMUPK%0D%0A%3A..KEYS%0D%0A%3AIf+getKey%281%29%0D%0A%3AY%2B2%1CY%0D%0A%3AEnd%0D%0A%3AIf+getKey%282%29%0D%0A%3AX-2%1CX%0D%0A%3AEnd.png)

(http://clrhome.org/homer/%3AIf+getKey%283%29%0D%0A%3AX%2B2%1CX%0D%0A%3AEnd%0D%0A%3AIf+getKey%284%29%0D%0A%3AY-2%1CY%0D%0A%3AEnd%0D%0A%3AIf+getKey%2854%29%0D%0A%3A%21If+C^8.png)

(http://clrhome.org/homer/%3Asub%28ASB%2C1%2C3%2CX%2B3%2CY-4%29%0D%0A%3AEnd%0D%0A%3AEnd.png)

Just in case you don't understand this code completely, it first checks if 2ND is held down (if didn't memorize the keycode table, it's 54). Then it tests to see if the counter is a multiple of 8. This adds a little pause between consecutive bullets; otherwise, a bullet is added each pass of the loop, and you'd quickly get a continuous stream of bullets!

And if the conditions are right, the program adds a "type 1" bullet with a speed of 3 pixels per pass (a bit faster than the ship at 2) placed at a position three pixels to the right of the ship and four pixels up, which positions a 2- by 4-pixel bullet nicely. Don't bother compiling yet, since we haven't added any of the actual bullet-drawing code. That's in the next section.

Bullets can move (http://clrhome.org/tutorials/shmup/03/)

Let's take a break from coding. Put this in ASHMUPD.

(http://clrhome.org/homer/PROGRAM%3AASHMUPD%0D%0A%3A..DATA%0D%0A%3A%C3%8118183C7E66FF9981%5D%1CGDB0%0D%0A%3A%C3%81C0C0C0C000000000%5D%1CPic0.png)

That's the sprite for the bullet, of course (2 by 4).

All right, break's over. Back to the code!

(http://clrhome.org/homer/PROGRAM%3AASHMUPM%0D%0A%3A..MOVEMENT%0D%0A%3AIf+B%0D%0A%3AFor%28I%2C1%2CB%29%0D%0A%3AIf+{{I*4%2BL%84-1%1CJ}-{J-2}%1C{J}}%3E64%0D%0A%3Asub%28RSB%2CI-1%1CI%29%0D%0A%3AEnd.png)

(http://clrhome.org/homer/%3AEnd.png)

That goes in a new program called ASHMUPM (you can check in the main program to see where the code'll be run).

It might look confusing at first, but that's because of how optimized it is. [Insert self-congratulating remarks here.] First of all, the For( loop goes from 1 to L, not 0 to L-1, unlike most indexes, which start at zero. This is because since I is checked against the maximum value (either L or L-1) every pass of the loop, there's a speed optimization in using L. We'll be taking care of that extra 1 in the actual code.

The next line is a gigantic if statement that already moves the bullet. It looks complicated, but just look at the inside first: "{I*4+L4-1?J}." It takes I (the current index being parsed), multiplies it by four (the number of bytes per element), and adds L4-1. Why L4-1? If you just add L4, you get the next element's first byte (since we're starting the loop at 1), so if you subtract one from that, it would be the current element's Y-value. The pointer to that is stored to J for later reference.

Then the {J-2} is subtracted. Since J points to the current Y-value, two bytes before it would be the speed of the bullet. And since our bullets go upwards, we subtract. All of this gets stored back to {J}.

Now here's the (über1337) optimization: Since the store statement is storing to a non-constant pointer (thanks to the J), it returns the pointer stored to, which you can use for further operations. In this case we wrap the whole thing with braces to get the value again and test if it's greater than 127 (because if its signed value is less than zero, its unsigned value is greater than 127; we actually save a few bytes by not having to use sign{). If it did, we call Lbl RSB, which we'll set up next.

Finally, a working gun (http://clrhome.org/tutorials/shmup/04/)

Lbl RSB is a subroutine, so that means we go back to prgmASHMUPR. Here's the code:

(http://clrhome.org/homer/PROGRAM%3AASHMUPR%0D%0A%3A..ROUTINES%0D%0A%3ALbl+ASB%0D%0A%3AIf+B%3C64%0D%0A%3Ar%84%1C{r%83%1C{r%82%1C{r%81%1C{B%2B1%1CB*4%2BL%84-4}%2B1}%2B1}%2B1}%0D%0A%3AEnd.png)

(http://clrhome.org/homer/%3AReturn%0D%0A%3ALbl+RSB%0D%0A%3ACopy%28r%81*4%2BL%84%2B4%2C-4%2CB-1%1CB-r%81*4%2B1%29%0D%0A%3AReturn.png)

All this does is remove the element from the list. For a full explanation, see the guide to arrays (http://clrhome.org/tutorials/arrays/04/).

Now there's one last thing we need to do before the program works. Back when we finished the first little demo we commented out all the lines importing programs that didn't exist (yet). Now that we've made them, take all the periods out. You probably don't need a screenshot for this.

Finally, it's time to actually display the bullets. Since they're in an array, the program would have to loop through the array and display each one, but conveniently, we already have one such loop set up in prgmASHMUPM. Let's just edit that to save ourselves another loop:

(http://clrhome.org/homer/PROGRAM%3AASHMUPM%0D%0A%3A..MOVEMENT%0D%0A%3AIf+B%0D%0A%3AFor%28I%2C1%2CB%29%0D%0A%3AIf+{{I*4%2BL%84-1%1CJ}-{J-2}%1C{J}}%3E64%0D%0A%3Asub%28RSB%2CI-1%1CI%29%0D%0A%3AElse.png)

(http://clrhome.org/homer/%3APt-On%28{I*4%2BL%84-2%1CJ}%2C{J%2B1}%2CPic0%29%0D%0A%3AEnd%0D%0A%3AEnd%0D%0A%3AEnd.png)

The code is pretty similar to the stuff in prgmASHMUPM (in fact, we could have saved a few more bytes by combining the two), so I won't explain it here. If you want a review, it's here. (http://clrhome.org/tutorials/shmup/03/)

And it works!

(http://clrhome.org/tutorials/shmup/shmup2.gif)

Next: Enemieshomer/The+Game+is+near.+Prepare+++Done.png[/img]
Title: Re: Making a shoot-em-up in Axe
Post by: willrandship on February 28, 2011, 08:22:12 pm
Cool! I'd love to try this out.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: FinaleTI on February 28, 2011, 08:41:20 pm
Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on February 28, 2011, 08:44:08 pm
Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.

Smaller, too. But yeah, there are a couple of opts I left out, such as combining the display routine as part of ASHMUPM. I might fix that eventually.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: FinaleTI on February 28, 2011, 08:48:20 pm
True. Plus, since it's a tutorial, optimizing the hell out of it might not be the best idea, if only for readability's sake.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on February 28, 2011, 08:49:09 pm
True. Plus, since it's a tutorial, optimizing the hell out of it might not be the best idea, if only for readability's sake.

<_< Whoops.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: FinaleTI on February 28, 2011, 08:51:02 pm
Of course, if you explain it well enough, like with that 4 byte shifting thing, then it probably wouldn't be too much of a problem.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on February 28, 2011, 08:54:28 pm
Which 4-byte shifting code, and for that do you mean it's explained well enough, or should I explain more?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: FinaleTI on February 28, 2011, 08:57:24 pm
The bullet adding subroutine. It made sense to me. I sometimes have trouble grasping weird Axe optimizations and quirks, so I thought it was well explained.

EDIT: I'm talking about the explanation in the other tutorial, the array one. I think it was used for adding enemies to the array.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: ztrumpet on February 28, 2011, 09:50:07 pm
Looks great!  Are you going to keep the ship on the screen?

Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.
Smaller, too.
I think it would make it bigger, as long as you already have the ClrDraw and DispGraph commands elsewhere.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on February 28, 2011, 10:55:41 pm
Looks great!  Are you going to keep the ship on the screen?

What do you mean?

Cool! One suggestion I have is to use DispGraphClrDraw. It would make the program faster, but I'm not sure about smaller.
Smaller, too.
I think it would make it bigger, as long as you already have the ClrDraw and DispGraph commands elsewhere.

Nope, it's just a tutorial, so that's all the code :)
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: ralphdspam on March 01, 2011, 12:00:36 am
Thanks! This is really helpful.  :)
Can you attach the source to make it easier to read?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on March 01, 2011, 12:04:19 am
Ah, I'll do that when I actually finish the example program ;D

And kalan_vod on RevSoft suggested putting the code in [code] blocks. Would that be helpful?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Runer112 on March 01, 2011, 01:52:03 am
Awesome tutorial! ;D But because I'm OCD about optimization, I feel like optimizing this. Since this is a tutorial, feel free to not include these optimizations, as they'll probably just make the code more confusing. But I want to try to optimize it anyway. :hyper:



Code: (ASHMUP) [Select]
:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat getKey(15)
:C+1→C
:prgmASHMUPK
:prgmASHMUPM
:ClrDraw
:If B
:For(I,1,B)
:Pt-On({I*4+L₄-2→J},{J+1},Pic0)
:End
:End
:Pt-Change(X,Y,GDB0)
:DispGraph
:End
:prgmASHMUPR
       
Code: (ASHMUP Optimized) [Select]
:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat 0
:C+1→C
:prgmASHMUPK
:ClrDraw
:B
:While
:-1→I
:!If {*4+L₄+2→J+1}+{J-1}→K*4-256<996
:Pt-On({J},K,→{J+1}Pic0)
:Else
:Isub(RSB)
:End
:I
:End
:Pt-On(X,Y,GDB0)
:DispGraph
:EndIf getKey(15)
:Return
:prgmASHMUPR

Optimization notes (savings listed in brackets):



Code: (ASHMUPK) [Select]
:..KEYS
:If getKey(1)
:Y+2→Y
:End
:If getKey(2)
:X-2→X
:End
:If getKey(3)
:X+2→X
:End
:If getKey(4)
:Y-2→Y
:End
:If getKey(54)
:!If C^8
:sub(ASB,1,3,X+3,Y-4)
:End
:End
       
Code: (ASHMUPK Optimized) [Select]
:..KEYS
:!If ^8
:If getKey(54)
:sub(ASB,,⁻3,X+3,Y-4)
:End
:End
:!If getKey(3)-getKey(2)+1*2+X
:+2
:End
:min(-2,88)→X
:!If getKey(1)-getKey(4)+1*2+Y
:+2
:End
:min(-2,56)→Y

Optimization notes:



Code: (ASHMUPR) [Select]
:..ROUTINES
:Lbl ASB
:If B<80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:End
:Return
:Lbl RSB
:Copy(r₁*4+L₄+4,-4,B-1→B-r₁*4+1)
:Return
       
Code: (ASHMUPR Optimized) [Select]
:..ROUTINES
:Lbl ASB
:Return!If B-80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:Return
:Lbl RSB
:Copy(→r₁*4+L₄+4,-4,B-1→B-r₁*4+1)

Optimization notes:



Confusing enough? ;) Ask me if you have questions about any of this and I might be able to remember what the hell I was thinking.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on March 01, 2011, 09:26:00 am
Dammit, Runer :crazy:

Code: (ASHMUP) [Select]
:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat getKey(15)
:C+1→C
:prgmASHMUPK
:prgmASHMUPM
:ClrDraw
:If B
:For(I,1,B)
:Pt-On({I*4+L₄-2→J},{J+1},Pic0)
:End
:End
:Pt-Change(X,Y,GDB0)
:DispGraph
:End
:prgmASHMUPR
       
Code: (ASHMUP Optimized) [Select]
:.SHMUP An awesome game.
:prgmASHMUPD
:prgmASHMUPI
:Repeat 0
:C+1→C
:prgmASHMUPK
:ClrDraw
:B
:While
:-1→I
:If {*4+L₄+2→J+1}+{J-1}→K*4-256<996
:Pt-On({J},K,→{J+1}Pic0)
:Else
:Isub(RSB)
:End
:I
:End
:Pt-On(X,Y,GDB0)
:DispGraph
:EndIf getKey(15)
:Return
:prgmASHMUPR

Optimization notes (savings listed in brackets):
  • [3 bytes, 10 cycles per iteration] – Main getKey(15) loop changed to post-check format
  • [31 bytes, ~104 cycles per bullet] – prgmASHMUPM merged into the already existing bullet drawing loop, removing the need for 2 separate loop structures
  • [8 bytes] – Removed check that B≠0 before the bullet loop; it is unnecessary because the loop already handles this properly
  • [8 bytes, ~38 cycles per bullet] – Bullet loop structure optimized
  • [3 bytes, 16 cycles] – Due to the bullet loop no longer being a For() loop, the value of the loop variable can now be carried into the first function inside the loop
  • [3 bytes, 12 cycles] – Bullet velocity added to y value instead of subtracted from y value
  • [⁻3 bytes, ⁻26 cycles] – Bullet offscreen check changed from <127 to *4-256<996; this is a more compact emulation of (VALUE<64) or (VALUE>248) which would appear to me to be an improved logic statement for this scenario
  • [9 bytes, 57 cycles] – J value when checking bullet y value reused to get x value for Pt-On() simply with {J}
  • [6 bytes, 43 cycles] – Reused the value stored in J from the offscreen check to get the x coordinate for Pt-On()
  • [4 bytes, ~18 cycles] – Stored the bullet y value into a variable when checking the y value to avoid needing to retrieve again later in the Pt-On() command; also moved the code to store the new y value to the bullet's data in the middle of the Pt-On() command so the fact that it returns a pointer doesn't disrupt calculations
  • [3 bytes, 16 cycles] – sub(RSB) called without saving the argument into r1 first
  • [126 bytes] – Player sprite drawn with Pt-On() instead of Pt-Change(), removing the need for a second bulky sprite routine
  • [-1 bytes, ? cycles] – A Return added after the main loop, before prgmASHMUPR; without it, after the user presses clear, program execution would just continue into the first subroutine in prgmASHMUPR, potentially causing big problems

Yeah, I was thinking of combining the loops. If I do I might use some but not all of those optimizations (I don't think most Axe coders would understand five lines of that lol).

Why is post-check more optimized than a Repeat loop, though?

And thanks for catching that missing Return. Didn't see that.

Code: (ASHMUPK) [Select]
:..KEYS
:If getKey(1)
:Y+2→Y
:End
:If getKey(2)
:X-2→X
:End
:If getKey(3)
:X+2→X
:End
:If getKey(4)
:Y-2→Y
:End
:If getKey(54)
:!If C^8
:sub(ASB,1,3,X+3,Y-4)
:End
:End
       
Code: (ASHMUPK Optimized) [Select]
:..KEYS
:!If ^8
:If getKey(54)
:sub(ASB,,⁻3,X+3,Y-4)
:End
:End
:!If getKey(3)-getKey(2)+1*2+X
:+2
:End
:min(-2,88)→X
:!If getKey(1)-getKey(4)+1*2+Y
:+2
:End
:min(-2,56)→Y

Optimization notes:
  • [3 bytes, 16 cycles] – Weapon fire checking moved to the top and the counter check moved before the key check, allowing for the counter value to be carried from the previous statement in prgmASHMUP
  • [3 bytes, 10 cycles] – Omitted the bullet type from sub(ASB), as a 1 can be carried from the getKey(54) check
  • Bullet velocity changed from 3 to ⁻3, as the velocity is now added to the y value instead of subtracted from it
  • [⁻22 bytes, too lazy to calculate cycles] – New approach to movement key checking; also makes sure the ship doesn't leave the screen

Thanks! Doesn't seem too hard to understand/explain, so I'll include that.

Code: (ASHMUPR) [Select]
:..ROUTINES
:Lbl ASB
:If B<80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:End
:Return
:Lbl RSB
:Copy(r₁*4+L₄+4,-4,B-1→B-r₁*4+1)
:Return
       
Code: (ASHMUPR Optimized) [Select]
:..ROUTINES
:Lbl ASB
:Return!If B-80
:r₄→{r₃→{r₂→{r₁→{B+1→B*4+L₄-4}+1}+1}+1}
:Return
:Lbl RSB
:Copy(→r₁*4+L₄+4,-4,B-1→B-r₁*4+1)

Optimization notes:
  • [2 bytes, ~5 cycles] – Changed the check that the number of bullets is not too high from an if statement into a conditional return statement
  • [5 bytes, 20 cycles] – Changed If B<80 to !If B-80; the second is an optimized version of If B=80, which should work equally well because B can be 80 but should never be greater than 80
  • [1 byte] – Removed the last Return, as Axe automatically adds a Return at the end of the program

I like leaving the last Return in because it helps code readability, and it makes it easier to add new subroutines after it. It doesn't actually affect the compiled code, right?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Runer112 on March 01, 2011, 05:13:50 pm
Why is post-check more optimized than a Repeat loop, though?

I think I remember mentioning why this is in a previous post somewhere... ah yes, here it is:

Speaking of optimizing control structures, any chance for Do...While loops?

I'd like Switch cases more than Do...While loops, but I don't really know how the second works.


Do...While loops are a lot like While...End loops, except that the check to advance to another iteration of the loop is at the end instead of the beginning. This turns out to be more optimized. Whereas a While...End loop has two jumps, one to exit the loop if the condition is false and one to jump back to the beginning, a Do...While combines the two into one jump that jumps back to the beginning if the condition is true.

I don't know how good that explanation was, but perhaps this pseudocode will help clarify it. Numbers in brackets indicate the number of bytes the real code takes:

While...End loop                                                                          Do...While loop                                                                              
Code: [Select]
;While
[x] Loop condition
[2] Check if loop condition is true or false
[3] Exit the loop if false

;Loop contents

;End
[3] Jump back to the beginning of the loop


       
Code: [Select]
;Do
;[0] Start of the loop, takes no actual code



;Loop contents

;While
[x] Loop condition
[2] Check if loop condition is true or false
[3] Jump back to the beginning of the loop if true




I like leaving the last Return in because it helps code readability, and it makes it easier to add new subroutines after it. It doesn't actually affect the compiled code, right?

I agree that it improves readability, but because Axe automatically adds a return to the end of programs it's redundant and a waste of 1 byte. You can always improve readability while maintaining optimization by keeping the final Return, but commenting it out.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: ztrumpet on March 01, 2011, 05:55:56 pm
Looks great!  Are you going to keep the ship on the screen?

What do you mean?
Are you going to make it so the player's sprite cannot go off the screen?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on March 01, 2011, 06:17:18 pm
Why is post-check more optimized than a Repeat loop, though?

I think I remember mentioning why this is in a previous post somewhere... ah yes, here it is:

Oh, right, I keep thinking Repeat is the same as in BASIC (post-checking).

I like leaving the last Return in because it helps code readability, and it makes it easier to add new subroutines after it. It doesn't actually affect the compiled code, right?

I agree that it improves readability, but because Axe automatically adds a return to the end of programs it's redundant and a waste of 1 byte. You can always improve readability while maintaining optimization by keeping the final Return, but commenting it out.

Axe actually checks if there's already a Return at the end. If there is, it isn't added.

Looks great!  Are you going to keep the ship on the screen?

What do you mean?
Are you going to make it so the player's sprite cannot go off the screen?

Yeah, definitely. Runer112's code takes care of that, and I think I'll use it for ASHMUPK. That part of it should be fairly easy to understand.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Runer112 on March 01, 2011, 06:21:01 pm
Axe actually checks if there's already a Return at the end. If there is, it isn't added.

I was pretty sure it didn't check if the code already ends in a Return. Upon quickly double-checking this, it appears that Axe will always add a final Return whether or not your code ends in one. Do you get different results?
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on March 01, 2011, 06:25:21 pm
I swear Quigibo mentioned he optimized the Return...

Maybe it was just a feature request. I'll check.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: DJ Omnimaga on March 02, 2011, 04:38:25 am
Wow nice tutorial Deep Thought. It should really be useful. It's so well detailed too :D
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Quigibo on March 03, 2011, 03:49:29 am
I swear Quigibo mentioned he optimized the Return...

Maybe it was just a feature request. I'll check.

It was available at one time, but I removed this feature for some reason because there was a case where this behavior was not desired.  I can't seem to remember what it was though... if I can't think of anything, I'll add it back in.
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on April 06, 2011, 07:54:53 pm
Finished combining the bullet display routine into ASHMUPM and cleaned up a couple other things. You'll probably have to refresh the page to see the changes (due to page caching).

Also starting to work on the enemies section. It's a lot easier now that I have a link cable :D

EDIT: Whoa ... it does run a lot faster O.O
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Builderboy on April 06, 2011, 08:34:22 pm
I believe it was removed because of something to do with subprograms and memory leaks o.O
Title: Re: Making a SHMUP (shoot-em-up) in Axe
Post by: Deep Toaster on April 06, 2011, 08:41:10 pm
Found it:

  • Return optimization in no longer automatic due to possible program leaks.

When would it cause a memory leak?