iphone development blog

Monday, February 25, 2008 @ 11:43 AM

Part 10: That's All Folks!

iPhone Minds Developer's Journal - Crossword Puzzle
Well, our two week developer's feature has come to an end. I hope you all enjoyed the various discussions and behind-the-scene tips and tricks. Maybe we'll do this again in the future.

Without further ado, here's a link to the final game:


I've also embedded the game below (though it's more fun to play on the iPhone):


Here are some things to keep in mind while playing the game. If the page gets refreshed mid-game, you'll have to start over again. Try not to open new Safari windows when playing on an iPhone or iPod Touch. If you need to open other windows, return to the game window before exiting Safari or turning off your phone, otherwise the page will automatically refresh when you come back to the game after returning to Safari. Also, two clues are repeated in the puzzle. This is not a glitch. Get creative and think about what the two answers might be based on the size of the words you're working with.

UPDATE: It appears that the 1.1.4 iPhone and iPod Touch firmware update preserves the state of Safari "tabs/windows." Meaning, you can start the crossword game in one window and open another without having to worry about loosing your place in the game and starting all over again. I'd encourage everyone to download the 1.1.4 update.

Enjoy!

// Ryan Jennings

Labels: , , ,


MindComet at 11:43 AM - View Post | 5 comments




Sunday, February 24, 2008 @ 6:30 PM

Part 9: Gratuitous Eye-Candy

iPhone Minds Developer's Journal - Crossword Puzzle
Games just aren't interesting without snazzy graphics. But graphics need to be downloaded, and you want to limit as many unnecessary downloads as possible when developing applications for the iPhone.

Until now, our game required only two images. The background image, and the image border used for grid highlights. Together these assets weigh in at about 24.5K. We also embedded a number of images in Javascript, which beefed up that file. All together the images, HTML file (which includes CSS) and Javascript file amount to 50K. That's really good for a complete game and means that the game will load quickly if you need to access it using EDGE.

But of all the things that irk me in life, only one is intolerable. A shitty ending to a video game. I mean you spend 40+ hours of your life (many of which are extremely aggravating) glued to the game. Is it too much to ask for a slick 5+ minute movie ending? Something brilliant that wraps up everything at the end. It only seems appropriate. So, I think it's important to give the winner of our puzzle something nice to look at when they're done.

End Screen:
Okay, it's not much, but hey, this game is being developed for demonstration purposes. Still, I think it satisfies the "snazzy graphics" requirement. Keep in mind that by the time you see the curl, the grid will be filled with letters. I didn't want to give away all of the words so I left the grid empty.

In my "Preloading Commonly Used Game Assets" post I mentioned that it would not make sense to embed all images in Javascript using URI Kitchen. The curl PNG weighs 69K. That's more than the entire game weighed up until this point. So, is it worth forcing the user to wait for this image to download when they won't even see it until the end of the game? Sure it is. Because we're not going to "force them to wait."

Since this image isn't required until the very end of the game (and it would probably take anyone at least 15 mins to solve the puzzle), we're going to load the image behind the scenes while the user is playing the game. Here's how we'll do it:

First, we set the game up. This includes waiting for all of the assets (except the curl) to download. Once the HTML has been parsed, Javascript hides the iPhone address bar, reads the game's XML file and builds the game's grid. The game is now ready to be played.

At this point we execute the following Javascript:
setTimeout(function() {
var curl = new Image();
curl.src = 'curl.png';
curl.onload = function() {
var img = document.createElement('img');
img.id = 'curl';
img.src = curl.src;
document.getElementById('body').appendChild(img);
img.onclick = function() { resetBoard(across, down); };
};
}, 30000);

Again, the above script will not execute until the game has completely downloaded and is playable by the user. This script then waits 30 seconds before it downloads and sets up the curl image, giving the game more than enough time to prepare the asset before it's required.

For dramatic effect, we're going to fade the curl in when the user has completed the puzzle. There are a number of Javascript resources you can utilize to create fades. MooTools is a great option. But since we're not relying on MooTools to manage the styles or effects of any of the other elements that make up the game, it doesn't make sense to include the MooTools Javascript library (an extra download) only to access it for one effect. There's a simpler way.

The Mac software company Panic has, for a long time, been a leader in creating compelling, and shockingly easy to use, Web applications (they of course make brilliant Mac applications as well). On their site I found a small snippet of math which we can use to fade the curl.

Javascript:
function cubicOut(t, b, c, d) { return c*((t = t/d-1)*t*t+1)+b; }
If you use this code within your projects, please give credit to Panic.

Using the cubicOut function, we can do the following:
var curlanim = { time:0, begin:0, change:0.0, duration:0.0, timer:null };

function curlFade(start, end, duration) {
if (end == 1) document.getElementById('curl').style.visibility = 'visible';
if (curlanim.timer != null) { clearInterval(curlanim.timer); curlanim.timer = null; }
curlanim.time = 0;
curlanim.begin = start;
curlanim.change = end-start;
curlanim.duration = duration;
curlanim.timer = setInterval(function() { fadeAnimDo(end) }, 15);
}

