How to make transitions between images in canvas (unity)? - c#

So I have 3 same images, but with different colors which I want to cycle in my menu. They are only with different hue/saturations and I want them to slowly reduce their alpha 1 by 1 so that the one behind pops up and then restart the cycle.
I'm trying to set a public image and reduce it's alpha, but it's not smooth.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Fading : MonoBehaviour {
public Image image;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
//image.CrossFadeAlpha(50, 5, false);
image.GetComponent<CanvasRenderer>().SetAlpha(0.1f);
image.CrossFadeAlpha(10f, 4f, false);
}
}

CrossFadeAlpha should only be called to start the image fade transition, via a button press or similar, as the method works similar to a Coroutine, example (on mouse button press);
public class ImageFade : MonoBehaviour
{
[SerializeField]
private Image m_img;
[SerializeField]
private float m_fadeDuration;
[SerializeField]
private bool m_ignoreTimeScale;
public void Update()
{
if (Input.GetMouseButtonDown(0))
m_img.CrossFadeAlpha(0f, m_fadeDuration, m_ignoreTimeScale);
if (Input.GetMouseButtonDown(1))
m_img.CrossFadeAlpha(1f, m_fadeDuration, m_ignoreTimeScale);
}
}
However if you would prefer to control this functionality manually, you have to take a longer approach. Below you can see that every time the mouse is pressed, the fade multiplier is negated, making it always 1 or -1. This value is then multiplied by the fraction of time needed for this update (before being added to the current alpha value);
Time.deltaTime / m_fadeDuration
As well as this, the boolean m_requiresUpdate, makes sure to avoid unnecessary updates, setting itself to false after the fade is complete.
public class ImageFade : MonoBehaviour
{
[SerializeField]
private Image m_img;
[SerializeField]
private float m_fadeDuration;
[SerializeField]
private bool m_ignoreTimeScale;
private int m_fadeMultiplier;
private float m_alpha;
private bool m_requiresUpdate;
public void Start()
{
m_fadeMultiplier = 1;
m_alpha = 1f;
}
public void Update()
{
//Toggle subtracting/adding
if (Input.GetMouseButtonDown(0))
{
m_fadeMultiplier = -m_fadeMultiplier;
m_requiresUpdate = true;
}
//Update
if (m_requiresUpdate)
{
//Fade
m_alpha = Mathf.Clamp(m_alpha + (m_fadeMultiplier * (Time.deltaTime / m_fadeDuration)), 0f, 1f);
m_img.canvasRenderer.SetAlpha(m_alpha);
//Finished fading
if (m_alpha == 0f || m_alpha == 1f)
m_requiresUpdate = false;
}
}
}
Hope this ties everything together nicely for you.

Related

Unity/C# How to run Script continuously on User Interface Select Event Trigger

Complete beginner here, I'm looking to smoothly rotate my sphere continuously while the button it is a child of is selected in the UI.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonHandler : MonoBehaviour
{
private Vector3 rotationDirection = new Vector3(0, .25f, 0);
private float smoothTime = 1;
private float convertedTime = 200;
private float smooth;
public void SetText()
{
Transform target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
}
}
The issue is that when I go to select my button, the function is only ran a single time, whereas I want it to run continuously until deselected.
** Note this is using the Select Event Trigger
Any help would be greatly appreciated!
Please let me know if you need additional information to answer
Thanks!
The way I see it, you could use the Update() function (native to MonoBehaviour) to run the few lines of code that you have and rotate your sphere.
Then set up functions for when the button is clicked and when it is released to change a boolean variable that is evaluated in the Update() function...
Breif example below...
private bool rotate = false;
void Update()
{
if (rotate)
{
Transform target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
}
}
public void StartRotation() //on click
{
rotate = true;
}
public void StopRotation() //on release
{
rotate = false;
}
Further improvements might include Finding the "Globe" object in the constructor and storing a reference to it for future use during rotation.
Another method involves setting the rotation speed of the sphere rather than directly transforming (rotating) it constantly (make the Physics Enginge do the work) but try this first to get it working.
Extending Nicholas Sims' answer a slightly better option would be a Coroutine. Coroutines can be seen as temporary Update method as by default each iteration step is Don right after the Update call.
This way you don't waste resources to an Update method when the trigger is not activated anyway.
Also note that Find in general is a very expensive call and should be avoided wherever possible! The least you want to do is using it in Update every frame! Instead store the reference as early as possible .. maybe even already via a field in the Inspector!
// For storing the reference
// If possible already reference this via the Inspector
// by drag&drop to avoid Find entirely!
[SerializeField] private Transform target;
private bool rotate;
private void Start()
{
if(!target) target = GameObject.Find("Globe");
}
public void ToggleRoutine()
{
rotate = !rotate;
if(!rotate)
{
StopAllCoroutines();
}
else
{
StartCoroutine (RotateContinuously());
}
}
private IEnumerator RotateContinuously()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
// You should use Find only once .. if at all. You should avoid it wherever possible!
if(!target) target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
If you rather want a "While Pressed" behaviour, so you want the rotation going on while the user holds the mouse/button pressed and stop as soon as he released it, you can use the IPointerXYHandler interfaces like
public class WhilePressedRotation : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] private Transform target;
private void Start ()
{
if(!target) target = GameObject.Find("Globe");
}
public void OnPointerDown(PointerEventData pointerEventData)
{
StartCoroutine(RotateContinuously());
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// Actually not really needed
// but not sure if maybe required for having OnPointerExit work
}
public void OnPointerExit(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
private IEnumerator RotateContinuously()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
// You should use Find only once .. if at all. You should avoid it wherever possible!
if(!target) target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
}
Or a more general code
public class WhilePressedButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] private UnityEvent whilePressed;
public void OnPointerDown(PointerEventData pointerEventData)
{
StartCoroutine(ContinuousButton());
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// Actually not really needed
// but not sure if maybe required for having OnPointerExit work
}
public void OnPointerExit(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
private IEnumerator ContinuousButton()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
whilePressed.Invoke();
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
}
And reference a method in whilePressed like usually in a button onClick via Inspector or code.

