How to delete the bullet and still shoot? - unity3d

I'm following a code monkey tutoreal on making a third person game. I tried to copy his code but when he deletes the bullet on impact he can shoot again but when I do it, it does not work.
Bullet code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletProjectile : MonoBehaviour
{
private Rigidbody bulletRigidbody;
[SerializeField] private GameObject playerArmature;
[SerializeField] private Transform vfxHitEnemy;
[SerializeField] private Transform vfxMissEnemy;
private void Awake()
{
bulletRigidbody = GetComponent<Rigidbody>();
}
private void Start()
{
float speed = 20f;
bulletRigidbody.velocity = transform.forward * speed;
}
public void OnTriggerEnter(Collider other)
{
if (other.GetComponent<targetScript>() != null)
{
//hit target
Instantiate(vfxHitEnemy, transform.position, Quaternion.identity);
}
else
{
Instantiate(vfxMissEnemy, transform.position, Quaternion.identity);
}
Destroy(gameObject);
}
}
shooting code:
if (starterAssetsInputs.shoot)
{
Vector3 aimDir = (mouseWorldPosition - bulletSpawn.position).normalized;
Instantiate(pfBullletProjectile, bulletSpawn.position, Quaternion.LookRotation(aimDir, Vector3.up));
starterAssetsInputs.shoot = false;
}

When the bullet is deleted, it won't exist anymore in your game so there won't be anything to shoot anymore.
To fix this you should make your bullet as a prefab, to do this you can just drag your bullet object to the assets folder and maybe make a special folder for those kind of objects.
Then instead of instantiating the bullet that is already in your scene you need to spawn the prefab, then when the bullet is deleted the prefab will still exist and you will be able to spawn it again.

Related

Shooting bullets in unity2D

