Draw line on canvas, but under text in an UI - unity3d

How can i draw this line on my canvas and image, BUT under text?
so basically the render order should be this:
canvas
image
line
text
so text would be on top of every one of them.
right now i am almost there, but the line is over the text.
i am using world space on my canvas, and the event camera is set to Main Camera.
Picture

Add a "canvas" in the Hierarchy.
Add a "Image" under the "canvas" in the Hierarchy.
Add a "GameObject" under the "Image" in the Hierarchy.
Add a C# script in the Assets. The C# script named as "NewBehaviourScript".
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class NewBehaviourScript : MaskableGraphic
{
Static NewBehaviourScript self;
NewBehaviourScript()
{
self = this;
}
// To redraw from outside this class, call NewBehaviourScript.redraw()
static public void redraw()
{
print("redraw");
self.SetAllDirty();
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
UIVertex vertex = UIVertex.simpleVert;
vertex.color = Color.red;
// draw a diagonal red line from bottom left to top right
// first triangle
vertex.position = new Vector2(0, 0); // lower left position
vh.AddVert(vertex);
vertex.position = new Vector2(canvas.pixelRect.width+2, canvas.pixelRect.height);
vh.AddVert(vertex);
vertex.position = new Vector2(canvas.pixelRect.width-2, canvas.pixelRect.height);
vh.AddVert(vertex);
// second triangle
vertex.position = new Vector2(2, 0);
vh.AddVert(vertex);
vertex.position = new Vector2(-2, 0);
vh.AddVert(vertex);
vertex.position = new Vector2(canvas.pixelRect.width, canvas.pixelRect.height);
// position it on the upper right.
vh.AddVert(vertex);
// draw two elongated triangles to form a straight line
vh.AddTriangle(0, 1, 2); // only triangles can be drawn
vh.AddTriangle(3, 4, 5); // only triangles can be drawn
}
}
Click the "GameObject" to show Inspector view, and click "Add component" button.
Add ""Canvas Renderer" into the "GameObject".
Drag "NewBehaviourScript" (in the assets) and drop it on the GameObject (in Hierarchy).
Add a "Text (Legacy)" under the "GameObject" in the hierarchy
With screen captured guide is here. This is an article I wrote.
https://kuukaix.hatenablog.com/entry/2022/10/02/181043

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.

How do i get the the tile in front of the player in unity 2d

I'm trying to get all tiles around the player in unity 2d tileset.
What I would like to happen when player presses U I Would like to get all tiles surrounding the player including the one underneath the player or simply the one 1 tile in front of them.
I'm trying to make a farming Game where when the player pulls out an item it will highlight on the tilemap where they are about to place it.
Please note I'm not asking for full code, I just want what ever solution allows me to get tiles near the players position
Edit, found a solution by editing someone elses code but im not sure if there's a better way, if not I would also like this to work based on player rotation currently it places a tile above the player. Code Source https://lukashermann.dev/writing/unity-highlight-tile-in-tilemap-on-mousever/
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Tilemaps;
public class PlayerGrid : MonoBehaviour
{
private Grid grid;
[SerializeField] private Tilemap interactiveMap = null;
[SerializeField] private Tilemap pathMap = null;
[SerializeField] private Tile hoverTile = null;
[SerializeField] private Tile pathTile = null;
public GameObject player;
private Vector3Int previousMousePos = new Vector3Int();
// Start is called before the first frame update
void Start()
{
grid = gameObject.GetComponent<Grid>();
}
// Update is called once per frame
void Update()
{
var px = Mathf.RoundToInt(player.transform.position.x);
var py = Mathf.RoundToInt(player.transform.position.y);
var tilePos = new Vector3Int(px, py, 0);
if (!tilePos.Equals(previousMousePos))
{
interactiveMap.SetTile(previousMousePos, null); // Remove old hoverTile
interactiveMap.SetTile(tilePos, hoverTile);
previousMousePos = tilePos;
}
// Left mouse click -> add path tile
if (Input.GetMouseButton(0))
{
pathMap.SetTile(tilePos, pathTile);
}
// Right mouse click -> remove path tile
if (Input.GetMouseButton(1))
{
pathMap.SetTile(tilePos, null);
}
}
}
try to use the layer field inside the inspector, you can create new Layers inside the Project Manager and there you can easily create Layers like "Background" "Scenery" "Player" "Foreground".. after this you can assign them individually to your Tiles also its important to have different grids otherwise you will not be able to assign different layers to it i hope it worked already

GameObject (Prefab object ) created by script does not appear in Game View but appears in Scene View in Unity3d

