December 21, 2009

Aggro Detection with Javascript

So you want to make some games? Sure it sounds easy, but until you dive into it you’ll never notice all the little things that go into the backend of it that make or break your game. While this isn’t as big a deal in current browser-based games (they’re either muds, flash or static maps) things are changing in the browser. Everything is getting faster, and things like javascript based animation is actually feasible! So what is an aspiring game developer to do except buckle in and start programming their games?

Once you begin researching you’ll notice that while a lot of browser-based text games take into account Collision detection, they seem to be missing Aggro detection. Aggro stands for either aggravation or aggression and is essentially a way to tell if a monster will attack you or not.. As you’ve probably noticed some monsters tend to rush at you from across the map, while others seem to completely ignore you until you attack them. This is Aggro.

One of the easiest ways to detect aggro is to simply add a variable to your monsters to signify “sight”. This sight variable will dictate how far away you need to be before a monster will attack you. Set this to 100 to have a monster rush at you from across your map or set it to 0 so that it will never attack unless you attack first.

Detection aggro is as simple as following two formula’s. The first will be to figure out the distance between you and the monster and the second will simply be a quick check to see if you fall within the monsters aggro radius. To start off, you need to grab the jQuery javascript framework. While you CAN do this from scratch, jQuery lets you do it a lot easier, plus you can probably add a bit more jazz to your current game if you’re not already using it. If you’re unsure of how jQuery works, take a look at our quick intro to jQuery or just check out the jQuery documentation. Now just cobble together a quick webpage and include the jQuery library. I’m using the minified version 1.3.2 of jQuery, so if you’re not using the same one, make sure you replace that line.

<html>
<head>
	<title>Collision Detection</title>
	<style type="text/css">
	table#grid{
		border-style: solid; 
		border-width: 0px 1px 1px 0px; 
		border-color: #CCC; 
		padding: 0px;
	}
	table#grid tr th{
		border-style: solid; 
		border-width: 1px 0px 0px 1px; 
		border-color: #CCC; 
		text-align: center; 
		color: #333; 
		font-size: 0.7em; 
		font-weight: bold; 
		font-family: "Arial";
		background-color: #EDEDED;
	}
	table#grid tr th.run{
		width: 12px;
	}
	table#grid tr td{
		width: 50px; 
		height: 50px; 
		border-style: solid; 
		border-width: 1px 0px 0px 1px; 
		border-color: #CCC; 
		text-align: center; 
		color: #E0E0E0; 
		font-size: 0.7em; 
		font-weight: normal; 
		font-family: "Arial";
	}
	table#grid tr td:hover{
		color: #333;
		background-color: #F3F3F3;	
	}
	.player{
		background-color: #333; 
	}
	.monsterA{
		color: #333; 
		background-color: #AA0000;
	}
	div#debug{
		border-style: solid; 
		border-color: #333; 
		border-width: 1px; 
		float: right; 
		border-color: #EDEDED;
		text-align: left; 
		color: #333; 
		font-size: 0.9em; 
		font-weight: normal; 
		font-family: "Arial";
		width: 300px; 
		padding: 10px;
	}
	.monsterN{
		background-color: #00AA00;
	}
	</style>
	<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
</head>
<body>
 
</body>
</html>

You’ll notice a bunch of CSS in there, but you can ignore that. It’s pretty straight forward and really it’s just meant to pretty things up a little. Now we’re going to add a quick grid so that you can get an easy visual representation of where you are and where your monsters are and if they will aggro or not. You can copy and paste the following line a few times, and change the y coordinate (x,y) to match which row you’re on.

<tr>
	<td>0,1</td><td>1,1</td><td>2,1</td><td>3,1</td><td>4,1</td><td>5,1</td><td>6,1</td><td>7,1</td><td>8,1</td><td>9,1</td>
</tr>

Finally, I’ve just added a “debug” section, so you can get a textual representation of what’s going on. It’s just a div placed above the grid.

