I have this Class called ToggledFloat. It wraps a float but also adds a bool telling if it is enabled or not.
[System.Serializable]
public class ToggledFloat
{
public float value;
public bool enabled = false;
}
[System.Serializable]
public class ClassWIthNestedProp
{
public ToggledFloat aTogFloat;
}
public class Test : MonoBehaviour
{
public ClassWIthNestedProp eCA;
public ToggledFloat tF;
}
I can easily make a Custom Property Drawer for this, and it looks right, when the editor draws this property at indentation level "0". However when I look at ToggledFloat properties nested inside another property they look wrong.
My propertyDrawer looks like this:
[CustomPropertyDrawer(typeof(ToggledFloat))]
public class ToggledPropertyEditor : PropertyDrawer
{
public override float GetPropertyHeight (SerializedProperty property, GUIContent label)
{
var propVal = property.FindPropertyRelative("value");
float contentUnfoldedHeight = EditorGUI.GetPropertyHeight (propVal, label);
return contentUnfoldedHeight;
}
// Draw the property inside the given rect
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
int iL = EditorGUI.indentLevel;
//EditorGUI.indentLevel = 0; //- Set this to "0" to make thing work but non-indented
SerializedProperty valueProp = property.FindPropertyRelative("value");
SerializedProperty enabledProp = property.FindPropertyRelative("enabled");
//Label left of checkbox
float labelWidth = GUI.skin.label.CalcSize(label).x;
Rect labelRect = new Rect(0/*position.x*/, position.y, labelWidth, 16);
EditorGUI.LabelField(labelRect, label, GUIContent.none);
EditorGUI.DrawRect(labelRect, new Color(0,0,1,.1f));
//Checkbox
Rect enableRect = new Rect();
enableRect.xMin = labelRect.xMax;
enableRect.yMin = labelRect.yMin;
enableRect.width = 16;
enableRect.height = 16;
EditorGUI.PropertyField(enableRect, enabledProp, GUIContent.none);
EditorGUI.DrawRect(enableRect, new Color(0,1,0,.1f));
//Value
Rect valueRect = new Rect();
valueRect.xMin = enableRect.xMax;
valueRect.yMin = enableRect.yMin;
valueRect.xMax = position.xMax;
valueRect.yMax = position.yMax;
EditorGUI.DrawRect(valueRect, new Color(1,0,0,.1f));
bool enabled = GUI.enabled;
GUI.enabled = enabledProp.boolValue;
EditorGUI.PropertyField(valueRect, valueProp, new GUIContent(""), true);
GUI.enabled = enabled;
EditorGUI.indentLevel = iL;
}
}
When the inspector draws an instance of class Test, it looks like this:
The Colored rects are only there for allowing me to debug where those rects actually are. The weird thing is that the text-labels are offset from the coloured rects, even though they get the same rect as argument. This is of course only a problem if I want coloured rects in my inspector - but the real problem is that it seems that this offset problem causes nested checkboxes to not work. I cannot click checkboxes on a nested property.
If I then explicitly set EditorGUI.IndenLevel = 0, then the coloured rects and the labels coincide and the toggle buttons work properly - but I then loose the automatic indentation, that I would really like to use.
Can someone tell me what I am overlooking
The DrawRect method doesn't consider the indent level. You have to adjust rect yourself to draw indented rectangle. Fortunately, There is a function to get the intented rect.
From UnityCsReference EditorGUI
static void DrawIndentedRect(Rect rect, Color color)
{
EditorGUI.DrawRect(EditorGUI.IndentedRect(rect), color);
}
Captured Inspector IndentedRect
However it doesn't work properly.
From the implementation of IndentedRect, it reduces the width of rect.
static void DrawIndentedRect(Rect rect, Color color)
{
// Indent per level is 15
rect.x += EditorGUI.indentLevel * 15;
EditorGUI.DrawRect(rect, color);
}
or
static void DrawIndentedRect(Rect rect, Color color)
{
var indentedRect = EditorGUI.IndentedRect(rect);
indentedRect.width = rect.width;
EditorGUI.DrawRect(indentedRect, color);
}
Captured Inspector DrawIndentedRect
Related
We're trying to capture an area of the screen for our players to share. In the editor, the capture works fine, it captures the exact area. On Android however, the area seems to vary on the device itself. On one device it captures a much larger area and on another it's a smaller area. But, the final image size is always the same. This makes me think that a scaling or translation isn't correct when capturing the screen. However, visually everything scales correctly.
I'm using Unity 2020.1.10
I assign the panel I want a screenshot of to recT.
public class ElementScreenshot : MonoBehaviour
{
public RectTransform rectT; // Assign the UI element which you wanna capture
//public Image img;
int width; // width of the object to capture
int height; // height of the object to capture
// Use this for initialization
void Start()
{
width = System.Convert.ToInt32(rectT.rect.width);
height = System.Convert.ToInt32(rectT.rect.height);
}
public IEnumerator takeScreenShot(string filePath)
{
yield return new WaitForEndOfFrame(); // it must be a coroutine
Vector2 temp = rectT.transform.position;
var startX = temp.x - width / 2;
var startY = temp.y - height / 2;
var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(startX, startY, width, height), 0, 0);
tex.Apply();
// Encode texture into PNG
var bytes = tex.EncodeToPNG();
Destroy(tex);
File.WriteAllBytes(filePath, bytes);
}
public string Capture()
{
var filePath = Path.Combine(Application.temporaryCachePath, "shared_image.png");
StartCoroutine(takeScreenShot(filePath)); // screenshot of a particular UI Element.
return filePath;
}
}
The issue was with using rect. The following change fixed the issue.
public IEnumerator takeScreenShot(string filePath)
{
yield return new WaitForEndOfFrame(); // it must be a coroutine
var r = RectTransformToScreenSpace(rectT);
var tex = new Texture2D((int)r.width, (int)r.height, TextureFormat.RGB24, false);
tex.ReadPixels(r, 0, 0);
tex.Apply();
// Encode texture into PNG
var bytes = tex.EncodeToPNG();
Destroy(tex);
File.WriteAllBytes(filePath, bytes);
}
public static Rect RectTransformToScreenSpace(RectTransform transform)
{
Vector2 size = Vector2.Scale(transform.rect.size, transform.lossyScale);
return new Rect((Vector2)transform.position - (size * 0.5f), size);
}
I'm trying to develop a VR app where you can move around and interact with music (moving the audio sources, and so on). So, you can't actually die in this app, which is why a fade to black on trigger wouldn't help me. I'm trying to get a fade to black and show the credits after a certain amount of time (specifically, when the music is over). Maybe even a fade out (black) and a fade in (to another scene containing the credits) would do the trick. I know very little about programming, so I could really use some help.
I'm using Unity3D 2018.2.9f1
The app is for the Samsung's Gear VR
Timer
For any stuff using timers or wait I find Coroutines most of the time the best options.
You could either get the audio clip length and than wait for it
public AudioClip clip;
private void Start()
{
StartCoroutine(Wait(clip.length));
}
private IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
// start fadeout here
}
or you could wait until it finishes playing
public AudioSource source;
// or wherever you start the music adioclip
private void Start()
{
source.Play ();
yield return new WaitUntil(()=> !source.isPlaying);
// start fadeout here
}
Fading
Here it depends a lot ... do you really switch Scenes by using LoadScene etc or do you just want to enable/disable certain content within one Scene?
And do you have 3D objects with materials and renderers or only 2D content using Unity.UI components?
The simplest solution for sure would be to place one overlay Canvas with the desired color as Image and simply fade it in and out. This is actually already available in the AssetStore and basically does
Uses an Overlay Canvas on top of everything with a certain color and animates it's alpha value (again using a Coroutine)
Makes sure that canvas is DontDestroyOnLoad so it isn't removed when we switch scenes
FadeIn the Canvas/Image => Fades out the current Scene
Load the other scene
FadeOut the Canvas/Image => Fades in the new Scene
Here is the source code (just cleaned it up a little)
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Fader : MonoBehaviour
{
private string _fadeScene;
private float _alpha;
private CanvasGroup _myCanvas;
private Image _bg;
private float _lastTime;
private bool _startedLoading;
private float _fadeInDuration;
//Set callback
private void OnEnable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
//Remove callback
private void OnDisable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
public void InitiateFader(CanvasGroup canvasGroup, Image image, string scene, Color fadeColor, float fadeInDuration, float fadeOutDuration)
{
DontDestroyOnLoad(gameObject);
_fadeInDuration = fadeInDuration;
_fadeScene = scene;
//Getting the visual elements
_myCanvas = canvasGroup;
_bg = image;
_bg.color = fadeColor;
//Checking and starting the coroutine
_myCanvas.alpha = 0.0f;
StartCoroutine(FadeIt(FadeDirection.Out, fadeOutDuration));
}
private enum FadeDirection
{
In,
Out
}
private IEnumerator FadeIt(FadeDirection fadeDirection, float fadeDuration)
{
var timePassed = 0.0f;
switch (fadeDirection)
{
case FadeDirection.Out:
do
{
_alpha = Mathf.Lerp(0, 1, timePassed / fadeDuration);
_myCanvas.alpha = _alpha;
timePassed += Time.deltaTime;
yield return null;
} while (timePassed < fadeDuration);
_alpha = 1;
SceneManager.LoadSceneAsync(_fadeScene);
break;
case FadeDirection.In:
do
{
_alpha = Mathf.Lerp(1, 0, timePassed / fadeDuration);
_myCanvas.alpha = _alpha;
timePassed += Time.deltaTime;
yield return null;
} while (timePassed < fadeDuration);
_alpha = 0;
Initiate.DoneFading();
Debug.Log("Your scene has been loaded , and fading in has just ended");
Destroy(gameObject);
break;
}
}
private void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
//We can now fade in
StartCoroutine(FadeIt(FadeDirection.In, _fadeInDuration));
}
}
and
public static class Initiate
{
private static bool areWeFading;
//Create Fader object and assing the fade scripts and assign all the variables
public static void Fade(string scene, Color col, float fadeOutDuration, float fadeInDuration)
{
if (areWeFading)
{
Debug.Log("Already Fading");
return;
}
var init = new GameObject("Fader", typeof(Canvas), typeof(CanvasGroup), typeof(Image), typeof(Fader));
init.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
var fader = init.GetComponent<Fader>();
areWeFading = true;
fader.InitiateFader(init.GetComponent<CanvasGroup>(), init.GetComponent<Image>(), scene, col, fadeOutDuration, fadeInDuration);
}
public static void DoneFading()
{
areWeFading = false;
}
}
than You call this in a dedicated component like in order to be able to do it e.g. in a button's onClick
public class DemoScript : MonoBehaviour
{
//name of the scene you want to load
public string TargetSceneName;
public Color LoadToColor = Color.black;
public float FadeInDuration = 1.0f;
public float FadeOutDuration = 1.0f;
public void GoFade()
{
Initiate.Fade(TargetSceneName, LoadToColor, FadeOutDuration, FadeInDuration);
}
}
or since it is static simply use
Initiate.Fade(TargetSceneName, LoadToColor, FadeOutDuration, FadeInDuration);
from anywhere.
Instead of the LoadSceneAsync you could also do your enabling and disabling stuff if you prefer to do it in only one scene.
However in VR it is actually a bad idea to fade to completely black and let the user see nothing. It might lead to disorientation and cybersickness ...
How to create an interactive GUI object in Unity - Editor Window?
I can draw the static quadrangle like code below. But I want the effect like the reference video that starts from 0:22 to 0:30.
public class EditorCanvas {
public static void DrawQuad(int x, int y, int width, int height, Color color) {
Rect rect = new Rect(x, y, width, height);
Texture2D texture = new Texture2D(1, 1);
texture.SetPixel(0, 0, color);
texture.Apply();
GUI.skin.box.normal.background = texture;
GUI.Box(rect, GUIContent.none);
}
}
public class MyWindow : EditorWindow {
void OnGUI() {
EditorCanvas.DrawQuad(100, 75, 50, 50, Color.black);
}
}
You can declare a rect which contains the current position of your box. In this example the position is initialized to 0,0 for a size of 100,100.
Then for each time you move the mouse while clicking (EventType.MouseDrag) you add the mouse movement since the last event (Event.delta) to the box position.
In order to get a smoothly drag & drop you have to tell to unity that you have an event so he can repaint. (Event.use)
Rect _boxPos = new Rect(0, 0, 100, 100);
void OnGUI()
{
if (Event.current.type == EventType.MouseDrag &&
_boxPos.Contains(Event.current.mousePosition))
{
_boxPos.position += Event.current.delta;
}
GUI.Box(_boxPos, "test");
if (Event.current.isMouse)
Event.current.Use();
}
So now you can easily adapt your DrawQuad method.
I am new to unity I have set the width and color for the line that should appear when I swipe the screen..but its not working, don't know whether my code is wrong
using UnityEngine;
using System.Collections;
public class LinesHandler : MonoBehaviour
{
public Color c1 = Color.yellow;
public Color c2 = Color.red;
private GameObject lineGO;
private LineRenderer lineRenderer;
private int i = 0;
void Start()
{
lineGO = new GameObject("Line");
lineGO.AddComponent<LineRenderer>();
lineRenderer = lineGO.GetComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Mobile/Particles/Additive"));
//lineRenderer.SetColors(c1, c2);
lineRenderer.SetWidth(0.05F, 0);
lineRenderer.SetVertexCount(0);
}
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if(touch.phase == TouchPhase.Moved)
{
lineRenderer.SetVertexCount(i+1);
Vector3 mPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 15);
lineRenderer.SetPosition(i, Camera.main.ScreenToWorldPoint(mPosition));
i++;
/* Add Collider */
BoxCollider bc = lineGO.AddComponent<BoxCollider>();
bc.transform.position = lineRenderer.transform.position;
bc.size = new Vector3(0.1f, 0.1f, 0.1f);
}
if(touch.phase == TouchPhase.Ended)
{
/* Remove Line */
lineRenderer.SetVertexCount(0);
i = 0;
/* Remove Line Colliders */
BoxCollider[] lineColliders = lineGO.GetComponents<BoxCollider>();
foreach(BoxCollider b in lineColliders)
{
Destroy(b);
}
}
}
}
}
Now I am getting a pink color line while swiping on the screen.how can I change that color to white.
also is it possible to reduce the width of line...
Apparently you're setting it right, but the pink color is the default material when the material is missing. I guess its just that it cannot find the material you're trying to apply.
You should check this out.
You'll find an example for the width and colors as well.
This is essentially the same question as posted here: Animating a custom property in a CALayer
over a year ago and hasn't been answered.
Im creating a custom layer and drawing a circle on it. I'd like to be able to animate the radius of the circle (and other properties later). From what i've read, i've set it up like so:
public class CircleLayer : CALayer
{
//[Export("radius")]
//public float Radius { get;set; }
//EDIT: I've now changed the radius field to what is coded below
public float Radius;
[Export("radius")]
public float getRadius()
{
return Radius;
}
[Export("setRadius:")]
public void setRadius(float val)
{
Radius = val;
}
public float Thickness {get;set;}
public CGColor Color {get;set;}
public float GlowAmount {get;set;}
private SizeF GlowOffset {get;set;}
[Export ("needsDisplayForKey:")]
static bool NeedsDisplayForKey (NSString key)
{
Console.WriteLine(key.ToString());
if(key.Equals("radius"))
{
return true;
}
else
return false;
}
public CircleLayer ()
{
if(GlowAmount == 0.0f)
GlowAmount = 10f;
GlowOffset = new SizeF(0f,0f);
//CALayer.NeedsDisplayForKey("radius");
}
public override void DrawInContext (CGContext context)
{
base.DrawInContext (context);
Console.WriteLine("drawing...........");
PointF centerPoint = new PointF(125,125);//this.Frame.Width/2,this.Frame.Height/2);
//Outer circle
context.AddEllipseInRect(new RectangleF(centerPoint.X - Radius,
centerPoint.Y - Radius,
Radius * 2,
Radius * 2));
//Inner circle
context.AddEllipseInRect(new RectangleF(centerPoint.X - InnerRadius,
centerPoint.Y - InnerRadius,
InnerRadius * 2,
InnerRadius * 2));
//Fill in circle
context.SetFillColor(Color);
context.SetShadowWithColor(GlowOffset,GlowAmount,GlowColor);
context.EOFillPath();
}
}
But it just doesn't work. I never get the radius key reported when NeedsDisplayForKey gets called (and prints them to the console). I can animate standard properties no problem (eg: scale)
Edit: Note that I can now successfully modify the value of the property Radius using SetValueForKey. If I do this I need to call SetNeedsDisplay() to update the screen, however i still cannot get the animation to work at all.
Edit #2: Sample attached: http://dl.dropbox.com/u/8617393/GraphicsTest1.zip
This has unfortunately been impossible to do with MonoTouch - but we've fixed it for the next beta (5.3.3) which will hopefully be released soon.
Once 5.3.3 has been released you can use this sample: https://github.com/xamarin/monotouch-samples/tree/monotouch-5.4/CustomPropertyAnimation to see how to do it.