coroutine animation not playing as expected - c#

so everything seems to be running as far as the AI switching different States, however when it gets in range with the player the State freezes in attack and does not play any Attack Animation, I have created the AI with Bone Rigging, Im wondering if this may be affecting it, here is my script for calling the Animation and the script for one the animation has finished to go back to idleFollow state.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeleeWeapon : Weapon
{
[SerializeField] private float attackDelay = 1f;
private Collider2D damageAreaCollider2D;
private Animator animatorAttack;
private bool attacking;
private readonly int useMeleeWeapon = Animator.StringToHash(name:"UseMeleeWeapon");
private void Start()
{
damageAreaCollider2D = GetComponent<BoxCollider2D>();
animatorAttack = GetComponent<Animator>();
//animatorAttack.Play(useMeleeWeapon);
}
public override void UseWeapon()
{
StartCoroutine(routine: Attack());
}
/*protected override void Update()
{
base.Update();
// FlipMeleeWeapon();
}*/
private IEnumerator Attack()
{
if (attacking)
{
yield break;
}
// Attack
attacking = true;
damageAreaCollider2D.enabled = true;
animatorAttack.Play(useMeleeWeapon);
// Stop Attack
yield return new WaitForSeconds(attackDelay);
damageAreaCollider2D.enabled = false;
attacking = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Decisions/Attack Completed", fileName = "AttackCompleted")]
public class DecisionAttackCompleted : AIDecision
{
public override bool Decide(StateController controller)
{
return AttackCompleted(controller);
}
private bool AttackCompleted(StateController controller)
{
if (controller.CharacterWeapon.CurrentWeapon.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0).length
> controller.CharacterWeapon.CurrentWeapon.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0).normalizedTime)
{
return true;
}
return false;
}
}

Ok I fixed the issue, I ended up placing this code into the WeaponMelee script, but now im dealing with a weird glitch where the AI keeps tring to face the left instead of the player when they are close to eachother. this has to do with the boxcollider2D so ive placed a capsulecollider2d inside of him and changed the script to capsulecollider2d as well. but the issue is still happening
private void OnTriggerEnter2D(Collider2D collision)
{
StartCoroutine(routine: Attack());
}

Related