function fadeAnimDo(e) {
if (curlanim.time > curlanim.duration) {
clearInterval(curlanim.timer);
curlanim.timer = null;
if (e == 0) document.getElementById('curl').style.visibility = 'hidden';
} else {
var alpha = cubicOut(curlanim.time, curlanim.begin, curlanim.change, curlanim.duration);
document.getElementById('curl').style.opacity = alpha;
curlanim.time++;
}
}

In the last post I mentioned that the matrix array keeps track of whether or not correct letters have been added to the 225 different spaces that make up the puzzle. If it is determined that the puzzle is filled with correct letters (using the matrix array), then the game has been won and we initiate the curl fade with the following code: curlFade(0, 1, 50).

And that's it! The tutorial portion of this feature is complete. Although we didn't post every bit of the game's Javascript, I hope we've explained enough to provide a good sense as to how everything was developed. Come back tomorrow for a link to the working game!

// Ryan Jennings

Labels: , , , ,


MindComet at 6:30 PM - View Post | 0 comments




Friday, February 22, 2008 @ 5:24 PM

Part 8: Maximizing Screen Real Estate

iPhone Minds Developer's Journal - Crossword Puzzle
In the previous post I included a new image of the game. Let me explain how the user is going to interact with the game grid.

Pressing on any square will active a selection (see image below) focusing on whatever horizontal (across) word includes that square. The actual square pressed will turn blue. Pressing anywhere on the now visible selection will rotate it 90° (using the blue square as a control point) to focus on the vertical (down) word. Pressing the selection a second time will deactivate it. You can also deactivate the selection at any time by pressing outside of the selection.

A clue will only be visible underneath the puzzle while a selection is activated. It will initially read something like this:

29 DOWN Starbucks order.
Press here to answer.

I decided early on that I didn't want to continually display an input field for answering. Rather, I only wanted to display it when the user was actually ready to answer.

Since iPhone applications are dynamic, we can make smart use of the screen real estate and only display certain UI elements when necessary. Here's how I implemented the "now-you-see-it-now-you-don't" input field:

HTML:
<span id="outer-answer"><input type="text" id="answer" value="" /><span style="text-decoration:underline">Press here to answer.</span></span>


CSS:
#outer-answer { position:relative; }
#answer {
width: 208px;
height: 20px;
border: none;
padding: 0;
margin: 0;
position: absolute;
opacity: 0;
text-align: center;
line-height: 20px;
margin-left: 56px;
font: bold 13px Georgia,serif;
}


Javascript:
document.getElementById('answer').onfocus = function() {
this.style.opacity = 1;
};

document.getElementById('answer').onblur = function() {
if (this.value != '') {
var letters = this.value.split('');
var correct = xmlhttp.responseXML.getElementsByTagName(node)[0].getAttribute('a').split('');
var numCorrect = 0;
for (var i = 0; i < selected.length; i++) {
if (letters[i]) {
if (letters[i].toUpperCase() == correct[i]) {
eval('ctx.drawImage(i'+letters[i].toLowerCase()+'b, '+((selected[i].id%15)*18)+', '+(Math.floor(selected[i].id/15)*18)+')');
matrix[selected[i].id] = 1;
numCorrect++;
} else {
matrix[selected[i].id] = 0;
}
}
}
if (numCorrect == correct.length) {
alert('Correct!');
clearSelection();
} else if (numCorrect == 0) {
alert('Incorrect, try again.');
document.getElementById('answer').value = '';
document.getElementById('answer').focus();
} else if (numCorrect != correct.length) {
alert('Only some letters are correct.');
clearSelection();
}
checkForWin();
}
};

The input field is actually always present (positioned on top of the "press here" text), but it starts off invisible. When you press to answer the field becomes active and the iPhone displays its keyboard. Press the keyboard "Done" button or anywhere on the grid to commit your answer.

So, that's how it's done. Before I wrap up this post, I'll explain some of the other things that are happening in the Javascript above.

Once the user clicks the iPhone's "Done" button we first check to make sure they have actually entered text. If they have, we create an array of the text and match it against an array of the letters that make up the correct answer to the clue. We check each letter one by one. If the letters match, we add the letter to the game grid. The matrix array keeps track of whether or not correct letters have been added to the 225 different spaces that make up the puzzle. Ultimately if all letters are correct, you've won the game.

// Ryan Jennings

Labels: , , , ,


MindComet at 5:24 PM - View Post | 0 comments




Thursday, February 21, 2008 @ 2:53 AM

Part 7: Taking Advantage of Web Kit CSS Styles

iPhone Minds Developer's Journal - Crossword Puzzle
One of the biggest benefits of designing Web applications specifically for the iPhone is that you have access to many Web Kit CSS properties that have not yet been supported in today's other popular browsers, including Firefox.

Things like:
  • Borders with rounded corners
  • Image borders
  • Box shadows
  • Multiple background images
If you've ever used Google's iUI, a user interface library for Safari development on the iPhone (originally developed by Joe Hewitt), you may be using image borders and not even be aware of it. In today's post I'll explain how we'll take advantage of image borders in our crossword puzzle game.

The buttons used in iUI all rely on image borders. Image borders make it easy to create scalable image-based buttons (with rounded corners and all types of other snazzy eye candy). Accomplishing something like this previously required 4+ nested elements (plus 4+ images) each responsible for the rendering a specific corner of the button. Now you need only one element and one image and two CSS attributes.

