Changing sprite texture pixels results in gigantic grey square - c#

I have a 32x32 sprite that I am trying to access the pixel data of and modify.
To do this I am simply taking the sprite's texture, creating a new texture based on the old and then changing the pixel values of the new texture. I then create a new sprite with the modified texture and change the SpriteRenderer's sprite parameter to the new sprite.
However, when I actually run my script what I get is a huge grey square, easily 10x the size of the original 32x32 sprite. I'm very new to unity so I'm not sure why this is happening. Any insight would be great.
private Sprite sprite;
private Texture2D texture;
// Use this for initialization
void Start ()
{
sprite = this.gameObject.GetComponent<SpriteRenderer>().sprite;
texture = sprite.texture;
Texture2D newTexture = modifyTexture(texture);
SpriteRenderer sr = this.gameObject.GetComponent<SpriteRenderer>();
sr.sprite = Sprite.Create(newTexture, new Rect(0, 0, newTexture.width, newTexture.height), new Vector2(0, 0), 10);
}
public Texture2D modifyTexture(Texture2D baseTexture)
{
Texture2D newTexture = new Texture2D(baseTexture.width, baseTexture.height);
int x = 0;
while(x < newTexture.width)
{
int y = 0;
while(y < newTexture.height)
{
Color currentPixel = baseTexture.GetPixel(x,y);
Color modifiedPixel = currentPixel;
modifiedPixel.r = (float)(modifiedPixel.r + 0.10);
modifiedPixel.b = (float)(modifiedPixel.b + 0.10);
modifiedPixel.g = (float)(0.10);
newTexture.SetPixel(x, y, modifiedPixel);
y++;
}
x++;
}
Debug.Log(newTexture.GetPixel(5, 5).ToString());
return newTexture;
}

After modifying pixels of a Texture, you must call the Apply function upload the modified pixels to the graphics card.
public Texture2D modifyTexture(Texture2D baseTexture)
{
Texture2D newTexture = new Texture2D(baseTexture.width, baseTexture.height);
int x = 0;
while (x < newTexture.width)
{
int y = 0;
while (y < newTexture.height)
{
Color currentPixel = baseTexture.GetPixel(x, y);
Color modifiedPixel = currentPixel;
modifiedPixel.r = (float)(modifiedPixel.r + 0.10);
modifiedPixel.b = (float)(modifiedPixel.b + 0.10);
modifiedPixel.g = (float)(0.10);
newTexture.SetPixel(x, y, modifiedPixel);
y++;
}
x++;
}
//Upload changes to Graphics graphics card
newTexture.Apply();
Debug.Log(newTexture.GetPixel(5, 5).ToString());
return newTexture;
}

Related

How to slice sprite by script ?(not use Editor)

