paint polygon collider during gameplay - unity3d

I'm working on a 2D game in which some objects have a triangular vision, done with a polygon collider
Is it possible to paint this collider so that it is visible during the gameplay?

Assuming your mean PolygonCollider2D and that it always has 3 points in the correct order I guess you could do something like
using System.Linq;
...
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter), typeof(PolygonCollider2D))]
public class MeshCreator : MonoBehaviour
{
void Awake ()
{
var points = GetComponent<PolygonCollider2D>().points;
var meshFilter = GetComponent<MeshFilter>();
var mesh = new Mesh();
// Just a shorthand for something like
//var list = new List<Vector3>();
//foreach(var p in points)
//{
// list.Add(p);
//}
//mesh.vertices = list.ToArray();
mesh.vertices = points.Select(p -> (Vector3) p).ToArray();
// Here create two triangles (each 3 vertices)
// just because I don't know if you have the points in the correct order
mesh.triangles = new int[]{0,1,2,0,2,1};
meshFilter.mesh = mesh;
}
}
and put this component next to the PolygonCollider2D. It will then replace this Object's mesh with the triangle.
The material you can already set beforehand in the MeshRenderer.
For getting this overlap area the simplest solution would be Semi-Transparent materials. Otherwise you might need a special shader.

Related

Lightning and display issues with Unity CanvasRenderer

My goal is to draw simple, colored meshes on a Canvas in Unity. I set up a Canvas and chose Render Mode World Space. I then added a GameObject, set its RectTransform, added a CanvasRenderer (later also a MeshFilter and MeshRenderer) and a script to create my mesh. Creating the mesh works fine but i have to major problems. The first is that the lightning does not work correctly when using CanvasRenderer instead of MeshRenderer:
My second problem is that no matter the order in the hierarchy as soon as i add a second object to my canvas (e.g. an image with the color white) my custom mesh and its color are no longer visible. Only the white image.
Here is the script i used. The last few lines are comments or not based on what Renderer i tried.
using UnityEngine;
public class MeshCanvas : MonoBehaviour
{
public Material material;
Mesh mesh;
Vector3[] vertices;
int[] triangles;
float canvasHeigt;
float canvasWidth;
private void Update()
{
canvasHeigt = FindObjectOfType<Canvas>().GetComponent<RectTransform>().rect.height;
canvasWidth = FindObjectOfType<Canvas>().GetComponent<RectTransform>().rect.width;
vertices = new Vector3[]
{
new Vector3(0, 0),
new Vector3(0, canvasHeigt),
new Vector3(canvasWidth, canvasHeigt),
new Vector3(canvasWidth, 0)
};
triangles = new int[]
{
0, 1, 2,
2, 3, 0
};
mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
/*
this.GetComponent<MeshFilter>().mesh = mesh;
this.GetComponent<MeshRenderer>().material = material;
*/
CanvasRenderer cr = this.GetComponent<CanvasRenderer>();
cr.SetColor(Color.black);
cr.materialCount = 1;
cr.SetMaterial(material, 0);
cr.SetMesh(mesh);
}
}
For problem 1: If you just want a normal unlit UI element, you should be using the UI/Default material, not a Standard material.
If you do want lighting, In your Canvas properties, set 'Additional Shader Channels' to 'Everything'. Otherwise lighting will not be calculated correctly. You can use a Standard material in this case, or one of the UI/Lit materials.
As for problem 2, I'm not sure. Unity batches all UI elements together for performance reasons, so maybe setting the mesh directly on the CanvasRenderer is breaking the batching, or maybe it's to do with the material you're using.
If you just want to be able to add your own custom 2D shapes to a Canvas, the better way is to make a class that derives from Graphic or MaskableGraphic and override the OnPopulateMesh method to provide your custom mesh vertices.
The advantages here are that it will behave like any other UI element with all the same parameters etc. and you can add it to the Create GameObject menus to easily add it to your scene.

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.

Why Does My Mesh Combiner Script Make Child Objects Invisible And Change Their Placements?