<div id="debug">
	<b>Debug</b><br><br>
</div>

The Code
Now for the code that will give us a visual representation of our player and our monsters. First off we are going to initiate a few variables that we will be using throughout our script. They’re pretty self explanatory.

var playerPlaced = false; 
var monster = []; 
var player = [];

Now we’re going to set it up so that when we click a square it will check if our player is already placed or not. If he is NOT placed, then we are going to place him and update our “debug” section.

$('#grid tr td').click(function(){
	var distance;
});
if(!playerPlaced){
	player['loc'] = $(this).html().split(','); 
	player['sight'] = 0; 
	$(this).html('@'); 
	$(this).addClass('player'); 
	playerPlaced = true; 
	$('#debug').append('Player: '+player['loc']+'<br>');
}

Notice that we’ve set player['sight'] to 0. Why? Simply because your player doesn’t have an aggro radius. He may or may not attack at your discretion, so it’s irrelevant. A monster however, will ALWAYS attack if you get within its aggro radius (assuming no other modifiers).

The next bit will just place our monsters if there is already a player on the field. When we do, however, we are going to call some functions that will take care of calculating distance and checking if the monster will aggro. We’ll go over those first, since they’re more complicated.

Distance
To calculate the distance of two points we are simply going to take the x,y coordinates of both our monster and player and perform some math on them. The formula that we will be using is actually the distance formula between two points (and you wondered when you would ever use it…) and something that’s been drilled into us in every math class. The formula itself is a basically sqrt(sq(x1-x2)+sq(y1-y2)) so translating that to our game code it will look more like

sqrt(sq(player[x]-monster[x])+sq(player[y]-monster[y]))

The squaring and square rooting would take us a lot more work, if JavaScript didn’t already have those built in! Our actual distance formula in JavaScript (after being placed in a function) will look like this:

function getDistance(from,to){
	distance = Math.floor(Math.sqrt(Math.pow((from[0]-to[0]),2) + Math.pow((from[1]-to[1]),2))); 
	return distance; 
}

Intersection
Basically intersection in our scenario will take a look at the player and see if he falls within the aggro radius of the monster. It’s a very simple check that just goes if (distance < aggroRad) then it will aggro.

function willAggro(aggro,distance){
	if(distance > aggro){
		return false; 
	}
	else{
		return true;
	}
}

Monster Insertion
Finally, we’re going to do our check to see if a monster will aggro a player or not depending on its distance to the player.

monster['loc'] = $(this).html().split(','); 
monster['sight'] = 1; 
$(this).html('X');
 
distance = getDistance(player['loc'],monster['loc']); 
if(willAggro(monster['sight'],distance)){
	$(this).addClass('monsterA');
}
else{
	$(this).addClass('monsterN');
}
$('#debug').append('<b>Monster:</b> '+monster['loc']); 
$('#debug').append(' <b>Distance:</b> '+distance+''); 
$('#debug').append(' <b>Aggro:</b> '+willAggro(monster['sight'],distance)+'<br>');

And that’s all there is to it. If you run this page and take a look, you’ll notice that clicking a grid will place your player and clicking anywhere else will cause the monsters to appear in either green (if too far away to aggro) or red (if it will aggro). This is also noted in the debug section!

Here is our completed javascript section

$(document).ready(function(){
	var playerPlaced = false; 
	var monster = []; 
	var player = [];
 
	$('#grid tr td').click(function(){
		var distance;
		if(!playerPlaced){
			player['loc'] = $(this).html().split(','); 
			player['sight'] = 0; 
			$(this).html('@'); 
			$(this).addClass('player'); 
			playerPlaced = true; 
			$('#debug').append('Player: '+player['loc']+'<br>');
		}
		else{
			monster['loc'] = $(this).html().split(','); 
			monster['sight'] = 1; 
			$(this).html('X');
 
			distance = getDistance(player['loc'],monster['loc']); 
			if(willAggro(monster['sight'],distance)){
				$(this).addClass('monsterA');
			}
			else{
				$(this).addClass('monsterN');
			}
			$('#debug').append('<b>Monster:</b> '+monster['loc']); 
			$('#debug').append(' <b>Distance:</b> '+distance+''); 
			$('#debug').append(' <b>Aggro:</b> '+willAggro(monster['sight'],distance)+'<br>'); 
		}
	}); 
}); 
 
 
function getDistance(from,to){
	distance = Math.floor(Math.sqrt(Math.pow((from[0]-to[0]),2) + Math.pow((from[1]-to[1]),2))); 
	return distance; 
}	
 