How can I make my jump and land animations play reliably?

I can't make my jump and land animations play reliably Currently the jump animation doesn't play and the land animation seems to play randomly.
I've been working with unity 3d for several months but I am new to using animations. I may just be missing some basic information.
At one point the jump animation would play, but only after the player was in the air. How do I make animations play at the right time reliably?
This is my player movement script where I'm currently calling the jump and land animations from.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMvmt : MonoBehaviour
{
public Rigidbody player;
public float sideForce = 5;
public float jumpForce = 5;
public float fallingForce = 10;
public SphereCollider col;
public LayerMask groundLayers;
public Animator jump;
public Animator land;
private void Start()
{
jump = GetComponent<Animator>();
land = GetComponent<Animator>();
}
private void Update()
{
if ( Input.GetKey("a"))
{
player.AddForce(-sideForce, 0, 0);
}
if ( Input.GetKey("d"))
{
player.AddForce(sideForce, 0, 0);
}
if (IsGrounded() && Input.GetKey("w"))
{
jump.SetBool("playJump",true);
Invoke("StopJumpAnimation", .4f);
player.AddForce(0, jumpForce, 0);
}
OncollisionEnter();
if (IsGrounded() == false)
{
player.AddForce(0, -fallingForce, 0);
}
}
private bool IsGrounded()
{
return Physics.CheckCapsule(col.bounds.center,
new Vector3(col.bounds.center.x,
col.bounds.min.y, col.bounds.center.z), col.radius * .9f, groundLayers);
}
void StopJumpAnimation()
{
jump.SetBool("playJump", false);
}
void StopLandAnimation()
{
land.SetBool("playLand", false);
}
void OncollisionEnter()
{
land.SetBool("playLand", true);
Invoke("StopLandAnimation", .3f);
}
}
I can show you pictures of the animator if that helps. I might just need a basic explanation of how to use the animator since I'm new to animation.

Unity run code on touch with a UI Image - c#