I'm trying to shoot bullets in a 2D top-down-shooter game but they just stay rest and doesnt move
I tried to add "addforce" but they didnt move. I changed it with "new Vector2" but that time it doesnt follow my cursor. istead of it shoots in a different same direction.I want to shoot to my cursor. and if possible I want to add a fire delay between object and make it possible to attack repeatly while holding mouse left button.
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
public Transform firePoint;
public GameObject bulletPrefab;
public float bulletForce = 15f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
Shoot();
}
void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab,firePoint.position, firePoint.rotation);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firePoint.right* bulletForce, ForceMode2D.Impulse);
}
}//class
`

why does my raycast sometimes shoot in the wrong direction?

I'm trying to shoot a raycast from a gameobject at the front of a gun that is being carried by an XR hand. The gun shoots fine most of the time, but sometimes, for some reasons beyond my fragile little minds comprehension, it shoots in the wrong direction. It shoots in random directions, but never more than 90 degrees of where it should be. This is my code. [I have attached a video of it happening.][1] Every time I think I find a pattern, it changes. All I can say is that I have reason to believe it's related to mesh colliders. Only very vague reason though. Edit: https://youtu.be/JxC4wq9nVRw here's an updated video, it shows the problem more clearly. I also changed the redundant if statements.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class GunBasic : MonoBehaviour
{
public Transform BulletShootPoint;
public ParticleSystem muzzleFlash;
public GameObject impactEffect;
public AudioSource audioSource;
public AudioClip audioClip;
public float Ammo;
public TextMeshPro text;
public void Fire()
{if (Ammo > 0)
{
muzzleFlash.Play();
audioSource.PlayOneShot(audioClip);
RaycastHit hit;
if (Physics.Raycast(BulletShootPoint.transform.position, BulletShootPoint.transform.forward, out hit) && Ammo > 0)
{
Debug.Log(hit.transform.name);
GameObject impactDestroy = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactDestroy, 2f);
}
Ammo -= 1f;
}
}
private void Update()
{
text.text = Ammo.ToString();
}
public void Reload()
{
Ammo = 10;
}
public void OnTriggerEnter(Collider other)
{
if(other.tag == "ReloadTag")
{
Ammo = 10f;
}
}
}
It was a bug in the xr interaction toolkit. It was fixed by an update. Sorry I didn’t try this sooner, thanks for all the help.

OnCollisionEnter Change Text when triggered

I'm very new to Unity, but not to C#. I'm looking to make a simple solution where when i move a ball into a hole - an event is triggered. The event in this case is changing the text of a TMPro object. I'm seeing nothing being returned when these 2 objects interact using the below code.... Looking for any help on this. More context can be given if needed.
Hole.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class Hole : MonoBehaviour
{
// Update is called once per frame
[SerializeField]
private Collision collision;
private TextMeshProUGUI text;
private void Start()
{
text = GetComponent<TextMeshProUGUI>();
}
void Update()
{
//text.text = "Hole hit!";
}
public void OnCollisionEnter(Collision collision)
{
text.text = "Hole hit!";
}
}
BallMover.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallMover : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(horizontal, vertical);
transform.position += movement * Time.deltaTime;
}
}
Thanks in advance
The Error below "Object Reference not set to instance of an object" is coming up when these 2 object collide...
You need to check are there colliders in both objects.
Collision is a physical event for this reason you have to move the ball with physics, if you move it with transform, it is very likely that you will miss the physics events.
You have to add a "Rigidbody" component to the ball to move physically.
So as you said in a comment you already checked that
You are using Rigidbody
Both objects have Collider components
So additionally make sure
both Collider components are set to IsTrigger = false
Whenever there is physics and Rigidbody involved you do not want to set stuff via transform in Update as this breaks the physics and collision detection! Rather use Rigidbody.MovePosition within FixedUpdate
so something like e.g.
public class BallMover : MonoBehaviour
{
[SerializeField] private Rigidbody _rigidbody;
private void Awake ()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
var horizontal = Input.GetAxis("Horizontal");
var vertical = Input.GetAxis("Vertical");
var movement = new Vector3(horizontal, vertical);
_rigidbody.MovePosition(_rigidbody.position + movement * Time.deltaTime);
}
}

How can I write my script without using public?

So I'm currently trying to add my player object with it's script into my camera script that is written for camera object.
using UnityEngine;
public class CameraController : MonoBehaviour
{
public Transform target;
private Vector3 offset;
private float damping = 0.2f;
public bool canMove = true;
public PlayerMovement player;
private void Start()
{
player = gameObject.GetComponent<PlayerMovement>();
}
void FixedUpdate()
{
if (player.faceRight)
{
offset = new Vector3(1f, 0.5f, -10f);
transform.position = Vector3.Lerp(transform.position, target.position, damping) + offset;
}
else if (!player.faceRight)
{
offset = new Vector3(-1f, 0.5f, -10f);
transform.position = Vector3.Lerp(transform.position, target.position, damping) + offset;
}
}
}
My problem is that I can't write player = gameObject.Find("Player"); because unity is saying something like "those are different types of elements" but I can drag my player object if I write public PlayerMovement player; and it works. The thing is I want to learn how to use that without dragging my object.
First of all you can expose and assign private (and protected) fields via the Inspector by using the attribute [SerializeField] without being forced to make them public like e.g.
[SerializeField] private PlayerMovement player;
private void Awake()
{
// if it's not referenced via the Inspector
// get it from this object on runtime as fallback
if(!player) player = GetComponent<PlayerMovement>();
}
And then, well, your field player is of type PlayerMovement while Find returns a GameObject → Not the same type.
Now if you want to get that component from the same object this script is attached to .. then why should you use the expensive Find at all?! You already know the object! Use only GetComponent to get the component attached tot he same object as this script (just like in the code above).
If you really want to use Find then you want to use GetComponent on the result, not on gameObject which is the reference to that scripts own GameObject. You would rather do
[SerializeField] private PlayerMovement player;
private void Awake()
{
if(!player) player = GameObject.Find("player").GetComponent<PlayerMovement>();
}
or if there is anyway only one instance of that type in your scene you can also simply use FindObjectOfType
[SerializeField] private PlayerMovement player;
private void Awake()
{
if(!player) player = FindObjectOfType<PlayerMovement>();
}
In general: If you can reference something via the Inspector it is always better doing so. It is way more efficient because the reference will be serialized into your scene or prefab asset so when it is loaded you already "know" the target reference. Using Find is very expensive and even though it was optimized a lot in later versions also GetComponent is work you could be avoiding ;)
I added my both GameObject and PlayerMovement attributes. Wrote gameObject first, which is called as "Player". Then by using my GameObject, I interacted with my script which is called PlayerMovement.
using UnityEngine;
public class CameraController : MonoBehaviour
{
public Transform target;
private Vector3 offset;
private float damping = 0.2f;
public bool canMove = true;
private GameObject playerObject;
private PlayerMovement playerMovementScript;
private void Start()
{
player = GameObject.Find("Player");
player = gameObject.GetComponent<PlayerMovement>();
}
void FixedUpdate()
{
if (player.faceRight)
{
offset = new Vector3(1f, 0.5f, -10f);
transform.position = Vector3.Lerp(transform.position, target.position, damping) + offset;
}
else if (!player.faceRight)
{
offset = new Vector3(-1f, 0.5f, -10f);
transform.position = Vector3.Lerp(transform.position, target.position, damping) + offset;
}
}
}
I fixed that problem when I was writing that issue here. So I'm kinda proud of myself but also I want to share my solution so if anyone encounter a problem like this, they can fix their problems. If anyone has better solution please share.

Life bar doesn't change after players destroy collider

I am trying to do a 2d game and my object doesn't affect the player's lifebar after they collide. The player's healthbar will will get a bigger lifetime but I think something it's wrong with the scripts. (Also the collider of object that needs to be destroy is "is trigger" checked). I put this PowerUp on the object, and the character script and healthbar script on the player.
Character.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class Character : MonoBehaviour
{
Rigidbody2D rb;
float dirX;
[SerializeField]
float moveSpeed = 5f, jumpForce = 600f, bulletSpeed = 500f;
Vector3 localScale;
public Transform barrel;
public Rigidbody2D bullet;
// Use this for initialization
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}
void FixedUpdate()
{
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
}
void Jump()
{
if (rb.velocity.y == 0)
rb.AddForce(Vector2.up * jumpForce);
}
void Fire()
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
HealthBar.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthBar : MonoBehaviour
{
public Slider slider;
public Gradient gradient;
public Image fill;
public float health = 100;
public void SetMaxHealth(int health)
{
slider.maxValue = health;
slider.value = health;
fill.color = gradient.Evaluate(1f);
}
public void SetHealth(int health)
{
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
PowerUp.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PowerUp : MonoBehaviour
{
public float multiplayer = 1.4f;
public GameObject pickupEffect;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Pickup(other);
}
}
void Pickup(Collider player)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
HealthBar stats =player.GetComponent<HealthBar>();
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
First of all as others already mentioned for a 2D game with Rigidbody2D and (hopefully) Collider2D components you want to use OnTriggerEnter2D instead!
The Physics and Physics2D are two completely separated engines and don't know each other. A 3D OnTriggerEnter will never get called for 2D physics events like collisions etc.
Then note that
Also the collider of object that needs to be destroy is "is trigger" checked
this is exactly the wrong way round. If your object shall track a OnTriggerEnter then the one that enters (the player) should have isTrigger enabled! In most cases you don't want to do this because the player shall e.g. actually collide with the floor and not fall through it etc.
So what you would need is rather putting an additional script on the player itself that waits for other trigger objects to enter!
Then to be sure either debug your code line by line or add additional logs in order to see what happens:
PowerUp
// put this on the pickup item
public class PowerUp : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private float multiplayer = 1.4f;
[SerializeField] private GameObject pickupEffect;
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
PowerUpDetector (on the player)
// put on player!
public class PowerUpDetector : MonoBehaviour
{
// reference this via the Inspector
[SerializeField] private HealthBar healthbar;
private void Awake()
{
if(!healthBar) healthBar = GetComponent<HealthBar>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("PowerUp"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var powerup = other.GetComponent<PowerUp>();
if(!powerup)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
powerup.Pickup(healthbar);
}
}
Well, and then what you do is only changing the float value
stats.health *= multiplayer;
but you never update the GUI accordingly like you would do when instead using
stats.SetHealth(stats.health * multiplayer)
(Btw: I think you mean multiplier ;) )
I would suggest to rather implement a property like e.g.
public class HealthBar : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private Slider slider;
[SerializeField] private Gradient gradient;
[SerializeField] private Image fill;
[SerializeField] private float health = 100;
public float Health
{
get { return health; }
set
{
health = value;
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
// be careful with variable names if you have this name already
// for a class field .. was ok this time but might get confusing
public void SetMaxHealth(int value)
{
slider.maxValue = value;
// The property handles the rest anyway
Health = value;
}
}
so now instead of calling SetHealth you simply assign a new value to Health and its setter is automatically execute as well so your GUI is updated.
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.Health *= multiplayer;
Destroy(gameObject);
}
Your qustions are below,
player will get a bigger lifetime
Also the collider of object that needs to be destroy
What situation possibly you could have
Check your Character is Triggered, so it calls 'OnTriggerEnter' and also check tag.
Check your powerUp item that checked true as trigger, 'isTrigger'
Check if collider or hit object scale is tiny, it won't work with collider.
If you have nothing with above check-list, then it should be working