function willAggro(aggro,distance){
	if(distance > aggro){
		return false; 
	}
	else{
		return true;
	}
}

This is a very simple way to check aggro and the reason I placed it in its own function was incase you needed to modify it in any way (for example if a monster was under 10 levels its aggro would be 0 always). 

November 10, 2009

Simple Website Instant Messenger – Client-side Functionality

This is part two of the Simple Website Instant Messenger (SWIM for short, I love it when acronyms just work themselves out) and will cover adding some client-side functionality to our system. For part 1, click here.

Client-side functionality essentially translates itself to “using JavaScript to do some funcy (eh? get it? Funcy? Functional?) things”. It will allow us to add a level of interaction that users are familiar with and that they come to expect from applications. We want them to have the ability to minimize or maximize the windows, as well as close them. We also want to do all of this without page refreshes. Since actions like this are on a per-session basis, we don’t need to have any server-side code to handle it. We can let the browsers do all the work for us.

For this section we’ll be making some heavy use of jQuery and some rather complex DOM manipulation. I’ve also opted to use the long-hand version of everything instead of shorthand. This is so that you guys will be able to read the code and such. I’ve opted to use the production version of jQuery, but if you’re just getting started, you may as well download the development version. The bonus of this is that if you’re just learning you can easily pop in to the jQuery code and see how things are done. As well, if you are using an advanced IDE (such as Dreamweaver) you’ll get full code hinting. You can do this by heading to this website and downloading the appropriate version. I also recommend that you bookmark the documentation for jQuery (http://docs.jquery.com) as you’ll end up using it a lot.

Setup jQuery in whatever folder you’ve stored the HTML/CSS file from the previous tutorial (setup is actually just copy/paste). You’ll then need to link the script in your HTML by adding the following line of code to your HTML file in the head section of the code

<script src="jquery.min.js" type="text/javascript"></script>

Note the src attribute contains the name of the jquery file that I am using. You may have to edit it so that the name of the file matches the name of the file you downloaded.

Directly below that you can add this line as well

<script src="im.js" type="text/javascript"></script>

That line of code will link to our not-yet-created javascript file which will hold all the client-side functionality for our IM script. Once that’s done you can pop open your editor and get started with the actual jQuery code. However, before we continue with the tutorial note that this is NOT a jQuery primer. This tutorial is expecting you to have some knowledge of jQuery and javascript syntax. If you do not, please read this jQuery primer tutorial first before continuing.

The first thing we are going to do is ensure that our “information” window has a toggle-state. Instead of using jQuery’s built in toggle() method, I’ve decided to go with a simple boolean check.

$(document).ready(function){
var windowOpen = false;
$('.info').click(function(){
			if(windowOpen){
				$('.more').fadeOut('fast');
				$(this).children('span').children('img').attr('src','/icon/bullet_arrow_up.png');
				windowOpen = false;
			}
			else{
				$('.more').fadeIn('fast');
				$(this).children('span').children('img').attr('src','/icon/bullet_arrow_down.png');
				windowOpen = true;
			}
	});
});

Basically, this will apply the “onclick” event to our main window launching button. When clicked it will check if the window is already open and depending on the value of windowOpen, it will close/open it accordingly.

