Change hand IK over time instead of it just snapping to 0 and 1 - unity3d

I'm trying to get my characters rig to go from hand IK 0 to 1 over a period of time instead of just snapping to it. How could I go about doing that?
public TwoBoneIKConstraint rightHandIK; //on animator manager script // getting hand IK from inspector so can change weight
public TwoBoneIKConstraint leftHandIK; //on animator manager script
if (animatorManager.isAimingGunn)
{
animatorManager.leftHandIK.weight = 1;
animatorManager.rightHandIK.weight = 1;
horizontalMovementInput = 0f;
verticalMovementInput = 0f;
}
else if(animatorManager.isAimingGunn == false)
{
animatorManager.rightHandIK.weight = 0;
animatorManager.leftHandIK.weight = 0;
}

For this purpose you need to run a Tweener. This is a basic float Tweener that smoothly changes the floats by running Coroutine.
public IEnumerator DoTween(Func<float> getter , Action<float> setter, float targetValue, float duringTime = 1f)
{
var progress = 0f;
var init = getter();
while (progress < 1)
{
progress += Time.deltaTime/duringTime;
setter.Invoke(Mathf.Lerp(init, targetValue, progress));
yield return new WaitForEndOfFrame();
}
}
Do this to run Tweener in if code, Also, to zero the weight only, set zero in the targetValue parameter:
// How to run tweener?
// for E.G set IK weight to 1 during 2 seconds
StartCoroutine(DoTween(
() => animatorManager.leftHandIK.weight,
x => animatorManager.leftHandIK.weight = x,
1f, 2f
));
Also, if you want to create similar effects in the code, it suggests using the great DoTween unity plug-in.

Related

Unity : How can I create and scale up 100 cubes over time one by one with using Vector3.Lerp function

