How to know coloring is complete on the sprite? - unity3d

I am trying to make color game in that there are three object border, 3d cube, and a pointer(tube), here i'm painting a 3d cube with a pointer by changing its texture and put a sprite on the cube so i want to detect the condition while i almost complete the coloring inside the sprite border how can i do that in unity?
Like in this image
I want to know how to detect almost complete coloring within the boundry sprite.

To detect if the coloring inside the sprite border is almost complete, you can use a combination of Unity's built-in collision detection and image processing techniques. Something like this:
First create a sprite mask on the sprite border to mask the cube image. This will allow you to apply an effect to only the area inside the border.
Then sample the color of each pixel inside the border using the GetPixels method of the Texture2D class. Store these values in an array.
Calculate the average color of all the pixels in the array.
Compare the average color to a pre-defined threshold color to determine if the coloring is complete. If the average color is close enough to the threshold color, you can assume that the coloring is complete.
Repeat steps 2-4 in a loop while the player is painting the cube to continuously check if the coloring is complete.
Once the coloring is complete, you can trigger an event or perform some other action in your game.
using UnityEngine;
public class ColorDetection : MonoBehaviour
{
public SpriteRenderer spriteRenderer;
public Texture2D maskTexture;
public Color thresholdColor;
private void Update()
{
Color averageColor = GetAverageColor();
if (IsColorSimilar(averageColor, thresholdColor))
{
Debug.Log("Coloring is complete");
}
}
private Color GetAverageColor()
{
Texture2D texture = spriteRenderer.sprite.texture;
Rect spriteRect = spriteRenderer.sprite.rect;
Color[] maskPixels = maskTexture.GetPixels();
Color[] spritePixels = texture.GetPixels((int)spriteRect.x, (int)spriteRect.y, (int)spriteRect.width, (int)spriteRect.height);
int count = 0;
Color sum = Color.clear;
for (int i = 0; i < maskPixels.Length; i++)
{
if (maskPixels[i].a > 0)
{
count++;
sum += spritePixels[i];
}
}
return sum / count;
}
private bool IsColorSimilar(Color a, Color b)
{
float delta = 0.05f;
return Mathf.Abs(a.r - b.r) < delta && Mathf.Abs(a.g - b.g) < delta && Mathf.Abs(a.b - b.b) < delta;
}
}

Related

Best way to create a 2d top down race track procedurally

I am attempting to create a 2d top-down car racing game. This game will have a random road map each time the player plays the game. I have thought about doing this in two different ways: A tilemap, or just generate the roads by placing different prefabs (straight roads, turns, etc). I have decided to go with the prefab route.
The way I believe it should work is to have prefab square "tiles" which have their own colliders set on the edges so I can tell if a player goes off the track in which case they blow up. I would have a MapGenerator Script which will generate an initial random map by keeping track of the last tile placed (including its location and road type: left turn, straight, right, etc). This script will then keep adding onto the road randomly as the player gets closer and closer to the end which makes it an infinite road.
I just want to know if this is just not efficient or if I am thinking of this completely wrong.
Here are a couple of images showing my road tiles which I made in photoshop and then one prefab for a straight road (take note of the colliders on its edges).
A similar game to one I want to make is Sling Drift which I can provide the link if you want. I don't know the policy on adding links to forum chat.
Also, here is my code for the map generator:
//Type of tyle, types are normal (straight road or horizontal road) and turns
public enum MapTileType
{
NORMAL,
N_E,
N_W,
S_E,
S_W
}
//structure for holding the last tile location and its type.
public struct TypedTileLocation
{
public TypedTileLocation(Vector2 pos, MapTileType tyleType)
{
m_tileType = tyleType;
m_position = pos;
}
public Vector2 m_position;
public MapTileType m_tileType;
}
public class MapGenerator : MonoBehaviour
{
//Map Tiles
public GameObject m_roadTile;
public GameObject m_turnNorthWestTile;
//holds all the tiles made in the game
private List<GameObject> m_allTiles;
//Map Tile Widths and Height
private float m_roadTileWidth, m_roadTileHeight;
//Used for generating next tile
TypedTileLocation m_lastTilePlaced;
private void Awake()
{
//store the initial beginning tile location (0,0)
m_lastTilePlaced = new TypedTileLocation(new Vector2(0,0), MapTileType.NORMAL);
//set height and width of tiles
m_roadTileWidth = m_roadTile.GetComponent<Renderer>().bounds.size.x;
m_roadTileHeight = m_roadTile.GetComponent<Renderer>().bounds.size.y;
m_allTiles = new List<GameObject>();
}
// Start is called before the first frame update
void Start()
{
SetupMap();
}
void SetupMap()
{
//starting at the beginning, just put a few tiles in straight before any turns occur
for (int i = 0; i < 6; ++i)
{
GameObject newTempTile = Instantiate(m_roadTile, new Vector2(0, m_roadTileHeight * i), Quaternion.identity);
m_lastTilePlaced.m_tileType = MapTileType.NORMAL;
m_lastTilePlaced.m_position.x = newTempTile.transform.position.x;
m_lastTilePlaced.m_position.y = newTempTile.transform.position.y;
m_allTiles.Add(newTempTile);
}
//now lets create a starter map of 100 road tiles (including turns and straigt-aways)
for (int i = 0; i < 100; ++i)
{
//first check if its time to create a turn. Maybe I'll randomly choose to either create a turn or not here
//draw either turn or straight road, if the tile was a turn decide which direction we are now going (N, W, E, S).
//this helps us determine which turns we can take next
//repeat this process.
}
}
void GenerateMoreMap()
{
//this will generate more map onto the already existing road and then will delete some of the others
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
}
}
Thanks!
Have you tried splines? They let you make curvy paths like race tracks easily. If not, here is a video that might help: https://www.youtube.com/watch?v=7j_BNf9s0jM.

