Collision with enemy to inflict damage - c#

I'm having some problems with Unity. I'm trying to make it so that if an enemy collides with the player, the player loses a health point. My C# code is below.
before you look at the code, I wanted to say that the enemies are rigid bodies so that the object bullets can affect them. I made an extra capsule to be a part of the player body that can be a rigid body so that the code can detect the collision. Do you think that would work? I'm unsure if it's easier for a rigid body to detect another rigid-body collision or if it doesn't care.
public class playerhealth : MonoBehaviour {
private int curHealth;
private int playerLives;
public GUIText winText;
public GUIText healthText;
public GUIText livesText;
void Start() {
curHealth = 3;
playerLives = 3;
SetHealthText();
SetLivesText();
winText.text = "";
}
void FixedUpdate()
{
// where physics codes go
}
// HERE'S WHERE THE COLLISIONS STUFF IS
void OnCollisionEnter(Collider rigidbody) {
if (rigidbody.gameObject.tag == "Enemy") {
curHealth = curHealth - 1;
SetHealthText();
}
if (rigidbody.gameObject.tag == "reloader") {
playerLives = playerLives - 1;
SetLivesText();
}
}
// setting GUI TEXT and reloading level
void SetHealthText() {
healthText.text = "Health Points: " + curHealth.ToString();
if (curHealth <= 0) {
Application.LoadLevel("shootingworld");
playerLives = playerLives - 1;
}
if(curHealth >= 10) {
playerLives+= 1;
}
}
void SetLivesText() {
livesText.text = "Lives: " + playerLives.ToString();
if (playerLives <= 0) {
winText.text = "GAME OVER";
}
}
}

You're making a number of assumptions here, some of which are wrong. I'll try to point them out.
Adding a RigidBody to a gameobject is the right idea, but it's the Collider component that determines the shape and size of the object's collision. Consider adding a BoxCollider, SphereCollider or CapsuleCollider to both.
I assume you're having trouble getting the objects to actually collide, this may be the solution.
Also,
void OnCollisionEnter(Collider rigidbody){
The parameter you've named 'rigidbody' is not guaranteed to be a RigidBody component. According to documentation
The Collision class contains information about contact points, impact velocity etc.
The proper syntax for OnCollisionEnter has a Collision parameter, not a Collider.
To access the rigidbody on the Collider, you'd have to use getcomponent on the object found by the Collider and check if the RigidBody component exists. I'm not sure this is what you're after, but the misleading parameter name should be checked.
Anyway you've got the right idea regarding comparing a Collider's gameobject by tag. All you need to do is enforce the tag on the object, either in the editor or through code.

You are using this:
void OnCollisionEnter(Collider collision) {
}
Collider is used for Ontrigger...
Try this:
void OnCollisionEnter(Collision collision) {
}
Hope this help ! :)

You try to this may help full also
void OnCollisionEnter(Collision collision)
{
}
Documentation is :
http://docs.unity3d.com/Documentation/ScriptReference/Collider.OnCollisionEnter.html

Related

Unity-3D, Enemy using Raycast and NavMesh keeps seeing me through walls

