I have an animation in 4 PNG images. I want to have the frames played through over the course of 1/2 second in the order 1-2-3-4-2-1 with transparency transitions.
What I wrote was supposed to have the first frame appear immediately when the parent object holding the different sprites is generated, then have it turn transparent over 1/12 of a second while the second frame turns opaque, and so forth until the last frame ends its transparent-opaque-transparent cycle.
It's probably not the most efficient way, but I made a prefab of en empty object under which are placed the 6 sprite-frames, with each sprite given an individual script.
I'm posting the first three scripts as an example:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Frame1 : MonoBehaviour
{
private SpriteRenderer thisSprite;
private Color alpha;
private float timer;
// Start is called before the first frame update
void Start()
{
alpha.a = 255;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
}
// Update is called once per frame
void Update()
{
timer = timer + Time.deltaTime;
alpha.a -= timer * 3060;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
if (timer >= 1/12)
{
Destroy(gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Frame2 : MonoBehaviour
{
private SpriteRenderer thisSprite;
private Color alpha;
private float timer;
private int direction;
// Start is called before the first frame update
void Start()
{
alpha.a = 0;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
}
// Update is called once per frame
void Update()
{
if (direction == 0)
{
timer = timer + Time.deltaTime;
alpha.a += timer * 3060;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
if (timer >= 1/12)
{
direction = 1;
}
}
if (direction == 1)
{
timer = timer - Time.deltaTime;
alpha.a += timer * 3060;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
if (timer >= 1/6)
{
Destroy(gameObject);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Frame3 : MonoBehaviour
{
private SpriteRenderer thisSprite;
private Color alpha;
private float timer;
private int direction;
// Start is called before the first frame update
void Start()
{
alpha.a = 0;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
timer -= 1 / 12;
}
// Update is called once per frame
void Update()
{
if (direction == 0)
{
timer = timer + Time.deltaTime;
alpha.a += timer * 3060;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
if (timer >= 1 / 12)
{
direction = 1;
}
}
if (direction == 1)
{
timer = timer - Time.deltaTime;
alpha.a += timer * 3060;
thisSprite.GetComponent<SpriteRenderer>().color = alpha;
if (timer >= 1 / 6)
{
Destroy(gameObject);
}
}
}
}
They all seem to be visible the moment they are generated, and they don't fade away or even get destroyed at all. What is the issue?
Thanks.
As the comments suggest, using animations is a viable alternative. However, your code will not work simply because alpha accepts a value from 0 to 1 instead of 0 to 255.
So simply adjust your logic to fade from 1 down to 0 and you should see your fade transitions.
I have 2 images (2 players), these will be refilled each time a player fires (working as a reload animation). Here is the code for the Reload class
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
public class Timer : MonoBehaviour {
public Image fillImg1;
public Image fillImg2;
float timeAmt = 1;
float time;
void Start() {
// GameObject RC = GameObject.Find("ReloadCanvas");
fillImg2 = GetComponent<Image>();
time = timeAmt;
}
public void Update() {
if (Input.GetKeyDown("space"))
{
if (fillImg2 != null)
{
Debug.Log("Got to P2 Reload");
while (time > 0)
{
time -= Time.deltaTime;
fillImg2.fillAmount = time / timeAmt;
}
Debug.Log("Time reset - " + Time.deltaTime);
time = timeAmt;
}
else { Debug.Log("Fill Image 2 is null"); }
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
if (fillImg2 != null)
{
Debug.Log("Got to P1 Reload");
while (time > 0)
{
time -= Time.deltaTime;
fillImg1.fillAmount = time / timeAmt;
}
Debug.Log("Time reset - " + Time.deltaTime);
time = timeAmt;
}
else { Debug.Log("Fill Image 1 is null"); }
}
}
}
My problem is that even though i have the images filled in the canvas, the 'else if the images are null' check are being triggered.
Here is an image of the ReloadCanvas
The issue is that on Start() you are pointing your fillImg2 image to a null. When you use GetComponent<>() you are checking the component of a GameObject which your fillImg2 is already an image.
Remove fillImg2 = GetComponent<Image>(); from your Start method and it should no longer be null.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveObjects : MonoBehaviour
{
public float speed = 3f;
private GameObject[] objectstoMove;
private List<GameObject> objectsMoving = new List<GameObject>();
private float distanceTravelled = 0;
private Vector3 lastPosition;
// Use this for initialization
public void Init()
{
objectstoMove = GameObject.FindGameObjectsWithTag("Test");
objectsMoving = new List<GameObject>(objectstoMove);
lastPosition = objectstoMove[objectstoMove.Length].transform.position;
}
// Update is called once per frame
void Update()
{
if (objectstoMove != null)
{
float step = speed * Time.deltaTime;
for (int i = 0; i < objectstoMove.Length; i++)
{
if(distanceTravelled >= 50.0f)
{
objectsMoving.Remove(objectsMoving[objectsMoving.Count]);
}
objectsMoving[i].transform.Translate((objectsMoving[i].transform.up + objectsMoving[i].transform.forward) * step);
distanceTravelled += Vector3.Distance(objectsMoving[objectsMoving.Count].transform.position, lastPosition);
lastPosition = objectsMoving[objectsMoving.Count].transform.position;
}
}
}
}
In this part I want to take the last object in the list and move it to the start of the list:
if(distanceTravelled >= 50.0f)
{
objectsMoving.Remove(objectsMoving[objectsMoving.Count]);
}
The idea in general is to move the last item object from the list to the start of the list and keep moving the objects all the time but each time the last object in the list is distanceTravelled >= 50.0f move it to the start of the list. Same idea as cyclic if I'm not wrong.
Do something like this:
if(distanceTravelled >= 50.0f)
{
var moveToFirst = objectsMoving.Last();
objectsMoving.RemoveAt(objectsMoving.Count-1);
objectsMoving.Insert(0, moveToFirst);
}
This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 4 years ago.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GateControl : MonoBehaviour
{
public Transform door;
public float doorSpeed = 1.0f;
public bool randomDoorSpeed = false;
[Range(0.3f, 10)]
public float randomSpeedRange;
private Vector3 originalDoorPosition;
// Use this for initialization
void Start()
{
originalDoorPosition = door.position;
}
// Update is called once per frame
void Update()
{
if (randomDoorSpeed == true && randomSpeedRange > 0.3f)
{
StartCoroutine(DoorSpeedWaitForSeconds());
}
door.position = Vector3.Lerp(originalDoorPosition,
new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
Mathf.PingPong(Time.time * doorSpeed, 1.0f));
}
IEnumerator DoorSpeedWaitForSeconds()
{
doorSpeed = Random.Range(0.3f, randomSpeedRange);
yield return new WaitForSeconds(3);
}
}
Making StartCoroutine inside the Update is a bad idea. But I want that it will take one random speed when running the game then will wait 3 seconds and change to a new random speed then wait another 3 seconds and change for another new random speed and so on.
And while it's waiting 3 seconds to keep the current speed constant until the next change.
Update:
This is what I tried:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GateControl : MonoBehaviour
{
public Transform door;
public float doorSpeed = 1.0f;
public bool randomDoorSpeed = false;
public bool IsGameRunning = false;
[Range(0.3f, 10)]
public float randomSpeedRange;
private Vector3 originalDoorPosition;
// Use this for initialization
void Start()
{
IsGameRunning = true;
originalDoorPosition = door.position;
StartCoroutine(DoorSpeedWaitForSeconds());
}
// Update is called once per frame
void Update()
{
door.position = Vector3.Lerp(originalDoorPosition,
new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
Mathf.PingPong(Time.time * doorSpeed, 1.0f));
}
IEnumerator DoorSpeedWaitForSeconds()
{
var delay = new WaitForSeconds(3);//define ONCE to avoid memory leak
while (IsGameRunning)
{
if (randomDoorSpeed == true && randomSpeedRange > 0.3f)
doorSpeed = Random.Range(0.3f, randomSpeedRange);
yield return delay;
}
}
}
But there are two problems.
The first problem is that every 3 seconds, when it's changing the speed of the door, it's also changing the door position from its current position. So it looks like the door position is jumping to another position and then continue from there. How can I make that it will change the speed meanwhile the door keep moving from its current position ?
Second problem is how can I change the randomDorrSpeed flag so it will take effect while the game is running? I Want that if randomDorrSpeed is false use the original speed of the door (1.0) and if it`s true use the random speed.
You already know that the coroutine should start from Start:
void Start()
{
//initialization
StartCoroutine(DoorSpeedWaitForSeconds());
}
So make the coroutine a loop with the proper terminate condition:
IEnumerator DoorSpeedWaitForSeconds()
{
var delay = new WaitForSeconds(3);//define ONCE to avoid memory leak
while(IsGameRunning)
{
if(randomDoorSpeed == true && randomSpeedRange > 0.3f)
doorSpeed = Random.Range(0.3f, randomSpeedRange);
if(!randomDoorSpeed)
doorSpeed = 1;//reset back to original value
yield return delay;//wait
}
}
For the other question you asked, if you think about it you can't possibly use ping pong with dynamic speed based on Time.time. You need to change it like this:
bool isRising = true;
float fraq = 0;
void Update()
{
if (isRising)
fraq += Time.deltaTime * doorSpeed;
else
fraq -= Time.deltaTime * doorSpeed;
if (fraq >= 1)
isRising = false;
if (fraq <= 0)
isRising = true;
fraq = Mathf.Clamp(fraq, 0, 1);
door.position = Vector3.Lerp(originalDoorPosition,
new Vector3(originalDoorPosition.x, originalDoorPosition.y, 64f),
fraq);
}
You can solve the original problem without coroutines:
public float timeBetweenChangeSpeed = 3f;
public float timer = 0;
void Update ()
{
// Add the time since Update was last called to the timer.
timer += Time.deltaTime;
// If 3 seconds passed, time to change speed
if(timer >= timeBetweenChangeSpeed)
{
timer = 0f;
//Here you call the function to change the random Speed (or you can place the logic directly)
ChangeRandomSpeed();
}
}
And about opening and closing doors. Here is a script I used to control doors in a maze game I worked in:
You need to set empty gameObjects with to set the boundaries, that is until what point you want to move the door one opening or when closing. You place this empty gameobjects in your scene and link them to the scrip in the correct field. The script will take the transform.position component on it's own. There is also a trigger enter to activate the door when a character approaches. If you dont need that part, I can edit the code tomorrow.
You can use this script also to move platforms, enemies... in general anything which can move in a straight line.
using UnityEngine;
using System.Collections;
public class OpenDoor : MonoBehaviour {
// define the possible states through an enumeration
public enum motionDirections {Left,Right};
// store the state
public motionDirections motionState = motionDirections.Left;
//Variables for State Machine
bool mOpening = false;
bool mClosing = false;
//bool mOpened = false;
//OpenRanges to open/close the door
public int OpenRange = 5;
public GameObject StopIn;
public GameObject StartIn;
//Variables for Movement
float SpeedDoor = 8f;
float MoveTime = 0f;
int CounterDetections = 0;
void Update () {
// if beyond MoveTime, and triggered, perform movement
if (mOpening || mClosing) {/*Time.time >= MoveTime && */
Movement();
}
}
void Movement()
{
if(mOpening)
{
transform.position = Vector3.MoveTowards(transform.position, StopIn.transform.position, SpeedDoor * Time.deltaTime);
if(Vector3.Distance(transform.position, StopIn.transform.position) <= 0)
mOpening = false;
}else{ //This means it is closing
transform.position = Vector3.MoveTowards(transform.position, StartIn.transform.position, SpeedDoor * Time.deltaTime);
if(Vector3.Distance(transform.position, StartIn.transform.position) <= 0)
mClosing = false;
}
}
// To decide if door should be opened or be closed
void OnTriggerEnter(Collider Other)
{
print("Tag: "+Other.gameObject.tag);
if(Other.gameObject.tag == "Enemy" || Other.gameObject.tag == "Player" || Other.gameObject.tag == "Elevator")
{
CounterDetections++;
if(!mOpening)
Opening();
}
}
void OnTriggerStay(Collider Other)
{
if(Other.gameObject.tag == "Elevator")
{
if(!mOpening)
Opening();
}
}
void OnTriggerExit(Collider Other)
{
if(Other.gameObject.tag == "Enemy" || Other.gameObject.tag == "Player")
{
CounterDetections--;
if(CounterDetections<1)
Closing();
}
}
void Opening()
{
mOpening = true;
mClosing = false;
}
void Closing()
{
mClosing = true;
mOpening = false;
}
}
Using timer and setting an interval. The delegate event will fire every time the interval is reached.
var t = new Timer {Interval = 3000};
t.Elapsed += (sender, args) => { /* code here */};
I want to put an intro (like intro.jpg) after the "Powered by Unity" intro and I want the intro to be displayed for 2 seconds.
I assume your first scene is called Game.
Create a new Scene called i.e "Introduction".
In this scene, add your image element and assign it a script :
using UnityEngine;
using System.Collections;
public class Intro : MonoBehaviour {
bool shouldGo = false;
float timeout = 2.0f;
void Update () {
if (shouldGo) {
timeout -= Time.deltaTime;
if (timeout <= 0.0f) Go();
return;
} else {
int percentageLoaded = (int)(Application.GetStreamProgressForLevel("Game") * 100.0f);
}
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) {
if (Application.CanStreamedLevelBeLoaded("Game")) shouldGo = true;
}
if (Input.GetMouseButtonDown(0)) {
if (Application.CanStreamedLevelBeLoaded("Game")) shouldGo = true;
}
}
void Go() {
Application.LoadLevel("Game");
}
}
Then in Build Settings, add your new Introductionscene and move it to the 0 position by dragging on top of the list.