Unity 2D - Keep Player Object in Boundary of it's Parent Panel

In Unity i have a UI Panel which has a player object (an UI Image object).
I moving player object into planel with user inputs (keyboard or touch)
I can't keep player object in it's parent panel,
Please check below image, I want to keep player inside of Red Panel
Here is my Tried Code
public Camera MainCamera; //be sure to assign this in the inspector to your main camera
private Vector2 screenBounds;
private float objectWidth;
private float objectHeight;
private RectTransform pnlBackgroundTransform;
private void Start()
{
pnlBackgroundTransform = GameObject.Find("PnlBackground").GetComponent<RectTransform>();
screenBounds = MainCamera.ScreenToWorldPoint(new Vector3(pnlBackgroundTransform.rect.width , pnlBackgroundTransform.rect.height , MainCamera.transform.position.z));
objectWidth = transform.GetComponent<SpriteRenderer>().bounds.extents.x; //extents = size of width / 2
objectHeight = transform.GetComponent<SpriteRenderer>().bounds.extents.y; //extents = size of height / 2
}
void LateUpdate()
{
Vector3 viewPos = transform.position;
viewPos.x = Mathf.Clamp(viewPos.x, screenBounds.x * -1 + objectWidth, screenBounds.x - objectWidth);
viewPos.y = Mathf.Clamp(viewPos.y, screenBounds.y * -1 + objectHeight, screenBounds.y - objectHeight);
Debug.Log(screenBounds);
Debug.Log(viewPos);
transform.position = viewPos;
}
I'd say it's not very usual having the player implemented as a UI element, and instead you should be implementing it outside the UI/Canvas system.
The UI/Canvas system uses a set of rules of placing and scaling to deal with responsive design. You have at least 4 values (excluding rotation) to place something on the screen: anchor, pivot, position and scale.
For example: if you want to create a square you can either set it's size in absolute pixel values or relative values (to parent). If you're using absolute values, your UI Scale Mode, defined on the Canvas object, should affect the visual results.
This means the UI/Canvas is for elements that should adapt to the screen, such as buttons, dialogs, labels, etc. Taking advantage of device parameters to improve the UX.
Outside the UI/Canvas system, things are directly based on Linear Algebra: you have a 3D vector space (a "World") where everything exists with an absolute size and position. Then, your Camera stretches and twists the whole world to match what your current perspective. That means your object will always have the same size, regardless of screen size.
Now, assuming you have a very specific reason to implement your game into UI, there are a few ways you can do it. I'll assume you're using absolute values. Please note all the units used here are pixels, and the effect should be different for devices with different resolutions and sensible to the UI Scale Mode parameter. Also, please note I've set both anchors min and max to (0,0), the bottom left corner (default is screen center, (0.5,0.5)), in order to avoid negative coordinates.
The following script is attached to the player's UI Image.
public class UIMovementController : MonoBehaviour
{
public float speed = 5.0f;
new private RectTransform transform;
private Rect canvasRect;
private void Start()
{
transform = GetComponent<RectTransform>();
canvasRect = GetComponentInParent<Canvas>().pixelRect;
}
void Update()
{
// Keyboard Input (Arrows)
Vector2 move = new Vector2(0,0);
if (Input.GetKey(KeyCode.UpArrow)) { move.y += speed; }
if (Input.GetKey(KeyCode.DownArrow)) { move.y -= speed; }
if (Input.GetKey(KeyCode.LeftArrow)) { move.x -= speed; }
if (Input.GetKey(KeyCode.RightArrow)) { move.x += speed; }
transform.anchoredPosition += move;
// Position clamping
Vector2 clamped = transform.anchoredPosition;
clamped.x = Mathf.Clamp(clamped.x, transform.rect.width / 2, canvasRect.width - transform.rect.width / 2);
clamped.y = Mathf.Clamp(clamped.y, transform.rect.height / 2, canvasRect.height - transform.rect.height / 2);
transform.anchoredPosition = clamped;
}
}