This next section of code will handle opening the chat windows. Since we want to make sure that only windows that AREN’T opened can be opened (redundant? yes, but completely necessary). This will ensure that if a window is already open, it won’t open it again.

	$('.contacts span').live('click',function(){
		var id = $(this).attr('id'); 
		var name = $(this).attr('name'); 
		var found = false; 
		for(i in openWindows){
			if(openWindows[i] == id+name){
				found = true;
			}
		}
 
		if(!found){
			$('.im-bar').prepend('<span class="task" id="'+id+'"><div class="convo"><img src="/icon/cancel.png" id="close-win" alt="Cancel"><span class="system">You have entered a conversation with '+name+'</span><textarea name="message" id="message"></textarea></div><div class="win" name="'+name+'">'+name+'</div>');
			$('#'+id).children('.convo').css('display','inline');
			openWindows[openWindows.length] = id+name;
			$('#'+id).children('.win').css('padding-top','50px');
			convoOpen = true;
			var offset = (openWindows.length*50)+(openWindows.length*15);
			$('#'+id).children('.convo').css('right',offset+'px');
			$('#'+id).children().children('textarea').focus();
		}
 
		$('.more').fadeOut('fast');
		$('.info').children('span').children('img').attr('src','/icon/bullet_arrow_up.png');
		windowOpen = false;
	});

The code itself remains fairly straightforward albeit long. First it does some variable assignment and then checks the openWindows array to see if it can find this particular window. If it can’t, it simply continues with the code. First it creates the new window and places it at the front of the open-window list. Then it just adds it to the openWindows array and does a bit more manipulation to create a text-box and align it properly with the current tab. It also opens the window and focuses on the text area. Finally it fades out the main information window and resets the image and boolean var.

Something to note here: Since we want openWindows and convoOpen to be accessible to ALL our functions, we can place it next to the “openWindow” declaration at the top of the script. It would look like this:

var openWindows = new Array(); 
var convoOpen = false;

This next bit will just pop open the window if you click on the tab, and minimize it again if you click again.

$('.task .win').live('click',function(){
		if(convoOpen){
			$(this).prev().css('display','none'); 
			$(this).css('padding-top','5px'); 
			convoOpen = false;
		}
		else{
			$(this).prev('.convo').css('display','block'); 
			$(this).css('padding-top','50px');
			convoOpen = true;
		}		   
	});

We’re nearing the end of our code. This block of code will handle the “Close” button on a conversation window. If it is clicked, the window is removed from the tab bar and the openWindows array and the remaining positions are re-calculated.

	$('.task .convo #close-win').live('click',function(){
		var id = $(this).parent().parent().attr('id');
		var name = $(this).parent().next().attr('name');
		for(i in openWindows){
			if(openWindows[i] == id+name){
				openWindows.splice(i,1);
				break;
			}
		}
		$(this).parent().parent().remove(); 
		convoOpen = false;
		var i = openWindows.length;
		$('.im-bar').children().children('.convo').each(function(){
			var offset = (i*50)+(i*15);
			$(this).css('right',(offset)+'px');
			i--;
		});	
	});

This last block of code will take any text in our textarea and submit it to the server when you press enter. The AJAX portion of this tutorial will be following, so in the mean-time everything else is showed here. The message is sent to the server, and then it is also automatically placed into the conversation window. We also strip out all tags from the code to ensure that nothing breaks our layout or is able to get through.

$('.task .convo #message').live('keyup',function(e){
		if(e.which == 13){
			data = $(this).val();
			data = data.replace(/<\/?[^>]+>/gi, '');
			// Send message
			$(this).val('');
			$(this).prev().addClass('text');
			if($(this).prev().hasClass('system')){
				$(this).prev().removeClass('system');
				$(this).prev().text('');
			}
			if(data != '' && data != '\n'){
				$(this).prev().append('<p><b>'+yourName+':</b> '+data+'</p>');
				$(this).prev().attr({ scrollTop: $(this).prev().attr("scrollHeight") });
			}
		}
	});

