- Home /
Flip a Texture?
Hi there! Allow me to ask you another newbie question please... In photoshop I flip a 2d image like this: edit -> transform -> flip; how would I do that in Unity? I mean WITH code.
See, I did some web researches but every click draw me even more far away, toward topics related to textures but not to the solution. I know every Shader has this Tiling and Offset thing, and I've read that to flip a texture horizontally you just need to change to -1 the Tiling in your inspector... fact is that I couldn't be able to grasp how to do that with code.
I tried a brutish renderer.lightmapTilingOffset = new Vector4(-1,1,0,0) but nothing happens, I also tried renderer.material.mainTextureTiling = new Vector2(-1,0) but ofcourse mainTextureTiling doesn't exist ( why?).
I'm ignorant but eager to know how, I'm not asking for plain code, maybe just a push in the right direction... my searches brought me here: http://docs.unity3d.com/Documentation/Manual/ShaderTut1.html; This is pertinent but non exactly what I need here, am I wrong? I also stumbled upon this http://docs.unity3d.com/Documentation/ScriptReference/Material.html but as you can see, there is mainTextureOffset but not Tiling.
I'm missing something... thanks a lot for your patience ( AND if it turns out an answer already exist, forgive my thoughtlessness)!
Answer by robertbu · Oct 15, 2013 at 10:01 PM
renderer.material.SetTextureScale("_MainTex", new Vector2(-1,1));
Note this assumes that your shaders main texture is defined as _MainTex. This is true of most shaders but not all. While not specific to your question, changing material properties takes a shared material and generates a new material instance and therefore breaks draw call batching. An alternate method of flipping the texture that does not break batching is to change the UV coordinates directly in the mesh (more complicated).
Hi robertbu! Thanks a lot, it certainly works! I've read something about the complicated method you say... I'll try to learn something more about this whole texture world ( again, my knowledge is scarce)... meantime, if you happen to be in a very generous mood and feel to bestow me some more informations about the topic, I'd be pleased to read them. Thanks again!
Here is a bit of code that flips a texture using the UVs of the mesh. It will only work on a two triangles, four vertices planes. The new Quad game object (Game Object > Create Other > Quad) meets this criteria. Also the CreatePlane editor script will work as long as 'Width Segments' and 'Length Segments' are both set to 1. A 'V' key will flip it vertically, a 'H' key will flip it horizontally, and a 'B' key will flip it both directions.
#pragma strict
function Update() {
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.H))
FlipQuadUV(true, false);
else if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.V))
FlipQuadUV(false, true);
else if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.B))
FlipQuadUV(true, true);
}
function FlipQuadUV(horzFlip : boolean, vertFlip : boolean) {
var mesh : $$anonymous$$esh = GetComponent($$anonymous$$eshFilter).mesh;
var uvs = mesh.uv;
if (uvs.Length != 4) {
Debug.Log("Error: not a four vertices mesh");
return;
}
for (var i = 0; i < uvs.Length; i++) {
if (horzFlip) {
if ($$anonymous$$athf.Approximately(uvs[i].x, 1.0))
uvs[i].x = 0.0;
else
uvs[i].x = 1.0;
}
if (vertFlip) {
if ($$anonymous$$athf.Approximately(uvs[i].y, 1.0))
uvs[i].y = 0.0;
else
uvs[i].y = 1.0;
}
}
mesh.uv = uvs;
}
Right now I've resolved by using the same trick developers of Unity 2d sample project.
We must know the facing of our 2d sprite in the beginning, and make a bool to check if it's true. $$anonymous$$y assets have sprites only facing right, so my boolean is facingRight ( true).
Then, when something happens and the sprite must change facing, I use this code:
void Flip(){
facingRight = !facingRight;
//Time to change your scale, best way is doing it with a method that works both ways around as above...
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
//Thanks Unity for this trick
}
So... if you have an enemy that checks its target ( player) position, the code looks like this in the end:
void Update () {
Facing();
if(isTargetRight && !facingRight){
Flip();
}
if(!isTargetRight && facingRight){
Flip ();
}
}
void Facing () {
//Check if the target is Right or Left
float targetX = target.x;
float myX = transform.position.x;
if(targetX<myX){
isTargetRight = false;
}
else{
isTargetRight = true;
}
}
It works good :D
Answer by Simonius Skjorn · Aug 12, 2014 at 11:05 PM
Another method would be to set the scale of the SpriteRenderer object directly:
Vector3 scale = renderer.transform.localScale;
scale.x = -scale.x;
renderer.transform.localScale = scale;
Easiest by far and will get batched since Unity 4.5.X. Or the same thing via rotation (see the comment below that the scale might be problematic in some cases):
Vector3 angles = renderer.transform.localEulerAngles;
angles.y = 180;
renderer.transform.localEulerAngles = angles;
Of course, this will depend on the pivot of the Sprite. If set to center, it will behave as expected. Otherwise you'll need to move it a bit.
@Simonius Skjorn - I've seen a number of questions come across this list where colliders did not behave as they should when using -x to flip. Since sprites are rendered on both sides, rotating 180 on the 'y' may be a better choice.
@robertbu That's interesting. I haven't come across this problem yet, but I'll keep it in $$anonymous$$d. I would have thought it doesn't matter (aside from the rotation computation being a bit more extensive).
i'm using 4.6X and find the scale.x version does break batching, where as using the rotating method doesn't, so appears to be the better solution.
I'm now using the method posted in my comment to the answer, what do you think of it?
Answer by silentreaver · Aug 14, 2020 at 03:10 PM
Sadly no solution is going to be really efficient without going into threading, which you may or may not be able to do, as to flip the texture you have to move all the pixels in the image around to truly "flip" the image. I've attempted to create the most efficient method for flipping a texture horizontally. This avoids performing multiple get or set pixels, and processes the pixels 2 at a time. This should work for your project:
public static Texture2D FlipTexture(this Texture2D original)
{
int textureWidth = original.width;
int textureHeight = original.height;
Color[] colorArray = original.GetPixels();
for (int j = 0; j < textureHeight; j++)
{
int rowStart = 0;
int rowEnd = textureWidth - 1;
while (rowStart < rowEnd)
{
Color hold = colorArray[(j * textureWidth) + (rowStart)];
colorArray[(j * textureWidth) + (rowStart)] = colorArray[(j * textureWidth) + (rowEnd)];
colorArray[(j * textureWidth) + (rowEnd)] = hold;
rowStart++;
rowEnd--;
}
}
Texture2D finalFlippedTexture = new Texture2D(original.width, original.height);
finalFlippedTexture.SetPixels(colorArray);
finalFlippedTexture.Apply();
return finalFlippedTexture;
}