Jan 202012
 

I had a big plan for this week’s Megabite, but my demonstration wasn’t quite ready in time.  Fortunately though, it was on this very same topic, and it gives us all something else to look forward to next friday.

In this article, I’m going to answer the question that I’ve gotten a few times regarding Stellar: “How did you create the blocks”? The blocks in question are the ones within the “Skirmish Mode”, seen here:


Well, the blocks themselves were easy to create, but it’s the tunneling system and colorization script that really started bringing things together a bit more.  It wasn’t long ago that the colors were random and chaotic.

The script works at first in a similar manner to a roguelike – first you fill all the spaces, then you actually cut away the areas you want open.  In doing, you’ll save yourself a massive headache.  So with a script like the one below, what we’re doing is basically making a giant grid of thousands of tiny blocks:

for (var i =0; i < cuber; i++) {
 if (i > 0) {
 Status = "Generating " + (i).ToString() + " of " + cuber.ToString();
 }
 if (i == (cuber - 1)) {
 Status = "Manipulating Terrain Elements";
 }
 yield;
 transform.position.x = transform.position.x + cubeSize;
 for (var i2=0; i2 < cuber; i2++) {
 var eachCube = Instantiate (genCube, transform.position, Quaternion.identity);
 eachCube.renderer.material.color = Color(0.51,0.36,0.13,1);
 transform.position.z = transform.position.z + cubeSize;
 }
 transform.position.z = minZ;
 }

A few important things to note here:

  • I used “yield;” in the script so that the GUI could update and allow a user to not think that the game has frozen.  Generating a massive world can take a lot of time.
  • If you’re going to try this, make sure your camera is not looking at the generation in progress.  Cull away the rendering until after all is finished and you’ll save yourself much needed processing power.
  • I’ve rendered each cube as my standard “dirt” color.  The idea is to add in “veins” of slightly different colors to give the cubes a more natural look.
  • The cubes themselves are simply unscripted primitive cubes with a standard grey appearance by default.  They are very simple, and only contain a collider component.

Now, we want to set up a starting location for our tunneling system.  What we’re doing here is moving a gameObject around the cubes and shooting out a physics ray to select cubes that lay in front of it.  A ray is a single line, so we’re also going to add two additional lines to cut out a larger path.  In addition, we’re going to do this in short bursts of 100 meters and have the tunnel weave a bit randomly.  When the script detects cubes, it destroys them and we have a straight line of demolition.  The code for the tunnel detector looks something like this:

function KillFwd() {
 var hits = (Physics.RaycastAll (transform.position, transform.forward, 100));
   for (hit in hits) {
    if (hit.transform.tag == "genBlock"){
    Destroy(hit.transform.gameObject);
    }
   }
}

The code that executes the above script is:

function KillVein(startHere : Vector3) {
 Status = "Creating Routes & Generating Frustration";
 yield;
 for (x=0; x < 4; x++) {
 transform.position = startHere;
 transform.rotation = Quaternion.identity;
 transform.Rotate(Vector3.up * (90 * (x+1)));
 for (i=0; i < 100; i++) {
 KillFwd();
 transform.Translate(Vector3.right * 10);
 KillFwd();
 transform.Translate(Vector3.left * 20);
 KillFwd();
 transform.Translate(Vector3.right * 10);
 var move = Random.Range(0,90);
 var rot = Random.Range(-60,60);
 transform.Translate(Vector3.forward * move);
 transform.Rotate(Vector3.up * rot);
 }
 }
}

We do this in a few places, and what we end up with is the leftover “islands” that create the world.  When I lay down the enemy spawn points and bases, I have some additional code that clears the area first, but that’s pretty much the basics.

Colorization is done in a very similar way – we randomize more lines and instead of destroying them, this time we change the way they are displayed:

function StabColor(theColor : Color) {
 var hits = (Physics.RaycastAll (transform.position, transform.forward, 100));
 for (hit in hits) {
   if (hit.transform.tag == "genBlock"){
   hit.transform.renderer.material.color = theColor;
   }
 }
}

As you can see, it’s a very similar script.  The script that runs this function is actually a bit more complex than the tunneling though:

function AddColor() {
 var colorHere : Vector3;
 for (var i = 0; i < 3; i++) {
 colorHere.x = Random.Range(minX, maxX);
 colorHere.y = 0;
 colorHere.z = Random.Range(minZ, maxZ);
 Colorize(colorHere, Color(0.66,0.34,0,1));
 }
 for (i = 0; i < 3; i++) {
 colorHere.x = Random.Range(minX, maxX);
 colorHere.y = 0;
 colorHere.z = Random.Range(minZ, maxZ);
 Colorize(colorHere, Color(0.60,0.40,0,1));
 }
 for (i= 0; i < 15; i++) {
 colorHere.x = Random.Range(minX, maxX);
 colorHere.y = 0;
 colorHere.z = Random.Range(minZ, maxZ);
 ColorBomb(colorHere, Color(0.71,0.29,0,1));
 }
}
function ColorBomb(startHere : Vector3, theColor : Color) {
 var range : int = Random.Range(15, 45);
 var blocks : Collider[] = Physics.OverlapSphere(startHere, 100);
 for (each in blocks) {
 if (each.transform.tag == "genBlock") {
 each.transform.renderer.material.color = theColor;
 }
 }
}
function Colorize(startHere : Vector3, theColor : Color) {
 //print(startHere + " " + theColor);
 Status = "Creating Routes & Generating Frustration";
 yield;
 for (x=0; x < 4; x++) {
 transform.position = startHere;
 transform.rotation = Quaternion.identity;
 transform.Rotate(Vector3.up * (90 * (x+1)));
 for (i=0; i < 100; i++) {
 StabColor(theColor);
 transform.Translate(Vector3.right * 10);
 StabColor(theColor);
 transform.Translate(Vector3.left * 20);
 //StabColor(theColor);
 transform.Translate(Vector3.right * 10);
 var move = Random.Range(0,90);
 var rot = Random.Range(-60,60);
 transform.Translate(Vector3.forward * move);
 transform.Rotate(Vector3.up * rot);
 }
 }
}

The “ColorBomb” function has another really nifty tool – Physics.OverlapSphere.  With this handy unity function, we’re able to grab a “circle” of blocks and colorize them.  With this, I was able to add a bit more of a blotchy and natural look in addition to the veins of color.

I’m working on a separate project to show some of the integrated features of the Unity Terrain generator – hopefully it will be ready for next week’s Megabite.  Until then, if you have any questions about what you see here, feel free to drop a comment below!

Jan 132012
 

When I first began with Unity, I noticed a great deal of things in tutorials were coded within the Update() section of a script.  As you may already know, this portion of a script will execute once per frame.  Depending on the framerate of a user, this could be a good thing or a bad thing.  If anything, it’s unpredictable at best and taxing on a machine if you have a piece of complex code within.

The option also exists for FixedUpdate(), which is more predictable but can have a tendency to be just as taxing for complex code.  What do we do when we want to run something every second or so instead of every rendering frame or according to the physics timescale?  For that, we use the Invoke methods.

//This script will call our TestFunction() every 2 seconds
function Start() {
   InvokeRepeating("TestFunction", 2, 2);
} 

function TestFunction() {
print("Test function run at: " + Time.time);
}

In combination with CancelInvoke(), this method is far less taxing overall, especially if you’re using complex code.  As opposed to Update, if we run the code once per second and the game is running at 60fps, we’ve just saved our machine from running the code 59 extra times.  If you have some type of loop in the function itself or anything that would have drained resources, this can be a godsend.

Invoke() itself has a great functionality as well, and can be used in conjunction with things like CancelInvoke() or isInvoking().  Invoke() allows you to also set a delay on a function without the hassle of using yield in the first line of code, and it gives you more versatility overall with the possibility of stopping the code from executing if needed.  For example:

//This script does something like a megaman-type cannon effect.  The user has to hold down the space key for 3 seconds, uninterrupted by enemy fire.  If interrupted, the fire functions will cancel out.
private var chargedUp : boolean = false;
var chargeAnimation : Transform;
var gunTip : Transform;
function Update() {
  If (Input.GetKey("space") && !isInvoking("FireCannon") && !chargedUp) {
     ChargeGun();
     Invoke("FireCannon", 3);
  }
} 
function ChargeGun() {
     Instantiate(chargeAnimation, gunTip.transform.position, Quaternion.identity);
     Invoke("ReadyCharge", 3);
}
function ReadyCharge () {
chargedUp = true;
}
function FireCannon() {
     if (Input.GetKey("space") && chargedUp == true) {
       Fire();
     }
}
function OnCollisionEnter (other : Collider) {
     if (other.transform.tag == "enemyBullet") {
       if (isInvoking("FireCannon")) {
         CancelInvoke("FireCannon");
       }
       if (GameObject.FindGameObjectWithTag("chargeAnimation")) {
         var theAni : Transform = GameObject.FindGameObjectWithTag("chargeAnimation");
         Destroy(theAni.gameObject);
         CancelInvoke("ReadyCharge");
       }
     }
}

