I am making a game where there is a color picker to change the color of an GameObject where clicking on a color in the button should take back the color it points though inside the buttons and pass it to the material of the game object. It mostly works fine but there is some issue with the cursor position which brings in a different color, but not the color to which it is pointed. Requesting help.
public RectTransform Rect;
public Text TestText;
public Texture2D ColorTexture;
public GameObject Plane;
public void PickColor()
{
Vector3 imagePos = Rect.position;
float globalPosX = Input.mousePosition.x - imagePos.x;
float globalPosY = Input.mousePosition.y - imagePos.y;
int localPosX = (int)(globalPosX * (ColorTexture.width / Rect.rect.width));
int localPosY = (int)(globalPosY * (ColorTexture.height / Rect.rect.height));
float x = Input.mousePosition.x * (ColorTexture.width / Rect.rect.width);
float y = Input.mousePosition.y * (ColorTexture.width / Rect.rect.width);
Color Col = ColorTexture.GetPixel(localPosX, localPosY);
TestText.color = Col;
SetActualColor(Col);
}
void SetActualColor(Color Col)
{
TestText.color = Col;
Plane.GetComponent<MeshRenderer>().sharedMaterial.color = Col;
}
RectTransform.rect.width and RectTransform.rect.height aren't actually in screen size pixels as opposed to Input.mousePosition.`They are scaled with the canvace/screen, so that you always get a significant error with your correct formula.
You need to determine the global position of the corners and calculate the dimensions from that. Luckily there is a build in function for that.
Vector3[] corners = new Vector3[4];
Rect.GetWorldCorners(corners);
Vector2 dimensions = corners[2] - corners[0];
int localPosX = (int)(globalPosX * (ColorTexture.width / dimensions.x));
int localPosY = (int)(globalPosY * (ColorTexture.height / dimensions.y));
You also need to make sure that the pivot of the rect is set to (0,0) since the texture coordinates start from the bottom left.
Related
How do I convert image position to camera viewport view?
I have a radar and I use a camera to show that viewport on the screen but it only works on a set rect which works fine for only 1 type of screen, the one that i set.
How can I match this rect to a image transform position. so if the screen changes and as it moves, the ui around it will move the camera view with it too.
This is for the screen of an iPhone XS Max (2688x1242)
I would want something like overlayCamera.rect = transform.position
Here is the code that I use to show the camera rect.
private void UpdateCameraViewportRect()
{
float aspectRatio = (float)Screen.width / Screen.height;
float desiredScreenWidth = 0.271f;
float viewHeight = 0.831f;
float viewWidth = 0.78f;
float width = desiredScreenWidth;
float height = width * aspectRatio;
if (height > 1.0f)
{
height = 1.0f;
width = height / aspectRatio;
}
// Controls the position of the view
overlayCamera.rect = new Rect(
viewHeight - width, viewWidth - height,
width, height
);
/*
// Method 1 -> Does not work
Vector3 p = overlayCamera.ViewportToWorldPoint(new Vector3(1, 1, overlayCamera.nearClipPlane));
Debug.Log(p);
// Method 2 -> Does not work
Vector3 screenPos = overlayCamera.WorldToScreenPoint(overlayCamera.transform.position);
float posx = screenPos.x - (width * 0.5f);
float posy = screenPos.y - (height * 0.5f);
Debug.Log(posx + " & " + posy);
// Method 3 -> Does not work
overlayCamera.rect = radarBorder.rectTransform.rect;
*/
}
How can I center the viewport rect in the center of the gold border image?
Solution: Create a RenderTexture instead and show it in a RawImage to move around the UI.
Ok I may have misunderstood but it looks like the approach is to adjust the UI dynamically based on the aspect ratio of the screen.
It may be best to lay out the UI normally in the editor and force the aspect ratio of the overlayCamera to be 1:1 using Camera.aspect:
void Start(){
var cam = GetComponent<Camera>();
cam.aspect = 1;
cam.ResetAspect();
}
EDIT:
While it is possible to adjust where on the screen a camera renders, as you can see its a little messy. Common practice is to render to a RenderTexture and place that on a material inside the UI.
So as the title says I'm trying to map properly a octahedron sphere using a 3D Perlin Noise as a procedural texture.
I suppose it has something to do about the UVs or about the edges vertices of the texture (left, right, probably even top and down). It's a texture with size 512*512, but it can be 1024*1024.
I've been documentating, trying other techniques, using normal maps, tangets, etc but i still can't figure out how to solve that seam (keep in mind it should be procedurally generated) to generate a surface around the sphere to update it during runtime (in that way I can change the noise (shape of the terrain) as well as the colours).
By the way, when I do the same with a prepared texture (1024*512) with the edges properly corrected the seam disappear, but what I want is the ability to change it in run time (can survive without it but would be nice to have it)
private void OnEnable()
{
if(autoUpdateTexture)
{
if (texture == null)
{
texture = new Texture2D(resolution, resolution, TextureFormat.RGB24, true);
texture.name = "Procedural Texture";
texture.wrapMode = TextureWrapMode.Repeat;
texture.filterMode = FilterMode.Trilinear;
texture.anisoLevel = 9;
GetComponent<MeshRenderer>().sharedMaterial.mainTexture = texture;
}
FillTexture();
}
}
public void FillTexture()
{
if (texture.width != resolution)
{
texture.Resize(resolution, resolution);
}
Vector3 point00 = transform.TransformPoint(new Vector3(-0.5f, -0.5f));
Vector3 point10 = transform.TransformPoint(new Vector3(0.5f, -0.5f));
Vector3 point01 = transform.TransformPoint(new Vector3(-0.5f, 0.5f));
Vector3 point11 = transform.TransformPoint(new Vector3(0.5f, 0.5f));
NoiseMethod method = Noise.noiseMethods[(int)type][dimensions - 1];
float stepSize = 1f / resolution;
for (int y = 0; y < resolution; y++)
{
Vector3 point0 = Vector3.Lerp(point00, point01, (y + 0.5f) * stepSize);
Vector3 point1 = Vector3.Lerp(point10, point11, (y + 0.5f) * stepSize);
for (int x = 0; x < resolution; x++)
{
Vector3 point = Vector3.Lerp(point0, point1, (x + 0.5f) * stepSize);
float sample = Noise.Sum(method, point, frequency, octaves, lacunarity, persistence);
if (type != NoiseMethodType.Value)
{
sample = sample * 0.5f + 0.5f;
}
texture.SetPixel(x, y, coloring.Evaluate(sample));
}
}
texture.Apply();
}
So, I have 2 images, one showing the 3D generated noise in the sphere (when I save the textue to png it just goes to 2D, something obvious)
And the other one, showing that 3D noise IN the sphere with a seam at the edges, so the thing is get that 3D noise in the sphere without the seam.
If you need any more related info, please let me know, as this is giving me a nice headache.
procedural texture in 2D
3D noise on sphere
I'm setting up an automatic system to be able to attach a sprite and it will gather all its colours and the world position of each sprite. A list/class of all the colours used has been set up but how would get the position of all these sprites?
I have already tried doing this mathematically like getting the complete size of the sprite and then working out the size of each pixel and then working out the position from that. But this seems flawed due to the position of the sprite possibly changing.
Sprite ColouredSpriteTexture = ColoredSprite.GetComponent<SpriteRenderer>().sprite;
Texture2D ColouredTexture = ColouredSpriteTexture.texture;
float XsizeF = ColoredSprite.transform.localScale.x;
int Xsize = (int)XsizeF;
float YsizeF = ColoredSprite.transform.localScale.y;
int Ysize = (int)YsizeF;
List<Color> TempList = new List<Color>();
//Could spawn pixels by getting x and y size and dividing them by 100 50/100 = 0.50f
//if the tile has a color then spawn pixel if not 0.50 += 0.50
//TODO test if this logic will work
float PixelSize = XsizeF / 100;
float currentPos = PixelSize;
for (int x = 0; x < Xsize; x++)
{
for (int y = 0; y < Ysize; y++)
{
int listAmount = TempList.Count;
Color ColoredTex = ColouredTexture.GetPixel(x, y);
float TextureAlpha = ColoredTex.a;
if (!TempList.Contains(ColoredTex) && TextureAlpha != 0)
{
TempList.Add(ColoredTex);
ColorByNumber tempColor = new ColorByNumber();
tempColor.Color = ColoredTex;
tempColor.ColorNumber = listAmount;
ColorOptions.Add(tempColor);
}
if(TextureAlpha == 1)
{
GameObject ColorPixel = Instantiate(PixelPrefab);
ColorPixel.transform.localScale = new Vector3(XsizeF, YsizeF, 0);
ColorPixel.transform.SetParent(this.transform);
ColorPixel.name = "Pixel (" + x.ToString() + "," + y.ToString() + ")";
}
}
}
All I would need is somehow each pixel returning its position so I can store this data and be able to spawn anything on top of this pixel.
I haven't had a chance to test this math yet so there may be some mistakes in it:
Every graphical image in Unity has a PPU, this and the object scale are going to be a huge factor. For argument sake I am going to clearly define these for 1 object.
Image dimensions : 128x128
PPU: 64
Scale: 1,1,1
Object Bounds: would
come from the renderer, which I am unsure if that bounds already
takes in account the scale(Most likely) however in the case you
cannot use that you can calculate the ObjectBoundsWidth or height
just by dividing the width or height of the texture by the PPU.
This should give you bounds of the texture in world space.
We are also going to make an assumption that we are only working on the X and Y axis and ignore the Z axis, if you want to use Z instead of Y then just make the necessary changes to be Z Scale and Z position and Z Bounds.
World position of a pixel located at 2,10. Per the documentation the pixel coordinates start at the lower left this means 0,0 is the bottom left corner, and 2,10 is 2 pixels left and 10 pixels up.
EDIT:
So I plugged all of this into a google sheet and determined the previous algorithm I provided was wrong here is the correct one in a pseudo code format
// This function takes in either the x or y, and the width or height of
// the bounds, then the x or y position of the object attached to.
// It also assumes the pivot is the center of the sprite.
float CalculateWorldPosOfPixelCoordinate(int coord, float boundsSize, float position, float scale)
{
float PixelInWorldSpace = 1.0f / PPU;
float startPos= position - (boundsSize* 0.5f * scale);
return startPos + (PixelInWorldSpace * coord) * scale;
}
This is using objectBounds we determined ourselves that is why we are multiply by scale.
this would give use a world position of: -0.97, -0.84
The algorithm i believe is the same for Y, just replace the coord with the Y position, and the bounds with the height instead of the width.
Like I said this could be wrong as I havent had a chance to test it, this also does not account for rotation either.
Yo, Hi everybody
Is there any Way to draw a circle? I don't want to use a Texture/sprite to draw a circle Because the Player is the Circle so the Circle Should move ... and also I'm Trying to make it so the Player/Circle's Size gets bigger and bigger When he eats some food blablabla...
anyways, if anybody knows how to do it please Tell me.
OTHERWISE : IS THERE A WAY TO CHANGE A TEXTURE HEIGHT / WIDTH , THEN I WILL MAKE A SIMPLE CIRCLE TEXTURE AND CHANGE HEIGHT / WIDTH OF IT.
Thanks.
You can use 3D primitives like 'Someone' :> already posted or use the C3.XNA.Primitives2D libary where you can use a extension for SpriteBatch to draw a circle
public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, float thickness);
If you use the same value for radius and thickness the circle appears filled.
I didn't find the offizial download link, but there are also uploads at sourceforge.
Also you can generate a circle dynamically via code like:
public static Texture2D GenerateCircleTexture(GraphicsDevice graphicsDevice, int radius, Color color, float sharpness)
{
int diameter = radius * 2;
Texture2D circleTexture = new Texture2D(graphicsDevice, diameter, diameter, false, SurfaceFormat.Color);
Color[] colorData = new Color[circleTexture.Width * circleTexture.Height];
Vector2 center = new Vector2(radius);
for (int colIndex = 0; colIndex < circleTexture.Width; colIndex++)
{
for (int rowIndex = 0; rowIndex < circleTexture.Height; rowIndex++)
{
Vector2 position = new Vector2(colIndex, rowIndex);
float distance = Vector2.Distance(center, position);
// hermite iterpolation
float x = distance / diameter;
float edge0 = (radius * sharpness) / (float)diameter;
float edge1 = radius / (float)diameter;
float temp = MathHelper.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
float result = temp * temp * (3.0f - 2.0f * temp);
colorData[rowIndex * circleTexture.Width + colIndex] = color * (1f - result);
}
}
circleTexture.SetData<Color>(colorData);
return circleTexture;
}
Sharpness below 1f blurs the circle.
The only way to draw primitives (e.g. circles) is in 3D:
https://msdn.microsoft.com/en-us/library/bb196414.aspx
Or you can load a texture that is 1*1, stretch it into a line and then use a bunch of those lines to make a circle.
Use:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
to stretch the texture.
Or you can just use a circle texture and stretch it.
If you are making something like agar.io then you might want to use a texture combined with the circle primitive so you can make the circle 'wobbly'.
I am currently developing a class for my XNA game whose rendering the lights on the image. At the time, i have made the source to draw my lightmap, however, the FPS is very low in my source. I know that it is brutally reduced upon looping through each pixel, however, I do not know any other way to get & set each pixel on my Texture in XNA but using the "For" statement?
Current Source:
public struct Light
{
public int Range;
public int Intensity;
public Color LightColor;
public Vector2 LightLocation;
public Light(int _Range, int _Intensity, Color _LightColor, Vector2 _LightLocation)
{
Range = _Range;
Intensity = _Intensity;
LightLocation = _LightLocation;
LightColor = _LightColor;
}
}
public class RenderClass
{
[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern bool MessageBox(IntPtr h, string S, string C, int a);
public static Texture2D RenderImage(Light[] LightLocations, Texture2D ScreenImage, Viewport v, bool ShadowBack = false)
{
Texture2D[] Images = new Texture2D[LightLocations.Count()];
int curCount = 0;
/*LOOP THROUGHT EACH LIGHT*/
foreach (Light LightLocation in LightLocations)
{
/*VARIABLES*/
Color LightColor = LightLocation.LightColor;
int Range = LightLocation.Range;
int Intensity = LightLocation.Intensity;
/*GET COLORS*/
int Width = v.Width;
int Height = v.Height;
Color[] Data = new Color[Width * Height];
ScreenImage.GetData<Color>(Data);
/*VARIABLES TO SET COLOR*/
Color[] SetColorData = new Color[Width * Height];
/*CIRCEL*/
int Radius = 15 / 2; // Define range to middle [Radius]
int Area = (int)Math.PI * (Radius * Radius);
for (int X = 0; X < Width; X++)
{
for (int Y = 0; Y < Height; Y++)
{
int Destination = X + Y * Width;
#region Light
/*GET COLOR*/
Color nColor = Data[Destination];
/*CREATE NEW COLOR*/
Vector2 MiddlePos = new Vector2(LightLocation.LightLocation.X + Radius, LightLocation.LightLocation.Y + Radius);
Vector2 CurrentLocation = new Vector2(X, Y);
float Distance;
Distance = Vector2.Distance(MiddlePos, CurrentLocation);
Distance *= 100;
Distance /= MathHelper.Clamp(Range, 0, 100);
Vector3 newColors = nColor.ToVector3();
nColor = new Color(
newColors.X,
newColors.Y,
newColors.Z,
Distance / 100);
/*SET COLOR*/
SetColorData[Destination] = nColor; // Add to array
#endregion
#region Shadow
#endregion
}
}
ScreenImage.SetData<Color>(SetColorData);
Images[curCount] = ScreenImage;
curCount++;
}
return Images[0]; // Temporarily returning the first image of the array.
}
}
As you can see, this is a slow and bad method. So I was wondering, is there a better way to get & set each pixel?
Thanks in advance, dotTutorials! =)
I think that job would be best done in a pixel shader.
You could create an Effect file that operates over one light at a time.XNA uses DX9 so you'll be limited to 128 constant registers, which I think you can use to squeeze up to three lights.
So you set your lightmap as a render target, loop through all the lights, set the constant data on your effect, render a render-target-sized quad and in your pixel shader compute your lighting equation.
In essence something like that:
// In LoadContent
RenderTarget2D lightmapRT = new RenderTarget2D(graphics.GraphicsDevice,
128,
128,
false, //No mip-mapping
SurfaceFormat.Color,
DepthFormat.Depth24);
// We now render to the lightmap in Render method
graphics.GraphicsDevice.SetRenderTarget(lightmapRT);
// Lightmap is black by default
graphics.GraphicsDevice.Clear(Color.Black);
// Use the sprite batch to draw quads with custom shader
spriteBatch.Begin(0, BlendState.Opaque, null, null, null, lightmapFx);
foreach (var light in lights)
{
// Pass the light parameters to the shader
lightmapFx.Parameters["Viewport"].SetValue(new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height));
lightmapFx.Parameters["Range"].SetValue(light.Range);
lightmapFx.Parameters["Intensity"].SetValue(light.Intensity);
lightmapFx.Parameters["LightColor"].SetValue(light.LightColor);
lightmapFx.Parameters["LightLocation"].SetValue(light.LightLocation);
// Render quad
spriteBatch.Draw(...);
}
spriteBatch.End();
And the FX file would look something like that:
float Range;
float Intensity;
float3 LightColor;
float2 LightLocation;
float2 Viewport;
struct VStoPS
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
VStoPS VS(in float4 color : COLOR0,
in float2 texCoord : TEXCOORD0,
in float4 position : POSITION0)
{
VStoPS vsout = (VStoPS)0;
// Half pixel offset for correct texel centering.
vsout.Position.xy -= 0.5;
// Viewport adjustment.
vsout.Position.xy = position.xy / Viewport;
vsout.Position.xy *= float2(2, -2);
vsout.Position.xy -= float2(1, -1);
// Pass texcoords as is
vsout.TexCoord = texCoord;
return vsout;
}
float4 PS(VStoPS psin)
{
// Do calculations here
// Here I just set it to white
return float4(1.0f, 1.0f, 1.0f, 1.0f);
}
technique Main
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}
Note that this non-tested code and probably full of errors. I leave it up to you to figure out what needs to go in the pixel shader.