I'm a new user to Unity3D and need a hand.
I have some code which I want to be activated when the UI image is pressed and to end once the press has been released. The game is going to run on both Android and IOS so needs to be supported by both platforms.
Here is my code:
public class RPB : MonoBehaviour {
public Transform LoadingBar;
public Transform TextIndicator;
[SerializeField] private float currentAmount;
[SerializeField] private float speed;
[SerializeField] private float numSec;
// Update is called once per frame
void Update () {
if (currentAmount < 100) {
currentAmount += speed * Time.deltaTime;
} else if (currentAmount > 100){
currentAmount = 0;
numSec += 1;
}
LoadingBar.GetComponent<Image>().fillAmount = currentAmount /100;
TextIndicator.GetComponent<Text> ().text = ((int)numSec).ToString () + "s";
}
}
How would I be able to do this?
Thank you
In regards to your issue with the touch screen, I would recommend using Unity's UI button system. It works very well with both Android and iOS platforms. I recommend watching these videos to understand how to write a function that the interface will call for you: https://unity3d.com/learn/tutorials/topics/user-interface-ui/ui-button
On another note, running GetComponent<>() every frame is incredibly taxing on a mobile system.
I recommend the following change:
using UnityEngine;
using UnityEngine.UI;
public class RPB : MonoBehaviour {
public Image LoadingBar;
public Text TextIndicator;
// other variables redacted //
void Start () {
LoadingBar = GetComponent<Image>();
TextIndicator = GetComponent<Text>();
}
void Update () {
// other functions redacted //
LoadingBar.fillAmount = currentAmount / 100;
TextIndicator.text = ((int)numSec).ToString () + "s";
}
public void TouchFunction() {
// Do the thing here. //
}
}
I have some code which I want to be activated when the UI image is
pressed and to end once the press has been released
I would have have advised you to use the Button component too but it doesn't have click down and up events. It only has onClick event which means you can only detect when a there is a Button click not when pressed and released.
This can be done with the Image component. Implement IPointerDownHandler and IPointerUpHandler then override the OnPointerDown and OnPointerUp functions. These functions will be called when there is a mouse click and release on your Image. Attach the script to the Image and that should do the job assuming that the current code you have is logically correct.
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class YourScript : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public Transform LoadingBar;
public Transform TextIndicator;
[SerializeField]
private float currentAmount;
[SerializeField]
private float speed;
[SerializeField]
private float numSec;
bool isPressed = false;
public void OnPointerDown(PointerEventData eventData)
{
// someCode();
Debug.Log("Mouse Down");
isPressed = true;
}
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Mouse Up");
isPressed = false;
}
void Update()
{
if (isPressed)
{
someCode();
}
}
void someCode()
{
if (currentAmount < 100)
{
currentAmount += speed * Time.deltaTime;
}
else if (currentAmount > 100)
{
currentAmount = 0;
numSec += 1;
}
LoadingBar.GetComponent<Image>().fillAmount = currentAmount / 100;
TextIndicator.GetComponent<Text>().text = ((int)numSec).ToString() + "s";
}
}

Changing the opacity of a transparent texture

