How to create dialogbox in Unity (not using UnityEditor)? - c#

I want to use dialog boxes (having two options).
I tried UnityEditor, but when I build the project to create an exe file, it didn't work because scripts having UnityEditor references are just working in edit mode. After searching on the Internet for hours, there were two suggestions (both didn't work).
First one: Using #if UNITY_EDITOR before code and ending with #endif. In this case, It was built without an error but there were no dialog boxes in my game at all.
Second one: Putting the script under Assets/Editor directory. In this case, I couldn't add the script to my game object. Maybe, creating a new script under Editor directory and pasting UnityEditor used lines in it would work but I couldn't figured out how to do this.
I used:
#if UNITY_EDITOR
if (UnityEditor.EditorUtility.DisplayDialog("Game Over", "Again?", "Restart", "Exit"))
{
Application.LoadLevel (0);
}
else
{
Application.Quit();
}
#endif
I also tried adding " using UnityEditor; " and encapsulating it with the preprocessor command I mentioned. It is also useless.
Is there anyone knowing how to use UnityEditor in run mode or how to create dialog boxes in a different way?

if I understand right, you need a popup window, when the character dies (or the player failed). The UnityEditor classes are for extending the editor, but in your case you need an in game solution. This can be achived with gui windows.
Here is a short script in c# that achives this.
using UnityEngine;
using System.Collections;
public class GameMenu : MonoBehaviour
{
// 200x300 px window will apear in the center of the screen.
private Rect windowRect = new Rect ((Screen.width - 200)/2, (Screen.height - 300)/2, 200, 300);
// Only show it if needed.
private bool show = false;
void OnGUI ()
{
if(show)
windowRect = GUI.Window (0, windowRect, DialogWindow, "Game Over");
}
// This is the actual window.
void DialogWindow (int windowID)
{
float y = 20;
GUI.Label(new Rect(5,y, windowRect.width, 20), "Again?");
if(GUI.Button(new Rect(5,y, windowRect.width - 10, 20), "Restart"))
{
Application.LoadLevel (0);
show = false;
}
if(GUI.Button(new Rect(5,y, windowRect.width - 10, 20), "Exit"))
{
Application.Quit();
show = false;
}
}
// To open the dialogue from outside of the script.
public void Open()
{
show = true;
}
}
You can add this class to any of your game objects, and call its Open mehtod to open the dialogue.

Have a look at Unity GUI Scripting Guide.
Example:
using UnityEngine;
using System.Collections;
public class GUITest : MonoBehaviour {
private Rect windowRect = new Rect (20, 20, 120, 50);
void OnGUI () {
windowRect = GUI.Window (0, windowRect, WindowFunction, "My Window");
}
void WindowFunction (int windowID) {
// Draw any Controls inside the window here
}
}
Alternatively you could show a textured plane in the center of the camera.

Related

Unity new Input System not registering Callbacks

