hello i'm trying to create an ai for my characters but it doesnt work
i want it to shoot my character on sight but it's just wandering in the scene and not shooting
not even my debug.log works
public Transform[] Targets;
private int DestPoint = 0;
private NavMeshAgent Agent;
public Transform Player;
public Rigidbody Bullet;
public Transform Instantiator;
void Start()
{
Agent = GetComponent<NavMeshAgent> ();
Agent.autoBraking = false;
}
void Update()
{
if (Vector3.Distance(transform.position, Player.position) < 30f)
{
Debug.Log ("Shoot");
transform.LookAt (Player);
Shoot ();
}
else if (Vector3.Distance(transform.position, Player.position) > 30f)
{
GotoNextPoint ();
}
}
void GotoNextPoint()
{
Agent.destination = Targets [DestPoint].position;
DestPoint = (DestPoint + 1) % Targets.Length;
}
void Shoot()
{
Rigidbody Clone = Instantiate (Bullet, Instantiator.position, Instantiator.rotation) as Rigidbody;
Clone.AddForce (Vector3.forward);
}
The distance is simply never less than 30. In your else:
else if (Vector3.Distance(transform.position, Player.position) > 30f)
{
GotoNextPoint ();
}
do Debug.Log(Vector3.Distance(transform.position, Player.position));
so you can see what values you're getting:
else if (Vector3.Distance(transform.position, Player.position) > 30f)
{
Debug.Log(Vector3.Distance(transform.position, Player.position));
GotoNextPoint ();
}
Related
so I have this code :
public class ball_physics : MonoBehaviour
{
public Rigidbody ball;
public open2close claw;
public Vector3 offset;
Rigidbody rb;
private float forceMultiplier = 20;
private bool isShoot;
Vector3 start_position;
public path path;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.Sleep();
start_position = transform.position;
}
void Update()
{
// *************
}
void Shoot(Vector3 Force)
{
if (isShoot)
{
print("isshot false");
return;
}
rb.AddForce(new Vector3(Force.x, Force.y,Force.z)* forceMultiplier);
isShoot = true;
path.Instance.HideLine();
}
private void OnTriggerStay(Collider ball)
{
if (isShoot)
{
return;
}
print("ontriggerstay");
if (claw.isClosed && claw.transform.gameObject.tag == "claw" )
{
print("claw");
//rb.Sleep();
transform.position = claw.rightClaw.transform.position + offset;
Vector3 forceInit = (start_position - transform.position);
path.Instance.UpdateTrajectory(forceInit * forceMultiplier, rb, transform.position);
}
}
private void OnTriggerExit(Collider ball)
{
if (claw.isClosed && claw.transform.gameObject.tag == "claw")
{
rb.WakeUp();
}
if (claw.isClosed == false)
{
Shoot(start_position - transform.position);
}
}
}
So on my other codes I have OnStayTrigger, which basically a clawhand grabs a ball and pulls. But this code is suppose to show a trajectory line and shoot. When I shoot the first time it works. But when I try again, The clawhand can grab the ball but it doesnt show the trajectory line nor shoots it. So im guessing this is the code that needs to be fixed. How can I make OnTriggerStay work all the time. I wouldnt want to change any code because it works perfectly. How can I just keep updating the OnTriggerstay so it can work?
At first glance it appears that You never set isShoot back to false, so it would never fire again because in OnTriggerStay you have it return if isShoot = true.
I've created a "game" where you can attack enemies with a sword. All damages applies to only one enemy, even if i attack the other one. Also when I destroy the first I get this message
MissingReferenceException: The object of type 'RectTransform' has been destroyed but you are still trying to access it.
How do I fix it?
Here's my code:
Attached to the enemy
public class EnemyHealth : MonoBehaviour
{
private Transform healthBar;
private Transform canvas;
public float maxHealth = 100f;
public float damage = 10f;
float currentHealth;
float barLength;
private void Start()
{
currentHealth = maxHealth;
GameObject gfx = gameObject.transform.Find("GFX").gameObject;
canvas = gfx.transform.Find("Health Bar");
GameObject canv = canvas.gameObject;
healthBar = canv.transform.Find("Green Bar");
barLength = canvas.GetComponent<RectTransform>().rect.width * canvas.localScale.x;
}
public void GetStruck()
{
currentHealth -= damage;
if (currentHealth < 0) currentHealth = 0;
healthBar.localScale = new Vector3(currentHealth / maxHealth, 1f, 1f);
Vector3 p = healthBar.position;
p -= damage / (maxHealth * 2) * barLength * transform.right * -1;
healthBar.position = p;
if(currentHealth == 0)
{
Destroy(gameObject);
}
}
}
And the sword attack
using UnityEngine;
public class SwordAttack : MonoBehaviour
{
public Animator animator;
private EnemyHealth enemyHealth;
private bool isAttacking;
private void Start()
{
enemyHealth = GameObject.FindGameObjectWithTag("Enemy").GetComponent<EnemyHealth>();
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
isAttacking = true;
animator.Play("swordSwing", -1, 0f);
}
else if(animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1)
{
isAttacking = false;
}
animator.SetBool("isAttacking", isAttacking);
}
private void OnTriggerEnter(Collider other)
{
if(isAttacking && other.CompareTag("Enemy"))
{
enemyHealth.GetStruck();
}
}
}
private void Start()
{
enemyHealth = GameObject.FindGameObjectWithTag("Enemy").GetComponent<EnemyHealth>();
}
This is your mistake. You are only getting 1 enemy's health at your start method and you are always decreasing from that enemy's health. Instead you can get the hitted enemy's health in your OnTriggerEnter method and apply damage to that enemy.
Trying to run a raycast from my camera to Z = 0 that will hit objects on the TransparentFX layer and temporarily make them transparent as they are likely blocking the view of the player. But the raycast never hits anything.
Camera
https://imgur.com/lyTo8OZ
Tree
https://imgur.com/bgNiMWR
ClearSight.cs
[RequireComponent(typeof(Camera))]
public class ClearSight : MonoBehaviour
{
[SerializeField]
private LayerMask raycastLayers;
// Update is called once per frame
void Update()
{
Vector3 forward = transform.TransformDirection(Vector3.forward) * 10;
Debug.DrawRay(transform.position, forward, Color.green);
RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, transform.TransformDirection(Vector3.forward), 10f, raycastLayers);
if(hits != null && hits.Length > 0)
{
Debug.Log("Found objects blocking player!");
foreach(RaycastHit2D hit in hits)
{
Debug.Log("Making " + hit.transform.gameObject.name + " transparent!");
AutoTransparent at = hit.transform.GetComponent<AutoTransparent>();
if(at == null)
{
at = hit.transform.gameObject.AddComponent<AutoTransparent>();
}
at.MakeTransparent();
}
}
}
}
AutoTransparent.cs
[RequireComponent(typeof(SpriteRenderer))]
public class AutoTransparent : MonoBehaviour
{
[SerializeField]
private SpriteRenderer[] renderTargets;
[SerializeField]
private float transparentRecoveryTime = 0.1f;
private bool isTransparent = false;
private float transparencyTimer = 0;
private void Update()
{
if (isTransparent)
{
UpdateTransparencyTimer();
}
}
private void UpdateTransparencyTimer()
{
transparencyTimer += Time.deltaTime / transparentRecoveryTime;
if(transparencyTimer >= 1)
{
MakeOpaque();
}
}
public void MakeTransparent()
{
transparencyTimer = 0;
if (!isTransparent)
{
isTransparent = true;
foreach (SpriteRenderer renderer in renderTargets)
{
Color c = renderer.color;
renderer.color = new Color(c.r, c.g, c.b, 0.3f);
}
}
}
public void MakeOpaque()
{
isTransparent = false;
foreach(SpriteRenderer renderer in renderTargets)
{
Color c = renderer.color;
renderer.color = new Color(c.r, c.g, c.b, 1);
}
}
}
Figured it out. I was using RaycastHit2D and Physics2D.RaycastAll, which uses Vector2 parameters so the Z forward variable was being taken out of the equation. Switched up to Box Collider and Physics.RaycastAll and it works great.
I am developing a 2D game. There are different enemies in the game, but I am being stuck with one of them. The enemy is a monster with a hammer in the hand. When the player enters in it's range, it runs towards the player and attacks him with the hammer. Everything worked fine. I made a prefab of it and used it in the rest of my game. But then I noticed that enemy is attacking and even the script is working as I can see the hammer collider at the time of enemy attack. But that collider was not damaging the player. I checked everything from script to tags and colliders, but nothing worked. Then I created a separate scene to sort out the issue. I just dragged my player and the enemy from prefab folder to the scene and guess what it was working there. Which mean that if there was only one enemy (one instance of it), everything worked but not when I created the second instance of the same enemy with the same scripts and everything else. Just can't sort out the issue. Hammer Enemy
Monster script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Monster : MonoBehaviour {
private static Monster instance;
public static Monster Instance
{
get {
if (instance == null) {
instance = GameObject.FindObjectOfType<Monster> ();
}
return instance;
}
}
//Movement Variables
public float movementSpeed;
private IMonsterState currentState;
public Animator MyAnimator{ get; set;}
public GameObject Target{ get; set;}
bool facingRight;
public bool Attack{ get; set;}
public bool TakingDamage{ get; set;}
public float meleeRange;
public Transform leftEdge;
public Transform rightEdge;
public float pushBackForce;
public EdgeCollider2D attackCollider{ get; set;}
//public EdgeCollider2D monsterhammer;
public bool InMeleeRange
{
get{
if (Target != null) {
return Vector2.Distance (transform.position, Target.transform.position) <= meleeRange;
}
return false;
}
}
// Use this for initialization
void Start () {
ChangeState(new IdleState());
MyAnimator = GetComponent<Animator> ();
attackCollider = GetComponentInChildren<EdgeCollider2D> ();
//attackCollider=monsterhammer;
facingRight = true;
}
// Update is called once per frame
void FixedUpdate () {
if (!Attack) {
attackCollider.enabled = false;
}
if (!TakingDamage) {
currentState.Execute ();
}
LookAtTarget ();
}
private void LookAtTarget()
{
if (Target != null) {
float xDir = Target.transform.position.x - transform.position.x;
if (xDir < 0 && facingRight || xDir > 0 && !facingRight) {
ChangeDirection ();
}
}
}
public void ChangeState(IMonsterState newState)
{
if (currentState != null) {
currentState.Exit ();
}
currentState = newState;
currentState.Enter (this);
}
public void Move()
{
if (!Attack) {
if ((GetDirection ().x > 0 && transform.position.x < rightEdge.position.x) || (GetDirection ().x < 0 && transform.position.x > leftEdge.position.x)) {
MyAnimator.SetFloat ("speed", 1);
transform.Translate (GetDirection () * (movementSpeed * Time.deltaTime));
}
else if (currentState is MonsterPatrol)
{
ChangeDirection ();
}
}
}
public Vector2 GetDirection()
{
return facingRight ? Vector2.right : Vector2.left;
}
public void ChangeDirection()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
public void MoveLeft()
{
facingRight = false;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
public void MoveRight()
{
facingRight = true;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
void OnTriggerEnter2D(Collider2D other)
{
currentState.OnTriggerEnter (other);
}
void OnCollisionStay2D(Collision2D other)
{
if (other.gameObject.tag == "Player") {
playerHealth thePlayerHealth = other.gameObject.GetComponent<playerHealth> ();
thePlayerHealth.addDamage (2);
//if (playerHealth.damaged) {
pushBack (other.transform);
//}
}
}
void pushBack(Transform pushedObject)
{
//Vector2 pushDirection = new Vector2 (0, (pushedObject.position.y - transform.position.y)).normalized;
//pushDirection *= pushBackForce;
Rigidbody2D pushRB = pushedObject.gameObject.GetComponent<Rigidbody2D> ();
pushRB.velocity = Vector2.zero;
if (pushedObject.position.x > transform.position.x) {
pushRB.AddRelativeForce (Vector3.right * pushBackForce);
} else {
pushRB.AddRelativeForce (Vector3.left * pushBackForce);
}
}
public void MeleeAttack()
{
attackCollider.enabled = true;
}
}
The problem most likely lies with:
get {
if (instance == null) {
instance = GameObject.FindObjectOfType<Monster> ();
}
return instance;
}
GameObject.FindObjectOfType<Monster>(); Will always return the first object found of this type as stated in the docs under description.
This means that when you add multiple Monsters into your scene your variable instance will get filled with the same Monster for all instances (the first one it finds in your hierarchy)
Now since you havn't posted your Player script I'll have to do some assuming now:
You are probably checking your <Monster> instance somewhere in your player script to see if it is near enough to the player to attack and hurt it. This will not be the case for all monsters except the single monster found by your FindObjectOfType<Monster>() . You could test this by manually placing each monster right next to your player, and most likely if for example you have 5 monsters in your scene 1 will attack, and 4 won't.
To fix this you can:
Assuming you want the current instance of the Monster script in instance simply apply this to it (instance = this)
store all your monsters in an array using FindObjectsOfType<Monster>() (notice the s after object) which will return all instances of the type monster. as found in the docs
I'm new at programming. I started a Unity3D course at udemy, I already finished the course, but I'm trying to add a buff to my game. What I want is when my player gets that buff, his shot speed inscreases, I tried to do it but im getting this error when I collide with it
NullReferenceException: Object reference not set to an instance of an object
Powerup.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Galaxy Shooter/Scripts/Powerup.cs:54)
EDIT
Album With usefull images
Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour {
public bool canTripleShoot = false;
public bool movSpeedBoost = false;
public bool shield = false;
[SerializeField]
private GameObject _laserPrefabs;
[SerializeField]
private GameObject _tripleShootPrefab;
[SerializeField]
private GameObject _shieldGameObject;
[SerializeField]
private GameObject _explosionPrefab;
[SerializeField]
private float _speed = 5.0f;
[SerializeField]
private GameObject[] _engines;
[SerializeField]
private float _fireRate = 0.25f;
private float _canFire = 0.0f;
public int playerHp = 3;
public int _hitcount = 0;
private UIManager _uiManager;
private GameManager _gameManager;
private SpawnManager _spawnManager;
private AudioSource _audioSource;
// Use this for initialization
void Start () {
_audioSource = GetComponent<AudioSource>();
_spawnManager = GameObject.Find("Spawn_Manager").GetComponent<SpawnManager>();
_uiManager = GameObject.Find("Canvas").GetComponent<UIManager>();
_gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
transform.position = new Vector3(0, 0, 0);
if(_uiManager != null)
{
_uiManager.UpdateLives(playerHp);
}
if(_spawnManager != null)
{
_spawnManager.StartSpawn();
}
}
// Update is called once per frame
void Update ()
{
Movement();
//ativar ao pressionar espaço ou botão esquerdo do mouse
if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButton(0))
{
Shoot();
}
}
//renderização e cooldown dos tiros
private void Shoot()
{
if (Time.time > _canFire)
{
_audioSource.Play();
if (canTripleShoot== true)
{
Instantiate(_tripleShootPrefab, transform.position, Quaternion.identity);
}
else
{
Instantiate(_laserPrefabs, transform.position + new Vector3(0, 0.95f, 0), Quaternion.identity);
}
_canFire = Time.time + _fireRate;
}
}
//calculo de dano
public void Damage()
{
if(shield == true)
{
shield = false;
_shieldGameObject.SetActive(false);
return;
}
_hitcount++;
if(_hitcount == 1)
{
_engines[0].SetActive(true);
}
if(_hitcount == 2)
{
_engines[1].SetActive(true);
}
playerHp--;
_uiManager.UpdateLives(playerHp);
if(playerHp < 1)
{
Instantiate(_explosionPrefab, transform.position, Quaternion.identity);
_gameManager.gameOver = true;
_uiManager.ShowTitleScreen();
Destroy(this.gameObject);
}
}
public void ShieldUp()
{
shield = true;
_shieldGameObject.SetActive(true);
}
//controle da velocidade de movimento e teleporte
private void Movement()
{
float controleHorizontal = Input.GetAxis("Horizontal");
float controleVertical = Input.GetAxis("Vertical");
//velocidade de movimento
if(movSpeedBoost == true)
{
transform.Translate(Vector3.up * _speed * controleVertical * Time.deltaTime * 2.0f);
transform.Translate(Vector3.right * _speed * controleHorizontal * Time.deltaTime * 2.0f);
}else
{
transform.Translate(Vector3.up * _speed * controleVertical * Time.deltaTime);
transform.Translate(Vector3.right * _speed * controleHorizontal * Time.deltaTime);
}
//limita jogar até o centro da tela
if (transform.position.y > 0)
{
transform.position = new Vector3(transform.position.x, 0, 0);
}
//limita jogar até a borda inferior
else if (transform.position.y < -4.2f)
{
transform.position = new Vector3(transform.position.x, -4.2f, 0);
}
//teleporta jogar se sair da tela na horizontal
else if (transform.position.x < -9.45f)
{
transform.position = new Vector3(9.45f, transform.position.y, 0);
}
else if (transform.position.x > 9.45f)
{
transform.position = new Vector3(-9.45f, transform.position.y, 0);
}
}
//duração triple shot
public IEnumerator TripleShotPowerDownRoutine()
{
yield return new WaitForSeconds(5.0f);
canTripleShoot = false;
}
public void TripleShotPowerUpOn()
{
canTripleShoot = true;
StartCoroutine(TripleShotPowerDownRoutine());
}
//duração boost de movimento
public IEnumerator MovSpeedBoostDownRoutine()
{
yield return new WaitForSeconds(5.0f);
movSpeedBoost = false;
}
//ativa boost de movimento e inicia contagem de duração
public void MovSpeedBoost()
{
movSpeedBoost = true;
StartCoroutine(MovSpeedBoostDownRoutine());
}
}
Laser
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Laser : MonoBehaviour {
[SerializeField]
public float _speed = 10.0f;
[SerializeField]
public bool _laserBoost = false;
// Use this for initialization
void Start () {
_laserBoost = false;
if (_laserBoost == true)
{
_speed = 100f;
}
}
// Update is called once per frame
void Update () {
//código gigante e dificil de decifrar ~irony
transform.Translate(Vector3.up * _speed * Time.deltaTime);
if (transform.position.y > 6f)
{
if(transform.parent != null)
{
Destroy(transform.parent.gameObject);
}
Destroy(gameObject);
}
}
public IEnumerator LaserBoostDuration()
{
yield return new WaitForSeconds(10f);
_laserBoost = false;
}
public void LaserBoost()
{
_laserBoost = true;
StartCoroutine(LaserBoostDuration());
}
}
Powerup
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Powerup : MonoBehaviour
{
[SerializeField]
private float _speed = 3.0f;
[SerializeField]
private int powerupID;
[SerializeField]
private AudioClip _clip;
// Update is called once per frame
void Update ()
{
//movimenta o powerup para baixo
transform.Translate(Vector3.down * _speed * Time.deltaTime);
//destroy o power ao sair da tela
if (transform.position.y <-7)
{
Destroy(this.gameObject);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Player")
{
//acessa o player
Player player = other.GetComponent<Player>();
if(player != null)
{
if(powerupID == 0)
{
//ativa tripleshoot
player.TripleShotPowerUpOn();
}
else if(powerupID == 1)
{
//ativa speedboost
player.MovSpeedBoost();
}
else if(powerupID == 2)
{
//ativar shield
player.ShieldUp();
}
else if (powerupID == 3)
{
Laser laser = GameObject.Find("laser").GetComponent<Laser>();
laser.LaserBoost();
}
}
//detroy powerup
AudioSource.PlayClipAtPoint(_clip, transform.position);
Destroy(this.gameObject);
}
}
}
The error is occuring at
laser.LaserBoost();
One of the two:
1) you don't have a object named "laser" in Scene hierarchy;
2) this object exists but doesn't have a component of type Laser attached to it.
Edit: Now that I saw your image, I can confirm it is the case 1) above. You have a Prefab named "laser", but is doesn't exist in the Scene hierarchy. If you want it to come to existence only when needed, you must instantiate the Prefab in scene.
If you want to search for the Prefab by its name, there is a way, but you'll need to have a folder named Resources inside another named Assets... More info here
Instead, I suggest you another approach. First, you will need a reference to your Prefab in some object, depending on the logic of your program. You seem to have a GameManager class, there could be a good place. Add a new field inside there (if it is the case) like this:
[SerializeField]
public Laser laser
// (...)
Then, change the code that generates the error. GameObject.Find tries to find an object that already exists in scene. In your case, you want to instantiate a clone of a Prefab, by passing a reference to it. Something like this:
else if (powerupID == 3)
{
// I don't know for sure how can you access your GameManager in your program. I will suppose your player have a reference to it.
// You shall change the position depending on your game logic
Laser laserClone = (Laser) Instantiate(player.gameManager.laser, player.transform.position, player.transform.rotation);
laserClone.LaserBoost();
}
Now, select the object in Scene that will have the reference (again, I am supposing it is gameManager) to see its inspector, than fill the laser field with a reference to your Prefab that has a Laser component. Just be sure the clone object will be destroyed when it is not needed.
But... To be very honest, I don't think you will get the result you are expecting anyway. If what you want is just faster shoot rate, the way you're trying to do it now seems pretty convoluted to me. Why don't you do it just like the triple shoot you have? Add a flag inside your player, when it is true the fireRate value changes.