Detecting collisions of enemies - c#

I need to detect the collisions of the same type of enemies in XNA.
If a collision is detected, I wanted the enemies to turn around and walk in the other direction.
If i save all instances to a List how can I detect if different(But same) type of enemies collide?
This is in C# XNA.
EDIT: I have enemies that are in a class called "Enemy", all enemies in my game are created from this class, I need to be able to check if the enemies have collided.
EDIT 2:
Here is a code sample:
// Fields.
private List<Enemy> enemies = new List<Enemy>();
// Add our enemies when we need to.
enemies.Add(new Enemy(this, position, spriteSet));
// Here is it's update method.
private void UpdateEnemies(GameTime gameTime)
{
foreach (Enemy enemy in enemies)
{
enemy.Update(gameTime);
// This code works because it's comparing the player.
if (enemy.BoundingRectangle.Intersects(Player.BoundingRectangle))
{
OnPlayerKilled(enemy);
}
// This code is always ture because enemy is enemy I can't figure out how to solve this.
if (enemy.BoundingRectangle.Intersects(enemy.BoundingRectangle))
{
// Make enemies turn... This if statement is the problem.
}
}

To determine if two enemies have collided, you need to find the Rectangle each of them is occupying. Then you can use Rectangle.Intersects(Rectangle) to find out if they are overlapping.
Re: Edit:
Every enemy is always checking for collision with itself. Not what you want :(|)
Something like this is what you want:
foreach (Enemy enemy1 in enemies)
{
foreach (Enemy enemy2 in enemies)
{
if (enemy1 != enemy2 && enemy1.BoundingRectangle.Intersects(enemy2.BoundingRectangle))
{
// enemy1 is colliding!
}
}
}

Related

Ranged attack in Unity3D

I have two objects: a player and an enemy. The main problem is the enemy, which should start shooting at the player when it approaches him at a certain distance (in short, just start the animation).
At first I tried to implement this through Vector 3, as with other ordinary opponents. But he is as stupid and crutch as possible, however, you yourself can see everything in the pinned.
I started to implement it more allegedly correctly, through the trigger and the collider (box collider) of the enemy itself. And everything works right as it should, but there is a nuance. The enemy also has boxing implemented through the box collider, the player, hitting which, causes damage to him. There is only one box collider for these two tasks, and since I had to increase this collider so that the enemy could stop in front of the player at a certain distance. Because of this, the player can hit at a great distance (at which the enemy can shoot) from the enemy and the enemy takes damage anyway.
I tried to make a separate object the size of the enemy himself and use it as a box to receive damage. Then he already transmits data about receiving damage to the enemy object. But this does not work, all links between scripts and objects are made, but he does not want to transfer data. That is, simply making two colliders for different tasks does not work.
In general, here my powers are all. I searched the entire Internet, but I did not find how to implement this mechanic in a different way so that it does not conflict with others. Therefore, I ask the help of the local experts, where I screwed up.
private Animator ch_animator;
// Start is called before the first frame update
void Start()
{
myAgent = GetComponent<NavMeshAgent>();
myAnim = GetComponent<Animator>();
EnemyH = GetComponent<GDHealth>();
}
// Update is called once per frame
void Update()
{
dist = Vector3.Distance(/*checker.*/transform.position, target.transform.position);
if (dist > range)
{
myAgent.enabled = false;
myAnim.SetBool("Idle", true);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", false);
}
if (dist <= range & dist > atRange)
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
}
if (dist <= atRange)
{
StartCoroutine(Attack());
}
if (PlayerH._health <= 0)
{
atRange = 0;
}
if (EnemyH._health < 0)
{
myAgent.enabled = false;
}
}
public IEnumerator Attack()
{
yield return new WaitForSeconds(0.5f);
myAgent.enabled = false;
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", true);
}
void OnTriggerStay(Collider col)
{
if (col.tag == "Player")
{
//gameObject.GetComponent<Animator>().SetBool("Attack", true);
StartCoroutine(Attack());
transform.LookAt(col.transform.position);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
}
}
void OnTriggerExit(Collider col)
{
if (col.tag == "Player")
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
//gameObject.GetComponent<Animator>().SetBool("Attack", false);
}
}
But this does not work, all links between scripts and objects are made, but he does not want to transfer data.
From this description it could be anything: wrong layers, no rigidbody on either of objects, misstyped tags in OnTriggerStay method.
In my project, I successfully created 2 colliders for 2 separate tasks, so this is how I would see it in your problem:
Use two colliders
Attach one collider with one script to the enemy object - this collider should have a size of the enemy. The OnTriggerStay method here should deal damage to the enemy, check for death, etc.
Create child object to the enemy. Attach new collider to it with the size of enemy's attack range. Attach a script with OnTriggerStay method that will stop enemy and begin ranged attack (or whatever you want to do).
If this doesn't work: check collision matrix or try adding a kinematic rigidbody to either of objects.
Measure distance between player and the enemy in update (which you are already doing) and apply necessary code based on distance (stop or attack) thus replacing one of the colliders.
Hope that helps!

