Last Good Quote: Son's are the seasoning on our lives. - Someone on Facebook

Monday, April 26

CSS: Tabbed Menu

Free Code. This comes directly from this site http://www.webcredible.co.uk/user-friendly-resources/css/css-round-corners.shtml



Creates rounded menus.

You will need the following four images:
  • - Right Tab
  • Left Tab

  • Right Tab Hover

  • Left Tab Hover



  • Code

    <html>

    <style>
    #navigation a
    {
    color: #000;
    background: #ffa20c url(left-tab.gif) left top no-repeat;
    text-decoration: none;
    padding-left: 10px
    }

    #navigation a span
    {
    background: url(right-tab.gif) right top no-repeat;
    padding-right: 10px
    }

    #navigation a, #navigation a span
    {
    display: block;
    float: left
    }

    /* Hide from IE5-Mac \*/
    #navigation a, #navigation a span
    {
    float: none
    }
    /* End hide */

    #navigation a:hover
    {
    color: #fff;
    background: #781351 url(left-tab-hover.gif) left top no-repeat;
    padding-left: 10px
    }

    #navigation a:hover span
    {
    background: url(right-tab-hover.gif) right top no-repeat;
    padding-right: 10px
    }

    #navigation ul
    {
    list-style: none;
    padding: 0;
    margin: 0
    }

    #navigation li
    {
    list-style: none;
    float: left;
    margin: 0;
    }
    </style>



    <body>
    <h1>Tabbed Menu Test</h1>

    <ul id="navigation">
    <li><a href="#"><span>Home</span></a></li>
    <li><a href="#"><span>Services</span></a></li>
    <li><a href="#"><span>Take a tour</span></a></li>
    <li><a href="#"><span>About us</span></a></li>
    <li><a href="#"><span>Contact us</span></a></li>
    </ul>

    </body>
    </html>

    Friday, April 23

    Javascript - 2D Map Library

    This article is a wrap up of a series of articles in which I walk through building a simple 2d top down map library. Below are the articles that build this library:
    For those of you that want the code, scroll past the explanations, it's at the bottom.

    Style.css.
    While testing and playing I usually will include the style in one page, for the ease of editing, but it's always a good idea to separate the style sheet from the main body of code. This allows an artist to come along and play with the look and feel of the site without screwing with your brilliantly created code.

    Map.js.
    All the general re-usable functions for mapping. If you ever finish coding a function and think, man I'll never have to touch this again, its so perfect and I'll use it all the time! First, slap yourself, cause we all know it's going to have a bug in it at some time. Second realize that the function in question, is prime material to go into a library of sorts. The functions in map.js are generic and can be kept from game to game as you build different things. At least that is my hope.

    TileClicked()
    I left tile clicked in the main body of the page because the actions you might want to take when a tile is clicked will change from game to game. Thus it is not library material.

    DestReached()
    This is a new function that was not discussed in other articles. Once a unit is done moving to it's destination of location, this function is called. I figured game builders might want to change this function depending on the game.

    Play()
    This is the heart of your game, if you want enemies to run around randomly, here is where you put that stuff. This function will call itself every few milliseconds, the more code in this function the slower your game is going to run.

    ObjectsCollided()
    This function is called when ever two units combine. The unit that moved last and did the collision is passed along with the id of each unit that is involved in the collision. You'll want to customize this function.


    The Body Tag
    The body tag calls the buildMap and addUnit function. I'd expect that you will want to change that.

    Div Elements
    There are two divs in the html one to hold the map and another to hold the score. You can move those around, but they must be somewhere in the page for the library to work.

    Map.js Function List

    BuildMap(width, height)
    Builds a map of width and height. It will also add walls to all edges of the map, so units don't wonder off it. Comment out the "addBorders()" line if you don't want this.

    AddBorders()
    Ummm, it adds borders...nuff said

    addWall(x,y)
    Turns tile x,y into a wall. Sets tile's flag = -1, sets the background color

    addUnit(unitID, x, y)
    Adds a unit with that ID to the map at tile location x, y. Multiple units can be added. If unitID is 1000 it is given the css class "unit". Otherwise the class is "enemy"

    setWalkableFlag(x, y, flag)
    Sets tile x, y "flag" variable to the flag passed.

    jumpUnit(unitID, tileDestX, tileDestY)
    Moves a unit directly to that tile, no animation at all...unit just jumps to location.

    move(unitID, dir)
    Move a unit one tile in the direction specified. Valid directions are N, E, S, W. This is an animated move (one pixel at a time). This uses the moveUnit() function.

    moveUnit(unitID, tileDestX, tileDestY)
    Moves a unit to the tile passed in. If at anytime the unit hits a none walkable tile, the unit will move back to last position. (no route finding). Calls DestReached() when completed.

    isWalkable(x,y)
    Returns "Y" or "N" if pixel x, y is walkable

    isWalkableTile(x,y)
    Returns "Y" or "N" if tile x, y is walkable

    getTop(obj)
    Return the top pixel (y) of the object. Could use some optimization

    getLeft(obj)
    Return the left pixel (x) of the object. Could use some optimization

    translateTileX(x), translateTileY(y)
    return the tileX that pixel x is located within

    getTileX(unitID), getTileY(unitID)
    return the tile x and tile y that the unit is located on

    Here be code!

    Style Sheet (style.css)


    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .food {
    background:black;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }



    Map.js (the library)



    var originX = 0;
    var originY = 0;
    var tileSize = 16;
    var mapHeight = 0;
    var mapWidth =0;
    var score=0;
    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 40;


    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("flag","0");
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }

    addBorders();



    }

    function addBorders()
    {
    for(x=0;x<mapWidth;x++)
    {
    addWall(x, 0);
    addWall(x, mapHeight-1);
    }


    for(y=0;y<mapHeight;y++)
    {
    addWall(0, y);
    addWall(mapWidth-1,y);
    }
    }

    function addWall(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.setAttribute("flag","-1");
    tile.style.background = "#777"
    }


    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);

    setWalkableFlag(x, y, unitID);

    document.getElementById("divMap").appendChild(d);
    }

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    }

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }



    function move(unitID, dir)
    {
    x = getTileX(unitID)
    y = getTileY(unitID)

    if(dir == "N")
    {
    y--;
    }

    if(dir == "E")
    {
    x++;
    }

    if(dir == "S")
    {
    y++;
    }

    if(dir == "W")
    {
    x--;
    }


    if(isWalkableTile(x, y) == "Y")
    {
    moveUnit(unitID, x, y);
    }


    }


    function moveUnit(unitID, tileDestX, tileDestY)
    {

    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    alert("no tile found");
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    if(isWalkable(newX, newY) == "Y" && isWalkable(newX+tileSize-2, newY + tileSize-2) == "Y")
    {
    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }
    else
    {
    destReached(unitID, destX, destY);
    }
    }

    }

    function isWalkable(x,y)
    {
    var tile = document.getElementById("tile_" + translateTileX(x) + "_" + translateTileY(y))
    if(tile.getAttribute("flag") >= 0) return "Y";


    return "N";
    }


    function isWalkableTile(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(tile.getAttribute("flag") >= 0) return "Y";


    return "N";
    }

    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = Math.floor(getLeft(unit)/tileSize - originX);
    y = Math.floor(getTop(unit)/tileSize - originY);

    return y;
    }




    Basic HTML page (index.html)


    <html>
    <link type="text/css" href="style.css"rel="stylesheet" />
    <script language=javascript src="map.js"></script>

    <script language=javascript>
    function tileClicked(x,y)
    {
    //called whenever a tile is clicked
    moveUnit(1000, x, y);
    }

    function destReached(unitID, x, y)
    {
    //called when ever the unit is done moving to a spot

    }


    function play()
    {
    //once called, this will loop forever, checking the game state and other things
    setTimeout("play()",playSpeed);
    }


    function objectsCollided(unit, unitID, enemyID)
    {
    //called whenever two objects collide

    }

    </script>

    <body onload="buildMap(10,10); addUnit(1000,4,4);">

    <div id="divMap"></div>

    <div id="score" style="font-family:verdana;size:14px; background:green;position:absolute;top:5px;left:350px; width:100px height:15px;border:5px solid green;">
    Score: 0
    </div>


    </body>
    </html>

    Thursday, April 22

    Stock Guessing - Programatically

    I've always been interested in stock analytics. Mostly because I have this un-reasonable dream that if I can program a stock "machine" which will trade stocks for me and make me richer than my wildest dreams.

    *Watches as you fall on the floor laughing*

    So now that you are done laughing....I started a blog about a year ago on creating such a program. If your interested it's here. I don't think I'm going to post to it anymore and it..."wonders"...a bit.

    Last night, I fired up the old program I wrote and tinkered with it for a little while. Tormenting myself with that dream I mentioned. To my surprise, I found something interesting...

    The program is setup to run once a day, pick a few stocks and then invest $1,000 into it. It then examines it's buys every day and sells the stock. There is a lot of other stuff going on, but that's the important part.

    If I had $10,000 to invest over the past 60 days and I had done ever trade and sell recommended by the program. I'd have gained $3,679.02.

    I'd have done 33 trades, assuming a $10 commission on each buy and sell, the commissions would cost me $660.

    The program churns through around 3,500 stocks (NYSE and NASDAQ) looking for good trades. It's a lucky day when it finds two picks in a given day.

    For those who are interesting..here are the last 10 or so picks it spit out.




    I'm at a loss on how to further test the program to ensure this is not some coincidence. Any suggestions?

    Monday, April 5

    Code: Simple 2D Javascript Map - Part 6

    This code will create a map grid, when you click the grid the green box will move to the tile you clicked. Red boxes will "fall" from the top down at random intervals. If the green box hits a red box, the red box disappears and starts to fall again. Think of it as a catching game.

    This was built to demonstrate one form of collision detection.In my mind there are two ways you can do collision detection. The first method is what most systems and algorithms perform:
    • Calculate the new position of the unit
    • Cycle through all other units to see if one of them occupies the new position
    • If no one is occupying then move the unit

    The second method is to make tiles as walkable as a unit steps on the tile. Then rather then cycling through all the units, you just check the tile the unit is moving to.

    • Calculate the new position of the unit
    • Check to see if that tile is occupied
    • If no one is occupying then move the unit

    The second method means that it "cost" more to move a unit. However it cost less than cycling through all units, every time there is any movement on the screen.

    How did I implement? First, I had to edit the buildMap() function so that each tile has a flag for who is standing on it. So I added the following line inside my buildMap() loops:

    d.setAttribute("flag","0");


    I then needed a function to set and unset this flag. If you un comment out a few lines, it will change the background of the tile, so that you can see which tiles get marked as unwalkable.

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    /*
    if(flag > 0)
    {
    tmpTile.style.background = "yellow";
    }
    else
    {
    tmpTile.style.background = "#BBB";
    }
    */
    }


    I also need the unit to "remember" what tile they are on (currTile) and what tile they were on (priorTile). To do this, I add the following code to the addUnit() function:


    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);
    setWalkableFlag(x, y, unitID);


    Again, I needed a few "wrapper" functions. One translates a pixel point X to a tile X. To keep things clean I changed the getTile function to use the new translate function. Again, there is plenty of room to optimize.


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }


    Now we get to the meat and potatoes of coding. The move unit function is where all the collision detection takes place. Reading through the logic of this function reads as follows:
    1. Grab the unit object and the destination tile object
    2. If the destination tile does not exist, stop
    3. Move 1 pixel closer to the destination
    4. Get the tile located at the center of the new position. (Center of the unit)
    5. If that tile is not flagged or if it is flagged as this unit
    6. Move the unit to the new location
    7. Set the priorTile and currentTile values
    8. Flag the new current tile as walkable
    9. Flag the old prior tile as unwalkable
    10. If we still have not found our destination, repeat in 5 milliseconds
    11. If there is a collision (back at #5), call the collide function



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++; if(y > destY) newY--;
    if(y < destY) newY++; checkX = newX + tileSize/2; checkY = newY + tileSize/2; var newTile = document.getElementById("tile_" + translateTileX(checkX) + "_" + translateTileY(checkY)) if(newTile.getAttribute("flag") == "0" || newTile.getAttribute("flag") == unitID) { unit.style.left = newX; unit.style.top = newY; unit.setAttribute("currTileX",getTileX(unitID)); unit.setAttribute("currTileY",getTileY(unitID)); if(unit.getAttribute("priorTileY") != unit.getAttribute("currTileY") || unit.getAttribute("priorTileX") != unit.getAttribute("currTileX") ) { // //mark the current tile as UNwalkable // setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), unitID); // //mark the old tile as WALKABLE // setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0"); } if(newX != destX || newY != destY) { setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5); } } else { // //we collided //snap back to last tile // objectsCollided(unit, unitID, newTile.getAttribute("flag")); } }


    In our game, if the user/player manages to collide with an enemy we will pop the enemy back up to the top for them to start over.



    function objectsCollided(unit, unitID, enemyID)
    {
    var tmpTile = document.getElementById("tile_" + unit.getAttribute("priorTileX") + "_" + unit.getAttribute("priorTileY"))
    unit.style.top = getTop(tmpTile);
    unit.style.left = getLeft(tmpTile);

    if(unitID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(enemyID, x, y);
    }

    if(enemyID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(unitID, x, y);
    }

    }



    One last change, I had to make was in the jumpUnit() function. I found that I needed to flag and unflag the tiles as the unit jumped.

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }


    And that's the entire change I made...the following is the entire code set:

    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;
    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("flag","0");
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);

    setWalkableFlag(x, y, unitID);

    document.getElementById("divMap").appendChild(d);
    }

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    /*
    if(flag > 0)
    {
    tmpTile.style.background = "yellow";
    }
    else
    {
    tmpTile.style.background = "#BBB";
    }
    */
    }

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    //if(newX < 0) newX = 0;
    //if(newX > mapWidth-1) newX = mapWidth-1;
    //if(newY < 0) newY = 0;
    //if(newY > mapHeight-1) newY = mapHeight-1;

    checkX = newX + tileSize/2;
    checkY = newY + tileSize/2;

    var newTile = document.getElementById("tile_" + translateTileX(checkX) + "_" + translateTileY(checkY))
    if(newTile.getAttribute("flag") == "0" || newTile.getAttribute("flag") == unitID)
    {
    unit.style.left = newX;
    unit.style.top = newY;

    unit.setAttribute("currTileX",getTileX(unitID));
    unit.setAttribute("currTileY",getTileY(unitID));

    if(unit.getAttribute("priorTileY") != unit.getAttribute("currTileY") || unit.getAttribute("priorTileX") != unit.getAttribute("currTileX") )
    {
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), unitID);

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");
    }

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }
    }
    else
    {
    //
    //we collided
    //snap back to last tile
    //
    objectsCollided(unit, unitID, newTile.getAttribute("flag"));


    }



    }


    function objectsCollided(unit, unitID, enemyID)
    {
    var tmpTile = document.getElementById("tile_" + unit.getAttribute("priorTileX") + "_" + unit.getAttribute("priorTileY"))
    unit.style.top = getTop(tmpTile);
    unit.style.left = getLeft(tmpTile);

    if(unitID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(enemyID, x, y);
    }

    if(enemyID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(unitID, x, y);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = Math.floor(getLeft(unit)/tileSize - originX);
    y = Math.floor(getTop(unit)/tileSize - originY);

    return y;
    }


    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < maxNumUnits)
    {
    unitID = 1001+numUnits
    x = Math.floor(Math.random()*9);
    speed = Math.floor(Math.random()*3);
    addUnit(unitID,x,0);
    var unit = document.getElementById("unit_" + unitID);
    unit.setAttribute("xVel", 0);
    unit.setAttribute("yVel", speed+1);
    numUnits++;
    }

    for(i=0;i<numUnits;i++)
    {
    unitID = 1001+i;
    var unit = document.getElementById("unit_" + unitID);
    x = getTileX(unitID) + parseInt(unit.getAttribute("xVel"));
    y = getTileY(unitID) + parseInt(unit.getAttribute("yVel"));

    if(y >= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4);play();">

    <div id="divMap"></div>

    </body>
    </html>


    Question: How would you go about adding a score indicator?

    Code: Simple 2D Javascript Map - Part 5

    This code will show how to create enemies and move them around the screen. It's going to randomly create 5 red boxes which will "fall" from the top of the grid to the bottom. The user can click a tile and the green box will move to the tile clicked. There is no collision detection...yet. I'm keeping this as simple as possible so in this example, "enemies" will drop from the top of the screen. Your job is to dodge them.

    The play function will get called every one second (playSpeed). It will add a new enemy until it has added 5 enemies. It chooses a random location at the top of the map, places the unit there.

    It will then pick a random speed and set the y velocity (think downward speed) equal to this random speed value.

    The second piece has the magic. It cycles through all enemy units and sets their next location to 3 spaces below where the unit currently is. Notice that this where our new functions get called.

    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < unitid =" 1001+numUnits" x =" Math.floor(Math.random()*9);" speed =" Math.floor(Math.random()*3);" unit =" document.getElementById(" i="0;i= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    Other changes



    Again, we have some plain Jane functions to get the tile location (x and y) of where our units is located. Make a note to yourself, there is plenty of room to optimize this code. It's dog slow.


    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return x;
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return y;
    }


    We also need to create a style for our enemy units. So I added this to the style.

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }


    Since we are moving random objects around, we need to start tracking the map height and width. So I changed the build map function.

    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++) y=0;y<height;y++);x++)


    Again, because we are moving enemies around randomly, need a bit of error catching added to the move function. If we try to move to a tile that does not exist, just stop moving.

    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile)
    ......


    Finally, I needed a function which will take an existing unit and "jump" them to a specific place on the map. This means they don't "move" one pixel at a time, but instead are immediately placed at that location:

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    }


    Last but not least, we have to call our new play function from within the body tag.

    <body onload="buildMap(10,10); addUnit(1000,4,4); play()">





    Finally, the entire page looks like this:


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;
    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }


    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    }



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return x;
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return y;
    }


    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < maxNumUnits)
    {
    unitID = 1001+numUnits
    x = Math.floor(Math.random()*9);
    speed = Math.floor(Math.random()*3);
    addUnit(unitID,x,0);
    var unit = document.getElementById("unit_" + unitID);
    unit.setAttribute("xVel", 0);
    unit.setAttribute("yVel", speed+1);
    numUnits++;
    }

    for(i=0;i<numUnits;i++)
    {
    unitID = 1001+i;
    var unit = document.getElementById("unit_" + unitID);
    x = getTileX(unitID) + parseInt(unit.getAttribute("xVel"));
    y = getTileY(unitID) + parseInt(unit.getAttribute("yVel"));

    if(y >= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4); play()">

    <div id="divMap"></div>

    </body>
    </html>

    Friday, April 2

    Code: Simple 2D Javascript Map - Part 4

    What's that you say, you want to move the character around the screen.

    OK, ok, ok...here we go.

    Because we will need to get the exact position of the div objects, I use the following two functions (In all fairness there are better, faster, more accurate ways to get the position of a div tag, but this has worked for me and is simple enough for my simple mind to grasp.)

    You pass them an object and they give you back the position of the object.


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }



    A bit boring, nothing to fascinating in those functions. However here is where things get a bit more exciting. The following function will move any unit object to the desired Tile position.

    We pass in the unitID we want to move, and the TILE x and y we want to move to. The function will:
    • Get the unit's and the tile's position
    • Compare the two positions and move the unit closer to the tile by one pixel.
    • At the very end of the function, if we have not reached our destination, we call the same function in 5 milliseconds.



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;



    if(x > destX) newX--;
    if(x <> destY) newY--;
    if(y < left =" newX;" top =" newY;">

    Now that moves the unit around, but no one is actually calling this function so



    Full Code:

    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }

    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;



    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">

    <div id="divMap"></div>

    </body>
    </html>



    For giggles, I used this quick function to test play it. The function adds a number of units and randomly moves them around the screen. My desktop (Intel 2 Duo 2.2GH with 2GB Ram) can handle about 20 units in firefox, before it gets a bit odd.


    var playSpeed = 3000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    while(numUnits < i="0;i

    Code: Simple 2D Javascript Map - Part 3

    All right, let's put a character on the board!

    It's not difficult, first we create a function that will add the character for us. So we know that we will need to know the following:
    • A distinct ID for the unit (unitID)
    • The x and y of where to place the unit.


    Things to note:
    • We have to get the tile at X, Y.
    • We set the new div's tile to the Top and Left of the tile
    • We give the div tag a distinct "ID"
    • We give the new div tag a class of "unit"



    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }


    We've created a new css class "unit" so we need to add the following to our styles:


    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }


    Finally, we actually add the unit to the map when the body is loading.

    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">


    So the full page, looks like this...


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }

    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">

    <div id="divMap"></div>

    </body>
    </html>

    Code: Simple 2D Javascript Map - Part 2

    So, we've built a map in javascript and displayed it. Quite impressive...or maybe not.

    Now we want to make something happen when the user clicks on a tile. In this example, we will change the background. First we add a simple function which we will call whenever a tile is clicked.

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }


    Now, we will attach this function to all the tiles, so within our buildMap() function we add this line of code:

    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")


    That's it...now when you click a tile, it turns yellow. Why don't you try to change it so that the background has an image and the image changes as you click it?

    Full Code:


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }


    </script>
    <body onload="buildMap(10,10)">

    <div id="divMap"></div>

    </body>
    </html>

    Followers