- Home /
Math to have a bullet penetrate stuff in 2D?
Before I ask my question here is an example of a 2D grid of pixels, showing a bullets travel and the pixels it hits, like I want to happen:
So I have a 2D game where I can blow up parts of the world, spray pixels onto the world, and I want to implement bullet penetration (for like rifles) where the player shoots at some angle, the bullet collides with the terrain (checking against a texture2d's alpha values) and then looks like it digs into the terrain by removing about ten pixels in the same direction the bullet was moving. So yeah problem is the way I faked it was having 16 possible angles to dig a line in the terrain based on the velocity (x and y) of the bullet when it collides - but that doesn't look good enough, and I would need many many cases (if velocity x > velocity y amount n then dig n some direction) to cover more angles and be more realistic.
How could I code it to work like my image suggests, measuring all the pixels that would be hit, within ten pixels, from a given point, with the velocity on x and y, and collision x and y point given?
PS the velocity ends up being high on a bullet, it reports usually more than 1k in velocity on x and y values, showing how fast it was moving at collision time after effects of gravity and such... so trying to use that number to my advantage requires normalizing or something I guess... I am pretty lost what to try guys! Any suggestions for what to try to do instead of hardcoding a pile of generic angles, and instead like, mathematically deciding which pixels to remove?
Sorry for all the vague questions lately guys haha.
I might be in a different ballpark altogether, but I might suggest (if you're bullet and ground pixels are 3D meshes) set the bullet's collider to trigger, give the bullet a specific tag "Bullet" for example, add a 'OnTriggerEnter' script to the pixel prefabs. This script will have a variable that stores the 'mass' or 'stoppingpower' of the specific pixel. Then, in the OnTriggerEnter event, make it so that it pulls the script containing the bullet's velocity from the bullet and modify the velocity with this mass. So say:
If the bullet's velocity is -300, -300, -200 , and the 'mass' of the pixel is 60, you "could" take 60% of all values away over a time frame (Lerp maybe?)... Then make a $$anonymous$$imum value that has to be met by at least one of the values in order to keep the bullet from being destroyed...
So, like in the previous example, 40% of 300 is 120... then it hits another pixel with 60 mass leaving it at 48... then another at 60 leaving it at 19.2 then another at 60, so on and so forth... until your bullet has its' last value fall under 1 or something.
Again. I don't really know the specifics but this is just an idea.
Well the world is actually texture2d(s) - where setpixel/getpixel is used to deter$$anonymous$$e clear air pixels (alpha of 0) or solid colored "terrain" pixels (alpha of 1). It gets pretty complicated from here - using an array of int's ins$$anonymous$$d of checking getpixel on texture2d(s) because I figure that its faster - and the world is split into many separate sprite objects, each with a chunk (cell I call them) of the worlds overall texture... and yeah so like velocity on the bullets (which work fine as explosives, taking a radius of pixels and removing them) is only 2d, velocity X and Y values, that get handed off to another script when the bullet collides and gets placed in a pool...
And then... I have been doing some math like "if velocity X and Y are positive, and if x > almostUprightValue or if y > almostDiagonalValue then specify an exact proprietary hardcoded line of pixels to remove from the collision point to ten pixels away...
That won't work for me because it looks to... hardcoded! So maybe your answer will work for me - I'll look into it and if it works out I'll totally accept your answer!
Answer by troien · Nov 24, 2014 at 07:54 PM
I think for this you could use the Bresenham's line algorithm. (This method is used to draw a line between to points and can be used for this)
In this example, I used this code as a start, and then changed it to better suit our needs.
private static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }
/// <summary>
/// Returns the pixels that this ray will intersect.
/// </summary>
/// <param name="endPoint">The direction we are going</param>
/// <param name="amount">This is the maximum amount of pixels we will find</param>
/// <returns></returns>
public List<Vector2> GetNextPoints(Vector2 direction, int amount)
{
List<Vector2> returnValue = new List<Vector2>(amount);
int x0 = 0; // Start x
int y0 = 0; // Start y
int x1 = Mathf.RoundToInt(direction.x * amount); // End X
int y1 = Mathf.RoundToInt(direction.y * amount); // End y
bool steep = Mathf.Abs(y1 - y0) > Mathf.Abs(x1 - x0);
if (steep) { Swap<int>(ref x0, ref y0); Swap<int>(ref x1, ref y1); }
if (x0 > x1) { Swap<int>(ref x0, ref x1); Swap<int>(ref y0, ref y1); }
int dX = (x1 - x0), dY = Mathf.Abs(y1 - y0), err = (dX / 2), ystep = (y0 < y1 ? 1 : -1), y = y0;
for (int x = x0; x <= x1; ++x)
{
if (steep)
returnValue.Add(new Vector2(y, x));
else
returnValue.Add(new Vector2(x, y));
err = err - dY;
if (err < 0)
{
y += ystep;
err += dX;
}
}
return returnValue;
}
Calling it would be something like this:
public void Start()
{
List<Vector2> result = GetNextPoints(new Vector2(3, 1).normalized , 10);
for (int i = 0; i < result.Count; i++)
{
GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = result[i];
}
}
Note that this code doesn't always return the next 'amount' of pixels, but all crossed pixels between 0,0 and 'direction * amount' and the amount of crossed pixels could be less then 'amount', depending on the angle.
Actually you know what, that line drawing algorithm is used elsewhere in my project haha, I never thought though to apply it here as well... Hmm...
EDIT oh yeah I remember raycasting is using this... I'll see what I can figure out and return to let you know if it worked for me! Thanks troien!
Here is how it turned out (setpixel ins$$anonymous$$d of create primitive):
You Sir - are awesome!