Because of the isInvoking() directive, the above script is responsive immediately but will only execute once if the conditions are met.  Obviously the Fire() function would reset chargedUp to false as well as actually launch the projectile, but the innovative nature of isInvoking() allowed us to also set the rate of refire unless we decided on extra delay between shots.

For things such as general movement, the Update section of script is safe enough to use.  The same can be said regarding camera movement or a few other common features.  In truth, if your project is small you may not even see the difference in performance for a PC/Mac or Web build because of the powerful processors found in most gaming machines.  Where you may see the most benefit in something like this is in mobile development or in large projects with a lot going on at once.  Even so, it’s best practice to try and write your code to not do anything unnecessary.  That way, additions to your game will have more available resources and you can do more at once.

With things available such as InvokeRepeating(), you might immediately be able to move code from Update() into its own function and set a delay of your choosing.  Even if you execute once every 0.1 seconds, you should immediately have a performance increase of that script section of at least 200% (at 30fps, which is low).  InvokeRepeating can be launched from the Start() of a script, or from another function if needed.

As a side note, simply running the command CancelInvoke() without adding the name of a function will actually cancel all invocations on the current script.  After learning these commands and really starting to work with them, I can safely say there is a lot of my old code that could be better revised.  Learning these as soon as possible will likely safe you a great deal of time and energy.  :)

Dec 162011
 

Working with Remote Procedure Calls – “RPC”s – is a bit confusing at first.  First, let me go into a bit of applicable context and make things more understandable.

An RPC is needed when we are going to execute a function on a remote machine while connected on a network.  They use what is called a “Network View” to accomplish this, and of course your game must be network-connected.  (There are plenty of different downloads via Unity and the Asset store directly that will get you that far to begin with).

The scripting behind an RPC works similar to a regular function, except it has a tag like this:

@RPC
function MyRPCFunction() {
iDidSomething = true;
}

To execute the function, you need to start it from the Network View component, which can be easily attached to any GameObject.  Via javascript, we do something like this:

networkView.RPC(“MyRPCFunction”, RPCMode.Server);

The RPCMode requirement is tell Unity who we are communicating with.  In this case, what would happen here is that the server for the game would get the “iDidSomething” variable changed, but nobody else would – including the original sender.  This feature is invaluable for manipulating data across a network.  When choosing a mode, we can choose:

  • All
  • AllBuffered
  • Others
  • OthersBuffered
  • Server

Now the trickiness lies in the contingency that we need to be careful to make sure everything flows.  Whatever object our script is attached to must have a Network View component, if we’re trying to change variables or execute things on another connection, we need to be certain that those things already exist on their system as well (If a server has a script that clients do not, for example.)

It can be frustrating at first, but eventually writing these functions becomes second nature.  Fortunately Unity’s development console will give you a list of RPC errors just like any other scripting issue.  For the most part, it will be fairly easy to pin down problems by running your editor as a server and a build as a client, and then vice-versa with your eye on the error readout.  In this way, you can see if the problem is client-side or server-side.

There are many different ways to set up the server/client relationship within Unity.  Because of a more recent project, most of my experience is with an Authoritative server setup, meaning that the server makes all the final calls – each client sends input to the server, and the server moves the player around on their own screen.  This choice was made to ultimately make the game “fair” for multiple players in the event that someone has a sub-par connection.   While I will show how this works within this article, I wanted to first touch on another subject that boggled me as I developed:

Network.Instantiate

