Author Topic: Arrays in Axe (Enemy/Bullet Code Tutorial)  (Read 16421 times)

0 Members and 1 Guest are viewing this topic.

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Arrays in Axe (Enemy/Bullet Code Tutorial)
« on: February 20, 2011, 01:40:14 pm »
This is going to be a monster tutorial that'll take a while for me to write, so here are the first five parts:

Arrays and Bullet Code

Axe doesn't natively support any data structures, but that doesn't mean you can't use any in your Axe programs. Because Axe allows you to manipulate the bytes and bits of your calculator all you want, you can make your structures, made in whatever way best suits you. Axe is a versatile tool.

One of the most useful data structures (by far) is the array. An array is just a list of data elements, which could be anything—bullets, enemies, lobsters, etc. In fact, if you think about it, a tilemap is a type of array (a two-dimensional one).

So how do you make an array in Axe? Well, first you need to decide where to put it. Any safe RAM area (L1, L2, etc.) will do. Just make sure it's reasonably big enough. Arrays can take a lot of memory.

Now decide how each element is going to be stored. What are you representing with each element? Just for the sake of example, let's say you're making a program to keep track of some squares floating around the screen but always going in one direction. You'd need to keep track of its X and Y values, as well as how fast it's moving horizontally and vertically. That would be four bytes per element. (You could probably cut it down to three or even two if you desparately neede to, but I'll keep things simple.) The array could look like this:

Like with everything in Axe, we start counting from 0 because it makes our lives much, much easier.

To start working on our example, make a program called ASQUARE as follows:

This will be our main program. It should be pretty simple to understand: it first initializes some data (the code for which we're going ot put in a subprogram called ASQUI), then goes in a loop where it draws the squares until you quit. prgmASQUR will hold our subroutines.

For program ASQUI, just put this in:

That's it. L will hold the current size (length, number of elements) of the array. It'll get updated whenever we modify the array (in the subroutine itself).

Displaying objects in the array

An array of data (or any other data structure) is pretty much useless unless you do something with it. Here we'll make the subroutine DA that displays all the elements in the array.

First, make a new program called ASQUR and put this inside. All our routines will go in it.

Quote from: Draw all elements
Variables:
The only global variable you really need will be one giving you the size (length) of the array (L).

L - current length of array (initialized to 0 at the beginning of the program and updated by the routine itself)

Input:
None.

Output:
None. It draws the sprites to the buffer, but that's pretty much it.

Code:

You probably don't need too much explaining, so I'll keep it short. First, there's "If L." This is to avoid an (almost) infinite loop when L happens to be zero. (We loop from 1 to L and subtract four more instead of from L-1 to L-1 because it's a lot faster to check each pass of the loop.) Then the routine loops through every element, using Pt-Change( to display each one.

We use Pt-Change( here because it's the easiest to work with when you want to draw a moving object. It works like this: Just before the main program calls DispGraph, we draw all the squares onto the screen so they appear, and once the screen finishes displaying, all the squares get removed with the same routine to make it "clean" again. This makes things easier if you're making a complex tilemap game where redrawing the entire screen every frame takes too much time.

Anyway, now that you've defined the subroutine, you can compile your program now! And run it! what does it do?

Nothing. It waits until you press CLEAR. That's because you haven't added any elements to the array yet. We'll get there in the next step.

Manipulating the array

Now a routine to add (push) an element to the end of the array. Put this in program ASQUR.

Quote from: Pushing an element
Variables:
The only global variable you really need will be one giving you the size (length) of the array. Let's call it L.

L - current length of array (initialized to 0 at the beginning of the program and updated by the routine itself)

Input:
Since each element holds four values, let's make each one an argument.

r1 - X-value of new square
r2 - Y-value of new square
r3 - X-speed of new square
r4 - Y-speed of new square

Output:
None. (Of course, you could easily modify it to return a useful value, such as a pointer to the element added.)

Code:

Scary? Fine, I'll break it down.

Before it does anything, it checks if there are 177 elements in the list. This is because L1 can only hold 714 bytes of data, which is approximately 178*4 elements. If you're using any safe RAM location, make sure you change this limit accordingly. Having too many elements is called an overflow, which could mess up whatever data comes after L1 (in this case the variables A through T.

First look at the very inside of the mess of braces, at the line "L+1?L*4+L1-4." The real action starts there. First, it increments L by one (you probably know why). Since that command returns the value of L, you can keep doing operations on it (multiplying by four in this case). Since each element in our example is four bytes long, L*4 gets the offset of the next element in the array. But there's a problem here: Since you incremented L already, this now points four bytes ahead of where you're supposed to be. We take care of this by adding only L1-4 to the total.

That gives us a pointer to where to store the first byte of the element, so "r1?{L+1?L*4+L1-4}" would store the first byte there.

Now here's the fun part: By storing to a variable location, the pointer you stored to is returned in HL. That means that you can keep on storing to the byte after it by simply adding one! That's why the line above is enclosed by "r2?{ ... +1}": you just add one to get the next byte, then store to it. You can keep going like this for as long as you want; it's the single most optimized way to store a mass of variable data in Axe!

Next up we'll actually add the pretty squares. Promise.

Actually doing something

Finally we're going to add the actual enemies (squares). Here are some ideas for how they should be added:

• Enemies always start at the center of the screen. That would be (44, 28).
• Enemies should move in a random direction.
• Enemies spawn at random times.

To do this, we need to back to the main program, into the main loop (Repeat getKey(15):End). Change prgmASQUARES into this:

All the changes come just before the first sub(DA).

You probably understand this too. If a random two-byte integer is less than 4096 (that's a chance of 4096/65536, or 1/16), stick a square in the middle of the screen and give it a random X- and Y-speed. Then quit when the user presses CLEAR.

Before we compile, there's one thing that's missing. Something that every enemy/bullet system needs to have—movement. So change prgmASQUARES again:

All the new additions are right after the ones you just added. The point here is that it has to be before the first sub(DA). Otherwise, you'd be changing the squares' position after they get drawn but before they get erased, so they'd end up being "erased" from a different location.

Well? Compile and run!

It works! Amazing, eh? You can let it run for as long as you want, and even though it slows down quite a bit with a lot of enemies on the screen, they all move on their own!

The only problem now is that they keep wrapping around the screen. Usually that's not what you want to happen, since if an enemy or bullet goes off the screen, it should stay off the screen. We'll take care of that in the next section.

Getting rid of the extras

To make the squares more realistic enemies, we're going to remove them when they go off the screen. That calls for a new routine:

Quote from: Removing an element
Variables:
And yet again, you need L.

L - current length of array (initialized to 0 at the beginning of the program and updated by the routine itself)

Input:
Well, we need to know which element ot remove.

r1 - Index of element to remove

Output:
None. (Again, you can modify it to return something useful, but we won't here.)

Code:

You can probably tell that it's a copy statement. What we're trying to do is copy everything after the element to be removed four bytes back—overwriting the element you want to remove. The routine also takes care of the array length variable (L) by subtracting one.

The only thing that should seem weird here is the extra "+1" at the end. It might not make sense, but it takes care of the case where you're trying to remove the very last element. If that extra little bit of code weren't there, the Copy( statement would try to copy 0 bytes backwards, which gets translated to a copy a 65536 bytes, which is definitely not what you want. This extra bit doesn't do us any harm besides slowing the program down a tiny, tiny bit, but it takes care of that scenario for us.

So now that you have the routine down, let's actually use it. Go back into the main program and change it to this:

All the changes are in the For(I,1,L) loop. It basically tests the X- and Y-values after they're changed to see if they're completely off the screen, and if so, they get removed. The reason I jump to a lable RM instead of writing the code twice is because it's smaller and even gets rid of any chance of some certain nasty coincidences I won't talk about here. The point is it works.

Arrays in arrays (in arrays)

So there you are. Arrays in Axe.

Before we leave you to coding in peace, there's one last trick we're here to teach you: how to make arrays whose elements are arrays in their own right, or arrays of arrays.

Arrays in arrays? What's the point of that?

Surprisingly, you can do a lot with them. For example, say you have a puzzle game where each level has a fixed set of enemies. Instead of making a gigantic if-elseif-elseif-elseif... structure, you can put the enemy data for each level in an array, then organize that whole thing into an array.

But here's the problem: How do you store an array of elements that aren't a fixed size? The real problem comes in parsing it—how could you loop through a list whose elements could be any number of bytes long?

Like with any varying array, the solution comes in embracing the pointers. By that we mean simply that you don't have to put the structures themselves into the array; just point to them. Here's an example.

First, create the raw data you're going to put into an array.

Here are the first-level arrays:

Notice we use Data( with each pointer followed by an r (since pointers are always two bytes). The 3 and 2 in the beginning denote the number of elements in that array, since this value can change. Now it's the arrays' turn to be referenced from an array, in much the same way:

The increments are there to offset the length prefixes on those arrays.

To do something with this, let's loop through the array and all its arrays to print whatever is stored there:

With that, let's call it a day. Enjoy your newfound powers and happy coding!

[img]http://clrhome.org/
« Last Edit: April 19, 2012, 06:44:29 pm by Deep Thought »

Happybobjr

• James Oldiges
• LV11 Super Veteran (Next: 3000)
• Posts: 2325
• Rating: +128/-20
• Howdy :)
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #1 on: February 20, 2011, 01:44:10 pm »
How do you deal with speed in axe, I have never gotten that
« Last Edit: February 20, 2011, 04:41:40 pm by Deep Thought »
School: East Central High School

Axe: １.０.0
TI-84 +SE  ||| OS: 2.53 MP (patched) ||| Version: "M"
TI-Nspire    |||  Lent out, and never returned
____________________________________________________________

Munchor

• LV13 Extreme Addict (Next: 9001)
• Posts: 6199
• Rating: +295/-121
• Code Recycler
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #2 on: February 20, 2011, 01:45:44 pm »
How do you deal with speed in axe, I have never gotten that

Speed like of the processor (MHz?)
« Last Edit: February 20, 2011, 04:41:46 pm by Deep Thought »

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #3 on: February 20, 2011, 01:46:17 pm »
How do you deal with speed in axe, I have never gotten that

When you move an object, just add more to its X/Y values to make it go faster.

And part 2 has been added! This'll be the last part for today.
« Last Edit: February 20, 2011, 04:41:52 pm by Deep Thought »

leafy

• CoT Emeritus
• LV10 31337 u53r (Next: 2000)
• Posts: 1554
• Rating: +475/-97
• Seizon senryakuuuu!
Re: Arrays in Axe (Bullet Code Tutorial)
« Reply #4 on: February 20, 2011, 01:46:31 pm »
Yay, Ive always wondered how to remove a certain object from an array.
In-progress: Graviter (...)

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Re: Arrays in Axe (Bullet Code Tutorial)
« Reply #5 on: February 20, 2011, 01:48:18 pm »
Yay, Ive always wondered how to remove a certain object from an array.

Sorry, that's planned for part 5 or 6, so it'll take a while.

Next will be how to let the objects move around the screen.

Munchor

• LV13 Extreme Addict (Next: 9001)
• Posts: 6199
• Rating: +295/-121
• Code Recycler
Re: Arrays in Axe (Bullet Code Tutorial)
« Reply #6 on: February 20, 2011, 01:48:57 pm »
Speed:

Code: [Select]
Repeat getKey(15)Pt-On(X,Y,Pic1If getKey(1)Y+1->Y.Just change the 1 above to raise speed.EndDispGraphEnd

FinaleTI

• Believe in the pony that believes in you!
• CoT Emeritus
• LV10 31337 u53r (Next: 2000)
• Posts: 1830
• Rating: +121/-2
• Believe in the pony that believes in you!
Re: Arrays in Axe (Bullet Code Tutorial)
« Reply #7 on: February 20, 2011, 02:02:08 pm »

Looks good, but aren't labels only supposed to be up to 3 chars?
« Last Edit: February 20, 2011, 02:04:09 pm by FinaleTI »

Spoiler For Projects:

My projects haven't been worked on in a while, so they're all on hiatus for the time being. I do hope to eventually return to them in some form or another...

Spoiler For Pokemon TI:
Axe port of Pokemon Red/Blue to the 83+/84+ family. On hold.

Spoiler For Nostalgia:
My big personal project, an original RPG about dimensional travel and a few heroes tasked with saving the world.
Coding-wise, on hold, but I am re-working the story.

Spoiler For Finale's Super Insane Tunnel Pack of Doom:
I will be combining Blur and Collision Course into a single gamepack. On hold.

Spoiler For Nostalgia Origins: Sky's Story:
Prequel to Nostalgia. On hold, especially while the story is re-worked.

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #8 on: February 20, 2011, 02:03:06 pm »
Dang, I've always used 2, so I didn't know what the limit was...

EDIT: Fixed.

EDIT2: Finished the first example program! Next I'll be explaining how to get rid of the enemy when it goes off the screen and how to make it shoot bullets

EDIT3: You know it's long when a post takes up half the page O.o
« Last Edit: February 21, 2011, 09:40:25 pm by Deep Thought »

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #9 on: February 21, 2011, 09:41:12 pm »

Yay, Ive always wondered how to remove a certain object from an array.

Well finally I've added it Look under "Getting rid of the extras."

ztrumpet

• The Rarely Active One
• CoT Emeritus
• LV13 Extreme Addict (Next: 9001)
• Posts: 5712
• Rating: +364/-4
• If you see this, send me a PM. Just for fun.
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #10 on: February 21, 2011, 09:58:57 pm »
I love how it is so far.  Great job Deep!
If I'm wrong, please correct me!
Unfinished Projects:
 Elmgon 14% Basic Movement Demo Homescreen Game Pack 80% Basic Latest Release Cube Droid Saves the Galaxy 65% Axe Demo Detonate 70% Axe
Completed Projects:
Exodus | Midnight |Drifter | Axe Snake | Jump! | Factory Theta | Spider | Plot Drop | Papi Jump | Numb3rs | Nibbler | Boost | Duel Tile Map Editor | Homescreen Map Editor | Key Group Check | Oasis

leafy

• CoT Emeritus
• LV10 31337 u53r (Next: 2000)
• Posts: 1554
• Rating: +475/-97
• Seizon senryakuuuu!
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #11 on: February 21, 2011, 10:15:21 pm »
Ooohhh, you use the Copy( and pointer before...ahhh, that's genius.
In-progress: Graviter (...)

Deep Toaster

• So much to do, so much time, so little motivation
• LV13 Extreme Addict (Next: 9001)
• Posts: 8217
• Rating: +758/-15
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #12 on: February 21, 2011, 10:23:35 pm »
And what's great is that you can easily change it a bit to remove elements of any size, and even remove multiple elements at once (for example if two objects are always tied to each other). There are even ways to make arrays of different-sized elements A bit hard to keep at a decent speed, but really fun to work with.

DJ Omnimaga

• Clacualters are teh gr33t
• CoT Emeritus
• LV15 Omnimagician (Next: --)
• Posts: 55935
• Rating: +3153/-232
• CodeWalrus founder & retired Omnimaga founder
Re: Arrays in Axe (Enemy/Bullet Code Tutorial)
« Reply #13 on: February 21, 2011, 11:53:50 pm »
Nice Deep! Some people wondered how to have games like shoot-em-ups before and were unsure because they didn't knew how to deal with arrays. Should be pretty useful!

Deep Toaster

• So much to do, so much time, so little motivation