How can I create and scale up 100 cubes over time one by one with using Vector3.Lerp function. I need to be dependent on the duration variable. I need an efficient way.
Though your question is pretty vague:
Use a Coroutine like e.g.
// Adjust these in the Inspector
[SerilaizeField] private Vector3 targetScale = Vector3.one;
[SerilaizeField] private Vector3 startScale = Vector3.zero;
[SerilaizeField] private float durationPerCube = 1f;
[SerilaizeField] private Transform[] cubes;
private IEnumerator ScaleUpRoutine()
{
if(cubes.Length == 0) yield break;
foreach(var cube in cubes)
{
var timePassed = 0f;
while(timePassed < durationPerCube)
{
var factor = timePassed / duration;
// optional easing-in and -out
//factor = Mathf.SmoothStep(0, 1, factor);
cube.localScale = Vector3.Lerp(startScale, targetScale, factor);
timePassed += Time.deltaTime;
yield return null;
}
// just to be sure set a hard value
cube.localScale = targetScale;
}
}
You start it whenever you want via StartCoroutine e.g.
private void Start()
{
StartCoroutine(ScaleUpRoutine);
}
If you rather wanted it based on overall duration you could do
// Adjust this in the Inspector
[SerilaizeField] private float overallDuration = 2f;
private IEnumerator ScaleUpRoutine()
{
if(cubes.Length == 0) yield break;
var durationPerCube = overallDuration / cubes.Length;
...
Keep in mind though that yield return null; currently waits for at least one frame per cube so if your overallDuration is very small (< 100 frames) it might not be accurate. In this case you might want to add a StopWatch in order to only yield return a frame after a certain real-time threshold like
...
private IEnumerator ScaleUpRoutine()
{
if(cubes.Length == 0) yield break;
var stopWatch = new StopWatch();
stopWatch.Restart();
...
while(timePassed < durationPerCube)
{
var factor = timePassed / duration;
// optional easing-in and -out
//factor = Mathf.SmoothStep(0, 1, factor);
cube.localScale = Vector3.Lerp(startScale, targetScale, factor);
timePassed += Time.deltaTime;
if(stopWatch.ElapsedMilliseconds >= maxMillisecondPerFrame)
{
yield return null;
stopWatch.Restart();
}
}
...
For the creating part: You shouldn't.
Depending on your needs I would either
Use a hidden object that already holds 100 cubes in the scene. Only display it when needed
Extend the same idea a bit further and use pooling instead

unity update skipping calculation in a frame

I'm Instantiating tiles as a path, on every four instantiating's tiles, they should start instantiating towards right, after four instantiating's, they go straight again.
to check this logic I, made a variable spawnOffset and incrementing +1 each frame.
if spawnOffset % 4 == 0 change direction
but, i'm not getting a change in direction in regular intervals, when i debug, the frame skips and so is the logic
public GameObject go;
public Transform Playertransform;
public Vector3 tileSpawnOffset = Vector3.zero;
private Vector3 direction = Vector3.forward;
public int SpawnOffset = -3;
private bool turnRight = false;
void Update()
{
SpawnPath();
ChangeDirection();
}
void SpawnPath()
{
if (Playertransform.position.z > SpawnOffset)
{
tileSpawnOffset += direction;
Instantiate(go, this.transform.position + tileSpawnOffset, this.transform.rotation, this.transform);
SpawnOffset++;
}
}
void ChangeDirection()
{
if (SpawnOffset % 4 == 0)
{
turnRight = !turnRight;
}
direction = turnRight == true ? Vector3.right : Vector3.forward;
}
and but when i tried with time instead
float time = 0f;
void ChangeDirection()
{
time += Time.deltaTime;
if (time > 1)
{
turnRight = !turnRight;
time = 0;
}
direction = turnRight == true ? Vector3.right : Vector3.forward;
}
it works perfectly fine. so, how could i fix it. I, don't want use time, i want to change direction exactly after 4 tiles spawned
Since your always calling both SpawnPath(); ChangeDirection(); everyframe if (Playertransform.position.z > SpawnOffset) is ever false twice in a row your turnright bool gets flipped regardless every frame your on a multiple of 4. You don't need to call ChangeDirection if you didn't spawn a new tile. if you just remove that call from update and add it at right after your increment SpawnOffset its probably going to fixe it.

Unity-I can't seem to lerp my GameObject's Color

I'm just trying to make an observer pattern program one object randomly changes color and causes a group of other objects to change the same color, but I wanted them to gradually change over 5 seconds. I'm trying lerp, but it just instantly swaps colors. I think it maybe has something to do with the lerp's starting color, because the main object is constantly shifting colors and new colors become old colors. So I need to think of how choose a starting color for the lerp. I'm not sure if this has to do with my the lerp isn't working, but it's what I'm considering. If anyone else has any suggestions, I would appreciate it. Thank you.
public class Subject : MonoBehaviour {
public float timer = 0.0f;
public GameObject[] observers;
float t = 0;
Color oldColor;
void Update () {
t += Time.deltaTime / 5.0f;
timer += Time.deltaTime;
if (timer >= 10f) {
Color newColor = new Color(Random.value, Random.value, Random.value, 1.0f);
GetComponent<Renderer>().material.color = newColor;
for (int i = 0; i < observers.Length; i++) {
observers[i].GetComponent<Renderer>().material.color = Color.Lerp(oldColor, newColor, t);
}
newColor=oldColor
timer = 0;
}
}
}
What Color.Lerp(Color a, Color b, float t) does is
Linearly interpolates between colors a and b by t
For example, Color.Lerp(Color.blue, Color.red, 0.5f) returns a half way interpolation between blue and red.
When you say Color.Lerp(oldColor, newColor, 5) it will return newColor, because
t is clamped between 0 and 1. When t is 0 returns a. When t is 1
returns b.
So 5 is the same thing as putting in 1.
So you will need to keep a t variable somewhere that starts at 0 and increments up to 1, you put into Lerp.
For example:
float t = 0;
private void Update()
{
t += Time.deltaTime / 5.0f; // Divided by 5 to make it 5 seconds.
this.GetComponent<Renderer>().color = Color.Lerp(oldColor, newColor, t);
}
https://docs.unity3d.com/ScriptReference/Color.Lerp.html
EDIT
Also whats happening in your code, assuming you reset t when timer hit
Timer >= 10.0f
Lerp once
Reset timer to 0
.
What you could do instead is something like this (i took out the observers and stuff just to make it easier to see)
float t, timer;
bool lerping = false;
Color newColor;
void Update()
{
t += Time.deltaTime / 5.0f;
timer += Time.deltaTime;
if (timer >= 10f)
{
lerping = true;
t = 0;
newColor = new Color(Random.value, Random.value, Random.value, 1.0f);
Debug.Log("Lerping!");
timer = 0;
}
if (lerping)
{
GetComponent<Renderer>().material.color = Color.Lerp(oldColor, newColor, t);
if (t >= 1.0f)
{
lerping = false;
Debug.Log("Stopped lerping");
}
}
}

Lifting platforms is not working as it should

I would like to make some lifting platforms in my game, so if the platform went down, the characters can't go over it. I have written a script for it, but for some reason the "lifting up" is not working as intended. It won't go back to its starting place, but it will go a bit below. And for some reason it won't go smoothly to the place where it should, just "teleport" there and done. I thougt multiplying Time.deltaTime with a const will help, but it is the same.
Here is my code, any help would be appreciated:
public class LiftingPlatform : MonoBehaviour {
private Transform lift;
private bool isCanBeLifted;
private float timeToLift;
public float timeNeededToLift = 5f;
private Vector3 startPos;
private Vector3 downPos;
private Vector3 shouldPos;
private bool isDown;
public GameObject[] collidingWalls;
// Use this for initialization
void Start () {
lift = transform;
isCanBeLifted = true;
timeToLift = 0f;
isDown = false;
startPos = transform.position;
downPos = new Vector3(startPos.x, startPos.y - 5f, startPos.z);
}
// Update is called once per frame
void Update () {
timeToLift += Time.deltaTime;
if (timeToLift >= timeNeededToLift) {
if (isCanBeLifted) {
if (isDown) {
shouldPos = Vector3.Lerp(startPos, downPos, Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = true;
}
else if (!isDown) {
shouldPos = Vector3.Lerp(downPos, new Vector3(startPos.x, startPos.y, startPos.z), Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = false;
}
}
timeToLift = 0;
}
if (!isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(true);
}
}
else if (isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(false);
}
}
}
void OnTriggerEnter(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = false;
}
}
void OnTriggerExit(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = true;
}
}
}
These lifting platforms are a child of another Platforms object.
It doesn't look like you are updating the object's position every frame. You are only checking if the total time passed is greater than the time needed to lift, and then updating the position to a value that is dependent on the delta time (using the Vector3.Lerp function).
What I would do is in the update step, if timeToLift is greater then timeNeededToLift, subtract the latter from the former and invert the value of isDown. Then, in your Vector3.Lerp, make the third argument (timeToLift / timeNeededToLift) instead of (Time.deltaTime * 10). Can you try that and see if it works?
The third argument for Vector3.Lerp is the "blending factor" between the two vectors, 0 is the first vector, 1 is the second, and 0.5 is in between. If the total time is greater than the time needed to lift, but the delta time is not greater than 1, it will get the position of the platform using a blending factor of less than 1, resulting in a platform that didn't move fully.