I've been trying to set an enemy on a patrol path while not chasing my player character. I want to use RayCast so that the enemy can spot the player and begin to chase the player. It functions as I intended. However, even when there's an obstacle or wall between us, or when I approach from behind, the enemy 'sees' my player and starts to chase me. It seems to ignore the Raycast, and instead focuses on the proximity to the enemy.
Enemy spotting player through wall
public class EnemyController : MonoBehaviour
{
private NavMeshAgent enemy;// assaign navmesh agent
private Transform playerTarget;// reference to player's position
private float attackRadius = 10.0f; // radius where enemy will spot player
public Transform[] destinationPoints;// array of points for enemy to patrol
private int currentDestination;// reference to current position
public bool canSeePlayer = false;
private Ray enemyEyes;
public RaycastHit hitData;
private void Awake()
{
enemy = GetComponent<NavMeshAgent>();
playerTarget = GameObject.Find("Player").GetComponent<Transform>();
enemyEyes = new Ray(transform.position, transform.forward);
}
private void Start()
{
Physics.Raycast(enemyEyes, attackRadius);
}
private void Update()
{
Lurk();
Debug.DrawRay(transform.position, transform.forward * attackRadius);
}
void Lurk()
{
Debug.Log("Lurking");
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
//check if raycast hits playerLayer and enemy is close enough to attack
if (Physics.Raycast(enemyEyes, out hitData, attackRadius * 2, layerMask: ~6) && distanceToPlayer < attackRadius)
{
Debug.Log("You hit " + hitData.collider.gameObject.name);
ChasePlayer();
}
else
{
canSeePlayer = false;
Patrol();
}
}
void Patrol()
{
if (!canSeePlayer && enemy.remainingDistance < 0.5f)
{
enemy.destination = destinationPoints[currentDestination].position;
UpdateCurrentPoint();
}
}
void UpdateCurrentPoint()
{
if (currentDestination == destinationPoints.Length - 1)
{
currentDestination = 0;
}
else
{
currentDestination++;
}
}
void ChasePlayer()
{
StartCoroutine(ChaseTime());
canSeePlayer = true;
transform.LookAt(playerTarget.position);
Vector3 moveTo = Vector3.MoveTowards(transform.position, playerTarget.position, attackRadius);
enemy.SetDestination(moveTo);
}
IEnumerator ChaseTime()
{
Debug.Log("Chasing");
yield return new WaitForSeconds(10.0f);
if (!Physics.Raycast(enemyEyes, out hitData, attackRadius * 2, layerMask: ~6))
{
canSeePlayer = false;
Debug.Log("Lurking");
Lurk();
}
}
}
I've removed the tilde "~" for the layermask, but then the enemy doesn't ever see the player.
I've initialised and set a layer mask reference to the 'playerLayer' and used it in place of "layermask: ~6", to no avail.
I've used the int reference to the Player layer, to no avail.
I've used bitwise operator to reference the player layer, to no avail.
I've removed the distanceToPlayer conditional, but the enemy doesn't see the player.
I've adjusted the length of the Ray but if it's ever shorter than the attack radius, it doesn't work.
I don't understand why you need a layer mask. If you want the Raycast to hit something in front of the player then you must include all layers in the Raycast.
What you need to do is remove the layermask from Raycast and in the if statement check if the out hit collider is on Player and if the player is in attack radius. Here is a simple outline
If(raycast)
{
if(hit.collider.gameobject==player && player in proximity)
{
Then can see is true
}
}
You can give this article on Unity Raycast a read to understand more on layermask.
regarding player detection through a wall,I would try adding an extra if statement in the Lurk method.This condition would check if the raycast is touching the wall, if so, do not proceed with the method, if not, continue. Sorry for giving the code in this form but I don't have access to a computer and the phone doesn't want to cooperate
enter image description here

How can I detect when multiple rigid body velocities are under 0.1?

I am developing an 8 ball pool/billiards game in which I want to find out when all the balls on the table stop.
I tried using this piece of code however the number of balls that have stopped keeps on incrementing to massive numbers when the value should be 16 or less.
IEnumerator CheckObjectsHaveStopped()
{
bool allSleeping = false;
Rigidbody[] GOS = FindObjectsOfType(typeof(Rigidbody)) as Rigidbody[];
while (!allSleeping)
{
allSleeping = true;
foreach (Rigidbody GO in GOS)
{
if (GO.velocity.magnitude <= 0.1)
{
Balls_Stopped += 1;
Debug.Log("Balls Stopped = " + Balls_Stopped);
yield return null;
}
}
}
if (Balls_Stopped == Balls_Left)
{
print("All objects sleeping");
//Do something here
}
}
Depending on your implementation requirements, it may be easier to treat each Ball as their own object. Then you can add a method/property to the Ball object such as "IsMoving".
I also recommend either serializing these ball objects in the inspector, or using FindObjectsOfType once at the beginning of your game so it is not impacting the performance of your game.
For example:
//Ball.cs
[RequireComponent(typeof(Rigidbody))]
public class Ball : MonoBehaviour {
private Rigidbody rigidbody;
public bool IsMoving => rigidbody.velocity.magnitude > 0.1f;
private void Awake() {
rigidbody = GetComponent<Rigidbody>();
}
}
//Ball managing script
public class BallManagerScript : MonoBehaviour {
private Ball[] balls;
private void Awake() {
balls = FindObjectsOfType<Ball>();
}
public bool CheckIfAnyBallsAreMoving() {
foreach (Ball ball in balls) {
if (ball.IsMoving) return true;
}
return false;
}
}
This will allow you to have an array of Balls, which you can cycle through and check if each independent one is moving. From there, use that function to see if any of the balls are moving.
Make sure to add the Ball script to all balls in your game, so they can be referenced and checked.
using [RequireComponent(typeof(Rigidbody))]will ensure that each GameObject that Ball is attached too, will have a Rigidbody (because your script has a depdendency of Rigidbody)
Using LINQ you could do:
using System.Linq;
...
var allStopped = GOS.All(r => r.velocity.magnitude <= 0.1f);

