Log In  
Follow
asommer

Web developer, systems admin, network admin, fan of The Arsenal, and working on becoming a game developer using Pico-8 as a starting place.

Party On!

SHOW MORE

Remember That Movie From The 80s

Coming up with the title or this post made me remember that movie Enemy, Mine from the 80s. I think it was starring Dennis Quaid and Lewis Gossit Jr. They were enemies crash landed on a hostile deserted world and had to work together to survive. Then of course they become friends and have a bunch of adventures.

Interesting that they were both starship pilots and I'm working on a space shooter game... cause you know I liked them when I was a kid... in the 80s...

Making Sparks

To copy another element from Cave Berries, because I really like the way the laser sparks when it hits a wall, I added a sparks function/method to the bulletconstruct function:

obj.sparks = function(this)
  if (this.des > 0) then
    for i = 1,4 do
      rnd_x = flr(rnd(12))
      modx = 2
      rnd_y = flr(rnd(4))
      c = 11

      if (i % 2 == 0) then rnd_x += modx, c = 3 end

      pset(this.position.x - 4 + rnd_x, this.position.y + rnd_y, c)
    end
  end
end

The sparks method (I'm going to go ahead and refer to a function on an object/table as a method) also needs to be called in the _draw method in order to actually have the pset function work:

foreach(bullets, function(obj)
    spr(obj.sprite, obj.position.x, obj.position.y)
    obj.sparks(obj)
end)

The above is the updated foreach bullets in the _draw function. Also, inside the bulletconstruct destroy method call it:

obj.destroy = function(this)
  this.des += 1
  this.sparks(this)
  this.hitbox = {x=0,y=0,w=0,h=0}
end

There now we have a cool visual... but no sound effect cause you know... space and all.

First Enemy

As you might be able to tell I whipped up an enemy ship that shoots out a red and orange bullet/bomb/beam out our plucky hero. To get things working we'll copy 'n paste a lot of code from the asteroid and bullet objects. Add an enemy function:

function enemy()
  local obj = {}

  -- only come in from random top area.
  x = flr(rnd(127))

  -- set the direction randomly left, right, or center
  xs = {0, 1, 2}
  rnd_x = xs[flr(rnd(3)) + 1]
  if(rnd_x == 2)then rnd_x = -1 end
  obj.dir = rnd_x

  obj.position = {x=x, y=0}
  obj.sprite = 22

  obj.update = function(this)
    this.position.y += 1
    this.position.x += obj.dir

    if(this.position.x == hero.position.x or this.position.x + 8 == hero.position.x + 8)then obj.fire(obj) end

    -- remove it when it goes off screen
    if(this.position.y>127)then del(asteroids, this)end
    if(this.hp <= 0)then this.des += 1 end
    if(this.des >= 20)then this.sprite = 24 end
    if(this.des >= 40)then del(enemies, this) end
  end

  obj.explode = function(this)
    this.sprite = 23
  end

  obj.hitbox = {x=0, y=2, w=8, h=8}
  obj.hp = 10
  obj.des = 0

  obj.fire = function(this)
    if (this.des == 0) then
      text = "enemy fire!!! ..."

      add(bullets, bullet(this.position.x, this.position.y + 10, 'down', 6))
    end
  end

  return obj
end

At this point the enemy is pretty much the same as an asteroid. Notice the single line if in the update method though. If the enemy happens to line up with the hero on the x axis, then FIRE! The fire method is new and basically adds a bullet to the bullets array.

Notice that the function call is different than what we used before. I did some refactoring of the $construct functions and made them "first class". Not sure what assigning a function to a variable gets you in Lua, but it seems to work the same as using the function keyword.

Also, I setup the symbols-tree-view package for Atom to be able to list the functions in a list in the right-hand side of the editor window. I find it super useful when jumping around between functions. The details can be found in this thread. Thanks to @Sanju for pointing it out to me.

Updated bullets

Here's the updated bullet definition:

function bullet(x, y, dir, sprite)
  local obj = {}
  obj.position = {x=x, y=y}
  obj.sprite = sprite
  obj.des = 0
  obj.dir = dir

  obj.update = function(this)
    if(this.des == 0 and this.dir == 'up')then this.position.y -= 6 end
    if(this.des == 0 and this.dir == 'down' and this.sprite == 6)then this.position.y += 4 end

    -- it's hit something
    if(this.des > 0)then this.destroy(this) end
    if(this.des >= 5)then del(bullets, this) end

    -- remove it when it goes off screen
    if(this.position.y<0)then del(bullets, this)end
  end

  obj.hitbox = {x=0, y=0, w=8, h=8}

  obj.destroy = function(this)
    this.des += 1

    this.sparks(this)
    this.hitbox = {x=0,y=0,w=0,h=0}
  end

  obj.sparks = function(this)
    -- if(this.des > 0)then pset(this.position.x, this.position.y, 10) end
    if (this.des > 0) then
      for i = 1,4 do
        rnd_x = flr(rnd(12))
        modx = 2
        rnd_y = flr(rnd(4))
        c = 11

        if (i % 2 == 0) then rnd_x += modx, c = 3 end

        pset(this.position.x - 4 + rnd_x, this.position.y + rnd_y, c)
      end
    end
  end

  --return the bullet
  return obj
end

The main changes are in the parameter list. Since enemies will be firing down and will use different beams we need to specify a direction and a sprite. Then in the update method the direction is checked and the bullet's y position is either incremented or decremented.

Updating update

Next, the _update function needs adjusted to take into account enemies and having enemy bullets hit our hero. Before we dive into that let's take a look at the each_enemies function. The _update function was starting to get a little crowded so I moved some looping code into another function:

function each_enemies()
  foreach(enemies, function(enemy)
      enemy.update(enemy)

      -- collide enemies with bullets
      foreach(bullets, function(bullet)
        if (enemy.hp > 0) then
          if (collide(enemy, bullet)) then
            text = "enemy hit!!!"
            -- display bullet sprite
            bullet.destroy(bullet)

            enemy.hp -= 1

            -- knock it back a little
            enemy.position.y -= 2

            if(enemy.hp <= 0) then enemy.explode(enemy)end
          end
        end
      end)

      -- enemy collides with hero?
      if (collide(hero, enemy) and enemy.des == 0 and hero.des == 0) then
        sfx(01)
        enemy.hp -= 1
        hero.hp -= 1

        -- knock it back a litte
        enemy.position.y -= 2
        enemy.position.x -= enemy.dir + enemy.dir

        if(enemy.hp <= 0) then enemy.explode(asteroid) end
        if(hero.hp <= 0) then hero.explode(hero) end
      end
  end)
end

Basically the same as the asteroids loop this function uses a foreach to loop the enemies array (added to the _init function) and inside the enemies loop the bullets array is looped an collide is used to check for hits on the enemy ship. Like the astroids loop collide is also used to check for collisions with the hero ship. Thinking to have a "drone" type enemy that will try to blast into the hero, but we're not quite there yet.

Then inside the _update function add:

each_enemies()

Getting due for some major refactoring, but that'll probably be a post all by itself.

Hero Bullet hits

In the hero object setup inside the _init function we need to do another bullet loop:

-- bullet hits hero
foreach(bullets, function(bullet)
  if (this.hp > 0) then
    if (collide(this, bullet)) then
      text = "hero hit!!!"
      sfx(4)

      -- display bullet sprite
      bullet.destroy(bullet)

      this.hp -= 1

      -- knock it back a little
      this.position.y += 2

      if(this.hp <= 0) then this.explode(this)end
    end
  end
end)

The loop is placed in the hero's update method and is similar to the other bullet loops. This loop uses collide to check if they are hitting the hero, sets the text for debug purposes, plays a sound cause there's probably be some noise when something hits the outside of a ship in space, subtracts a hp while knocking the hero back a little, and then a check to see if the hero is out of hp and if so call the explode method.

Conclusion

Currently the enemies only enter the screen when the btn(5) is pressed. So I think next I'll work on some AI/attack patterns. Should make the game more fun.

Then the great refactor might happen. Not sure when I'll hit the token ceiling, but I feel like I've still got some space... (heh)

Party On!

P#26765 2016-08-11 10:18 ( Edited 2016-08-11 14:18)

SHOW MORE

A Hit, A Hit, A Very Palpable Hit

We've got lasers firing and astroids asteroiding, or whatever it is they do, so now we need to be able to actually hit objects with our lasers. And the reverse, to know when an object has been hit by a laser, or another object for that matter.

Since starting this foray into game development I've continually been impressed about how supportive and helpful the Pico-8 community is. The forums have loads of great posts with comments that are usually helpful and questions ask to help someones understanding. Very fun to be a part of it in whatever small way I can.

Updating bullets

As the guide in Pico-8 Zine #3 teaches the bullets (lasers in this case) need to have a hitobx to determine when they collide with something. With the hero laser there are two "beams" so we'll need to add a hitbox for each one. Update the bulletconstruct function with:

obj.hitbox = {x=0, y=0, w=8, h=8}

This uses the entire sprite for a hitbox, but I think it makes sense because the "beams" are spread apart and if something is hitting it from the side it's still hitting it. Ya, I think that makes sense.

Next, add a hitbox (and an hp value which we'll get to later) to the astroid in the asteroidconstruct function:

obj.hitbox = {x=0, y=2, w=8, h=8}
obj.hp = 5
obj.des = 0

The hero will also need a hitbox in the _init function add:

hitbox = {x=4, y=3, w=8, h=8},

Actually Doing The Hitting

Everything now has a hitbox so it's time to copy/integrate some more code from the DOM8VERSE article. In the foreach asteroids loop inside the _update function add a bullets loop:

foreach(asteroids, function(asteroid)
  asteroid.update(asteroid)
  foreach(bullets, function(bullet)
    if (asteroid.hp > 0) then
      if (collide(asteroid, bullet)) then
        del(bullets, bullet)
        asteroid.hp -= 1

        -- knock it back a little
        asteroid.position.y -= 2
        asteroid.position.x -= asteroid.dir + asteroid.dir

        if(asteroid.hp <= 0) then asteroid.explode(asteroid)end
      end
    end
  end)
end)

Inside the inner bullets foreach we're checking the asteroid.hp to make sure it's not 0 then checking to see if a bullet is colliding with the astroid. If a bullet has hit the astroid it is deleted from the bullets array, the astroid's hp is decremented by 1 and it is blasted back a little. Also, if the asteroid's hp is <= 0 (not sure if it can actually go below zero since it's being decremented by 1 each time, but better safe than sorry) the asteroid's explode function/method is called.

I used the same collide function as the guide:

function collide(obj, other)
  if other.position.x+other.hitbox.x+other.hitbox.w > obj.position.x+obj.hitbox.x and
      other.position.y+other.hitbox.y+other.hitbox.h > obj.position.y+obj.hitbox.y and
      other.position.x+other.hitbox.x < obj.position.x+obj.hitbox.x+obj.hitbox.w and
      other.position.y+other.hitbox.y < obj.position.y+obj.hitbox.y+obj.hitbox.h then

    return true
  end
end

It's a great way to see if two objects have connected their hitboxes and I'm not sure if I can improve it.

Exploding asteroids

After five hits by a laser it's bye bye asteroid, but it'd be cool to have it sort of come apart and degrade. In the asteroidconstruct function add:

obj.explode = function(this)
  this.sprite = 18
end

This function/method (need to look up what the proper term is for lua functions on an object) simply set's the asteroid's sprite to a new one. Here's a screeny:

Changing the sprite once isn't all that great, but changing it again after a few cycles is a little cooler. Add these lines to the update function/method in astroidconstruct:

obj.update = function(this)
  this.position.y += 1
  this.position.x += obj.dir

  -- remove it when it goes off screen
  if(this.position.y>127)then del(asteroids, this)end
  if(this.hp <= 0)then this.des += 1 end
  if(this.des >= 20)then this.sprite = 19 end
  if(this.des >= 40)then del(asteroids, this) end
end

Now the asteroid's update function will check if the des attribute is no longer 0 (des for destroyed, heh) and if so increment it. After des is greater than 20 the sprite changes again and after it reaches 40 the astroid is removed.

It's pretty much the effect I was going for.

Ship Bashing

Destroying asteroids is one thing, but what happens if they crash into our hero? The code to check for hero hits is pretty much the same as for an asteroid. I created an update function on the hero object setup in the _init function and moved some code from the _update function into it:

update = function(this)
  -- text = "updating hero..."
  b0=btn(0)
  b1=btn(1)
  b2=btn(2)
  b3=btn(3)
  b4=btnp(4, 0)
  b5=btn(5)

  if (b0 or b1 or b2 or b3) then move_hero() end
  if (b4) then hero_fire() end

  if(hero.des > 0)then hero.des += 1 end
  if(hero.des >= 20)then this.sprite = 21 end
  if(hero.des >= 40)then this.sprite = 22 end
  if(hero.des >= 60)then gameover() end
end,

The button check code is now scoped to the hero which makes it a little more readable I guess. Notice the hero.des check pretty much the same as the asteroid's. When the hero reaches 0 the gameover function is called though:

function gameover()
  text = "game over man"
  if(btn(4) or btn(5))then run() end
end

This simple function sets the text printed to the screen then calls the run function when button 4 or 5 is pressed. The run function essentially resets the cart.

Also, we'll add an explode function/method to the hero:

explode = function(this)
  this.sprite = 20
  this.des = 1
end,

Once again update the _update function to check if an asteroid has collided with the hero. Inside the foreach asteroids loop add:

-- asteroid collids with hero?
if (collide(hero, asteroid) and asteroid.des == 0 and hero.des == 0) then
  sfx(01)
  asteroid.hp -= 1
  hero.hp -= 1

  -- knock it back a litte
  asteroid.position.y -= 2
  asteroid.position.x -= asteroid.dir + asteroid.dir

  if(asteroid.hp <= 0) then asteroid.explode(asteroid) end
  if(hero.hp <= 0) then hero.explode(hero) end
end

Pow, now asteroids can take out our plucky hero.

Conclusion

It's fun to have a game over state and to blast some space rocks. I'm pretty satisfied with the way things explode too.

I'm kind of going for the effect of things exploding in space like the movie Gravity, but you know in 8 bit.

Feels good to make progress...

Party On!

P#26696 2016-08-09 10:13 ( Edited 2018-02-28 20:27)

SHOW MORE

Space Rocks

Adding asteroids to the mix seems like a good idea. They don't have bullets (hopefully) so they should be relatively easy to have float down the screen. Then again maybe they'll come into the the scene from any angle... haven't really decided on that part yet.

I think using the same type of object with an update method and an "array" of objects as we used with the bullets/lasers should work for asteroids too.

Creating Asteroids

Whip up a astroidconstruct function super similar to the bulletconstruct function:

asteroidconstruct = function()
  local obj = {}

  -- only come in from random top area.
  x = flr(rnd(127))

  -- set the direction randomly left, right, or center
  xs = {0, 1, 2}
  rnd_x = xs[flr(rnd(3)) + 1]
  if(rnd_x == 2)then rnd_x = -1 end
  text = rnd_x
  obj.dir = rnd_x

  obj.position = {x=x, y=0}
  -- obj.position = {x=52, y=52}
  obj.sprite = 17
  obj.update = function(this)
    this.position.y += 1
    this.position.x += obj.dir

    -- remove it when it goes off screen
    if(this.position.y>127)then del(asteroids, this)end
  end
  return obj
end

There will be differences though. First off we set the x coordinate to a random value from 0 - 127. Then a there's some statements to get a random direction for the asteroid left, right, or center. The value is applied to the object. The sprite and position are then set.

After all that the update "method" is used to make the asteroid track across the screen in a top to bottom ish movement. Finally, after the asteroid leaves the screen it is removed from the asteroids object/array.

Here's a look at sprite 17:

[img=/img/asteroid_sprite.png]

Oh ya, shading... woo!

Creating Random asteroids

Now that we can create an astroid and have it blast down the screen in a southerly direction wee need to randomly send some 'roids through space.

After trying a few things out I came up with these relatively simple statements to be added to the _update function:

ast_timer += 1
if(ast_timer % 100 == 0)then add(asteroids, asteroidconstruct()) end

So if the ast_timer variable is divisible by 100 then a new asteroid is created and it's update function will meander it through the play area. Which reminds me, back up in the _init function create the ast_timer (asteroid timer) variable:

function _init()
  -- states
  -- 0 idle
  -- 1 moving
  -- 2 firing

  hero = {
    x = 58,
    y = 100,
    sprite = 0,
    state = 0,
    pst = 0
  }

  bullets = {}
  asteroids = {}
  ast_timer = 0

  text = "welcome to rumpus blaster"
end

Great, now every so often a chunky asteroid will fly through.

Updating Bullets

Adding the remove asteroid if off the screen line makes me realize something similar for bullets is needed too.

In the update function (or is it now a method???) inside the bulletconstruct function add this line after the this.position.y* update line:

if(this.position.y<0)then del(bullets, this)end

Conclusion

Things are progressing, but not quite as fast as I'd like. Mostly due to the day job taking up more development time than I'd like, but then again it's nice to make some money.

Another thing I've noticed while developing games is that the temptation to play the game I'm working on as well as other games is a lot hight than when I'm developing a web app, mobile app, etc. I guess it's kind of obvious, but I didn't really take it into account before setting off on this adventure.

I think the next thing to work on is making bullets/lasers actually hit something...

Party On!

P#26453 2016-08-04 08:55 ( Edited 2016-08-04 12:56)

SHOW MORE

Refactor Fun

After reading more of the Pico-8 Zine 2 it's crazy obvious that I should use a "Finite State Machine" to control the hero ship, enemies, etc. I've been looking through the code of the iconic P.A.T. Shooter and it seems a state machine is used to keep track of everything.

It makes sense and it seems to allow for more "portable" functions. As in hey this function is great I can totally use it pretty much as is in a whole other project. Yay, me! Yay, us guys! (at least that's what I want to say)

Using init

The first thing to refactor, I guess, is the global variables declared at the top of the file. They can be blasted into the _init function and everything should work the same:

function _init()
  hero = {
    x = 58,
    y = 100,
    sprite = 0,
    state = 0,
    pst = 0
  }

  bullets = {}
  asteroids = {}

  text = "welcome to rumpus blaster"
end

Notice the two new attributes state and pst. The state will hold a number designating what the current state of the hero is: 0=idle, 1=moving, 2=firing. The pst is for the player state time which will help track how long the player has been in the current state. It will be incremented in _update().

Changing State and Updating Functions

Okay I lied, the hero.state and hero.pst won't be directly updated in _update(), but in the move_hero and hero_fire functions. Following the guide add a change_state():

function change_state(p, s)
  p.state = s
  p.pst = 0
end

The functions takes an object p and a number/state s. The object's state and pst attributes are then updated. Thinking to use this same function for enemies as well.

Then edit move_hero adding this line at the top:

  change_state(hero, 1)

And the same in hero_fire:

  change_state(hero, 2)

Finally, to be more state machine like (I think) update the _update function:

function _update()
  b0=btn(0)
  b1=btn(1)
  b2=btn(2)
  b3=btn(3)
  b4=btnp(4, 0)
  b5=btn(5)

  if (b0 or b1 or b2 or b3) then move_hero() end
  if (b4) then hero_fire() end

  foreach(bullets, function(obj)
      obj.update(obj)
  end)
end

Conclusion

I think things are now in a better state (haaa). Or at least some ground work has been laid to make it easier to manage multiple characters in the game at one time. Also, might help determine when/what things are happening around the screen.

New territory for me so I'm sure I'll be learning a lot the more I work with it.

Party On!

P#26312 2016-08-02 08:23 ( Edited 2016-08-02 16:23)

SHOW MORE

FIRE!!!

When I hear the word FIRE (all caps necessary) I always think of Captain Kirk at the end of Undiscovered Country:

And it's now time to make our hero ship fire it's own weapons. In this case we'll start with lasers and go from there. Thinking to have some type of bomb/missile to add a little extra kick to our plucky hero.

Plucky, ya definitely plucky!

Adding the Sprite

In this case the sprite is borrowed again from Cave Berries. Except the color is going to be changed up to green and dark_green.

I created a simple alternating green/dark_green pattern on the edge of the 8x8 sprite position. This will match up with the "top" of the hero ship's "laser pylons".

Finding Help

I found it kind of tricky to have the laser fire when the Z key is pressed. It's simple to make the sprite appear when btn(4) returns true, but it's more complicated to then have the sprite "fly" up the screen.

I guess when you think about it it's the same principal as making the hero ship move up the screen...

Either way I was about to post a question to the forum asking if there are any good "bullet/projectile" tutorials for Pico-8 games, but then I said to myself "self, you should check all the PICO-8 Zines first". So even though I was only about half way through PICO-ZINE #2, I went ahead and downloaded #3. And blamo! A great tutorial by @schminitz.

Coding the laser

Or rather coding the bullets. I grabbed the bulletconstruct function pretty much directly from the tutorial, but I did make some modifications to adapt it for my game:

bulletconstruct = function(x, y)
  local obj = {}
  --an array containing x and y position
  obj.position = {x=x, y=y}

  --the sprite number used to draw the bullet
  obj.sprite = 16

  --define an ‘update’ function that will be called by the program
  obj.update = function(this)
    --move the bullet to the right
    this.position.y -= 6
  end

  --return the bullet
  return obj
end

The main change is to update the this.position.y instead of the x position and to subtract 6 because my "bullets/lasers" are longer. Plus cranking it up to 6 makes them move faster.

The tutorial says to loop over all the objects in the game in the _draw and _update functions, but I'm not there yet so I created a bullets global object at the top and adjusted the foreach loops like so:

function _update()
  move_hero()
  hero_fire()

  foreach(bullets, function(obj)
      obj.update(obj)
  end)
end

function _draw()
  cls();

  updatetext()

  -- draw hero
  drawhero()
  move_hero()

  foreach(bullets, function(obj)
      spr(obj.sprite, obj.position.x, obj.position.y)
  end)
end

I then created the hero_fire function:

function hero_fire()
  if btnp(4, 0) then
    sfx(00)

    rx = hero.x
    lx = hero.x

    ry = hero.y - 5
    ly = hero.y - 5

    add(bullets, bulletconstruct(rx, ry))
    add(bullets, bulletconstruct(lx, ly))
  end
end

Using the btnp function I learned about in the tutorial when button 4 is pressed the left and right "laser pylon" positions are calculated then the bullets are added using the add function to the global bullets object.

Laser Sound, pew pew

Notice the sfx call at the beginning of the hero_fire function. I whipped up a simple, sort of muted, sound effect:

This will probably need to be revisited, but it works for now and is great feedback.

Conclusion

Getting this simple functionality working is a great way to learn Pico-8 development, and I guess some Lua too. I keep coming across other things I'll need, or potentially need, to do. Things like a finite state machine, whatever that is, and keeping an object of game "characters" to be able to update them all at once.

Fun stuff!

Party On!

P#26015 2016-07-28 07:20 ( Edited 2016-07-28 11:23)

SHOW MORE

Cranked missiles up to 11, added shields, and life to the hero and still couldn't break 2000 points...

LOL

P#25925 2016-07-26 14:19 ( Edited 2016-07-27 09:24)

SHOW MORE

Making Exhaust Look Good

Using a straight up sprite for an exhaust trail didn't look that cool to me. Similar space shooter games have different ways of animating fire/exhaust coming out of a ship, but I really like the "sparkly" effect in games like Cave Berries when the eye laser hits something.

Brief flashes of bright pixels are surprisingly satisfying...

Sidebar... a little Refactoring

Before we jump into exhaust awesomeness I want to send a big thanks to @morningtoast and @Connorses for some great feedback on my last post. morningtoast suggested to assign "attributes" to a Lua Table when it's declared very similar to JavaScript, Ruby, etc. So the hero statement becomes:

hero = {
  x = 58,
  y = 100,
  sprite = 0,
}

Which is cleaner and less lines of code. @Connorses offered a great suggestion for created a slew of "actors" at one time with a function. Totally will look into that deeper in a future post.

Using pset in exhaust()

The built in pset function is quite handy it allows you to set the color of any pixel identified by x, y coordinates. Wonder if you could build an entire game with only using loops, ifs, and pset...

I created an exhaust function to determine the direction (based on button number) of the hero ship and adjust some trailing pixels with pset:

function exhaust(x, y, button)
  rnd_x = 0
  modx = 0
  mody = 0
  submod = 0
  len = 0

  if button == 2 then
    -- random color of red, yellow, or orange
    rnd_color = flr(rnd(3)) + 8

    -- adjust pixel location.
    rnd_x = 3
    mody = 8
    modx = 1
    submod = 2
    len = 30

    -- add some base fire.
    pset(x + rnd_x, y + mody, 10)
    pset(x + rnd_x + modx, y + mody + 1, 9)

  elseif button == 3 then
    -- random color of red, yellow, or orange
    rnd_color = flr(rnd(3)) + 8

    -- adjust pixel location.
    rnd_x = 3
    mody = -1
    modx = 1
    submod = 2
    len = 30

    -- add some base fire.
    pset(x + rnd_x, y + mody, 10)
    pset(x + rnd_x + modx, y + mody + 1, 9)

  elseif button == 0 then
    -- random color of dark_gray, light_gray, or white
    rnd_color = flr(rnd(3)) + 5

    -- adjust pixel location.
    rnd_x = 8
    mody = 4
    submod = -9
    len = 10
  elseif button == 1 then
    -- random color of dark_gray, light_gray, or white
    rnd_color = flr(rnd(3)) + 5

    -- adjust pixel location.
    mody = 4
    submod = 11
    len = 10
  end

  for i = 1,10 do
    rnd_y = flr(rnd(len))

    -- makes exhaust wide.
    if (i % 2 == 0) then rnd_x += modx end

    -- makes exhaust go up.
    if (button == 3) then rnd_y = -rnd_y end

    -- make exhaust come out the right side.
    if (button == 0) then rnd_y = 0 end
    if (button == 0) then rnd_x = flr(rnd(len)) end

    -- make exhaust come out the left side.
    if (button == 1) then rnd_y = 0 end
    if (button == 1) then rnd_x = flr(rnd(len)) end

    pset(x + rnd_x - submod, y + mody + rnd_y, rnd_color)
  end
end

This function is pretty long, and I'm sure there's a bunch of places to optimize, but it works. At the top of the function I set some variables to determine the x and y of the pixel to set. Then the number of the button parameter is checked.

Inside each if statement the color is selected. I shamelessly got the color function:

rnd_color = flr(rnd(3)) + 8

From the Cave Berries cart. In the case above it will select a number from 1-3 then if you add 8 you get an index for the colors red, orange, and yellow. Very slick!

The next statements determine where to position the exhaust pixels: top, bottom, left, or right by adding/subtracting from x,y. Lastly, a few "base" exhaust pixels are added to make things a little beefy.

After the ifs is a for loop which adds random pixels in the desired location. For top and bottom exhaust the x pixel is "waggled" some to make a wider spread.

Conclusion

I'd like to refactor this function to set each needed variable before the loop. That way the button wouldn't have to be checked again.

At least it feels like a lot of duplication calling if so many times.

Party On!

P#25916 2016-07-26 10:25 ( Edited 2016-07-26 14:25)

SHOW MORE

Movement

I learned how to move an "object"/"pixels" around the screen while working through the Pico-8 Zine paddle game example. It seems to work well and I was quickly able to move the paddle back and forth. Very satisfying.

While looking at the source of other games I noticed they used a pretty similar approach. One thing they did use that seems more segmented is to use Tables to create key/value pairs for attributes of an object.

Coming from web development this looks suspiciously like an object...

The Hero

In this case I created a hero table and set some starting attributes:

-- hero
hero = {}
hero.x = 58
hero.y = 100
hero.sprite = 0

So the hero will use sprite 0 and start at x 58 and y 100 which is the lower middle of the screen.

move_hero()

With the hero placed the move_hero() function does the heavy lifting:

function move_hero()
  if btn(0) then
    hero.x -= 1
  elseif btn(1) then
    hero.x += 1
  elseif btn(2) then
    hero.y -= 2
  elseif btn(3) then
    hero.y += 2
  end
end

Notice that moving along the x axis is incremented/decremented by 1 instead of 2 along the y axis. I thought it'd be interesting if the hero had main engines and smaller maneuvering thrusters for side to side movement.

Exhaust

I thought it would be cool to have an exhaust sprite coming out of the ship depending on which direction it's moving. To start that off I created four different versions of the hero ship. One with the "aft" engine lit, one with the "fore" engine lit, one with a starboard thruster lit, and a final one with a port thruster lit.

Like the nautical terms? Don't worry I had to look it up on Wikipedia to remember which one is right and left.

The move_hero() function now looks like:

function move_hero()
  if btn(0) then
    text = "button 0, left..."

    hero.sprite = 2
    spr(006, hero.x + 8, hero.y)

    hero.x -= 1
  elseif btn(1) then
    text = "button 1, right..."

    hero.sprite = 3
    spr(007, hero.x - 8, hero.y)

    hero.x += 1
  elseif btn(2) then
    text = "button 2, up..."

    hero.sprite = 0
    spr(004, hero.x, hero.y + 8)

    hero.y -= 2
  elseif btn(3) then
    text = "button 3, down..."

    hero.sprite = 1
    spr(005, hero.x, hero.y - 8)

    hero.y += 2
  end
end

As you can see I also added a text "element" that will change depending on which button is pushed. This seemed like a nice debugging technique to make things clear while in the game.

[img=/img/hero_ship_sprites.png]

_update and _draw

The _update and _draw functions are pretty simple:

function _update()
  move_hero()
end

function _draw()
  cls();

  updatetext()

  -- draw hero
  drawhero()
  move_hero()
end

I'm calling move_hero() in _update and _draw because it wouldn't show the exhaust sprite unless I called move_hero at least once in _draw. I guess that makes sense, but my first thought was that you could call spr in any function and have it draw to the screen even if the calling function is only executed in _update. Guess that is not the case.

Also, the little debugging function updatetext is:

text = "welcome to rumpus blaster"

function updatetext()
  print(text, 12, 6, 15)
end

Lastly, gotta have the cls() call in _draw to clear the screen. Pretty interesting if you play another game then load yours without calling cls...

Conclusion

That's a quick rundown of what I have so far. It's been a fun to allow a character to move along both axis. To improve the exhaust I'd like to make it sort of sparkly. We'll leave that for the next post.

Party On!

P#25652 2016-07-21 05:20 ( Edited 2016-07-21 15:52)

SHOW MORE

Discovery, or The Future is in the Past

For a few months I've been chewing on the idea of developing a 8 bit style game to run on Raspberry Pi alongside old ROMS from major game developers. I was thinking the idea was original, but after some reflection I realize I must have picked it up somewhere...

I ordered a C.H.I.P (well okay a couple of them) back in like November and read up on the PocketCHIP about the same time. While reading about PocketCHIP I read, and probably even watched some videos, about the Pico-8 fantasy console. I think that was my first exposure to Pico-8.

This is when I think I internalized the idea of a "brand new" pixelated "game engine" for developing games that run on low end hardware.

I am so glad that Zep and Lexaloffle did the hard work and developed such a cool platform for others to play in.

Learning Pico-8 Development

I started out watching Youtube videos of people developing/modifying games with Pico-8. One awesome feature of Pico-8 is that the editor, graphics creator, map editor, music, and sounds are all in the same interface. It's bananas really!

From there I read the first issue of the Pico-8 Zine which has great tutorials on creating a paddle and other games. It walks you through the code, creating sounds, and adding some sweet jams. Reading through some other tutorials was also super helpful.

Once the foundation of how the coding, graphics, and sounds all fit together I started my own project.

Rumpus Blaster

Being super new to game development I'm going to be basically copying, but hopefully adding some new features, graphics, etc, current games until I get the feel of things. I think this might be a good way to learn anyway...

So for my first game I'll try a space shooter similar to Galaga, but with far fewer ships. The P.A.T. Shooter cart is an inspiration and a high bar to shoot for. There's a lot to like about this title and I found that playing it with a USB NES controller is a lot of fun.

My Development Environment

Since I'm on a Mac the installation directory, or maybe it's more like the data directory, for Pico-8 is:

/Users/$USER/Library/Application Support/pico-8

Where $USER is the current username of the workstation and if you're on a Windows or Linux machine the path is slightly different. See the manual for the exact location for your platform.

The carts subdirectory is where games are downloaded and where new games are stored while in development. To make it easier to work with I created a symlink from the /Users/$USER/Library/Application Support/pico-8/carts directory to ~/work/carts:

ln -s /Users/$USER/Library/Application Support/pico-8/carts ~/work/

That way I can do a simple

cd work/carts

from my home directory.

Next, I fired up the Atom editor to start creating/editing carts. Since the .p8 files are simple text you can easily change things up in your favorite editor. Obviously this works best with the code part of the file, but I imagine if you're super brave you can change the sprite by editing the numbers by hand... I guess.

After getting access to my carts with in a more "professional" editor I installed the language-pico8 package with:

apm install language-pico8

This gives syntax highlighting (based on the Lua language highlighting) and also shows a sort of preview of the sprite map which is very nice.

Finally, I created a Git repository to have my carts under version control:

git init
git add .
git commit -am "Initial import."
git push

At this point I put the files into a private Git repo, but will probably create a repo on Github in the near future.

Workflow

I find the workflow is pretty quick to get used to. Especially if you've done any mobile app development or web development. Basically you edit code in the editor Alt+Tab to the Pico-8 console execute:

LOAD $CARTNAME
RUN

And your game fires up and you can see if things behave the way you expect. Also, if there's a syntax error the RUN command exits and prints a helpful line number.

One sort of "gotcha" is when creating sprites inside the Pico-8 console you want to make sure that you've saved before Alt+Tabing back to the editor. If you don't save in both the console and the external editor changes in one can overwrite changes in the other. Which isn't always fun. Guess that's what source code management is for though.

Conclusion

I'll be honest, I've wanted to develop games since the first time I played Wolfenstein 3d circa 1989 (maybe 1988) on a neighbors computer. He was way ahead of his time and I wished I had taken more advantage of having a neighbor with a computer... I think basketball and bicycles were more interesting at the time.

Getting to game development after learning web and some mobile development feels good though. Not to mention understanding how operating systems work from a systems administration standpoint.

This will be a lot of fun...

Party On!

P#25548 2016-07-19 10:41 ( Edited 2018-04-30 12:10)

Follow Lexaloffle:          
Generated 2024-03-19 09:46:59 | 0.090s | Q:20