Changing the color of a gameobject shows as white not the colour requested

I have a set of gameobjects (simple cubes). I can set their initial colour when instantiating them. However when I try and change the colour by code, the object in the game view and inspector show as white, but in the colour picker show the correct colour!
There is a single directional light (the default one).
IEnumerator ColourChange()
{
Color targetColour = new Color(Random.Range(0, 255), Random.Range(0, 255), Random.Range(0, 255));
Debug.Log("color = " + targetColour);
for (int x = 0; x < CreateCubeGrid.GRIDSIZE; x++) {
for (int z = 0; z < CreateCubeGrid.GRIDSIZE; z++) {
CreateCubeGrid.cubeGrid[x,z].GetComponent<Renderer>().material.color = targetColour;
}
yield return new WaitForSeconds (0.05f);
}
}
Colours are 0 to 1 not 0 to 255.
Use Color32 if you want to use 0-255 values.
Color32 Documentation
In order to change the material's color, you must tell it exactly what color you're trying to change, by using Shader.Find("_YourColor") (Emission, Albedo, etc).
An approach that should work for materials using Standard Shader can be seen below:
private void ChangeColor(Color color)
{
//Fetch the Renderer from the GameObject.
Renderer rend = GetComponent<Renderer>();
//Find and set the main Color ("_Color") of the Material to the new one
rend.material.shader = Shader.Find("_Color");
rend.material.SetColor("_Color", color);
}
You can read more about changing a Material's color at Unity's Documentation.

Is there any way to make some rectangular areas of a mask to be transparent?

I am using UGUI to make a Novice guide to guide people to play my game.
And need the whole UI be mask, but some rectangular areas to be lighted.
How to do?
Create a new gameobject and add a image component to it. Create a image with transparent areas where you want your ui to be visible. Assign that image to the image component. Then add a mask component
Put your other gui elements inside this gameobject so that is could overlap and hide everything except transparent areas. Here is the picture of demo setup.
IMHO, what you want to achieve is not easy to be done perfectly in Unity. Here is my personal solution:
I put a black panel below every other GUI, so that it darkens my entire screen.
I put an empty game object called BrightRoot below the panel, so that everything under BrightRoot will float over and "brightened".
In my tutorial script, I add function to look for a UI game object by name and change its parent to the BrightRoot. In example:
// To brighten the object
GameObject button = GameObject.Find("PlayButton");
Tansform oldParent = button.transform.parent;
button.transform.SetParent(BrightRoot, true);
// To darken it again
button.transform.SetParent(oldParent, true);
The perfect solution would be to write a UI shader that darken any pixel outside some rectangles and brighten the inside. Then set that shader to all UI objects.
Editted:
This just another easy method, using UI Vertex effect. Just need to implement IsPointInsideClipRect, put this component to your UI objects, and set the rectangles list:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
[AddComponentMenu("UI/Effects/Clip")]
public class Clip : BaseVertexEffect
{
// We need list of rectangles here - can be an array of RectTransform
public RectTransform[] ClipRects;
public override void ModifyVertices(List<UIVertex> vertexList)
{
if (!IsActive())
{
return;
}
bool isClipped = true;
for (int i = 0; i < count; i++)
{
UIVertex uiVertex = vertexList[i];
foreach (RectTransform rect in ClipRects)
{
if (IsPointInsideClipRect(rect, uiVertex.position))
{
isClipped = false;
break;
}
}
}
Color32 color = isClipped ? new Color32(0.5f, 0.5f, 0.5f, 0.5f) : new Color(1.0f, 1.0f, 1.0f, 1.0f);
for (int i = 0; i < count; i++)
{
UIVertex uiVertex = vertexList[i];
uiVertex.color = color;
}
}
private static bool IsPointInsideClipRect(RectTransform rect, Vector3 position)
{
// ...
}
}

