Unity slider controlling animation - c#

I'd like to create a slider that displays the current frame of an animation within unity, and when you move the slider it updates the current frame within the animation.
So far I have (edited code)
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider sliderScrubber;
public Animator animator;
void Start()
{
float t = animator
.GetCurrentAnimatorStateInfo(0)
.normalizedTime;
//animator.speed = 0.0001f;
//slider.onValueChanged.AddListener(OnValueChanged);
}
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
Debug.Log("animationTime (normalized) is " + animationTime);
sliderScrubber.value = animationTime;
}
public void OnValueChanged(float changedValue)
{
animator.speed = 0.0001f;
animator.Play("Take 001", -1, sliderScrubber.normalizedValue);
}
}
At the moment this changes the current frame in the animation when you adjust the slider, but when the animation is played, the slider doesn't update.
How can I get this to work in both directions?
Many thanks

First be sure to delete all code lines
animation.speed = 0.0001f
which appear to be leftover from a tutorial or debugging.
What about
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
// in case of a looping anime, animationTime = animationTime %1f;
Debug.Log("animationTime is " + animationTime);
// of course remove that in production
slider.value = animationTime;
}
Note of course set the scrubber min/max to 0f, 1f in the editor.
Note, you may prefer to make a "scrubber" for a joint position; also works nice.
Note, subtle issue: experienced programmers may prefer to set the animation speed to zero, while the user's finger is holding down the slider; probably OK without doing this.
NOTE take care to delete all references to animation.speed. It apears to be a carry-over from an incorrect tutorial.

Ok, so after much throwing things around to see what sticks, this is giving me the result I'm after.
I'd like to thank #JoeBlow for all his help.
Somebody buy that guy a drink.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider sliderScrubber;
public Animator animator;
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
Debug.Log("animationTime (normalized) is " + animationTime);
sliderScrubber.value = animationTime;
}
public void OnValueChanged(float changedValue)
{
animator.Play("Take 001", -1, sliderScrubber.normalizedValue);
}
}
I will update this again when I have a play pause button working and a slider that can change the speed of playback.

I think you almost got it just instead of assigning the value directly in the update function:
sliderScrubber.value = animationTime;
Use this:
sliderScrubber.Set(animationTime, false);
the false parameter is to call the OnValueChange() function in your case you don't want that.
Addionally you can check if the animation is being played to avoid unnecesary calls to the Set function:
http://answers.unity3d.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html
and check your slider.maxValue to adjust it to the animation length.

Related

Scaling a gameObject through C# script?

