Adding Animation to Inputfield and save the transform - unity3d

I wanted to add a simple search animation to my input field in my unity UI.
This is my InputField and the idea is, when I select it, it should expand slowly, and when I deselect it, it should shrink back to its normal form.
This is the rect Transform Component of this inputfield. I added to the input field and Event Trigger Component and an Animator. I created two Animations called SearchAnimation and DeselectAnimation and added them to my AnimationController called 'SearchController'.
This is how I designed my SearchController:
I set the Transitions between the defaultState and SearchAnimation to be listening to the SelectBool and DeselectBool (the name already describes its purpose).
Then I added the following script to my input Field so that those two booleans will be set accordingly to the event trigger:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class OnClickScript : MonoBehaviour {
Animator anim;
void Start()
{
anim = GetComponent<Animator>();
}
public void OnSelect()
{
anim.SetBool("SelectBool", true);
anim.SetBool("DeselectBool", false);
GetComponent<RectTransform>().sizeDelta = new Vector2(450, 50);
GetComponent<RectTransform>().localPosition.Set(-275, 0, 0);
}
public void OnDeselect()
{
anim.SetBool("DeselectBool", true);
anim.SetBool("SelectBool", false);
GetComponent<RectTransform>().sizeDelta = new Vector2(200, 50);
GetComponent<RectTransform>().localPosition.Set(-130, 0, 0);
}
}
But after the animation is played the inputfield is set back to its inital size and location. How do I fix this?

Easy way:
Create one more clips. The clip is made of only one key which is the Size Delta of the SelectedState. Then make this clip the animation clip of the state.
I put my test project here.
With above approach, once you want to change the size of default state and selected state, you will have to change all four animation clips manually.
Hard way
Using AnimationClip.SetCurve to create animation from the script. With this approach, you can create more maintainable animations. But it's not easy to create complex animations with scripts.
Suggestions:
Using Pivot:
In the script, you are changing local position to prevent the input field moving up. Instead of changing the local position value, you can set the pivot Y to 1 if you want the input field to expand downward.
Using trigger:
Instead of using two bool variables, you can simply use one Trigger to trigger the animation start and move to next state.

Related

How can I control each blend shape value from a script using the blend shape name not by index?

For example I want to change the mouth blend shape value from 0 to 23 randomly to simulate talking when I'm showing text. It's no really the lips will move like the text it's just to simulate talking when the text show so I want to make something more or less randomly values between 0 and 23.
I tried this but it's not working it does nothing. It's not changing the mouse blendshape value at all.
The scripts is attached to the object with the blendshapes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlendShapesController : MonoBehaviour
{
private SkinnedMeshRenderer bodySkinnedMeshRenderer;
// Start is called before the first frame update
void Start()
{
bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
}
// Update is called once per frame
void Update()
{
int rand = Random.Range(0, 23);
bodySkinnedMeshRenderer.SetBlendShapeWeight(0, rand);
}
}
Solution : Since the object with the SkinnedMeshRenderer is a child and the parent have a Animator component I had to change the Animator component settings on the parent object to Animate Physics and Always Animate.
I'm using by index not by name but it's working so I will consider it for the meanwhile as a solution since it's working.

Unity3D How can I select multiple objects in 3D with a drag and select / lasso select?