Destructible tiles in unity's tilemap

I'm creating a 2d game and I want to make it so that tiles are destructible whenever a projectile collides with a tile. The problem is I don't know how to get the tile that the projectile is colliding with, I would like to make it so that it area of damage.
However for now I want to make it so that once the projectile reaches the ground it destroys the tile it's sitting on I've made a collider2d to help me with that but I still have no idea how to get the tile that's the projectile is sitting on.
IEnumerator OnCollisionEnter2D(Collision2D collision)
{
//Projectile has reached the ground and is in collision with some tile
Vector2 hit = gameObject.transform.position;
Debug.Log("y" + hit.y);
hit.y =- 3; // i have no idea what y i should put in order to get the tile
if (x.gameObject.tag != "Player")
{
Collider2D[] collidedwith = Physics2D.OverlapCircleAll(this.gameObject.transform.position, radius);
//what should the hit vector be in order to destroy the tile.
tilemap.SetTile(tilemap.WorldToCell(hit), null);
}
}
A screenshot for better explanation (I would like to get rid of the blue tile):
If possible I would like to make it into an aoe projectile but for now it's not necessary
Assuming you have colliders in place, that is:
Collider in the rocket with is trigger selected (to allow OnTriggerEnter2D be triggered)
Collider on the tiles to actually trigger the OnTriggerEnter2D of the rocket during the collision
Now, to detect collisions between rocket and tiles, do the following:
Give the tile gameObject (prefab) the tag name tile
Replace your code in the rocket with:
Script
// It will be triggered the the rocket crashes against the tile
void OnTriggerEnter2D(Collider2D col)
{
if(collision.gameObject.tag == "tile")
{
// Destroy the tile the rockets collided with
Destroy(collision.collider.gameObject);
// Destroy the rocket itself
Destroy(gameObject);
}
}
ok i came up with a solution but this solution doesn't really have the feature i really wanted which is, i want the projectile to do aoe damage
Tilemap tilemap = GetComponent<Tilemap>();
Vector3 hitPosition = Vector3.zero;
foreach (ContactPoint2D hit in collision.contacts)
{
Debug.Log(hit.point);
hitPosition.x = hit.point.x - 0.1f;
hitPosition.y = hit.point.y - 0.1f ;
tilemap.SetTile(tilemap.WorldToCell(hitPosition), null);
}

SetDestination and active agent/NavMesh issue with Unity Game