Setting Up Skill in Unity

These past month+ I learned many things by making a game in Unity.I have a lot of fun doing so. But some thing are still confusing me. I'm trying to setup a skill to the character and it goes almost well. When the character is casting the skill, the skill goes behind the character and not in front. So i thought to play with positions and rotations to make it work but still nothing. Worth to mention that the prefab has it's own motion. So my code so far is this. So help would be great and some teaching about the logic behind the skills system would be much appreciated. So take a look:
using UnityEngine;
public class MagicSkill : MonoBehaviour
{
public GameObject hand; // Players right hand
public GameObject fireballPrefab; // Fireball particle
Vector3 fireballPos; // Adding the transform.position from the players hand
Quaternion fireballRot; // Adding the rotation of the players hand
private bool isPressed = false; //Skill button (mobile)
public Animator animator; // Casting spell animation
void Update()
{
fireballPos = hand.transform.position; // Getting the position of the hand for Instatiating
fireballRot.x = hand.transform.rotation.x; // Getting the rotation of the hand for x Axis
fireballRot.y = hand.transform.rotation.y; // Getting the rotation of the hand for y Axis (Here i try to modify the values but still nothing)
fireballRot.z = hand.transform.rotation.z; // Getting the rotation of the hand for z Axis
if (isPressed == true)
{
animator.SetBool("Magic", true);
if (hand.transform.position.y >= 20) // Here I got the exact position of the hand for when to
Instatiate the skill
{
for (int i = 0; i < 1; i++) // For some reason it instatiates too many prefabs (I think it's because it's in Update method)
{
Instantiate(fireballPrefab, fireballPos, Quaternion.Euler(fireballRot.x, fireballRot.y, fireballRot.z));
Invoke("Update", 2.0f); // I'm trying to slow down the pressed button so that it want spawn every frame
}
}
}
else
{
animator.SetBool("Magic", false);
}
}
public void MagicSkills()
{
if (isPressed == false)
{
isPressed = true;
}
else
{
isPressed = false;
}
}
}
After some playing around with the code I managed to find a solution.Here I post the working code for me at least.Maybe it will help someone else too.For this to work properly you must remove the Event Trigger OnPointerUp from your button.Many thanks for the help Guilherme Schaidhauer Castro
using UnityEngine;
public class MagicSkill : MonoBehaviour
{
public GameObject hand; // Players right hand
public GameObject fireballPrefab; // Fireball particle
public Animator animator; // Casting spell animation
Vector3 fireballPos; // Adding the transform.position from the players hand
private bool isPressed = false; //Skill button (mobile)
void Update()
{
fireballPos = hand.transform.position; // Getting the position of the hand for Instatiating
if (isPressed == true)
{
animator.SetBool("Magic", true);
if (hand.transform.position.y >= 20) // Here I got the exact position of the hand for when to Instatiate the skill
{
Instantiate(fireballPrefab, fireballPos, Quaternion.identity);
isPressed = false;
}
}
else
{
animator.SetBool("Magic", false);
}
}
public void MagicSkills()
{
if (isPressed == false)
{
isPressed = true;
}
else
{
isPressed = false;
}
}
}
Keep a reference to the character's transform and use that transform to instantiate the fireball instead of using the rotation from the hand. The rotation of the hand is not changing in relation to the body, so that's why the ball is always going in the same direction.

Collision working, particle fx not so much C#