I am struggling to find a good tutorial or informations that would allow me to select multiple objects in 3D in a user friendly manner.
So far, the best tutorial I found is this one : https://sharpcoderblog.com/blog/unity-3d-rts-style-unit-selection. The tutorial works by using the transform.position of the selectable objects and checking if it within the user's selection.
What I wish is to have the user be able to select a unit even if it is only partially within the user's selection such as most RTS games do ( both in 2D and 3D ).
One possibility would be to create a temporary mesh using the camera's clipping distances and the user's selection and then check for collisions but I was not able to find any tutorials using this method nor do I know if it is the best approach to the subject.
If I understand correctly you want to
somehow start a selection
collect every object that was "hit" during the collection
somehow end the collection
Couldn't you simply use raycasting? I will assume simple mouse input for now but you could basically port this to whatever input you have.
// Just a little helper class for an event in the Inspector you can add listeners to
[SerializeField]
public class SelectionEvent : UnityEvent<HashSet<GameObject>> { }
public class SelectionController : MonoBehaviour
{
// Adjust via the Inspector and select layers that shall be selectable
[SerializeField] private LayerMask includeLayers;
// Add your custom callbacks here either via code or the Inspector
public SelectionEvent OnSelectionChanged;
// Collects the current selection
private HashSet<GameObject> selection = new HashSet<GameObject>();
// Stores the current Coroutine controlling he selection process
private Coroutine selectionRoutine;
// If possible already reference via the Inspector
[SerializeField] private Camera _mainCamera;
// Otherwise get it once on runtime
private void Awake ()
{
if(!_mainCamera) _mainCamera = Camera.main;
}
// Depending on how exactly you want to start and stop the selection
private void Update()
{
if(Input.GetMouseButtonDown(0))
{
StartSelection();
}
if(Input.GetMouseButtonUp(0))
{
EndSelection();
}
}
public void StartSelection()
{
// if there is already a selection running you don't wanr to start another one
if(selectionRoutine != null) return;
selectionRoutine = StartCoroutine (SelectionRoutine ());
}
public void EndSelection()
{
// If there is no selection running then you can't end one
if(selectionRoutine == null) return;
StopCoroutine (selectionRoutine);
selectionRoutine = null;
// Inform all listeners about the new selection
OnSelectionChanged.Invoke(new HashSet<GameObject>(selection);
}
private IEnumerator SelectionRoutine()
{
// Start with an empty selection
selection.Clear();
// This is ok in a Coroutine as long as you yield somewhere within it
while(true)
{
// Get the ray shooting forward from the camera at the mouse position
// for other inputs simply replace this according to your needs
var ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
// Check if you hit any object
if(Physics.Raycast(ray, out var hit, layerMask = includeLayers ))
{
// If so Add it once to your selection
if(!selection.Contains(hit.gameObject)) selection.Add(hit.gameObject);
}
// IMPORTANT: Tells Unity to "pause" here, render this frame
// and continue from here in the next frame
// (without this your app would freeze in an endless loop!)
yield return null;
}
}
}
Ofcourse you could do it directly in Update in this example but I wanted to provide it in a way where you can easily exchange the input method according to your needs ;)
From UX side you additionally might want to call a second event like OnSelectionPreviewUpdate or something like this every time you add a new object to the selection in order to be able to e.g. visualize the selection outcome.
I might have understood this wrong and it sounds like you rather wanted to get everything inside of a drawn shape.
This is slightly more complex but here would be my idea for that:
Have a dummy selection Rigidbody object that by default is disabled and does nothing
don't even have a renderer on it but a mesh filter and mesh collider
while you "draw" create a mesh based on the input
then use Rigidbody.SweepTestAll in order to check if you hit anything with it
Typed on smartphone but I hope the idea gets clear
I think I would try to create a PolygonCollider2D because it is quite simple comparing to creating a mesh. You can set its path (outline) by giving it 2D points like location of your pointer/mouse. Use the SetPath method for it. You can then use one of its methods to check if another point in space overlaps with that collider shape.
While the PolygonCollider2D interacts with 2D components you can still use its Collider2D.OverlapPoint method to check positions/bounds of your 3D objects after translating it to 2D space.
You can also use its CreateMesh method to create a mesh for drawing your selection area on the screen.
You can read more about the PolygonCollider2D here.
Hope it makes sens and hope it helps.

HoloLens - Unity: How to change color of cursor?

