Shooting extra balls - Block breaker game bug (Unity 2D) - c#

I'm making a block breaker game that the player can get extra balls and shoot those balls with the original one.
Does anyone knows what is wrong? If there is not enough information, just tell me and I can provide it here.
Thanks
=========== CHANGED THE CODE TO BE MORE CLEAN ===========
Created my own pool logic to get more control and created a ExtraBallManager.cs to handle only functions related to that.
Here is how it is now (also I figured out that I didn't have to use coroutine):
public class ExtraBallManager : MonoBehaviour
{
private Ball ballController;
private GameManager gameManager;
public float ballWaitTime;
private float ballWaitTimeSeconds;
public int numberOfExtraBalls;
public int numberOfBallsToFire;
public ObjectPool objectPool;
//public TextAlignment numberOfBallsText;
// Start is called before the first frame update
void Start()
{
ballController = FindObjectOfType<Ball>();
gameManager = FindObjectOfType<GameManager>();
ballWaitTimeSeconds = ballWaitTime;
numberOfExtraBalls = 0;
numberOfBallsToFire = 0;
}
void Update() {
if(ballController.currentBallState == Ball.ballState.fire || ballController.currentBallState == Ball.ballState.wait){
if(numberOfBallsToFire > 0){
ballWaitTimeSeconds -= Time.deltaTime;
if(ballWaitTimeSeconds <= 0){
GameObject tempBall = objectPool.GetPooledObject("Temp Ball");
if(tempBall != null){
tempBall.transform.position = ballController.ballLaunchPosition;
tempBall.SetActive(true);
//It seems the error occurs in here
//sometimes it just don't add this tempBall to the
//list
gameManager.ballsInScene.Add(tempBall);
tempBall.GetComponent<Rigidbody2D>().velocity = ballController.initialBallVelocity;
ballWaitTimeSeconds = ballWaitTime;
numberOfBallsToFire--;
}
ballWaitTimeSeconds = ballWaitTime;
}
}
}
if(ballController.currentBallState == Ball.ballState.endShot){
numberOfBallsToFire = numberOfExtraBalls;
}
}
}
My Ball.cs part where switch the Ball State:
void Start()
{
arrow.SetActive(false);
currentBallState = ballState.aim;
rigidBody = GetComponent<Rigidbody2D>();
ballTransform = GetComponent<Transform>();
lineRenderer = arrow.GetComponent<LineRenderer>();
gameManager = FindObjectOfType<GameManager>();
stoppedPosition = transform.position;
currentBallState = Ball.ballState.aim;
gameManager.ballsInScene.Add(this.gameObject);
}
// Update is called once per frame
void Update()
{
switch(currentBallState) {
case ballState.aim:
if (Input.GetMouseButtonDown(0) && gameObject.tag == "Ball"){
MouseClicked();
}
if(Input.GetMouseButton(0) && gameObject.tag == "Ball"){
MouseDragged();
}
if(Input.GetMouseButtonUp(0) && gameObject.tag == "Ball"){
ReleaseMouse();
}
break;
case ballState.fire:
break;
case ballState.wait:
stoppedPosition = transform.position;
/*
if(gameManager.IsThereAnyExtraBallOnScene()){
currentBallState = ballState.endShot;
}*/
if(gameManager.ballsInScene.Count == 1){
currentBallState = Ball.ballState.endShot;
}
break;
case ballState.endShot:
for(int i = 0; i < gameManager.blocksInScene.Count; i++){
if(gameManager.blocksInScene[i]){
gameManager.blocksInScene[i].GetComponent<BlockMovementController>().currentState = BlockMovementController.blockState.move;
}
}
gameManager.level += 1;
gameManager.SpawnBlocks();
currentBallState = ballState.aim;
break;
default:
break;
}
}

Related

Instantiate Spawning Hundreds of items instead of 1

I have written a script that detects when something collides with my game object. When something collides with it, the collision may last 1 - 2 seconds but I only want one game object to spawn. When I run the scene and collide with it hundreds of game objects spawn instantly and everything crashes.
I have tried using Thread.Sleep() and IEnumerator waitor() but no luck so far.
Any ideas would be appreciated, I will attach the code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
public class ColliderHit : MonoBehaviour
{
public GameObject topLevelMenu;
public GameObject sublevel;
public GameObject topMenuItem;
public GameObject menuItemTouched;
public GameObject itemToSpawn;
public bool topLevelItem;
void OnTriggerEnter(Collider col)
{
if (col.gameObject.name != "" || col.gameObject.name == "RightHandVisual" || col.gameObject.name == "OculusHand_R" || col.gameObject.name == "HandGrabInteractor" || col.gameObject.name == "OVRHandPrefab")
{
if (topLevelItem)
{
topLevelMenu.SetActive(false);
sublevel.SetActive(true);
sublevel.transform.position = topMenuItem.transform.position;
sublevel.transform.rotation = topMenuItem.transform.rotation;
}
else
{
StartCoroutine(waiter());
var itemsPos = menuItemTouched.transform.position;
var itemsRot = menuItemTouched.transform.rotation;
var itemsSca = menuItemTouched.transform.localScale;
GameObject spawned = Instantiate(itemToSpawn);
spawned.transform.rotation = itemsRot;
spawned.transform.localScale = itemsSca;
var zpos = itemsPos.z - (0.1f);
spawned.transform.position = new Vector3(itemsPos.x, itemsPos.y, zpos);
}
}
}
IEnumerator waiter()
{
//Wait for 4 seconds
yield return new WaitForSeconds(4);;
}
}
Solution to fix your code as is
That's not how Co-routines work. The code that needs to be await needs to be within the coroutine:
bool isOnCollideCooldown = false;
void OnTriggerEnter(Collider col)
{
if (col.gameObject.name != "" || col.gameObject.name == "RightHandVisual" || col.gameObject.name == "OculusHand_R" || col.gameObject.name == "HandGrabInteractor" || col.gameObject.name == "OVRHandPrefab")
{
if (topLevelItem)
{
topLevelMenu.SetActive(false);
sublevel.SetActive(true);
sublevel.transform.position = topMenuItem.transform.position;
sublevel.transform.rotation = topMenuItem.transform.rotation;
}
else
{
// Return if it is instantiated
if (isOnCollideCooldown) return;
// Otherwise spawn and wait
StartCoroutine(waiter());
}
}
}
IEnumerator waiter()
{
// Go onto cooldown
isOnCollideCooldown = true;
// Spawn the object
var itemsPos = menuItemTouched.transform.position;
var itemsRot = menuItemTouched.transform.rotation;
var itemsSca = menuItemTouched.transform.localScale;
GameObject spawned = Instantiate(itemToSpawn);
spawned.transform.rotation = itemsRot;
spawned.transform.localScale = itemsSca;
var zpos = itemsPos.z - (0.1f);
spawned.transform.position = new Vector3(itemsPos.x, itemsPos.y, zpos);
//Wait for 4 seconds
yield return new WaitForSeconds(4);
// Go off cooldown
isOnCollideCooldown = false;
}
Additional Solution
The issue is that when you spawn the object, it instantly collides with the other objects. So on the script with the object that spawns you can use the following:
Collider col;
public float noCollisionTime = 1;
void Awake() {
col.enabled = false;
StartCoroutine(EnableCollider());
}
IEnumerator EnableCollider()
{
yield return new WaitForSeconds(noCollisionTime);
col.enabled = true;
}
You could try declaring a
bool isInstantiated = false;
and a method like this:
void ResetInstantiation()
{
isInstantiated = false;
}
then, check if it's already been instantiated:
else
{
StartCoroutine(waiter());
var itemsPos = menuItemTouched.transform.position;
var itemsRot = menuItemTouched.transform.rotation;
var itemsSca = menuItemTouched.transform.localScale;
if (isInstantiated) return;
GameObject spawned = Instantiate(itemToSpawn);
spawned.transform.rotation = itemsRot;
spawned.transform.localScale = itemsSca;
var zpos = itemsPos.z - (0.1f);
spawned.transform.position = new Vector3(itemsPos.x, itemsPos.y, zpos);
isInstatiated = true;
Invoke("ResetInstantiation", 4.0f);
}
This way, you set the value to true with stops the instantiation and reset the flag after 4 seconds. (or however many you wish)

Enemy AI not working properly with NavMesh

I been trying to create an AI for the enemy to patrol between some points and, when spot player in a certain range, the enemy stop the patrol and follows the player.
The thing is, if I only have the enemy to follow the player, works perfectly the same happens with just the patrol, but both together doesn't seem to work. The enemy acts in a weird way.
My code:
public void Start()
{
_navMeshAgent = this.GetComponent<NavMeshAgent>();
anim = GetComponent<Animator>();
if (_navMeshAgent == null)
{
Debug.LogError("not attached to " + gameObject.name);
}
else
{
if (_patrolPoints != null && _patrolPoints.Count >= 2)
{
_currentPatrolIndex = 0;
anim.SetBool("idle", true);
SetDestination();
}
else
{
Debug.Log("Insufficient patrol points.");
}
}
}
void Update()
{
if (player == null)
{
player = GameObject.Find("Player Character").GetComponent<Transform>();
}
if (Vector3.Distance(player.transform.position, this.transform.position) < 10)
{
chacePlayer();
}
else
{
patrol();
}
bool patrol()
{
Debug.Log("patrolling");
//Check if we're close to the destination.
if (_travelling && _navMeshAgent.remainingDistance <= 1.0f)
{
_travelling = false;
anim.SetBool("idle", false);
anim.SetBool("move", true);
//If we're going to wait, then wait dumbass!
if (_patrolWaiting)
{
_waiting = true;
_waitTimer = 0f;
anim.SetBool("idle", true);
anim.SetBool("move", false);
}
else
{
ChangePatrolPoint();
SetDestination();
}
}
//Instead if we're waiting...
if (_waiting)
{
_waitTimer += Time.deltaTime;
if (_waitTimer >= _totalWaitingTime)
{
_waiting = false;
anim.SetBool("move", true);
anim.SetBool("idle", false);
ChangePatrolPoint();
SetDestination();
}
}
return true;
}
}
private void SetDestination()
{
if (_patrolPoints != null)
{
Vector3 targetVector = _patrolPoints[_currentPatrolIndex].transform.position;
_navMeshAgent.SetDestination(targetVector);
_travelling = true;
////anim.SetBool("idle", false);
////anim.SetBool("move", true);
}
}
//Selects a new patrol point in the available list, but
//also with a small probability allows for us to move forward or backwards.
private void ChangePatrolPoint()
{
//Unity generate random number between 0 and 1
if (UnityEngine.Random.Range(0f, 1f) <= _switchProbability)
{
//decides if go forward or backwards: whatever the value, make the oposite
_patrolForward = !_patrolForward;
}
if (_patrolForward)
{
//if the patrolpoint exceedes patrolpoints.count, go backs to zero
_currentPatrolIndex = (_currentPatrolIndex + 1) % _patrolPoints.Count;
}
else
{
if (--_currentPatrolIndex < 0)
{
_currentPatrolIndex = _patrolPoints.Count - 1;
}
}
}
void chacePlayer()
{
Vector3 direction = player.transform.position - this.transform.position;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
Quaternion.LookRotation(direction), 0.1f);
direction.y = 0;
if (direction.magnitude > 2)
{
this.transform.Translate(0, 0, 0.05f);
Debug.Log("chacing");
}
}
At first I thought it was because the code was running both together so I put a Debug to see if it was that, but the debug of the patrol stops every time the enemy is following the player.
Can someone please help me?

