Bullet script problem in unity: transform is not responding - c#

I'm trying to use a script to make bullets spawn and move but they just spawn and drop to the ground due to the gravity on the rigidbody, but no gravity seems to be applied.
I'm suspecting I need to declare something I didn't declare, but I'm pretty new to coding so I can't seem to solve it. Here's the script I've got so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shoot : MonoBehaviour
{
public GameObject projectile;
public float speed = 1000;
void Start()
{
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Instantiate(projectile, transform.position, projectile.transform.rotation);
projectile.transform.Translate(Vector3.back * Time.deltaTime * speed);
Debug.Log("Shoot!");
}
}
}
And here's a screen recording of the problem:
https://drive.google.com/file/d/14KTSx7fJMhs-se40sZFDFmTJGv0Kqylk/view?usp=sharing
projectile refers to the bullet prefab I've created and attached to the spawnPoint gameObject, which as you can see it's on the tip of the rifle
Any help would be highly appreciated!

There are multiple issues here
You spawn a new copy of (I assume) the prefab projectile but then instead of moving this new spawned object you try to move the original prefab projectile.
→ You rather want to move the new spawned object instead!
you move only for the one single frame where GetKeyDown is true.
→ For a continuous movement you would need to keep calling the movement continously once every frame!
So what you would (see bottom point) be doing is rather have a dedicated component on the projectile prefab
public class Bullet : MonoBehaviour
{
public float speed = 1000;
private void Update ()
{
// might have to tweak the direction e.g. Vector3.forward according to your needs
transform.Translate(Vector3.back * Time.deltaTime * speed);
}
}
And then simply do
public class Shoot : MonoBehaviour
{
public Bullet projectile;
void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Instantiate(projectile, transform.position, transform.rotation);
Debug.Log("Shoot!");
}
}
}
However, since you are using a Rigidbody and want to move your bullet using Physics and get a working Collision Detection you shouldn't use Transform at all.
Rather disable the gravity on the Rigidbody and then do
public class Shoot : MonoBehaviour
{
public Rigidbody projectile;
public float speed = 1000;
void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
var newBullet = Instantiate(projectile, transform.position, transform.rotation);
// Might have to tweak the direction using -transform.forward according to your needs
newBullet.velocity = transform.forward * speed;
Debug.Log("Shoot!");
}
}
}

Related

How can I make it so that the player loses health when an enemy collides with a different object?

Here an image of what my game looks like so far so that you can get a better idea but I need it so that when one of the outer capsules touches the red box the player (middle capsule) loses health.
I have tried creating a new script which checks for collisions but I couldn't get it to work and am unsure where to go from here. Below is my code for how the health bar works and pressing the space bar reduces health for demonstration purposes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
public float MaxHealth;
public Slider _slide;
private float currentHealth;
void Start()
{
currentHealth = MaxHealth;
_slide.maxValue = MaxHealth;
_slide.value = MaxHealth;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
TakeDamage(25f);
if(currentHealth <=0)
{
//move to game over
}
}
void TakeDamage(float Damage)
{
currentHealth = currentHealth - Damage;
_slide.value = currentHealth;
}
}
At one point I tried using
void Update()
{
(collision.collider.name == "Barrier");
TakeDamage(25f);
if(currentHealth <=0)
{
//move to game over
}
}
but realised this is completely wrong, as well as trying to add a box collider to the red "barrier" to aid this but it didn't fix anything.
Update 2:
Also tried changing it so that the enemies move towards the barrier and not the player and added the code:
private void OnTriggerEnter(Collider other) {
TakeDamage(25f);
if(currentHealth <=0)
{
//move to game over
}
}
which doesn't present any errors but rather just doesn't do anything. The enemy capsules just go through it and to the centre, with no health being lost.
You need to check for collisions using OnCollisionEnter3D and then use the TakeDamage function inside of it. Here is an example:
public class PlayerHealth: MonoBehaviour
{
void OnCollisionEnter3D(Collision3D col) //Check for collision
{
TakeDamage(25f);
}
}
Note that for this function to work, both objects need a 3d collider and a rigid body. If you don't want your character to fall, just set gravity to 0.

Player health will not go down

i have tried to make it so whenever the enemy collides with Player1 health drops, i have all of the public floats and transforms setup, but the health in the debug menu shows it just doesnt go down, any ideas?
(attached to player1)
using System.Collections;
using System.Collections;
using UnityEngine;
public class PlayerHealth : MonoBehaviour
{
private float health = 0f;
public Transform player;
[SerializeField] private float maxHealth = 100f;
private void Start() {
health = maxHealth;
}
public void UpdateHealth (float mod) {
health += mod;
if (health > maxHealth){
health = maxHealth;
} else if (health <= 0f){
health = 0f;
Debug.Log("Player Respawn");
}
}
}
attached to enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAttack : MonoBehaviour
{
[SerializeField] private float attackDamage = 10f;
[SerializeField] private float attackSpeed = 1f;
public Transform player;
private float canAttack;
// Start is called before the first frame update
private void OnCollisionStay2D(Collision2D other) {
if (other.gameObject.tag == "Player1"){
if (attackSpeed <= canAttack){
other.gameObject.GetComponent<PlayerHealth>().UpdateHealth(-attackDamage);
canAttack = 0f;
}else {
canAttack += Time.deltaTime;
}
}
}}
You are serializing the Max Health, not the current health, so the value shown in the inspector will never change. Add the SerializeField to the health value as well and you'll see the current health.
[SerializeField]
private float health = 0f;
Beyond that, it's difficult to see if the code isn't working. I would ensure that the tag you're checking for is actually set on the Player, and add a debug message to OnCollisionStay2D to ensure the collision handler is being called. If it isn't, ensure you have the collider components on both the enemy game object and the player gameobject.
Depending on your intention, you may want to move the canAttack modifier to Update(), since your current logic will only "refresh" the canAttack timer when the enemy is actually colliding with the player. If the enemy hits a player and the player runs away, and comes back ten minutes later and runs into the enemy, the enemy won't attack, they'll only start "healing" their canAttack timer until it's ready again. Maybe this is what you want, but I suspect that it should go into Update() so enemies that have already attacked "rest up" at all times.

