I am making a shooting game using Raycasts in Unity. I have a gameObject which is a cube (The ground for my game). That cube can be destroyed by using Destroy() method. How do I make it indestructible so that it doesn't get destroyed even after using the Destroy() method?
This my FPS gun script so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public float damage = 20f;
public float range = 150f;
//public GameObject ground;
public Camera fpsCamera;
void Start()
{
//DontDestroyOnLoad(ground);
}
void Update()
{
if (Input.GetButtonDown("Fire1")) {
Shoot();
}
}
void Shoot()
{
RaycastHit hitObject;
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
Destroy(hitObject.transform.gameObject);
}
}
It sounds like you want to sort which objects are destroyed somehow. The most simple way is of course to not call the Destroy method on that object in the first place.
However since you explain in the comments you wish to shoot and destroy some objects but not all. I would in your case make a script which either checks the tag, layer or calls a method in a script on each object.
For example, you try something similar to this:
ObjectProperties : MonoBehaviour{
public boolean CanBeDestroyed = true;
}
Add that script to the objects you shoot at.
And then when your raycast hits an object, use GetComponent on the object shot to try get ObjectProperties from that object. If the object hit has a ObjectProperties that isn't null, and that ObjectProperties has CanBeDestroyed set to be true. Then you may destroy the object.
The most simple way of achieving this, is using tags. Create a tag by highlighting the object. In the Inspector open the tags tab. Then create a tag and assign it to your object. The last thing you need to do is to check in code which tag the object you hit has.
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
switch (hitObject.tag)
{
case "immortal": //Nothing happens
break;
case "veryweak": //Objects with this tag get destroyed
Destroy(hitObject.transform.gameObject);
break;
default: //Objects that do not have a tag and do not match any of the above get destroyed
Destroy(hitObject.transform.gameObject);
break;
}
}
Hope that helps.
Related
I am trying to create a simple scriptable object for my shoot ability. I have that aspect working, but as I try to set my Transform to my player, it does not update the shoot position. I am very new to C#, and this script isnt complete. I still need to add the functionality to destroy the created objects. Any help would be greatly appreciated. I suspect I need to add an update function but im am not certain how to do this.
using UnityEngine.InputSystem;
using UnityEngine.AI;
using UnityEngine;
namespace EO.ARPGInput
{
[CreateAssetMenu]
public class Shoot : Ability
{
public Transform projectileSpawnPoint;
public GameObject projectilePrefab;
public float bulletSpeed = 10;
public float bulletLife = 3;
public override void Activate(GameObject parent)
{
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
void OnCollisionEnter(Collision collision)
{
Destroy(projectile);
}
}
}
}
I'm still new to Unity and coding also, so take my advice with a load of salt :P.
It may be best to have a transform on your character (say just past the barrel of the player's gun) that you can put as the projectileSpawnPoint. In your code the projectileSpawnPoint is never set. Your first line of code in the "Activate" method should be something like:
public override void Activate(GameObject parent)
{
projectileSpawnPoint = playerGunBarrelTransform.transform.position;
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
For destroying the projectile afterward you can keep it as you have it in OnCollision. howeer, with bullets in particular, since they tend to be instantiated A LOT and then destroyed afterward it would be best to use an object pooler for them to instantiate several of them on start and then disable and enable them as needed so you can resuse them instead of making new ones every time.
you have to create a new script that derives from Monobehaviour for your projectiles. attach that script to the projectile prefab and place the OnCollisionEnter method in that script. now your projectiles should get destroyed when touching another collider. make sure that there is a rigidbody component attached to the projectile.
I am in the middle of creating a simple 3d platformer in unity and i'm trying to make it so if you are in a certain radius and you hit the "f" key the enemy will disappear. the way i'm checking to make sure the enemy is close enough to be attacked is to have trigger sphere and when its triggered and you press "f" the enemy will be removed. i'm currently trying to see what collided with the trigger but i cant figure it out. here is my current code
using UnityEngine;
public class PlayerCombat : MonoBehaviour
{
public GameObject Enemy1;
public GameObject Enemy2;
public GameObject Enemy3;
public GameObject Enemy4;
// Update is called once per frame
void OnTriggerEnter (Trigger triggerInfo)
{
if (triggerInfo.collider.tag == "Enemy" & Input.GetKey("f"))
{
if (triggerInfo.collider.name == Enemy)
{
Enemy1.SetActive(false);
}
}
}
}
The signature is and has always been OnTriggerEnter(Collider) otherwise that message method will not be recognized by Unity and not get called at all!
And then you already have the according Collider ... what else do you need?
public class PlayerCombat : MonoBehaviour
{
// There is no need to know the enemy references beforehand at all
// you will get all required references from the Collider parameter of OnTriggerEnter itself
// I personally would however expose these two settings to the Inspector to be more flexible
// this way you can adjust these two settings within Unity without having to touch your code
public string listenToTag = "Enemy";
public KeyCode listenToKey = KeyCode.F;
// The signature has to be this otherwise the message method is never invoked at all
private void OnTriggerEnter (Collider other)
{
// Rather use "CompareTag" instead of `==` since the latter will silently fail
// for typos and nonexistent tags while "CompareTag" shows an error which is good for your debugging
// And in general you want to use the logical "&&" instead of the bitwise operator "&" for bools
if (other.CompareTag(listenToTag) && Input.GetKey(listenToKey))
{
// simply set the object you collide with inactive
other.gameObject.SetActive(false);
}
}
}
Finally just make sure that all your enemy instances actually have the tag Enemy
First, I'm pretty sure OnTriggerEnter takes a Collider as its parameter, no? https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
You can get the name of the collided object like so
//Upon collision with another GameObject,
private void OnTriggerEnter(Collider other)
{
Debug.Log($"collided with {other.gameObject.name}");
//you can check for specific components too
MyComponent myComponent = other.GetComponent<MyComponent>();
if (myComponent != null) {
// do something if it has that component on it!
}
}
I'm making a game in Unity VR in which I punch numbers and get points.
The gloves are responsible for detecting collisions between the numbers that they hit. On my gloves I have a PunchScript component and the numbers each have a rigidBody and collider.
The problem is that no collisions seem to ever occur. I placed a Debug.LogError inside of the collision detection code to assert this.
I tried switching on/off kinematics on all objects and used different collision systems to no avail.
Here's my PunchScript component:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PunchScript : MonoBehaviour
{
public SteamVR_TrackedObject hand;
private Rigidbody rBody;
private bool visible = true;
// Start is called before the first frame update
void Start()
{
rBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rBody.MovePosition(hand.transform.position);
rBody.MoveRotation(hand.transform.rotation);
// print(rBody.velocity.magnitude* 1000);
}
void OnCollisonEnter(Collision other)
{
Rigidbody otherR = other.gameObject.GetComponentInChildren<Rigidbody>();
if (other.gameObject.name == "frpnchbg") {
Debug.LogError("Hit!");
}
if (other == null)
return;
Vector3 avgPoint = Vector3.zero;
foreach (ContactPoint p in other.contacts) {
avgPoint += p.point;
}
avgPoint /= other.contacts.Length;
Vector3 dir = (avgPoint - transform.position).normalized;
otherR.AddForceAtPosition(dir *50f* rBody.velocity.magnitude, avgPoint);
}
}
Here's how the glove object looks in the Unity inspector.
It is very important to write the name of Unity callback methods correctly, otherwise Unity will not be able to detect them on the object (and as a result, can never execute them).
In your case, you have misspelled the OnCollisionEnter callback.
Instead of OnCollisonEnter it should be OnCollisionEnter.
I am trying to make my player hit an object, destroying the object and triggering an animation, but everything I try causes an error. I am relatively new at c# so the answer may be obvious but I need help. How can I set it up so that the collision will cause the object to disappear and the player to play an animation? Here is the script I am currently trying.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class succ : MonoBehaviour
{
public float speed = .15f;
public static float jumpSpeed = 170f;
void Start()
{
GetComponent<ConstantForce2D>().enabled = false;
GameObject.Find("goal");
}
public bool animation_bool;
private object coll;
private object other;
void Update()
{
OnCollisionStay2D(Collision2D coll);
{
if (coll.gameObject.tag == "succ") ;
{
animation_bool = true;
GetComponent<Animator>().SetBool("succ", animation_bool);
GetComponent<ConstantForce2D>().enabled = true;
Destroy(other.object);
}
}
}
private void Destroy(object gameObject)
{
throw new NotImplementedException();
}
private void OnCollisionStay2D(Collision2D collision2D, object coll)
{
throw new NotImplementedException();
}
}
There are a few things I can see that are wrong, but I'll start by answering your question.
I suggest you change your MonoBehaviour method OnCollisionStay2D to OnCollisionEnter2D. OnCollisionStay2D is "sent each frame where a collider on another object is touching this object's collider". OnCollisionEnter2D is "sent when an incoming collider makes contact with this object's collider".
I believe you are looking for the latter since you only want to trigger this once during the collision. You are also destroying the other object, making it impossible to call OnCollisionStay2D anymore even if you wanted to do so.
You should also remove your Update method. I honestly do not understand what you are trying to achieve there now. All of the OnCollision methods get called automatically; you do not have to call them yourself.
Then you can use the Awake and OnCollisionEnter2D methods as follows
public class Succ : MonoBehaviour
{
private Animator animator;
private void Awake()
{
// You can already get a reference to the Animator on Awake
// This way you do not have to do it on every collision
animator = GetComponent<Animator>();
}
// Use OnCollisionEnter2D instead since the code
// needs to be excecuted only once during the collision
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("succ")
{
// Assuming that you only want to trigger an animation once
// to reflect attacking or colliding, you could use SetTrigger
// instead. Otherwise you need to use SetBool again to set it
// back to false. You should then change the Animator parameter
// accordingly, from a bool to a trigger.
animator.SetTrigger("succ");
Destroy(collision.gameObject);
}
}
}
Apart from this, I have a few things I would like to comment on:
I am not sure what you are trying to achieve by setting your ConstantForce2D component to false on Start and then setting it to true on collision.
You seem to be using GameObject.Find on Start. GameObject.Find is something that should be very rarely used. It can be extremely expensive, especially if your Scene has a lot of GameObjects in it; this is because it simply goes through the Hiearchy, comparing the parameter string to names of GameObjects until it either finds a match or runs out of GameObjects.
Moreover, you are using GameObject.Find on Start to look for a GameObject, but then you do not store that anywhere, making the whole finding process completely pointless.
Overall, I recommend you to take a look at all of the different learning resources offered by Unity themselves. Your question is about fairly basic functionality that is certainly covered during all of the different tutorials.
using UnityEngine;
using System.Collections;
public class chicken_for : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
if (collision.gameObject.Quad == collisionObject){
Application.LoadLevel("SciFi Level");
}
}
}
I am attempting to when A person touches a quad, he goes to this sci-fi fortress. It however says the name 'collision' does not exist in the current context.
You're referencing a variable (collision) that doesn't exist. You've got a variable collisionObject that doesn't exist, too.
collision is typically the name of the argument to the OnCollisionEnter method. Your code should probably be inside this method instead of FixedUpdate. I'm guessing that you've copied code from a tutorial somewhere but put it in the wrong method.
collisionObject on the other hand is harder to guess, but I expect that if your script is intended to be a component on the player object, then collisionObject should be your quad; if the script is on the quad then collisionObject should be the player.
Either way, you need to declare that variable - probably as a public field so that you can populate it from the inspector.
Add a new function and make sure the player is tagged as player in the inspector. You also need to make sure that there is a collider on the quad that the player touches and a rigidbody component on the player.
using UnityEngine;
using System.Collections;
public class chicken_for : MonoBehaviour {
//This function will handle the collision on your object
void OnCollisionEnter (Collider col){
if(col.gameobject.tag == "Player"){ //if colliding object if tagged player
Application.LoadLevel("SciFi Level"); //load the sci-fi level
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
This should get you roughly where you want to be!