Jump to content

[Solved - SOLUTION INSIDE] Using a collision map for a large static map.


dittofittoe
 Share

Recommended Posts

Is there anything in Pixi.js where I can read the colour data of a texture or sprite?

I have a large map and instead of drawing collision lines, I want to just use an identical map but with 2 colours (magenta, alpha clear) and when the player collides with that colour pixel, it counts as a collision.

 

 

Edited by dittofittoe
Link to comment
Share on other sites

The demos dont seem to be working for me.

 

Edit nvm got it working,t y

 

OK, I now get the pixel data returned as an array, how do I check for any collision with a RGB value greather than 1? (Black area is walkable, white i a block).

Edited by dittofittoe
Link to comment
Share on other sites

On 7/20/2021 at 11:16 PM, ivan.popelyshev said:

Someone modified those examples, they are public. Sorry, cant do much about it, you have to look in source code and find whatever is wrong there. Code looks fine, at least you know where to go :)

I have decided to take a slightly different approach.

I am using this function to convert a sprite into an rgba array.

function convImage(_sprite){
    var imageData = app.renderer.plugins.extract.pixels(_sprite);

    var _red = imageData[0];
    var _green = imageData[1];
    var _blue = imageData[2];
    var _alpha = imageData[3];

    console.log(_red);
    console.log(_green);
    console.log(_blue);
   return imageData;
}

It gets converted to something but the values don't match the image, var _red, _green and _ blue (x:0 and y:0) should have the value of:

255,255,255,255,

 

But instead it it becomes 0, 0, 0, 255.

 

 

There are some white values in there but for some reason it doesn't match the image. 

The image shows the console output, and what x 0 and y 0 look like.

 

Edit: added ab image of the collision map. maybe it needs to be rotated or something? As sometimes a collision is logged but in random spots, not the white parts (where collision should be be logged).

 

unknown-4.png

Screenshot_20210728-014924_Gallery.jpg

Edited by dittofittoe
Link to comment
Share on other sites

I am using this function to convert a sprite into an rgba array.

this function estimates sprite bounds, creates renderTexture, readPixels it.

that means, if you have any transform on sprite - pixi has to render it shifted. There were so many of errors in those, that we really cannot guarantee the result. 

Alternative: you can do renderTexture creation, bounds estimation and readPixels yourself.

My advice is to actually use good old getImageData on canvas with said image, like in code i provided.

Link to comment
Share on other sites

7 hours ago, ivan.popelyshev said:

I am using this function to convert a sprite into an rgba array.

this function estimates sprite bounds, creates renderTexture, readPixels it.

that means, if you have any transform on sprite - pixi has to render it shifted. There were so many of errors in those, that we really cannot guarantee the result. 

Alternative: you can do renderTexture creation, bounds estimation and readPixels yourself.

My advice is to actually use good old getImageData on canvas with said image, like in code i provided.

using getImageData returns the same kind of array as I got before;

 

Function to return sprite as array:

function convImage(_sprite){

    const texture = _sprite._texture;
    const width = texture.orig.width;
    const height = texture.orig.height;
    const baseTexture = texture.baseTexture;

    var _uint8array =  genColorMap(baseTexture);

    var _red = _uint8array.data[0];
    var _green = _uint8array.data[1];
    var _blue = _uint8array.data[2];
    var _alpha = _uint8array.data[3];

    console.log(_red.data);
    console.log(_green.data);
    console.log(_blue.data);
   return _uint8array.data;
}

 

genColorMap function:


function genColorMap(baseTex) {
    if (!baseTex.resource) {
        return false;
    }
    const imgSource = baseTex.resource.source;
    let canvas = null;
    if (!imgSource) {
        return false;
    }

    let context = null;
    if (imgSource.getContext) {
        canvas = imgSource;
        context = canvas.getContext('2d');
    } else if (imgSource instanceof Image) {
        canvas = document.createElement('canvas');
        canvas.width = imgSource.width;
        canvas.height = imgSource.height;
        context = canvas.getContext('2d');
        context.drawImage(imgSource, 0, 0);
    } else {
        return false;
    }
 
    const w = canvas.width, h = canvas.height;
    baseTex.colormap = context.getImageData(0, 0, w, h);
  //  console.log(baseTex.colormap);
    return baseTex.colormap;
}

 