Destroyed Bullet Won't Make Clones and Shoot After around 3 seconds

I made a script that shoots a bullet and destroys it after 3 seconds, however it destroys the original bullet after it is shot which makes unity unable to make copies of it to shoot another bullet, An okay solution might be to make a copy of the bullet and shoot the copy however I do not know how to do that.
This is the script for the gun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletPrefab, bulletLife);
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation);
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
This is the script for the bullet.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shot : MonoBehaviour
{
// Start is called before the first frame update
public float speed = 3000f;
void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
}
From what I see, you destroy the bulletPrefab but you never actually assign an object for it. You can have a separate GameObject and do that when instantiating inside the Shoot method. Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private GameObject bulletShot // This is what I added
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletShot); // Removed the timer from here
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
bulletShot = Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation); // Assigned the bullet to the separate GameObject
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
That way, when you try to destroy it, there will actually be something to destroy. However, this will create a new issue. The previously instantiated object will be destroyed instantly when you shoot again. You can fix that by creating a list and adding the bullets there. However, that is NOT efficient. And since you are going to start all over, I suggest you go for an Object Pool. What is that? Glad you asked!
There is a very good video by Jason Weimann on YouTube about object pooling. You might want to give it a go, it is old but definitely not outdated.
https://www.youtube.com/watch?v=uxm4a0QnQ9E
You might need to keep a reference to the shot bullet, to be able to destroy with a coroutine after 3 seconds, it later on like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private Queue<GameObject> myQ = new Queue<GameObject>(); //queue to keep track of the shot bullet and handle the delayed destroy
private IEnumerator coroutine;
void Start() {
coroutine = DelayedDestroy(3f);
}
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
//Destroy(bulletPrefab, bulletLife); Commented to avoid immediate destruction
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
myQ.Enqueue(Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation));
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
StartCoroutine(coroutine);
}
private IEnumerator DelayedDestroy(float waitTime) {
yield return new WaitForSeconds(waitTime);
GameObject nullcheck = myQ.Dequeue();
if (nullcheck != null) { //in case the queue is empty
Destroy(nullcheck );
}
}
}
Did not debug that, it is to give you the idea. You can check the Queue data structure and coroutines.
Check pooling to achieve what you are making even better :)

Unity prefab movement

Hello i want to create a group of the same prefab following the player in my game. I already got the prefab intantiation to follow the player but when there is more than one they just follow the exact same path on top of each other. is there a way where they can follow the player but act like a bunch of bees moving?
Thanks!
This is the script on my prefab:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KillerMovement : MonoBehaviour {
public GameObject player;
void FixedUpdate()
{
Vector2 toTarget = player.transform.position - transform.position;
float speed = 0.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
The best solution depends on you game logic, but you may consider having a delay before starting following (you can tweak the delay depending on the position you want to the particular object to assume in the trail.
using System.Collections;
using UnityEngine;
public class Follower : MonoBehaviour
{
public GameObject player;
public float delay = 0f;
public float speed = .5f;
bool isReady = false;
void Start()
{
StartFollowing();
}
public void StartFollowing()
{
StartCoroutine(WaitThenFollow(delay));
}
IEnumerator WaitThenFollow(float delay)
{
yield return new WaitForSeconds(delay);
isReady = true;
Debug.Log(gameObject.name);
Debug.Log(Time.time);
}
void FixedUpdate()
{
if (isReady && player != null)
{
Vector2 toTarget = player.transform.position - transform.position;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
}
I've called StartFollowing in the Start method for you to test the code. You should call this method whenever approrpiate in your game logic.

unity3D, enemy following issue

I'm trying to make enemies follow my player when the player enters the radius area of an enemy, but make the enemy stop following when my bullet hits object or enters radiusArea.
See my gif for more detail:
Gif
script:
using UnityEngine;
using System.Collections;
public class FlyEnemyMove : MonoBehaviour
{
public float moveSpeed;
public float playerRange;
public LayerMask playerLayer;
public bool playerInRange;
PlayerController thePlayer;
// Use this for initialization
void Start()
{
thePlayer = FindObjectOfType<PlayerController>();
}
// Update is called once per frame
void Update()
{
flip();
playerInRange = Physics2D.OverlapCircle(transform.position, playerRange, playerLayer);
if (playerInRange)
{
transform.position = Vector3.MoveTowards(transform.position, thePlayer.transform.position, moveSpeed * Time.deltaTime);
//Debug.Log(transform.position.y);
}
//Debug.Log(playerInRange);
}
void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(transform.position, playerRange);
}
void flip()
{
if (thePlayer.transform.position.x < transform.position.x)
{
transform.localScale = new Vector3(0.2377247f, 0.2377247f, 0.2377247f);
}
else
{
transform.localScale = new Vector3(-0.2377247f, 0.2377247f, 0.2377247f);
}
}
}
I hope someone can help me :(
Physics2D.OverlapCircle detects only the collider with the lowest z value (if multiple are in range). So you either need to change the z values so the player has the lowest or you need to work with Physics2D.OverlapCircleAll and check the list to find the player. Or you could change your layers so only the player itself is on that specific layer you feed into the overlap test.

Categories