1

I am making a game in HTML5, JS.

In the game I perform the collisions by circle-shapes since my sprites are rotating.

The player has different shapes since it has different sprites when it holds different weapons.

The radius of the player is half the sprites width / height.

The sprites are resized from 16x16 to 50x50 pixels (50 / 16 = 3.125, so 3.125 times bigger)

My question is: How do you calculate the radius of the smallest circle-shape that the sprite would fit in? Like this:

Normal Sprite: https://i.stack.imgur.com/01Fkd.jpg

Red = To calculate radius of (how?), Blue = Radius I use (sprite width or height / 2): https://i.stack.imgur.com/LVJBp.jpg

(For a better look of the images open them in a image program or something to zoom in please.)

Jason C
  • 38,729
  • 14
  • 126
  • 182

3 Answers3

2

Assuming you have a rectangular sprite with width = w and height = h. There are two options:

  • External Circle: simply calculate the hypotenuse of the rectangle r = sqrt(w^2 + h^2)

enter image description here

  • Internal Circle: in this case the diameter will be the shortest side of the rectangle. eg: r = w/2

enter image description here enter image description here

As for the transparent pixels. Is the transparent boundary of a fixed size? If so then simply subtract that value from the radius calculation. If not, then you could try this:

  • Load the image
  • Start with the outer most layer of pixels and check to see if there are transparent pixels
  • If yes then move to the next layer in
  • and so on
  • once you find a layer with no transparent pixels, stop

You now know how many pixels of transparency there are. Simply subtract that from the radius calculation.

In case you don't know how to get the pixel colour this might help: Get pixel color from canvas, on mouseover

Of course it would be slow to do this every single time. I would recommend opening your sprite in Photoshop or whatever and measuring the appropriate size.


It sounds like you are trying to achieve pixel perfect collision for non circular shapes. Maybe you should look into that instead, eg: Per-Pixel collision code

Community
  • 1
  • 1
winkerVSbecks
  • 1,173
  • 1
  • 10
  • 24
2

Several steps to getting your answer...

Demo: http://jsfiddle.net/m1erickson/hvT63/

enter image description here

Determine the bounding box of the player-pixels (excluding transparent pixels).

  • Use .getImageData to get the individual pixel data for the image.

  • Examine each vertical column from the left and determine where the first player-pixel occurs.

  • Examine each vertical column from the right and determine where the first player-pixel occurs.

  • Examine each horizontal row from the top and determine where the first player-pixel occurs.

  • Examine each horizontal row from the bottom and determine where the first player-pixel occurs.

Now you have top, left, bottom & right bounding values.

Determine the radius of the bounding box (== half of its diagonal):

var dx=right-left;
var dy=bottom-top;
var radius=Math.sqrt(dx*dx+dy*dy)/2;

So your containing circle has that calculated radius.

Here's example code:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:20px; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    ctx.translate(.5,.5);

    var img=new Image();
    img.crossOrigin="anonymous";
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/sprite.png";

    function start(){

        var cw=canvas.width;
        var ch=canvas.height;

        ctx.drawImage(img,cw/2-img.width/2,ch/2-img.height/2);

        var data=ctx.getImageData(0,0,cw,ch).data;
        var leftX=getLeft(data,cw,ch);
        var rightX=getRight(data,cw,ch);
        var topY=getTop(data,cw,ch);
        var bottomY=getBottom(data,cw,ch);
        var w=rightX-leftX;
        var h=bottomY-topY;
        var cx=leftX+w/2;
        var cy=topY+h/2;
        var radius=Math.sqrt(w*w+h*h)/2;

        ctx.beginPath();
        ctx.arc(leftX+w/2,topY+h/2,radius,0,Math.PI*2);
        ctx.closePath();
        ctx.stroke();

        ctx.strokeRect(leftX,topY,w,h);

    }

    function getLeft(data,width,height){
        for(var x=0;x<width;x++)
        for(var y=0;y<height;y++)
        {
            if(data[(width*y+x)*4+3]>0){ return(x); }
        }
    }

    function getRight(data,width,height){
        for(var x=width-1;x>=0;x--)
        for(var y=height-1;y>=0;y--)
        {
            if(data[(width*y+x)*4+3]>0){ return(x); }
        }
    }

    function getTop(data,width,height){
        for(var y=0;y<height;y++)
        for(var x=0;x<width;x++)
        {
            if(data[(width*y+x)*4+3]>0){ return(y); }
        }
    }

    function getBottom(data,width,height){
        for(var y=height-1;y>=0;y--)
        for(var x=width-1;x>=0;x--)
        {
            if(data[(width*y+x)*4+3]>0){ return(y); }
        }
    }


}); // end $(function(){});
</script>

</head>

<body>
    <h4>1. Calc boundingbox of non-transparent pixels<br>2. Calc radius that contains that boundingbox</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
0

Are your sprites uniformly circles or can they be rotating rectangles/squares? In the case of squares and rectangles you'll want to find the hypotenuse (use Pythagorean's Theorem) and then take half of that to use as a radius. That way as you rotate it the longest diagonal will always fit within the box. Circles are a given.

nvll
  • 21
  • 1
  • No, I mean like how to get the radius of the actual "player" inside the sprite fitten inside a circle –  Dec 26 '13 at 16:49
  • 1
    There is pixels with transparency around the player and those should not be count. –  Dec 26 '13 at 16:50