how can I show indication from where enemy is coming? - c#

im making a car runner game (all objects are in 3d) where in cops are chasing the player
im spawning the cops from 4 different place (i.e left,right,top,bottom)
private int lastSpawnPos;
[SerializeField]
private Transform[] spawnPos;
void SpawnPoliceCar()
{
GameObject policeCar = ObjectPooling.instance.GetPooledObject("PoliceCar");
int r = UnityEngine.Random.Range(0, spawnPos.Length);
while (lastSpawnPos == r)
{
r = UnityEngine.Random.Range(0, spawnPos.Length);
}
Vector3 policeCarPos = policeCar.transform.position;
policeCarPos = new Vector3(spawnPos[r].position.x, 0, spawnPos[r].position.z);
policeCar.SetActive(true);
policeCar.GetComponent<Damage>().DefaultSetting();
lastSpawnPos = r;
currentPoliceCar++;
}
im calling this method in update() & this script is applied to empty game object in the scene. Since this code works perfectly fine
but now i wanted to add arrow indication on the screen where the cops are spawning as well as i wanted to rotate the arrow as per the direction. im spawning 4 cops at a time.
can any 1 help me in this pls im new to this platform & stuck here from long time

When you're spawning cops you can store them in the List<Transform> cops and remove them (if it's supported to destroy or remove cops from the game). So you'll have player's position and all the cops positions.
Using this data you can find the distance from the cop to the player using Vector3.Distance or Vector2.Distance(if you want to ignore the z coordinate of player and cops). Using this methods you can loop throug cops List and find closets/farthest cop from the player (if you want to show not any cop but closest - the most recently spawned, or closest).
After you have found cop gameobject which you want the arrow to point to you can turn the arrow to this gameobject using Transform.LookAt method. Call this method every Update() call and your arrow's rotation will follow selected cop.
Update:
For the farthest cop your code can look smth like that:
private int lastSpawnPos;
[SerializeField]
private Transform[] spawnPos;
//Your arrow
public GameObject Arrow;
//Player
public Transform Player;
//Storing all spawned cops
public List<Transform> Cops;
void SpawnPoliceCar()
{
GameObject policeCar = ObjectPooling.instance.GetPooledObject("PoliceCar");
int r = UnityEngine.Random.Range(0, spawnPos.Length);
//Adding cop to the list when spawned
//TODO: do not forget to remove from the list, when cop is removed (back to the pool)
Cops.Add(policeCar.transform);
while (lastSpawnPos == r)
{
r = UnityEngine.Random.Range(0, spawnPos.Length);
}
Vector3 policeCarPos = policeCar.transform.position;
policeCarPos = new Vector3(spawnPos[r].position.x, 0, spawnPos[r].position.z);
policeCar.SetActive(true);
policeCar.GetComponent<Damage>().DefaultSetting();
lastSpawnPos = r;
currentPoliceCar++;
}
//Call this on update
void PointArrow()
{
Transform farthestCop = null;
float maxDistance = 0.0f;
//Find the farthes cop
foreach (var cop in Cops)
{
var distance = Vector3.Distance(Player.position, cop.position);
if (distance > maxDistance)
{
farthestCop = cop;
maxDistance = distance;
}
}
//If there are no cops - can't point an arrow
if(farthestCop == null) return;
//Point an arrow on the cop
Arrow.transform.LookAt(farthestCop);
}
I can't exactly say that it is what you are looking for without knowing the full game and code. Hope this will help

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 to fix spasming and buggy instantiation in Unity

I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}

Bullet not shooting enemy center in unity

can you please help me to create correctly shoot of bullet to enemy center with my method ?
private void Update()
{
if (GameObject.FindGameObjectWithTag("Enemy") && !_shooting)
{
StartCoroutine(Shoot());
_shooting = true;
}
}
private IEnumerator Shoot()
{
int random = Random.Range(1, Camera.main.transform.childCount);
int rnd = Random.Range(0, transform.childCount);
Instantiate(Bullet, transform.parent);
target = Camera.main.transform.GetChild(1).transform.position;
var rb = transform.parent.GetChild(transform.parent.childCount - 1).GetComponent<Rigidbody2D>();
rb.transform.position = positions[rnd];
transform.GetChild(rnd).GetComponent<Transform>().localScale = new Vector2(1.2f, 1.2f);
StartCoroutine(ReturnDotSize(rnd));
while (Vector2.Distance(rb.transform.position, target) > 0.1f)
{
rb.transform.position = Vector2.MoveTowards(rb.transform.position, target, shootSpeed * Time.deltaTime);
yield return null;
}
yield return new WaitForSeconds(ReloadTime);
_shooting = false;
}
Now bullet shoot only one time. I was try to write break instead of yield return null, but in this time bullet not shoot to center of enemy.In addition, I tried to write "_shooting = false" over "yield return null" and in this case the bullet clearly goes to the center of the enemy, but the "ReloadTime" delay does not work. Now I need the bullet to accurately go to the center of the enemy and work correctly "ReloadTime".
For customiztion sake and performance reasons, you should create a script Enemey.cs attached to your Enemey GameObject that has a public transform field named "BulletTarget". This Bullet Target Transform is a child element of your Enemey game object and you can then freely place it where you see the center of your enemy object. Perfomance wise you cen then use FindObjectsOfType which is better and less fragile then by name.