Edit: After carefully inspecting the collision map it appears the first two pixels were black not white, I fixed the image and the pixel array is correct.

 

Time to check some logic, I will update this with the solution.

Edited by dittofittoe
Link to comment
Share on other sites

Cool, so for anyone who wants to do this (I had trouble finding a source on how to do this):

 

1. Create 2 functions which a) Convert a sprite into an RGBA pixel array, b) convert that array into a multidimensional array seperating X and Y. I used these two functions from the link Ivan provided (slightly modified, but you need these mods for it to work)


const checkPixels = (sprite) => {

    const texture = sprite._texture;
    const width = texture.orig.width;
    const height = texture.orig.height;
    const baseTexture = texture.baseTexture;

     genColorMap(baseTexture);

    const colormap = baseTexture.colormap;
    const data = colormap.data;
    const res = baseTexture.resolution;


    let count = 0;
    let tmpArr = [];
    let lineArr = [];
    const newArr = [];
    for(let i = 0, len = data.length; i <= len; i++){
        if(count > 3){
            lineArr.push(tmpArr);
            tmpArr = [];
            count = 0;
        }
        if(lineArr.length === width){
            newArr.push(lineArr);
            lineArr = [];
        }
        tmpArr.push(data[i])
        count++;
    }


    return newArr;
}

function genColorMap(baseTex) {
    if (!baseTex.resource) {
        return false;
    }
    const imgSource = baseTex.resource.source;
    let canvas = null;
    if (!imgSource) {
        return false;
    }

    let context = null;
    if (imgSource.getContext) {
        canvas = imgSource;
        context = canvas.getContext('2d');
    } else if (imgSource instanceof Image) {
        canvas = document.createElement('canvas');
        canvas.width = imgSource.width;
        canvas.height = imgSource.height;
        context = canvas.getContext('2d');
        context.drawImage(imgSource, 0, 0);
    } else {
        return false;
    }
 
    const w = canvas.width, h = canvas.height;
    baseTex.colormap = context.getImageData(0, 0, w, h);

    return true;
}

 

2. Create an array of the result of the checkPixels function, similar to this (cSprite is the sprite of my collision map):

var cSpriteArray = checkPixels(cSprite);

 

Now cSpriteArray gets the value of all the pixel data in cSprite. To correspond an index to the players position; you can call it like this:

cSpriteArray[playersY][playersX][RGBA value (out of 4)]

The RGBA is stored as;

[0] Red

[1] Green

[2] Blue

[3] Alpha.

 

My collision map is just black and white, and I need to check where the player collides with a white value (basically any RGB value higher than 0);

 

5. Here is how I do that (simplified version just under it) - Here I am taking into account the edges of the player by adding the width/2 or height/2 because the anchor point is in the middle of the player sprite:

         if(this.cSpriteArray[_player.y+ _player.currentSprite.height/2][_player.x][0] > 0 || this.cSpriteArray[_player.y- _player.currentSprite.height/2][_player.x][0] > 0 ||
             this.cSpriteArray[_player.y][_player.x+ _player.currentSprite.width/2][0] > 0 || this.cSpriteArray[_player.y][_player.x- _player.currentSprite.width/2][0] > 0){

            console.log("map collision");
         }

 

 

simplified (not precise with the players edge:

         if(this.cSpriteArray[_player.y][_player.x][0] > 0){
            console.log("map collision");
         }

I hope anyone who spends hours looking for a solution to this can find this useful :)

 

GL

Edited by dittofittoe
Link to comment
Share on other sites

  • dittofittoe changed the title to [Solved - SOLUTION INSIDE] Using a collision map for a large static map.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...