11.03.2015

LMaGT 2 - Basic collisions with jQuery

In the last post, I ended with a couple static enemies, and a player that can move left and right. It's a pretty good start, but needs a bit more work before we can really call it a working prototype. The player needs to be able to shoot, and the enemies need to know they're being shot. The first part is easy, the second part has a couple harder bits but it can be done! Like last time, you can (click here) to get this project folder and follow along.

Before jumping in, it's worth pointing out that shooting bullets (or whatever else you'd like to call them) needs to be handled like player movement. A key-binding needs to be added for keyDown and keyUp, but doing it this way will fire a shot every frame the button is held down. You can leave it that way if you want, but I want a bit more control over the firing button. First, this gets added into the main.js file to detect that you're shooting:

var keyShoot = false;
var bulletWaitTime = 10; // Number of frames to wait
var bulletTimer = 0;        // Number of frames waited
function handleKeyDown(e) {
  if (e.keyCode == 32) {
    keyShoot = true;
  }
};

function handleKeyUp(e) {
  if (e.keyCode == 32) {
    keyShoot = false;
    bulletTimer = bulletWaitTime;
  }
};

function calculateInput() {
  // This bit keeps the player within the bounds of the screen
  var playerPos = parseInt($player.css('left').replace('px', ''));
  if (playerPos <= 30) {
    keyLeft = false;
    $player.stop().css({left: '30px'});
  } else if (playerPos >= ($gameArea.width() - $player.width() - 30)) {
    keyRight = false;
    $player.stop().css({left: $gameArea.width() - $player.width() - 30 + 'px'});
  }

  /* Left and Right movement functions go here! */

  if (keyShoot && bulletTimer >= bulletWaitTime) {
    createBullet();
    bulletTimer = 0;
  } else if (bulletTimer < bulletWaitTime) {
    bulletTimer++;
  }
};


When the spacebar is pressed down, a bullet element is created, bulletTimer is set to zero, and will tick back up towards bulletWaitTime each frame. Releasing the spacebar resets the timer, allowing another shot to be fired. You can also adjust the rate of fire by changing bulletWaitTime. Nice! Now that they're being detected properly, we can work on making it appear on screen, and moving. You can use this first bit for some basic css styling:

/* In main.css */
  .bullet {
box-sizing: border-box;
display: inline-block;
position: absolute;
margin: 0;
padding: 0;
width: 5px;
height: 20px;
background-color: gold;
border: 2px dashed white;
}

/* In main.js */
var bulletSpeed = 100;
var $player = '';
var $gameArea = '';

function createBullet() {
var bulletPos = {
left: $player.position().left + ($player.width() / 2),
top: $player.position().top - 30
};
$gameArea.append($('<div/>').addClass('bullet').css(bulletPos));
};

function moveBullets() {
$('.bullet').animate({
'top': '-=' + bulletSpeed
},{
duration: 100,
easing: 'linear'
}).dequeue();
};

function mainLoop() {
 calculateInput();
 moveBullets();
};

$(document).ready(function() {
$player = $('.player');
$gameArea = $('#game-area');
});


Bullets spawn in the #game-area relative to the player's position, centered just above the .player element. They animate the same way the player does, but instead of listening for input, moveBullets() gets called as part of the mainLoop() each frame. Before you can try it out, there's a little bit of cleanup to do. We can move the .player div out of the #player-box into the #game-area, and get rid of the #player-box completely. We can also consolidate the css, making a couple minor changes to set up for the next couple steps. This blog is more about function than design though, so I suggest checking the project folder for specifics. Now that things are cleaned up, you can try it out! The player moves and the bullets fire. This is also a good spot to try out different animation speeds, easing styles, different behaviours to make it your own. Once you've got it moving like you want, let's talk about collisions.

Said briefly, collisions are when 2 or more defined areas come in contact and/or overlap. For the purposes of this project, that means we need to know the boundaries of each enemy box and each bullet. It doesn't matter if the enemies overlap, but if a bullet shares any of the same space with an enemy, we need to know which ones are colliding and destroy them both. So where do we start? There's a lot of ways to do it, this is just one way I've come up with. It needs some fine tuning before it's perfect, but it works great for now.

function detectCollision() {
  $('.bullet').each(function() {
    var $bullet = $(this);
  var bulletPos = {
  top: $bullet.position().top,
  left: $bullet.position().left - 2,
  right: $bullet.position().left + $bullet.width() + 2,
  bottom: $bullet.position.top + $bullet.height()
  }; 
  var hit = false;

  $('.enemy').each(function() {
if (!hit) {
var $enemy = $(this);
var enemy = {
top: $enemy.position().top,
left: $enemy.position().left,
  right: $enemy.position().left + $enemy.width(),
  bottom: $enemy.position().top + $enemy.height()
};

  if (bulletPos.top <= enemy.bottom) {
if (bulletPos.right > enemy.left && bulletPos.left < enemy.right ||
bulletPos.left < enemy.right && bulletPos.right > enemy.left ) {
  hit = true;
  $bullet.remove();
  $enemy.remove();
  }
  }
  }
  });

  if (bulletPos.top < 0 - $bullet.height()) {
  $bullet.remove();
  }
  })
};