Color submeshes in unity3d

I have a main_mesh that has 10 submeshes, I wonder how I can change the color of of these submeshes to a different color (e.g submesh1 will have a red color, submesh2 will have a blue color,...etc). Any advise please?
UPDATE:
This is how I'm getting my mesh which has 10 submeshes:
SkinnedMeshRenderer smr = gameobject1.GetComponent<SkinnedMeshRenderer>();
Mesh main_mesh = smr.sharedMesh;
SkinnedMeshRenderer smr = gameobject1.GetComponent<SkinnedMeshRenderer>();
Mesh main_mesh = smr.sharedMesh;
smr.materials[0].color = Color.red; // Change submesh1 to red color
smr.materials[1].color = Color.blue; // Change submesh2 to blue color
...
smr.materials[n].color = ... // Change submesh n to whatever color
Since you've added the tag Unityscript i'll assume that you want to be able to change submeshes inside a script.
Assignation as parameter
The first solution would be to have add a public parameter to you script that would be an array of Mesh. Then assign manually each submesh to the array through the inspector. Now you can access the material of each mesh and change it's color.
public class MyScript : MonoBehaviour {
public Mesh[] submeshes;
// Update is called once per frame
void Update () {
for (int i = 0; submeshes[i]; i++) {
// Return the first material of the mesh renderer, use .materials if multiple Material are applied
submeshes[i].renderer.material.color = Color.red;
}
}
}
Note that I used Mesh as type for my array, but you could directly use Material if you only want to change the color.
Also, if submeshes have the exact same material, It'll change the color for all submeshes, not just one. You need to have one material per mesh.
While this solution is not viable if your number of submeshes change dynamically, this solution is pretty simple and straighforwarded.
Use children
Instead of assigning every submeshes manually, you can dynamically change the color by accessing children
public class MyScript : MonoBehaviour {
public Mesh myObject;
// Update is called once per frame
void Update () {
Material[] array = myObject.GetComponentsInChildren<Material>();
for (int i = 0; array[i]; i++) {
array[i].color = Color.red;
}
}
}
This solution allows you to have N material assigned to your submeshes.
Here is the documentation for GetComponentsInChildren
Edit
Short answer If you want a specific answer to your case, it depends on the materials and shaders assigned to your skinned Mesh Renderer because they can override or alter your childrens' materials. If not, the below code should work.
SkinnedMeshRenderer smr = gameobject1.GetComponent<SkinnedMeshRenderer>();
Mesh main_mesh = smr.sharedMesh;
Mesh[] submeshes = main_mesh.GetComponentsInChildren<Mesh>();
for (int i = 0; submeshes[i]; i++) {
// If your submesh already have a material, remove the first line below !
submeshes[i].renderer.material = new Material(Shader.Find("Diffuse"));
submeshes[i].renderer.material.color = Color.red;
}
This solution create a new material for each submesh, which is quite brutal.
In the inspector, you should assign one material to each submeshes and then use always the same material with different colors.
In case it doesn't work
When you want to change the color of one specific mesh, this mesh needs to have his own material. The color of the mesh will depends on this materials and it's properties (shaders, textures, colors).
With a Skinned Mesh Renderer, you generally use Diffuse Material with textures to apply colors to one complex mesh. In some case, this mesh apply the color to it's childrens.
When using a Skinned Mesh Renderer, you usually use a UV texture. This particular texture is created based on your 3D object and is used to apply multiple color on it (sometimes also it's childrens). Here is a simple example of UV texture and here is a more complex example.
Note that, as a mesh Renderer, a skinned mesh renderer can have multiple materials which make the situation more complex but the principle remains the same.
SkinnedMeshRenderer smr = gameobject1.GetComponent<SkinnedMeshRenderer>();
Mesh main_mesh = smr.sharedMesh;
With your code if main_mesh use a UV texture, you have two solutions
Remove the texture then apply a color to it's children
Create a specific UV texture which apply colors as you want.