So I've been using Unity's Input System and it was working correctly until unfortunately it wasn't.
I didnt change any settings but all of a sudden the callbacks are not being registered anymore.
I tried resetting everything and just building it up from scratch but it still doesnt work.
Heres the steps I took:
Right-Click > Create > Input Actions
Open Input Actions editor, setup a new binding for an action.
Mark "Generate C# Class" checkbox.
input.cs is created, in Scripts Folder.
New script which uses input.cs to receive callbacks and perform actions when input is received from the player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class MouseControl : MonoBehaviour
{
private Input input;
private void OnEnable()
{
input = new Input();
input.Player.Enable();
lastPosition = this.transform.position;
input.Player.MoveCamera.performed += DragCamera;
print("Inputs enabled: " + input.Player.enabled); //Prints true at start of Game.
}
void DragCamera(InputAction.CallbackContext ctx)
{
print("StartedDragCamera");
if (!Mouse.current.leftButton.isPressed)
{
dragging = false;
return;
}
//create plane to raycast to
Plane plane = new Plane(Vector3.up, Vector3.zero);
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (plane.Raycast(ray, out float distance))
{
if (Mouse.current.leftButton.wasPressedThisFrame)
{
startDrag = ray.GetPoint(distance);
dragging = true;
}
else
targetPosition += startDrag - ray.GetPoint(distance);
}
}
As soon as the key is pressed, the DragCamera() method should run, and the "Started Dragging" should show up in the console, but it doesnt.
I tried just binding it to the leftMouse butoon on the Mouse to see if it worked but unfortunately it doesnt work either.
The weirdest thing is that I had it working for an hour with no issues and then all of a sudden it stopped registering the inputs.
I turned off all UI and everything else so its just an empty scene to test this.
There is no UI Input Modules active either that could be interfering.
The Action Map is setup properly in the editor so I only have a LeftBUtton on Mouse binding that should send a callback to all subsribed methods(only DragCamera in this case).
Thanks

How to draw a mesh as wire-frame in Unity, so it shows up in play-mode / build? (like with Gizmos.DrawMesh)

I used Gizmos.DrawMesh in unity but when I build the program, I can't see any colors.
My code:
private void OnDrawGizmos()
{
if(mesh)
{
Gizmos.color = Color.red;
Gizmos.DrawMesh(mesh, transform.position, transform.rotation);
}
}
When I built the app, the colors I coded didn't show up. I'm wondering if Gizmos can only be used when I view it in the unity editor. when built, it is no longer usable.
Why aren't these colors showing up?
Yes, Gizmos are only visible in the editor. Maybe this can help you out:
https://github.com/popcron/gizmos
Edit:
Sorry I did not see that you want to use DrawMesh(), that's not supported by that lib. (if you want to use it anyway you can add it through the package manager "add from url" and insert "com.popcron.gizmos")
Are you creating the mesh in code? If so, you could use a line mesh: https://docs.unity3d.com/ScriptReference/MeshTopology.html
It should also be possible to convert a triangle mesh into a line mesh without too much trouble.
Edit2:
(I think this is overkill and does not offer much configuration)
I wrote a class that adds a context menu to the component "Mesh Filter". Place it in a folder named "Editor" and you should be able to right-click on a MeshFilter and convert it with "Convert To Line Mesh...". (The line mesh is very basic without a custom shader, so you wont get anything like line thickness)
If I have time I'll write a script that converts the lines to two triangles instead, that should then solve the issue.
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public static class MeshConverterEditor
{
[MenuItem("CONTEXT/MeshFilter/Convert To Line Mesh...")]
public static void ConvertToLineMesh(MenuCommand menuCommand)
{
MeshFilter mf = menuCommand.context as MeshFilter;
Mesh m = mf.sharedMesh;
if (m.GetTopology(0) != MeshTopology.Triangles)
{
Debug.LogError("Can only convert triangle meshes.");
return;
}
mf.sharedMesh = ConvertToLineMesh(m);
}
public static Mesh ConvertToLineMesh(Mesh mesh)
{
var lineMesh = new Mesh();
var tris = mesh.triangles;
List<int> lineIndices = new List<int>(tris.Length * 2);
for (int i = 0; i < tris.Length; i += 3)
{
lineIndices.Add(tris[i]);
lineIndices.Add(tris[i + 1]);
lineIndices.Add(tris[i + 1]);
lineIndices.Add(tris[i + 2]);
lineIndices.Add(tris[i + 2]);
lineIndices.Add(tris[i]);
}
lineMesh.vertices = mesh.vertices;
lineMesh.uv = mesh.uv;
lineMesh.normals = mesh.normals;
lineMesh.tangents = mesh.tangents;
lineMesh.SetIndices(lineIndices, MeshTopology.Lines, 0, true);
return lineMesh;
}
}
Edit3:
I found a very simple and nice looking library:
https://github.com/Chaser324/unity-wireframe
Just copy the folders:
unity-wireframe-master\Assets\Wireframe\Shaders
unity-wireframe-master\Assets\Wireframe\Examples\Materials
into your project, drag a material on your GameObject and you should be good.

OnMouseDown not work in Vuforia - Unity C#

I'm testing AR in Unity with Vuforia and I can't have the event OnMouseDown() working correctly.
It happens that the first time I hit play it works but just one time.
I've already checked that the collider is activated and well positioned.
Also I see that the check of the script in the GameObject (Cube in this case) is not enabled, it doesn't even appear like the rest of the scripts when components are made.
This is the code:
using System.Collections.Generic;
using UnityEngine;
public class Click : MonoBehaviour
{
void OnMouseDown()
{
Debug.Log("CLICK!!!");
}
}
I don't have any error messages or warnings in the console.
This is the repository, branch develop:
https://github.com/emicalvacho/MapaMentalAR.git
I just found out: You are always hovering the Hola text on the object .. not the cube. It blocks the raycast!
How I found out: I wrote a simple script for finding out what is currently hovered:
public class RayDebugger : MonoBehaviour
{
private void OnGUI()
{
GUI.color = Color.green;
var hovering = EventSystem.current.IsPointerOverGameObject();
var isHovering = hovering ? "Yes" : "No";
GUI.Label(new Rect(100, 100, 200, 200), $"Is hovering something? - {isHovering}");
if (!hovering) return;
var pointer = new PointerEventData(EventSystem.current) { position = Input.mousePosition };
var raycastResults = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointer, raycastResults);
if (raycastResults.Count > 0)
{
GUI.Label(new Rect(100, 200, 200, 200), $"Currently Hovered: {raycastResults[0]}");
}
}
}
As you can see it is always your "Hola" Text component:
(and yes I just used a "dynamic image" target :D )
You can fix this in a few steps:
Disable RaycastTarget on the Text component:
this way it doesn't interfere with the pointer raycast
For getting a 3D collider your Camera should have a PhysicsRaycaster component attached:
I don't know why exactly but it only works if you use a Perspective Camera. Vuforia somehow seems to have a trouble with an Orthographic one .. understandable because for such a camera no distances exist. So rather switch you camera to Perspective
Now I can add and click on the cubes:
Btw
I don't think it worked with Overlay, but I can try.
as the info box says without a Camera referenced (which is the case in your scene) a ScreenSpace - Camera just behaves equal to a ScreenSpace - Overlay.