Here's the CSS:
.button {
border-width: 0 5px;
-webkit-border-image: url(toolButton.png) 0 5 0 5;
}



When using -webkit-border-image you first specify the image to use, then set the areas that will act as your borders. In the example above, we're only going to scale the asset horizontally, so we'll only need a border of 5 pixels on the left and right. The top and bottom do not require a border.
Click Me

For more information on image borders check out Apple's Safari CSS Reference: Supported CSS Properties.

In the crossword puzzle game we'll use an image border to highlight the currently selected squares in the puzzle grid. Here's what it will look like:

Using an image border is convienent because we can scale the border horizontally and vertically based on the length of the word we're working with. After entering my answer, in this case "CAL," the letters would appear in the highlighted area on the grid.

// Ryan Jennings

Labels: , , , , , , ,


MindComet at 2:53 AM - View Post | 0 comments




Tuesday, February 19, 2008 @ 8:09 PM

Part 6: Taming XML

iPhone Minds Developer's Journal - Crossword Puzzle
Though it's easy to get Javascript to read from an XML file, pulling out the appropriate data can sometimes be tricky. Here are some tips to help you tame the XML beast.

Refer to the code we posted yesterday. If the XMLHttpRequest loads without error, then we can use the xmlhttp variable to access the contents of the loaded XML file. To understand how to work with xmlhttp (or whatever you named the XMLHttpRequest instance), relate it to Javascript's document variable.

document.getElementsByName('menu') will return an array of the elements on your page that all share the name "menu."

xmlhttp.responseXML.getElementsByTagName('menu') will return an array of the "menu" XML nodes (tags).

Now, nodes can have both attributes and values. In the following example, "Burns or Byron" is the c attribute of the a1 node and "Timothy Parker" is the value of the editor node.

XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<editor>Timothy Parker</editor>
<across>
<a1 a="POET" c="Burns or Byron" n="1" cn="1" />
</across>

getElementsByTagName will always return an array. In the example above there is only one a1 node, therefore there's no reason to keep an array of a1 nodes. Using the code below, we can save to variable only the first returned node (since that's all that exists).
var a1 = xmlhttp.responseXML.getElementsByTagName('a1')[0];

Now, access the c attribute like this:
var c = a1.getAttribute('c');

Doing that is easy, but accessing node values is a little more confusing. Here's the code to access the editor node's value:
var editor = xmlhttp.responseXML.getElementsByTagName('editor')[0];
var editorValue = editor.firstChild.nodeValue;

The firstChild method gives you access to a node's first child. But the editor node does not appear to have any children. Well, it apparently (or not so apparently) does. When attempting to access the value of a node, imagine the value text as being a node itself... access the nodeValue of the "text node" and you'll get the result you're looking for. Or, in more simple terms, always use .firstChild.nodeValue to access the value of a node.

Another XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<across>
<a1 a="POET" c="Burns or Byron" n="1" cn="1" />
<a2 a="BLAB" c="Give everything away" n="6" cn="5" />
<a3 a="TALKS" c="Gives everything away" n="11" cn="9" />
<a4 a="ALSO" c="Too" n="16" cn="14" />
<a5 a="YOHO" c="Exclamation by Captain Jack Sparrow" n="21" cn="15" />
<a6 a="ELIOT" c="''A Cooking Egg'' writer" n="26" cn="16" />
<a7 a="LIAR" c="Ananias, for one" n="31" cn="17" />
<a8 a="PREY" c="Target in the wild" n="36" cn="18" />
<a9 a="ALONE" c="In isolation" n="41" cn="19" />
</across>

In the above example, all of the across child nodes are named differently, so it would be cumbersome to "getElementsByTagName" each one separately. Instead, take advantage of the childNodes collection to return an array of the nodes.
var across = xmlhttp.responseXML.getElementsByTagName('across')[0].childNodes;

There is more to learn about XMLHttpRequest, but by simply knowing how to use the getElementsByTagName, getAttribute, firstChild and nodeValue methods and the childNodes collection, you'll have an easy time accessing all types of data from XML documents.

Before I close this lengthy post, there is one more thing I'd like to mention with regards to accessing XML files. It appears that you can not use Javascript to access local XML files using XMLHttpRequest, you can only access files found on a domain. I am completely perplexed as to why this is and if anyone out there has knowledge of the reason, or knows a workaround, I'd love to hear from you.

// Ryan Jennings

Labels: , , , , ,


MindComet at 8:09 PM - View Post | 0 comments




@ 1:16 AM

Part 5: Importing Data From an XML File

iPhone Minds Developer's Journal - Crossword Puzzle
Most programs, including games, require data. More often than not this data must be updated regularly so as to sustain the usefulness of the application. This type of data is usually stored externally (out of the application's main code) since it makes very little sense to require the application's user to download a new version of the program every day in order to receive the latest and greatest data. Therefore, with respect to our game, it makes a great deal of sense to store the data for each crossword puzzle in an external XML file. This way puzzles can easily be loaded individually by referencing a different XML file.

To import data from an XML file, we use the XMLHttpRequest Javascript object as follows:

var url = 'xml/070814.xml';
var xmlhttp = new XMLHttpRequest();

if (xmlhttp != null) {
xmlhttp.onreadystatechange = stateChange;
xmlhttp.open('GET', url, true);
xmlhttp.send(null);
} else {
alert('Your browser does not support XMLHTTP.'); }
}

