SpriteRenderer shrinks after copying it's texture - unity3d

I have a SpriteRenderer with a texture, and I want to make a copy of the texture and assign this copy to the same SpriteRenderer
//Copy of the texture that will be changes in runtime
[HideInInspector]
public Texture2D tempTexture;
//FilterMode of the copied texture
[SerializeField]
private FilterMode filterMode = FilterMode.Bilinear;
/// <summary>
/// Create a copy of the texture that is used by a SpriteRenderer and unload the original texture from the memory
/// </summary>
void Start()
{
var spriteRenderer = GetComponent<SpriteRenderer>();
if(spriteRenderer==null)
{
Debug.LogError("SpriteRenderer is null");
return;
}
var tex = spriteRenderer.sprite.texture;
if (tex == null)
{
Debug.LogError("Sprite's texture is null");
return;
}
Debug.Log("original texture size is: " + tex.width + " : " + tex.height);
tempTexture = new Texture2D(tex.width, tex.height, tex.format, false);
tempTexture.filterMode = filterMode;
try
{
var colors = tex.GetPixels();
tempTexture.SetPixels(colors);
}
catch(Exception ex)
{
Debug.LogError(ex.Message);
return;
}
tempTexture.Apply();
Resources.UnloadAsset(tex);
spriteRenderer.sprite = Sprite.Create(tempTexture, spriteRenderer.sprite.textureRect, Vector2.one * 0.5f);
}
It I don't change texture's size (or make it bigger) in the Inspector by overriding it everything works perfect. But if I make the size of the texture smaller, then the sprite shrinks when my script completed its job.
One more time. If the texture has size, let's say, bigger than 512x512, and I change it to 512 in the Inspector, then the sprite shrinks and becomes two times smaller.
The problem is in my script, because if I disable it - everything is okay, even if i override the texture's size.
Any ideas about how to fix it?

I have found a solution
var oldSprite = spriteRenderer.sprite;
spriteRenderer.sprite = Sprite.Create(tempTexture, spriteRenderer.sprite.textureRect, Vector2.one * 0.5f, oldSprite.pixelsPerUnit, 0, SpriteMeshType.Tight, oldSprite.border);
border property of the sprite is responsible for its size

Related

Unity: Reduce size of Render Texture before executing Texture2D.ReadPixels

I'm working on a code where I basically have to take a low quality screenshot about every 30 milliseconds. The script is attached to a camara.
What I want to do is reduce the render texture size. The way the code is right now changing either W or H basically gets me a SECTION of of all that is being seen by the camara instead of a reduced size version. So my question is how can I resized or downsample what is read into the screenshot (Texture2D) but that it still is a representation of the entire screen.
public class CameraRenderToImage : MonoBehaviour
{
private RemoteRenderServer rrs;
void Start(){
TimeStamp.SetStart();
Camera.onPostRender += OnPostRenderCallback;
}
void OnPostRenderCallback(Camera cam){
if (TimeStamp.HasMoreThanThisEllapsed(30)){
TimeStamp.SetStart();
int W = Screen.width;
int H = Screen.height;
Texture2D screenshot = new Texture2D(W,H, TextureFormat.RGB24, false);
screenshot.ReadPixels( new Rect(0, 0, W,H), 0, 0);
byte[] bytes = screenshot.EncodeToPNG();
System.IO.File.WriteAllBytes("check_me_out.png", bytes);
TimeStamp.Tok("Encode to PNG and Save");
}
}
// Remove the onPostRender callback
void OnDestroy()
{
Camera.onPostRender -= OnPostRenderCallback;
}
}
If you need to resize your render texture from script you can refer to the next code snippet
void Resize(RenderTexture renderTexture, int width, int height) {
if (renderTexture) {
renderTexture.Release();
renderTexture.width = width;
renderTexture.height = height;
}
}
To make it possible to resize the render texture you first need to make sure it is released.
To get Texture2d:
private Texture2D ToCompressedTexture(ref RenderTexture renderTexture)
{
var texture = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
var previousTarget = RenderTexture.active;
RenderTexture.active = renderTexture;
texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
RenderTexture.active = previousTarget;
texture.Compress(false);
texture.Apply(false, true);
renderTexture.Release();
renderTexture = null;
return texture;
}

Can't double tap