Unity3D Third Person Controller and Animations

My problem is that a custom walk animation I have set in the third person controller of Unity3D is not shown.
The animation is imported from a FBX file with the model#walk.fbx structure. I know the animation works, because if I use it as the idle animation it shows. On the other hand, it also doesn't seem to be an issue with the controller script, as it works fine with the prototype character.
It seems that the animation being played is the one selected in the Animation component (which has 'Play Automatically' selected). Any attempts to change the animation from the third person controller work for the prototype character, but not for mine.
I don't have nor want run and jump animations. With the prototype character I set the walk animation on each of those with no adverse effects. The controller doesn't turn off animation this way, seeing that there are no log entries in the console from the third person controller script. The relevant line with the CrossFade call is getting called.
Any clues as to where I could look next? Is it more likely an issue with the controller, or with the animation? Something else completely?
Update: Below is the code of my controller. It works fine when I use the sample model of the construction worker that is provided with Unity. The lines with _animation.CrossFade are getting called at the expected times. Using Play or Blend instead doesn't help. There are no errors logged in the console.
For our custom animations however it doesn't work. I am now suspecting the issues lies with the model. Unfortunately I am not at liberty to share a sample of that model. I've asked the animator for further details on how he created the FBX export. Are there any specific settings he needs to use for the model to work in Unity? It remains odd though that the animations do work if I add them indepently to the scene.
// Require a character controller to be attached to the same game object
#script RequireComponent(CharacterController)
public var idleAnimation : AnimationClip;
public var walkAnimation : AnimationClip;
public var walkMaxAnimationSpeed : float = 0.75;
private var _animation : Animation;
enum CharacterState {
Idle = 0,
Walking = 1,
}
private var _characterState : CharacterState;
// The speed when walking
var walkSpeed = 2.0;
var speedSmoothing = 10.0;
var rotateSpeed = 500.0;
var targetPrecision = 5;
var targetMaxDistance = 200;
// The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
private var lockCameraTimer = 0.0;
// The current move direction in x-z
private var moveDirection = Vector3.zero;
// The current x-z move speed
private var moveSpeed = 0.0;
// The last collision flags returned from controller.Move
private var collisionFlags : CollisionFlags;
// Are we moving backwards (This locks the camera to not do a 180 degree spin)
private var movingBack = false;
// Is the user pressing any keys?
private var isMoving = false;
private var isControllable = true;
private var isTargetting : boolean = false;
private var targetPoint : Vector3 = Vector3.zero;
function Awake () {
moveDirection = transform.TransformDirection(Vector3.forward);
_animation = GetComponent(Animation);
if(!_animation)
Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
if(!idleAnimation) {
_animation = null;
Debug.Log("No idle animation found. Turning off animations.");
}
//_animation[idleAnimation.name] = idleAnimation;
if(!walkAnimation) {
_animation = null;
Debug.Log("No walk animation found. Turning off animations.");
}
//_animation[walkAnimation.name] = walkAnimation;
}
function UpdateSmoothedMovementDirection () {
var cameraTransform = Camera.main.transform;
// Forward vector relative to the camera along the x-z plane
var forward = cameraTransform.TransformDirection(Vector3.forward);
forward.y = 0;
forward = forward.normalized;
// Right vector relative to the camera
// Always orthogonal to the forward vector
var right = Vector3(forward.z, 0, -forward.x);
var v = Input.GetAxisRaw("Vertical");
var h = Input.GetAxisRaw("Horizontal");
// Are we moving backwards or looking backwards
if (v < -0.2)
movingBack = true;
else
movingBack = false;
var wasMoving = isMoving;
isMoving = Mathf.Abs (h) > 0.1 || Mathf.Abs (v) > 0.1;
// Target direction relative to the camera
var targetDirection = h * right + v * forward;
// Lock camera for short period when transitioning moving & standing still
lockCameraTimer += Time.deltaTime;
if (isMoving != wasMoving)
lockCameraTimer = 0.0;
// We store speed and direction seperately,
// so that when the character stands still we still have a valid forward direction
// moveDirection is always normalized, and we only update it if there is user input.
if (targetDirection != Vector3.zero) {
// If we are really slow, just snap to the target direction
if (moveSpeed < walkSpeed * 0.9) {
moveDirection = targetDirection.normalized;
}
// Otherwise smoothly turn towards it
else {
moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
moveDirection = moveDirection.normalized;
}
}
// Smooth the speed based on the current target direction
var curSmooth = speedSmoothing * Time.deltaTime;
// Choose target speed
//* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0);
_characterState = CharacterState.Idle;
// Pick speed modifier
targetSpeed *= walkSpeed;
_characterState = CharacterState.Walking;
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
}
function UpdateTargettedMovementDirection () {
var cameraTransform = Camera.main.transform;
var wasMoving = isMoving;
isMoving = true;//Mathf.Abs (h) > 0.1 || Mathf.Abs (v) > 0.1;
// Target direction relative to the camera
// var targetDirection = h * right + v * forward;
var targetDirection = Vector3.zero;
targetDirection.x = targetPoint.x - transform.position.x;
targetDirection.z = targetPoint.z - transform.position.z;
targetDirection = targetDirection.normalized;
//Debug.Log("Target direction is " + targetDirection);
// Lock camera for short period when transitioning moving & standing still
lockCameraTimer += Time.deltaTime;
if (isMoving != wasMoving)
lockCameraTimer = 0.0;
// We store speed and direction seperately,
// so that when the character stands still we still have a valid forward direction
// moveDirection is always normalized, and we only update it if there is user input.
if (targetDirection != Vector3.zero) {
// If we are really slow, just snap to the target direction
if (moveSpeed < walkSpeed * 0.9) {
moveDirection = targetDirection.normalized;
}
// Otherwise smoothly turn towards it
else {
moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
moveDirection = moveDirection.normalized;
}
}
// Smooth the speed based on the current target direction
var curSmooth = speedSmoothing * Time.deltaTime;
// Choose target speed
//* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0);
_characterState = CharacterState.Idle;
// Pick speed modifier
targetSpeed *= walkSpeed;
_characterState = CharacterState.Walking;
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
}
function Update() {
if (!isControllable) {
// kill all inputs if not controllable.
Input.ResetInputAxes();
}
var distance : float = 0;
if (Input.GetMouseButtonUp(0)) {
if (isTargetting) {
isTargetting = false;
// Debug.Log("Stopped moving");
FaceCamera();
} else {
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var layerMask = 1 << 8; // Terrain is layer 8
var hit : RaycastHit;
Physics.Raycast(Camera.main.transform.position, ray.direction, hit, 1000, layerMask);
distance = Vector3.Distance(transform.position, hit.point);
if (distance <= targetMaxDistance && hit.point != Vector3.zero) {
targetPoint = hit.point;
isTargetting = true;
// Debug.Log("Mouse up at hit " + hit.point + " at distance " + distance);
} else {
isTargetting = false;
// Debug.Log("Ignored mouse up at hit " + hit.point + " at distance " + distance);
}
}
}
if (isTargetting) {
// Debug.Log("Moving to " + targetPoint);
distance = Vector3.Distance(transform.position, targetPoint);
if (distance < targetPrecision) {
// Debug.Log("Reached point " + targetPoint + " at distance " + distance);
isTargetting = false;
FaceCamera();
} else {
UpdateTargettedMovementDirection();
}
} else {
UpdateSmoothedMovementDirection();
}
// Calculate actual motion
var movement = moveDirection * moveSpeed;
movement *= Time.deltaTime;
// Move the controller
var controller : CharacterController = GetComponent(CharacterController);
collisionFlags = controller.Move(movement);
// ANIMATION sector
if (_animation) {
if (controller.velocity.sqrMagnitude < 0.1) {
_animation.CrossFade(idleAnimation.name);
} else {
//_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0, walkMaxAnimationSpeed);
_animation.CrossFade(walkAnimation.name);
}
} else {
Debug.Log("Animation is null!");
}
// ANIMATION sector
// Set rotation to the move direction
transform.rotation = Quaternion.LookRotation(moveDirection);
}
function OnControllerColliderHit (hit : ControllerColliderHit ) {
// Debug.DrawRay(hit.point, hit.normal);
if (hit.moveDirection.y > 0.01)
return;
}
function GetSpeed () {
return moveSpeed;
}
function GetDirection () {
return moveDirection;
}
function IsMovingBackwards () {
return movingBack;
}
function GetLockCameraTimer () {
return lockCameraTimer;
}
function IsMoving () : boolean {
return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5;
}
function Reset () {
gameObject.tag = "Player";
}
function FaceCamera() {
var cameraTransform = Camera.main.transform;
// Forward vector relative to the camera along the x-z plane
var forward = cameraTransform.TransformDirection(Vector3.forward);
forward.y = 0;
forward = forward.normalized;
moveDirection = -forward;
}
Update 2: The settings used to create the animations are in these screenshots. Are they correct?
Not (yet :-) an answer but I need more space and images.
Since 3.5 Unity3d the way to handle imports based on animation#model.fbx notation has changed and some people reported trouble (s. for example BIG Unity 3.5.0f1 problem with Skinned Rig - Major FAIL). The problem arises when a 2nd root bone comes to play but I could solve this by dragging the animations to the model file prefab (most of my animations are contained in the model and the remaining 2 are no big pain).
Just to be really sure that I understood your answer in the comments section right, you have something like this:
That means:
Within the character model the animations array contains all animations
The number of bones and all names are exactly like in your prototype character
If you open an animation view, you can see a read-only list of all animations of the character selected in hierarchy view
Assuming this is fine some more suggestions:
I can't see any place in the code where you set WrapMode.Loop or animation speed. Are you sure it is configured as Loop in inspector and is not overwritten somewhere.
CrossFade with no parameters assumes 0.3 seconds
What happens after the cross fade call? Does the DefaulTake stops playing?
Can you drag the Walk animation as default
Do you use different animation layers?
What output do you get when you set up Debug.Log ("Walk: " + player.animation.IsPlaying ("Walk"));
[Update]
Let's look at the import settings. Do you have Split Animations
checked and are there maybe wrong values entered at Start and
End like 1-1?
Regarding Toggling of IsPlaying: Please create more output about the AnimationState properties. Most notable speed, length, enabled, weight, ... I have a suspicion that the animation is too short or played too fast.
A walk animation that is no bone animation sounds a bit strange. On the other hand console would cry out loud if bone names are not matching. So I assume there is no problem now.
That is strange, can you post the source&demo?
You can try to check:
Check if the animation names and properties are set correctly in the editor.
Create an object without the controller and make sure the animations were imported correctly.