function stateChange() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
/*
A status of 200 means that the XML file was
loaded and parsed without error. At this point
you'll want to sort through all of the data so
that it can be used by your program. I'll give
you some tips on how to do this tomorrow.
*/
} else {
alert('Problem retrieving XML data.');
}
}
}

We'll talk a little more about reading from XML files tomorrow.

// Ryan Jennings

Labels: , , , , ,


MindComet at 1:16 AM - View Post | 0 comments




Thursday, February 14, 2008 @ 8:15 PM

Part 4: Preloading Commonly Used Game Assets

iPhone Minds Developer's Journal - Crossword Puzzle
Imagine playing The Legend of Zelda on your iPhone. Awesome, right? Well now imagine opening a treasure chest and finding this inside: . What the hell? Looks like the image of your treasure hasn't loaded yet! (Okay, that's actually the icon to represent that an image can't be found, but play along with me.) Similar things could happen in your game if your assets are not downloaded by the time they need to be. Fortunately, there's a trick to prevent this from happening.

If you've ever used Dreamweaver, you're probably familiar with the code that's used to preload images:

function simplePreload() {
var args = simplePreload.arguments;
document.imageArray = new Array(args.length);
for (var i = 0; i < args.length; i++) {


document.imageArray[i] = new Image;

document.imageArray[i].src = args[i];
}
}


Look at the highlighted line. In Javascript you can create Image objects. By doing so, you can store images in the DOMs memory and quickly access them later. But preloading images in this fashion still requires an HTTP call out to the image so that it can be downloaded. When designing games for the iPhone, we want to limit the number of HTTP requests as much as possible. There's actually a way to embed some of your images in your Javascript file so that there's no need to download them.

If you've ever tried to open a GIF or JPEG in a text editor you'll notice that the guts of the image are a bunch of letters, numbers and symbols. There's a free program online, called the URI Kitchen that will read an image and print out the letter/number/symbol/gremlin guts for you.

To use the program, go to URI Kitchen, upload a file and then copy the resulting data from the address bar. Here's the data I got when feeding the program the above question mark image:

data:image/gif,GIF89a%10%10%D5*%80%C1.%8A%C9%2C%86%C5!o%B4%2B%83%C3-%88%C7%2C%86%C6%2C%85%C5%90%AE%D3j%92%C3)%7F%C0%26y%BC%8E%BD%E0%8A%AA%D0%92%AF%D44%7F%BF%87%A7%CFm%94%C4%25v%BA%84%A5%CE%7D%A0%CB%80%A2%CC*%81%C2%8D%AC%D2(%7D%BE%D3%E3%F1z%9E%CAs%99%C7%23s%B7%D3%E4%F1p%96%C6)%7D%BF'%7C%BEv%9B%C8%E3%EF%F7.%89%C8%C6%DD%EE%8C%B8%DCS%95%CA%B9%D6%ECT%98%CCF%91%C8*%83%C2%8D%BB%DDR%95%CA%9B%C2%E16%86%C2)%80%C0F%92%CA%2B%84%C4%9C%C6%E3%F1%F7%FB%8D%BC%DE(%7D%BF%24t%B8%25w%BA%23r%B7'z%BD%2F%8C%CA%22p%B5%94%B1%D4h%90%C2%FF%FF%FF!%F9%04%2C%10%10%06%AE%40%9EpH%2C%F2%1C%BE%A4r%B9t%20%7C%BA%A8t%1A%F5!.%BE%80%D6%608x%0D%23%AD%EF%D2%F0%15%0A1%0B%83q%92%C1%2Cg_%03%E2%13%08T4%D1%2B%E5%13)%EC%3E%10%13%3E%04%04(%2B5.%3E3%18%85%3E%13%15%3E%0A%1F%1F%18-%24%269%94%3E%15%14%3E5%A35%20%2C%25%0B%0B%20%A3%3E%14%1A%3E9%B19%12%0F%0F%12%B29%3E%1A!%3E7%BE7%1C%1D%1D%1C%BF7%3E!%1B%3E6%CB6%3B%19%19%3B%CC6%3E%1B%1E%3E8%D88%03%DB%03%D98%3E%1E%11%3E%3B%E4%E5%E6%E4%3E%11%09L%ECJ%09%3D%F0%F1%F2%F3%3DA%3B

Pretty, huh? Now, take that data, and use it as follows:

var questionMark = new Image();
questionMark.src = 'data:image/gif,GIF89a%10%10%D5*%80%C1.%8A%C9%2C%86%C5!o%B4%2B%83%C3-%88%C7%2C%86%C6%2C%85%C5%90%AE%D3j%92%C3)%7F%C0%26y%BC%8E%BD%E0%8A%AA%D0%92%AF%D44%7F%BF%87%A7%CFm%94%C4%25v%BA%84%A5%CE%7D%A0%CB%80%A2%CC*%81%C2%8D%AC%D2(%7D%BE%D3%E3%F1z%9E%CAs%99%C7%23s%B7%D3%E4%F1p%96%C6)%7D%BF'%7C%BEv%9B%C8%E3%EF%F7.%89%C8%C6%DD%EE%8C%B8%DCS%95%CA%B9%D6%ECT%98%CCF%91%C8*%83%C2%8D%BB%DDR%95%CA%9B%C2%E16%86%C2)%80%C0F%92%CA%2B%84%C4%9C%C6%E3%F1%F7%FB%8D%BC%DE(%7D%BF%24t%B8%25w%BA%23r%B7'z%BD%2F%8C%CA%22p%B5%94%B1%D4h%90%C2%FF%FF%FF!%F9%04%2C%10%10%06%AE%40%9EpH%2C%F2%1C%BE%A4r%B9t%20%7C%BA%A8t%1A%F5!.%BE%80%D6%608x%0D%23%AD%EF%D2%F0%15%0A1%0B%83q%92%C1%2Cg_%03%E2%13%08T4%D1%2B%E5%13)%EC%3E%10%13%3E%04%04(%2B5.%3E3%18%85%3E%13%15%3E%0A%1F%1F%18-%24%269%94%3E%15%14%3E5%A35%20%2C%25%0B%0B%20%A3%3E%14%1A%3E9%B19%12%0F%0F%12%B29%3E%1A!%3E7%BE7%1C%1D%1D%1C%BF7%3E!%1B%3E6%CB6%3B%19%19%3B%CC6%3E%1B%1E%3E8%D88%03%DB%03%D98%3E%1E%11%3E%3B%E4%E5%E6%E4%3E%11%09L%ECJ%09%3D%F0%F1%F2%F3%3DA%3B';

I can now access questionMark anytime I want to load that image in my game. And I can draw the image to the canvas (at coordinate 100,50) like this:

ctx.drawImage(questionMark, 100, 50);

Now, you won't want to use this approach for every image in your game. You'll still have to elegantly handle load times for large images. But this approach works great for smaller, commonly used images. What are the most commonly used assets in a crossword puzzle? The numbers used to label the grid, and the letters that will spell out each word. And those are the exact images that we'll "preload" into Javascript using this method.

// Ryan Jennings

Labels: , , , , , ,


MindComet at 8:15 PM - View Post | 1 comments




Wednesday, February 13, 2008 @ 12:50 PM

Part 3: Jumping Into Code

iPhone Minds Developer's Journal - Crossword Puzzle
Okay, let's start programming this game. A couple of weeks ago... no wait, a little over a month ago... I posted about the canvas. The canvas is a really great way to draw to (or animate) a Web page. So, since the iPhone does not yet support Flash, utilizing the canvas makes a lot of sense.

Note: Yesterday's illustration stated that the grid takes up 271x271 pixels, yet here we say that the outer DIV that holds all of the inner squares is 270x270 pixels. This is because the inner squares do not include the outer stroke that surrounds the grid. The outer stroke (and all inner grid lines) are part of the background image. The inner squares have a 1px left margin to separate them from one another, so: (1+17)*15 = 270.
Since more qualified people have already written great tutorials on how to use the canvas, it may behoove you to read up a bit if you become confused with what follows.

Let's code the grid. There are two ways we can display it. We can use the canvas to draw the grid. Or we can create 225 17x17 pixel DIVs and float them all to the left within a 270x270 pixel parent DIV. The first option sounds easier, but we're actually going to do a little of both.

First we'll use the canvas to draw the representation of the grid. Here is some simplified code:

HTML
<div id="squares"></div>
<canvas id="crossword" width="269" height="269"></canvas>

Javascript
var ctx = document.getElementById('crossword').getContext('2d');
ctx.fillStyle = '#ffffff';
for (var i = 0; i < 225; i++) {
ctx.fillRect((i-Math.floor(i/15)*15)*18, Math.floor(i/15)*18, 17, 17);
}

The code above will simply draw out 225 white squares, positioned exactly where we want them on the grid. It does not yet determine which squares should be black (the regions of the grid that are not part of the puzzle). We're ultimately going to read from an XML file to understand which squares are white and which are black. We'll get to that later.

The user will be interacting with the grid in order to read and solve clues, so we also need a separate clickable region for each of the 225 inner squares. That's where the second option comes into play. Let's add that logic to the for loop:

Javascript

for (var i = 0; i < 225; i++) {
ctx.fillRect((i-Math.floor(i/15)*15)*18, Math.floor(i/15)*18, 17, 17);



var newSquare = document.createElement('DIV');
document.getElementById('squares').appendChild(newSquare);
newSquare.className = 'square';
newSquare.id = i;
newSquare.name = i;
newSquare.onclick = function() {
// code to execute onclick
};

}


We've given each "pressable" DIV an id and name so that "onclick" we can figure out which clues they belong to. We'll get to that later as well.

For today, that is all. We're going to start slow and ramp up towards the end. Tomorrow I'll provide a neat trick for embedding images into Javascript so that you don't have to wait for them to individually download. A great way to preload images. It's very cool.

// Ryan Jennings

Labels: , , , ,


MindComet at 12:50 PM - View Post | 0 comments




@ 2:08 AM

Part 2: Design Considerations

iPhone Minds Developer's Journal - Crossword Puzzle
Earlier today news of an imminent Flash upgrade from the iPhone was flooding the Internet, though no concrete dates were specified. When a Flash plug-in is released it will open the doors for a lot of game programmers who are unfamiliar with coding using Javascript and the HTML DOM. But for now, this is all that's available. So, let's continue with our tutorial...

When designing games for the iPhone keep in mind that although the screen's resolution is 320x480 pixels, some of the vertical pixels are lost to the status bar, address bar and lower toolbar... 64 pixels are lost, to be exact. So, if you want your entire game to display "above the fold," design it to be 320x416 pixels. (Above the fold is a reference web designers throw around a lot to appear cool. It refers to the topmost part of a Web page -- the area that is initially viewable. The content that shows up as you scroll down the page is "below the fold.")

iPhone Minds Developer's Journal - Crossword Puzzle
If you look at a printed crossword puzzle you'll notice that the game consists of two main parts: 1) the grid, and 2) the "across" and "down" clues. Both take up a lot of real estate. So how do we fit everything onto the iPhone screen?

First let's discuss the grid (or board, whatever you'd like to call it). The squares that make up the grid need to be small enough so that a 15x15 matrix will fit within the design. They also need to be large enough so that even a person with fat fingers -- *cough* Scott *cough -- can press a single square. After mocking up a number of grid sizes I realized that although I could theoretically size each individual square at 19x19 pixels and still fit all 225 (15 rows x 15 columns) on the screen, I didn't have much room for anything else. So I settled on 17x17. This means that we loose 271x271 pixels to the grid.