How to collide two objects without moving either object in Unity

I have a setup similar to this mockup in my 3D Unity game.
Each object has a collider as well as a rigid body. I am moving the object using mouse using simple mouse input events
private void OnMouseDown()
{
isSelected = true;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 touchLocation = InputController.Instance.getTapLocation(); // Gets touch/mouse location using Raycast
_transform.position = track.GetPositionOnPath(touchLocation); //Uses vector projection math
}
private void OnMouseUp()
{
if (isSelected)
isSelected = false;
}
The object movement is constrained to a path using Vector projection
public Vector3 GetPositionOnPath(Vector3 touchLocation)
{
//Make the vector for the track u
Vector3 u = end.position - start.position;
//Make the touch location vector from the track start position v
Vector3 v = touchLocation - start.position;
float uv = Vector3.Dot(u, v);
float uMagSqr = Mathf.Pow(u.magnitude, 2);
Vector3 p = (uv / uMagSqr) * u; //Projection Formula (u.v)/(|u|^2) * u
Vector3 finalVector = start.position + p;
//Clamp the final vector to the end.positions of the track
if (start.position.x > end.position.x)
finalVector.x = Mathf.Clamp(finalVector.x, end.position.x, start.position.x);
else
finalVector.x = Mathf.Clamp(finalVector.x, start.position.x, end.position.x);
if (start.position.z > end.position.z)
finalVector.z = Mathf.Clamp(finalVector.z, end.position.z, start.position.z);
else
finalVector.z = Mathf.Clamp(finalVector.z, start.position.z, end.position.z);
return finalVector;
}
I want to make it in such a way that other objects in the way would obstruct the movement of the selected object as shown in the mockup. The solutions I thought of were to increase the weights of the objects so that they won't fly away when they collide, or to keep every object kinematic except the currently selected one. But they both have the same issue that the objects simply slide through each other.
It seems like a simple thing to do, but I can't seem to find any resources to help me out. I would really appreciate some help on how to tackle this problem and implement the mechanic as I intend. Thanks in Advance.
I have yet to find the exact solution I am looking for. But since it is highly discouraged to move the position of a rigid body object, I switched over to a physics-based movement based on information I found on this and this thread. Here is my current implementation:
private void OnMouseDown()
{
isSelected = true;
_rigidbody.isKinematic = false;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 target = InputController.Instance.getTapLocation();
//_rigidbody.MovePosition(target);
force = _track.GetPositionOnPath(target) - _transform.position;
}
private void OnMouseUp()
{
if (!isSelected) return;
isSelected = false;
_rigidbody.isKinematic = true;
force = Vector3.zero;
}
private void FixedUpdate()
{
_rigidbody.velocity = force;
}
However, this comes at the cost of the movement not being perfectly responsive to the input given by the player as shown in this gif.
This is a fix so I am posting this as an answer but any other better options are much appreciated.

Place object without overlap others

Im having a problem in one of my projects in unity. I basicly have a system where you click an object and then you click somewhere inside an area and the object goes to that position. the problem is that those objects can't overlap. And they can't use physics. to detect colision with each other. I have been working on this for a long time I got something but still not totally working and I hoped someone could help me.
What im basicly doing is getting all objects near the click and then if there are some just calculate the directions between the click and those objects and then add them to the position that seems to work sometimes they don't overlap other times they do and they go to far away and I need them to be near the click.
code:
public Vector3 GetPossiblePosition(List<GameObject> nearbyDiscs, Vector3 position)
{
float initialY = transform.position.y;
Vector3 idealPosition = position;
List<Vector3> directions = new List<Vector3>();
if(nearbyDiscs.Count > 0)
{
foreach (GameObject disc in nearbyDiscs)
{
Vector3 newDirection = position - disc.transform.position;
directions.Add(newDirection);
}
for (int i = 0; i < directions.Count; i++)
{
idealPosition += directions[i] / directions.Count;
List<GameObject> discs = CheckForNearbyDiscs(idealPosition);
if (discs.Count < 1)
break;
}
}
idealPosition.y = initialY;
return idealPosition;
}
behaviour:
You can easily do this using Physics2D.OverlapCircleAll
public static Collider2D[] OverlapCircleAll(Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);
this method returns an array of colliders, you can simply check overlapping if the length of the returning array is greater than one and can handle accordingly.

Categories

Resources