There was a distinct lack of information out there on this subject, so I wanted to try and explain how this worked as it applied to me.  Being that my last project was my first networked project, there were a few things I needed to wrap my head around:

  • When we just use Instantiate on a server or client, nothing is networked automatically.  Thus, it can’t be seen on another machine unless we go the extra mile to be sure.
  • If we do an RPC call and make everybody Instantiate an object, that object is seen by all, but also controlled via each machine.  It STILL isn’t truly networked unless we go that extra mile.
  • Network.Instantiate will basically make a single object across ALL machines.  It does all the extra mile work for us, and it is controlled by the machine that spawns it.  If you are using this command, make sure it only is run by one machine (like the server).  If everybody runs the code at the same time, you will have a mess.

“The extra mile”

There are situations where we want to do something similar to Network.Instantiate, but perhaps not exactly.  So, let me be clear about what it actually does when we use it.  Network.Instantiate is powerful because it automatically serializes the object.  In essence, we do have a single object as far as the network is concerned, and when it is affected, it is affected network-wide.  In truth, the command is not as powerful as what it does behind the scenes – automatic serialization.

As we move forward, we will find a great many situations in which we want serialized data.  A health bar is a good example.  Let’s say for instance that an enemy spawns with 100 health.  That enemy is hit by an existing player, and now it has 50 health.  Now, a brand-new player enters the game.  Without serialized data, that new player would perceive that the enemy has full health if the data was not serialized correctly.

So, how do we serialize?  Well, we need a Network View to send/receive the data.  Whatever script contains the health variable can be dragged onto it so it is being “watched”.  Now, we need to tell unity what needs to be transferred over the net:

function OnSerializeNetworkView(stream : BitStream, info :NetworkMessageInfo) {
if (stream.isWriting) {
var healthC : int = currentHealth;
stream.Serialize(healthC);
} else {
var healthZ : int = 0;
stream.Serialize(healthZ);
currentHealth = healthZ;
} }

This section of code came directly from the unity reference for OnSerializeNetworkView.  This is another fancy built-in Unity function that comes in very handy for this exact purpose.  It determines automatically what to send/receive based on ownership, and it will be sure your health variable is the same throughout the network.

Back to the RPCs

After all that, we now understand a bit more about the inner-workings of a network.  Now, let’s put it into a basic, understandable script.  What we’ll do here is have an object that is instantiated into an auth-networked game (Not network.instantiated, just regularly.)  As such, both the player and the server see it on their own screens, and we want to take the player input and give it to the server to make our object shoot.

function Update() {

if (Network.isClient) {
if (Input.GetKey(“space”)) {
shootButton = true;
} else {
shootButton = false;
}
if (shootButton = true && Time.time > nextFire) {
nextFire = Time.time + 1.5;
networkView.RPC(“Fire”, RPCMode.Server);
}
}
}

@RPC
function Fire() {
var fired = Network.Instantiate(bullet, transform.position, Quaternion.identity,0);
fired.GetComponent(bulletControl).enabled = true;
}

So, in this case our player’s spacebar is being monitored for input and rate of fire.  On the server side, we have the server create the bullet and enable the script to control how it moves.  (That way seemed to simplify things much more for me when dealing with eliminating the bullet later in a very clean manner.)  We’ve minimized the amount of RPC data that will be sent for this scenario, which will save a bit of bandwidth as well (instead of sending an RPC at every keypress, we do it only when something can be fired.)  In addition, because the server created the bullet, the server can easily destroy it with Network.Destroy.

Bufferiffic

Lastly, I wanted to speak about the buffer.  Network.Instnatiate automatically does it, so it’s likely that it’s something you’ll want to consider in your design.  An RPC function to “All” or “Others” can also be “AllBuffered” or “OthersBuffered”.  When you buffer, you are keeping a queue of commands that will execute for players if they connect to the server after the commands are run.

This is very useful to consider, because the last thing you want is non-synchronized objects in-game because of bad timing.  RPCs can add things to the buffer, and things can also be manually removed from the buffer in the same manner.

So, before you have a client fire out an RPC call to All or Others, be sure to ask yourself if a player joining thereafter will need to know that this thing just happened.  If so, you will need to be sure your RPCMode is buffered.

 

Nov 222011
 

This entry isn’t so much an article as just a demonstration showing some of the models I’ve created for Unity.  Use the zoom/rotate controls after selecting an asset to see it in whatever view you would like.


Nov 182011
 

I have yet to have any real problems with the Unity asset importing features, but the reason I wanted to write this article was to explain some of the quirks that might make things easier for new Unity developers.  In this article, the two major things I want to cover are:

  • Importing textured 3d models into your game
  • Importing sound files and using them correctly
