I got a script like this to create a raycast from maincamera and hit the enemy.
void FixedUpdate(){
if (fire) {
fire = false;
RaycastHit hit;
if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit, range)){
if (Enemy.distance < 80) {
if (hit.collider.tag == "body") {
Debug.Log ("Bullet in the body.");
Enemy.bodyshot = true; //Changing other script variable.
} else if (hit.collider.tag == "head") {
Debug.Log ("Bullet in the head.");
Enemy.headshot = true; //Changing other script variable.
}
}
}
}
}
Enemy Script in Update;
if (headshot) { //headshot variable is static to reach from other script.
anim.SetTrigger ("isDying");
speed = 0;
death = true;
}
if (bodyshot) { //bodyshot variable is static to reach from other script.
anim.SetTrigger ("isSore");
}
So, when I shoot an enemy, all enemies are dying at the same time. Because these scripts are attached to all enemies. I need to change bodyshot and headshot variables without using static. What can I do to separate them ?
When it comes to raycast, you only need the raycast script attached to an empty GameObject. It does not have to be attached to each individual Object that you want to perform the raycast on.
The only time you have to attach script to a GameObject you want to detect clicks on is when using the Unity EventSystem to detect clicks on the objects.
So, remove this script from other GameObjects and just attach it to one GameObject.
Note:
If you want to access or do something to the GameObject that the ray just hit, the GameObject is stored in the hit variable.
RaycastHit hit;...
Destroy(hit.collider.gameObject);
You can also access scripts attached to the Object hit:
hit.collider.gameObject.GetComponent<YourComponent>().doSomething();
Instead of checking to see what the tag of the collider is, you can first check to make sure the collider is one of the colliders attached to your object, and then check the tag for body location. For example:
public Collider[] coll;
void Start() {
coll = GetComponents<Collider>();
}
void FixedUpdate(){
if (fire) {
fire = false;
RaycastHit hit;
if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit, range)){
if (Enemy.distance < 80) {
if (hit.collider == coll[0] || hit.collider == coll[1]) {
if (hit.collider.tag == "head"){
or to simplify you can make coll[0] the head and [1] the body and ignore checking for a tag.
edit: As Programmer mentioned, this is not an efficient way to do this since you will be casting a ray for every object that has this script and you really only want to be casting a single ray.
Related
I have a GameObject which when picked up and collides with a certain collider(detect) then it should drop the object and proceed to perform a specific event (Pouring a liquid) inside the collider.
The problem i have is that when on collision, the gameobject is not able to stay inside the collider. It gets thrown out when on Collision enter.
Both the collider and the picked up gameobject have rigidbodies because for the picked gameobject the isKinematic is set to true due to the liquid it holds/carries. Therefore for an OnCollisionEnter event to happen one of the colliders should have a non kinematic rigidbody as from the Unity Doc.
I tried changing the project settings from the phyiscs section to Enable contacts and see if the rigidbody maybe the cause, this is after removing a rigidbody from the detect collider but it was still a dead end. This came about from my research using this forum in unity.
So the question is what should i do to make the gameobject perform the event while inside the collider?
Does the Rigidbodies from the two colliders (picked gameobject and detect) make this not happen?
This is what the object should do on Collision enter!
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("FunnelPour"))
{
Debug.Log("Ready To Pour!");
if (interactor.currentlyPickedUpObject != null)
{
interactor.DropObject();
}
rb.useGravity = false;
StartCoroutine(startPour());
}
}
The action or event on how the object should work is under the PourFunnel method.
void PourFunnel()
{
RaycastHit raycastHit;
if (Physics.Raycast(transform.position, transform.forward, out raycastHit, distanceOfRaycast, detectPour))
{
Debug.Log("Pour the beaker to left");
isPouring = true;
transform.position = positionToPourLeft;
rb.constraints &= ~RigidbodyConstraints.FreezeRotationX;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(leftRotationAmount, transform.right), Time.deltaTime * 50);
}
else if (Physics.Raycast(transform.position, -transform.forward, out raycastHit, distanceOfRaycast, detectPour))
{
Debug.Log("Pour the beaker to Right");
isPouring = true;
transform.position = positionToPourRight;
rb.constraints &= ~RigidbodyConstraints.FreezeRotationX;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(rightRotationAmount, transform.right), Time.deltaTime * 50);
}
}
IEnumerator startPour()
{
yield return new WaitForSeconds(0);
Debug.Log("Pour the beaker!");
PourFunnel();
yield return new WaitForSeconds(timeToResume);
Debug.Log("Position the beaker to normal!");
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.identity, Time.deltaTime * 100);
yield return new WaitForSeconds(0.5f);
rb.constraints = RigidbodyConstraints.FreezeRotation;
isPouring = false;
rb.useGravity = true;
Debug.Log("Position the Beaker back to where it was picked up!");
transform.position = positionToDrop;
}
It sounds a bit like you would want to use the collider as a trigger (tick on IsTrigger) and then use the function OnTriggerEnter in stead of OnCollisionEnter.
When two objects hit each other (their colliders enter each other) AND none of the colliders are marked as triggers AND at least one of the objects have a (non-kinematic) RigidBody attached, we will get a 'collision'. This collision will trigger OnCollisionEnter but it will also make Unity's physics engine see this as a physical collision and move the involved non-kinematic RigidBodies according to the collision details. This is why your game object gets 'thrown' away when you collide with it.
If two objects hit each other AND at least one of their colliders is marked as trigger AND at least one of the objects has a RigidBody attached (can be kinematic or not) then the function OnTriggerEnter will be triggered. When one of the involved colliders is marked as trigger, Unity will not see it as a physical collision and they are free to move inside each other without the physics engine kicking in.
hey guys I am trying to destroy a specific object coming towards me when touch it just like guitar hero.
my code right now just destroy wherever I touch
I am new to coding so I appreciate a basic explanations thx you
{
private float forcemult = 200;
private Rigidbody rb;
Ray ray;
RaycastHit hit;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.AddForce(transform.forward * forcemult * Time.deltaTime);
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
}
if (Physics.Raycast(ray, out hit ))
{
Debug.Log("hit something");
Destroy(hit.transform.gameObject);
}
else
{
if (Input.touchCount > 0)
{
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
}
}
You can give your gameobject a specific tag and then check if the gameobject you hitted has this tag.
Something like:
if(hit.collider.tag == "Enemy") {
// Do Stuff
}
This is the simplest way to check for a collision with a specific gameobject.
You can also check if the name of the gameobject you hitted has the name you want for example:
if(hit.collider.gameobject.name == "Enemy01") {
// Do Stuff
}
I personally don't like the name checking variant, because it only works reliably in specific situations.
Another method you can use is attaching a script to the gameobject and then check if the gameobject has this script:
YourScript script = hit.collider.gameobject.GetComponent<YourScript>();
if(script != null) {
// Do stuff
}
This is probably the best way of handling this because you can instantly call an event after the collision happened.
The part of the code that is responisble for detecting objects is if (Physics.Raycast(ray, out hit )). You can add two additional inputs to this method to destory the objects you desire. The first input is maxDistance which is the max distance the ray should check for collisions, the second one is layerMask which is a Layer mask that is used to selectively ignore colliders when casting a ray.
Define a new layer in unity and assign it to the objects you wish to desotry.
click on edit layers:
Define a serialized field of type layerMask and assign the layer (Target in my case) you defined in the inspector:
// assign in the inspector
[SerializeField] private LayerMask toHit;
// your code
//..
// you can define the hit variable in the if statement if you
// don't need it somewhere else using the syntax I have used,
// otherwise remove the var keyword, the 100 is the maxDistance value,
// use any value you wish. toHit determines what physic engine selects
// and what ingores.
if(Physics.Raycast(ray, out var hit, 100, toHit))
{
Debug.Log($"hit: {hit.transform.name}");
Destroy(hit.transform.gameObject);
}
(Unity Doc)
I am working on a simple game where the goal is to help a Player catch specific objects with a tag "Present".
After taking care of the models, animations I am now working on the collisions and counting UI.
For the collisions, on my Player Controller (I am using the ThirdPersonUserController from the player Unity Standard Assets - which simplified the whole animation process), I added the following functions:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count == 0)
{
winText.text = "Thanks to you the Christmas will be memorable!";
}
}
However, like this, when the Player hits an object with the tag "Present", even though the counting is working properly, the player disappears.
I have tried to change the OnCollisionEnter to an OnTriggerEnter, like this:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
However when the Player hits the objects with the tag "Present", they don't disappear.
My Player has a Capsule Collider and a Rigidbody.
The objects with the tag "Present" have a Box Collider and a Rigidbody.
Any guidance on how to make my Player stay in scene while removing the other objetcs and reducing the count is appreciated.
Few things. You are destroying the incorrect game object:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject); // this is destroying the current gameobject i.e. the player
count = count - 1;
SetCountText();
}
}
Update to:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject); // this is destroying the other gameobject
count = count - 1;
SetCountText();
}
}
Always use CompareTag() which is optimized for performance.
Setting the Collider IsTrigger property will then make use of the OnTriggerEnter events and not the OnCollisionEnter anymore.
On the first physics update where the collision is detected, the
OnCollisionEnter function is called. During updates where contact is
maintained, OnCollisionStay is called and finally, OnCollisionExit
indicates that contact has been broken. Trigger colliders call the
analogous OnTriggerEnter, OnTriggerStay and OnTriggerExit functions.
See the docs
Your present object is using a non-trigger collider, so you should use OnCollisionEnter. The CompareTag call you tried in OnTriggerEnter is preferable. You should use that.
Also, you are currently trying to destroy the gameobject that the ThirdPersonUserController is attached to (gameObject). Instead, destroy the gameobject of the colliding collider (other.gameObject) with Destroy(other.gameObject);:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
I have a gameobject called "Target" which contains the animator controller, I have a capsule collider on a gameObject in the tree for Target called Robot. The current script I have is attached to a gun object and is as follows and destroys the object and it disappears when the raycast collides with the collider, and prints out the tag and name of the collider it hit(just for testing purposes):
RaycastHit hit;
if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit, range)) {
if (hit.transform.tag == "Target") {
print(hit.transform.gameObject.name);
print(hit.transform.gameObject.tag);
hit.transform.gameObject.GetComponent<Animator>().SetBool("isHit", true);
Destroy(hit.transform.gameObject);
target.getTargets = GameObject.FindGameObjectsWithTag ("Target");
target.targetCount = target.getTargets.Length -1;
target.countText.text = (target.targetCount ).ToString ();
}
GameObject impact = Instantiate (impactEffect, hit.point, Quaternion.LookRotation (hit.normal));
Destroy (impact, 1f);
}
}
However when i active the animator controller the raycast no longer seems to hit the collider at all, the name nor tag is printed out. I have a isHit variable in the animator that is supposed to play a death animation when set to true and then disappear as it should when no animator is present but I'm not sure how to access the animator on the main object from the child object that has the collider.
It is hard to say why your code does not work or print out the values you are looking for.
Try debugging it and see where it ends up.
However your question on how to access the animator on the main object. Add root to your line.
hit.transform.root.GetComponentInChildren<Animator>().SetBool("isHit", true);
I removed the ".gameObject" because it is not needed. Also GetComponentInChildren will find the first occurence of Animator starting at the root.
I am trying to recreate a simple laser puzzle mechanic like seen in The Talos Principle - where i have a laser emitter that i can move and rotate, and when the beam (raycast and LineRenderer) hits a specific object, that object will become "active". However when the object is no longer being hit by a laser it should "deactivate".
I am having troubles with the Deactivate part. Is there a way to tell the object that it is no longer being hit by a raycast, or add a collider to the LineRenderer maybe? Or some third way to tell the object that it is no longer being hit by any lasers.
When your target is hit by a raycast, you could use the RaycastHit reference to acquire a script and update the cooldown.
Lets say we have RaySender and RayReceiver.
RaySenderScript
public class RaySenderScript{
RaycastHit Hit;
void FixedUpdate(){
//SendRaycast and store in 'Hit'.
if (Hit.collider != null)
{ //If raycast hit a collider, attempt to acquire its receiver script.
RayReceiverScript = Hit.collider.gameObject.GetComponent<RayReceiverScript>();
if (RayReceiverScript != null)
{ //if receiver script acquired, hit it.
RayReceiverScript.HitWithRay();
}
}
}
}
RayReceiverScript
public class RayReceiverScript{
public float HitByRayRefreshTime = 1f;
float RayRunsOutTime;
public bool IsHitByRay = false;
void Start()
{ //Initialize run out time.
RayRunsOut = Time.time;
}
void Update()
{
if (Time.time > RayRunsOutTime)
{ //check if time run out, if it has, no longer being hit by ray.
IsHitByRay = false;
}
}
public void HitWithRay(){ //method activated by ray sender when hitting this target.
IsHitByRay = true;
RayRunsOutTime = Time.time + HitByRayRefreshTime;
}
}
Sender strikes Receiver with a ray.
Sender has a reference to Receiver, it uses GetComponent() to access it. It can then say receiverScript.HitWithRay();
Receiver keeps checking if it no longer is receiving, if it isnt, it stops being hit by ray.
Add all objects hit by laser to collection and check if current target is in this collection. If it is not there, then it is "deactivated".