How to start animation state only once per object distance check? - c#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
public class SpreadedFireDistanceCheck : MonoBehaviour
{
public Transform player;
public List<GameObject> spreadedFires = new List<GameObject> ();
private Animator anim;
private bool doOnce = true;
void Start()
{
spreadedFires = GameObject.FindGameObjectsWithTag("Spreaded Fire").ToList();
anim = player.GetComponent<Animator>();
}
private void Update()
{
foreach(GameObject spreadedFire in spreadedFires)
{
if(Vector3.Distance(player.transform.position, spreadedFire.transform.position) < 5f && doOnce)
{
anim.Play("Walk Backward");
doOnce = false;
}
else
{
doOnce = true;
}
}
}
}
There are 8 spreaded fires objects and I want to make that if the player is getting close to one of the spreaded fires play animation once.
The problem is that I need to make a loop so it will play the animation 8 times instead only once depending on how close the player is to one of the spreaded fires. And if two or more spreaded fires objects are in range also play the animation once.

If what you want is if any of your spreadedFire is close enough, then play the fire animation (only) once in each frame, then you can use LinQ Any Operator:
using System.Linq;
......
private void Update()
{
if (spreadedFires.Any(x => IsCloseEnough(x)))
{
anim.Play("Walk Backward");
}
}
private bool IsCloseEnough(GameObject spreadedFire)
{
return Vector3.Distance(player.transform.position, spreadedFire.transform.position) < 5f;
}

Related

Multiple objects, one animation?

I want to move multiple characters using a single animation. I tried to use loop for this but failed. Can you help? I want to return the characters I added under GameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ses : MonoBehaviour
{
AudioSource audioSource;
public Animator animator;
public GameObject[] karakul;
void Start()
{
audioSource = GetComponent<AudioSource>();
//animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
}
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "sayiCollider")
{
audioSource.Play();
foreach (var item in karakul)
{
animator.SetBool("clap", true);
}
}
else
{
animator.SetBool("clap", false);
}
}
}
enter image description here
enter image description here
enter image description here
You are iterating through the GameObjects, and every time set a param on an animator, which is nothing to do with those GameObjects.
Usually, the crowd has solved another way for performance, but to answer your question, you need to add an Animator component to all GameObject you want to animate and call the SetBool on them, something like that:
[SerializeField] private Animator[] _animators;
private void OnTiggerEnter(Collider other)
{
bool isSayi = other.CompareTag("sayiCollider");
if (isSayi) audioSource.Play();
else audioSource.Stop();
for (int i = 0; i < _animators.Length; i++) _animators[i].SetBool("clap", isSayi);
}

Score Not Updating

I have been trying to make an OnTriggerEnter Score system and it has not been updating and is showing no errors so am I doing something wrong that I dont know about here is my Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class ScoreManger : MonoBehaviour
{
public TextMeshProUGUI MyscoreText;
private int ScoreNum;
// Start is called before the first frame update
void Start()
{
ScoreNum = 0;
MyscoreText.text = ScoreNum.ToString();
}
void update() {
MyscoreText.text = ScoreNum.ToString();
}
public void OnTriggerEnte2D(Collider2D col){
if(col.tag == "Score"){
ScoreNum += 1;
Debug.Log("It Worked");
MyscoreText.text = ScoreNum.ToString();
Destory(col.gameObject);
}
}
}
You spelled OnTriggerEnter2D wrong.
Or you haven't marked at least one of the colliders you are interacting with as a trigger.
Or you haven't attached the script to a gameobject.

Why Update() doesnt set .text?

I have a Script Coin-Counter connected with a Text. Every time the Player and the Coin are colliding the coinScore decreases by 1. If I want to display the coinScore in Update() this doesn't work. Why?
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinScore : MonoBehaviour
{
[SerializeField] private Text coinScorer;
private int coinScore;
private int oldCoinScore;
void Update()
{
coinScorer.text = coinScore.ToString(); // This doesn't work.
oldCoinScore = coinScore;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
coinScore += 1;
Destroy(gameObject);
//coinScorer.text = coinScore.ToString(); //This works.
}
}
Your collision method is destroying this game object.
There is no update to run after that.

C# Unity Queue NullReferenceException

