Recalling that Last We Recalled Asteroids
This is a continuation of the
Starting Out With Lowerdash Tutorial. In this tutorial series we are building an Asteroids clone using Lowerdash.
Previously on Starting Out With Lowerdash...
- You learned what objects are
- You learned that Modules and Instances are objects
- You learned that women are not objects
- You learned that objects have behavior and state
One Man's Junk...
Last time we found that the player's
Ship had very well defined
behaviors:
- rotate left and right
- shoot
- thrust
For this part of the tutorial we'll be filling in the gaps left in our definition of a ship. The next tutorial will finish fleshing out the behaviors and state of the
Ship Module; then we'll move on to actually having the player pilot an instance of it.
Far Out Man...
You may have noticed we glossed over some things going on with the
Ship. Lets back up and look at those.
One of the biggest question you should have is: How do we really know whether its here or there?
Like, how do we know where any of us are, man?
You might say the
Ship is a "being" or "entity". Something that has a position in space and interacts with matter. All the other objects I pointed out in the game description also fit this description - those asteroids, flying saucers, and bullets all share common properties.
Since this is a game about things floating through space, lets define these properties on a new type:
SpaceJunk.
SpaceJunk exists in our game's world; which is the graphical screen on your 3DS. SmileBasic already ships with a way to represent things on the screen natively -
Sprites. But since this is a Lowerdash tutorial, we don't want to be dealing with management numbers and sprites everywhere.
SpaceJunk will represent the wrapper for SmileBasic's built-in sprites, bridging the gap to our "type system".
SpaceJunk is also a great place to implement the iconic screen wrap around Asteroid's has. When some junk reaches the edge of the screen it should continue moving from the opposite edge. This needs to happen every time the entity updates, and shouldn't be something we think about.
SpaceJunk has:
- X Position
- Y Position
- X Velocity
- Y Velocity
- A Sprite Management ID
SpaceJunk can:
- move
- be placed
- hit other things
- update
- wrap from one end of the screen to the other.
Well, we have a lot of specialized knowledge about junk now, best embrace it... Start by making another blank file, and saving it as "_SpaceJunk".
We'll make our Module, but this time with a few additions:
MODULE SpaceJunk
MEM SP%
MEM X#, Y#
MEM VX#, VY#
EXPORT DEF new(SPID%)
me.SP% = SPSET(SPID%)
RETURN me
END
EXPORT DEF move DX#, DY#
me.X# = me.X# + DX#
me.Y# = me.Y# + DY#
END
EXPORT DEF place X#, Y#
me.X# = X#
me.Y# = Y#
END
EXPORT DEF update
me.X# = me.X# + me.VX#
me.Y# = me.Y# + me.VY#
SPOFS me.SP%, me.X#, me.Y#
END
END
Woah! Woah... Woah... Woah Woah... A lot of stuff just happened.
I just threw a mostly complete Module at you; theres going to be a lot to take in. Lets try breaking it down slowly.
MEM SP%
MEM X#, Y#
MEM VX#, VY#
This line tells our
SpaceJunk Module that an
Instance of it will have five
member variables - the junks position, its speed and its sprite management number.
EXPORT DEF new(SPID%)
me.SP% = SPSET(SPID%)
RETURN me
END
This line sets the value of the variable SP%
belonging to the object that
me refers to.
In this line, we are getting a new Sprite management number for the passed in Sprite Defintion ID. We store this management number as part of the
me Instance because we want to keep it around for re-use later! We'll be able to directly access this sprite from any Module that is
SpaceJunk through its
SP% property too!
EXPORT DEF update
me.X# = me.X# + me.VX#
me.Y# = me.Y# + me.VY#
SPOFS me.SP%, me.X#, me.Y#
END
This
method will be what our game loop can call to keep this piece of junk updating while the game is running. Here,
me is an Instance of SpaceJunk thats been returned from
new.
me.SP% refers to the management number we got in the
constructor, and
me.X% and
me.Y% are the position that has been set through the
move and
place methods.
Junk Translocating
As I mentioned,
SpaceJunk needs to wrap around the screen. For the purposes of this game, we'll assume we aren't changing any screen settings and using the default 400x240 resolution of the top screen.
We want this wrapping behavior to apply whenever the
SpaceJunk updates it's sprite - that is in the
update method.
Clamping a value to the screen is something we want to re-use, but don't need on every instance of a module. We can use whats called a "static function" to add a DEF to
SpaceJunk thats always available.
STATIC DEF wrapTo(V, X)
IF V < 0 THEN RETURN X + V
IF V > X THEN RETURN V - X
RETURN V
END
Now just call the wrapping function in the
update method to enable the iconic effect:
EXPORT DEF update
me.X# = me.X# + me.VX#
me.Y# = me.Y# + me.VY#
me.X# = _.wrapTo(me.X#, 400.0)
me.Y# = _.wrapTo(me.Y#, 240.0)
SPOFS me.SP%, me.X#, me.Y#
END
Adding "physics"
I mentioned one more action
SpaceJunk should be able to do: hit other things. Let's add that now that we know the basics of whats happening. SmileBasic ships with a built-in collision check for its sprites, and we'll be using it.
First, modify the constructor to turn on collision for this
SpaceJunk's sprite:
EXPORT DEF new(SPID%)
me.SP% = SPSET(SPID%)
SPCOL me.SP%, TRUE 'respect scale
RETURN me
END
Now, we want to check if there is a collision every frame of the game, and if so trigger the
SpaceJunk's "getting hit" behavior:
EXPORT DEF update
...
SPOFS me.SP%, me.X#, me.Y#
IF SPHITSP(me.SP%) != -1 THEN me.onHit
END
We also need to add this "getting hit" behavior:
EXPORT DEF onHit
'Intentionally Empty
END
Why did we make an empty
method? When something that
is SpaceJunk gets hit, it can have its own
onHit defining a different reaction.
This concept uses a feature called "Dynamic Dispatch" in Lowerdash.
SpaceJunk without its own
onHit will call this empty DEF and do nothing on collision. But if a Module using
SpaceJunk has a different
onHit method, the Lowerdash runtime will find it first during the
update method and call it instead.
We've completely defined a piece of
SpaceJunk with the concepts that it is a moveable sprite, and can hit things. Go Ahead and Save this File!
Completed:
MODULE SpaceJunk
MEM SP%
MEM X#, Y#
MEM VX#, VY#
EXPORT DEF new(SPID%)
me.SP% = SPSET(SPID%)
SPCOL me.SP%, TRUE 'respect scale
RETURN me
END
EXPORT DEF move DX#, DY#
me.X# = me.X# + DX#
me.Y# = me.Y# + DY#
END
EXPORT DEF place X#, Y#
me.X# = X#
me.Y# = Y#
END
STATIC DEF wrapTo(V, X)
IF V < 0 THEN RETURN X + V
IF V > X THEN RETURN V - X
RETURN V
END
EXPORT DEF update
me.X# = me.X# + me.VX#
me.Y# = me.Y# + me.VY#
me.X# = _.wrapTo(me.X#, 400.0)
me.Y# = _.wrapTo(me.Y#, 240.0)
SPOFS me.SP%, me.X#, me.Y#
IF SPHITSP(me.SP%) != -1 THEN me.onHit
END
EXPORT DEF onHit
'Intentionally Empty
END
END
Tying it Together
Now you may be wondering when we would be getting back to the player's
Ship. We just covered a lot of ground, and you might want to take a moment to let it sink in; so I'll see you next time in:
Part 3!