I trying to slice Sprite(type casted to Texture2D) by script,
when project is running on Android or IOS Platform
is it possible by script??
I trying to use UnityEditor Class and it is work on Computer
but When I trying to Build Android or IOS It is failed.
void OnPreprocessTexture()
{
TextureImporter textureImporter = (TextureImporter)assetImporter;
textureImporter.textureType = TextureImporterType.Sprite;
textureImporter.spriteImportMode = SpriteImportMode.Multiple;
textureImporter.mipmapEnabled = false;
textureImporter.filterMode = FilterMode.Point;
}
public void OnPostprocessTexture(Texture2D texture)
{
Debug.Log("Texture2D: (" + texture.width + "x" + texture.height + ")");
int spriteSize = 350;
int colCount = texture.width / spriteSize;
int rowCount = texture.height / spriteSize;
List<SpriteMetaData> metas = new List<SpriteMetaData>();
for (int r = 0; r < rowCount; ++r)
{
for (int c = 0; c < colCount; ++c)
{
SpriteMetaData meta = new SpriteMetaData();
meta.rect = new Rect(c * spriteSize, r * spriteSize, spriteSize, spriteSize);
meta.name = c + "-" + r;
metas.Add(meta);
}
}
TextureImporter textureImporter = (TextureImporter)assetImporter;
textureImporter.spritesheet = metas.ToArray();
}
public void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
{
Debug.Log("Sprites: " + sprites.Length);
}
It is not working When running project on Android or IOS
[What I want]
Procedure
During running on Android or IOS Platform
1) Receive some Images from server (Url or file)
2) Load Image on C# script
3) Change type Images to Texture or Sprite ect...
4) Slice Images(Don't use Editor)
5) Save Pieces of Image
6) Use piece of Image
What I want is all procedure worked by Script
TextureImporter belongs to the UnityEditor namespace which doesn't exist in a built app but only within the Unity Editor itself. → You can not use this!
What you can do is using Sprite.Create to generate a sprite from a given Texture2D.
Cropping
If it is actually only about cutting out a certain part of the texture to use it as sprite than you only need to define in the rect parameter the part of the texture you want to use from that image.
// Wherever you get the texture from
Texture texture = ...;
// E.g. for using the center of the image
// but half of the size
var rect = new Rect(texture.width / 4, texture.height / 4, texture.width / 2, texture.height / 2);
// Create the sprite
var sprite = Sprite.Create(texture, rect, Vector2.one * 0.5f);
where rect is the
Location of the Sprite on the original Texture, specified in pixels.
Slicing
If you additionally want a slicing border (which you usually define in the Sprite Editor within Unity) there is an overload of Sprite.Create that additionally takes a border parameter e.g.
var borders = new Vector4(2, 2, 2, 2);
var sprite = Sprite.Create(texture, rect, Vector2.one * 0.5f, 100, SpriteMeshType.FullRect, borders);
where border
Returns the border sizes of the sprite.
X=left, Y=bottom, Z=right, W=top.
API doesn't say it but I guess like the rect values those values are also in pixels.
I solved using rect and Sprite Rnederer
here is my code
void Start()
{
rect = new Rect(0f, 0f, 255, 255);
this.GetComponent<SpriteRenderer>().sprite = Sprite.Create(img, rect, new Vector2(0.5f, 0.5f));
this.GetComponent<RectTransform>().localScale = new Vector3(100, 100, 0);
StartCoroutine(Update());
}
/*
* rect = new Rect(i, 0, 255, 255);
this.GetComponent<SpriteRenderer>().sprite = Sprite.Create(img, rect, new Vector2(0.5f, 0.5f));*/
IEnumerator Update()
{
while (true)
{
if (i < 1000)
{
i += 255;
if (i > 770)
{
i = 0;
}
}
yield return new WaitForSeconds(0.25f);
rect = new Rect(i, 0f, 255, 255);
this.GetComponent<SpriteRenderer>().sprite = Sprite.Create(img, rect, new Vector2(0.5f, 0.5f));
}
}

Cropping texture2d on all sides in xna c#

i am trying to crop a texture2d in xna. i have found the following code which will crop the image on the top and right sides, i have played around with the code and cannot figure a way to crop all sides at a specific interval. below is the code i have been trying to modify:
any help or ideas would be much appreciated.
Rectangle area = new Rectangle(0, 0, 580, 480);
Texture2D cropped = new Texture2D(heightMap1.GraphicsDevice, area.Width, area.Height);
Color[] data = new Color[heightMap1.Width * heightMap1.Height];
Color[] cropData = new Color[cropped.Width * cropped.Height];
heightMap1.GetData(data);
int index = 0;
for (int y = 0; y < area.Y + area.Height; y++) // for each row
{
for (int x = 0; x < area.X + area.Width; x++) // for each column
{
cropData[index] = data[x + (y * heightMap1.Width)];
index++;
}
}
cropped.SetData(cropData);
Here is the code to crop a texture. Note that the GetData method can already select rectangular subsection of the image - there is no need to manually crop.
// Get your texture
Texture2D texture = Content.Load<Texture2D>("myTexture");
// Calculate the cropped boundary
Rectangle newBounds = texture.Bounds;
const int resizeBy = 20;
newBounds.X += resizeBy;
newBounds.Y += resizeBy;
newBounds.Width -= resizeBy * 2;
newBounds.Height -= resizeBy * 2;
// Create a new texture of the desired size
Texture2D croppedTexture = new Texture2D(GraphicsDevice, newBounds.Width, newBounds.Height);
// Copy the data from the cropped region into a buffer, then into the new texture
Color[] data = new Color[newBounds.Width * newBounds.Height];
texture.GetData(0, newBounds, data, 0, newBounds.Width * newBounds.Height);
croppedTexture.SetData(data);
Of course, keep in mind that SpriteBatch.Draw can take a sourceRectangle parameter, so you may not even need to copy the texture data around at all! Just use a subsection of the original texture. For example:
spriteBatch.Draw(texture, Vector2.Zero, newBounds, Color.White);
(Where newBounds is calculated in the same way as in the first code listing.)

Collision detection for Tiny Wings-esque Windows Phone game?