How can I respawn pickups in an array and through a coroutine?

I'm having difficulty getting my gold pickups to respawn after they've been destroyed on death. The idea is, if the player fails to pick up the 5 gold bars, activates a checkpoint, and dies, the current gold is destroyed and it resets once the screen has faded from black.
I currently have a Coroutine in my Health Manager that runs correctly if the player dies and resets them. I have a Gold Pickup script that destroys the gold if they haven't been picked up. I just can't seem to get them to re-instantiate. I've tried adding the instantiate code within the Health Manager's coroutine and within the Gold Pickup script. Nothing seems to work. If I'm not getting errors saying 'Array index is out of range' it's 'object reference not set to an instance of an object' etc.
public class GoldPickup : MonoBehaviour{
public int value;
public GameObject pickupEffect;
public GameObject[] goldBarArray;
public HealthManager healthManager;
public Checkpoint checkpoint;
private Vector3 goldRespawnPoint;
private Quaternion goldStartPosition;
void Start()
{
//To destroy multiple objects at once, use FindGameObjectsWithTag.
//GetComponent is considered more efficient than FindObjectOfType, but the latter avoids any errors saying an object reference hasn't been set.
goldBarArray = GameObject.FindGameObjectsWithTag("Gold");
healthManager = FindObjectOfType<HealthManager>();
//FindObjectOfType<Checkpoint>();
checkpoint = FindObjectOfType<Checkpoint>();
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
}
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
FindObjectOfType<GameManager>().AddGold(value);
Instantiate(pickupEffect, transform.position, transform.rotation);
Destroy(gameObject);
}
}
public void DestroyGold()
{
//For Statics, an object reference isn't necessary. Use the FindObjectOfType to find the appropriate script and reference the Type, such as HealthManager.
if (checkpoint.checkpoint1On == false)
{
foreach (GameObject Gold in goldBarArray)
{
Destroy(Gold);
Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
//healthManager.RespawnCo();
}
}
}
/*public void GoldReset()
{
if (healthManager.isRespawning == true)
{
if (checkpoint.checkpoint1On == false)
{
StartCoroutine("GoldRespawnCo");
}
}
else if (_respawnCoroutine != null)
{
StopCoroutine(_respawnCoroutine);
_respawnCoroutine = StartCoroutine("GoldRespawnCo");
}*/
/*public IEnumerator GoldRespawnCo()
{
if (checkpoint.checkpoint1On == false)
{
Instantiate(goldPrefab, goldRespawnPoint, goldStartPosition);
transform.position = goldRespawnPoint;
transform.rotation = goldStartPosition;
}
else
{
yield return null;
}
}*/
/*if (thePlayer.gameObject.activeInHierarchy == false)
{
Destroy(gameObject);
Instantiate(goldBar, transform.position, transform.rotation);
}
else
{
if (thePlayer.gameObject.activeInHierarchy == true)
{
transform.position = respawnPoint;
transform.rotation = startPosition;
}
}*/
}
public class HealthManager : MonoBehaviour
//The counters will count down and will keep counting down based on the length variables
public int maxHealth;
public int currentHealth;
public PlayerController thePlayer;
//public GoldPickup goldPickup;
//public GoldPickup[] goldPickup;
public float invincibilityLength;
public Renderer playerRenderer;
public float flashLength;
public float respawnLength;
public GameObject deathEffect;
public Image blackScreen;
public float fadeSpeed;
public float waitForFade;
public bool isRespawning;
//public GameObject goldBar;
//To reference another script's function, such as in the DeathTrigger script, make a public DeathTrigger, give it a reference name, and put it into the Start function. Use the reference name and assign it using GetComponent. Call another script's method by using the reference name, followed by a dot and the name of the method. Eg: deathTrigger.DestroyGold().
private Quaternion startPosition;
//private Quaternion goldPosition;
private float flashCounter;
private float invincibilityCounter;
private Vector3 respawnPoint;
//private Vector3 goldRespawnPoint;
private bool isFadetoBlack;
private bool isFadefromBlack;
//private Coroutine _respawnCoroutine;
//private Vector3 goldRespawnPoint;
//private Quaternion goldStartPosition;
void Start()
{
currentHealth = maxHealth;
respawnPoint = thePlayer.transform.position;
startPosition = thePlayer.transform.rotation;
//goldPickup = GetComponent<GoldPickup>();
//goldRespawnPoint = goldBar.transform.position;
//goldStartPosition = goldBar.transform.rotation;
//goldRespawnPoint = transform.position;
//goldStartPosition = transform.rotation;
//goldPickup = FindObjectOfType<GoldPickup>();
//goldRespawnPoint = goldBar.transform.position;
//goldPosition = goldBar.transform.rotation;
}
void Update()
{
//These functions are checked every frame until the player takes damage
if (invincibilityCounter > 0)
{
invincibilityCounter -= Time.deltaTime;
flashCounter -= Time.deltaTime;
if (flashCounter <= 0)
//The Flash Counter is currently set at 0.1 and will be within the 0 region as it counts down. During this period, the playerRenderer will alternate between on and off
{
playerRenderer.enabled = !playerRenderer.enabled;
//The Flash Counter will keep counting down and reloop depending on the Flash Length time
flashCounter = flashLength;
}
//This makes sure after the flashing and invincibility has worn off that the player renderer is always turned back on so you can see the player
if (invincibilityCounter <= 0)
{
playerRenderer.enabled = true;
}
}
if (isFadetoBlack)
{
blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 1f, fadeSpeed * Time.deltaTime));
if (blackScreen.color.a == 1f)
{
isFadetoBlack = false;
}
}
if (isFadefromBlack)
{
blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 0f, fadeSpeed * Time.deltaTime));
if (blackScreen.color.a == 0f)
{
isFadefromBlack = false;
}
}
}
public void HurtPlayer(int damage, Vector3 direction)
{
//If the invincibility countdown reaches zero it stops, making you no longer invincible and prone to taking damage again
if (invincibilityCounter <= 0)
{
currentHealth -= damage;
if (currentHealth <= 0)
{
Respawn();
}
else
{
thePlayer.Knockback(direction);
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
}
}
}
public void Respawn()
{
//A StartCoroutine must be set up before the IEnumerator can begin
if (!isRespawning)
{
StartCoroutine("RespawnCo");
}
}
//IEnumerators or Coroutines will execute the code separately at specified times while the rest of the code in a codeblock will carry on executing as normal.
//To prevent an error appearing below the name of the Coroutine, be sure to place a yield return somewhere within the code block. Either yield return null or a new WaitForSeconds.
public IEnumerator RespawnCo()
{
if (GameManager.currentGold < 5)
{
isRespawning = true;
thePlayer.gameObject.SetActive(false);
Instantiate(deathEffect, respawnPoint, startPosition);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
//To reference another script's function quickly and just the once, use the FindObjectOfType function. This is considered to be slow however.
FindObjectOfType<GoldPickup>().DestroyGold();
//GetComponent<GoldPickup>().DestroyGold();
//Instantiate(goldBar, goldRespawnPoint, Quaternion.identity);
isFadefromBlack = true;
//goldRespawnPoint = goldBar.transform.position;
//goldStartPosition = goldBar.transform.rotation;
isRespawning = false;
thePlayer.gameObject.SetActive(true);
thePlayer.transform.position = respawnPoint;
thePlayer.transform.rotation = startPosition;
currentHealth = maxHealth;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
GameManager.currentGold = 0;
GetComponent<GameManager>().SetCountText();
StopCoroutine("RespawnCo");
/*isRespawning = true;
thePlayer.gameObject.SetActive(false);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
isFadefromBlack = true;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
SceneManager.LoadScene("Level 1");
GameManager.currentGold = 0;*/
}
else if(GameManager.currentGold >= 5)
{
isRespawning = true;
thePlayer.gameObject.SetActive(false);
Instantiate(deathEffect, respawnPoint, startPosition);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
isFadefromBlack = true;
isRespawning = false;
thePlayer.gameObject.SetActive(true);
thePlayer.transform.position = respawnPoint;
thePlayer.transform.rotation = startPosition;
currentHealth = maxHealth;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
}
}
/*public void HealPlayer(int healAmount)
{
currentHealth += healAmount;
if(currentHealth > maxHealth)
{
currentHealth = maxHealth;
}
}*/
public void SetSpawnPoint(Vector3 newPosition)
{
respawnPoint = newPosition;
}
public class Checkpoint : MonoBehaviour
public HealthManager theHealthManager;
public Renderer cpRenderer;
public Renderer postRenderer;
public SpriteRenderer pcRenderer;
public Material cpOff;
public Material cpOn;
public Material postOff;
public Material postOn;
public GameObject[] infoPanels;
public bool checkpoint1On;
//Make sure to assign a value to a bool with '=' and in an 'if' statement somewhere in the code to prevent warnings.
//private bool checkpoint1IsActivated;
private bool infoPanel1Activated;
void Start()
{
theHealthManager = FindObjectOfType<HealthManager>();
}
void Update()
//Key presses are better handled in the Update function and will recognise keys being pressed once every frame.
{
if (checkpoint1On == true)
{
if (infoPanel1Activated == false)
{
if (Input.GetKeyDown(KeyCode.Space))
{
infoPanels[0].SetActive(true);
infoPanel1Activated = true;
}
}
else
{
if (infoPanel1Activated == true)
{
if (Input.GetKeyDown(KeyCode.Space))
{
infoPanels[0].SetActive(false);
infoPanel1Activated = false;
}
}
}
}
}
public void Checkpoint1On()
{
cpRenderer.material = cpOn;
postRenderer.material = postOn;
pcRenderer.color = new Color(1f, 1f, 1f, 1f);
checkpoint1On = true;
}
//[] makes a variable an Array (a list). The 'foreach' loop will check through all the Checkpoint objects
//Checkpoint[] checkpoints = FindObjectsOfType<Checkpoint>();
//For each Checkpoint Array called 'checkpoints', look for 'cp' and turn the others in the list off
/*foreach (Checkpoint cp in checkpoints)
{
cp.CheckpointOff();
}
theRenderer.material = cpOn;*/
public void Checkpoint1Off()
{
cpRenderer.material = cpOff;
postRenderer.material = postOff;
pcRenderer.color = new Color(1f, 1f, 1f, 5f);
checkpoint1On = false;
}
public void OnTriggerStay(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (GameManager.currentGold >= 5)
{
if (Input.GetKeyDown(KeyCode.Return))
{
theHealthManager.SetSpawnPoint(transform.position);
Checkpoint1On();
checkpoint1On = true;
}
}
else if (GameManager.currentGold <= 5)
{
checkpoint1On = false;
}
}
}
In your DestroyGold() function, you instantiate the gold like this:
foreach (GameObject Gold in goldBarArray)
{
Destroy(Gold);
Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
//healthManager.RespawnCo();
}
But transform.position and transform.rotation only get the position and rotation of the current object (i.e. whatever your script is attached to). So not only are you spawning all the gold in the same spot, it's spawning the gold at the location of the object that holds your script, not where you actually want it to go!
Without knowing much about the objects in your scene, here's what I can tell you: try creating a Transform[] to store the locations where you want to respawn the gold. Also, make sure you assign the goldRespawnPoint and goldStartPosition BEFORE you call Instantiate() in your foreach loop. Finally, just a general tip: you should never use variable == true or variable == false in an if statement. You can just use if(variable) or if(!variable), respectively. It will work just the same while being more readable and reducing the amount of code you need to write.
EDIT 1: In response to comments, I've added specific code examples for implementing these suggestions.
To start, you're probably getting the out of range error because of goldBarArray[5]. Since arrays start at index 0, you can only access up to element n-1 in a size n array. More on how to fix this in the next step.
Now for the Transform array. In the area where you declare your public variables (at the top of the script), add the line
public Transform[] spawnPoints;
Then, back in Unity you will be able to assign those spawn points in the Inspector.
EDIT 2: Additionally, in the foreach loop you're trying to instantiate one of the gold bars from the scene, but those are getting deleted with the Destroy(Gold); statement. Instead, you should be instantiating from the prefab which won't get destroyed. To do this, add
public GameObject goldPrefab;
up with the rest of your public variables. Then, in the Editor create a prefab by dragging one of the gold bars from the Hierarchy into your Assets folder. Finally, set that prefab to be the value of goldPrefab in the Inspector.
Now, you actually can clean up your foreach loop a little bit. You can get rid of the goldRespawnPoint and goldStartPosition lines because the respawn locations will be contained in the Transform array we just created. Again, without knowing how your scene is structured I've needed to just make an educated guess about what will work. Give this loop a try:
int spawnPointCounter = 0;
foreach(GameObject Gold in goldBarArray){
Destroy(Gold);
Transform currentSP = spawnPoints[spawnPointCounter];
Instantiate(goldPrefab, currentSP.position, currentSP.rotation);
spawnPointCounter++;
}