To begin, we’re going to start with 3d models.  Personally, I’ve been using Cheetah3d which is a very inexpensive and basic modeling program.  You can also use Blender as a no-cost alternative, and you really shouldn’t have major issues regardless of the file type you prefer.  While Cheetah3d defaults to a .jas type of file, I have now taken to saving as .fbx because it seems to be more common and universal for other modeling programs.  However, it’s important to note that Unity itself has no issue reading or importing any of the file types I’ve come across.
Obviously, the first thing we want to do here is design and texture a 3d model.  For this article, I chose to use a terra-cotta pouring vase, seen below within Cheetah3d.
When rendered within the 3d modeling program, we get something like this:
To understand the path ahead, what we want to end up with here are basically 2 files – the object itself which is a series of polygons, and the texture that wraps around those polygons.  If we were just to save it and import it now, we would end up with just a shape without any texture.  To get the textures into a usable file, we simply bake them into a .png picture file.
We don’t need to know how everything works exactly except that our program has a UV editor and an option to bake the texture for us.  I select the object, select “bake texture” from the menu, and open the UV editor to see this:
On the left, you can see the flat mapping of every polygon that can be affected by applied textures.  You’ll notice that on the right, I’ve chosen UV1 and set a decent texture resolution of 1024×1024.  When this picture is rendered, we get a result that looks like this:
Save your main file wherever you like, and be sure to save this image file along with it.  Within Unity, we can right-click on our project window to get the option to “Import new Asset” or “Open in Explorer (or Finder)”.  Whichever you choose, add your files into your game assets and you’ll see Unity work for a moment to calculate everything it needs.
 Once it’s within your project window, you can manipulate settings on the prefab that Unity creates.  Before anything else, be sure to drop your texture image file onto the little window within the material object.  With this file added, you can see in the preview window of the 3d model prefab exactly how everything is rendered.
In some cases, the object may look a bit funky at this point.  This is something that was slightly frustrating to me early on, but I found that Unity compensates through the inspector window and can correct the display.  Take a look at the pictures below and you’ll see the rendering difference when “Normals” are imported versus calculated for this model:

 

Once you have your object set up correctly, drag it from your project window into the scene and take a look – hopefully everything looks as you expect.  There are many more advanced ways to work with materials and texturizing, so if things are not exact, you can change your resolution or settings within your modeling program to your liking.

Another important thing to note is that if you work with the saved model file directly from your assets folder, you can change your model on the fly.  Simply edit and save to instantly update everything within unity.  (keep in mind you may need to bake your texture again if you make big changes).

While I’ve yet to work with the major professional modeling applications such as Maya or 3d Studio Max, I can’t imagine they would be much tougher than what we’ve done here with Cheetah.  I look forward to working with those programs when I have the chance and perhaps writing a more specific tutorial if the process is different.

On the next page, we’ll work with importing and utilizing sound files within our project.

Oct 282011
 

Like the rest of the Megabite series, I will be instructing by using Unity3d, which has proved itself to me time and again over the past few projects.  Most recently, I was able to use the trial of Unity Android to get myself published onto the Android Market, and though in my case the game was nearly finished for its format as a web-based game, I merely had to switch platforms within the settings and alter a few lines of code to add things such as accelerometer controls.

In this article, I will be talking about the basics of going from “New Project” to “Hey check out the app I just published”.

To begin, you will need a trial copy of Unity3d Android at the minimum, which can be found here.  The licensed copies of the software or the Pro version are $400 or $1500 dollars respectively (which may seem steep until you get an idea of the features and differences), but a trial is enough to get yourself started and even get a working game ready for publication.  If you are on a trial copy, you will see “trial copy” watermarked in small text at the bottom-right portion of the finished product, but the software isn’t limited in any other respect that I’ve come across so far.

You should download and install the Android SDK as well, and update it with the packages you are aiming to target.  While this tutorial will not cover the SDK inner-workings, Google has a great deal of pre-existing documentation on the basics if you choose to use it.  For our purposes, we don’t need to spend much more time than to install it.

Getting your game together

If you have existing code, you may see some errors pop up if you were lazy in the way you declared variables (like I was).  if you have a script that contains something like:

var myVariable;

You will likely see a warning that forces you to declare what type of variable you are creating.  Since you should already know what it is you’re trying to DO with this variable, this isn’t a big issue.  You simply define it as a string, integer, float, game object, or whatever you need.

