Search code examples
javascripthtmlmultidimensional-arrayhtml5-canvasjavascript-objects

Generate collision objects from multidimensional array for HTML5 tile map


I am creating an HTML5 platform game using objects for collision detection and using a 2d tile map to render the level. That is all working.

Level map rendered

I want to use the same 2d array to build the object array dynamically to allow the player to build maps as required and also for ease of creating the maps in the first place. When hardcoding the object array, everything works so I know that the collision detect and game engine work.

While I can create objects for each individual array element, I am looking to build objects that have width based on the number of matching elements in the array, (each element is 25x25) i.e. if 3 array elements are 1,1,1 then the object will have a width of 75. Maybe some code will help explain:

The following tile array

var arr1 = [
    [0,0,0,1,1,1,1,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,2,2,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [3,3,3,0,0,0,0,0,0,0]
];

should produce the following object array:

[
    {x: 75, y: 0, width: 100, height: 25, value: 1},
    {x: 75, y: 50, width: 50, height: 25, value: 2},
    {x: 0, y: 100, width: 75, height: 25, value: 3}
]

but it instead it is producing the following:

[
    {x: 75, y: 0, width: 25, height: 25, value: 1},
    {x: 100, y: 0, width: 25, height: 25, value: 1},
    {x: 125, y: 0, width: 25, height: 25, value: 1}
]

My logic is obviously wrong but I can't for the life of me get it.

Example code is below:

Any help really appreciated:

var tmpObj = {};
var objArr = [];
var arr1 = [
    [0,0,0,1,1,1,1,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,2,2,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [3,3,3,0,0,0,0,0,0,0]
];

for (let i=0; i<arr1.length; i++) {
    for (let j=0; j<arr1[i].length; j++) {
        if (arr1[i][j] > 0 && arr1[i][j] < 6) { // platform blocks only 1 - 5
            if (tmpObj.x === undefined) {
                tmpObj = {
                    x: j * 25,
                    y: i * 25,
                    width: 25,
                    height: 25,
                    value: arr1[i][j]
                }
            } else if (arr1[i][j] == arr1[i][j-1] && arr1[i][j] == tmpObj.v) {
                tmpObj.w += 25;
            } else if (arr1[i][j] !== tmpObj.v) { // new tile type
                objArr.push(tmpObj);
                tmpObj = {
                    x: j * 25,
                    y: i * 25,
                    width: 25,
                    height: 25,
                    value: arr1[i][j]
                }
            } else {
                objArr.push(tmpObj);
                tmpObj = {};
            }
        }
    }
}
console.log(objArr);

Solution

  • Looking at what you are trying to do your implementation is way too complicated. Rather than hunt down the bug (for which I would have used devTools and stepped through the code line by line to find where the problem was.) I rewrote the function using a while loop to find the width of joined tiles.

    I took liberty with the object property names but I am sure you can change it to your needs.

    const objArr = [];
    const arr1 = [
        [0,0,0,1,1,1,1,0,1,0],
        [2,0,0,0,0,0,0,0,0,3],
        [0,0,0,4,4,0,4,4,4,4],
        [0,0,0,0,0,0,0,0,0,0],
        [3,3,3,5,5,4,0,0,0,0]
    ];
    const tileSize = 25;
    
    for (let i = 0; i < arr1.length; i++) {
        const row = arr1[i]
        for (let j = 0; j < row.length; j++) {
            if (row[j] > 0 && row[j] < 6) {
                let count = j + 1;
                while (count < row.length && row[count] === row[j]) { count += 1 }
                objArr.push({
                    x: j * tileSize,
                    y: i * tileSize,
                    w: tileSize * (count - j),
                    h: tileSize,
                    tile: row[j]
                });
                j = count - 1;
            }
        }
    }
    
    // show results
    objArr.forEach(log);
    
    
    
    
    
    
    
    
    
    // unrelated to answer. (I hate the console SO has for snippets.)
    function log(data){
        show.appendChild(
            Object.assign(
                document.createElement("div"), {
                    textContent : 
                         Object.keys(data).reduce((str, key) => {
                              return str + (" " + key+ ": " + data[key]).padEnd(8,".");
                              }, ""
                         )
                   }
             )
       );
    }                     
    <code id="show"></code>