I am working in unity 5 on a game and I am currently getting this error:
"SetDestination" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.NavMeshAgent:SetDestination(Vector3)
EnemyMovement:Update() (at Assets/Scripts/Enemy/EnemyMovement.cs:25)
My code for the enemyMovement script is:
using UnityEngine;
using System.Collections;
public class EnemyMovement : MonoBehaviour
{
Transform player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
NavMeshAgent nav;
void Awake ()
{
player = GameObject.FindGameObjectWithTag ("Player").transform;
playerHealth = player.GetComponent <PlayerHealth> ();
enemyHealth = GetComponent <EnemyHealth> ();
nav = GetComponent <NavMeshAgent> ();
}
void Update ()
{
if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
{
nav.SetDestination (player.position);
}
else
{
nav.enabled = false;
}
}
}
Now I know that there is almost an identical question here: Unity - "SetDestination" can only be called on an active agent that has been placed on a NavMesh. UnityEngine.NavMeshAgent:SetDestination(Vector3) However, the question does not seem to have a firm response and I have tried doing some of the responses and nothing appears to work. I will say that my game does work fine. However, I have added an ability that if the player presses 'B' then all of the enemies will die on the screen. When I press 'B' the enemies die but the system 'crashes' and I get that error message.
EnemyManager.cs:
public void Kill(int InstanceID)
{
if (EnemyManager.Instance.EnemyList.ContainsKey(InstanceID))
EnemyManager.Instance.EnemyList.Remove(InstanceID);
}
public void DeathToAll()
{
Dictionary<int, Object> TempEnemyList = new Dictionary<int, Object>(EnemyManager.Instance.EnemyList);
foreach (KeyValuePair<int, Object> kvp in TempEnemyList)
{
// kvp.Key; // = InstanceID
// kvp.Value; // = GameObject
GameObject go = (GameObject)kvp.Value;
go.GetComponent<EnemyHealth>().Death();
}
}
Enemyhealth.cs
public void Death ()
{
// The enemy is dead.
isDead = true;
// Turn the collider into a trigger so shots can pass through it.
capsuleCollider.isTrigger = true;
// Tell the animator that the enemy is dead.
anim.SetTrigger ("Dead");
// Change the audio clip of the audio source to the death clip and play it (this will stop the hurt clip playing).
enemyAudio.clip = deathClip;
enemyAudio.Play ();
// Kill the Enemy (remove from EnemyList)
// Get the game object associated with "this" EneymHealth script
// Then get the InstanceID of that game object.
// That is the game object that needs to be killed.
EnemyManager.Instance.Kill(this.gameObject.GetInstanceID());
}
The problem here is that while you're checking if enemyHealth.currentHealth > 0 before calling nav.SetDestination(), that value doesn't get changed when the enemies are killed by the player's ability. Instead, it seems that the flag isDead is set on the enemy, without affecting the actual health.
To ensure nav.SetDestination() doesn't get called when the enemy is dead (because it seems at that point, the enemy is no longer a valid active agent), just add an additional condition to your if statement:
if(enemyHealth.currentHealth > 0 && !enemyHealth.isDead && playerHealth.currentHealth > 0)
{
nav.SetDestination (player.position);
}
Hope this helps! Let me know if you have any questions. (You may need to make isDead public for this to work.)
An alternative solution is to make a property IsDead which returns whether enemyHealth.currentHealth > 0, and kill enemies by setting their health to 0. Then you don't need to have a health tracker and a separate isDead flag that must be explicitly set. That's more of a design decision though as it won't affect the functionality.
I was having a similar issue where my navmesh agent was not able to reach the setdestination() point ,the navmesh agent just stopped so what i was doing wrong was that my setdestination() point was bit high from navmesh and that point was becoming out of range the agent.
So I created a foot empty gameobject that was near to the navmesh "in height".
and my agent was then able to reach the point

Physics.IgnoreCollision Doing Nothing

