Unity3d sort of 2D array in Inspector - c#

I have an object which has for example 5 meshes where I would like to change the color in runtime. But every mesh has different colors to change. For example mesh 1 can be changed to blue, red and orange and mesh 2 can be changed to orange, yellow,green,purple and white.
I would like to have a c# script where I have an array with all 5 meshes. And for each array I would like to have another array appear in the inspector, where I can put in the different colors.
This is no problem if the mesh count would always be 5. But I want it to be flexible. For example, another object only has 3 meshes than I only need 3 color arrays instead of 5.
I have never worked with Editor Scripts, but maybe there is a easy workaround for that?
I am working with Unity 5.3
Thank you.

public class Test: MonoBehaviour
{
public Models [] models;
private int index = 0;
[SerializeField] private MeshFilter meshFilter = null;
[SerializeField] private Renderer rend = null;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
if(++index == models.Length)index = 0;
meshFilter.mesh = models[index].mesh;
int currIndex = models[index].currentIndex;
rend.material = models[index].materials[currIndex];
}
if(Input.GetKeyDown(KeyCode.A))
{
if(++models[index].currentIndex == models[index].materials.Length)models[index].currentIndex = 0;
int currIndex = models[index].currentIndex;
rend.material = models[index].materials[currIndex];
}
}
}
[System.Serializable]
public class Models{
public Mesh mesh;
public int currentIndex;
public Material[]materials;
}
This will show the Models array in your inspector. You can give a size for it, then each element opens up a mesh slot and an array of materials. Give it a size and you can start dragging materials.
I would suggest to add 2 items in the array, then add two meshes (Unity has some default ones like sphere and cube). Then create some basic materials of colors and add them to the materials arrays of each object.
Then you can run and press Space to change mesh and A to change color. You just need to use your own after that.

Related

Unity astar pathfinding project - how to pick a random position from the grid?

I bought astarpathfinding project pro recently.
I am making enemy ai and I want it to move randomly before it finds its target.
My project is in 2d.
How do I pick a random position on the grid?
if you can, can you show me some example of it?
Not really sure about the plugin that you bought, but If you are using tilemaps, you could iterate over every tile in each tilemap in your grid(only once on level loading) and generate a random number of x and y to choose a valid tile position from and make the Ai move there using the A* algorithm.
Edit: May not be the best way to write this, but this is how I iterated over them in the past, you can add or change things in it for your future needs if you want.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
public class TileMapCollision : MonoBehaviour
{
[SerializeField] private GameObject gridObject;
private TileBase tile;
public List<Vector3> StoredStaticSolidTilePositions;
private void Awake()
{
TileSearch();
}
private void TileSearch()
{
List<Tilemap> tilemaps = gridObject.GetComponentsInChildren<Tilemap>().ToList();
foreach (Tilemap tilemap in tilemaps)
{
tilemap.CompressBounds();
BoundsInt TilemapBounds = tilemap.cellBounds;
for (int x = TilemapBounds.xMin; x < TilemapBounds.xMax; x++)
{
for (int y = TilemapBounds.yMin; y < TilemapBounds.yMax; y++)
{
Vector3Int localPos = new Vector3Int(x,y, (int)tilemap.transform.position.z);
Vector3 worldPos = tilemap.CellToWorld(localPos);
if (tilemap.HasTile(localPos))
{
if (tilemap.CompareTag("Solid"))
{
StoredStaticSolidTilePositions.Add(worldPos);
}
}
}
}
}
}
}
I don't know what tool you are using or how exactly your "grid" looks like but from the comments it sounds like you have a TileMap.
In general for picking a random number from a value there is Random.Range. (Note that the second (upper) parameter is exclusive for int.)
Then you can get TileMap.size and simply generate a position from three random indices within the TileMap's dimensions
var gridSize = tilemap.size;
var randomPos = new Vector3Int(Random.Range(0, gridSize.x), Random.Range(0, gridSize.y), Random.Range(0, gridSize.z));
var randomTile = tilemap.GetTile(randomPos);

Having trouble creating a duplicate of a game object with input.getkeydown(KeyCode.Space) in C# and Unity