I have a transparent texture of a chain link fence. I want the fence to fade in as the player approaches from the z direction. The problem I am having is that because the fence is transparent the opacity slider disappears and uses the image transparency. (I want the transparent texture to fade in) My current code:
public class WallFader : MonoBehaviour {
public GameObject wallone;
private Vector3 wallonetransform;
private Color wallonecolor;
public GameObject player;
private Vector3 playerposition;
private float PPX;
private float PPZ;
// Use this for initialization
void Start()
{
wallonetransform = wallone.GetComponent<Transform>().position;
wallonecolor = wallone.GetComponent<Renderer>().material.color;
}
// Update is called once per frame
void Update () {
playerposition = player.transform.position;
PPX = playerposition.x;
PPZ = playerposition.z;
// Distance to the large flat wall
float wallonedist = wallonetransform.z - PPZ;
if (wallonedist > 10)
{
wallonecolor.a = 0;
}
else
{
//fade in script
}
}
The fence never fades or disappears when wallonedist is > 10
Color is a struct which means that changing it won't change the instance of of the Renderer. It is a copy of a color from the Renderer. If you change the color, you have to re-assign the whole color back to the Renderer for it to take effect.
public class WallFader : MonoBehaviour
{
public GameObject wallone;
private Vector3 wallonetransform;
private Color wallonecolor;
Renderer renderer;
public GameObject player;
private Vector3 playerposition;
private float PPX;
private float PPZ;
// Use this for initialization
void Start()
{
wallonetransform = wallone.GetComponent<Transform>().position;
renderer = wallone.GetComponent<Renderer>();
wallonecolor = wallone.GetComponent<Renderer>().material.color;
}
// Update is called once per frame
void Update()
{
playerposition = player.transform.position;
PPX = playerposition.x;
PPZ = playerposition.z;
// Distance to the large flat wall
float wallonedist = wallonetransform.z - PPZ;
if (wallonedist > 10)
{
wallonecolor.a = 0;
renderer.material.color = wallonecolor; //Apply the color
}
else
{
//fade in script
}
}
}

Control animation and execute script when animation ends

My project is a military fps and i'm having some problems with animations.
I have 3 different weapons, 1 animator controller for each one and every weapon has a "enter" and "leave" animation. Like CS, COD, etc...
I need to know when my "leave" animation ends to disable the gameobject, enable the other one and play the "enter" animation.
I tryed to do this: http://answers.unity3d.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html but without sucess.
I'll leave here a print of the animator controller, the hierarchy and the script, if u need more details, just need to say.
Animator controller of the weapon number 1
All transitions to "Sair" (leave animation) have a trigger (AK47_sair) and the transition to "Extit" state have a trigger ("AK47_SairControlador")
On my code, when i press 2 (change to weapon number 2) i want to do the transition.
This is the hierarchy, my script is attached to "Jogador".
With my actual code, it disable tha AK47 gameobject when the leave animation still playing.
using UnityEngine;
using System.Collections;
public class FirstPerson : MonoBehaviour {
public float speed;
public float normalSpeed = 5.0f;
public float slowSpeed = 2.5f;
public float crchSpeed = 2.5f;
private Transform tr;
private float dist; // distance to ground
public float mouseSensitivity = 5.0f;
public float verticalRotation = 0.0f;
public float updownRange = 60.0f;
private float verticalSpeed = 0.0f;
public float jumpSpeed = 5.0f;
CharacterController player;
private GameObject AK47;
private GameObject Faca;
public float shootingRate = 0.15f;
public float shootCooldown;
private bool agachado = false;
public float camOriginalPositionY;
public float camCrouchPositionY;
private Animator controladorAnimacaoAK;
private Animator controladorAnimacaoFaca;
public CapsuleCollider playerCollider;
public Camera CameraPrincipal;
public int ArmaSelecionada;
public int UltimaArma;
void Start () {
player = GetComponent<CharacterController>();
shootCooldown = 0;
controladorAnimacaoAK = player.GetComponentInChildren<Animator>();
playerCollider = gameObject.GetComponent<CapsuleCollider> ();
CameraPrincipal = Camera.main;
ArmaSelecionada = 1;
AK47 = CameraPrincipal.transform.FindChild ("ak47_final_animado").gameObject;
}
void Update () {
if (Input.GetKeyDown (KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2)) {
UltimaArma = ArmaSelecionada;
ArmaSelecionada = 2;
if(UltimaArma == 1) {
controladorAnimacaoAK.SetTrigger("AK47_Sair");
controladorAnimacaoAK.SetTrigger("AK47_SairControlador");
AK47.SetActive (false);
}
}
if (Input.GetKeyDown (KeyCode.Alpha1)) {
UltimaArma = ArmaSelecionada;
ArmaSelecionada = 1;
// controladorAnimacaoAK.SetTrigger ("AK47_Entrar");
}
if (ArmaSelecionada == 1) {
// diz ao controlador da anim se o player esta a movimentar-se ou nao
controladorAnimacaoAK.SetFloat ("AK47_Deslocacao", player.velocity.magnitude);
//Debug.Log (player.velocity.magnitude);
// dispatar tiros
PlayerShoot PlayerShootScript = player.GetComponent<PlayerShoot> ();
if (shootCooldown > 0) {
shootCooldown -= Time.deltaTime;
}
if (Input.GetButton ("Fire1")) {
if (shootCooldown <= 0) {
shootCooldown = shootingRate;
PlayerShootScript.FireShoot ();
// animaƧao
controladorAnimacaoAK.SetBool ("AK47_Disparar", true);
}
} else {
// animaƧao
controladorAnimacaoAK.SetBool ("AK47_Disparar", false);
}
if (Input.GetKeyDown (KeyCode.R)) {
controladorAnimacaoAK.SetTrigger ("AK47_rec");
}
}
}
}
In Unity 5 you've got this new thing which is Animation State Machine Behavior : State Machine Behaviours
You can use it to specify behavior when the Animation Controller enters or leave specific states.
For exemple here I've got my door which have an Open and Close state, and let's say that I want to play a sound when the door is opening.
Here I clicked Opening, then Add Behaviour and set a random name for the test (Behavior Test in my case)
Then I just need to implement the function void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) to play a sound at the first frame the animation is running.
[SerializeField]
AudioClip open_sound;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<AudioSource>().clip = open_sound;
animator.GetComponent<AudioSource>().Play();
}
In your case, you would want to implement a behavior in the state Disparar which implements the function OnStateExit([...]) and handle the weapon change.
To go a bit further I don't think you should handle the weapon change directly in the animation state, but maybe your script could send an event catched by a Game Controller that will actually handle the change of weapon.

Categories