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++;
}
}
Related
In my Unity game (top-down 2D shooter) there are some enemies flying along a path (via DOTween). My player casts a ray into the scene in order to get an enemy-object right in front of the player. So far so good.
Now the problem is that I want only one enemy-object as a result, i.e. the raycast should stop when it hits an enemy-object for the very first time. How do I achieve this?
I need only one enemy-object hit by the raycast because there is a crosshair in my game and when the raycast starts and the enemies fly along the path the crosshair jumps forth and back (and I don't want this - the crosshair should stay at the first enemy hit by the raycast).
Raycast video
This is my code (attached to the player):
void FixedUpdate() {
//crosshair: Cast a ray straight up.
float _size = 12f;
Vector2 _direction = this.transform.up;
RaycastHit2D _hit = Physics2D.Raycast(this.transform.position, _direction, _size);
if (_hit.collider != null && _hit.collider.tag == "EnemyShipTag") {
// We touched something!
Vector2 target = new Vector2(_hit.collider.gameObject.transform.position.x, _hit.collider.gameObject.transform.position.y);
const float moveTime = 0.1f;
float step;
step = Vector2.Distance(crosshairGO.transform.position, target);
crosshairGO.transform.position = Vector2.MoveTowards(crosshairGO.transform.position, target, step / moveTime * Time.deltaTime);
Vector2 _pos3 = new Vector2(this.transform.position.x, crosshairGO.transform.position.y);
crosshairGO.transform.position = _pos3;
crosshairBegin = false;
} else {
// Nothing hit
Vector2 _pos2 = new Vector2(this.transform.position.x, 4.5f);
if (crosshairBegin) {
crosshairGO.transform.position = _pos2;
} else {
Vector2 _pos4 = new Vector2(this.transform.position.x, crosshairGO.transform.position.y);
crosshairGO.transform.position = _pos4;
}
}
}
If I understand you right. You could create a bool value and set it true after you hit something.
For example:
Vector2 _pos3 = new Vector2(this.transform.position.x, crosshairGO.transform.position.y);
crosshairGO.transform.position = _pos3;
crosshairBegin = false;
youHitSomething = true;
Before you create the ray
you could write an if
if(!youHitSomething)
{
float _size = 12f;
Vector2 _direction = this.transform.up;
RaycastHit2D _hit = Physics2D.Raycast(this.transform.position, _direction, _size);
if (_hit.collider != null && _hit.collider.tag == "EnemyShipTag")
{
// We touched something!
// your Code
youHitSomething = true;
}
else
{
// Nothing hit
// your code
}
}
To let the ray cast again you could create a new method
public void ActivateRay()
{
youHitSomething = false;
}
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
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);
}
My script puts an object at the position of the mouse, regardless of the position.
I'd like to limit where I can place the object to a small area around the player sprite.
Say if mouse X if greater than (X position + a little) of the players X than the object wont Instantiate. I've tried if statements along these lines but haven't been able to get it to work.
Here is the placement script.
public GameObject seedlings;
public GameObject player;
Vector3 mousePOS = Input.mousePosition;
// Use this for initialization
void Start(){}
// Update is called once per frame
void Update()
{
PlantInGround();
}
void PlantInGround()
{
Vector3 mousePOS = Input.mousePosition;
if (Input.GetMouseButtonDown(0))
{
mousePOS.z = +12;
mousePOS = Camera.main.ScreenToWorldPoint(mousePOS);
Instantiate(seedlings, (mousePOS), Quaternion.identity);
Debug.Log(mousePOS);
}
}
Grateful for any help.
To check if the your seedling position is near your player's position:
float maxDist = 3F; //radius within the player that the seedling can be instantiated
if ( (mousePOS - player.transform.position).magnitude < maxDist )
{
//Do Something
}
You could compare the distance squared instead, since magnitude involves a Sqrt() call which is expensive, but given that you are doing this only on a mouse click, it shouldn't really matter much.
Of course, you have to be sure that your player is about 12 units away from the camera's forward viewing direction.. given that you are doing this:
mousePOS.z = +12;
mousePOS = Camera.main.ScreenToWorldPoint(mousePOS);
This is what ended up working. Leaving it for others. I honestly don't know why the coordinates just lined up. Thanks for the help #Lincon.
public class PlantItem : MonoBehaviour
{
public GameObject seedlings;
public GameObject player;
Vector3 mousePOS = Input.mousePosition;
void Update()
{
if (!Input.GetMouseButtonDown(0))
{
PlantInGround();
}
}
void PlantInGround()
{
Vector3 mousePOS = Input.mousePosition;
mousePOS.z = +12;
mousePOS = Camera.main.ScreenToWorldPoint(mousePOS);
if (((player.transform.position.y < mousePOS.y + 0.5) && (player.transform.position.y > mousePOS.y - 1.5)) && ((player.transform.position.x < mousePOS.x + 1) && (player.transform.position.x > mousePOS.x - 1)))
{
Instantiate(seedlings, mousePOS, Quaternion.identity);
}
}
}
In my project written in Unity 5, I am performing the object touch operation with raycast. But, since my object is moving, sometimes raycast cannot reach to my when I touch. As a solution, using fixed update, I set the Fixed Timestep from 0.02 to 0.01 on the project settings. However, at this time, it behaves as if I were touching twice and it receives two touches. I should not use update function and I need to do this using fixed update. How can I solve the problem?
Below is my code:
void FixedUpdate()
{
if (PlayerPrefs.GetInt ("oyunsonu") == 0) {
// Code for OnMouseDown in the iPhone. Unquote to test.
RaycastHit hit = new RaycastHit ();
for (int i = 0; i < Input.touchCount; ++i) {
if (Input.GetTouch (i).phase.Equals (TouchPhase.Began)) {
Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch (i).position);
touchY = Input.GetTouch (i).position.y;
if (Physics.Raycast (ray, out hit)) {
//Debug.Log (hit.transform.gameObject.name.ToString ());
GameObject myBallObject;
myBallObject = GameObject.FindWithTag ("Ball");
Instantiate (effect, myBallObject.transform.position, transform.rotation);
GetComponent<AudioSource> ().PlayOneShot (saundFile [3], 1);
//TextAnimation.Show ();
TouchObjectScript myTouchObjectScript = myBallObject.GetComponent<TouchObjectScript> ();
myTouchObjectScript.BallForce (hit.transform.gameObject.tag.ToString (), touchY, screenHeight);
//PlayerPrefs.SetInt ("aaa", 0);
}
}
}
}
}
You use Update() to detect input or move non physics/rigidbody objects. You use FixedUpdate() to move physics object. Watch UPDATE AND FIXEDUPDATE video from Unity.
Doing GameObject.FindWithTag("Ball"); each time there is touch and raycast is not good. Constantly checking PlayerPrefs.GetInt("oyunsonu") is not good either. Cache your variables if they will be used in a loop multiple times. Below is your fixed code.
AudioSource myAudio;
GameObject myBallObject;
TouchObjectScript myTouchObjectScript;
int oyunsonu = 0;
void Start()
{
myAudio = GetComponent<AudioSource>();
myBallObject = GameObject.FindWithTag("Ball");
myTouchObjectScript = myBallObject.GetComponent<TouchObjectScript>();
oyunsonu = PlayerPrefs.GetInt("oyunsonu");
}
void Update()
{
if (oyunsonu == 0)
{
// Code for OnMouseDown in the iPhone. Unquote to test.
RaycastHit hit = new RaycastHit();
for (int i = 0; i < Input.touchCount; ++i)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(i).position);
touchY = Input.GetTouch(i).position.y;
if (Physics.Raycast(ray, out hit))
{
//Debug.Log (hit.transform.gameObject.name.ToString ());
Instantiate(effect, myBallObject.transform.position, transform.rotation);
myAudio.PlayOneShot(saundFile[3], 1);
//TextAnimation.Show ();
myTouchObjectScript.BallForce(hit.transform.gameObject.tag.ToString(), touchY, screenHeight);
}
}
}
}
}