I'm currently working on a Windows Phone game similar to Tiny Wings and I've been having trouble with collision detection between the player's character and the hills. I've been looking into a few different ways to do this including the Farseer Physics engine and a Silverlight tutorial
Player's character initialization
lander = new Lander(new Vector2(graphics.PreferredBackBufferWidth / 4, 100));
LoadContent
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
primitiveBatch = new PrimitiveBatch(GraphicsDevice);
// Load textures
landerTexture = this.Content.Load<Texture2D>(".\\Sprites\\boysmall");
// Initialize lander based on texture
lander.Width = landerTexture.Width;
lander.Height = landerTexture.Height;
// Load the parallaxing background
bgLayer1.Initialize(Content, "hillsbackground", GraphicsDevice.Viewport.Width, -2, 150);
bgLayer2.Initialize(Content, "hillsbackground2", GraphicsDevice.Viewport.Width, -3, 220);
// Load hills
string[] hillsArray = new string[2]{"textured","hillsmooth"};
mainHill.Initialize(Content, hillsArray, GraphicsDevice.Viewport.Width, -4);
// Load Sky
mainBackground.Initialize(Content, "mainbackground", GraphicsDevice.Viewport.Width, -1, 0);
}
I understand that fundamentally, collision detection looks for intersection points between two rectangles, and have done that for the "lander", but don't know how I can do it for the hills and how to make it slide down smoothly? let me know if you need any additional code, thanks for your help!
Rectangle attempt for Lander
public Rectangle GetScreenRectangle()
{
// calculate the rectangle from the current location
// and the current source rectangle
var rectangle = new Rectangle(
(int)graphics.PreferredBackBufferWidth / 4,
(int)100,
lander.Width,
lander.Height);
return rectangle;
}
Hills Code
class MainHill
{
// The image representing the parallaxing background
Texture2D texture;
// An array of positions of the parallaxing background
//Vector2[] positions;
// The speed which the background is moving
int speed;
// Object array holding position and texture of each live texture
object[,] positionsArray = new object[2, 2];
public void Initialize(ContentManager content, Array hillsArray, int screenWidth, int speed)
{
// Load the background texture we will be using
//texture = content.Load<Texture2D>("textured");
// Set the speed of the background
this.speed = speed;
// If we divide the screen with the texture width then we can determine the number of tiles need.
// We add 1 to it so that we won't have a gap in the tiling
//positions = new Vector2[screenWidth / texture.Width + 2];
texture = content.Load<Texture2D>("textured");
// Set the initial positions of the parallaxing background
for (int i = 0; i < screenWidth / texture.Width + 2; i++)
{
//Pull out random texture
Random random = new Random();
string[] randomHill = new string[2];
randomHill[0] = "textured";
randomHill[1] = "hillsmooth";
string currentString = randomHill[(int)(random.Next(0, 2))];
texture = content.Load<Texture2D>(currentString);
// We need the tiles to be side by side to create a tiling effect
positionsArray[i,0] = new Vector2(i * texture.Width, 290);
positionsArray[i, 1] = texture;
}
}
public void Update(ContentManager content)
{
Debug.WriteLine("=========================");
// Update the positions of the background
for (int i = 0; i < positionsArray.GetLength(0); i++)
{
Vector2 current_position = (Vector2)positionsArray[i, 0];
// Update the position of the screen by adding the speed
current_position.X += speed;
// Check the texture is out of view then put that texture at the end of the screen
if (current_position.X <= -texture.Width)
{
// Figure out a possibly new texture if it's out of view
Random random = new Random();
string[] newHill = new string[2];
newHill[0] = "textured";
newHill[1] = "hillsmooth";
string currentString = newHill[(int)(random.Next(0, 2))];
texture = content.Load<Texture2D>(currentString);
Color[] textureData = new Color[texture.Width * texture.Height];
texture.GetData(textureData);
current_position.X = texture.Width * (positionsArray.GetLength(0) - 1);
positionsArray[i, 1] = texture;
}
positionsArray[i, 0] = current_position;
}
}
public void Draw(SpriteBatch spriteBatch, ContentManager content)
{
for (int i = 0; i < positionsArray.GetLength(0); i++)
{
Vector2 current_position = (Vector2)positionsArray[i, 0];
//Random random = new Random();
//string[] hillsArray = new string[2];
//hillsArray[0] = "textured";
//hillsArray[1] = "hillsmooth";
//string currentString = hillsArray[(int)(random.Next(0,2))];
//texture = content.Load<Texture2D>(currentString);
texture = (Texture2D)positionsArray[i, 1];
spriteBatch.Draw(texture, current_position, Color.White);
}
}
}

Texture2D is turning black