Unity OnTriggerEnter

I want my Player to collide with the object capsule.
This action should destroy the capsule and add a speed value of 10 to the player.
But this code is not working :
public class PlayerController : MonoBehaviour {
public KeyCode moveL;
public KeyCode moveR;
public float horizontal = 0;
public int laneNum = 2;
public string controllocked = "n";
public float speed;
void Update ()
{
GetComponent<Rigidbody>().velocity = new Vector3(horizontal, GM.verticalVelocity, speed);
if ((Input.GetKeyDown(moveL)) && (laneNum > 1) && (controllocked == "n"))
{
horizontal = -2;
StartCoroutine(StopSlide());
laneNum = laneNum - 1;
controllocked = "y";
}
else if ((Input.GetKeyDown(moveR)) && (laneNum < 3) && (controllocked =="n"))
{
horizontal = 2;
laneNum = laneNum + 1;
StartCoroutine(StopSlide());
controllocked = "y";
}
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.tag == "lethal")
{
Destroy(gameObject);
}
if (other.gameObject.name == "Capsule")
{
Destroy(other.gameObject);
speed = 10;
}
}
IEnumerator StopSlide()
{
yield return new WaitForSeconds(.5f);
horizontal = 0;
controllocked = "n";
}
What I've tried so far is speed += 10 and speed++ neither works.
Well, at first try to check your player, What collider type that you use in the player?
Make sure you check trigger in the collider component and add rigidbody into it.
The capsule object must have rigidbody on it.
Hope it help.
Use OnTriggerEnter(Collider collider) if you want to use triggerenter. Collision enter work if trigger is not checked

