Contact point between OverlapCircle and certain collider in Unity - c#

I am trying to get the contact point between a Collider and OverlapCircleAll to play an animation on that point.
This is the method I am using for the attack.
private IEnumerator BasicAttackBehaviour()
{
canAttack = false;
Collider2D[] enemiesToDamage = Physics2D.OverlapCircleAll(attackPos, attackRange, whatIsEnemy);
for (int i = 0; i < enemiesToDamage.Length; i++)
{
enemiesToDamage[i].GetComponentInParent<Enemy>().TakeDamage(damage);
}
PlayAttackAnimation();
yield return new WaitForSeconds(attackDelay);
canAttack = true;
}
And this is the "TakenDamage" method:
public void TakeDamage(int damage)
{
health -= damage;
GameObject bloodEffect = Instantiate(takenDamageVFX, transform.position, transform.rotation);
}
I want to instantiate the VFX "bloodEffect" on the position the enemy is hitten instead of "transform.position".

You may want to switch to CircleCastAll, it will give you contact points in world space coordinates:
https://docs.unity3d.com/ScriptReference/Physics2D.CircleCastAll.html
You can set the distance parameter to 0.0f if you don't want to do a moving circle and just a single non-moving circle.

Answering to my question and thanking to the boss whom resolved my question above, here goes my fix just in case someone needs to visualise the example.
private IEnumerator BasicAttackBehaviour2()
{
canAttack = false;
RaycastHit2D[] raycastHits = Physics2D.CircleCastAll(attackPos, attackRange, directionLookingAt, 0, whatIsEnemies);
for(int i = 0; i < raycastHits.Length; i++)
{
Vector2 pointHitten = raycastHits[i].point;
raycastHits[i].collider.gameObject.GetComponent<Enemy>().TakeDamage(damage, pointHitten);
}
PlayAttackAnimation();
yield return new WaitForSeconds(attackDelay);
canAttack = true;
}
Using CircleCastAll I am able to retrieve the point of the RaycastHit2D and pass it to the enemy.
And then, I can display the blood effect on the right position of the impact.
public void TakeDamage(int damage, Vector2 pointHitten)
{
health -= damage;
GameObject bloodEffect = Instantiate(takenDamageVFX, pointHitten, transform.rotation);
}

Related

Checking if RigidBody is grounded?

I have a GameObject with a Rigidbody, Mesh Collider, Skinned Mesh Renderer, and the below script.
I'm trying to check if it is Grounded, but the Console continuously spits out "Not Grounded!" when it is, so obviously something is wrong here. Can anyone please help?
public class GroundCheck : MonoBehaviour
{
public float Height;
bool IsGrounded;
Ray ray;
MeshRenderer renda;
private void Start()
{
Height = renda.bounds.size.y;
}
void Update()
{
if (Physics.Raycast(transform.position, Vector3.down, Height))
{
IsGrounded = true;
Debug.Log("Grounded");
}
else
{
IsGrounded = false;
Debug.Log("Not Grounded!");
}
}
}
Another option for checking if the rigidBody is grounded is using the OnTriggerStay function.
void OnTriggerStay(Collider other)
{
if (other.Transform.Tag == "Ground")
{
IsGrounded = true;
Debug.Log("Grounded");
}
else
{
IsGrounded = false;
Debug.Log("Not Grounded!");
}
}
I've checked your code with a simple scene with a plane and a cube, and it works.
It only spawns NotGrounded when it's clearly "floating" arround or the object has the half of it's body outside the plane.
Check those things drawing the Ray, this should give your more information about what is going wrong with your mesh.
Also if the problem is how the game is perceiving the Height of your Skinned Mesh you can also use SkinnedMeshRenderer.localBounds who returns the AABB of the object.
There is a better way to check if your rigidbody is grounded than collision checking and rays. But first, why is collision checking not a good idea: If your level is a single model the walls will too be tagged with the "Ground" tag and hitting a wall will return true, which you don't want. Rays can be used, but that's too much math you don't need to waste time with.
Instead of using new objects and methods, just check if the position is changing:
private float lastYposition;
private bool grounded;
void Start() {
lastYposition = transform.position.y;
}
void Update() {
grounded = (lastYposition == transform.position.y); // Checks if Y has changed since last frame
lastYposition = transform.position.y;
}
From here it's up to you what logic you are going to add.
Get the closest contact point to the bottom. Check if the contact normal is within range.
void OnCollisionStay(Collision collision)
{
var bottom = renderer.bounds.center;
bottom.y -= renderer.bounds.extents.y;
float minDist = float.PositiveInfinity;
float angle = 180f;
// Find closest point to bottom.
for (int i = 0; i < collision.contactCount; i++)
{
var contact = collision.GetContact(i);
var tempDist = Vector3.Distance(contact.point, bottom);
if(tempDist < minDist)
{
minDist = tempDist;
// Check how close the contact normal is to our up vector.
angle = Vector3.Angle(transform.up, contact.normal);
}
}
// Check if the angle is too steep.
if (angle <= 45f) IsGrounded = true;
else IsGrounded = false;
}
void OnCollisionExit(Collision collision)
{
IsGrounded = false;
}
I came up with this answer after reading amitklein's answer.
private void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (transform.position.y == 0f)
{
rb.AddRelativeForce(Vector3.up * jumpForce);
}
}
}
I used this approach to jump using addrelativeforce of rigid body only when the position of y in 0.

