I've been dealing with this problem (that started happening for no reason) for the past 3 days, and with deadlines tightening, it's driving me crazy.
So the gist is, the player presses a button, and the camera moves to a location and unlocks the mouse, so the player can click on the interactable (in this case, a diary).
What I've done to try and fix this:
-Created development build, no errors, and nothing in the output.log
-Reimported all the assets
-Started a new project and imported the assets there
-Upgraded from Unity 2018.1 to 2018.3
-Removed the script and attached it again
-Built for different systems
-Restarted Unity and my PC
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiaryController : MonoBehaviour
{
public Camera mainCam;
public float moveSpeed = 10.0f;
public GameObject cameraObject;
public GameObject targetObject;
private bool movingTowardsTarget = false;
private float lerpSpeed = 0.225f;
public Transform fromRot;
public Transform toRot;
private bool inPosition = false;
public bool onSwitch = false;
public bool freezecamera = false;
public bool freezeplayer = false;
public WheelchairController Mov;
public bool EPressed = false;
public GameObject Crosshair;
public MouseControl myMouseScript;
private bool onDiary = false;
void Start()
{
onDiary = false;
mainCam = Camera.main;
}
// Update is called once per frame
void Update()
{
FreezeCamera();//Checks if the camera has been frozen
FreezePlayer();//Checks if the player has been frozen
if (onSwitch && Input.GetKeyDown(KeyCode.E))
{
CheckPlayer();
EPressed = true;
}
if (onSwitch && movingTowardsTarget)
{
GoToDiary();
Crosshair.SetActive(false);
}
else if (onSwitch && !movingTowardsTarget && EPressed)
{
GoToChair();
Crosshair.SetActive(true);
}
}
void CheckPlayer()
{
if (movingTowardsTarget)
{
movingTowardsTarget = false;
freezecamera = false;
}
else
{
fromRot = fromRot.transform;
toRot = toRot.transform;
// freezecamera = true;
freezeplayer = true;
movingTowardsTarget = true;
}
}
void GoToDiary()
{
StartCoroutine(DelayFreeze());
MoveTowardsTarget(targetObject);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.Confined;
Crosshair.SetActive(false);
freezecamera = true;
freezeplayer = true;
onDiary = true;
}
void GoToChair()
{
StartCoroutine(DelayMov());
MoveTowardsTarget(cameraObject);
freezecamera = false;
Cursor.lockState = CursorLockMode.Locked;
Crosshair.SetActive(true);
onDiary = false;
}
IEnumerator DelayMov()
{
Debug.Log("DelayActivated");
yield return new WaitForSeconds(2);
freezeplayer = false;
}
IEnumerator DelayFreeze()
{
if (!freezeplayer)
{
Debug.Log("DelayActivated");
yield return new WaitForSeconds(1);
freezeplayer = true;
}
}
void MoveTowardsTarget(GameObject target)
{
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, moveSpeed * Time.deltaTime);
transform.rotation = target.transform.rotation;
if (Vector3.Distance(transform.position, target.transform.position) < 0.1)
{
inPosition = true;
Debug.Log("InPosition");
if (inPosition)
{
transform.rotation = target.transform.rotation;
if (Mathf.Abs(fromRot.localEulerAngles.y - toRot.localEulerAngles.y) < 3)
{
transform.rotation = target.transform.rotation;
//movingTowardsTarget = false;
inPosition = false;
Debug.Log("RotatingCamera");
}
}
else
{
transform.rotation = Quaternion.Lerp(fromRot.rotation, toRot.rotation, Time.time * lerpSpeed);
Debug.Log("RotatingCamera2");
}
}
}
void FreezeCamera()
{
if (freezecamera)
{
Debug.Log("CameraFrozen");
myMouseScript.rotationSpeed = 0.0f;
// MouseControl.mouseLookEnabled = false;
ZoomIn.zoomEnable = false;
Crosshair.SetActive(false);
}
else if (!freezecamera)
{
myMouseScript.rotationSpeed = 0.5f;
//MouseControl.mouseLookEnabled = true;
//ZoomIn.zoomEnable = true;
//Crosshair.SetActive(true);
}
}
void FreezePlayer()
{
if (freezeplayer)
{
Debug.Log("PlayerFrozen");
Mov.moveSpeed = 0.0f;
Mov.turnSpeed = 0.0f;
Mov.freeze = true;
}
else if (!freezeplayer)
{
Mov.moveSpeed = 0.4f;
Mov.turnSpeed = 2.5f;
Mov.freeze = false;
}
}
void OnGUI()
{
if (onSwitch)
{
if (!freezeplayer)
{
GUI.Box(new Rect(0, 0, 200, 20), "Press E to open diary");
}
else
{
GUI.Box(new Rect(0, 0, 200, 20), "Press E to close diary");
}
}
}
}
Here's how it behaves in the editor:
https://i.gyazo.com/4570740f027b736789ed1ab13488bf44.gif
Here's how it behaves in the build:
https://i.gyazo.com/6f1b0485855c24c81abb2278f4361a36.gif
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fading : MonoBehaviour
{
[Header("Fading")]
public List<GameObject> objectsToFade = new List<GameObject>();
public float duration;
public Coroutine fadeCoroutine;
public bool automatic = false;
public bool startFading = false;
[Header("Random")]
public bool randomObjects = false;
public bool randomDuration = false;
public bool faded = false;
private bool fadeInOut = false;
private bool coroutineIsRunning = false;
private List<Material> objectsToFadeMaterials = new List<Material>();
private bool randomOnce = false;
private Material randomMaterial;
private float originalDuration;
private void Start()
{
originalDuration = duration;
for (int i = 0; i < objectsToFade.Count; i++)
{
objectsToFadeMaterials.Add(objectsToFade[i].GetComponent<Renderer>().material);
}
}
private void Update()
{
if (startFading)
{
if (automatic)
{
if (!coroutineIsRunning)
{
Fade();
}
}
else
{
if (Input.GetKeyDown(KeyCode.G))
{
Fade();
}
}
}
}
private void Fade()
{
fadeInOut = !fadeInOut;
if (fadeCoroutine != null)
StopCoroutine(fadeCoroutine);
if(randomDuration)
{
duration = Random.Range(1, 20);
}
else
{
duration = originalDuration;
}
if (randomObjects && objectsToFade.Count > 1)
{
if (randomOnce == false)
{
randomMaterial = objectsToFadeMaterials[Random.Range(0, objectsToFadeMaterials.Count)];
randomOnce = true;
}
if (fadeInOut)
{
fadeCoroutine = StartCoroutine(FadeTo(randomMaterial, 0, duration));
}
else
{
fadeCoroutine = StartCoroutine(FadeTo(randomMaterial, 1, duration));
}
}
else
{
for (int i = 0; i < objectsToFadeMaterials.Count; i++)
{
if (fadeInOut)
{
fadeCoroutine = StartCoroutine(FadeTo(objectsToFadeMaterials[i], 0, duration));
}
else
{
fadeCoroutine = StartCoroutine(FadeTo(objectsToFadeMaterials[i], 1, duration));
}
}
}
}
public IEnumerator FadeTo(Material material, float targetOpacity, float duration)
{
Color color = material.color;
float startOpacity = color.a;
float t = 0;
coroutineIsRunning = true;
while (t < duration)
{
t += Time.deltaTime;
float blend = Mathf.Clamp01(t / duration);
color.a = Mathf.Lerp(startOpacity, targetOpacity, blend);
material.color = color;
if(t > duration)
{
coroutineIsRunning = false;
}
if(color.a == 1)
{
randomOnce = false;
}
if(color.a == 0)
{
faded = true;
}
yield return null;
}
}
}
I know that if the color.a is 0 then the object faded out finished then I set faded to true.
In the second script that is attached to a teleporter I want to start teleporting if the object faded out then teleport to the next teleporter and fade in back and so on.
This script is attached to each teleporter :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Teleport : MonoBehaviour
{
public Fading fading;
public List<GameObject> objectsToTeleport = new List<GameObject>();
//Start is called before the first frame update
void Start()
{
objectsToTeleport = GameObject.FindGameObjectsWithTag("ObjectToTeleport").ToList();
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
StartCoroutine(fading.FadeTo(objectsToTeleport[0].GetComponent<Renderer>().material, 0, 2));
}
}
and this script is the teleporting script :
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.WebSockets;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
public class Teleporting : MonoBehaviour
{
public List<ObjectToTeleport> objectsToTeleport;
public List<ObjectToTeleport> currentlyTeleportedObjects;
public List<GameObject> teleporters;
public float teleportingTime;
public float teleportingStartTime;
public bool startTeleporting = false;
public GameObject[] groups;
public bool loop = false;
public bool random = false;
public int teleportationsCount = 0;
[Serializable]
public class ObjectToTeleport
{
public GameObject teleportableObject;
public int teleportOrder;
}
public void Start()
{
StartTeleporting();
}
private void Update()
{
}
public void StartTeleporting()
{
if (startTeleporting)
{
if (teleporters.Count > 1 && objectsToTeleport.Count > 0)
{
InvokeRepeating("MoveTeleportableObjects", teleportingStartTime, teleportingTime);
}
}
}
private void MoveTeleportableObjects()
{
if (teleportationsCount < objectsToTeleport.Count)
currentlyTeleportedObjects.Add(objectsToTeleport[teleportationsCount]);
for (int i = 0; i < currentlyTeleportedObjects.Count; i++)
{
if (!loop)
{
MoveObjects(i);
}
else
{
MoveObjects(i);
}
}
teleportationsCount++;
}
private void MoveObjects(int i)
{
GameObject destinationTeleporter = teleporters[currentlyTeleportedObjects[i].teleportOrder];
currentlyTeleportedObjects[i].teleportableObject.transform.position = destinationTeleporter.transform.position;
if (currentlyTeleportedObjects[i].teleportOrder < teleporters.Count - 1)
{
currentlyTeleportedObjects[i].teleportOrder++;
}
else if (loop == true)
{
{
currentlyTeleportedObjects[i].teleportOrder = 0;
}
}
}
}
I need somehow a synchronization between the fading and the teleporting in the Teleport script.
But it seems a bit complicated.
The Fading script on it's own and the Teleporting script on it's own are working fine but making a synchronization between them is the problem.
Update :
What I tried :
In the Fading script I added two methods FadIn and FadeOut and calling the FadeOut in the Teleport script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fading : MonoBehaviour
{
[Header("Fading")]
public List<GameObject> objectsToFade = new List<GameObject>();
public float duration;
public Coroutine fadeCoroutine;
public bool automatic = false;
public bool startFading = false;
[Header("Random")]
public bool randomObjects = false;
public bool randomDuration = false;
public bool faded = false;
private bool fadeInOut = false;
private bool coroutineIsRunning = false;
private List<Material> objectsToFadeMaterials = new List<Material>();
private bool randomOnce = false;
private Material randomMaterial;
private float originalDuration;
private void Start()
{
originalDuration = duration;
for (int i = 0; i < objectsToFade.Count; i++)
{
objectsToFadeMaterials.Add(objectsToFade[i].GetComponent<Renderer>().material);
}
}
private void Update()
{
if (startFading)
{
if (automatic)
{
if (!coroutineIsRunning)
{
Fade();
}
}
else
{
if (Input.GetKeyDown(KeyCode.G))
{
Fade();
}
}
}
}
private void Fade()
{
fadeInOut = !fadeInOut;
if (fadeCoroutine != null)
StopCoroutine(fadeCoroutine);
if(randomDuration)
{
duration = Random.Range(1, 20);
}
else
{
duration = originalDuration;
}
if (randomObjects && objectsToFade.Count > 1)
{
if (randomOnce == false)
{
randomMaterial = objectsToFadeMaterials[Random.Range(0, objectsToFadeMaterials.Count)];
randomOnce = true;
}
if (fadeInOut)
{
fadeCoroutine = StartCoroutine(FadeTo(randomMaterial, 0, duration));
}
else
{
fadeCoroutine = StartCoroutine(FadeTo(randomMaterial, 1, duration));
}
}
else
{
for (int i = 0; i < objectsToFadeMaterials.Count; i++)
{
if (fadeInOut)
{
fadeCoroutine = StartCoroutine(FadeTo(objectsToFadeMaterials[i], 0, duration));
}
else
{
fadeCoroutine = StartCoroutine(FadeTo(objectsToFadeMaterials[i], 1, duration));
}
}
}
}
private IEnumerator FadeTo(Material material, float targetOpacity, float duration)
{
Color color = material.color;
float startOpacity = color.a;
float t = 0;
coroutineIsRunning = true;
while (t < duration)
{
t += Time.deltaTime;
float blend = Mathf.Clamp01(t / duration);
color.a = Mathf.Lerp(startOpacity, targetOpacity, blend);
material.color = color;
if(t > duration)
{
coroutineIsRunning = false;
}
if(color.a == 1)
{
randomOnce = false;
}
yield return null;
}
}
public IEnumerator FadeIn(Material material, float duration)
{
StartCoroutine(FadeTo(material, 1, duration));
yield return new WaitForSeconds(duration);
}
public IEnumerator FadeOut(Material material, float duration)
{
StartCoroutine(FadeTo(material, 0, duration));
yield return new WaitForSeconds(duration);
}
}
In the Teleport script
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Teleport : MonoBehaviour
{
public Fading fading;
public List<GameObject> objectsToTeleport = new List<GameObject>();
//Start is called before the first frame update
void Start()
{
objectsToTeleport = GameObject.FindGameObjectsWithTag("ObjectToTeleport").ToList();
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
StartCoroutine(fading.FadeOut(objectsToTeleport[0].GetComponent<Renderer>().material, 3));
}
private void OnTriggerExit(Collider other)
{
}
}
How do I continue from here ? what should I do next ?
There are multiple ways.
Simply yield the routine
You already have both as Coroutines so you could wrap them in another Coroutine and yield return it instead of StartCoroutine (this is something you should do in FadeIn and FadeOut anyway!)
// If this returns IEnumerator Unity automatically runs it as Coroutine
private IEnumerator OnTriggerEnter(Collider other)
{
yield return fading.FadeOut(objectsToTeleport[0].GetComponent<Renderer>().material, 3));
// Something after fading finished
Debug.Log("Fade Out finished!");
}
You could of course as well have them separately like
private void OnTriggerEnter(Collider other)
{
StartCorouine (EnterRoutine(other));
}
private IEnumerator EnterRoutine ()
{
yield return fading.FadeOut(objectsToTeleport[0].GetComponent<Renderer>().material, 3));
// Something after fading finished
Debug.Log("Fade Out finished!");
}
Callback
Another way would be passing in a callback that is executed once the routine finishes like e.g.
private void OnTriggerEnter(Collider other)
{
StartCoroutine(fading.FadeOut(objectsToTeleport[0].GetComponent<Renderer>().material, 3, OnFadeOutFinished));
}
private void OnFadeOutFinished ()
{
Debug.Log("Fade Out finished!", this);
}
or using a lambda, especially helpful if you want to do something with the other reference
private void OnTriggerEnter(Collider other)
{
StartCoroutine(fading.FadeOut(objectsToTeleport[0].GetComponent<Renderer>().material, 3, () => {
Debug.Log("Fade Out finished!");
}));
}
and have it as parameter in your routine like
private IEnumerator FadeTo(Material material, float targetOpacity, float duration, Action callback)
{
Color color = material.color;
float startOpacity = color.a;
float t = 0;
coroutineIsRunning = true;
while (t < duration)
{
t += Time.deltaTime;
float blend = Mathf.Clamp01(t / duration);
color.a = Mathf.Lerp(startOpacity, targetOpacity, blend);
material.color = color;
if(t > duration)
{
coroutineIsRunning = false;
}
if(color.a == 1)
{
randomOnce = false;
}
yield return null;
}
callback?.Invoke();
}
public IEnumerator FadeIn(Material material, float duration, Action callback = null)
{
yield return FadeTo(material, 1, duration, callback));
}
public IEnumerator FadeOut(Material material, float duration, Action callback = null)
{
yield return FadeTo(material, 0, duration, callback));
}
There are a lot of ways you could do this, but I personally in this case would create new coroutines FadeIn and FadeOut, instead of directly calling the following:
fadeCoroutine = StartCoroutine(FadeTo(objectsToFadeMaterials[i], 0, duration));
Then, at the end of your FadeOut coroutine you can take some additional step(s) to trigger a teleport or whatever else you need to trigger. It looks like you don't want your Fading to hold a reference to your Teleporting, which is smart, so you could choose to fire an event instead that your Teleporting component can subscribe to.
When using Vector3.MoveTowards, it won't do anything for either one, because one can't run which causes the other to not run.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlaneAI : MonoBehaviour
{
public Transform[] runwayPoints;
public Transform[] startTakeoffPoints;
public Transform[] takeoffPoints;
public NavMeshAgent navMeshAgent;
public bool takeoff = false;
bool departing = false;
bool moving = false;
int selectedRunway;
public IEnumerator Depart()
{
navMeshAgent.SetDestination(runwayPoints[selectedRunway = Random.Range(0, runwayPoints.Length)].position);
yield return new WaitForSeconds(3f);
departing = true;
}
public void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
StartCoroutine(Depart());
}
GetComponent<LineRenderer>().SetPosition(0, new Vector3(transform.position.x, 1, transform.position.z));
GetComponent<LineRenderer>().SetPosition(1, navMeshAgent.destination);
if (navMeshAgent.pathStatus == NavMeshPathStatus.PathComplete && departing && navMeshAgent.remainingDistance == 0)
{
Takeoff();
}
if (moving && Vector3.Distance(transform.position, startTakeoffPoints[selectedRunway].position) <= 0.001f)
{
transform.position = Vector3.MoveTowards(transform.position, takeoffPoints[selectedRunway].position, 3); // this one does not work it should be after the other one
transform.LookAt(takeoffPoints[selectedRunway]);
takeoff = false;
}
}
public void Takeoff()
{
navMeshAgent.enabled = false;
GetComponent<Collider>().enabled = false;
transform.LookAt(new Vector3(takeoffPoints[selectedRunway].position.x, 0, takeoffPoints[selectedRunway].position.z));
MoveTakeoff();
departing = false;
}
public void MoveTakeoff()
{
transform.position = Vector3.MoveTowards(transform.position, startTakeoffPoints[selectedRunway].position, 2); // this one does not work
moving = true;
takeoff = false;
}
}
There are no script errors, it just won't work.
The only errors that I don't think are related are:
"Invalid worldAABB. Object is too large or too far away from the origin."
"Invalid localAABB. Object transform is corrupt."
"Assertion failed on expression: 'IsFinite(d)'
UnityEngine.GUIUtility:processEvent(Int32, IntPtr)"
"Assertion failed on expression: 'IsFinite(outDistanceForSort)'
UnityEngine.GUIUtility:processEvent(Int32, IntPtr)"
"Assertion failed on expression: 'IsFinite(outDistanceAlongView)'
UnityEngine.GUIUtility:processEvent(Int32, IntPtr)"
You need to better separate your state transition logic from your your state activity logic. You have code that should run when a state transition occurs in the same blocks as code that should run every frame a state is active.
One way you could handle this is by turning your states into coroutines:
public class PlaneAI : MonoBehaviour
{
public Transform[] runwayPoints;
public Transform[] startTakeoffPoints;
public Transform[] takeoffPoints;
public NavMeshAgent navMeshAgent;
public bool takeoff = false;
bool departing = false;
bool moving = false;
int selectedRunway;
LineRenderer lr;
void Awake()
{
lr = GetComponent<LineRenderer>();
}
IEnumerator Depart()
{
navMeshAgent.SetDestination(runwayPoints[selectedRunway = Random.Range(0,
runwayPoints.Length)].position);
yield return new WaitForSeconds(3f);
departing = true;
while(departing)
{
if (navMeshAgent.pathStatus == NavMeshPathStatus.PathComplete
&& navMeshAgent.remainingDistance == 0)
{
StartCoroutine(MoveTakeoff());
yield break;
}
yield return null;
}
}
IEnumerator MoveTakeOff()
{
departing = false;
moving = true;
navMeshAgent.enabled = false;
GetComponent<Collider>().enabled = false;
transform.LookAt(new Vector3(takeoffPoints[selectedRunway].position.x, 0,
takeoffPoints[selectedRunway].position.z));
while (moving)
{
if (Vector3.Distance(transform.position,
startTakeoffPoints[selectedRunway].position) <= 0.001f)
{
StartCoroutine(TakeOff())
yield return break;
}
transform.position = Vector3.MoveTowards(transform.position,
startTakeoffPoints[selectedRunway].position, 2);
yield return null;
}
}
IEnumerator Takeoff()
{
moving = false;
takeOff = true;
transform.LookAt(takeoffPoints[selectedRunway]);
while (takeOff)
{
if (Vector3.Distance(transform.position,
startTakeoffPoints[selectedRunway].position) <= 0.001f)
{
takeoff = false;
yield break;
}
transform.position = Vector3.MoveTowards(transform.position,
takeoffPoints[selectedRunway].position, 3);
yield return null;
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
StopAllCoroutines();
StartCoroutine(Depart());
}
lr.SetPosition(0, new Vector3(transform.position.x, 1, transform.position.z));
lr.SetPosition(1, navMeshAgent.destination);
}
}
Ideally, the states should be a single enumerator instead of several booleans, but this hopefully shows what I'm getting at.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class loadingcolorful : MonoBehaviour
{
public float speed = 0.0f;
public bool fillAmount = false;
public bool rotate = false;
public bool changeRotationDir = false;
public bool changeFillDir = false;
private RectTransform rectTransform;
private Image imageComp;
// Use this for initialization
void Start()
{
imageComp = GetComponent<RectTransform>().GetComponent<Image>();
rectTransform = transform.parent.gameObject.transform.GetComponent<RectTransform>();
}
// Update is called once per frame
void Update()
{
if (fillAmount == true)
{
if (imageComp.fillAmount != 1f)
{
if (changeFillDir == true)
{
imageComp.fillAmount -= speed;
}
else
{
imageComp.fillAmount += speed;
}
}
else
{
imageComp.fillAmount = 0f;
}
}
else
{
if (imageComp.fillAmount == 0f)
{
imageComp.fillAmount = 0f;
}
else
{
imageComp.fillAmount = 1f;
}
}
if (rotate == true)
{
if (changeRotationDir == true)
{
rectTransform.Rotate(new Vector3(0, 0, -1));
}
else
{
rectTransform.Rotate(new Vector3(0, 0, 1));
}
}
}
}
The problem is at this place :
if (changeFillDir == true)
{
imageComp.fillAmount -= speed;
}
When it will get to 0 it will not continue to fill the image to the other direction just the image will stay empty. My guess is that the values of the FillAmount ragne is between 1 and 0.
Is there a way to make it happen ?
It's a bit of a hack, but you can switch to a negative scale value and start increasing the fillAmout again. This will make it fill in the other direction, taking the 0 point as pivot.
Assuming you are filling horizontally, flipping the scale would look something like this:
transform.localScale = new Vector3(-1, 1, 1);
A working solution like I wanted :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class loadingcolorful : MonoBehaviour
{
public float speed = 0.0f;
public bool fillAmount = false;
public bool rotate = false;
public bool changeRotationDir = false;
public bool changeFillDir = false;
public bool useBackGroundImage = true;
private RectTransform rectTransform;
private Image imageComp;
private Image backGroundImage;
// Use this for initialization
void Start()
{
imageComp = GetComponent<RectTransform>().GetComponent<Image>();
backGroundImage = transform.parent.gameObject.transform.GetComponent<Image>();
UseBackgImage();
}
// Update is called once per frame
void Update()
{
UseBackgImage(useBackGroundImage);
if (fillAmount == true)
{
if (imageComp.fillAmount != 1f)
{
if (changeFillDir == true)
{
if (imageComp.fillAmount == 0)
{
imageComp.fillAmount = 1f;
}
imageComp.fillAmount -= speed;
}
else
{
imageComp.fillAmount += speed;
}
}
else
{
imageComp.fillAmount = 0f;
}
}
else
{
if (imageComp.fillAmount == 0f)
{
imageComp.fillAmount = 0f;
}
else
{
imageComp.fillAmount = 1f;
}
}
if (rotate == true)
{
if (changeRotationDir == true)
{
rectTransform.Rotate(new Vector3(0, 0, -1));
}
else
{
rectTransform.Rotate(new Vector3(0, 0, 1));
}
}
}
private void UseBackgImage()
{
if (useBackGroundImage == true)
{
backGroundImage.enabled = true;
rectTransform = transform.parent.gameObject.transform.GetComponent<RectTransform>();
}
else
{
backGroundImage.enabled = false;
rectTransform = transform.GetComponent<RectTransform>();
}
}
private void UseBackgImage(bool UseBackGroundImage)
{
if (useBackGroundImage == true)
{
backGroundImage.enabled = true;
}
else
{
backGroundImage.enabled = false;
}
}
}
So I am making a VR game and what I want to do is, as soon as I press the trigger on the joystick the "sword" gets activated and can stay activated for 1 second, after that it has a cooldown of 1 second also in which it cannot get activated, and then it resets. I thought it would be simple but I can't for the life of me make it work.
Here's the code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR;
public class Sword : MonoBehaviour
{
public SteamVR_Action_Boolean ActivateSword;
public SteamVR_Input_Sources handType;
private bool IsSwordActivated = false;
private bool canSwordGetActivated = true;
private bool cooldownStart = false;
public Material activatedSword;
public Material defaultSword;
public float timeStamp;
public float timer = 0;
public float cooldown = 2;
void Start()
{
ActivateSword.AddOnStateDownListener(TriggerDown, handType);
ActivateSword.AddOnStateUpListener(TriggerUp, handType);
timeStamp = Time.time;
}
public void TriggerUp(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
Debug.Log("Trigger is up");
IsSwordActivated = false;
this.GetComponent<MeshRenderer>().material = defaultSword;
}
public void TriggerDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
if (canSwordGetActivated == true)
{
Debug.Log("Trigger is down");
IsSwordActivated = true;
cooldownStart = true;
this.GetComponent<MeshRenderer>().material = activatedSword;
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
if (IsSwordActivated == true)
{
Destroy(collision.gameObject);
}
}
}
private void Update()
{
//if (timeStamp <= Time.time)
//{
// if (IsSwordActivated == true)
// {
// timeStamp += 2;
// canSwordGetActivated = false;
// Debug.Log("test");
// }
//}
if (cooldownStart == true)
{
timer += Time.deltaTime;
cooldown -= Time.deltaTime;
if (timer >= 1f)
{
this.GetComponent<MeshRenderer>().material = defaultSword;
IsSwordActivated = false;
timer = 0;
}
if (timer == 0)
{
canSwordGetActivated = false;
}
if (cooldown <= 1f)
{
canSwordGetActivated = false;
}
if (cooldown <= 0)
{
cooldown = 2f;
canSwordGetActivated = true;
cooldownStart = false;
}
}
}
}
You should not use Update but a Coroutine for that. Especially the "cooldown" should run independently from the "timer".
// Not needed
//private bool cooldownStart = false;
//public float timer = 0;
// reference this already via the Inspector if possible
[SerializeField] private MeshRenderer _meshRenderer;
[SerializeField] private float cooldownTime = 1;
[SerializeField] private float maxEnabledTime = 1;
// Otherwise get it only ONCE on runtime
private void Awake()
{
if(!_meshRenderer) _meshRenderer = GetComponent<MeshRenderer>();
}
public void TriggerUp(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
Debug.Log("Trigger is up");
// stop the timeout
StopAllCoroutines();
// disable sword and start cooldown
StartCoroutine(SwordCooldown());
}
public void TriggerDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
if(IsSwordActivated) return;
if (!canSwordGetActivated) return;
Debug.Log("Trigger is down");
// start timeout
StartCoroutine(SwordEnabledTimer());
}
///<summary>
/// Disables sword and Runs cooldown before sword can be enabled again
///<summary>
private IEnumerator SwordCooldown()
{
canSwordGetActivated = false;
IsSwordActivated = false;
_meshRenderer.material = defaultSword;
yield return new WaitForSeconds(cooldownTime);
canSwordGetActivated = true;
}
///<summary>
/// Disables the sword and jumps to cooldown after it was enabled for too long
///<summary>
private IEnumerator SwordEnabledTimer()
{
canSwordGetActivated = false;
yield return new WaitForSeconds(maxEnabledTime);
StartCoroutine(SwordCooldown());
}
See WaitForSeconds
Sideeffect/Advantage:
Coroutines are often better read- and maintainable and more flexible then polling states in Update!
For example You can now very simple extend both routines and add a progress display for both like e.g.
private IEnumerator SwordCooldown()
{
canSwordGetActivated = false;
IsSwordActivated = false;
_meshRenderer.material = defaultSword;
var timePassed = 0f;
while(timePassed < cooldownTime)
{
var cooldownProgress = timePassed / cooldownTime;
// do something with the cooldownProgress value 0-1
timePassed += Mathf.Min(Time.deltaTime, cooldownTime - timePassed);
yield return null;
}
canSwordGetActivated = true;
}
One simple way of doing this would be a coroutine:
class Sword {
private bool isCoolingDown;
public void Swing() {
if (isCoolingDown) {
Debug.Log("Sorry, cooling down right now");
return;
}
StartCoroutine(DoSwingSword());
}
private IEnumerator DoSwingSword() {
isCoolingDown = true;
yield return new WaitForSeconds(1);
isCoolingDown = false;
}
}
This will use a bool to track if you are in cooled down state and automatically reset this bool after one second, without you having to track Time.deltaTime. You can then wrap the rest of your logic around this bool.
I'm creating a FPS game and I have the following issue:
Sometimes, when I shoot at the enemies the hit is not recognized, even if the player is shooting in front of them. However, when they attack the player, the hit is recognized normally.
They have a box collider and a rigidbody attached to them.
This script is attached to the player:
using System.Collections.Generic;
using UnityEngine;
public class DisparaArma : MonoBehaviour
{
private GerenciaArma gerenciaArma;
public float nDisparo = 15f;
private float TempoProximoDisparo;
public float damage = 20f;
private Animator ZoomCameraIn;
private bool zoomed;
private Camera Maincamera;
private GameObject mira;
// Start is called before the first frame update
void Start()
{
gerenciaArma = GetComponent<GerenciaArma>();
ZoomCameraIn = transform.Find(Tags.LOOK_ROOT).transform.Find(Tags.ZOOM_CAMERA).GetComponent<Animator>();
mira = GameObject.FindWithTag(Tags.MIRA);
Maincamera = Camera.main;
}
// Update is called once per frame
void Update()
{
Atira();
ZoomInAndOut();
}
void Atira()
{
if (Input.GetMouseButtonDown(0))
{
if(gerenciaArma.SelecionaArma().tipoBala == WeaponBulletType.BULLET)
{
gerenciaArma.SelecionaArma().AnimacaoTiro();
DisparaBala();
}
}
}
void ZoomInAndOut()
{
if (gerenciaArma.SelecionaArma().mira_tipo == TipoMira.AIM)
{
if (Input.GetMouseButtonDown(1))
{
ZoomCameraIn.Play(Animacoes.ZOOM_IN_ANIM);
// gerenciaArma.SelecionaArma().Aim(true);
mira.SetActive(false);
print("VaiZoom");
}
if (Input.GetMouseButtonUp(1))//
{
ZoomCameraIn.Play(Animacoes.ZOOM_OUT_ANIM);
//gerenciaArma.SelecionaArma().Aim(false);
mira.SetActive(true);
}
}
}
void DisparaBala()
{
RaycastHit hit;
if(Physics.Raycast(Maincamera.transform.position, Maincamera.transform.forward, out hit))
{
if (hit.transform.tag == Tags.ENEMY_TAG)
{
hit.transform.GetComponent<ScriptVida>().DanoAplicado(damage);
}
}
}
}
And this one is attached to the enemies:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class ScriptVida : MonoBehaviour
{
private IndioAnimações indio_Anim;
private NavMeshAgent navAgent;
private IndioController indio_Controller;
public float vida = 100f;
public bool is_Player, is_Cannibal, is_Tiger;
private bool morto;
// Start is called before the first frame update
void Awake()
{
if (is_Tiger || is_Cannibal)
{
indio_Anim = GetComponent<IndioAnimações>();
indio_Controller = GetComponent<IndioController>();
navAgent = GetComponent<NavMeshAgent>();
}
if (is_Player)
{
}
}
public void DanoAplicado(float damage)
{
if (morto)
return;
vida -= damage;
if (is_Player)
{
}
if (is_Tiger || is_Cannibal)
{
if (indio_Controller.EnemyState == EnemyState.PATROL)
{
indio_Controller.chase_Distance = 50f;
}
}
if (vida <= 0)
{
JogadorMorre();
morto = true;
print(vida);
}
}
void JogadorMorre()
{
if (is_Cannibal)//
{
GetComponent<Animator>().enabled = false;
GetComponent<BoxCollider>().isTrigger = false;
GetComponent<Rigidbody>().AddTorque(-transform.forward * 50f);
indio_Controller.enabled = false;
navAgent.enabled = false;
indio_Anim.enabled = false;
}
if (is_Tiger)
{
GetComponent<Animator>().enabled = false;
GetComponent<BoxCollider>().isTrigger = false;
GetComponent<Rigidbody>().AddTorque(-transform.forward * 50f);
indio_Controller.enabled = false;
navAgent.enabled = false;
indio_Anim.enabled = false;
}
if (is_Player)
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(Tags.ENEMY_TAG);
for (int i = 0; i < enemies.Length; i++)
{
enemies[i].GetComponent<IndioController>().enabled = false;
}
GetComponent<Movimentação>().enabled = false;
GetComponent<DisparaArma>().enabled = false;
GetComponent<GerenciaArma>().SelecionaArma().gameObject.SetActive(false);
}
if (tag == Tags.PLAYER_TAG)
{
Invoke("RestartGame", 3f);
}
else
{
Invoke("TurnOffGameObject", 3f);
}
}
void RestartGame()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("Gameplay");
}
void TurnOffGameObject()
{
gameObject.SetActive(false);
}
}
I think the problem is related to the box collider.
How could I solve this guys?