I have a question I can't figure out myself for a long time. If I have a cube game object in Unity and I want to change it's color to red when I press the space key, all I have to do is write a script and write:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GameObject Cube = GameObject.FindWithTag("Cube");
Cube.GetComponent<Renderer>().material.color = Color.red;
}
}
But what if I want to change it's color to blue when I press the space key again?
For example, you can use a variable is_red and check its value in your update. Or maybe I didn't understand the problem
If you want to change the color on the second click you can simply add a If-statement that checks if the color is already equal to the wanted color and if it is change it to another color.
Example:
if (Input.GetKeyDown(KeyCode.Space)) {
// Get Cube GameObject
GameObject Cube = GameObject.FindWithTag("Cube");
// Get the Cube Renderer
Renderer rd = Cube.GetComponent<Renderer>();
// Is the color equal to red if it already is,
// change it to blue instead
if (rd.material.color = Color.red) {
rd.material.color = Color.blue;
}
else {
rd.material.color = Color.red;
}
}
Unrelated:
If the GameObject that has the Cube Tag stays the same GameObject over the game, I would advise you to rather "fetch" the GameObject and it's Renderer Component once at the start of the game instead of each time you press the space Key, to increase performance.
private GameObject cubeGO;
private Renderer cubeRD;
private void Start {
// Get Cube GameObject
cubeGO = GameObject.FindWithTag("Cube");
// Get the Cube Renderer
cubeRD = Cube.GetComponent<Renderer>();
}
You can now use these variables inise the Input.GetKeyDown(){}
if (Input.GetKeyDown(KeyCode.Space)) {
// Is the color equal to red if it already is,
// change it to blue instead
if (cubeRD.material.color = Color.red) {
cubeRD.material.color = Color.blue;
}
else {
cubeRD.material.color = Color.red;
}
}

How to properly stretch image in Monogame screen

I am a beginner in Monogame and C#. I am making my first game.
I have changed the screen dimensions in Game1 constructor. I am drawing a sprite where I have set the Rectangle width and height to width and height of the screen to fit it on the screen. I am creating a scrolling background for which I have to change the position of the sprite. I am setting the position to Vector. Zero which is making the sprite stretched towards the right.
Game1
//member variables
Vector2 stage;
Texture2D fbTexture;
private List<ScrollingBackground> sb;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//change the screen size
graphics.PreferredBackBufferHeight = 900;
graphics.PreferredBackBufferWidth = 600;
Window.Title = "Flappy Bird Returns";
graphics.ApplyChanges();
}
protected override void LoadContent()
{
stage = new Vector2(graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
//Day Background
Texture2D dayTex = Content.Load<Texture2D>("Images/background-day");
Vector2 daySpeed = new Vector2(1, 0);
sb = new List<ScrollingBackground>()
{
//set the width and height to be the same as the screen
//dimensions then this will
//stretch the image past its original dimensions to fit.
new ScrollingBackground(this,
spriteBatch,
dayTex,
new Vector2(0,0),
new Rectangle(0,0, (int)stage.X, (int)stage.Y),
daySpeed)
};
foreach (ScrollingBackground scrollbackg in sb)
this.Components.Add(scrollbackg);
}
//ScrollingBackground.cs
private SpriteBatch spritebatch;
private Texture2D tex;
private Vector2 position1;
private Vector2 speed;
private Rectangle srcRect;
public ScrollingBackground(Game game, SpriteBatch spriteBatch, Texture2D tex, Vector2 pos, Rectangle srcRect, Vector2 speed) : base(game)
{
this.spritebatch = spriteBatch;
this.tex = tex;
this.position1 = pos;
this.speed = speed;
this.srcRect = srcRect;
}
public override void Draw(GameTime gameTime)
{
spritebatch.Begin();
spritebatch.Draw(tex, position1, srcRect, Color.White);
spritebatch.End();
base.Draw(gameTime);
}
//Without position1
spritebatch.Draw(tex, srcRect, Color.White);
You need to give an argument to the float scale parameter. This means you'll need to use a more specific signature for this method :
SpriteBatch.Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, **float scale**, SpriteEffects effects, float layerDepth)
At the same time, you'll need to specify default values for every other parameter. For example, here's how you'd write that same Draw() method with the aforementioned method signature :
public override void Draw(GameTime gameTime)
{
spritebatch.Begin();
spritebatch.Draw(tex, position1, srcRect, Color.White, 0f, Vector2.Zero, 2f, SpriteEffects.None, 0f);
spritebatch.End();
base.Draw(gameTime);
}
Here, I put your texture to 2x it's original size. Change that 2f to the size you want. Keep in mind the final size will be the width/height values of your srcRect multiplied by that number.

Taking snapshots of a image in Unity