I created a pin object by script attached it to a sphere object .
using UnityEngine;
public class InstantiateMarkerPin : MonoBehaviour
{
public float Xpos;
public float Ypos;
public float Zpos;
public GameObject gameObjectPinInstantiate;
// Start is called before the first frame update
private void Start()
{
Xpos = 0.09f;
Ypos = 0.50f;
Zpos = 1.1f;
//The original object, where to instantiate, and the orientation of the new object
GameObject marker = (GameObject)Resources.Load("gameObjectPin");
Vector3 location = new Vector3(Xpos, Ypos, Zpos);
Quaternion rotation = Quaternion.Euler(0, 0, 0);
//The object the script is attached to
GameObject world = this.gameObject;
//Instantiate the prefab
gameObjectPinInstantiate = Instantiate(marker, location, rotation, world.transform);
Debug.Log("InstantiateMarkerPin class : Marker Location 2 :X, Y, Z : " + gameObjectPinInstantiate.transform.position);
}
// Update is called once per frame
private void Update()
{
}
}
This script is attached to the sphere Object .My sphere Object have shader material of earth image (globe).
This Instantiated Prefabs (gameObjectPin) on sphere surface appears on scene but not on game screen ,When I select the camera object in the camera preview also this object does not appear .
Scene view
Scene View when camera is selected
I am new to Unity what should I check or correct to appear my created object on the sphere
basically I am trying to add pins to corresponding country and label it .Similar to the globe on this http://kitsdmcc.com/news
Gameobject is created when Play is clicked on the sphere object
When the Pin Object is selected on play mode
Oh now I see it! What you did was only setting its GIZMO via this menu
which is only displayed in the SceneView.
This has nothing to do with the rendering in the GameView but is just a way for easier seeing and finding certain types of objects in the SceneView since usually they would be invisible if not selected.
From the Glossary:
A graphic overlay associated with a GameObject
in a Scene
, and displayed in the Scene View
. Built-in scene
tools such as the move tool are Gizmos
, and you can create custom Gizmos using textures or scripting. Some Gizmos are only drawn when the GameObject is selected, while other Gizmos are drawn by the Editor regardless of which GameObjects
are selected.
As noted in the comments there is no Component at all on your GameObject so nothing is rendered in the Gameview.
Of course now you could enable Gizmos also for the GameView via the Gizmos toggle
but I guess what you rather are trying to achieve is rather rendering that icon in the final App.
You probably would like to use e.g. the SpriteRenderer component here. And simply drag in your Icon to the Sprite property.
You might have to change the Pin Texture's TextureType to Sprite (2D and UI).
In general I would also recommend to Create a Prefab instead of using the Resources folder here.
There are also some changes I would do to your code in general:
public class InstantiateMarkerPin : MonoBehaviour
{
[Header("Settings")]
// directly use a Vector3 for setting the values
// | default value for this field
// | (only valid until changed via Inspector!)
// v
public Vector3 TargetPosition = new Vector3(0.09f, 0.5f, 1.1f);
// Instead of using Resources simply reference the
// created Prefab here
public GameObject gameObjectPinPrefab;
[Header("Outputs")]
public GameObject gameObjectPinInstantiate;
private void Start()
{
// Careful this currently ovewrites any value set via the
// Inspector. Later you will probably want to remove this.
TargetPosition = new Vector3(0.09f, 0.5f, 1.1f);
//As said I would rather use a prefab here and simply reference it above in the field
//gameObjectPinPrefab = (GameObject)Resources.Load("gameObjectPin");
//Instantiate the prefab
gameObjectPinInstantiate = Instantiate(gameObjectPinPrefab, TargetPosition, Quaternion.identity, transform);
Debug.Log("InstantiateMarkerPin class : Marker Location 2 :X, Y, Z : " + gameObjectPinInstantiate.transform.position);
}
// Always remove empty Unity methods
// They are called as messages if they exist
// and only cause unnecessary overhead
}

Buttons without background, size, why?

I want create menu with buttons. Template button is cloned and placed in vertical layout. It worked but in the middle of work there is strange effect of rendering only text child od button, additionally gameobjects do not have width, are outside of canvas. On the screen , in the center is my template button, goal is to have two columns of buttons on left and right side.
using UnityEngine;
using UnityEngine.UI;
public class DivideDownUI : MonoBehaviour {
void Start () {
createBtnsRow(transform.Find("PanelLeft"));
createBtnsRow(transform.Find("PanelRight"));
}
void createBtnsRow(Transform panel)
{
for(int i = 1; i <= 10; i++)
{
GameObject btn = Instantiate(transform.Find("BtnTpl").gameObject);
btn.transform.parent = panel;
btn.SetActive(true);
btn.transform.Find("Text").gameObject.GetComponent<Text>().text = "" + i;
}
}
}
`
Resolved:
There was problem with positioning: After using rect transform instead of padding of layout group works as espected.

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)
{
// ...
}
}