function mainLoop() {
  calculateInput();
  moveBullets();
  detectCollision();
};

Each frame, the positions of each bullet and each enemy are fetched and compared. By comparing opposite sides (bullet top to enemy bottom, etc) you can determine if 2 sides are overlapping, and call it a successful collision. The little bit at the end is another check to destroy bullets whenever they go offscreen. When you try this out, you'll see a new problem. Enemies are getting pushed to the left when destroyed. They need to be displayed absolutely, which means they need to be placed automatically. It also means we can get remove the ones we placed in the index.html, leaving 2 empty divs in a <section>. The css can be consolidated a bit more too, as seen in the project folder linked above.

Here's a simple enemy placement function. Based on the size of the #enemy-box and your .enemies, this will fills the area with as many enemies as it can, while maintaining even spacing between them. It only needs to be called once (for now) during the $document.ready section. Like the previous function, this is a rough draft for testing, and will be fine tuned later.

function propagateEnemies() {
  var $enemybox = $('#enemy-box');
var enemySize = 100 + (15 * 2); // Tile size, plus spacing on both sides

var x = Math.floor($enemybox.width() / enemySize);
var xSpacing = ($enemybox.width() % enemySize) / (x + 1);
var y = Math.floor($enemybox.height() / enemySize);
var ySpacing = ($enemybox.height() % enemySize) / (y + 1);

for (var j = 0; j < y; j++) {
for (var i = 0; i < x; i++) {
var position = {
'top': (j * enemySize) + 15 + (ySpacing * 2) + 'px',
'left': (i * enemySize) + 15 + (xSpacing * 2) + 'px'
};
$enemybox.append($('<div/>').addClass('enemy').css(position));
  }
  }
};

$(document).ready(function() {
window.addEventListener('keydown', handleKeyDown, true);
window.addEventListener('keyup', handleKeyUp, true);
$player = $('.player');
$gameArea = $('#game-area');
propagateEnemies();
setInterval(mainLoop, 1000 / fps);
});

That's a good spot to end on. I like how it's coming together so far. The next entry will be the introduction of the navbar, and  'levels' that can be loaded from html content.

10.27.2015

LMaGT Part 1 - Building a skeleton

After some reflection, I realize the dungeon crawler is going to be a much larger project that I had planned. I've come up with a lot of ideas, and it needs more time and attention than I can give it. A more pressing matter is a dire need to update my main portfolio. Click here to check it out.

I built it from scratch while I was still attending Codify Academy. At it's core, it has some cool things I'm happy to have implemented. Outside of that, it's severely lacking in actual content and direction. When I wrote the last entry, I had planned on using the Dungeon Crawler as an interactive portfolio piece. Since it's growing in scope (or at least potential), I've come up with another way to make an interactive portfolio game.

It's similar to Galaga or Space Invaders. The player controls a ship along the bottom of the screen, and shoots at stuff. In this case, the 'stuff' is the content of my portfolio. Individual letters, pictures, headlines; each separate element on the page can be destroyed. Each section can be loaded/reset by clicking the nav bar buttons. The main interface should also be optional for browser compatibility, so everything has to work outside of 'interactive' mode, and should be responsive in both 'standard' and 'interactive' modes.

If you want to follow along, here's the project folder that covers this post. It's a pretty barebones template I've cobbled together with the base elements of the game in place. Here's a quick look at the index.html:

<body>
<section id="game-area">
  <div id="enemy-box">
    <div class="enemy"></div> <div class="enemy"></div>

    <div class="enemy"></div> <div class="enemy"></div> <br/>
    <div class="enemy"></div> <div class="enemy"></div>

    <div class="enemy"></div> <div class="enemy"></div> <br/>
    <div class="enemy"></div> <div class="enemy"></div>

    <div class="enemy"></div> <div class="enemy"></div> <br/>
    <div class="enemy"></div> <div class="enemy"></div>

    <div class="enemy"></div> <div class="enemy"></div> <br/>
  </div> 

  <div id="player-box">
    <div class="player"></div>
  </div>
</section>
</body>
Not a whole lot here. Just a thing that moves and shoots, and some things to shoot at. There's not a lot to talk about in the main.css either. For now, the enemies and player are colored boxes, to be replaced later. The main.js has a lot going on though, so I'll break it into a few pieces. Going through it chronologically, it flows like this:

var fps = 30;  //Target frame-rate

$(document).ready(function() {
    window.addEventListener('keydown', handleKeyDown, true);
    window.addEventListener('keyup', handleKeyUp, true);
    setInterval(mainLoop, 1000 / fps);
});