I'm trying to make a BTD game. Since different balloons (enemies) have different speeds, they collide with each other when traveling on the path. I'm currently using this code:
void OnCollisionEnter (Collision coll)
{
if (coll.gameObject.tag == "Enemy")
{
Physics.IgnoreCollision(coll.collider, gameObject.GetComponent<SphereCollider>());
}
}
However, it doesn't appear to be working at all. The enemies still collide with each other. On the otherhand, the collision with the enemies and bullets from towers is working.
void OnTriggerEnter (Collider col)
{
if (col.tag == "Bullet")
{
CurrentHP -= col.GetComponent<TackShooterBullet>().Damage;
}
I've tried layer-collision (enemies to same layer & unchecking of the same layer collision in the layer collision matrix, but that doesn't work either. The enemy contains a sphere mesh filter, sphere collider, mesh renderer, rigidbody, material, and 2 scripts. Is there a better way to avoid collisions between the enemies. I'm asking this question since I've seen duplicates, but their solutions aren't working at all. I can provide more of my code if needed.
Edit for Clarity: Again, what I'm trying to accomplish is have the enemies be able to go through each other.
Edit (Fixed Problem): I found out to avoid Enemy Collisions, I could also remove rigidbody. However, removing the ridigbody would mess up the bullet --> enemy trigger in the enemy class. Therefore, I just wrote the collision between bullet & enemy in the bullet class instead.
using UnityEngine;
public class TackShooterBullet : MonoBehaviour {
private GameObject target;
public float Damage;
// Use this for initialization
void Start () {
target = transform.parent.GetComponent<TackShooterRange>().Target; // Target = Enemy[0] (First Enemy To Enter Range - Enemy is Removed from JList when exiting Range)
}
// Update is called once per frame
void Update()
{
Damage = gameObject.transform.parent.transform.parent.GetComponent<TackShooterLimitingRange1>().level * 20; // Upgrade Level * 20 = Damage Done
if (target == null) // If Enemy Exits Range
{
Destroy(gameObject); // Destroy Bullet
}
if (target != null) // Enemy Exists In Range
{
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, 20 * Time.deltaTime); // Bullet Follows Enemy
Destroy(gameObject); // Destroy Bullet Upon Contact With Enemy
target.GetComponent<HealthOfEnemy>().CurrentHP -= Damage; // Enemy Loses Health
}
}
This allowed me to remove the OnTriggerEnter & OnCollisionEnter methods as well as the RigidBody from the Enemy Class as stated before, so these properties no longer affect the collisions between Enemies.
Unity has a built in function for easier collision detection called layer-based collision detection:
https://docs.unity3d.com/Manual/LayerBasedCollision.html
The documentation is really good. Just comment if you need further clarification.

pacing code execution in a Unity based Roguelike

[UPDATE] I tried using the suggestion below but am still having an issue where the enemies appear to move simultaneously and they each move twice when first seen if there is already an enemy in line of sight. All I really want to do is have a delay between the actions of enemies in the player s line of sight so they do not simultaneously move, play sounds etc...
The updated code is:
private IEnumerator takeAllEnemyActions()
{
// Cycle through all enemies and have them perform their actions
if (Enemies != null && Enemies.Length > 0)
{
bool foundEnemyInLoS = false;
Enemy lastEnemy = null;
foreach (GameObject enemy in Enemies)
{
// Cache the enemies script
Enemy thisEnemy = enemy.GetComponent<Enemy>();
// If it is not this enemies turn to move then move on to the next enemy
if (thisEnemy.NextMoveTick > GameManager.Instance.CurrentTick)
{
continue;
}
// Check to see is this enemy is the first enemy in the players line of sight (If there is one)
if (!foundEnemyInLoS && thisEnemy.isInLineOfSightOfPlayer())
{
// If so then we save it for last to ensure that if any enemy is called with a delay that the last one does not make the player wait to move
foundEnemyInLoS = true;
lastEnemy = thisEnemy;
continue;
}
// At this point only enemies whose turn it is and who are not being saved for later can act
thisEnemy.TakeEnemyActions();
// If the enemy was in the players line of sight wait 200ms before continuing
if (thisEnemy.isInLineOfSightOfPlayer())
{
yield return new WaitForSeconds(0.5f);
}
}
// if there were enemies in the players line of sight then one was saved for last
// Take its action without a delay so the player can go next
if (foundEnemyInLoS)
{
lastEnemy.TakeEnemyActions();
}
}
Original Question:
In a top down, 2d, turn based roguelike in Unity3d 5 I am having an issue with game pacing. I am using a very simple state machine to control execution but I want enemies that all move in the same turn to do so with a pause between each one if they are in the players line of sight so that animations and sound have time to occur without overlapping (Especially sounds). The only issue I am having is getting the actual pause to occur without affecting animations or camera movement (The camera may be sliding to follow a player movement and it was stopping). I only need about a 100ms pause before moving to the next enemy.
The enemies are taking their actions in a foreach loop in an EnemyManager script
private void takeAllEnemyActions()
{
// Cycle through all enemies and have them perform their actions
if (Enemies != null && Enemies.Length > 0)
{
foreach (GameObject enemy in Enemies)
if (enemy.GetComponent<Enemy>().NextMoveTick <= GameManager.Instance.CurrentTick)
enemy.GetComponent<Enemy>().TakeEnemyActions();
}
GameManager.States.NextPlayState();
}
I tried creating a routine to invoke each enemy to act but the issue there was that execution of other enemies continued.
I tried using a coroutine but again the issue was the game would move on into the next state.
I even tried a while-do loop using Time.RealTimeSinceStartup just to see what it would do (I know, a very VERY bad idea)
I'm certain this is simple and I am just having a brain cramp but I've been trying things and google-binging for hours with no progress. Thank goddness for git to roll back.
Does anyone have any suggestions on the best way to go? I'm not needing someone to write my code, I just need pointed in the right direction.
Thanks
Use a co-routine and yield for each statement. Here's some modified code.
private IEnumerator TakeAllEnemyActions() {
// Cycle through all enemies and have them perform their actions
if (enemies != null && enemies.Length > 0) {
foreach (var enemy in enemies) {
if (enemy.GetComponent<Enemy>().NextMoveTick <= GameManager.Instance.CurrentTick) {
enemy.GetComponent<Enemy>().TakeEnemyActions();
yield return new WaitForSeconds (0.1f);
}
}
}
GameManager.States.NextPlayState();
}
Call this method with
StartCoroutine(TakeAllEnemyActions());

Categories

Resources