Unity, editor-time script, On GameObject Added to Scene Event

Say you have a trivial prefab, "Box", which we'll say is nothing more than a standard meter cube.
1 - The prefab Box is in your Project panel
2 - Drag it to the Scene
3 - Obviously it now appears in the Hierarchy panel also, and probably selected and shown in Inspector
To be clear, game is NOT Play when you do this, you're only in ordinary Editor mode.
Is it possible to make a script (an "Editor script"?) so that,
when you do "1" and "2" above, (again this is in Editor mode, not during a game)
when 3 happens, we can affect the new Box item in the scene
So, simple example: we will set the Z position to "2" always, no matter where you drop it.
In short: Editor code so that every time you drag a prefab P to the scene, it sets the position z to 2.0.
Is this possible in Unity? I know nothing of "editor scripts".
It seems very obvious this should be possible.
You can add a custom window editor which implements OnHierarchyChange to handle all the changes in the hierarchy window. This script must be inside the Editor folder. To make it work automatically make sure you have this window opened first.
using System.Linq;
using UnityEditor;
using UnityEngine;
public class HierarchyMonitorWindow : EditorWindow
{
[MenuItem("Window/Hierarchy Monitor")]
static void CreateWindow()
{
EditorWindow.GetWindow<HierarchyMonitorWindow>();
}
void OnHierarchyChange()
{
var addedObjects = Resources.FindObjectsOfTypeAll<MyScript>()
.Where(x => x.isAdded < 2);
foreach (var item in addedObjects)
{
//if (item.isAdded == 0) early setup
if (item.isAdded == 1) {
// do setup here,
// will happen just after user releases mouse
// will only happen once
Vector3 p = transform.position;
item.transform.position = new Vector3(p.x, 2f, p.z);
}
// finish with this:
item.isAdded++;
}
}
}
I attached the following script to the box:
public class MyScript : MonoBehaviour {
public int isAdded { get; set; }
}
Note that OnHierarchyChange is called twice (once when you start dragging the box onto the scene, and once when you release the mouse button) so isAdded is defined as an int to enable its comparison with 2. So you can also have initialization logic when x.isAdded < 1
You could thy this:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteInEditMode]
public class PrintAwake : MonoBehaviour
{
#if UNITY_EDITOR
void Awake() .. Start() also works perfectly
{
if(!EditorApplication.isPlaying)
Debug.Log("Editor causes this Awake");
}
#endif
}
See https://docs.unity3d.com/ScriptReference/ExecuteInEditMode.html
Analysis:
This does in fact work!
One problem! It happens when the object starts to exist, so, when you are dragging it to the scene, but before you let go. So in fact, if you specifically want to adjust the position in some way (snapping to a grid - whatever) it is not possible using this technique!
So, for example, this will work perfectly:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteInEditMode]
public class PrintAwake : MonoBehaviour
{
#if UNITY_EDITOR
void Start() {
if(!EditorApplication.isPlaying) {
Debug.Log("Editor causes this START!!");
RandomSpinSetup();
}
}
#endif
private void RandomSpinSetup() {
float r = Random.Range(3,8) * 10f;
transform.eulerAngles = new Vector3(0f, r, 0f);
name = "Cube: " + r + "°";
}
}
Note that this works correctly, that is to say it does "not run" when you actually Play the game. If you hit "Play" it won't then again set random spins :)
Great stuff
Have a similar issue - wanted to do some stuff after object was dragged into scene (or scene was opened with already existed object). But in my case gameobject was disabled. So I couldn't use neither Awake, nor Start.
Solved via akin of dirty trick - just used constructor for my MonoBehaviour class. Unity blocks any attempts to use most of API inside MonoBehaviour constructors, but we could just wait for some time, for example via EditorApplication.delayedCall. So code looks like this:
public class ExampleClass: MonoBehaviour
{
//...
// some runtime logic
//...
#if UNITY_EDITOR
//Constructor
ExampleClass()
{
EditorApplication.delayCall += DoSomeStuffForDisabledObjectAfterCreation;
}
void DoSomeStuffForDisabledObjectAfterCreation()
{
if (!isActiveAndEnabled)
{
//Some usefull stuff
}
}
#endif
}
Monobehaviours have a Reset method, that only gets called in Editor mode, whenever you reset or first instantiate an object.
public class Example : MonoBehaviour
{
void Reset()
{
transform.position =
new Vector3(transform.position.x, 2.22f, transform.position.z);
Debug.Log("Hi ...");
}
}

