A few days ago I started learning unity, I implemented a very simple navMeshAgent so when I click on the map if the unit IsLeader == true (has de focus) de agent of the unit will move to de position, the problem is that when the unit stops moving it rotates 90 degrees
Here you have the unit class, LeaderMovement class and LeaderManager class.
Unit:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
public class Unit : MonoBehaviour
{
private NavMeshAgent agent;
private Animator animator;
public bool isLeader;
void Start()
{
agent = Agent;
isLeader = false;
animator = Animator;
}
// Update is called once per frame
void Update()
{
}
public bool IsLeader
{
get;
set;
}
public NavMeshAgent Agent
{
get;
set;
}
public Animator Animator
{
get;
set;
}
}
LeaderMovement
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class LeaderMovement : MonoBehaviour
{
RaycastHit hitInfo = new RaycastHit();
private Unit unit;
private Animator animator;
private void Awake()
{
unit = GetComponent<Unit>();
unit.Agent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(1))
{
ClickToMove();
}
if (Vector3.Distance(unit.Agent.destination, transform.position) <= unit.Agent.stoppingDistance )
{
animator.SetBool("isMoving", false);
}
}
public void ClickToMove()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hitInfo))
{
SetDestination(hitInfo.point);
}
}
private void SetDestination(Vector3 target)
{
if (unit.IsLeader==true) {
animator.SetBool("isMoving", true);
unit.Agent.SetDestination(target);
}
}
}
TeamManager
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TeamManager : MonoBehaviour
{
//control of the team
[SerializeField] private Unit leader;
[SerializeField] private Unit pastleader;
[SerializeField] private List<Unit> playerList;
int index;
// Start is called before the first frame update
void Start()
{
index = 0;
leader= playerList[index];
leader.IsLeader = true;
}
public void changeLeader()
{
try
{
index += 1;
pastleader = leader;
pastleader.IsLeader = false;
leader = playerList[index];
leader.IsLeader = true;
}
catch (ArgumentOutOfRangeException)
{
pastleader = leader;
pastleader.IsLeader = false;
index = 0;
leader = playerList[index];
leader.IsLeader = true;
}
}
public Unit getLeader()
{
return leader;
}
}
Im thankful to any help i can get
Related
I hope you all are doing well. I have been following a Unity tutorial for a rhythm game and I have found this bug that I could not get past. Essentially, my OnTriggerExit2D is getting called too early. I'll include a picture in the conclusion of this post. I have tried logging the game object and it seems that all of my button objects suffer the same fate. I have included a link of the tutorial that I have been following in the conclusion. Any help towards figuring this out would be helpful.
Tutorial Link: https://www.youtube.com/watch?v=PMfhS-kEvc0&ab_channel=gamesplusjames
What my game looks like, the missed shows up when I've hit it.
Debug Output
GameManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public AudioSource theMusic;
public bool startPlaying;
public BeatScroller theBS;
public static GameManager instance;
public int currentScore;
public int scorePerNote = 100;
public int scorePerGoodNote = 125;
public int scorePerPerfectNote = 150;
public int currentMultiplier;
public int multiplierTracker;
public int [] multiplierTresholds;
public Text scoreText;
public Text multiText;
// Start is called before the first frame update
void Start()
{
instance = this;
scoreText.text = "Score: 0";
multiText.text = "Multiplier: x1";
currentMultiplier = 1;
}
// Update is called once per frame
void Update()
{
if(!startPlaying){
if(Input.anyKeyDown){
startPlaying = true;
theBS.hasStarted = true;
theMusic.Play();
}
}
}
public void NoteHit(){
Debug.Log("Note Hit On Time");
if(currentMultiplier-1 < multiplierTresholds.Length){
multiplierTracker++;
if(multiplierTresholds[currentMultiplier-1] <= multiplierTracker){
multiplierTracker = 0;
currentMultiplier++;
}
}
multiText.text = "Multiplier: x"+currentMultiplier;
//currentScore += scorePerNote * currentMultiplier;
scoreText.text = "Score: "+currentScore;
}
public void NormalHit(){
currentScore += scorePerNote * currentMultiplier;
NoteHit();
}
public void GoodHit(){
currentScore += scorePerGoodNote * currentMultiplier;
NoteHit();
}
public void PerfectHit(){
currentScore += scorePerPerfectNote * currentMultiplier;
NoteHit();
}
public void NoteMissed(){
Debug.Log("MISSED!");
multiText.text = "Multiplier: x1";
}
}
BeatScroller
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeatScroller : MonoBehaviour
{
public float beatTempo;
public bool hasStarted;
// Start is called before the first frame update
void Start()
{
beatTempo = beatTempo / 60f;
}
// Update is called once per frame
void Update()
{
if(!hasStarted){
}else{
transform.position -= new Vector3(0f, beatTempo*Time.deltaTime, 0f);
}
}
}
ButtonController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ButtonController : MonoBehaviour
{
// Start is called before the first frame update
private SpriteRenderer theSR;
public Sprite defaultImage;
public Sprite pressedImage;
public KeyCode keyToPress;
void Start()
{
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(keyToPress))
{
theSR.sprite = pressedImage;
}
if(Input.GetKeyUp(keyToPress))
{
theSR.sprite = defaultImage;
}
}
}
noteObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class noteObject : MonoBehaviour
{
public bool canBePressed;
public KeyCode KeyToPress;
public GameObject hitEffect, goodEffect, perfectEffect, missedEffect;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyToPress))
{
if(canBePressed)
{
gameObject.SetActive(false);
if(Mathf.Abs(transform.position.y) > 0.25){
GameManager.instance.NormalHit();
Debug.Log("Normal Hit!");
Instantiate(hitEffect,transform.position, hitEffect.transform.rotation);
}else if(Mathf.Abs(transform.position.y) > 0.05f){
GameManager.instance.GoodHit();
Debug.Log("Good Hit!!");
Instantiate(goodEffect,transform.position, goodEffect.transform.rotation);
}else{
GameManager.instance.PerfectHit();
Debug.Log("PERFECT HIT!!!");
Instantiate(perfectEffect,transform.position, perfectEffect.transform.rotation);
}
}
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Activator")
{
canBePressed = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if(other.tag == "Activator")
{
Debug.Log("Exited collider on game object: "+ other.gameObject.name);
canBePressed = false;
GameManager.instance.NoteMissed();
Instantiate(missedEffect,transform.position, missedEffect.transform.rotation);
}
}
}
EffectObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EffectObject : MonoBehaviour
{
public float lifeTime = 1f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Destroy(gameObject, lifeTime);
}
}
Hmm ... You say that OnTriggerExit2D is called to early? I assume it's called when the elements are still inside one another? If that's the case I guess your bounding boxes don't have the right size, or the right shape. I see arrows, do they have a rectangular bounding box or a polygon one that follows their shape? Are all your bounding boxes the right size?
I figured out what was wrong thanks to AdrAs's comment.
Turns out I had to check the y position of my arrow and collider were well enough below the height of my button box collider. In addition to that, I reshaped my colliders. I found that this code did the trick for me. Thank You All For the Nudges in the right direction.
noteObject -> new OnTriggerExit2D
private void OnTriggerExit2D(Collider2D other)
{
if(other.tag == "Activator" && transform.position.y < -0.32)
{
Debug.Log("Exited collider on game object: "+ other.gameObject.name);
canBePressed = false;
GameManager.instance.NoteMissed();
Instantiate(missedEffect,transform.position, missedEffect.transform.rotation);
}
}
I've been trying to make my enemy fall when it's health reaches zero.
So I made a damage variable. The damage will subtract to the health and when it reaches zero or lower, he's supposed to turn into a ragdoll. But its seems as he's not dying in the first place..
In the gun script:
using UnityEngine;
public class gun : MonoBehaviour
{
public float damage = 10f;
public float range = 100f;
public Camera Cam;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Fire();
}
void Fire()
{
RaycastHit hit;
if(Physics.Raycast(Cam.transform.position, Cam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Enemy enemy = hit.transform.GetComponent<Enemy>();
if (enemy != null)
{
enemy.damage(damage);
}
}
}
}
}
Now in the enemy script:
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class Enemy : MonoBehaviour
{
Animator myAnim;
List<Rigidbody> ragdollRigids;
public float health = 100f;
public void damage (float amount)
{
health -= amount;
if(health <= 0)
{
Death();
}
}
void Death()
{
activateRagdoll();
}
//Update
void Start()
{
myAnim = GetComponent<Animator>();
ragdollRigids = new List<Rigidbody>(transform.GetComponentsInChildren<Rigidbody>());
ragdollRigids.Remove(GetComponent<Rigidbody>());
DeactivateRagdoll();
}
void Update()
{
}
//Ragdoll dependencies.
public void activateRagdoll()
{
myAnim.enabled = false;
for (int i = 0; i < ragdollRigids.Count; i++)
{
ragdollRigids[i].useGravity = true;
ragdollRigids[i].isKinematic = false;
}
}
public void DeactivateRagdoll()
{
myAnim.enabled = true;
for (int i = 0; i < ragdollRigids.Count; i++)
{
ragdollRigids[i].useGravity = false;
ragdollRigids[i].isKinematic = true;
}
}
}
Thank you in advance. :)
Solution
Your variables (damage and amount) are named differently but are used for the same purpose. I have edited your code. There should be no errors. If there are, contact me my commenting on my answer.
Code
Gun:
using UnityEngine;
public class gun : MonoBehaviour
{
public float damage = 10f;
public float range = 100f;
public Camera Cam;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Fire();
}
void Fire()
{
RaycastHit hit;
if(Physics.Raycast(Cam.transform.position, Cam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Enemy enemy = hit.transform.GetComponent<Enemy>();
if (enemy != null)
{
enemy.damage(damage);
}
}
}
}
}
Enemy:
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class Enemy : MonoBehaviour
{
Animator myAnim;
List<Rigidbody> ragdollRigids;
public float health = 100f;
public void damage (float damage)
{
health -= damage;
if(health <= 0)
{
Death();
}
}
void Death()
{
activateRagdoll();
}
//Update
void Start()
{
myAnim = GetComponent<Animator>();
ragdollRigids = new List<Rigidbody>(transform.GetComponentsInChildren<Rigidbody>());
ragdollRigids.Remove(GetComponent<Rigidbody>());
DeactivateRagdoll();
}
void Update()
{
}
//Ragdoll dependencies.
public void activateRagdoll()
{
myAnim.enabled = false;
for (int i = 0; i < ragdollRigids.Count; i++)
{
ragdollRigids[i].useGravity = true;
ragdollRigids[i].isKinematic = false;
}
}
public void DeactivateRagdoll()
{
myAnim.enabled = true;
for (int i = 0; i < ragdollRigids.Count; i++)
{
ragdollRigids[i].useGravity = false;
ragdollRigids[i].isKinematic = true;
}
}
}
My goal was to make my character pickup item on collider (2D) didn't work.
So here is what I've tried:
Player Controller Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 2f;
public Inventory inventory;
void Start()
{
}
public bool isGrounded;
public LayerMask groundLayers;
void Update()
{
// isgrounded?
isGrounded = Physics2D.OverlapArea(new Vector2(transform.position.x -
0.2f, transform.position.y - 0.2f),
new Vector2(transform.position.x + 0.2f, transform.position.y -
0.21f), groundLayers);
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if(Input.GetButtonDown("Jump") && isGrounded)
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f,
2.5f), ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(ControllerColliderHit hit)
{
IInventoryItem item = hit.collider.GetComponent<IInventoryItem>();
if (item != null)
{
inventory.AddItem(item);
}
}
}
HUD SCRIPT:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HUD : MonoBehaviour
{
public Inventory Inventory;
void Start ()
{
Inventory.ItemAdded += InventoryScript_ItemAdded;
}
private void InventoryScript_ItemAdded(object sender, InventoryEventArgs
e)
{
Transform inventoryPanel = transform.Find("InventoryPanel");
foreach(Transform slot in inventoryPanel)
{
// Border... Image
Image image = slot.GetChild(0).GetChild(0).GetComponent<Image>();
// We found empty slot!
if (!image.enabled)
{
image.enabled = true;
image.sprite = e.Item.Image;
// Todo store a reference;
break;
}
}
}
}
Inventory Script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
private const int SLOTS = 7;
private List<IInventoryItem> mItems = new List<IInventoryItem>();
public event EventHandler<InventoryEventArgs> ItemAdded;
public void AddItem(IInventoryItem item)
{
if(mItems.Count < SLOTS)
{
Collider collider = (item as MonoBehaviour).GetComponent<Collider>
();
if (collider.enabled)
{
collider.enabled = false;
mItems.Add(item);
item.OnPickup();
if (ItemAdded != null)
{
ItemAdded(this, new InventoryEventArgs(item));
}
}
}
}
}
Inventory Item Script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IInventoryItem
{
string Name { get; }
Sprite Image { get; }
void OnPickup();
}
public class InventoryEventArgs : EventArgs
{
public InventoryEventArgs(IInventoryItem item)
{
Item = item;
}
public IInventoryItem Item;
}
Rock Script (The object):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rock : MonoBehaviour, IInventoryItem
{
public string Name
{
get
{
return "Rock";
}
}
public Sprite _Image = null;
public Sprite Image
{
get
{
return _Image;
}
}
public void OnPickup()
{
// TODO: ADD LOGIC THAT WILL MAKE THE ROCK A 'WEAPON' TO CUT DOWN THE
TREE
gameObject.SetActive(false);
}
}
All of those scripts work, but whenever I join my game and Collide the player with the object (all 2d, 2D Box colliders, etc.) the character wont pick the item up and put it in it's inventory?
The scripts are referenced to each other.
What did I do wrong?
Physics 2D Screenshot:
Player inspector screenshot:
Rock (Object that needs to join his inventory)
One thing I noticed is that you are mixing the syntax of two events to create one that doesn't exist. void OnCollisionEnter2D(ControllerColliderHit hit) is not a built-in event in Unity. You probably mean to use void OnCollisionEnter2D(Collision2D hit):
private void OnCollisionEnter2D(Collision2D hit)
{
IInventoryItem item = hit.collider.GetComponent<IInventoryItem>();
if (item != null)
{
inventory.AddItem(item);
}
}
Another thing is that BoxCollider2D does not inherit from Collider. So, in AddItem, you should look for a Collider2D component instead:
public void AddItem(IInventoryItem item)
{
if(mItems.Count < SLOTS)
{
Collider2D collider = (item as MonoBehaviour).GetComponent<Collider2D>();
if (collider.enabled)
{
collider.enabled = false;
mItems.Add(item);
item.OnPickup();
if (ItemAdded != null)
{
ItemAdded(this, new InventoryEventArgs(item));
}
}
}
}
Consider this to be a partial solution because this may not capture all the changes needed... Let me know if this alone doesn't fix the problem in the comments below.
I have a particle system for when the enemy is destroyed. I have multiple enemies coming from the same path (using the same prefab) and the particle system is only working on the first enemy. Can someone tell me why it is not working on the others as well? Thank you.
EnemyShooting scrip (this is where the piece of code is for the explosion):
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;
public Transform explosion;
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
}
if (explosion)//EXPLOSION CODE
{
GameObject exploder = ((Transform)Instantiate(explosion, this.transform.position, this.transform.rotation)).gameObject;
Destroy(exploder, 2.0f);
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
}
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();
}
}
EnemySpawner : (I thought this script might help in a way so I attached it)
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 int EnemiesToNextLevel = 7;
public int KilledEnemies = 0;
public LevelManager myLevelManager;
public static EnemySpawner Instance = null;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while (CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
if (Instance == null)
Instance = this;
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
public void OnEnemyDeath()
{
CurrentNumOfEnemies--;
/*
if (CurrentNumOfEnemies < 5)
{
// You killed everyone, change scene:
LaserLevelManager.LoadLevel("NextLevelMenu");
}
*/
KilledEnemies++;
if (KilledEnemies >= EnemiesToNextLevel)
{
LaserLevelManager.LoadLevel("NextLevelMenu");
}
}
}
I'm currently developing within Unity 2018 and have made a script for decreasing a character's health on collision with an enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthManager : MonoBehaviour
{
public static int currentHealth;
public Slider healthBar;
void Awake()
{
healthBar = GetComponent<Slider> ();
currentHealth = 100;
}
void ReduceHealth()
{
currentHealth = currentHealth - 1;
healthBar.value = currentHealth;
}
void Update()
{
healthBar.value = currentHealth;
}
}
When I try to use said method in the scripting file for the enemy I get an error stating "Assets/Custom Scripts/BeetleScript.cs(46,28): error CS0122: `HealthManager.ReduceHealth()' is inaccessible due to its protection level"
The following is the enemy script initiating the variables being used and calling the method:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeetleScript : MonoBehaviour
{
Animator animator;
public GameObject cucumberToDestroy;
public bool cherryHit = false;
public float smoothTime = 3.0f;
public Vector3 smoothVelocity = Vector3.zero;
public PointsManager _ptsManager;
public HealthManager _healthManager;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
if (cherryHit)
{
var cm = GameObject.Find("CucumberMan");
var tf = cm.transform;
this.gameObject.transform.LookAt(tf);
// move towards Cucumber Man
animator.Play("Standing Run");
transform.position = Vector3.SmoothDamp(transform.position, tf.position,
ref smoothVelocity, smoothTime);
}
}
// Collision Detection Test
void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Player"))
{
_healthManager = GameObject.Find
("Health_Slider").GetComponent<HealthManager>();
_healthManager.ReduceHealth();
if (!cherryHit)
{
BeetlePatrol.isAttacking = true;
var cm = GameObject.Find("CucumberMan");
var tf = cm.transform;
this.gameObject.transform.LookAt(tf);
animator.Play("Attacking on Ground");
StartCoroutine("DestroySelfOnGround");
}
else
{
animator.Play("Standing Attack");
StartCoroutine("DestroySelfStanding");
}
}
}
}
Any help to fix this would be appreciated.
Your methods are private.
You have to write public in front of the method you want to access from outside the class.
public void ReduceHealth()
{
...
}
You need to make void ReduceHealth() to be public -> public void ReduceHealth()