var myVariable : String;

For me, this was simply a wake-up call that let me know I should be more specific.  Not to mention, it’s a good habit to develop anyway and it makes your code more readable when you go back to decipher work from earlier.  Suffice it to say that Android development was just a slight bit pickier in how things are read, but you can see it wasn’t a deal-breaker.

If this is your first time using Unity to develop, I suggest reading through earlier posts in this series to get a basic understanding of Unity development as a whole before you dive into the deep end.  In fact, we’re going to import the project that is still standing from #2 in the series and use that project to develop our first APK file.

We’ll start by switching the platform over to Android, as you can see on the left (File -> Build Settings). Once you’ve selected it, you simply click the button beneath to begin the automatic process.  Unity converts whatever it needs to behind-the-scenes while you wait.

Once the process is complete, you will see if there are any errors that might need to be handled.  In the case of the Megabite project, there were not – so we can move on.

Because we are now developing for a different resolution and such, you will notice that your “game” tab has some new view switching options.  Obviously Android has many different hardware setups, so you should keep these in mind as you test your game in the player.  Sometimes seeing what the game looks like in a different aspect will remind you about hiccups that may have occurred in your calculations or display setup.

What we’re going to do here is a landscape setup, so you will want to open your player settings to choose these options.  Go to Edit -> Project Settings -> Player, and you will see the different options available in the new Android tab.

While you’re in here, you can alter things such as your icon (will display on the Android device home screen or within the applications menu), the Product (game) name, and your company name.  Under Resolution and Presentation, I will choose “Landscape Left”, which seems the most compatible and common landscape option.

Fair warning: I did attempt to change some of the settings within “Other Settings” when I first published GIRType, and simply ended up crashing my phone.  I would suggest you stick to the default options unless you know specifically what you are choosing, but you should be sure to change these boxes when applicable:

For your first go-round, the Identifier is the important option.  The version number and version code will need to be raised for each publication to the market (or else google will give you an error), but to test things locally you can leave them unchanged for now.

Lastly, the publishing settings.  While the terminology here might be new or seemingly complicated, you really just need to create 2 passwords here.  Check the box for “Create new Keystore” and then browse – Unity will create a file and save it to the system.  Once done, you need to create a password for this keystone and confirm it.

The pull-down box for the “Alias” will have an option to create a new key, which will allow you to put in more information.  Here you will create a second password and re-enter it on the publication tab when finished.

Once all these basic things are accomplished, you will immediately be able to build the project into an APK file.  Open the build settings once more (be sure your scene is in the scene list.  If it isn’t, open your scene and hit “add current” and you’ll see it listed) and hit the Build button.  You’ll be prompted during the process to select your base folder for the Android SDK if Unity doesn’t automatically find it, but you can move on once selected.

Follow me onto the next page and we’ll get that file up and running.

Sep 302011
 

I don’t make any claim toward mastery of computer programming or game creation, but with the rise of indie games there seems to be much more of a question as to how one can create the game of their dreams – their very own indie game.  For this tutorial, I will be covering some basic concepts that were involved in creating this work in progress: G.I. R.type.



Available in Android Market
If the game isn’t showing for you, go ahead and grab the Unity web player – you’ll need it if you plan on playing any future titles here in the foreseeable future and you’ll need the (free) Unity 3 full download if you plan on using any of my information to create a game of your own.

Why Unity?

Fair question.  With the array of different tools out there, theres a few reasons why I chose Unity to move forward with.

  • It’s absolutely free for standalone PC/Mac games or Web games
  • Unity is capable of publishing to platforms such as Xbox, PS3, Android, and iPhone as well.
  • Unity utilizes javascript and C# in conjunction with many functions and shortcuts already included
  • There is a large community of helpful designers to answer questions – many of which are just a google away
  • Fully capable of both 3d and 2d games.  You can create a puzzle, platformer, FPS or Driving game to name a few, and do so without a great deal of prior programming knowledge.

To clarify a point early on: programming is not for everyone.  It is frustrating and tedious at times, and sometimes a misplaced character can keep an entire project from running properly.  That said, if you have a dream of getting your game out there and you see yourself making progress, the feeling can be exhilirating.

If you understand the warning here and wish to dig deeper, follow me onto page 2. :)