I am trying to create a simple dialogue system for my game in Unity. I've set up a special trigger for a Dialogue to start and the code is passing right variable but somehow it gets stuck at clearing the queue and throws a NullReferenceException.
I've seen through debugger that all variables and triggers work perfectly fine till cs:27 inside DialogueManager.cs. I've also checked the inspector to make sure everything is correctly assigned.
Dialogue class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Dialogue
{
public string name;
[TextArea(3,10)]
public string[] sentences;
}
DialogueTrigger class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NPC_DialogTrigger : MonoBehaviour
{
// Player
public Transform player;
// GameMaager to close
public GameObject Gameplay;
// Camera and Canvas to turn on
public GameObject DialogueManager;
// NPC Details
public GameObject InteractionNPCNameTextField;
public Transform interactionTransform;
public float radius = 3f;
private bool isBusy = false;
// DialogueStart
public GameObject DialogueStart;
void Start()
{
InteractionNPCNameTextField.gameObject.SetActive(false);
}
void Update()
{
float distance = Vector3.Distance(player.position, interactionTransform.position);
if (distance <= radius)
{
if (isBusy == false)
{
InteractionNPCNameTextField.gameObject.SetActive(true);
if (Input.GetKeyDown(KeyCode.E))
{
Dialogue();
Debug.Log("Started Dialogue procedure.");
}
}
}
else if (distance > radius)
{
InteractionNPCNameTextField.gameObject.SetActive(false);
}
}
public void Dialogue()
{
Gameplay.SetActive(false);
DialogueManager.SetActive(true);
DialogueStart.GetComponent<DialogueStart>().TriggerDialogue();
Debug.Log("Triggered Dialogue.");
}
void OnDrawGizmosSelected()
{
if (interactionTransform == null)
{
interactionTransform = transform;
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(interactionTransform.position, radius);
}
}
DialogueStart class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DialogueStart : MonoBehaviour
{
public Dialogue dialogue;
public void TriggerDialogue()
{
FindObjectOfType<DialogueManager>().StartDialogue(dialogue);
Debug.Log("Dialogue sent to dialogue manager.");
}
}
DialogueManager class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class DialogueManager : MonoBehaviour
{
public Text nameText;
public Text DialogueText;
private Queue<string> sentences;
public GameObject DialogueManagerUI;
void Start()
{
if (sentences == null)
{
sentences = new Queue<string>();
}
}
public void StartDialogue (Dialogue dialogue)
{
Debug.Log("Received dialogues: " + dialogue);
nameText.text = dialogue.name;
Debug.Log("Start Dialogue: " + sentences.Count);
sentences.Clear();
Debug.Log("Sentences Cleared: " + sentences);
foreach (string sentence in dialogue.sentences)
{
sentences.Enqueue(sentence);
}
DisplayNextSentence();
}
public void DisplayNextSentence()
{
if(sentences.Count == 0)
{
EndDialogue();
return;
}
string sentence = sentences.Dequeue();
DialogueText.text = sentence;
}
void EndDialogue()
{
Debug.Log("End of conversation.");
}
private void Update()
{
if (DialogueManagerUI.activeInHierarchy)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else if (!DialogueManagerUI.activeInHierarchy)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}
Visual Studio doesn't give me any errors.
Unity error code -> Screenshot
Debugger right before issue -> Screenshot2
Seems to me like the Queue is never assigned to sentences and it remains empty.
If it is so - why?
It sounds like DialogueManager.StartDialogue probably via DialogueStart.TriggerDialogue is called from somewhere in either Awake or at least before your Start was executed.
Especially
DialogueManager.SetActive(true);
lets assume the DialogueManager object is inactive at first anyway. So maybe your stuff is called before it is set to active.
This might also be due to the Start where you set
InteractionNPCNameTextField.gameObject.SetActive(false);
so any component on this GameObject might not have its Start method getting called. Maybe you referenced the wrong GameObject here?
Debugging would help to figure out in which order your methods get called.
In general my thumb rule for initializing is
Do everything that depends only on yourself in Awake
Do everything that depends on other components being setup already in Start
This way you can (almost) always be sure that stuff is already initialized when you need it.
However, these are only guesses but
The simple solution here:
You could already solve this by simply initializing the sentences right away using
private readonly Queue<string> sentences = new Queue<string>();
now it is definitely initialized even before Start or Awake get called!

Next Scene Not Loading

My problem is that when all the enemies are killed the scene that should be loaded is not loading. I did add the scene to the Build setting (it has an index of 3) but it is still not loading. The script I created is attached to an empty object and not directly to the sprite (is that okay?). Can someone tell me why the scene isn't loading? Thank you.
This image is for to show you the EnemySpawner empty object inspector
EnemySpawner Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour {
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public LevelManager myLevelManager;
public int maximumnumberofhits = 0;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while(CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
void OnCollisionEnter2D()
{
timesEnemyHit++;
if (timesEnemyHit == maximumnumberofhits)
{
CurrentNumOfEnemies--;
Destroy(this.gameObject);
}
if (CurrentNumOfEnemies == 0)
{
myLevelManager.LoadLevel("NextLevelMenu");
Debug.Log("LevelLoaded");
}
}
}
LevelManger script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
EnemyShooting Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if(CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
EnemyPathing script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyPathing : MonoBehaviour {
[SerializeField] List<Transform> WayPoints;
[SerializeField] float EnemyMovingSpeed = 5f;
int WayPointIndex = 0;
void EnemyMoving()
{
if (WayPointIndex <= WayPoints.Count - 1)
{
var TargetedPosition = WayPoints[WayPointIndex].transform.position; //The position of where the enemy needs to go
TargetedPosition.z = 0f;
var MoveThisFrame = EnemyMovingSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(this.transform.position, TargetedPosition, MoveThisFrame);
if(transform.position == TargetedPosition)
{
WayPointIndex++;
}
}
else
{
Destroy(gameObject);
}
}
// Use this for initialization
void Start () {
transform.position = WayPoints[WayPointIndex].transform.position;
}
// Update is called once per frame
void Update () {
EnemyMoving();
}
}
Problem
You're checking for collisions on the SPAWNER; when someone hits the Spawner it counts down enemies. But the Spawner doesn't have a collision box in the screenshot so it can never be hit. The Scene changing code can never be called.
So the game, based on the code, looks like this:
Spawn X enemies,
Hit the Spawner X times,
(Removed: Destroy the Spawner,)
Change scene.
I'm guessing this is conceptually incorrect and you actually want to check collisions on the spawned enemies, which will then count up the amount of destroyed enemies, and change the scene when they are all dead.
Solution
Conceptually, what you want is:
Spawn X enemies
Count up variable for every enemy
On Enemy death, count it down
When 0, change scene
So how do we code this?
Well, every enemy needs a reference to the object that holds the count. You can do this in several ways, when I personally do it I usually have just one spawner that is responsible for everyone so I make that one a Singleton, that can be references from anywhere:
EnemySpawner
public class EnemySpawner : MonoBehaviour
{
public static Spawner Instance = null;
int CurrentNumOfEnemies = 0;
// ... etc
void Start()
{
if (Instance == null)
Instance = this;
// Spawn enemies like you do already, CurrentNumOfEnemies++ for every spawned
}
public OnEnemyDeath() {
CurrentNumOfEnemies--;
if (CurrentNumOfEnemies < 1)
{
// You killed everyone, change scene:
LevelManager.LoadLevel("Your Level");
}
}
}
Enemy script (I don't know how your current code looks, but here's a minimal solution based on how I THINK your code looks):
void OnDestroy()
{
// This will run automatically when you run Destroy() on this gameObject
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
This will only work if you have exactly only ONE spawner. If you have multiple ones you will have to send a reference to the instance of its spawner to every spawned enemy. I can show you how to do ths too, if you wish.
Bonus content
LevelManager doesn't need to be on a GameObject, it can be static instead:
Remove the LevelManager script from any GameObject
Change your LevelManager code to this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class LevelManager
{
public static void LoadLevel(string name)
{
Debug.Log("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Now you can use it from ANYWHERE, without needing to initialize a reference to any script or GameObject:
LevelManager.LoadLevel("My Level");
myLevelManager.LoadLevel("NextLevelMenu"); is never executed, because you destroy the object in the if-test above.

Categories

Resources