I am trying to write this script that allows me to toggle a transform change on/off on a sphere. I think the logic is right but I really can't figure out what I'm missing here. (I am also getting (line 7)error CS0108: 'Scale.transform' hides inherited member 'Component.transform'. Use the new keyword if hiding was intended.)
By the way I'm a total beginner, this might be very simple but this is my first game ever!
Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scale : MonoBehaviour
{
public Transform transform;
Vector3 scale;
public float objectSize = 3.0f;
void ScaleOnOff()
{
transform = GetComponent<Transform>();
if (Input.GetKeyDown(KeyCode.S))
{
if (scale.x != objectSize)
{
scale = transform.localScale;
scale.x = objectSize;
transform.localScale = scale;
Debug.Log("condition1");
}
if (scale.x == objectSize)
{
scale = transform.localScale;
scale.x = 1;
transform.localScale = scale;
Debug.Log("condition2");
}
}
}
// Update is called once per frame
void Update()
{
ScaleOnOff();
}
}
Also, in the console both of the Debug.Log messages appear, so I'm wondering how can both conditions be true at the same time?
Thanks for your help!
There are a few errors, but that's normal if you are a beginner :).
There is already a variable called transform. Unity has some
shortcuts for getting components: the most used is transform. If you
try to delete the "Transform transform" line, in which you rightly
declare a variable, you will see that the code will give you no
errors. The variable provided by Unity is the same as GetComponent(), so you can delete this line as well, because
transform is by default the same as that.
In general, if the variables do not change during the game, as in
this case with transform, the values ​​must be taken at the beginning
of the game, or at least a few times, not continuously. In your code
this is not true because the transform variable as mentioned in the
previous point already has a value, but for any other variable you
assign a value that does not change, it is best to do it in Awake()
or Start().
Both messages are shown because the code does the following: if you
pressed the button, it checks if the value of scale.x is different
from "objectSize". (in the first frame scale.x is equal to 0 because
you haven't assigned it yet), If it's true (in the first frame it's
true), among other things it does, it makes scale.x equal to
objectSize. Continuing the code check if scale.x is equal to
"objectSsize" (which in the first frame as explained is true), then
also execute the second debug.log(). To solve this problem you can
try assigning the value of transform.localScale to "scale" in Start(), and instead use two separate if's, an if and an else. On else you
can find a lot of documentation online, so I don't want to explain it
here.
All together something like e.g.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scale : MonoBehaviour
{
Vector3 scale;
public float objectSize = 3.0f;
void Start()
{
scale = transform.localScale;
}
void ScaleOnOff()
{
if (Input.GetKeyDown(KeyCode.S))
{
if (scale.x != objectSize)
{
scale = transform.localScale;
scale.x = objectSize;
transform.localScale = scale;
Debug.Log("condition1");
}
else
{
scale = transform.localScale;
scale.x = 1;
transform.localScale = scale;
Debug.Log("condition2");
}
}
}
// Update is called once per frame
void Update()
{
ScaleOnOff();
}
}
Excuse me if I was long-winded, but being that you are a beginner, I wanted to explain everything to you well.
Good development!

On Mouse Click for "OnTriggerStay" to Add Score, Adds More Than Set Amount

Short and simple, object drops into trigger object, I click, score goes up. Score is set to +1, adds +2 to +5, no reasonable explanation (script and video provided)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Score : MonoBehaviour
{
public int currentScore;
public Text displayScore;
public int addScore;
void OnTriggerStay ()
{
if (Input.GetMouseButtonDown(0))
{
currentScore += addScore;
}
}
void Update ()
{
displayScore.text = currentScore.ToString();
}
}
Video, goes to Youtube
first of all, what exactly is calling the OnTriggerStay() function, because that is not the Unity function it should be like this:
void OnTriggerStay(Collider col)
{
if (Input.GetMouseButtonDown(0))
{
currentScore += addScore;
}
}
also note that if you use OnTrigger the ball must have a Rigidbody, other than that GetMouseButtonDown(0) should work as expected and worked fine for my tests.
My only explanation here is that the other script or any script changes currentScore, you can test that by setting the var private.
Edit:
i think i know your problem, i presume that multiple collider trigger the function so it is called multiple times per frame, since it's the same frame Input.GetMouseButtonDown(0) is true for each collider.
You should therefore add a check to the if case
if(Input.GetMouseButtonDown(0) && col.name.Equals("Yoyo")){
...
}
i presume that the ball is called yoyo ;)

Activating animation when within radius

I am trying to create a script for my enemy turret, but it is not going well. I have a couple animations of the turret being activated and deactivated. What I need is that based on the distance from the player, it plays either animation. So once it moves inside the detection radius it plays the activation animation and once it is outside it plays the deactivation animation. Most of the other ways I try require me to create an Animation Controller, which I have little experience in using. I want a simple way to play one animation once it is inside and play a different one when it is outside. I think there was a way to store the animation clip in the script, and then play it. I have attached my current script, so you know what I mean.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTurret : MonoBehaviour
{
public GameObject Player;
public float DistanceToPlayer;
public float DetectionRadius = 75;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("PlayerTank");
}
// Update is called once per frame
void Update()
{
DistanceToPlayer = Vector3.Distance(transform.position, Player.transform.position);
if (DistanceToPlayer<=DetectionRadius)
{
Debug.Log("Within Radius");
}
if (DistanceToPlayer >= DetectionRadius)
{
Debug.Log("Outside Radius");
}
}
}
Calculating and checking the distance of the player for every update() is not ideal. It will work, but it will do more work than it needs to when the player isn't even near it. Its not efficient.
What you may want to do if your player is a Rigidbody, is add a SphereCollider to the turret, set isTrigger=true, set the radius to be your detection radius, and handle the OnTriggerEnter() and OnTriggerExit() events to play or stop animations.
You can also add two public Animiation objects to the script, drag and drop your animations in the editor, then you can use animation.Play() and .Stop() etc. to control the animations.
Something similar to this. Not tested fully, but you can get the idea.
public float detectionRadius = 75;
public Animation activateAnimation;
public Animation deactivateAnimation;
void Start()
{
SphereCollider detectionSphere = gameObject.AddComponent<SphereCollider>();
detectionSphere.isTrigger = true;
detectionSphere.radius = detectionRadius;
detectionSphere.center = Vector3.zero;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
activateAnimation.Play();
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
deactivateAnimation.Play();
}
}
Your animations must not loop otherwise you will have to add more logic to check if animation.isPlaying and do your own animation.Stop() etc.