function mainLoop() {
    calculateInput();
    detectCollision();
};
Still pretty straightforward. First, set up listeners to bind keys to movement, then use setInterval() to manage the main loop. Then inside the loop, player input gets applied before bullet collision is detected. I've got some ideas on how to handle collisions, but I'm saving that for the next entry. For now, lets take a look at the event handlers and calculateInput().

var keyLeft = false;
var keyRight = false;
var playerSpeed = 25;

function handleKeyDown(e) {
  if (e.keyCode == 37 || e.keyCode == 65) {
  keyLeft = true;
  }
  if (e.keyCode == 39 || e.keyCode == 68) {
  keyRight = true;
  }
};

function handleKeyUp(e) {
  if (e.keyCode == 37 || e.keyCode == 65) {
  keyLeft = false;
  }
  if (e.keyCode == 39 || e.keyCode == 68) {
  keyRight = false;
  }
};

function calculateInput() {
  // Move right
  if (keyRight && !keyLeft) {
  $('.player').animate({
  'left': '+=' + playerSpeed
  },{
  duration: 200,
  easing: 'easeOutQuint'
  }).dequeue();
  }
  // Move left
  if (keyLeft && !keyRight) {
  $('.player').animate({
  'left': '-=' + playerSpeed
  },{
  duration: 200,
  easing: 'easeOutQuint'
  }).dequeue();
  }
}
The handleKey functions listen for both WASD and arrow keys, but I've trimmed vertical movement. Later, I'll add a listener for firing bullets here. In calculateInput(), you can see how the player element moves by changing it's left position based on which key you press. The duration and easing give the movement a smoother feel. You can try out different timings and easings to find one that works for you.

The last call to deQueue() is important. When a key is down and the animation is called, it gets called each frame that the key is held down. At 30 frames per seconds, an average keystroke would send 4 animation calls. These animations stack (queue) until each call has completed, and the player element will keep moving long after you've stopped pressing the key. By dequeueing the animation, the 'next' frame doesn't have to wait to process, and your player will handle more like you'd expect.

In the next post, I'll get bullets and destructible enemies working, and start setting up the navbar,

8.13.2015

Let's make a game together.

I've recently started putting together an idea for a new game. I've also wanted to make time to do some writing, so why not do both? This is going to be the first part of a longer series about developing a browser game with Html5 and AngularJS. Most of it will serve as a way for me to keep my thoughts and ideas together and work out problems. We'll learn how to do this together.

First, here's a list of all the ideas I want to contain in this project:
  • It's a roguelike-ish dungeon crawler, run on JavaScript and drawn to the canvas. Player data can be stored on Firebase.
  • There will be multiplayer elements, but wont be a 'multiplayer game'. There will be a common lobby for all active players to trade and sell items, but players will be dungeon crawling alone.
    • I like the idea of possibly/randomly finding another player's corpse with their loot.
    • Maybe have 2 chat channels. One for the lobby, one for global chat. Or, at least add player-to-player messaging.
    • Put a shop in the lobby, but markup the prices. 
  • Dungeons are procedurally generated, increasing in size, difficulty, and reward with depth. There's a LOT of room for creativity here, in terms of adding things to be generated.
  • Loot is also randomly generated. All equipable items (weapons, armor, boots, helmet, accessories) can be randomly enchanted or cursed. There should also be sets of unidentified items.
  • Players will have some basic stats to manage, but once they die, they lose their stats, inventory, and level progress.
On one hand, it's kind of a lot of stuff to work out. On the other hand, most of it will be generated content, so it's more a matter of making a couple really good generators first, and tie them together with a good UI. Granted, that's still a huge oversimplification, but those are going to be what drive this game so it's a good place to start. In the next post, I'll start setting up the project and outlining the general flow and design of the game.

7.07.2015

Revival!

It's been far too long since I've posted an update to anything here, but I should start getting back into the habit.

So where has all the time gone? Work, mostly. All of this was just something I could mess with in my off-time, and when that dried up so did this. No further updates to Omni, or the Tower project, but I'm not completely empty handed.

After years of meandering, I'm finally making a push to change my career and make this stuff my primary focus. Last fall I found Codify Academy, attended, learned, and graduated earlier this year. Web development is a bit of a shift from game development, but I ended up with an affinity for JavaScript which can help me do both. As such, this blog is going to be more focused on general development, and whatever I happen to be working on.

And that's not all! Along with my github repo expanding, I also now have a portfolio and a working app called Conclave (currently in open beta). Conclave is my first big project since finishing the Academy. It's a social platform built for the Academy, so mentors, students, and alumni have a central place to talk, share code, help with questions, communicate, and collaborate. It's built on AngularJS and Firebase, and will be a common topic on here as I talk about the ins and outs of how it works (and how to make your own!).

Feelin' good to be back.

- r.h