Unity - Actions Being Called Twice - OnTriggerEnter, OnClick, EVERYTHING? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 10 months ago.
Improve this question
So I'm creating a Sheep Counter Game and yesterday, when I went to bed everything was working great. Today, when I opened Unity up to do some finishing touches, everything I'm doing is being called twice...
So when I click the start button it calls the start game twice, then when my sheep hit obstacles it calls OnTriggerEnter twice. Idk what I did wrong or what happened and would prefer to not have to restart the whole project...
Console Log
Counter Script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Counter : MonoBehaviour
{
public TMP_Text savedText;
public TMP_Text deadText;
private int savedCount = 0;
private int deadCount = 0;
private void Start()
{
savedCount = 0;
deadCount = 0;
}
public void AddSavedSheep()
{
savedCount++;
savedText.text = "Saved: " + savedCount;
}
public void AddDeadSheep()
{
deadCount++;
deadText.text = "Dead: " + deadCount;
}
}
Sheep Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SheepControls : MonoBehaviour
{
private Rigidbody rb;
[Header("Movement")]
[Tooltip("How fast to move GameObject Forward.")]
public float forwardSpeed = 4.0f;
[Tooltip("Apply this much force to Rigidbody.")]
public float jumpForce = 5.0f;
private float groundY;
[Space]
[SerializeField] private bool jumping;
[SerializeField] private bool isDestroyed;
[SerializeField] private bool isSaved;
public ParticleSystem explosionParticles;
private Counter counter;
void Start()
{
rb = GetComponent<Rigidbody>();
groundY = (transform.position.y)+0.02f;
counter = FindObjectOfType<Counter>();
}
void Update()
{
transform.Translate(forwardSpeed * Time.deltaTime * Vector3.forward);
if (Input.GetKeyDown(KeyCode.Space) && !jumping)
{
if(transform.position.y < groundY)
{
Jump();
}
}
jumping = false;
}
private void Jump()
{
jumping = true;
rb.AddForce(Vector3.up * jumpForce);
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("ExplosiveWire"))
{
if (isDestroyed) return;
else
{
Debug.Log("Hit Wire");
isDestroyed = true;
Destroy(gameObject);
Instantiate(explosionParticles, transform.position,
explosionParticles.transform.rotation);
}
counter.AddDeadSheep();
}
if (other.CompareTag("Goal"))
{
if (isSaved) return;
else
{
Debug.Log("Reached Goal");
isSaved = true;
Destroy(gameObject);
}
counter.AddSavedSheep();
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Sheep"))
{
Physics.IgnoreCollision(this.GetComponent<Collider>(),
collision.gameObject.GetComponent<Collider>());
}
}
}
GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public bool isGameActive;
public GameObject titleScreen;
public GameObject sheepControllerPrefab;
public void StartGame(int difficulty)
{
titleScreen.SetActive(false);
isGameActive = true;
InvokeRepeating(nameof(SpawnSheepWave), 2.0f, 2.0f);
}
public void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void SpawnSheepWave()
{
Instantiate(sheepControllerPrefab, transform.position,
transform.rotation);
}
}
Start Button Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DifficultyButton : MonoBehaviour
{
private Button button;
private GameManager gameManager;
[Header("Difficulty Level")]
[Tooltip("The spawn rate will be divided by this number.")]
public int difficulty;
void Start()
{
gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
button = GetComponent<Button>();
button.onClick.AddListener(SetDifficulty);
}
public void SetDifficulty()
{
Debug.Log(gameObject.name + " was clicked.");
gameManager.StartGame(difficulty);
}
}
So, it's actually adding the score twice, so originally I thought maybe I need to do an isDestroyed or isSaved check and added that to the script but nothing seems to keep it from adding the score twice, or thinking I clicked the start button twice.
Double increment of the score
I feel like my code is fine, I just don't know what is going on with it and why it is calling everything twice. Do you think there is a solution, or that I might just have to recreate the game in a new project?
Would trying to disable the box collider when it enters the trigger maybe fix the problem?
There is only one BoxCollider on the sheep, and the one BoxCollider on the fence, and one BoxCollider on the goal. They all have rigid bodies and the calls are being made correctly, they just happen two times at once.
Thanks for any help you guys can provide!
I also tried moving the increment step before or after the if statements to see if it would do anything different but I get the same results.
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("ExplosiveWire"))
{
if (isDestroyed) return;
else
{
Debug.Log("Hit Wire");
isDestroyed = true;
Destroy(gameObject);
Instantiate(explosionParticles, transform.position,
explosionParticles.transform.rotation);
counter.AddDeadSheep();
}
}
if (other.CompareTag("Goal"))
{
if (isSaved) return;
else
{
Debug.Log("Reached Goal");
isSaved = true;
Destroy(gameObject);
counter.AddSavedSheep();
}
}
}
[Edit]
Added Image of scripts attached to game objects.
Scripts Attached to Game Objects
Each click you call start on your difficulty button. Which has already run. So you will get 2. See the choices in your start button inspector

Why is SetActive not working in my script?

A new problem has just appeared in my code. In OnTriggerEnter i'm testing if the player collides with the obstacle. When I tell the script to display a message it works fine. But when I replace the Debug.Log("test"), with: deadScreen.gameObject.SetActive(true) to enable my death screen, it just dosen't work and I don't know why. Any help would be appreciated. Thanks in advance.
⠀⠀
Here's my CollisionWithObstacle script that's attached to my obstacle:⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollisionWithObstacle : MonoBehaviour
{
public GameObject deadScreen;
private bool isShown = false;
public static bool alive = true;
void Start()
{
isShown = false;
}
private void OnTriggerEnter(Collider other)
{
alive = false;
}
private void Update()
{
if (!alive)
{
deadScreen.SetActive(true);
deadScreen.active = true;
isShown = true;
}
}
}
Most of the time that I encountered this problem is because other scripts are interacting with the gameobject. Furthermore if it's UI you can try to use CanvasGroup and switch alpha between 0-1 and block raycasts. Let me know how it works out for you