Since a title is required for each puzzle, very little room is left for the clues. It will be impossible to display them all at once, so instead let's display relevant clues when the user clicks on a particular row/column. We'll talk about how this is going to work later.

That's all for today. Coding goodies will come tomorrow.

// Ryan Jennings

Labels: , , , ,


MindComet at 2:08 AM - View Post | 0 comments




Monday, February 11, 2008 @ 3:47 PM

iPhone Minds Developer's Journal

iPhone Minds Developer's Journal - Crossword Puzzle
Though I don't frequent other iPhone development type blogs much, I will say that the ones I have checked out skimp on the information you really want. They'll cover mundane issues like how to hide Safari's address bar, or how to change your wallpaper, or how to create a web clip bookmark... you know... beginner's stuff. What? That's all that we've been posting? Uhhh... ummmm... well, I guess it's time to up the education.

Over the next two weeks we're going to walk you through the development of a complete and working game for the iPhone. Not a stupid "move-around-the-pieces-of-this-image-and-try-to-put-them-in-order" game. We're going to build a fully functional crossword puzzle game. We'll discuss the best way to approach design and usability, provide coding tips (as well as some source code), and suggest solutions to many of the obstacles you will face along the way. So, without further ado, here is Part 1 of our ten part series.

Part 1: Things to Keep In Mind


There are a couple of things to keep in mind when considering iPhone game development. The most important is that iPhone developers currently do not have access to many of the usability features that make the iPhone great -- things like pinch, expand, slide, drag, drop, etc. That may change when the SDK is announced later this month, but for now all we have is press (click).

Additionally, all of the applications must be built in Safari. Don't get me wrong, I for one am more than happy to use Safari as my development environment. After all, HTML/Javascript development is what I know. It's what I've spent the last 10 years working with. So I know how to do really amazing things with that medium. Still, Safari on the iPhone is less responsive than it is on your Mac or PC. For instance, there is a lag between when you press something on the page and when you can press something else on the page. The lag is only about a second, but it should be kept in mind when you begin thinking about the type of game that you want to create. Because of the lag, slower-paced games make more sense on the iPhone. Therefore developing a puzzle game for this tutorial was a no brainer. After all, there's not much need to race your way through a crossword puzzle.

Besides the fact that a crossword puzzle was a slower-paced game, this type of game also provided some interesting usability dilemmas, but we'll get into those tomorrow. We'll also discuss design considerations tomorrow. For now, take a look at the near-finished design below. Though I've started some of the development of this game, there is much yet to still do so things may change over the next two weeks.


I originally got the idea for this game after seeing the daily puzzle on USA Today's site, so it made sense to try and repurpose the online version of their game in an iPhone format. DO NOT get all of your game ideas from copyright sources. I'm simply doing this because though I want to code and design the game, I don't want to create each puzzle myself, so I'm going to use the XML feed that their Flash game uses in order to pull individual puzzles.

That's all for today. Feel free to email us at ask@iphoneminds.com with questions or comments regarding game development. Come back tomorrow when we'll discuss design considerations.

// Ryan Jennings

Labels: , , ,


MindComet at 3:47 PM - View Post | 0 comments




Tuesday, February 5, 2008 @ 5:51 PM

iPhone Web Clip Bookmark Icons



Since Apple announced custom Web Clips for your website everyone has been scrambling to write articles on how to do it even though almost everything you need to know is on Apple's own iPhone Dev Center site.

If you were paying attention, you noticed I said "almost". What no one has managed to do yet is tell you why your icon looks chopped and funky.