To further optimize my game, I wanted to combine the meshes of the hallways so that the frame rate would be higher. the code is intended to take all of the children inside of an empty game object(titled walls, floors, etc.) and combine their meshes into one. However, whenever I ran the script, all of the child objects would appear in completely random positions and were invisible. How can I make so that the objects all appeared at their original position and were visible?
How I set up the code is that I would place any repeated objects in an empty gameObject to easily categorize them (titled as Walls, Floors). Afterwards, I would assign the script to the empty gameObject and expect every repeated object in the empty gameObject to combine.
Here's an example:
Here's The Code:
using UnityEngine;
using System.Collections;
// Copy meshes from children into the parent's Mesh.
// CombineInstance stores the list of meshes. These are combined
// and assigned to the attached Mesh.
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class CombineMesh : MonoBehaviour
{
void Update()
{
if(Input.GetKeyDown(KeyCode.J))
{
CombineMeshes();
}
}
void CombineMeshes()
{
Quaternion oldRot = transform.rotation;
Vector3 oldPos = transform.position;
transform.rotation = Quaternion.identity;
transform.position = Vector3.zero;
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
int i = 0;
while (i < meshFilters.Length)
{
combine[i].mesh = meshFilters[i].sharedMesh;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
meshFilters[i].gameObject.SetActive(false);
i++;
}
var MeshFilter = transform.GetComponentInChildren<MeshFilter>();
MeshFilter.mesh = new Mesh();
MeshFilter.mesh.CombineMeshes(combine);
GetComponentInChildren<MeshCollider>().sharedMesh = MeshFilter.mesh;
transform.gameObject.SetActive(true);
transform.rotation = oldRot;
transform.position = oldPos;
}
}
Answering late in case anyone happens to be searching for this information.
Unity's documentation is uncharacteristically unhelpful, as is the internet in general.
Lengthy Explanation
Kayra's Code Corrected
Working Code: Helper Functions
Working Code: CombineMeshes
Lengthy Explanation:
Two things to keep in mind are
Meshes are defined via a Vector3[](see meshFilter.mesh.vertices), describing the coordinates of each vertex relative to its meshFilter's transform (so local space). This is very important later on.
There is no black magic involved - math is not black magic ;)
First, you should understand "transform.localToWorldMatrix" and "transform.worldToLocalMatrix"
These are very misleading names - all these Matrix4x4 actually do in Unity, is describe the linear transformation between
{position: Vector3.zero, rotation: Quaternion.identity, scale: Vector3.one} and
the given transform's {position, rotation, scale}.
Its telling you by how much to move in which direction, by how much to rotate in which direction, and by how much to scale.
In fact: transform.localToWorldMatrix == transform.worldToLocalMatrix.inverse -- they're the exact same thing, just flipped around.
See "Khan Academy" or "3blue1brown" on YouTube for great explanations of Linear Transformation
We usually don't use Matrices in Unity, because we can access/modify the position, rotation, and scale directly -- for us, it's just a different way of storing the same information.
I wrote some working code to visualise what's actually happening in Unity:
/// Demonstration of how transform.localToWorldMatrix works - very important for understanding CombineInstance.transform
/// Place two 3D objects in your scene, attach this script somewhere, and assign the 3D Objects to the public variables
/// This script will auto-run inside a coroutine 2 seconds after Start(), because I use the InputSystem package and maybe you still use if(Input.getKeyDown()).
/// It's the easiest way I know of to guarantee working code in your scene
public class StackOverflowExample : MonoBehaviour
{
public Transform parent;
public Transform child;
private IEnumerator exampleMethod;
/// <summary>
/// Just setting some conditions, in case you try out this code
/// </summary>
private void Start()
{
parent.position = new Vector3(5f, 0f, 3f);
child.parent = parent;
child.localPosition = new Vector3(2f, 0f, 2f);
//Only doing this because I use the InputSystem package, and others maybe don't (yet...)
exampleMethod = TransformMatrixExample();
StartCoroutine(exampleMethod);
}
//Only doing this because I use the InputSystem package, and others maybe don't (yet...)
private IEnumerator TransformMatrixExample()
{
//wait for 2 seconds just because...
yield return new WaitForSeconds(2f);
//parent.position = parent.position - parent.localToWorldMatrix.GetPosition();
// Commented out on purpose. Would result in 0,0,0 parent.position, because parent has no gameObject parent...
//and therefore break the demonstration:
child.position = child.position - parent.localToWorldMatrix.GetPosition();
// child.localPosition (see the inspector) now states (-3f,0f,-1f)
// however, child.Position (just unparent the gameObject in the inspector) is now (2f,0f,2f)
}
}
When you tell each CombineInstance what Matrix4x4 to use in
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
you are describing by how much to offset each soon-to-be-combined mesh's vertices' coordinates before combining it.
Once again, we usually access/modify the position, rotation, and scale directly - Mesh.CombineMeshes() uses a 4x4 matrix.
Here Matrix4x4.GetPosition() returns the vector leading from Vector3.zero to parent.position - which is contained inside that matrix
We are effectively moving the child object to the place it would be if the parent were at coordinates (0f,0f,0f).
The same thing happens with rotation and scale.
In the while-loop in your code, there's a statement:
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
The problem with this is that if the parent object (or the mesh you want to merge into) is not positioned at Vector3.zero, all the meshes will still pretend otherwise and offset themselves by the wrong amount. That is why you have to move the parent.transform.position to vector3.zero before assigning to the CombineInstances[].
So in my code example above:
parent.position = new Vector3(5f, 0f, 3f);
child.localPosition = new Vector3(2f, 0f, 2f);
If I first move the parent to V3.zero, the results given by transform.localToWorldMatrix.GetDistance():
combine[i].transform = meshFilters[i].transform.localToWorldMatrix :
parent's offset: V3(0f,0f,0f) --> parent.position
child's offset: V3(2f,0f,2f) --> child.position
This works because now, the vector from child.position to V3.zero == vector from child.position to parent.position.
If I were to use the transforms without first moving the parent to V3.zero, I would get the following results:
combine[i].transform = meshFilters[i].transform.localToWorldMatrix :
parent's offset: V3(5f, 0f, 3f) --> parent.position
child's offset: V3(7f, 0f, 5f) --> parent.position + child.localPosition
Because transform.localToWorldMatrix returns the vector from zero to that transform.position.
Remember Point1 of things to remember?
Vertex coordinates are defined in local space relative to their meshFilter's transform.
In other words:
all of parent mesh's vertices will offset by an additional V3(5f,0f,3f) --> parent.position
all of child mesh's vertices will be offset by an additional V3(7f,0f,5f) --> parent.position + child.localPosition
The statement is effectivley telling Unity the following (pseudocode):
foreach (Vector3 v in meshFilters[i].mesh.vertices)
{
v += meshFilters[i].localToWorldMatrix.GetPosition();
//and now append my vertex to the new MeshFilter's mesh...
//in other workds: Pretend that my parent is at 0,0,0 and I'm in the right spot already
}
The exact same principle holds true for rotation and scale. You can probably decipher that from the working code later on.
kayra yorulmaz's Corrected Code
I'm assuming that CombineMesh.cs is attached to the "Floors" gameObject,
and that "Floors" transform.rotation = (0f,0f,0f).
The Quaternion operations will be explained shortly.
So the kayra yorulmaz's code would have to be written as follows:
using UnityEngine;
// Copy meshes from children into the parent's Mesh.
// CombineInstance stores the list of meshes. These are combined
// and assigned to the attached Mesh.
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))] //because otherwise line 72 might throw an exception...
public class CombineMesh_Corrected : MonoBehaviour
{
void Update()
{
if(Input.GetKeyDown(KeyCode.J))
{
CombineMeshes();
}
}
void CombineMeshes()
{
Vector3 transformOffset = transform.position;
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
int i = 0;
while (i < meshFilters.Length)
{
Quaternion rotationOffset = Quaternion.FromToRotation(transform.eulerAngles, meshFilters[i].transform.eulerAngles);
meshFilters[i].transform.position -= transformOffset;
meshFilters[i].transform.rotation = Quaternion.Euler(meshFilters[i].transform.eulerAngles) * Quaternion.Inverse(rotationOffset);
combine[i].mesh = meshFilters[i].sharedMesh;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
meshFilters[i].gameObject.SetActive(false);
//we already stored the 4x4Matrix in combine[i].transform, so it's safe to change back now
meshFilters[i].transform.position += transformOffset;
meshFilters[i].transform.rotation *= rotationOffset;
i++;
}
MeshFilter meshFilter = transform.GetComponent<MeshFilter>();
meshFilter.mesh = new Mesh();
meshFilter.mesh.CombineMeshes(combine);
GetComponentInChildren<MeshCollider>().sharedMesh = meshFilter.mesh;
transform.gameObject.SetActive(true);
}
}
Working Code Example
However, it you create your own Matrix4x4 to describe the necessary linear transformation, you don't have to touch the gameObject's transforms at all.
Remember that Vertices (and therefore the meshes you're combining) are described relative to the meshFilter's transform.
So if we create a Matrix4x4 for each child meshFilter, describing how that child.meshFilter.transform is located relative to parent.meshFilter.transform, we can tell Unity where to place the vertices for the combined mesh:
So here's is the code based off what I just wrote for my own project.
Necessary Helping Functions
using UnityEngine;
public static class StackoverflowHelpers
{
/// <summary>
/// Returns the difference between quaterions, treated as local rotations because of the order...
/// https://answers.unity.com/questions/810579/quaternion-multiplication-order.html
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public static Quaternion FromTo(Quaternion from, Quaternion to)
{
return Quaternion.Inverse(from) * to;
}
public static Quaternion Add(Quaternion start, Quaternion difference)
{
return start * difference;
}
public static Quaternion Subtract(Quaternion start, Quaternion difference)
{
return start * Quaternion.Inverse(difference);
}
public static Vector3 RatioBetween( Vector3 fromScale, Vector3 toScale)
{
return new Vector3(
toScale.x/fromScale.x,
toScale.y/fromScale.y,
toScale.z/fromScale.z );
}
}
Actual CombineMesh Method
using System.Collections.Generic;
using UnityEngine;
public static class StackOverflow_CombineMesh
{
public static void Simple(List<MeshFilter> _meshFilters, bool _deleteOriginals = true)
{
CombineInstance[] combineInstances = new CombineInstance[_meshFilters.Count];
Transform parent = _meshFilters[0].transform;
for (int i = 0; i < _meshFilters.Count; i++)
{
/// set up the matrix describing the step from the parent mesh to the child mesh
Transform child = _meshFilters[i].transform;
Vector3 posOffset = child.position - parent.position;
posOffset.x *= 1/parent.localScale.x;
posOffset.y *= 1/parent.localScale.y;
posOffset.z *= 1/parent.localScale.z;
Matrix4x4 ParentToChildMatrix = Matrix4x4.TRS(
posOffset,
StackoverflowHelpers.FromTo(parent.rotation, child.rotation),
StackoverflowHelpers.RatioBetween(parent.lossyScale, child.lossyScale));
combineInstances[i].mesh = _meshFilters[i].mesh;
combineInstances[i].transform = ParentToChildMatrix;
child.gameObject.SetActive(false);
}
_meshFilters[0].mesh = new Mesh();
_meshFilters[0].mesh.CombineMeshes(combineInstances, true, true);
_meshFilters[0].gameObject.SetActive(true);
}
Bear in mind that meshes in unity have a maximum number of vertices (65,535 to be exact) - if you cross that limit your mesh won't render properly after all.
Have fun, keep learning!
Gecko
I think this is a common issue with Unity mesh combine, try changing this line (assuming this is all on the Parent game object):
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
to this:
combine[i].transform = meshFilters[i].transform.localToWorldMatrix * transform.worldToLocalMatrix
where transform.worldToLocalMatrix is the parent object. You could also try something like:
combine[i].transform = meshFilters[i].transform.localToWorldMatrix * meshFilters[i].transform.parent.transform.worldToLocalMatrix;
Depends on how you have it set up

Unity3D Procedural Generation : Grass?

Since yesterday I have been trying to create a procedural infinite world generation and for my new game and for the moment everything goes pretty well, except that I can't figure out how to add grass to my mesh. My world is generated using procedurally generated meshes of different levels of details. I created my terrain based on 3 "layers", the ground layer, a hills layer and a mountains layer. The texture on top of my terrain is entirely managed by my custom shader (which is very primitive for the moment because there is no blending).
So I really an't figure out how to add grass to such a world. I tried different technique with grass GameObjects, Planes etc but they all not worked... If someone can help me it could be awesome! I can explain you how I manage my vegetation script. I call the GenerateVegetation() function on the chunk load and I just randomly place the trees on top of the terrain using a raycasting to get the point and the normal (I check the slope with the y axis of the vertex's normal).
Here is my vegetation script (very basic for the moment I just done it now)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VegetationGeneration {
public static IEnumerator Generate(Vector2 chunk) {
// setting up the Vegetation holder child of the global chunk (hierachy);
Transform vegetationChunk = new GameObject ("Vegetation").transform;
vegetationChunk.parent = GameSettings.chunks [chunk];
// Get all the vegetation instances and loop start iterating the placement
foreach(Vegetation vegetation in MapPreview.instance.vegetation) {
float amount = Random.Range (vegetation.amount.x, vegetation.amount.y);
for(int i = 0; i < amount; i++) {
// Splitting the generation calculations each 15 trees (Only while playtime) (Performance)
if (!Application.isEditor && i % 15 == 0) yield return null;
// Getting a random position in the chunk
float chunkDiameter = GameSettings.worldChunkSize / 2f;
Vector3 position = new Vector3 (Random.Range (-chunkDiameter, chunkDiameter), 0f, Random.Range (-chunkDiameter, chunkDiameter)) + new Vector3(chunk.x, 0f, chunk.y);
// Shooting a raycast down to get the hit point and hit normal
// Check the slope angle
RaycastHit hit;
float slope;
if(Physics.Raycast(new Ray(position + Vector3.up * 100f, Vector3.down), out hit, 200f, MapPreview.instance.groundLayer) && (slope = hit.normal.y) > vegetation.maxSlope) {
// possible to spawn the vegetation at the given point
// Spawning the entity with the vegetation chunk parent
GameObject prefab = vegetation.vegetation [Random.Range (0, vegetation.vegetation.Length)];
GameObject entity = GameObject.Instantiate (prefab, vegetationChunk);
// setting parameters
entity.transform.position = hit.point;
entity.transform.localEulerAngles += new Vector3 (0f, Random.Range (0f, 360f), 0f);
// Getting a random scale and converting it to a uniform vector
float scale = Random.Range (vegetation.scale.x, vegetation.scale.y);
entity.transform.localScale = Vector3.one * scale;
// Putting the vegetation into the ground to avoit flying trees
entity.transform.position -= Vector3.up * vegetation.depth.Evaluate (slope);
// Set to a different layre than the ground to avoid trees over other trees (Raycasting)
SetLayer (entity.transform);
}
}
}
}
// A simple Recusrive function that will loop thru every child of the root
// TRansform and apply the right layer to it
static void SetLayer(Transform parent) {
parent.gameObject.layer = LayerMask.NameToLayer (MapPreview.instance.vegetationLayer);
foreach(Transform child in parent) SetLayer (child);
}
}
// Vegetation Object
[System.Serializable]
public class Vegetation {
public string name;
[Tooltip("Amount of vegetation per generated chunk (does not represent the real amount because they won't be placed if the place is not survivable)")]
public Vector2 amount = new Vector2(50,70);
public GameObject[] vegetation;
public Vector2 scale;
[Range(0f,1f)]
public float maxSlope = 0.7f;
public AnimationCurve depth = new AnimationCurve(new Keyframe[] { new Keyframe(0f, 0.05f), new Keyframe(1f, 0.25f) });
}
I would really appreciate some insight.

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.