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;
}
Related
I am trying to disable a component/script on a game object when the Raycast has not hit but enable it when it does hit. I can enable the script and that part works when the Raycast hits, however, when I move away from the object, it is still enabled and I cant seem to figure this out.
The entire code to do this is in my update function.
// Update is called once per frame
void Update()
{
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f));
float distance = 100f;
Debug.DrawRay(ray.origin, ray.direction * distance, Color.green);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
// hit!
Debug.Log("Raycast is hitting " + hit.transform.gameObject);
if (hit.transform.tag == "Crate")
{
crossair.color = Color.red;
hit.transform.GetComponent<Outline>().enabled = true; // this works
}
else
{
crossair.color = Color.white;
hit.transform.GetComponent<Outline>().enabled = false; //Does not work
}
}
}
You will need to store the currently active one in order to deactivate it in case
private const float distance = 100f;
private Outline currentHit;
void Update()
{
var ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f));
if (Physics.Raycast(ray, out var hit))
{
// hitting something
// Actually checking this way makes you tag redundant ;)
if (hit.transform.TryGetComponent<Outline>(out var outline))
{
// Hitting and outline!
Debug.DrawLine(ray.origin, hit.point, Color.red);
if(outline != currentHit)
{
// Hitting a different outline!
crossair.color = Color.red;
if(currentHit)
{
currentHit.enabled = false;
}
currentHit = outline;
currentHit.enabled = true;
}
}
else
{
// Hitting something that is not an outline
crossair.color = Color.white;
Debug.DrawLine(ray.origin, hit.point, Color.green);
if(currentHit)
{
currentHit.enabled = false;
currentHit = null;
}
}
}
else
{
// Not hitting anything
crossair.color = Color.white;
Debug.DrawRay(ray.origin, ray.direction * distance, Color.green);
if(currentHit)
{
currentHit.enabled = false;
currentHit = null;
}
}
}
In Unity 3D I'd like to create a crosshair for my top-down 2D-shooter that gradually moves to its target whenever the player has the same x-position as the target.
The problem is that I want a smooth animation when the crosshair moves to the target. I have included a small gif from another game that shows a crosshair I'd like to achieve. Have a look at it:
Crosshair video
I tried to do that with the following script but failed - the crosshair jumps forth and back when the enemies appear. It doesn't look so smooth like in the video I mentioned above.
The following script is attached to the player:
[SerializeField]
private GameObject crosshairGO;
[SerializeField]
private float speedCrosshair = 100.0f;
private Rigidbody2D crosshairRB;
private bool crosshairBegin = true;
void Start () {
crosshairRB = crosshairGO.GetComponent<Rigidbody2D>();
crosshairBegin = true;
}
void FixedUpdate() {
//Cast a ray straight up from the player
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!
Debug.Log("we touched the enemy");
Vector2 _direction2 = (_hit.collider.gameObject.transform.position - crosshairGO.transform.position).normalized;
crosshairRB.velocity = new Vector2(this.transform.position.x, _direction2.y * speedCrosshair);
crosshairBegin = false;
} else {
// Nothing hit
Debug.Log("nothing hit");
crosshairRB.velocity = Vector2.zero;
Vector2 _pos2 = new Vector2(this.transform.position.x, 4.5f);
if (crosshairBegin) crosshairGO.transform.position = _pos2;
}
}
I think you need create a new variable call Speed translation
with
speed = distance from cross hair to enemy position / time (here is Time.fixedDeltaTime);
then multiply speed with velocity, the cross hair will move to enmey positsion in one frame.
but you can adjust speed by mitiply it with some float > 0 and < 1;
I'm using Unity to move my player sprite in a 2D platformer where it'll go right if right key is pressed & left if left key is pressed.
The right movement is working fine, but whenever the player moves left, I'm changing the Y rotation to 180 and the player is supposed to show left animation only but it keeps rotating back and forth when going left.
See the video sample.
This is my code:
private Rigidbody2D rb;
private void FixedUpdate() {
InputManager();
}
private void InputManager()
{
float hDir = Input.GetAxis("Horizontal");
if (state != State.hurt) {
if(!anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
{
if (hDir < 0) // Go left
{
rb.velocity = new Vector2(-speedX, rb.velocity.y);
transform.Rotate(0f, 180f, 0f);
}
else if (hDir > 0) // Go right
{
rb.velocity = new Vector2(speedX, rb.velocity.y);
transform.Rotate(0f, 0f, 0f);
}
}
}
}
How can I make my player stick to left animation when going on left? Please do not suggest changing localScale as I know it works but for shooting purpose it's best that my player rotates.
Here is a thing transform.Rotate(0,180,0); is used to rotate 180 degree in y axis each time when this line of code execute that's why you player does not stop rotating
Here is the code you can use it will stop you player rotate according to the directions
private void InputManager()
{
float hDir = Input.GetAxis("Horizontal");
if (state != State.hurt)
{
if (!anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
{
if (hDir < 0) // Go left
{
rb.velocity = new Vector2(-speedX, rb.velocity.y);
this.transform.rotation = Quaternion.AngleAxis(180, Vector3.up);
}
else if (hDir > 0) // Go right
{
rb.velocity = new Vector2(speedX, rb.velocity.y);
this.transform.rotation = Quaternion.AngleAxis(0, Vector3.up);
}
}
}
}
You need to add a bool Variable that is checking if the player is facing right, as well as a Flip Function that flips him:
bool bFacingRight = true;
private Rigidbody2D rb;
int _switch;
private void FixedUpdate() {
InputManager();
}
private void InputManager()
{
if (state == State.hurt || anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
return;
float hDir = Input.GetAxis("Horizontal");
// Move the character by finding the target velocity
rb.velocity = new Vector2(hDir * 10f, rb.velocity.y);
if (hDir < 0 && bFacingRight) { // Flip Left
_switch = 0;
Flip(_switch);
}
else if (hDir > 0 && !bFacingRight) { // Flip Right
_switch= 1;
Flip(_switch);
}
}
private void Flip(int swth)
{
// Switch the way the player is labelled as facing.
bFacingRight = !bFacingRight;
// Multiply the player's x local scale by -1.
switch(swth)
{
case 0:
this.transform.rotation = Quaternion.AngleAxis(180, Vector3.up);
break;
case 1:
this.transform.rotation = Quaternion.AngleAxis(0, Vector3.up);
break;
}
}
I'm making a simple game in Unity in 2d. So I have the player and I have a grappler gun. And when I press the right mouse button and the mouse position is in the direction of any object it will grappler to this object. But my question is, I have a small circle (Grappling point) and it is very hard to put the mouse to the grappler point circle direction in the air. so can I add any offset f.e. when my mouse direction from the player is 2 units from the grappler point circle it will still grappler but automatically the grappler line will connect with the grappler point circle. Here is my grappler code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grappler: MonoBehaviour
{
public LineRenderer Line;
DistanceJoint2D Joint;
Vector3 TargetPos;
RaycastHit2D Hit;
public float Distance = 10f;
public LayerMask Mask;
public float Step = 0f;
void Start()
{
Joint = GetComponent<DistanceJoint2D>();
Joint.enabled = false;
Line.enabled = false;
}
void Update()
{
if(Joint.distance > 1f)
{
Joint.distance -= Step;
}else
{
Line.enabled = false;
Joint.enabled = false;
}
if(Input.GetMouseButtonDown(1))
{
TargetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
TargetPos.z = 0;
Hit = Physics2D.Raycast(transform.position, TargetPos - transform.position, Distance, Mask);
if (Hit.collider != null && Hit.collider.gameObject.GetComponent<Rigidbody2D>() != null)
{
Joint.enabled = true;
Joint.connectedBody = Hit.collider.gameObject.GetComponent<Rigidbody2D>();
Joint.distance = Vector3.Distance(transform.position, Hit.point);
Line.enabled = true;
Line.SetPosition(0, transform.position);
Line.SetPosition(1, Hit.point);
}
}
if (Input.GetMouseButton(1))
{
Line.SetPosition(0, transform.position);
}
if (Input.GetMouseButtonUp(1))
{
Joint.enabled = false;
Line.enabled = false;
}
}
}
Any ideas?? Thanks
It sounds like you should simply make your collision bigger by 2 units (if that's the amount you want to cheat by). And then instead of using the ray trace hit location, use the location of the object you hit instead.
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