Well, after exhaustive testing, we've figured it out. The iPhone cuts off 1 pixel on the left and right and 3 pixels at the bottom of your icon. It also rounds the corners and overlays a "glossy" appearance that makes it difficult for you to truly showcase your amazing design skillz in Corel Draw. Many people have posted that they created icons with larger dimensions (128x128 or 158x158) to solve the problem. Don't bother. It's a larger download for users and now more difficult for you to figure out how your graphic will render. Apple says build them 57x57. So our advice is to build them 57x57 - or keep reading...

We've created a Web Clip Photoshop template for you to utilize to create your own Web Clip Bookmark Icon masterpiece. The graphic below shows you the opened file. We've included the overlay, rounded corners and guides to help you find the exact center of your icon.



Click here to download

In order to use the template, you'll need to be familiar with Photoshop layers. Make sure you turn off the "overlay" layer before exporting as a PNG. The corners of your icon should NOT be rounded. The iPhone takes care of that.

I realize you may have stumbled across this post trying to figure out how to actually get a web clip icon on your site, so let use save you an extra click and just post Apple's instructions here:


Create A Web Clip Bookmark Icon

iPhone and iPod touch allow a user to save a Web Clip bookmark to your site on their Home Screen.

To specify a bookmark icon for all pages of a web site, place a PNG image named "apple-touch-icon.png" at the root directory of your web server - similar to the "favicon.ico" for site icons.

To override the site bookmark icon on a specific web page, insert a <link> element similar to <link rel="apple-touch-icon" href="/customIcon.png"> within the element of the page.

The bookmark icon dimensions should be 57x57 pixels. If the icon is a different size it will be scaled and cropped to fit.

Safari will automatically composite the icon with the standard "glassy" overlay so it looks like a built-in iPhone or iPod application.



BTW... as of the writing of this post, we have not put up a decent web clip for this site. Cut us a little slack, we've been busy.

//scott

Labels: , ,


MindComet at 5:51 PM - View Post | 2 comments




Monday, December 17, 2007 @ 5:19 PM

Basic AJAX Scroll Animation w/YUI

Basic AJAX Scroll Animation

Going through someone else's code is an art, you have to get into the developers mind and discover what he or she was thinking when writing the piece. While scavenging the Internet for an AJAX scroll animation, similar to that of the iPhone, I was so disgusted by the code I saw that I just wrote my own.

Some key things I had to keep in mind while writing this javascript code were, "How can I dynamically make one object slide off the screen while a second object slides onto the screen?" And "How can I keep users from going psychotic with my slider?"

To make the code dynamic, that was the easy part, I created a function calling out to the Yahoo UI Library (an AJAX source library). But to restrict when they user could slide the objects, that was the tricky part.

The YUI Library has three animation events that would allow me to limit when the slider would, well, slide...
  • onComplete

  • onStart

  • onTween

It was actually rather interesting how these three events functioned together. Checking if the animation is active (ie. isAnimated();) proved to be a thought provoking process. The onStart function would always return false while checking isAnimated, because onStart does a check right when you start the animation, not yet animated but almost animated. The onComplete function did exactly the same thing while checking isAnimated, it's done being animated. While the onTween function checks for every single tween or frame, it always returns true.

You can download the following YUI files from the Yahoo Developer Site (http://developer.yahoo.com/yui/):

yui/build/yahoo/yahoo.js
yui/build/event/event.js
yui/build/dom/dom.js
yui/build/animation/animation-min.js

<script type="JavaScript">
var movethis,target,x,y,goodToGo=true;

var waitForIt = function() {
var onTheMove = this.isAnimated();
if (onTheMove == true) {
goodToGo=false;
} else {
goodToGo=true;
}
}

function slide(moveThis, x, y) {
if (goodToGo == true) {
moveThis = document.getElementById(moveThis);
if (x != null && y != null) {
var anim = new YAHOO.util.Motion(moveThis, {
points: { by: [x, y] } });
}
anim.onTween.subscribe(waitForIt);
anim.onComplete.subscribe(waitForIt);
anim.animate();
}
}
</script>


// Jay, aka W3prodigy

Labels: , , , ,


MindComet at 5:19 PM - View Post | 1 comments




Monday, November 26, 2007 @ 3:40 PM

Build it Yourself! - Add On...

How to build an iPhone site - part 1b

We haven't forgot those of you looking for information on horizontal, or landscape view specs for building their iPhone site or webapp.

You'll have to wait for a post that shows you how to build a site that accommodates both or utilizes different stylesheets. We're currently planning on posting that tutorial with an actual template for you to download and use. Coming Very Soon, we promise.

For now, if you prefer to build a site or application that is supposed to be utilized in landscape view, just copy the text below into your HTML - before the </head> tag and your ready to go.

<meta name="viewport" content="width=480; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">


The script for removing the URL bar below works in either orientation of the iPhone or iPod Touch.

Also, we've put together the specs diagram for this view as well.

iPhone horizontal website specs
* the alternate content dimensions are if you hide the URL text field bar using the code above.

Labels: , ,


MindComet at 3:40 PM - View Post | 1 comments




Wednesday, November 21, 2007 @ 6:00 PM

Build it Yourself!

How to build an iPhone Website or Webapp

There are a few first steps you need cover in your code for an iPhone specific website or application. These keep the screen from zooming out and set the presentation to the best possible for the user.

Make your site fill the entire screen - no zooming
by default, the iPhone Safari browser zooms a webpage out to 25% of actual size. There are times when you may want it to zoom out or in for various needs, but for a starter, we're just going to show you how to make it zoom to a 1-to-1 ratio. This means that if you build the site at 320 pixels wide, a user will view the site at full width on load - no interaction required. If this seems unclear, please scroll down to the post about wallpapers and begin with the second Step 1 listed.

So, what do you need to do? Just copy the text below into your HTML - before the </head> tag and your ready to go.

<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">


One thing to note, this only covers the site in the basic portrait view mode. If a user turns the phone sideways, it will not scale to fit. We'll cover how to do that in a another post.

Hide that unsightly URL field
Okay, it's not really unsightly, but when dealing with limited space, every pixel (or 60 pixels in this case) counts. This one is as simple as the previous code snippet. Again, just place this before the </head> tag and your ready to go. On load the browser bar will still appear, but once the page has loaded it will slide up like magic and make your friends want to buy you expensive gifts. Seriously.

<script type="application/x-javascript">
function hideAddressBar() { window.scrollTo(0, 1); }
</script>


These are just the very basics of developing for the iPhone. In the future you can look forward to full site templates you can download here that allow you to just drop in images and links and you're ready to go. We're good like that.

As one last helpful reference, we've supplied an image below that outlines the iPhone specs and dimensions when in browser mode.

iPhone Website Design Specs
* the alternate content dimensions are if you hide the URL text field bar using the code above.

If you've developed a site or application for the iPhone, let us know. We'd love to help you show it off. We also like to keep an eye on the competition. ;)