I am trying to take snapshots of materials I used in my application in Unity. I simply add a directional light and a camera and in a perspective mode. Then I render the result to a texture and save it as a .png file. The result is good but there is a strange gizmo like figure in the middle of image. Here it is :
Camera and light is far enough from the object. Also I disabled light to see if it is caused by directional light. But didn't solve. Anyone knows what cause this elliptic figure? Thanks in advance.
Edit. Here is the code
public static Texture2D CreateThumbnailFromMaterial(Material _material, string _name, string _path)
{
GameObject sphereObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphereObj.name = _name;
sphereObj.GetComponent<Renderer>().material = _material;
Texture2D thumbnailTexture = CreateThumbnailFromModel(sphereObj, _path);
sphereObj.GetComponent<Renderer>().material = null;
Object.DestroyImmediate(sphereObj.gameObject);
return thumbnailTexture;
}
public static Texture2D CreateThumbnailFromModel(GameObject _gameObject, string _path)
{
Texture2D thumbnailTexture = new Texture2D(textureSize, textureSize);
thumbnailTexture.name = _gameObject.name.Simplify();
GameObject cameraObject = Object.Instantiate(Resources.Load("SceneComponent/SnapshotCamera") as GameObject);
Camera snapshotCamera = cameraObject.GetComponent<Camera>();
if (snapshotCamera)
{
GameObject sceneObject = GameObject.Instantiate(_gameObject) as GameObject;
sceneObject.transform.Reset();
sceneObject.transform.position = new Vector3(1000, 0, -1000);
sceneObject.hideFlags = HideFlags.HideAndDontSave;
// Create render texture
snapshotCamera.targetTexture = RenderTexture.GetTemporary(textureSize, textureSize, 24);
RenderTexture.active = snapshotCamera.targetTexture;
// Set layer
foreach (Transform child in sceneObject.GetComponentsInChildren<Transform>(true))
{
child.gameObject.layer = LayerMask.NameToLayer("ObjectSnapshot");
}
// Calculate bounding box
Bounds bounds = sceneObject.GetWorldSpaceAABB();
float maxBoundValue = 0f;
if (bounds.IsValid())
{
maxBoundValue = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
}
double fov = Mathf.Deg2Rad * snapshotCamera.GetComponent<Camera>().fieldOfView;
float distanceToCenter = (maxBoundValue) / (float)System.Math.Tan(fov);
cameraObject.transform.LookAt(bounds.center);
cameraObject.transform.position = bounds.center - (snapshotCamera.transform.forward * distanceToCenter);
cameraObject.transform.SetParent(sceneObject.transform);
snapshotCamera.Render();
thumbnailTexture.ReadPixels(new Rect(0, 0, textureSize, textureSize), 0, 0);
thumbnailTexture.Apply();
sceneObject.transform.Reset();
snapshotCamera.transform.SetParent(null);
RenderTexture.active = null;
GameObject.DestroyImmediate(sceneObject);
GameObject.DestroyImmediate(cameraObject);
// Save as .png
IO.IOManager.Instance.SaveAsPNG(_path + thumbnailTexture.name, thumbnailTexture);
}
return thumbnailTexture;
}
And here is my camera properties

Wrong result when using setPixel()

I'm dealing with a problem for a few days now with setPixel() on Texture2D.
What i'm doing is getting mouse position or touch position(on android), then using that in setPixel() with transparent color. But the result i'm getting occur elsewhere instead of exactly where the mouse is...
public class EarshPic : MonoBehaviour {
public SpriteRenderer sr;
public SpriteRenderer srO;
public Camera c;
// Use this for initialization
void Start () {
CreateCover();//This method is working fine
}
private void CreateCover()
{
Color color = new Color(0.5F, 0.5f, 0.5F, 1.0F);
int x = srO.sprite.texture.width;
int y = srO.sprite.texture.height;
Texture2D tmpTexture = new Texture2D(srO.sprite.texture.width,
srO.sprite.texture.height);
for (int i = 0; i < tmpTexture.width; i++)
{
for (int j = 0; j < tmpTexture.height; j++)
{
tmpTexture.SetPixel(i, j, color);
}
}
tmpTexture.Apply(true);
sr.sprite = Sprite.Create(tmpTexture, srO.sprite.rect,
new Vector2(0.5f, 0.5f),srO.sprite.pixelsPerUnit);
}
// I have problem in that method
// Vector2 v = mousePostion or touchpostion
void Eraser(Vector2 v)
{
Color color = new Color(0.5F, 0.5f, 0.5F, 0.0F);
sr.sprite.texture.SetPixel(v.x, v.y, color);
sr.sprite.texture.Apply(true);
}
// Update is called once per frame
void Update () {
if(Input.mousePosition!=null)
{
Eraser(Input.mousePosition);
}
if (Input.touchCount == 1)
{
Touch touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Moved:
Eraser(touch.position);
break;
}
}
}
}
Problem
You are mixing different coordinates. This is the case if the texture is not perfectly screen sized. Your click is in screen coordinates and you are using it to set the transparency in texture coordinates.
Solution
This one requires the use of 3D models with colliders and textures on them. For 2D scenario you can use a box and set its texture to your 2D sprite. I don't know any easier method, but hopefully there is.
You have to first convert the screen position to world coordinate ray. This can be done with Camera.ScreenPointToRay.
Then you need to Physics.Raycast that ray to chech which position of the 3d model's collider it is intersecting with.
The intersection point can be changed to texture coordinates with RaycastHit.textureCoord. In the previous link, you can find a complete code example of the whole process.