I tried to change the color via script on runtime of the cursor and it worked to 75%:
Mesh_top is the only part that does not change the color and I dont know why.
All 4 parts use the same material, named "cursormaterial".
What I tried:
Changing the color by referencing to cursormaterial
Changing the color by getting the component SkinnedMeshRenderer
Trying to use ProptertyBlock
In all three cases I got the same result. The only thing that works is before hitting play I can change the color, this will change the color of the whole cursor. Changing it on runtime works only for 3 of 4 parts...ยด
--Edit--
public SkinnedMeshRenderer cursorRendererOne, cursorRendererTwo, cursorRendererThree, cursorRendererFour;
private MaterialPropertyBlock _propBlock;
public Material material;
void Start()
{
_propBlock = new MaterialPropertyBlock();
}
public void OnInputDown(InputEventData eventData)
{
if (!isActivated)
{
//#1
material.color = Color.blue;
//#2
cursorRendererOne.sharedMaterial.color = Color.blue;
//#3
cursorRendererOne.GetPropertyBlock(_propBlock);
_propBlock.SetColor("_Color", Color.blue);
cursorRendererOne.SetPropertyBlock(_propBlock);
cursorRendererTwo.SetPropertyBlock(_propBlock);
cursorRendererThree.SetPropertyBlock(_propBlock);
cursorRendererFour.SetPropertyBlock(_propBlock);
isActivated = true;
}
Here u see the changed material, but the mesh_top looks but different:
This is a "Bug" (maybe an intended one?).
Open the Animation window (CTRL + 6)
And in the hierachy select the CursorVisual
If you now go to the animation called CursorWaitingAnim you can see there is a keyframe for the top_mesh color.
This single keyframe causes that the color of that tile can not be changed on runtime. The reason is that the animator runs after OnInputDown so it reverts the changes for any keyframed property.
So if you don't need the Waiting animation simply remove that keyframe.
=> you can manipulate the color at runtime again!
Alternatively you could replace it by a one that instead of fixing the color simply disables the SkinnedMeshRenderer instead which basically has more or less the same effect but doesn't screw the colors:

Have slider value set both by script and in UI using slider handle

So I have a UI Slider on my Canvas. I am also changing the value of the slider (and therefore position of the handle) from a script. But now the slider cant be move in the UI on runtime anymore. Once I remove the line in my code, it works again. Is there any way to change the value both from the script and in the UI via mouse at runtime, i.e. stop unity from blocking it in the UI when changing it in the script?
#Immersive was absolutely right. The value was changed within Update() on every frame. Changed it so it only is changed when necessary.
Thanks a lot.
using UnityEngine;
using UnityEngine.UI;
public class SliderExample : MonoBehaviour
{
public Slider slider;
public Text displaySliderValue;
private float sliderValue;
private void Update()
{
displaySliderValue.text = sliderValue.ToString();
sliderValue = slider.value;
}
}

Why the IK controller is not exist in the ThirdPersonController Animator component in the Inspector?

I'm trying to follow the instructions in the unity documents how to use Inverse Kinematics in this page:
Inverse Kinematics
But i don't have the IK Animator Controller when i select Controller for the Animator.
I tried adding the script now. But the hand the right hand is folded to the other side. It's not like it's holding the flash light: The script is attached to the ThirdPersonController. And i dragged to the script in the Inspector to the rightHandObj the EthanRightHand and to the lookObj i dragged the Flashlight.
But the hand seems to be wrong way.
This is the script i'm using now the IKControl:
using UnityEngine;
using System;
using System.Collections;
[RequireComponent(typeof(Animator))]
public class IKControl : MonoBehaviour
{
protected Animator animator;
public bool ikActive = false;
public Transform rightHandObj = null;
public Transform lookObj = null;
void Start()
{
animator = GetComponent<Animator>();
}
//a callback for calculating IK
void OnAnimatorIK()
{
if (animator)
{
//if the IK is active, set the position and rotation directly to the goal.
if (ikActive)
{
// Set the look target position, if one has been assigned
if (lookObj != null)
{
animator.SetLookAtWeight(1);
animator.SetLookAtPosition(lookObj.position);
}
// Set the right hand target position and rotation, if one has been assigned
if (rightHandObj != null)
{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandObj.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandObj.rotation);
}
}
//if the IK is not active, set the position and rotation of the hand and head back to the original position
else
{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 0);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 0);
animator.SetLookAtWeight(0);
}
}
}
}
Add an empty game object to the flashlight and target that instead of the flashlight object itself. Hit play and then fiddle with the placement of the empty object until it is where you want it. Then just turn the flashlight into a prefab, stop play mode and make sure the flashlight in the scene matches the prefab (you can just use revert to do that if needed).
Now it would be doing exactly what you want every time. You could even have multiple prefabs with the empty object positioned differently to allow characters with larger or smaller hands to hold it convincingly.