Input getKeyDown not firing

I'm making a 2d game in Unity. I have a player sprite with this script attached. The right and left keys move perfect but when I try to get the space bar, it doesn't work
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
public class Player : MonoBehaviour {
public float moveSpeed;
public float jumpHeight;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Rigidbody2D playerBody = GetComponent<Rigidbody2D>();
if (Input.GetKeyDown("space")){
print("fired");
//playerBody.velocity = new Vector2(playerBody.velocity.x, jumpHeight);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
playerBody.velocity = new Vector2(-moveSpeed, playerBody.velocity.y);
}
if (Input.GetKey(KeyCode.RightArrow))
{
playerBody.velocity = new Vector2(moveSpeed, playerBody.velocity.y);
}
}
}
I have also tried Keycode.Space instead of "space" which didn't work either
Also for some reason, I cannot remove or replace the unity3d tag but it's for a 2d unity game.
Your code appears to be all good, so it may be an error with your console window. Make sure in the top right corner of the console you have the comment box highlighted so anytime you print something in the code, it will appear. Also, I would suggest you use forces to make your character jump instead of velocity. In this case, you said the player's vertical velocity to jumpHeight, but never change it back so your character will constantly be moving upward. You should change that line to:
playerBody.AddForce (transform.up * jumpHeight);
and you may need to adjust the jumpHeight accordingly.

Unity - Resizing one GameObject to match another

I'm working on a project in Unity that involves regions that teleport any non-static object from one to the paired. That part's fine, but for convenience, I'm trying to write a part of the script that will resize one object if its pair is resized, such that they will always be of equal size. And so far it works - mostly. The only problem I encounter is when trying to resize through the Transform component - as in, typing in numbers in Inspector, or using the value sliders on X or Y or Z. The handles work fine. It's not a big deal, I suppose, but if I could figure out why this isn't working, so I can learn what to do in the future, I'd be very glad. Here's the code:
[ExecuteInEditMode]
public class TransferRegion : MonoBehaviour {
// Unrelated code...
public bool scaleManuallyAltered {
get; private set;
}
[SerializeField]
private TransferRegion pair;
private Vector3 scale;
// Called whenever the scene is edited
void Update () {
if (scale != gameObject.transform.localScale) {
scaleManuallyAltered = true;
scale = gameObject.transform.localScale;
}
if (pair && scaleManuallyAltered && !pair.scaleManuallyAltered) {
pair.transform.localScale = scale;
}
}
// Called AFTER every Update call
void LateUpdate () {
scaleManuallyAltered = false;
}
// Unrelated code...
}
If anyone can see some major logical failure I'm making, I'd like to know. If my code's a bit hard to understand I can explain my logic flow a bit, too, I know I'm prone to making some confusing constructs.
Thanks folks.
If you want one object to be the same scale as another, why not just simplify your code by setting the scale of the re sizing game object, directly to the scale of the game object it is based off of? For example, this script re sizes an object to match the scale of its pair while in edit mode:
using UnityEngine;
using UnityEditor;
using System.Collections;
[ExecuteInEditMode]
public class tester : MonoBehaviour
{
public Transform PairedTransform;
void Update()
{
if (!Selection.Contains(gameObject))
{
gameObject.transform.localScale = PairedTransform.localScale;
}
}
}
I tested this on two cubes in my scene. I was able to resizing using gizmos as well as manually typing in numbers to the transform edit fields in the inspector.
Edit: By taking advantage of Selection you can apply the scale change only to the object in the pair that is not selected in the hierarchy. This way the pairs wont be competing with each other to re scale themselves.
So I figured it out.
I'm not sure what was wrong with my original code, but eventually I decided to slip into the realm of good old handy events:
[ExecuteInEditMode]
public class TransferRegion : MonoBehaviour {
//...
[SerializeField]
private UnityEvent rescaled;
//...
void Update() {
if (scale != gameObject.transform.localScale) {
scale = gameObject.transform.localScale;
rescaled.Invoke();
}
}
//...
public void OnPairRescaled() {
gameObject.transform.localScale = pair.transform.localScale;
scale = gameObject.transform.localScale;
}
}
And just set the OnPairRescaled event to be the listener for the paired object's rescaled event.
Ta-da!

Categories