How to do "highlighting" on gameObject - unity3d

I'm making an block-like game and now I want to implement highlighting block (gameobject) while there is mouseOver that specific block.
I tried something like this (I'm not sure that this is best way to do this, but it's only one I got idea for):
#pragma strict
public class BlockSelecting extends MonoBehaviour {
public var hovering : boolean = false;
public var xpos : float;
public var ypos : float;
function Start () {
}
function Update () {
}
function OnMouseExit () {
hovering = false;
}
function OnMouseOver ()
{
hovering = true;
xpos = Input.mousePosition.x;
ypos = Input.mousePosition.y;
}
function OnGUI ()
{
GUI.DrawTexture(new Rect(xpos, xpos, 26, 26), (Resources.Load("highlight") as Texture2D));
}
}
This is not working, since Texture is not showing where mouse pointer is. Is there something I can't see or is this wrong way to do this? My highlight resource is just an 26x26 (block is 25x25) 2D texture of transparent rectangle, so it looks like it's highlighted...
P.S. My plan is to use hovering boolean to check if player is still hovering, if not texture should be deleted/hidden (any ideas on how to do this?).

lets say you want to change the color of the object that mouse is on to red so
you should use OnMouseEnter to check if mouse is on your object and OnMouseExit for when mouse exits the objects area and we set the it`s color back to its original color that was before changing it
private color tempColor;
void OnMouseEnter()
{
tempColor = renderer.material.color;
renderer.material.color = Color.red;
}
void OnMouseExit()
{
renderer.material.color = tempColor;
}

Related

(Unity) Is it possible to animate an object from a color back to it's original using an Animator?

The behavior I'm trying to get out of Unity's Animator looks like this in code:
Color animationColor = Color.red;
Color originalColor = GetComponent<Renderer>().material.GetColor("_Color");
//...
renderer.material.SetColor("_Color", Color.Lerp(animationColor, originalColor, timeElapsed/animationTime));
I have no problem getting the animator to Lerp between two set colors, but I would like to have destination color be the original color of the object. Can this be done with the Animation system?
I am not sure if it is possible just with the Animation System itself but you could just have a script attached which assigns a property named OriginalColor and have a function to return to this color when ever you want.
Such script could look like:
using UnityEngine;
using System.Collections;
public class ChangeColor : MonoBehaviour {
public Renderer Renderer;
public Color OriginalColor;
private bool _isOriginal
public bool DoLerp = false;
void Start() {
Renderer = GetComponent<Renderer>()
OriginalColor = Renderer .material.GetColor("_Color");
}
void Update() {
if(!DoLerp)
return;
// timeElepsed stuff
if(!isOriginal) {
renderer.material.SetColor(
"_Color",
Color.Lerp(
animationColor,
originalColor,
timeElapsed/animationTime
)
);
} else {
renderer.material.SetColor(
"_Color",
Color.Lerp(
originalColor,
animationColor,
timeElapsed/animationTime
)
);
}
// If timeElepsed = end time then set DoLerp to false
}
}

Change material on click Unity3D

I am trying to change material rendered on a cube upon click. Simple if else condition (I believe). I have tried
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Click");
if (GetComponent<Renderer>().material == brick)
{
Debug.Log("Brick");
GetComponent<Renderer>().material = normal;
}
else if(GetComponent<Renderer>().material == normal)
{
Debug.Log("Normal");
GetComponent<Renderer>().material = brick;
}
}
}
I am getting in the first loop ie GetMouseButtonDown. But Nothing happens to the cube after that. no errors or warnings.
I would rather use a bool flag instead of checking the material reference
// Set the initial value via the Inspector
// to decide whether this should start as brick or normal
[SerializeField] private bool isBrick;
// If possible already drag in the renderer via the Inspector
[SerializeField] private Renderer _renderer;
// If ever needed later let others know directly whether this is a brick
// this property allows read-only acccess
public bool IsBrick => isBrick;
private void Awake ()
{
// As fallback get the renderer ONCE on runtime
if(!_renderer) _renderer = GetComponent<Renderer>();
// Apply the initial state
_renderer.material = isBrick ? brick : normal;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Click");
Debug.Log(isBrick ? "Brick" : "Normal");
// Invert the bool flag
isBrick = !isBrick;
// Apply the new state
_renderer.material = isBrick ? brick : normal;
}
}

Unity renders object before updating Rigidbody2D's properties

I am trying to implement a way for the player to switch between a square and a circle. For this to work my player object has two colliders, one circle and one box. When switching between them I simply disable one collider and enable the other, and switch the current sprite. The issue arises when I switch from a circle to a square.
I want the square to be able to glide across the floor, whereas the circle is supposed to roll. In order to make the switch seamless I have to reorient the square to be aligned with the current velocity, and remove the angular velocity. This does seem to work, however there is a slight period of frames (or frame) where the square has the same rotation the circle had before switching. This seems odd to me since the new rotation and sprite is changed in the same part of the code. This is a video showcasing the issue.
If this is an issue resulting from the way the objects are rendered I can solve this another way. I would just like to understand why it happens.
Code snippet of the part that changes the properties from circle to square when switching:
else if (Input.GetKeyDown("2"))
{
// Update rotation of box to velocity:
float newAngleRadians = Mathf.Atan2(rb.velocity.y, rb.velocity.x);
float newAngleDegrees = newAngleRadians * 180 / Mathf.PI;
rb.rotation = newAngleDegrees;
rb.angularVelocity = 0;
Debug.Log(rb.rotation);
playerShape = Shape.SQUARE;
spriteRenderer.sprite = spriteArray[1];
circleCollider.enabled = false;
boxCollider.enabled = true;
updateShape = true;
}
Logging the angle of the rigidbody directly after setting it to newAngleDegrees shows that the rotation has been set correct, yet the issue persists.
And just in case it is needed, full code of the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class scr_StateMachine : MonoBehaviour
{
// Types of shapes:
public enum Shape { SQUARE, CIRCLE, TRIANGLE }
// Variables:
public Rigidbody2D rb;
public SpriteRenderer spriteRenderer;
public Sprite[] spriteArray;
public Shape playerShape;
public CircleCollider2D circleCollider;
public BoxCollider2D boxCollider;
private bool updateShape;
void Start()
{
playerShape = Shape.CIRCLE;
updateShape = true;
}
void Update()
{
// Get input for shape change:
if(Input.GetKeyDown("1"))
{
playerShape = Shape.CIRCLE;
spriteRenderer.sprite = spriteArray[0];
circleCollider.enabled = true;
boxCollider.enabled = false;
updateShape = true;
}
else if (Input.GetKeyDown("2"))
{
// Update rotation of box to velocity:
float newAngleRadians = Mathf.Atan2(rb.velocity.y, rb.velocity.x);
float newAngleDegrees = newAngleRadians * 180 / Mathf.PI;
rb.rotation = newAngleDegrees;
rb.angularVelocity = 0;
Debug.Log(rb.rotation);
playerShape = Shape.SQUARE;
spriteRenderer.sprite = spriteArray[1];
circleCollider.enabled = false;
boxCollider.enabled = true;
updateShape = true;
}
// Update movement script's shape:
if (updateShape)
{
GetComponent<scr_Movement>().shape = playerShape;
updateShape = false;
}
}
}
I think the issue is Rigidbody/Rigidbody2D physics are applied in fixed time steps (see FixedUpdate). Therefore yes, on a strong device it can definitely happen that you render some frames before the next FixedUpdate call kicks in and changes the Rigidbody/Rigidbody2D behavior.
I think what you could do is actually wait with the change for the next FixedUpdate call e.g. using a Coroutine and WaitForFixedUpdate like e.g.
public class scr_StateMachine : MonoBehaviour
{
// Types of shapes:
public enum Shape { SQUARE, CIRCLE, TRIANGLE }
// Variables:
public Rigidbody2D rb;
public SpriteRenderer spriteRenderer;
public Sprite[] spriteArray;
public Shape playerShape;
public CircleCollider2D circleCollider;
public BoxCollider2D boxCollider;
[SerializeField] private scr_Movement _scrMovement;
void Start()
{
if(!_scrMovement) _scrMovement = GetComponent<scr_Movement>();
ChangeShape(Shape.CIRCLE);
}
// Still get User Input in Update
void Update()
{
// Get input for shape change:
if(Input.GetKeyDown("1"))
{
ChangeShape(Shape.CIRCLE);
}
else if (Input.GetKeyDown("2"))
{
ChangeShape(Shape.SQUARE);
}
}
private void ChangeShape(Shape newShape)
{
// if the same shape comes in we already have -> nothing to do
if(newShape == playerShape) return;
// Stop any already running coroutine since we only want to handle the last input before the FixUpdate call
// https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopAllCoroutines.html
StopAllCoroutines();
// Start a new coroutine for the new shape
// see https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
StartCoroutine(ChangeShapeInNextFixedUpdate(newShape));
}
private IEnumerator ChangeShapeInNextFixedUpdate(Shape newShape)
{
// just in case again, if the same shape comes in we already have -> nothing to do
if(newShape == playerShape) yield break;
// Wait until the next FixedUpdate call
// see https://docs.unity3d.com/ScriptReference/WaitForFixedUpdate.html
yield return new WaitForFixedUpdate();
// Now do your required changes depending on the new shape
circleCollider.enabled = newShape == Shape.CIRCLE;
boxCollider.enabled = newShape == Shape.SQUARE;
switch(newShape)
{
case Shape.CIRCLE:
spriteRenderer.sprite = spriteArray[0];
break;
case Shape.SQUARE:
// Update rotation of box to velocity:
var newAngleRadians = Mathf.Atan2(rb.velocity.y, rb.velocity.x);
// see https://docs.unity3d.com/ScriptReference/Mathf.Rad2Deg.html
var newAngleDegrees = newAngleRadians * Mathf.Rad2Deg;
rb.rotation = newAngleDegrees;
rb.angularVelocity = 0;
Debug.Log(rb.rotation);
spriteRenderer.sprite = spriteArray[1];
break;
}
playerShape = newShape;
_scrMovement.shape = playerShape;
}
}

Can't double tap

I have a question I can't figure out myself for a long time. If I have a cube game object in Unity and I want to change it's color to red when I press the space key, all I have to do is write a script and write:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GameObject Cube = GameObject.FindWithTag("Cube");
Cube.GetComponent<Renderer>().material.color = Color.red;
}
}
But what if I want to change it's color to blue when I press the space key again?
For example, you can use a variable is_red and check its value in your update. Or maybe I didn't understand the problem
If you want to change the color on the second click you can simply add a If-statement that checks if the color is already equal to the wanted color and if it is change it to another color.
Example:
if (Input.GetKeyDown(KeyCode.Space)) {
// Get Cube GameObject
GameObject Cube = GameObject.FindWithTag("Cube");
// Get the Cube Renderer
Renderer rd = Cube.GetComponent<Renderer>();
// Is the color equal to red if it already is,
// change it to blue instead
if (rd.material.color = Color.red) {
rd.material.color = Color.blue;
}
else {
rd.material.color = Color.red;
}
}
Unrelated:
If the GameObject that has the Cube Tag stays the same GameObject over the game, I would advise you to rather "fetch" the GameObject and it's Renderer Component once at the start of the game instead of each time you press the space Key, to increase performance.
private GameObject cubeGO;
private Renderer cubeRD;
private void Start {
// Get Cube GameObject
cubeGO = GameObject.FindWithTag("Cube");
// Get the Cube Renderer
cubeRD = Cube.GetComponent<Renderer>();
}
You can now use these variables inise the Input.GetKeyDown(){}
if (Input.GetKeyDown(KeyCode.Space)) {
// Is the color equal to red if it already is,
// change it to blue instead
if (cubeRD.material.color = Color.red) {
cubeRD.material.color = Color.blue;
}
else {
cubeRD.material.color = Color.red;
}
}

Align variables horizontally in unity inspector

I'm writing a very simple class for storing data (at least, for now) for use in a Unity project to do with a learning AI. I want to be able to easily customize multiple agents in the inspector and the number of checkboxes stacked vertically makes this part of my project dominate the inspector. If I could simply have one section make use of the ample empty space on the right side of the inspector, that would be considerably less ugly.
I've been reading a lot about custom property drawers and inspector windows, but it seems like a lot of work, involving rewriting how the entire class is displayed, for one change.
For reference, here is the class itself.
[System.Serializable]
public class NNInfo
{
public string networkName = "Agent";
[Header("Movement Properties")]
public float movementSpeed = 10f;
public float rotationSpeed = 1f;
[Header("Learning Properties")]
public float learningRate = 0.1f;
public float momentum = 0f;
public Rewards rewardFunc;
public int[] hiddenLayers;
[Header("Other Properties")]
public int maxHealth = 1000;
[Header("Optional Inputs")]
public bool m_PointToNearestBall = false; // input which is 1 while the agent is facing a ball and -1 when facing away
public bool m_DistanceToNearestBall = false; // input which is 1 while the agent is at the ball and -1 when at max possible distance away
public bool m_PointToEndzone = false; // similar to m_PointToNearestBall but for the endzone
public bool m_Position = false; // two inputs which inform the player of its normalized x and y coordinates on the field
public bool m_WhetherHoldingBall = false; // tells the player whether its holding a ball
public bool m_CanSeeHealth = false; // Whether the player can know its own health
[Header("Optional Outputs")]
public bool m_ForwardLeft = false; // turn left and move forward simultaneously
public bool m_ForwardRight = false; // turn right and move forward simultaneously
public bool m_Reverse = false; // move backwards
public bool m_Flip = false; // instantly switch to opposite direction
public bool m_TurnToBall = false; // instantly turn to face nearest ball
public bool m_TurnToLeft = false; // instantly turn to face left side of field
public bool m_Attack = false; // attack a player (or idle if no contact)
}
Custom Property Drawer is keyword which should be interesting for you.
Writing your own code for managing those - lets you describe how you want to show your properties inside Inspector View in Unity editor.
To start, go to official documentation site, which contains code you can base on.
Code snippets (Javacrpt, c# version can be found under the link):
Object's code:
enum IngredientUnit { Spoon, Cup, Bowl, Piece }
// Custom serializable class
class Ingredient extends System.Object {
var name : String;
var amount : int = 1;
var unit : IngredientUnit;
}
var potionResult : Ingredient;
var potionIngredients : Ingredient[];
function Update () {
// Update logic here...
}
Editor code:
#CustomPropertyDrawer(Ingredient)
class IngredientDrawer extends PropertyDrawer {
// Draw the property inside the given rect
function OnGUI (position : Rect, property : SerializedProperty, label : GUIContent) {
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty (position, label, property);
// Draw label
position = EditorGUI.PrefixLabel (position, GUIUtility.GetControlID (FocusType.Passive), label);
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Calculate rects
var amountRect = new Rect (position.x, position.y, 30, position.height);
var unitRect = new Rect (position.x+35, position.y, 50, position.height);
var nameRect = new Rect (position.x+90, position.y, position.width-90, position.height);
// Draw fields - passs GUIContent.none to each so they are drawn without labels
EditorGUI.PropertyField (amountRect, property.FindPropertyRelative ("amount"), GUIContent.none);
EditorGUI.PropertyField (unitRect, property.FindPropertyRelative ("unit"), GUIContent.none);
EditorGUI.PropertyField (nameRect, property.FindPropertyRelative ("name"), GUIContent.none);
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty ();
}
}