Unity AI attack Simple FSM Stackoverflow error

i have my zombies wandering, chasing me on LOS & FOV but when they reach me ( Camera Rig Vive VR ), only one attack me (not eveytime but he try). I would like to make them all to attack me once they are close to me ( the best would be they make a circle around me ).
If i try to change distance or use a trigger collider to pass a boolean to true instead of calulate the remaining distance (in the attack coroutine), i have stack overflow error. I dont understand why. If Someone can explane me, it would be very nice.
Here is code for AI_Enemy :
using UnityEngine;
using System.Collections;
using UltimateSpawner;
using System.Collections.Generic;
public class AI_Enemy : MonoBehaviour
{
public enum ENEMY_STATE {PATROL, CHASE, ATTACK, DEAD};
public ENEMY_STATE CurrentState
{
get{return currentstate;}
set
{
//Update current state
currentstate = value;
//Stop all running coroutines
StopAllCoroutines();
switch(currentstate)
{
case ENEMY_STATE.PATROL:
StartCoroutine(AIPatrol());
break;
case ENEMY_STATE.CHASE:
StartCoroutine(AIChase());
break;
case ENEMY_STATE.ATTACK:
StartCoroutine(AIAttack());
break;
case ENEMY_STATE.DEAD:
break;
}
}
}
[SerializeField]
private ENEMY_STATE currentstate = ENEMY_STATE.PATROL;
[SerializeField] Animator ThisAnimator;
[SerializeField] AudioSource ThisAudioSource;
//Reference to patrol destination
[SerializeField] GameObject[] PatrolDestinations;
private AudioClip sound;
public AudioClip[] attacksSounds;
//Reference to line of sight component
private LineSight ThisLineSight = null;
//Reference to nav mesh agent
private UnityEngine.AI.NavMeshAgent ThisAgent;
//Reference to transform
private Transform ThisTransform = null;
//Reference to player health
public PlayerHealth PlayerHealth = null;
//Reference to player transform
private Transform PlayerTransform = null;
public Transform PatrolDestination;
[SerializeField] float timeBetweenAttacks = 1.4f;
private WaitForSeconds attackDelay;
//Damage amount per second
public float MaxDamage = 2.8f;
public static bool inRange = false;
void Awake()
{
ThisLineSight = GetComponent<LineSight>();
ThisAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
ThisTransform = GetComponent<Transform>();
ThisAnimator = GetComponent<Animator>();
ThisAudioSource = GetComponent<AudioSource>();
attackDelay = new WaitForSeconds(timeBetweenAttacks);
}
void Start()
{
//Configure starting state
ThisAgent.enabled = true;
PlayerHealth = GameManager.Instance.Player;
PlayerTransform = GameManager.Instance.EnemyTarget;
PatrolDestinations = GameObject.FindGameObjectsWithTag("Waypoint");
StartCoroutine(StartZombie());
}
public IEnumerator AIPatrol()
{
ThisAnimator.SetBool("Attack", false);
ThisAnimator.SetBool("Chase", false);
ThisAnimator.SetBool("Walk", true);
PatrolDestination = PatrolDestinations[Random.Range(0, (PatrolDestinations.Length - 1))].transform;
//Loop while patrolling
while (currentstate == ENEMY_STATE.PATROL)
{
//Set strict search
ThisLineSight.Sensitity = LineSight.SightSensitivity.STRICT;
ThisAgent.speed = 1f;
//Chase to patrol position
//ThisAgent.Resume();
ThisAgent.isStopped = false;
ThisAgent.SetDestination(PatrolDestination.position);
//Wait until path is computed
while(ThisAgent.pathPending)
yield return null;
if (ThisAgent.remainingDistance < 1.5f)
PatrolDestination = PatrolDestinations[Random.Range(0, (PatrolDestinations.Length))].transform;
//If we can see the target then start chasing
if (ThisLineSight.CanSeeTarget)
{
//ThisAgent.Stop();
ThisAgent.isStopped = true;
transform.LookAt(GameManager.Instance.EnemyTarget);
CurrentState = ENEMY_STATE.CHASE;
yield break;
}
//Wait until next frame
yield return null;
}
}
public IEnumerator AIChase()
{
ThisAnimator.SetBool("Attack", false);
ThisAnimator.SetBool("Chase", true);
ThisAnimator.SetBool("Walk", false);
ThisAgent.speed = 1.7f;
//Loop while chasing
while (currentstate == ENEMY_STATE.CHASE)
{
//transform.LookAt(GameManager.Instance.EnemyTarget);
//Set loose search
ThisLineSight.Sensitity = LineSight.SightSensitivity.LOOSE;
//Chase to last known position
//ThisAgent.Resume();
ThisAgent.isStopped = false;
ThisAgent.SetDestination(ThisLineSight.LastKnowSighting);
//Wait until path is computed
while(ThisAgent.pathPending)
yield return null;
//Have we reached destination?
if(ThisAgent.remainingDistance <= ThisAgent.stoppingDistance +0.5f)
{
//Stop agent
ThisAgent.isStopped = true;
//ThisAgent.Stop();
//Reached destination but cannot see player
if(!ThisLineSight.CanSeeTarget)
CurrentState = ENEMY_STATE.PATROL;
else //Reached destination and can see player. Reached attacking distance
CurrentState = ENEMY_STATE.ATTACK;
yield break;
}
//Wait until next frame
yield return null;
}
}
public IEnumerator AIAttack()
{
ThisAnimator.SetBool("Attack", true);
ThisAnimator.SetBool("Chase", false);
ThisAnimator.SetBool("Walk", false);
//Loop while chasing and attacking
while (currentstate == ENEMY_STATE.ATTACK)
{
//Chase to player position
ThisAgent.isStopped = false;
ThisAgent.SetDestination(GameManager.Instance.EnemyTarget.position);
//Wait until path is computed
while (ThisAgent.pathPending)
yield return null;
//Has player run away?
if(ThisAgent.remainingDistance > ThisAgent.stoppingDistance + 0.5f)
//if(!inRange)
{
//Change back to chase
CurrentState = ENEMY_STATE.CHASE;
yield break;
}
else
{
//Attack
GameManager.Instance.Player.TakeDamage(MaxDamage);
sound = attacksSounds[Random.Range(0, (attacksSounds.Length))];
ThisAudioSource.PlayOneShot(sound);
}
//Wait until next frame
yield return attackDelay;
}
yield break;
}
//Called when the enemy is defeated and can no longer move
public void Defeated()
{
Debug.Log("DEFEATED");
//Disable the navmesh agent
ThisAgent.enabled = false;
ThisAnimator.SetBool("Die", true);
SpawnableManager.informSpawnableDestroyed(gameObject, false);
CurrentState = ENEMY_STATE.DEAD;
EnemyManager.nbrZombies --;
EnemyManager.CountAllZombie();
}
public IEnumerator StartZombie()
{
yield return new WaitForSeconds(5);
CurrentState = ENEMY_STATE.PATROL;
}
}
Code for Line of Sight :
using UnityEngine;
using System.Collections;
//------------------------------------------
public class LineSight : MonoBehaviour
{
//------------------------------------------
//How sensitive should we be to sight
public enum SightSensitivity {STRICT, LOOSE};
//Sight sensitivity
public SightSensitivity Sensitity = SightSensitivity.STRICT;
//Can we see target
public bool CanSeeTarget = false;
public bool DebugFOV = false;
//FOV
public float FieldOfView = 120f;
//Reference to target
public Transform Target = null;
//Reference to eyes
public Transform EyePoint = null;
//Reference to transform component
private Transform ThisTransform = null;
//Reference to sphere collider
public SphereCollider ThisCollider = null;
//Reference to last know object sighting, if any
public Vector3 LastKnowSighting = Vector3.zero;
private Vector3 DirToTarget;
void Awake()
{
ThisTransform = GetComponent<Transform>();
ThisCollider = GetComponent<SphereCollider>();
LastKnowSighting = ThisTransform.position;
}
private void Start()
{
Target = GameManager.Instance.EnemyTarget;
}
//------------------------------------------
bool InFOV()
{
//Get direction to target
DirToTarget = Target.position - EyePoint.position;
//Get angle between forward and look direction
float Angle = Vector3.Angle(EyePoint.forward, DirToTarget);
//Are we within field of view?
if(Angle <= FieldOfView)
{
Debug.DrawRay(EyePoint.position, (Target.position - EyePoint.position), Color.cyan);
return true;
}
//Not within view
return false;
}
//------------------------------------------
bool ClearLineofSight()
{
RaycastHit Info;
if (Physics.Raycast(EyePoint.position, (Target.position - EyePoint.position), out Info, ThisCollider.radius *2))
{
//If player, then can see player
//if (Info.transform.CompareTag("MainCamera"))
if(Info.transform.gameObject.layer == LayerMask.NameToLayer("Gringan"))
return true;
}
return false;
}
//------------------------------------------
void UpdateSight()
{
switch(Sensitity)
{
case SightSensitivity.STRICT:
CanSeeTarget = InFOV() && ClearLineofSight();
break;
case SightSensitivity.LOOSE:
CanSeeTarget = InFOV() || ClearLineofSight();
break;
}
}
//------------------------------------------
void OnTriggerStay(Collider Other)
{
UpdateSight();
//Update last known sighting
if(CanSeeTarget)
LastKnowSighting = Target.position;
}
void OnDrawGizmos()
{
float totalFOV = 120.0f;
float rayRange = 3.9f;
float halfFOV = totalFOV / 2.0f;
Quaternion leftRayRotation = Quaternion.AngleAxis(-halfFOV, Vector3.up);
Quaternion rightRayRotation = Quaternion.AngleAxis(halfFOV, Vector3.up);
Vector3 leftRayDirection = leftRayRotation * transform.forward;
Vector3 rightRayDirection = rightRayRotation * transform.forward;
Gizmos.color = Color.red;
Gizmos.DrawRay(transform.position, leftRayDirection * rayRange);
Gizmos.DrawRay(transform.position, rightRayDirection * rayRange);
}
private void Update()
{
if(CanSeeTarget)
Debug.DrawRay(EyePoint.position, (Target.position - EyePoint.position), Color.yellow);
}
Thanks for helping me.
Firstly, you should be able to get the stack trace from your stack overflow, which would help a lot in tracking down these issues.
Although I don't know exactly what you're trying to do here, the most likely cause of a stack overflow from your code here is an infinite swapping between the attacking and chasing states.
If you follow the code through, currently chasing goes to attacking if range is less than or equal to a number, and attacking goes to chasing if range is more than that number.
Those conditions work currently, as they are mutually exclusive. If you were to change one of them to work off a trigger however (removing the mutual exclusivity), then you would have the potential to infinitely change state back and forth.

Categories

Resources