1.26.2016

LMaGT 3 - HTML integration and better collisions

The holidays are over, and now it's time to get back to work. In the last entry, I made a simple collision detector function handle bullet/enemy collision. The first major change to replace propagateEnemies() with a system that generates text as many collidable pieces. During testing, I realized the current detectCollisions() function needs a bit of work. This is the second major change to cover in this post.

As always, if you want to follow along, here is the zipped project folder, there's also a live demo, and now featuring a Github repo! I've also edited the previous posts with this, so the commits will line up properly.

Since I want this project to work as a personal website and a playable game, I want to keep things simple for future updates. To that end, I want the website and the game to be two separate views, and have the game read from the content of the website. It looks a little like this:
<section id="game-area">
  <div id="enemy-box"></div>
  <div class="player"></div>
</section>

<section id="standard-view">
  <div class="slide">
    <h1 class="slide-header">Slide Title</h1>
    <p>Your text here! </p>
  </div>
</section>

In the css, #game-area has width and height set to 100%, and the #standard-view is set to display: none. The #standard-view can be styled however you want, but for now, collidable text is only pulled from the <p> tags in the slide, and only one <p> per slide. You can have as many slide divs as you want, and with each slide corresponding to a different page of the website. With all of that in mind, lets talk about the script.

To give it a rough outline, it works in two parts. The first part converts the <p> tags into individual letters and caches it (so slides can be loaded/reloaded later when needed). The second part puts each letter into a jQuery object and appends it to the #game-area.
var textFromPage = []; // Holds all text from the page as letters, separated by slide
var $tiles = []; // Holds all jQuery objects currently on screen

function parseText() {
  $('.slide p').each(function() {
    var temp = this.innerText.split('');

    for (var i = 0; i < temp.length; i++) {
      if (!(/\w/.test(temp[i]))) {
        temp[i - 1] += temp[i];
        temp.splice(i, 1);
        i--;
      }
    }
    textFromPage.push(temp);
  });
};

function loadLevel(slideNum) {
  $tiles = [];
  for (i = 0; i < textFromPage[slideNum].length; i++) {
  $('#enemy-box').append($('<p>' + textFromPage[slideNum][i] + '</p>').removeClass('hit marked'));
  }

  $('#enemy-box p').each(function() {
  $tiles.push($(this));
  });
};
Author's note: There IS an unintended side-effect to this. With each letter being observed separately, 'words' are conceptually ignored, and causes them to wrap around line-breaks funny. The letters make it to the screen just fine, but some more steps will be added later to better adjust final formatting.