Disable script outside of the scene Unity 3D

In my chess game, I have a scene of pieces choice - black or white. After the user clicks on one of the pawns he/she gets a popup message/ It looks like this:
On Ok button click, the scene changes to the one with the board:
When the user chose black pieces he/she should see them closer to him/her, while if the user chose white pieces, they should be at the front. By default, in my scene pieces which are closer are black. I tried to achieve this by adding a texture change script on each figure (they will differ for white and black pieces):
void Start () {
GetComponent<Renderer>().material = Resources.Load<Material>)"Materials/Pieces/Marble/White Pawn");
}
However, how can I disable this script when I redirect to the scene if the user chose black pieces and the default view is needed.
Here is my code for popup window:
void OnGUI()
{
if (showPopUp)
{
GUI.Window(0, new Rect((Screen.width / 2) - 200, (Screen.height / 2) - 115
, 420, 180), ShowGUI, "Figures choice");
}
}
void ShowGUI(int windowID)
{
RedirectToMenu redirect = new RedirectToMenu();
guiStyle.fontSize = 22;
guiStyle.normal.textColor = Color.white;
GUI.Label(new Rect(80, 40, 200, 30), "You have chosen black pieces", guiStyle);
if (GUI.Button(new Rect(180, 110, 75, 35), "OK")){
showPopUp = false;
redirect.LoadAsset("UpgradedRoom");
SceneManager.LoadScene("UpgradedRoom");
}
}
I suppose I should access this script before loading the scene and disable if needed. But how can I access it outside of the scene with table, chessboard, and pieces? Or can I change the textures of game objects on another scene?
What I would do is use a static variable to remember whether the pieces are black or white. So, you'd set the variable before the scene loads and then check it after it is loaded. Therefore, if your class is called chess manager, your code for setting the variable might look like this:
public class ChessManager : Monobehavior {
public enum ChessColor { Black, White }
public static ChessColor playerColor
public void OnGUI() {
if(user chooses black) {
playerColor = ChessColor.Black;
//Load scene
}
else if(user chooses white) {
playerColor = ChessColor.White;
//Load scene
}
}
}
...And your code for enabling/disabling the script might be something like this, where ColorChanger is the script that sets the color:
public class ColorChanger : Monobehavior {
public void Start() {
if(ChessManager.playerColor == ChessManager.ChessColor.White) {
//Set texture
}
}
}
This would set the texture to something else when the user chooses white once the new scene is loaded. Don't forget to replace the if statements in ChessManager with the code that executes when a chess piece is selected (I'm assuming you're using legacy buttons for that, so you should replace if(user chooses color) with if(GUI.Button)). Hope I could help!
Also, as previously noted by someone else in the comments, it would probably be best if you used UnityEngine.UI instead of Unity's outdated legacy GUI system, but depending upon the project it might not be worth the effort.

Categories