I have a Texture2D that I'm loading from the Content Pipeline. That's working fine, but as soon as I try to use SetData on a completely different Texture2D all of the textures in my game go completely black:
This is in my HUDMeter class, the class that I want to be just red
Texture2D colorGrad = Content.Load<Texture2D>(GradientAsset);
Color[,] pixels = new Color[colorGrad.Width, colorGrad.Height];
Color[] pixels1D = new Color[colorGrad.Width * colorGrad.Height];
pixels = GetRedChannel(colorGrad);
pixels1D = Color2DToColor1D(pixels, colorGrad.Width);
System.Diagnostics.Debug.WriteLine(pixels[32,32]);
Gradient = colorGrad;
Gradient.SetData<Color>(pixels1D);
These are using Riemers tutorial
protected Color[,] GetRedChannel(Texture2D texture)
{
Color[,] pixels = TextureTo2DArray(texture);
Color[,] output = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
{
for (int y = 0; y < texture.Height; y++)
{
output[x,y] = new Color(pixels[x,y].G, 0, 0);
}
}
return output;
}
protected Color[,] TextureTo2DArray(Texture2D texture)
{
Color[] colors1D = new Color[texture.Width * texture.Height];
texture.GetData(colors1D);
Color[,] colors2D = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
for (int y = 0; y < texture.Height; y++)
colors2D[x, y] = colors1D[x + y * texture.Width];
return colors2D;
}
private Color[] Color2DToColor1D (Color[,] colors, int width)
{
Color[] output = new Color[colors.Length];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < colors.Length / width; y++)
{
output[x + y * width] = colors[x % width, y % (colors.Length/width)];
}
}
return output;
}
And here is the code to draw the sprites, this works fine and is how I always draw sprites:
batch.Draw(meter.Gradient, new Vector2(X, Y), Color.White);
Update:
I've actually found that the sprites that don't use the same file are not black. Does Texture2D.SetData<>() actually change the file itself? what is the use of that?
Update:
I just tried to use the Alpha as well as RGB and it's working. I'm thinking that there's something wrong with one of the conversion methods.
If you do this:
Texture2D textureA = Content.Load<Texture2D>("MyTexture");
Texture2D textureB = Content.Load<Texture2D>("MyTexture");
Both textureA and textureB refer to the same object. So if you call SetData on one of them, it will affect both of them. This is because ContentManager keeps an internal list of resources already loaded, so it doesn't have to keep reloading the same resource.
The solution would be to create a new Texture2D object of the same size, call GetData on the one loaded by ContentManager, and then SetData on the new texture.
Example (not tested):
Color[] buffer = new Color[textureA.Width * textureA.Height];
Texture2D textureB = new Texture2D(textureA.GraphicsDevice,
textureA.Width,
textureA.Height);
textureA.GetData(buffer);
textureB.SetData(buffer);
Dispose() of the new texture when you are finished with it (eg: in your Game.UnloadContent method). But never dispose of the one loaded by ContentManager (because, like I said, it is a shared object; use ContentManager.Unload instead).

How can I use a Shader in XNA to color single pixels?