Why is my pause menu script only working once? SetActive only working with false

I'm wondering why my pause script doesn't work as expected. It should freeze the time and bring up the Pause menu, but instead it only freezes and nothing happens after that. And i can't resume the game too
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class PauseMenu : MonoBehaviour
{
// Start is called before the first frame update
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
}
}
SetActive(false) will disable the game object, i.e. it won't update anymore. So if your pauseMenuUI is the object you call that on (or a descent of it) then your script won't be called anymore.
The solution is to put your script on another object (e.g. a parent of pauseMenuUI`).

Beginner having Problems with Restarting Scenes when Dying in Unity

so I’m trying to create a respawn system as a beginner, and everything seems to be working the first time but the second time it seems to be unbehaving. If anyone knows how to help me, I would appreciate it
LevelControl:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelControl : MonoBehaviour
{
public int index;
public string levelName;
public GameObject GameOverPanel;
public static LevelControl instance;
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Loading level with build index
SceneManager.LoadScene(index);
//Loading level with scene name
SceneManager.LoadScene(levelName);
//Restart level
//SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
public void LoadLevel1()
{
SceneManager.LoadScene("Game");
}
public GameObject GetGameOverScreen()
{
return GameOverPanel;
}
}
PlayerMovement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController2D controller;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
// Update is called once per frame
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
if (Input.GetButtonDown("Jump"))
{
jump = true;
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
}
void FixedUpdate()
{
// Move our character
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
//FindObjectOfType<GameManager>().EndGame();
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
GameObject.Find("Player").SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
}
}
}
Error In Unity Video: https://imgur.com/a/Sr0YCWk
If your wondering what I'm trying to do, well, when the 2 colliders of the Player and Enemy collide, I want a restart button to pop up, and the Character to get destroyed, and after that restart the level as is.
You didn't provide much but I try to work with what we have.
In LevelController you have
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
First of all just use
instance = this;
;)
Then you are doing
LevelControl.instance.GetGameOverScreenn().SetActive(true);
I don't see your setup but probably GetGameOverScreenn might not exist anymore after the Scene reload while the instance still does due to the DontDestroyOnLoad.
Actually, why even use a Singleton here? If you reload the entire scene anyway you could just setup the references once via the Inspector and wouldn't have to worry about them later after scene changes...
Also
GameObject.Find("Player").SetActive(false);
seems odd .. isn't your PlayerController attached to the Player object anyway? You could just use
gameObject.SetActive(false);
Ok, Normally it would be easier just to do this:
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
gameObject.SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
But this will NOT work if you want to attach the script to any other gameobjects for any reason. If you are then first make a game object variable containing the player, like this:
public GameObject Player = GameObject.Find("Player");
Then say
Player.SetActive(false);
This creates a player gameobject which you can access by calling the variable.

Coroutine stops working

I have two scripts with a coroutine in them. It works perfectly fine in the first one, but not in the second one, for no apparent reason.
It works in this one:
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.ImageEffects;
public class GameStartController : MonoBehaviour {
public Button startButton;
public GameObject cubeSpawner;
// Use this for initialization
private void Start() {
startButton = startButton.GetComponent<Button>();
}
public void StartGame() {
EnableCubeSpawner();
SpawnStartingCubes();
HideStartMenu();
StartCoroutine("FocusCamera");
PlayBackgroundMusic();
}
// Enables the cube spawner, so it can start spawning cubes
private void EnableCubeSpawner() {
cubeSpawner.SetActive(true);
}
private void SpawnStartingCubes() {
cubeSpawner.GetComponent<CubeSpawner>().GenerateStartingCubes();
}
private void PlayBackgroundMusic() {
var audio = GameObject.FindWithTag("Audio").GetComponent<AudioController>();
audio.PlayBackgroundMusic();
}
private void HideStartMenu() {
startButton.transform.parent.GetComponent<CanvasGroup>().interactable = false;
startButton.transform.parent.GetComponent<CanvasGroup>().alpha = 0f;
}
private IEnumerator FocusCamera() {
var camera = GameObject.FindWithTag("MainCamera").GetComponent<Camera>();
var velocity = 0f;
while (Mathf.Abs(camera.GetComponent<DepthOfField>().aperture) > 0.001f) {
Debug.Log(Mathf.Abs(camera.GetComponent<DepthOfField>().aperture));
camera.GetComponent<DepthOfField>().aperture = Mathf.SmoothDamp(camera.GetComponent<DepthOfField>().aperture, 0f, ref velocity, 0.3f);
yield return null;
}
camera.GetComponent<DepthOfField>().aperture = 0f;
}
}
The coroutine works just fine and the camera aperture goes smoothly from 0.6 to 0.
However in the second script this doesn't happen:
using System.Collections;
using System.Linq;
using UnityEngine;
using UnityStandardAssets.ImageEffects;
public class GameOverController : MonoBehaviour {
public void EndGame() {
StartCoroutine("UnfocusCamera");
DisableCubeSpawner();
DestroyAllCubes();
StopBackgroundMusic();
ShowStartMenu();
}
// Disables the cube spawner, so it can stop spawning cubes
private void DisableCubeSpawner() {
var cubeSpawner = GameObject.FindWithTag("CubeSpawner");
cubeSpawner.SetActive(false);
}
private void DestroyAllCubes() {
var gameObjects = FindObjectsOfType(typeof(GameObject));
foreach (var gameObject in gameObjects.Where(gameObject => gameObject.name.Contains("Cube"))) {
Destroy(gameObject);
}
}
private void StopBackgroundMusic() {
var audio = GameObject.FindWithTag("Audio").GetComponent<AudioController>();
audio.StopBackgroundMusic();
}
private void ShowStartMenu() {
var startMenu = GameObject.FindWithTag("StartMenu");
startMenu.GetComponent<CanvasGroup>().interactable = true;
startMenu.GetComponent<CanvasGroup>().alpha = 1f;
}
private IEnumerator UnfocusCamera() {
var camera = GameObject.FindWithTag("MainCamera").GetComponent<Camera>();
var velocity = 0f;
while (camera.GetComponent<DepthOfField>().aperture < 0.6f) {
Debug.Log(Mathf.Abs(camera.GetComponent<DepthOfField>().aperture));
camera.GetComponent<DepthOfField>().aperture = Mathf.SmoothDamp(camera.GetComponent<DepthOfField>().aperture, 0.6f, ref velocity, 0.3f);
yield return null;
}
// camera.GetComponent<DepthOfField>().aperture = 0f;
}
}
It only works for one frame (aperture goes from 0 to 0.03), and then just stops.
Why is this happening?
If you destroy (or disable) a game object, coroutines running on components attached to it will stop. I'm unable to find a primary source on this, but here are two other stack overflow questions where this was the problem:
Unity3d coroutine stops after while-loop
Unity - WaitForSeconds() does not work
The coroutine stops because the GameObject your GameOverController is attached to is destroyed. Presumably Unity checks whether an object still exists before resuming its coroutine, and if the object is destroyed, Unity does not continue to execute it.
To fix this problem, you can delay destroying the GameObject until the animation is complete (perhaps putting the destroy code after the while loop in the coroutine) or put the component on a GameObject that won't be destroyed.
In most cases this mean that your object or script became inactive. Best way to check this is to add OnDisable() method to your script and call logging from it

Categories