issue with unity navimesh and my builded ai code

firstly i'm sorry if sometimes my English will be a bit incomprehensible, i'll try to do my best.
Actually i'm trying to build a simple AI system with unity Navimesh Component, my goal is :
NPC will check witch player is nearest to it's position and go there to kill him.
if target player is facing me run away
my actual code is working but with some issues
private Transform player;
private UnityEngine.AI.NavMeshAgent myNMagent;
private Transform startTransform;
public float multiplyBy;
public Vector3 resultedVector;
// Use this for initialization
void Start () {
player = GameObject.FindGameObjectWithTag ("Player").transform;
myNMagent = this.GetComponent<UnityEngine.AI.NavMeshAgent> ();
RunFrom ();
}
// Update is called once per frame
void Update () {
//if is not facing got to eat him
if(!ISFacing())
{
GotToEat();
}
else
{
RunFrom ();
}
}
public void RunFrom()
{
startTransform = transform;
transform.rotation = Quaternion.LookRotation(transform.position - player.position);
Vector3 runTo = transform.position + transform.forward * multiplyBy;
NavMeshHit hit;
if(NavMesh.SamplePosition(runTo, out hit, 50, 1 << NavMesh.GetAreaFromName("Walkable")))
{
transform.position = startTransform.position;
transform.rotation = startTransform.rotation;
myNMagent.SetDestination(hit.position);
}
else
{
Debug.Log("not hitting");
}
}
private void GotToEat()
{
transform.rotation = Quaternion.LookRotation(player.position - transform.position);
myNMagent.SetDestination(player.transform.position);
}
private bool ISFacing()
{
float angle = 45;
float angleToPlayer = Vector3.Angle(player.transform.forward.normalized,( transform.position - player.transform.position).normalized);
if( angleToPlayer < angle)
{
return true;
}
else
return false;
}
bool IsHit(out Vector3 result)
{
for (int i = 0; i < 30; i++)
{
Vector3 fixedPoint = transform.position + transform.forward * multiplyBy;
NavMeshHit hit;
if (NavMesh.SamplePosition(fixedPoint, out hit, 10.0f,1 << NavMesh.GetAreaFromName("Walkable")))
{
result = hit.position;
return true;
}
}
result = Vector3.zero;
return false;
}
when NPC is going to reach the corner of my map it won't move anymore will freeze there.
i suppose that i should check if any hitting navmesh is "Walkable" or not, but my function will return always true, so i can't tell to navimesh to go in any other direction..
what i'm missing up?
A video of it
video

My bullet spawns randomly and does no damage