Each letter gets read in and pushed into a temp array. Each temp array gets pushed into textFromPage, such that any letter on the page can be found by textFromPage[slideNumber][letterNumber]. The if statement uses a regexp to look for any whitespaces, and skip them. This effectively lumps the whitespace into the previous character to preserve kearning on the screen, and to prevent creating an 'invisible' tile (a collidable target that can't been seen to be shot at). When a level needs to be loaded, each letter gets a <p> tag in a jQuery object, gets appended to the screen, and then they all get saved to the $tiles array. $tiles comes in handy later for getting object positions, instead of having to iterate through every element for every collision check. There's also a mention of 'hit' and 'marked' classes; those are part of the collision detection, so more on those in a bit.

This is a good time to quickly bring up the css again. To make sure that spacing is handled properly, make sure you add white-space: pre; and display: inline-block; to the #enemy-box p selector. You can also take this time to decide on your own styling: font choice, size, color is all important, but is entirely up to you. The css needed to make this script work is minimal and I only want to touch on the bits that are necessary.

To use these new functions, remove propagateEnemies(), and call these two functions before starting the mainLoop(). In fact, there's enough stuff there now, it can be consolidated to a proper init().
function init() {
  window.addEventListener('keydown', handleKeyDown, true);
  window.addEventListener('keyup', handleKeyUp, true);
  $player = $('.player');
  $gameArea = $('#game-area');
  $enemybox = $('#enemy-box');
  parseText();
  loadLevel(0); // Load the first slide by default
}

$(document).ready(function() {
  init();
  setInterval(mainLoop, 1000 / fps);
});

Now when you load the page, all the text from the first slide is presented as collidable targets. This is the first step. The next step is getting the bullets to stop being laggy, which means better collision detection. The core of the original detectCollisions() is used here, but the rules have been refined and reordered to save time during iteration. A big part of this involves a bullet knowing what it's target is, instead of having to figure out what it's hitting.

When a bullet is created, it looks through the cached objects' positions, determines which ones are in front of it, and chooses the lowest objects on the screen as the targets for that bullet and appends it to the bullet element. You check the position of the bullet against the target position while moving it, and log it as a successful collision. Most of the work is done in a new function fetchTarget(), with a couple small additions to the other bullets functions.
function fetchTarget(bulletPos) {
  var hitList = [];
  var lowBound = 0;

  for (var i = $tiles.length - 1; i >= 0; i--) {
    var $this = $tiles[i];
    if (!$this.hasClass('marked') &&
      (($this.position().left < bulletPos.left + 11 && $this.position().left + $this.width() > bulletPos.left - 5)))  {
      if ($this.position().top + $this.height() >= lowBound) {
        hitList.push($this);
        lowBound = $this.position().top + $this.height();
        $this.addClass('marked');
      } else {
        break;
      }
    }
  };

  hitList.push(lowBound);
  return hitList.reverse();
}


function createBullet() {
  var bulletPos = { /* left/top positioning */ };
  var bulletTarget = fetchTarget(bulletPos);
  $('#game-area').append($('<div/>').addClass('bullet').css(bulletPos).data({ 'target': bulletTarget }));
};


function moveBullets() {
  $('.bullet').each(function() {
    $(this).animate({ /* animation settings */}).dequeue();

    var target = $(this).data('target');
    var bulletTop = $(this).position().top;

    // Successful bullet-tile collision
    if (bulletTop <= target[0]) {
      for (var i = 1; i < target.length; i++) {
        target[i].addClass('hit');
      }
      $(this).remove();
     }

    // Remove bullet when it goes off-screen
    if (bulletTop < 25) {
      $(this).remove();
    }
  });
};

The first way to save time is to start from the end of the $tiles list instead of the top. The 'marked' class denotes that particular element has already been targeted by another active bullet can be ignored. You can also skip any element that isn't within range of the bullet (16px total width here). After you've determined everything in range of your shot, you can ignore the elements above (behind) your target. The target elements get marked for collision and pushed to a list with the lowBound. The reverse() call is partly to compensate for scanning the tiles backwards, but it's also easier to call target[0] than target[target.length - 1] when we need to get lowBound. 

You'll notice the collided tile element isn't being removed, it gets a 'hit' class. In the css, hit is only set to opacity: 0. The collided tile stays on the screen to preserve spacing, the 'marked' class prevents it from being detected for further collisions, and the 'hit' class keeps it hidden from view.

As an aside, if you wanted to have different weapon styles, this is a good place to start, To make a laser, ignore lowBound and return everything in the left-to-right range. To make a small explosion, return all neighboring elements too. To make a bigger explosion, find return all elements in a defined radius around any target. These are all ideas for later though. In the immediate future, I want to clean up parseText() a bit to recognize words as whole objects, and put loadLevel(n) through some tests. Stay tuned!

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

4.25.2014

The calm after the storm.

It's been well over a year since the last update, which is just terrible. And for that year, a whole lot of 'not much' got accomplished with Omni. So what happened?

  • Unemployment, so all my free time went into job hunting.
  • New job, so free time got focused on that.
  • Marriage, which takes a LOT of planning.
  • Honeymoon planning, which is to be expected.
  • Holiday stuff, which is always a mess, regardless of planning.
  • Finally the year rolled over, and I found the time to actually work on the game.



I had also made a self-imposed rule that I wouldn't post here again until I had something to show for it. I've been working pretty solid for the last couple months, so here's what I have so far:

  • A menu skeleton, complete with functional buttons.
  • Re-Re-Re-Re-Optimized Unicycle, Bicycle, and Big-Wheel vehicle models
  • The start of a brand new Character Controller
  • A rigidbody version of the Unicycle, that is (mostly) self balancing and has some movement.
  • A github repository
  • A potential partner, as a friend of mine may be able to lend some time and help with the core coding.

So what then now? Hopefully, more frequent blog updates. Some about actual game progress, probably a few how-to posts with some code snippets, theory, I dunno. I plan on writing up a proper post in the next day or two about the movement module I cobbled together (Spoiler alert: it's a simple and common idea, but not exactly intuitive). 

In the mean time, here's a little something.
Unfortunately, I don't have a splash image, or even a logo.

1.19.2013

This woulda been part two.

Part Two to the previous entry (regarding the animation dilemma) is long overdue. In fact, it went long enough that the problem sort of worked itself out. Partly. There's still a matter of practical implementation (actually doing it and making it work), but so far my current method of problem solving is working out alright. I basically keep looking at it, reading books and articles, watching tutorial videos, scrapping and restarting, and then eventually everything starts working. Then I scrap it once more to have a good working version.

Since the last entry, I've redone the Unicycle 4 times and it'll probably need at least a couple more fixes to get it just right. I was working on doing a whole custom Controller, Motor, and Camera scripts but I got the stock scripts working pretty well. Unity upgraded from 3.5 to 4.x now, and it's given me a few new things to play with and learn. One of those new things is improved scripting, and it works right out of the box so far. I'll have to tamper with the camera controls, but the vehicles actually move pretty well so far.

One of the other nice things about the new Unity is it comes with Mecanim: an animation studio. I've not had a chance to try it out yet, but it looks pretty extensive. There's a good chance I'll use it instead of the stock Blender Animation studio, but who knows. Lately, I've been more concerned with getting the controls working at all to worry about the model animation.

Another nice thing happened, Adobe Photoshop CS2 is now free to download and use for everyone! The way I understand it, it's too old to install properly on 64bit systems so Adobe dropped support for it, I guess to dissuade people from buying it. Anyway, supposedly there's an easy fix to install it on 64bit systems, so now I get free Photoshop to use. I really wasn't liking Gimp.

Here's where I stand now. Actually seeing it laid out is sorta disappointing, but not disappointing enough to stop me.


  • Unicycle Model is complete. I fixed the Blender -> Unity conversion problem, redesigned the UVMaps so people can do custom textures easier. The Tire-Treads UVMap needs to be redone, but it's functional as-is. 
  • The Bicycle Model rough draft is done, but needs to be redone from the ground up. The shape is alright, but it needs to be made a LOT more efficent. Then UVMapping, then texturing. 
  • The controls actually work. The model faces the right direction and moves like it's supposed to. No animation is attached yet.
  • A very very simple test level is set up. The platforms are simple enough that they can be created, moved, retextured, and manipulated in Unity on demand. 
  • Music software is ready, but I'm still looking for better options. Still need to get a usb-to-midi cable and do some sample loops.
And that's pretty much it for now. The next thing to work on is getting the camera to move the right way, setting the speed/drag/friction settings for the vehicle, and expand the test level a little. Then I'll look into animation. After all that's squared away, I'll work on redoing the bike, getting it set up, and see about adding sound and/or music.

The next entry is going to be about fixing some of the Blender to Unity bugs I've run into, because it was damned hard to weed through the internet to find the answers I needed. They seem to be common issues, and I want to just lay out the answers very plainly for anyone else that's having them.