- Home /
Destroying pixels of a dynamic 2D world based on hardness of a pixel?
I want to blow up pixels that are part of a 2D terrain, and be able to have the explosion penetrate further through pixels depending on how "hard" the pixels are considered, with 0 being invincible, and 254 being super soft, and 255 being an empty pixel (air). I am not totally sure how I might go about doing that, even though I already can check how "hard" a pixel is...
Here is an image explaining what I want to happen:
In this example the grass would be "soft" and the dirt "somewhat hard" for instance...
Right now my explosion takes a square of pixels surrounding the explosions location, then within a certain radius they are removed from the world (what the PURPLE radius would be doing in my example) but with no consideration for the "hardness" of a pixel, but rather only if it is open (air) or solid (anything but air).
Here is a snippet of the C# code for removing pixels in explosions I use right now:
/* Explode */
// Creates an "explosion" by finding all pixels near a point and launching them away
public void explode(int xPos, int yPos, int radius)
{
// we don't explode if were hitting the boundaries, or shooting the open sky at an edge will explode!
if (xPos > 1 && yPos > 1 &&
xPos < worldWidth - 1 && yPos < worldHeight - 1)
{
radiusSq = radius * radius; // get a square radius for this explosion
explodePosition.x = xPos; // set the position where it will happen
explodePosition.y = yPos;
randomNumber = Random.Range(0, 99); // generate a random number between 0 and 99 to select a random sprite using...
CreateExplodeSprite(explodePosition, 0); // this is for the graphical "explosion" sprite to show
// loop through every x from xPos-radius to xPos+radius
for (int x = xPos - radius; x < xPos + radius; x += GameMaster.GM.DestructionResolution) // this "DestructionResolution" is always 1
{
// first make sure that the x is within terrain's boundaries
if (x >= 0 && x < worldWidth)//pD.SourceTexture.width)
{
// next loop through every y pos from yPos-radius to yPos+radius, having a "box" of the terrain we will check for whether or not its "solid"
for (int y = yPos - radius; y < yPos + radius; y += GameMaster.GM.DestructionResolution)
{
if (y >= 0 && y < worldHeight)//pD.SourceTexture.height) // boundary check
{
// first determine if this pixel (or if any contained within its square area) is solid
int solidX = 0, solidY = 0;
bool solid = false;
// loop through every pixel from (xPos,yPos) to (xPos + destructionRes, yPos + destructionRes)
// to find whether this area is solid or not
for (int i = 0; i < GameMaster.GM.DestructionResolution && !solid; i++) // if destruction resolution wasn't 1, this would loop through the "resolution" of pixels, but its 1...
{
for (int j = 0; j < GameMaster.GM.DestructionResolution && !solid; j++) // same here, this loops only a single time!
{
if (GameMaster.GM.pixelMagic.isPixelSolid(x + i, y + j)) // returns true if the pixel is not "air" or a clear pixel
{
solid = true;
solidX = x + i; // keep a reference to where that solid pixel is
solidY = y + j;
}
}
}
if (solid) // we know this pixel is solid, now we need to find out if it's close enough
{
float xDiff = x - xPos; // get the difference on x
float yDiff = y - yPos; // and y
float distSq = xDiff * xDiff + yDiff * yDiff; // as well as the the square distance
// if the distance squared is less than radius squared, then it's within the explosion radius
if (distSq < radiusSq)
{
// finally calculate the distance
float distance = Mathf.Sqrt(distSq);
if (distance == 0) // error prevention!
distance = 0.001f; // prevent divide by zero later
// remove the static pixels
for (int i = 0; i < GameMaster.GM.DestructionResolution; i++) // only loops once
{
for (int j = 0; j < GameMaster.GM.DestructionResolution; j++) // only loops once
{
GameMaster.GM.pixelMagic.removePixel(x + i, y + j); // remove the pixel from our terrain
}
}
}
}
}
}
}
}
}
}
So how could I go about considering how hard a pixel is, and then make the explosion radius shorter when it is hitting more "harder" pixels, and longer when hitting "softer" pixels, which would make my explosion have smaller or larger radius (to a certain maximum) depending how "hard" a pixel is... and not allow it to destroy "invincible" pixels?
If you made it this far through this wall of text and have any ideas please comment or answer with anything that might push me in the right direction, and psuedocode or whatever is totally acceptable for me! I am sure others have run into similar problems within voxel engines and 3D destruction games, and perhaps other situations. I'll be here scratching my head about it.
Is this using a bought/downloaded asset package, or are they all your own creations?
Either way, the line dividing your intent appears to be line 37:
if (Game$$anonymous$$aster.G$$anonymous$$.pixel$$anonymous$$agic.isPixelSolid(x + i, y + j)) // returns true if the pixel is not "air" or a clear pixel
Depending on how it's retrieving data (for example, if it's an efficient-enough "is alpha zero?"), you could potentially create a variant which returns the value for that pixel rather than a boolean for whether it's "solid" or not.
Then, for each pixel, you can have a "blast strength" dropping off with its radius from the center pixel. If the value of the blast is greater than the value held in the pixel (or less than it, if 0 is solid and 255 (or 1, depending on scale) is empty), the pixel is eligible for destruction.
It's not based on any existing asset or anything, but very similar to Joe strouts asset "pixel surface" in nature.
I see what your saying, and I do have an alternative method that gives a int between 0 and 255 ins$$anonymous$$d of a bool value.
I guess what I'm hung up on is how can I keep track of something like "passed hardness" and if x amount has been "blown up" outwards, prevent any more removals after that.
Does this make sense? I guess this is very difficult to explain!
Answer by Eno-Khaon · May 08, 2016 at 12:22 AM
If the value for a pixel is "air" then it doesn't need to be eligible to be blasted out. An if condition would settle that easily enough.
For every other pixel, you could perform a breadth-first search. Rather than looking for anything in particular, though, simply use this as your means of working away from the starting pixel. Test the center pixel (the strongest point for the blast), then work your way out from there.
For any pixel that is not blown up, save a note of its position. Then, on subsequent pixels, determine whether it should be protected by the pixels not blown up. If not, determine whether they're weak enough to be blown up.
// Pseudo-code C#
public float protectionAngle = 0.95f; (Dot Product-based angle
System.Collections.Generic.List<Vector2> safePixels;
float blastRange; // Maximum range of blast
float blastStrength; // How strong is the blast initially?
safePixels = new List<Vector2>();
// Perform breadth-first search from center
foreach pixel from center, radiating to a maximum of "blastRange" pixels' distance ((pixel - center).magnitude)
{
bool safe = false;
for(int i = 0; i < safePixels.Count; i++)
{
if(Vector2.Dot(safePixels[i] - center, currentPixel - center) > protectionAngle)
{
safe = true;
}
}
if(blastStrength > pixelHardness)
{
// Blow it up...
// Then...
blastStrength -= (pixelHardness * falloffRate); // falloffRate should be tiny, unless you want most of the explosive force lost when the blast is barely strong enough
}
else
{
safePixels.Add(current Vector2);
}
}
It'll take some engineering to get it all working together, but here's the concept in brief:
Start in the middle of the blast.
Radiate out toward the ends, determining whether...
1) Is the pixel shielded from the blast? If so, you're done. It's safe.
2) Is the blast is strong enough to destroy the pixel?
--a) If not, that one will now shield others behind it.
--b) If so, how much will the blast's remaining strength be reduced?
Perform these tests on each pixel until you reach the maximum range from the center (or any other boundaries) and make sure not to retest any pixel more than once.
Also, I didn't really add it in sooner, but the blast strength per pixel would basically be calculated as something like:
Mathf.Lerp(0, blastStrength, (currentPixel - center).magnitude / blastRange);
You know that sounds about right. I guess it makes more sense to breadth first search outwards from the impact rather than gather a surrounding square and deter$$anonymous$$e distance.
Thanks for this, if it works I'll convert your comment to an answer!
Worked perfect! I have awarded you 3 entire points :P
Ended up using a queue to do breadth-first and using distance and "hardness" as almost a heuristic, I was able to get a cool result.
Thanks so much for the advice!
Your answer
![](https://koobas.hobune.stream/wayback/20220612080335im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Help With Animation 1 Answer
Keep SphereCollider radius the same, even though scale is changed 2 Answers
Getting radius of a sphere 2 Answers