kinda new to coding so help would be appreciated. I'm trying to duplicate this GameObject "cube" in unity and I'm having trouble with it. what im trying to do is duplicate the cube and get it to stack on top of each other over and over.
I know if i got this to work it would duplicte it in the same postion so you would only see it duplicate in the higharchy.
using System.Collections;
using UnityEngine;
public class cube : MonoBehaviour
{
public GameObject cube1;
public void update()
if(input.getKeyDown(KeyCode.Space))
{
instantiate cube1;
}
}
I assume you know the height of the cube, you are working with. In unity the default height is 1.0f(For the primitive cube).
Btw if your code is a pseudo code then its okey but if not, you need more training before writing such scripts, even tho this type of script is extremely easy to write.
(ps: i wrote this script in notepad++ hope it compiles :/)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// We start classes with capital letters in c# its a convention :)
public class Cube : MonoBehaviour
{
// Same applies to public class fields & Properties
// Marking a MonoBehaviour field as public will allow you to directly assign values to it
// inside the editor
public GameObject OriginalCube;
// Same can be achieved with private fields using the serializefield attribute
[SerializeField]
private float cubeHeight = 1.0f;
// In case you would like to store the duplicated cubes
public List<GameObject> Cubes = new List<GameObject>();
private void Awake()
{
// Adding the first cube to the list, i assume your cube is already in the scene
Cubes.Add(OriginalCube);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
// We instantiate a new cube and add it to the list
Cubes.Add(Instantiate(Cubes[Cubes.Count - 1]);
// We ask the previous cube position (the one we copied)
Vector3 previousCubePosition = Cubes[Cubes.Count - 2].transform.position;
// then we assign a new position to our cube raised by "1 unit" on the y axis which is the up axis in unity
Cubes[Cubes.Count - 1].transform.position =
new Vector3(previousCubePosition.x, previousCubePosition.y + cubeHeight, previousCubePosition.z);
}
}
}
One way you can have your goal achieved, is to add to your newly instantiated object's y position a constant amount. This constant amount will be increased each time you create a new duplicate of your object.
public GameObject cube1;
private int instantiateCounter = 0;
public float PULL_UP_AMOUNT = 30f;
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
instantiateCounter++;
GameObject newCube = Instantiate(cube1);
newCube.transform.position = new Vector3(cube1.transform.position.x, cube1.transform.position.y + instantiateCounter * PULL_UP_AMOUNT, cube1.transform.position.z);
}
}
The constant amount we're talking about is PULL_UP_AMOUNT.
Keep in mind, you can access your new duplicate's properties by saving the result of Instantiate method inside a new GameObject, just like I did.

Find and change property name in a shader

I want to change and set 3 things after setting the new material:
First The shader type to Unlit/Color
Second The albedo color to change it for example to: 255,0,0,255
Third The metallic value from 0 to 1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoorsLockManager : MonoBehaviour
{
public bool locked;
public Color lockedColor = Color.red;
public Color unlockedColor = Color.green;
public Renderer rend;
private GameObject[] doorPlanes;
private void Start()
{
doorPlanes = GameObject.FindGameObjectsWithTag("DoorPlane");
for (int i = 0; i < doorPlanes.Length; i++)
{
rend = doorPlanes[i].GetComponent<Renderer>();
if (locked)
{
rend.material.SetFloat("Metallic", 1);
rend.material.color = lockedColor;
}
else
{
rend.material.color = unlockedColor;
}
}
}
// Update is called once per frame
void Update ()
{
}
}
This line does nothing:
rend.material.SetFloat("Metallic", 1);
This is what I want to change:
When you need to change a shader property but you don't know what to use for the proper name to use, select the material, click on its settings icon then click on "Select Shader". From there, you will see all the shader property names and their types and you will know which function and name to use to change the shader's properties.
With the default standard shader it looks something like this:
You need to know this otherwise you would need to ask new question for each property you want to change.
Your Renderer reference:
public Renderer rend;
To set it, the SetXXX function is used:
rend.material.SetFloat("_Metallic", 1); //Metallic is a float
To get it the GetXXX function is used:
float metallic = rend.material.GetFloat("_Metallic"); //Metallic is a float
To change or get the The albedo color to 255,0,0,255.
rend.material.SetColor("_Color", new Color32(255, 0, 0, 255));
Color color = rend.material.GetColor("_Color");
Notice that I used Color32 instead of Color because Color32 takes values between 0 and 255 while Color expects values between 0 and 1.
To change the material's shader to "Unlit/Color", just find it then assign it to the material:
Shader shader = Shader.Find("Unlit/Color");
rend.material.shader = shader;
Note that the "Unlit/Color" shader doesn't have the _Metallic property. It has one one property and that is "_Color". You can use the first method I described to determine which properties it has before attempting to change them.
What if the shader is used on many different objects. I want all the
objects to change when the property is changed on one of them.
To change all the objects using the-same material, use Renderer.sharedMaterial instead of Renderer.material. The shared material changes the original material and every other objects should pick that new material up as long as you have not called Renderer.material on the material which actually makes a new copy for the said material and disconnects the renderer material from the original material.

GameObject is finded as active when it's not active in the hierarchy