I have a standard 800x600 window in my XNA project. My goal is to color each individual pixel based on a rectangle array which holds boolean values. Currently I am using a 1x1 Texture and drawing each sprite in my array.
I am very new to XNA and come from a GDI background, so I am doing what I would have done in GDI, but it doesn't scale very well. I have been told in another question to use a Shader, but after much research, I still haven't been able to find out how to accomplish this goal.
My application loops through the X and Y coordinates of my rectangular array, does calculations based on each value, and reassigns/moves the array around. At the end, I need to update my "Canvas" with the new values. A smaller sample of my array would look like:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
How can I use a shader to color each pixel?
A very simplified version of the calculations would be:
for (int y = _horizon; y >= 0; y--) // _horizon is my ending point
{
for (int x = _width; x >= 0; x--) // _width is obviously my x length.
{
if (grains[x, y] > 0)
{
if (grains[x, y + 1] == 0)
{
grains[x, y + 1] = grains[x, y];
grains[x, y] = 0;
}
}
}
}
..each time the update method is called, the calculations are performed and in example of the above loop, an update may look like:
Initial:
0,0,0,1,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
First:
0,0,0,0,0,0,0
0,0,0,1,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Second:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,1,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Final:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
Update:
After applying the Render2DTarget code and placing my pixels, I end up with an unwanted border on my pixels, always to the left. How can I remove this?
alt text http://www.refuctored.com/borders.png
alt text http://www.refuctored.com/fallingdirt.png
The some of the code for applying the textures is:
RenderTarget2D target;
Texture2D texture;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("grain");
_width = this.Window.ClientBounds.Width - 1;
_height = this.Window.ClientBounds.Height - 1;
target = new RenderTarget2D(this.GraphicsDevice,_width, _height, 1, SurfaceFormat.Color,RenderTargetUsage.PreserveContents);
}
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.SetRenderTarget(0, target);
this.GraphicsDevice.SetRenderTarget(0, null);
this.GraphicsDevice.Clear(Color.SkyBlue);
this.spriteBatch.Begin(SpriteBlendMode.None,SpriteSortMode.Deferred,SaveStateMode.None);
SetPixels(texture);
this.spriteBatch.End();
}
private void SetPixels(Texture2D texture)
{
for (int y = _grains.Height -1; y > 0; y--)
{
for (int x = _grains.Width-1; x > 0; x--)
{
if (_grains.GetGrain(x, y) >0)
{
this.spriteBatch.Draw(texture, new Vector2(x,y),null, _grains.GetGrainColor(x, y));
}
}
}
}
This method doesn't use pixel shaders, but if you're looking to use Texture2D's SetData method instead of making a call to SpriteBatch.Draw() for every pixel, you may find this useful. I used an array of uint instead of bool to represent your colors. If you can get away with an 8-bit color texture, you could may be able to speed this up by changing the texture format.
public class Game1 : Microsoft.Xna.Framework.Game
{
// Set width, height
const int WIDTH = 800;
const int HEIGHT = 600;
// Used to randomly fill in initial data, not necessary
Random rand;
// Graphics and spritebatch
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// Texture you will regenerate each call to update
Texture2D texture;
// Data array you perform calculations on
uint[] data;
// Colors are represented in the texture as 0xAARRGGBB where:
// AA = alpha
// RR = red
// GG = green
// BB = blue
// Set the first color to red
const uint COLOR0 = 0xFFFF0000;
// Set the second color to blue
const uint COLOR1 = 0xFF0000FF;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Set width, height
graphics.PreferredBackBufferWidth = WIDTH;
graphics.PreferredBackBufferHeight = HEIGHT;
}
protected override void Initialize()
{
base.Initialize();
// Seed random, initialize array with random picks of the 2 colors
rand = new Random((int)DateTime.Now.Ticks);
data = new uint[WIDTH * HEIGHT];
loadInitialData();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Create a new texture
texture = new Texture2D(GraphicsDevice, WIDTH, HEIGHT);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Run-time error without this
// Complains you can't modify a texture that has been set on the device
GraphicsDevice.Textures[0] = null;
// Do the calculations
updateData();
// Update the texture for the next time it is drawn to the screen
texture.SetData(data);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
// Draw the texture once
spriteBatch.Begin();
spriteBatch.Draw(texture, Vector2.Zero, Color.Purple);
spriteBatch.End();
base.Draw(gameTime);
}
private void loadInitialData()
{
// Don't know where the initial data comes from
// Just populate the array with a random selection of the two colors
for (int i = 0; i < WIDTH; i++)
for (int j = 0; j < HEIGHT; j++)
data[i * HEIGHT + j] = rand.Next(2) == 0 ? COLOR0 : COLOR1;
}
private void updateData()
{
// Rough approximation of calculations
for(int y = HEIGHT - 1; y >= 0; y--)
for (int x = WIDTH - 1; x >= 0; x--)
if (data[x * HEIGHT + y] == COLOR1)
if (y + 1 < HEIGHT && data[x * HEIGHT + (y + 1)] == COLOR0)
{
data[x * HEIGHT + (y + 1)] = data[x * HEIGHT + y];
data[x * HEIGHT + y] = COLOR0;
}
}
}
How about this...
Create two textures (800x600)
Initialize one of them to the initial values.
For each frame you render one texture to the other while updating the values in a pixelshader.
After rendering the resulting texture to the screen, you swap them, so they are ready for your next frame.
Edit:
You will need two instances of RenderTarget2D and create them with RenderTargetUsage.PreserveContents. You could start with SurfaceFormat.Color and use black for 0 and white for 1. (You may also be able to find a 8 bit format to save video memory.)
new RenderTarget2D(_device, 800, 600, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);
You assign them to the rendertaget like this:
_device.SetRenderTarget(0, myRenderTarget);
You use a RenderTarget2D as a texture, like this:
_device.Textures[0] = myRenderTarget.GetTexture();
Hope that helps... I can dig out more from my engine, so just ask.
What you're trying to do (draw pixel by pixel) is what DirectX does. XNA is a layer that is built on top of DirectX so that you don't have to draw pixel by pixel. If that's really what you want to do then you should probably be learning DirectX instead of XNA. You would probably find it much easier...
Add a billboard to your scene (directly in front of the camera, so that it takes up exactly 800x600 pixels).
Bind the binary array as a texture.
In the fragment (/pixel) shader calculate the color of the fragment using the 2D position of the fragment and the texture.

Categories

Resources