That is the basics of our Client-Side functionality to our Simple Website Instant Messenger system. The next installment will cover adding the AJAX function and modifying this last section sightly to reflect it.

November 5, 2009

jQuery Primer – An Intro to Client-side Functionality

If you’re interested in web development, you’ve doubtlessly come across jQuery and wondered about how you could utilize it in your own projects. jQuery looks a little daunting at first, but hopefully by the end of this little introduction to jQuery you’ll have a much firmer grasp on it and be able to take your projects to the next level.

What is it?
jquery1If you go by their website jQuery touts itself as being able to do damn near everything. And in truth, it does. It simplifies otherwise tedious tasks and allows you to add huge amounts of functionality to your website without much code. What it IS, however, is a Framework that provides you the ability to perform these tedious tasks in a way that’s simple and concise while achieving cross-browser support. This means that you can write your code quickly while knowing that it will work exactly how you expect it to in every browser. This cuts down on the amount of development time that you spend. Another bonus is the way that jQuery naming conventions make sense. Things like adding onclick handlers are as simple as adding .click(); to a selector (more on this later). This means that the learning curve is very shallow allowing you to literally just jump right now.

Getting started
Your first step will be to download jQuery. To do that, just head over to their website (http://www.jquery.com) and you can download it. It will be up to you whether you want to download the production or development versions. Both are the same, it’s just that the production code has been minified so it results in a much smaller file size. If you are just starting out however, I’d recommend going with the development version. If you have a full fledged IDE then your code hinting will come into play and you’ll be able to see descriptions of what the arguments are and such. However, even if you don’t, the development code will definitely be something you’ll want to peruse through in your spare time. You can learn a lot from looking at other peoples code. Once you have it downloaded, you can use it by simply including the following:

<script type="text/javascript" src="path/to/jquery.js"></script>

I’ve intentionally left the “src” attribute pointing to a random file as you will have to change it to point to wherever your jQuery file is.

First Steps
When using jQuery you have to remember that you’ll be spending a lot of time manipulating the DOM. In order for this to occur, you’ll have to wait till the DOM is loaded. However, because of where your script files are generally located, they may load before the DOM has completely finished. If this happens then your scripts will just run in to a lot of errors. jQuery comes with a built in way to check that the DOM is ready for use. All your jQuery scripts should run only AFTER the DOM is ready. In a separate script tag, you can add the following

$(document).ready(function(){ })

jQuery makes use of anonymous functions quite a bit, so you may want to make a note of that. You don’t NEED to have a function name in JavaScript. $ is a global variable created by jQuery as an accessor to all of its methods. This means that all your scripts in jQuery will utilize $ to perform it’s functions. $ will accept multiple types of arguments. You can either provide an ID, a Class or even an html tag for jQuery to work its magic on. I recommend you read up on the documentation for $ as it is one thing that you will definitely need to understand to perform complex manipulations.

You can go ahead and add

<a href="#">Link</a>

to the body section of your code. This is so that we have something to work with in jQuery.

Going back to our anonymous function, you can change it so that it looks like this

$(document).ready(function(){
$('a').click(function(){ alert('Works!'); }); 
}

Because of how powerful jQuery is, that will actually apply itself to EVERY anchor tag in the DOM. You can pick and choose which ones by applying numerous selectors. There are so many involved that that itself is the topic of another discussion. However, if you are looking to work on a particular id the code would be

$('#id')

and if you were working on a class you could use

$('.class')

Keep in mind that jQuery will always work on EVERYTHING that matches the selector that you have provided.

jQuery is also chainable. Because of how the code is written, you can keep tacking on different functions. For example, if we look through the effects section of the documentation you’ll see things like slideDown() and fadeIn(). These effects can be chained together to create multiple tiers of animations or events.

My recommendation is that you definitely check out the full documentation and spend a lot of time playing around with it. There will be a few more jQuery tutorials popping up on the website as I start preparing the gradual jump from no jQuery experience to enough experience to write the client-side functionality of our SWIM!