using UnityEngine;
using System.Collections;
using System;
public class TurretScript : MonoBehaviour
{
public float rangeToPlayer;
public GameObject bullet;
// public GameObject spherePrefab;
private GameObject player; // Tag for DynamicWaypointSeek
private bool firing = false; //Firing status
private float fireTime; // Fire Time
private float coolDown = 1.00F; // Time to cool down fire
private int health = 5; //Health of turret
private bool bDead;
private Action cur;
void Start()
{
player = GameObject.FindWithTag("Player");
bDead = false;
}
void Update()
{
if (PlayerInRange())
{ // PlayerInRange is bool function defined at the end
transform.LookAt(player.transform.position); //Rotates the transform (weapon) so the forward vector points at /target (Player)/'s current position
RaycastHit hit;
if (Physics.SphereCast(transform.position, 0.5F, transform.TransformDirection(Vector3.forward), out hit))
{ //The center of the sphere at the start of the sweep,The radius of the sphere,The direction into which to sweep the sphere.
if (hit.transform.gameObject.tag == "Player")
{ //information in hit (only interested in "Player")
if (firing == false)
{
firing = true;
fireTime = Time.time; //keep the current time
GameObject bul;
Quaternion leadRot = Quaternion.LookRotation(player.transform.position); //Calculate the angular direction of "Player";
bul = Instantiate(bullet, transform.position, leadRot) as GameObject; // existing object to be copied, Position of Copy, Orientation of Copy
//Destroy(bullet, 2.0f);
}
}
}
}
if (firing && fireTime + coolDown <= Time.time) // prvious time stored in fireTime + cool down time is less --> means the firing must be turn off
firing = false;
// Destroy(GameObject.FindGameObjectWithTag("Bullet"));
if (health <= 0)
cur = Deadstate;
}
protected void Deadstate()
{
if (!bDead)
{
bDead = true;
Explode();
}
}
void Explode()
{
Destroy(gameObject, 1.5f);
}
bool PlayerInRange()
{
return (Vector3.Distance(player.transform.position, transform.position) <= rangeToPlayer); //Vector3.Distance(a,b) is the same as (a-b).magnitude.
}
void OnTriggerEnter(Collider col)
{
HealthBarScript.health -= 10f;
}
}`
This is code I wrote for a enemy turret in my current unity project. The idea is that it will fire a bullet at the player once it's in range and stop when it's out of range and when the bullet collides with the player, the player will take damage, but I'm struggling to figure out why the bullet won't do any damage. Thoughts? Very much appreciate the help!
According to my experience: bullet speed may be too fast to detect collision.
Imagine that in 2d:
frame 1:
.BB.......OOO......
frame 2:
........BBOOO......
frame 3:
..........OOO..BB..
Where OOO is your object and BB is your bullet.
To fix this you can have a bigger collider for the bullet. Other workaround like "dynamic" collider are also possible.

Unity 2D C# gameObject does not activate other gameObjects if statements when colliding with each other. Why?

The way this game works is that the game starts off with a Largest Ball. When the rocket hits the large ball it splits into two medium balls and then into two small balls. When the rocket hits the smallest ball it gets destroyed.
The problem I'm having is that when the rocket collides with the ball. The rocket gets destroyed, but the ball does "not" divide into two Large balls and so forth.
I just noticed this and I'm wondering if my problem will be fixed if I turn this code statement to == "smallest ball" instead of !=.
if (target.tag == "Rocket")
{
if (gameObject.tag != "Smallest Ball")
{
InstantializeBallsonoff();
}
else {
AudioSource.PlayClipAtPoint(popsounds[Random.Range(0, popsounds.Length)], transform.position);
//play random audio in the popsounds array at current position of ball
gameObject.SetActive(false); //deactivate the gameobject
}
}
}//ontriggerenter
This is the full code for my ball Script
using UnityEngine;
using System.Collections;
public class Ball : MonoBehaviour {
private float forceX, forceY;
private Rigidbody2D ball;
[SerializeField]
private bool moveLeft, moveRight;
[SerializeField]
private GameObject originalBall;
private GameObject ball1, ball2;
private Ball ball1script, ball2script;
[SerializeField]
private AudioClip[] popsounds; //array
// Use this for initialization
void Awake () {
ball = GetComponent<Rigidbody2D>();
ballspeed();
}
// Update is called once per frame
void Update () {
ballmovement();
}
void InstantiatingBalls()
{
if (this.gameObject.tag != "Smallest Ball")
{
ball1 = Instantiate(originalBall); //create copy of originalball into ball1
ball2 = Instantiate(originalBall);
ball1.name = originalBall.name;
ball2.name = originalBall.name;
ball1script = ball1.GetComponent<Ball>(); //get the ball script
ball2script = ball2.GetComponent<Ball>();
}
}//InstantiatingBalls
void InstantializeBallsonoff() {
InstantiatingBalls();
Vector3 temp = transform.position; //start from current ball location
ball1.transform.position = temp;
ball1script.setmoveLeft(true);
ball2.transform.position = temp;
ball2script.setmoveRight(true);
ball1.GetComponent<Rigidbody2D>().velocity = new Vector2(0, 2.5f); //x,y
ball2.GetComponent<Rigidbody2D>().velocity = new Vector2(0, 2.5f); //x,y
AudioSource.PlayClipAtPoint(popsounds[Random.Range(0, popsounds.Length)], transform.position);
//play random audio in the popsounds array at current position of ball
gameObject.SetActive(false); //deactivate the gameobject
}//InstantializeBallsonoff
public void setmoveLeft(bool moveLeft) { //canMoveLeft
this.moveLeft = moveLeft;
this.moveRight = !moveLeft; //moveRight is now false b/c we set moveLeft to true
}
public void setmoveRight(bool moveRight) {//canMoveRight
this.moveRight = moveRight;
this.moveLeft = !moveRight;
}
void ballmovement() {
if (moveLeft) {
Vector3 temp = transform.position; //current position of ball
temp.x -= Time.deltaTime; // represent time per frame
transform.position = temp;
}
if (moveRight) {
Vector3 temp = transform.position; //current position of ball
temp.x += Time.deltaTime; // represent time per frame
transform.position = temp;
}
}
void ballspeed() {
forceX = 2.5f;
switch (this.gameObject.tag) {
//this refers to gameobject that holds this script
case "Largest Ball":
forceY = 11.5f;
break;
case "Large Ball":
forceY = 10.5f;
break;
case "Medium Ball":
forceY = 9f;
break;
case "Small Ball":
forceY = 8f;
break;
case "Smallest Ball":
forceY = 7f;
break;
}//switch
}//ballspeed
void OnTriggerEnter2D (Collider2D target) {
if (target.tag == "Ground") {
ball.velocity = new Vector2(0, forceY);
}
if (target.tag == "Right Wall") {
setmoveLeft(true);
/*moveRight = false;
moveLeft = true;*/
}
if (target.tag == "Left Wall")
{
setmoveRight(true);
/*moveRight = true;
moveLeft = false;*/
}
if (target.tag == "Rocket")
{
if (gameObject.tag != "Smallest Ball")
{
InstantializeBallsonoff();
}
else {
AudioSource.PlayClipAtPoint(popsounds[Random.Range(0, popsounds.Length)], transform.position);
//play random audio in the popsounds array at current position of ball
gameObject.SetActive(false); //deactivate the gameobject
}
}
}//ontriggerenter
}//ball
This is partial of my code where the rocket gets destroyed when it collides with the large ball & the top. This is the other part that I am having trouble with.
void OnTriggerEnter2D(Collider2D target) {
if (target.tag == "Top") {
Destroy(gameObject);
}
string[] ballhit = target.name.Split();
/*array ballhit
split = deletes the space between two words and make it so it takes 2 spaces in the array*/
for (int s = 0; s < ballhit.Length; s++) {
Debug.Log("The array contains: " +ballhit [s]);
if (ballhit.Length > 1)
{ //ball names will always be more than 1 length "Largest Ball"
if (ballhit[1] == "Ball")
{
Destroy(gameObject);
}//destroy object
}//ballhit name length
}// name increments
}//triggerCollider
This is my full Rocket Script
using UnityEngine;
using System.Collections;
public class Rocket : MonoBehaviour {
private Rigidbody2D rocket;
private float speed = 5f;
// Use this for initialization
void Awake () {
rocket = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
rocket.velocity = new Vector2(0, speed); //x, y rocket movement
}
void OnTriggerEnter2D(Collider2D target) {
if (target.tag == "Top") {
Destroy(gameObject);
}
string[] ballhit = target.name.Split();
/*array ballhit
split = deletes the space between two words and make it so it takes 2 spaces in the array*/
for (int s = 0; s < ballhit.Length; s++) {
Debug.Log("The array contains: " +ballhit [s]);
if (ballhit.Length > 1)
{ //ball names will always be more than 1 length "Largest Ball"
if (ballhit[1] == "Ball")
{
Destroy(gameObject);
}//destroy object
}//ballhit name length
}// name increments
}//triggerCollider
}//rocket
Your code is much more complicated than it needs to be an therefore the actual error is difficult to find.
First off, your moveLeft and moveRight bools are mutually exclusive. You need only one of them. I would event prefer to replace both with a public int currentDirection which you set to 1 for right and -1 for left.
Your new ballMovement method could then simply be:
void MoveBall() // C# methods are upper-case by convention
{
transform.position += Vector3.right * currentDirection * Time.deltaTime;
}
The way you check with which type of ball you are colliding is not very safe. I would suggest you use an enum to differentiate the ball sizes.
public enum BallSizes { Largest, Large, Medium, Small, Smallest }; // The different possible values
public BallSize size = BallSizes.Largest; // The balls current size, the biggest size by default
This also allows you to store your y-axis force values in a matching array and access them easily without the use of a switch or else-if:
private float[] forcesY = new float[]{ 11.5f, 10.5f, 9f, 8f, 7f };
void SetBallSpeed()
{
forceX = 2.5f;
forceY = forcesY[(int)size];
}
I think the problem you had with the ball not splitting is is connected to this.
Upon collision, you detected the type of ball you collided with, by examining its tag. But you always Instantiate the new balls using the same prefab, and I never saw you change the tag. So what type of ball are you creating?
If you use the method I described above, you can simply assign a general tag "Ball" and handle the rest with the BallSizes-enum. When creating the new balls you can do the following:
[SerializedField] // The SerializedField property makes it show up in the inspector, even though it is private
private Ball ballPrefab; // Attention: Type "Ball"
public void IsHit() // Method to execute upon rocket collision
{
if(ball.size != BallSizes.Smallest)
{
Ball leftBall = Instantiate(ballPrefab). // Notice that, when you assign you prefab as Type "Ball", you directly get the Ball-Component as the return value.
leftball.direction = -1;
leftball.size = (BallSizes)(size + 1); // Add one to size, to get get the next enum value, which is the next smaller size.
Ball rightBall = .... // Do same stuff for the right ball
}
Destroy(this.gameObject);
}
I don't know how you plan to visualise the type of ball, but you might want to add a float[] ballScales, which you use to shrink the ball's actual size, something like: leftBall.transform.localScale = Vector3.one * ballScales[(int)size].
Finally, another reason your collision does not work might well be, that you handle it in two different places. If you destroy the rocket after it detects collision with the ball, nothing has happend to the ball yet. If the ball is checking for collision afterward it will most likely not find the rocket, because you already destroyed it. A clean way to solve this, would be to let the rocket notify the ball of the collision:
void OnTriggerEnter2D(Collider2D target)
{
if(target.tag == "Top")
Destroy(gameObject);
else if(target.tag == "Ball")
{
target.GetComponent<Ball>().IsHit(); // We know this component should exist when the object is tagged "Ball"
Destroy(gameobject);
}
}
You can then remove that from you ball collision and shorten it to this:
void OnTriggerEnter2D (Collider2D target)
{
if(target.tag == "Ground")
ball.velocity = new Vector2(0, forceY);
if(target.tag == "Right Wall" || target.tag == "Left Wall")
direction *= -1; // Inverts the direction, + <-> -
}
Phew, that was a lot.
I hope this is still relevant and answers more questions than it raises.
Happy coding!

getting information from sphereCast's hit

I know how to get information from a RayCast but couldn't find anything good on sphereCast.
When I shoot a raycast to an enemy I can detect it's health component and reduce it's health.
It's code:
shootRay.origin = transform.position;
shootRay.direction = transform.forward
if (Physics.Raycast(shootRay, out shootHit, 100f, shootableMask))
{
EnemyHealth enemyHealth = shootHit.collider.GetComponent<EnemyHealth>();
if (enemyHealth != null)
{
enemyHealth.TakeDamage(damagePerShot, shootHit.point);
}
}
Now I want to do a similar thing with SphereCast but instead of one enemy I want to detect all enemys in the hit area and reduce their health.
if (Physics.SphereCast(shootRay, 5f, out shootHit, 100f, shootableMask))
{
// ???
}
According to this (http://answers.unity3d.com/questions/486261/how-can-i-raycast-to-multiple-objects.html) all you need to do is to use RaycastAll:
Example:
void Update() {
RaycastHit[] hits;
hits = Physics.RaycastAll(transform.position, transform.forward, 100.0F);
int i = 0;
while (i < hits.Length) {
RaycastHit hit = hits[i];
Renderer rend = hit.transform.GetComponent<Renderer>();
if (rend) {
rend.material.shader = Shader.Find("Transparent/Diffuse");
rend.material.color.a = 0.3F;
}
i++;
}
}

Categories