//scott

Labels: , , ,


MindComet at 6:00 PM - View Post | 0 comments




@ 2:03 PM

iPhone Wallpapers

How to use and create iPhone and iPod Touch Wallpapers

So the number one question asked of me by other iPhone owners is "how'd you get that fancy desktop?" I'm paraphrasing slightly, sue me. Based on the amount of times I've been asked that question, I thought it would be appropriate to get things rollin' on this blog by covering this very topic.

How to get a "fancy" wallpaper
Many people just take photos and then select the option to set photo as their wallpaper. That's ghetto. Yeah... I said it. Ghetto.

In order to do what all the cool kids are doing, you just need to follow a few quick and simple steps:

  1. Create a folder on your desktop (or wherever) and title it something that implies "wallpapers". "things on a wall" is acceptable.

  2. Find a fancy wallpaper online or create your own (instructions to follow).

  3. Save wallpaper to previously created folder.

  4. Open iTunes and plug in your iPhone/iPod Touch.

  5. Go to the "Photos" tab and select your newly created folder (illustrated below).

  6. Once the photos are sync'd up with your iPhone, open the photo gallery and select your image.

  7. Tap the icon in the bottom left of your photo and select "Use As Wallpaper".


iPhone wallpaper setup

You're done. If these instructions didn't work for you, then do the following:

  1. Turn off iPhone/iPod Touch.

  2. Place into original packaging or similar size box.

  3. Wrap in colorful paper.

  4. Give to someone else on Dec 25th because you don't deserve to use it.


Couple quick additional notes. You do not need to keep the photo on your iPhone/iPod Touch in order for it to stay in place as your wallpaper. Once you set it as a wallpaper, it's copied to some fantasy world inside the iPhone where we don't have to worry about it. If you replace a wallpaper with a new image, the old one is lost. The fantasy world only keeps track of one at a time.

Wallpaper Design
You'll find wallpapers for your iPhone and iPod Touch all over the internet. However, these are wallpapers that have simply been cut or designed to 320x480. "Why's this wrong" you might ask? I'm so glad you feel comfortable enough in our relationship to interupt already. Anyhow... the reason this is wrong is because the basic dimension of 320x480 doesn't take into account key elements that will cover up your wallpaper.

I've provided a graphic below to illustrate the various aspects and dimensions of the interface where the wallpaper is shown. The 2 biggest elements to be concerned with are the Date & Time and the Slider. Although your wallpaper is seen under them, you obviously don't want to put your personal tagline or company logo under these.

iPhone dimensions and specs

Now you're ready to create your own wallpapers. You can download a PSD template with guides for your wallpaper here. If you do not own and know how to use a decent graphics application such as Photoshop, please leave wallpaper design to the experts. Speaking of which...

Free iPhone / iPod Touch Wallpapers
I'm sure the first wallpaper below looks familiar. Ever since the first demonstration of the iPhone, it has been shown with the fish wallpaper. However, you may have noticed it's not on your phone. Many people have posted it on their sites since, so I felt compelled to do so here as well.

To download wallpapers from this site, just click the image to view the full size version and then right-click and select "Save as...". Then proceed with the first step 3 up above.


iPhone Wallpaper

iPhone Wallpaper

iPhone Wallpaper

iPhone Wallpaper

iPhone Wallpaper

iPhone Wallpaper


Well, that's it for now. This seems to be an awfully big post for something that only shows up on your iPhone / iPod Touch for a second. Oh well. Ryan is working on posting an intro to iPhone web design and coding later on today that will be far more insightful... hopefully.

//scott

Labels: , , ,


MindComet at 2:03 PM - View Post | 0 comments