So I build a custom class named Obstacles
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Obstacle{
public GameObject gameObj;
public Color color;
public GameObject[] particleEffects;
public static Vector3 SpawnLocation()
{
int positionQuadran = Random.Range(1, 3);
switch (positionQuadran)
{
//spawn above the player
case 1:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(4f - SpawnStars.closerToPlayer, 4.5f),
Random.Range(1, -3.2f));
//spawn benith the player
case 2:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(-0.5f, SpawnStars.closerToPlayer),
Random.Range(1f, -3.2f));
}
return Vector3.zero;
}
}
Next I created a pool with 6 objects, every two objects with the same color, so there are 3 distinct colors in the obstaclePool
The problem is that I am trying to find the inactive Obstacle, it works for the first Objects that have a color, but when I ended all the colors and iterate again through the loop it says that the Obstacle.gameObj is active in the hierarchy, I don't understand, every Obstacle.gameObj is inactive (cuz I set them to be inactive in the start() so every Obstacle should have an inactive Obstacle.gameObj BUT after I activate the first Obstacle.gameObj the next ones seem to not exist, the loop "finds" only the gameObj that are active and assumes that EVERY Object in the list is now active, when actually only the first 3 are active, the remaining 3 are NOT active...And I don't understand why...Why doesn't the code find the remaining gameObj as inactive?
foreach (Obstacle star in obstaclePool)
{
if (!star.gameObj.activeInHierarchy)
{
Color color = star.color;
color.a = 0.5f;
panel.GetComponent<Renderer>().material.color = color;
return star;
}
}
Soo I eventually figure it out:
So if I create a costum class like Obstacle I NEED TO MAKE SHORE that if the class has a variable like a GameObject or something that needs to be instantiated or assigned I will do that
This is how I a eventually solved the problem:
Obstacle go = new Obstacle
{
gameObj = Instantiate(prefab.gameObj)
};
poolList.Add(go);
So we literally create a new Object Obstacle and after that we make it's gameObj variable equal with Instantiate(prefab.gameObj), in this way we create every part of the Obstacle.

Why won't my sprite change during runtime?

On my scene view in unity, I have three gameobjects set up, each with a respective sprite renderer and sprites. Two of the three are smaller in dimensions than the third one.
The images displayed by both smaller sprites are different. What I'd like to do is to be able to change the sprite of the third one by clicking on the other two. Clicking on one of the smaller sprites will make the third larger sprite match the small sprite that was previously clicked.
Here are the relevant scripts assigned to the two smaller sprites.
1st one that handles which sprite is displayed on the small sprite:
public class CardModel : MonoBehaviour
{
SpriteRenderer spriteRenderer; // renders sprite
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
spriteRenderer.sprite = cardFronts[cardIndex];
else
spriteRenderer.sprite = cardBack;
}
void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>(); // retrieve our sprite renderer
DisplaySprite(); // show card's sprite
}
}
And 2nd one that makes the smaller sprites clickable:
public class ClickCard : MonoBehaviour
{
CardModel model; // used to retrieve card index and bool to assign to larger sprite
public GameObject largeSprite; // third large sprite
void OnMouseDown()
{
CardModel cModel = GetComponent<CardModel>(); // retrieve card model from smaller sprite
LargeCardModel largeCardModel = largeSprite.GetComponent<LargeCardModel>(); // retrieve card model for larger sprite
largeCardModel.matchSprite(cModel.show, cModel.cardIndex); // match large sprite with small sprite clicked
}
}
And finally, the script attached to the third large sprite:
public class LargeCardModel : MonoBehaviour
{
public int largeIndex; // determines which sprite to show
public Sprite[] largeCardFaces; // collection of large sprites
public Sprite largeCardBack; // large card back
SpriteRenderer largeRenderer; // renders the image upon the large sprite
void Awake()
{
largeRenderer = GetComponent<SpriteRenderer>(); // retrieve renderer
}
// method to allow sprite to be changed
public void matchSprite(bool showFace, int cIndex)
{
if (largeRenderer == null)
largeRenderer = GetComponent<SpriteRenderer>(); // for some reason, I need to include this line or else I get an exception
if (showFace)
largeRenderer.sprite = largeCardFaces[cIndex];
else
largeRenderer.sprite = largeCardBack;
}
}
For whatever reason, I can't get the image to the larger sprite to change during runtime however when I stop running the program, the large sprite changes to that of the last smaller sprite I clicked on.
Okay, bare with as I have not tested this in Unity yet.
I think you're over complicating what you are trying to do. What you want is basically when sprite 1 or 2 is clicked, sprite 3 becomes what you have just clicked.
Here's an idea to try out:
Small Card GameObject:
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
GameObject largeSprite; //the large sprite that we will be changing!
void Start()
{
largeSprite = GameObject.Find("Name of the large sprite"); //find our large sprite!
}
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
gameObject.sprite = cardFronts[cardIndex]; //or it could be gameObject.renderer.sprite, I havent tested!
else
gameObject.sprite = cardBack;
}
void Awake()
{
DisplaySprite(); // show card's sprite
}
void OnMouseDown()
{
largeSprite.sprite = gameObject.sprite; //make the large one have the same as the small one!
}
Explanation:
Assuming this works, I've completely removed the script from the large GameObject and merged the 2 scripts together in the small one.
In Start() we find the large card gameObject using GameObject.Find() that way Unity will find your object with the name you've supplied and will go from there.
In OnMouseDown() we basically say:
if you clicked on me
make the large card the same as me! I havent tested it so that line might be a bit off, but the idea is there.
All in all, this is now one script that is attached to your small card object. Like I said I havent tested it, but I think the idea is there.
Hope this helps! :)

Categories

Resources