I'll keep this brief and a bit short, but I currently have a particle system that seems to not be rendering even though my collision works.
I have trouble understanding other peoples work so I have not been able to find a solution I can understand.
Here is my code:
public float speed;
public Rigidbody rb;
public int health;
private float knockback;
private float knockup;
public ParticleSystem Eparticle; //*** variable for particle system ***
// Use this for initialization
void Start()
{
rb = GetComponent <Rigidbody>();
knockback = 100f;
knockup = 250f;
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "enemy")
{
health = health - 20;
rb.AddRelativeForce(Vector3.back * knockback);
rb.AddRelativeForce(Vector3.up * knockup);
Destroy(col.gameObject);
Instantiate(Eparticle);
}
if (col.gameObject.name == "endgoal")
{
SceneManager.LoadScene("level 1");
}
}
What am I doing wrong with my instantiate(Eparticle) line?
Could someone please talk me through a solution?
Thank You :)
You should invoke the Instantiate method at the position where you want the particle prefab to appear.
You could do something like this...
Instantiate(Eparticle,transform.position,transform.rotation);
Actually, you can also create (instantiate) a GameObject at runtime as follows...
GameObject obj= Instantiate(Eparticle,transform.position,transfrom.rotation) as GameObject;
This way, you have some sort of 'control' over the instantiated gameobject.
For instance, you can destroy the object after using it by calling the Destroy() method.
E.g.:
Destroy(obj,2f);//Destroys the created object after 2 seconds.
Of course, this is not a good way to go about it if you are going to be instantiating and destroying a lot of objects. You should read about Object Pooling for this purpose.

OnTriggerEnter called multiple times

This counter would randomly increment. It's driving me crazy.
When the player picks up all the game objects on the level (10), the level should restarts again.
That works, however, randomly with picking up objects it can either add +1 to the score, which is expected, or +2.
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Pickup : MonoBehaviour {
//Required Variables
public GameObject pointsObject;
public Text scoreText;
private int score;
private int scoreCount;
void Start()
{
score = 0;
SetScoreText();
scoreCount = GameObject.FindGameObjectsWithTag("Pickups").Length;
}
void Update()
{
if(score >= scoreCount)
{
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name);
}
SetScoreText();
Debug.Log("Found " + scoreCount + " Pickup Objects!");
}
void OnTriggerEnter(Collider col)
{
if(col.gameObject.CompareTag("Pickups"))
{
Destroy(col.gameObject);
score++;
}
}
void SetScoreText()
{
scoreText.text = "Score: " + score.ToString();
}
}
Can anyone see why I'm having this issue, as I can't see the reason why at all. Thanks in advance.
Not to revive a dead thread, but I ran into this issue recently. Bullets would count twice when triggered. Found out it was because both object had colliders with OnTrigger enabled. I placed the enemyHitbox collider on a separate layer (to not collide with anything else) and set it to OnTrigger false, while the playerBullet was set to OnTrigger true. This resolved the issue for me.
I'm assuming that both objects somehow caused a trigger event even though it was set for one hitting another with a set tag.
Hope this helps someone with a similar issue.
Using Unity2020 at this point btw.
This would have been a case of attaching the Pickup script to multiple GameObjects but I don't think so because the Pickup script would then have multiple instances and the score wont be incremented as mentioned in your question.
Since that is eliminated, it is very likely that you have more than 1 colliders (whether trigger or not) attached the GameObjects. This causes the OnTriggerEnter function to be called multiple times.
You have 2 solutions:
1.Find the duplicated colliders and remove them from the GameObjects. Check this on the Pickups GameObjects and other GameObjects they collider with.
2.Modify your code and make it understand there is a duplicated collider.
You can do this by making a list to store each of those 10 Pickups each time the OnTriggerEnter function is called. Check if collided GameObject exist in the List. If it exist in the List, don't increment. If it does not exist in the List, increment then add it to the List. This is very simple and easy logic to implement. Not tested but should work.
The code below is what it should look like if you go with solution #2.
public class Pickup : MonoBehaviour
{
//Required Variables
public GameObject pointsObject;
public Text scoreText;
private int score;
private int scoreCount;
List<GameObject> pickups = new List<GameObject>();
void Start()
{
score = 0;
SetScoreText();
scoreCount = GameObject.FindGameObjectsWithTag("Pickups").Length;
}
void Update()
{
if (score >= scoreCount)
{
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name);
}
SetScoreText();
Debug.Log("Found " + scoreCount + " Pickup Objects!");
}
void OnTriggerEnter(Collider col)
{
if (col.gameObject.CompareTag("Pickups"))
{
//Increment if the gameObject is [not] already in the List
if (!pickups.Contains(col.gameObject))
{
Destroy(col.gameObject);
score++;
//Now Add it to the List
pickups.Add(col.gameObject);
}
}
}
//Call this after GameOver to clear List
void resetPickupsList()
{
pickups.Clear();
}
void SetScoreText()
{
scoreText.text = "Score: " + score.ToString();
}
}

Categories

Resources