I have a plane game object on the 3D scene and I want to plot 2D graph z=f(x)=sin kx (btw, MathJaX does not work in this site), for example, on it. I am very new to Unity, could you tell me what should I do?
There are three ways to show a plot.
you create a bunch of small gameobjects and piece together lines,
you create a Texture2D, and draw into it.
When leaving Unity a litte, call Texture.GetNativeTexturePtr() and use D3D calls for this.
I think the 2 is what you might use best.
3. is leaving Unity a little and will not port across target platforms.
It leaves up to you how to do graphics on it. Using only SetPixel is not a really big graphics API.
Here's an example how to load a texture with graphics drawn at runtime.
To use it, create an object, don't forget to assign a material, and attach this script.
using UnityEngine;
public class DrawTex : MonoBehaviour
{
Material mat;
Texture2D tx;
void Start()
{
MeshRenderer rend;
rend = GetComponent<MeshRenderer>();
UnityEngine.Assertions.Assert.IsNotNull(rend);
mat = rend.material;
UnityEngine.Assertions.Assert.IsNotNull(mat);
tx = new Texture2D(128,128,TextureFormat.ARGB32,true);
// draw stuff.
for(int y=0;y<128;y++)
{
for(int x=0;x<128;x++)
{
float a,r,g,b;
r=g=b=a=0f;
if( x<20 || y<20 || x>108 || y>108 )
{a=1.0f;r=g=b=0.75f;}
else
{a=0.5f;r=b=0.25f+(x/256.0f);g=0.25f+(y/256.0f);}
tx.SetPixel(x,y,new Color(r,g,b,a));
}
tx.Apply(true); // now really load all those pixels.
}
mat.mainTexture = tx;
}
}
Hope this helps.
Related
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.
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.
I'm trying to add one of my scriptable tiles to my Unity Tilemap during runtime. I can paint the tile in the editor and everything looks fine but I would like to add it during runtime.
I tried instantiating the ScriptableObject asset itself, but it does not have the correct sprites it needs that the generated tile asset has in the editor. Also, I don't believe I should be instantiating the ScriptableObject asset for each tile since one asset is meant to be shared for all of the same tiles on the map.
The Tilemap API provides a way to add a tile, but no instructions or example of how to create the tile.
This is kinda off the top of my head, but I was messing around with something similar yesterday, instantiating a tile on click.
Vector3Int tilePos = map.WorldToCell(mousePos);
YourTileClass tileInstance = ScriptableObject.CreateInstance<YourTileClass>();
Texture2D tex = Resources.Load<Texture2D>("tileTexture") as Texture2D;
Sprite sprite = new Sprite();
sprite = Sprite.Create(tex, new Rect(0, 0, 400, 400), new Vector2(0.5f, 0.5f));
Tile tile = Resources.Load<Tile>("tileInstance") as Tile;
tileInstance .sprite = sprite;
yourTileMap.SetTile(tilePos , tile);
I am not 100% if that is the best way to do it, but it worked for me. Let me know if that does not work and I will double check the actual file.
Sorry, I can't comment, but not knowing what you've tried so far, have you tried assigning the ruletile in the inspector and then just assigning it with SetTile at runtime whenever you need to? From the sounds of it this should achieve what you're trying to do
public Tilemap tilemap;
public MyRuleTile myRuleTile;
tilemap.SetTile(coordinate, myRuleTile);
Assuming your ScriptableObject is inheriting from TileBase object you can do the following:
public Tilemap tilemap;
public TileBase tileToDraw; //Object to draw at runtime
//Get tilemap position from the worldposition
Vector3Int tilemapReferencePosition = tilemap.WorldToCell(reference.transform.position);//this can be the position of another gameObject e.g your Player or MousePosition
roadTileMap.SetTile(tilemapReferencePosition, tileToDraw);
I recently used something similar for a Fire Emblem style RPG where I wanted to draw all the potential directions a spell can be cast before casting it and I ended up with the following function
public void DrawPreAttack(GameObject reference, Spell spell) {
Vector3Int gridReferencePos = preAttackTileMap.WorldToCell(reference.transform.position);
foreach (Vector2 direction in spell.validDirections) {
for (int i = 0; i <= spell.maxDistance; i++) {
preAttackTileMap.SetTile(new Vector3Int(gridReferencePos.x + (i * (int)direction.x) , gridReferencePos.y + (i * (int)direction.y) , 0), preAttackTile);
}
}
}
I have provided two small scripts for you that show you exactly how to do this:
1) The ScriptableObject
After placing this script in the Assets/??? folder, you can create new ScriptableObjects (or rather "ScriptableTiles") by right-clicking in the folder -> ScriptableObjects/ScriptableTile. After you have created such an asset, you have to "fill" it with what you want to have in it. In this case: a Tile. Just drag any Tile from your folder into the "m_Tile" slot that the ScriptableTile provides for you.
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "NewScriptableTile", menuName = "ScriptableObjects/ScriptableTile")]
public class ScriptableTile : ScriptableObject
{
public Tile m_Tile;// Fill this with a Tile
public bool m_IsCollider;// This does nothing, it's just a showcase for what else could be stored in here
}
2) A simple "Placer"
Attach the "Placer" MonoBehaviour to a GameObject and give it a reference to a TileMap and your ScriptableTile. On Start() it will place the Tile in the middle of the chosen TileMap.
using UnityEngine;
using UnityEngine.Tilemaps;
public class ScriptableTilePlacer : MonoBehaviour
{
public Tilemap m_ExampleTileMap;
public ScriptableTile m_ExampleTile;
private void Start()
{
m_ExampleTileMap.SetTile(Vector3Int.zero, m_ExampleTile.m_Tile);
}
}
I've played around with creating TileMaps at runtime myself - you can get even more out of ScriptableTiles by adding more variables. For example, if you have a separate Collider TileMap, you could use a bool to mark certain Tiles as "IsCollider" or "IsNotCollider". Another idea would be to not store Tiles inside the ScriptableTile, but Sprites. This allows you to completely bypass the TilePalette - which is pretty much useless when it comes to runtime creation. Just write a short public Tile GetTileFromSprite() method inside the ScriptableTile. With a few lines of code you can create Tiles from Sprites and return them.
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.
I am making my first game in Unity 3D something like tower defense game. I have imported a Fbx model of a tower and attached it to a prefab. Now I want the nozzle of the tower to rotate and follow the enemy as the enemy passes by .
In the fbx model i imported i got I have two poly meshes one for the base of the tower that is fixed, and one for the top of the tower that will rotate. Now I tried to create two different gameObject with these two meshes but if I put them on same point they overlap. So I have to do manual alignment such that the nozzle sit correctly over the base.
I was wondering if there is any other way such that entire towers remain is one gameObject and I can rotate the upper part.
I did manage to solve my problem. Not sure if was the best way , but it works.
To upgrade the towers and transform only the nozzle part I essentially did this.
public class tryFbx : MonoBehaviour {
public GameObject[] ModelPrefab;
GameObject modelInstance;
Renderer rn = new Renderer();
// Attaching the model to prefab at runtime by creating a array of prefabs
public void AttachModelToPrefab(GameObject modelPrefab) {
modelInstance = GameObject.Instantiate(modelPrefab) as GameObject;
modelInstance.transform.position = transform.position;
modelInstance.transform.rotation = transform.rotation;
// Attach the model instance to the prefab shell
modelInstance.transform.parent = gameObject.transform;
}
void Start () {
}
// Update is called once per frame
void Update () {
if (GameManager.upgrade){
AttachModelToPrefab(ModelPrefab[GameManager.towerUpgradeLevel]);
foreach ( Renderer r in modelInstance.GetComponentsInChildren<Renderer>()){
// "polySurface98" is the name of the mesh I want to rotate. The tower and its upgrade have the same name.
if (r.name == "polySurface98")
rn = r;
}
// apply any transformation to the partial fbx
rn.transform.Translate(1,1,1);
}
}
}
You can create a hierarchy of GameObjects; you'll have one parent GameObject representing the tower and 2 children, being that you rotate one of them. Note that the coordinates of the children will be in relation to the parent's, so any "messy" manual calibration you make on the children will be contained.
Or, more complicated, you can create only one mesh that has an animation and apply it to one GameObject.