Unity Raycast has inconsistent behavior. Sometimes it collides with the collider it starts in and sometimes not - unity3d

The text in this image pretty much explains everything.
This is the code that creates the rays. You may ignore Debug.DrawRay().
RaycastHit hitInfo;
RaycastHit wall;
RaycastHit backFace;
if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) {
Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false);
Instantiate(rayOneHit, hitInfo.point, Quaternion.identity);
Enemy target = hitInfo.transform.GetComponent<Enemy>();
if(target != null) {
if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) {
Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false);
Instantiate(rayTwoHit, wall.point, Quaternion.identity);
if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) {
Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false);
Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z);
Instantiate(hitPoint, hitLocation, Quaternion.identity);
}
}
target.dealDamage(damage);
}
lineRenderer.SetPosition(0, aim.transform.position);
lineRenderer.SetPosition(1, hitInfo.point);
}
This is all inside of a function Shoot() which is called as follows:
void Update() {
if(Input.GetButtonDown("Fire1")) {
Shoot();
}
}
Full code if needed:
using System.Collections;
using UnityEngine;
public class Shooting : MonoBehaviour {
[SerializeField] Transform aim;
[SerializeField] LineRenderer lineRenderer;
public float damage = 30f;
public Transform barrelEnd;
public Transform crosshair;
public Light muzzleFlash;
public Object hitPoint;
public Object rayOneHit;
public Object rayTwoHit;
public LayerMask enemyLayer;
//public Light muzzleFlashSelf;
// public GameObject projectile;
public float force = 100f;
void Start() {
Cursor.visible = false;
muzzleFlash.enabled = false;
//muzzleFlashSelf.enabled = false;
}
void Update() {
if(Input.GetButtonDown("Fire1")) {
Shoot();
}
}
void Shoot() {
RaycastHit hitInfo;
RaycastHit wall;
RaycastHit backFace;
StartCoroutine(DrawLine());
StartCoroutine(Flash());
if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) {
Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false);
Instantiate(rayOneHit, hitInfo.point, Quaternion.identity);
Enemy target = hitInfo.transform.GetComponent<Enemy>();
if(target != null) {
if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) {
Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false);
Instantiate(rayTwoHit, wall.point, Quaternion.identity);
if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) {
Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false);
Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z);
Instantiate(hitPoint, hitLocation, Quaternion.identity);
}
}
target.dealDamage(damage);
}
lineRenderer.SetPosition(0, aim.transform.position);
lineRenderer.SetPosition(1, hitInfo.point);
}
}
IEnumerator DrawLine() {
lineRenderer.enabled = true;
float t = 0;
float time = 0.01f;
Vector3 orig = lineRenderer.GetPosition(0);
Vector3 orig2 = lineRenderer.GetPosition(1);
lineRenderer.SetPosition(1, orig);
for (; t < time; t += Time.deltaTime) {
yield return null;
}
lineRenderer.SetPosition(1, orig2);
lineRenderer.enabled = false;
}
IEnumerator Flash() {
float t = 0;
float time = 0.1f;
muzzleFlash.enabled = true;
//muzzleFlashSelf.enabled = true;
for (; t < time; t += Time.deltaTime) {
yield return null;
}
muzzleFlash.enabled = false;
//muzzleFlashSelf.enabled = false;
}
}
I've tried to start the 2nd raycast slightly further inside of the enemy, because I thought maybe it is hitting the very edge of the collider but this changed nothing. I've also tried to use ~enemyMask to exclude the enemy, but this just made the second ray not fire at all.

Related

How to use OnMouseUp in Unity?

I have drag and shoot script in my unity game(like in angry birds).It works correctly if I quickly release the mouse button, but when I hold it for a long time and then release , the object falls to the cursor level and its velocity(which is calculated by object.position - dragStartPosition) become too much.What's wrong with it?
I have these methods:
private Vector3 startPositionOnDrag;
public float throwForce;
private Vector2 initVelocity;
public bool isMoving = false;
public Rigidbody2D rigidbody;
private void OnMouseDown()
{
startPositionOnDrag = new Vector3(transform.position.x, transform.position.y, 0);
}
private void OnMouseDrag()
{
if (!isMoving)
{
Vector3 mousePosVector = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 diff = startPositionOnDrag - mousePosVector;
if (diff.magnitude < 1.5f)
{
transform.position = new Vector3(mousePosVector.x, mousePosVector.y, 0);
}
else
{
float diffLength = diff.magnitude;
Vector3 targetPosition = startPositionOnDrag - ((startPositionOnDrag - mousePosVector) * 1.5f/diffLength);
transform.position = new Vector2(targetPosition.x, targetPosition.y);
}
}
}
private void OnMouseUp()
{
if (!isMoving)
{
Vector2 diff = startPositionOnDrag - transform.position; //smthg wrong in magnitude
Vector2 directionToMove = (startPositionOnDrag - transform.position).normalized; //correct
initVelocity = directionToMove * throwForce * Mathf.Sqrt(diff.magnitude);
rigidbody.velocity = initVelocity;
isMoving = true;
}
}

Unity: When I instantiate my character, it instantiates twice

I'm working on a platformer game, and I instantiate my character after he gets hit by an enemy object. The problem is, my character is instantiating twice. Here's my code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerMove : MonoBehaviour
{
public GameObject player;
private Rigidbody2D rb;
private SpriteRenderer sprite;
private BoxCollider2D coll;
private Animator anim;
public float Speed;
public float JumpForce;
private float dirX = 0f;
private float fJumpPressedRemember = 0f;
private float fJumpPressedRememberTime = 0.2f;
[SerializeField] private LayerMask jumpableGround;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sprite = GetComponent<SpriteRenderer>();
coll = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * Speed, rb.velocity.y);
fJumpPressedRemember -= Time.deltaTime;
if (Input.GetButtonDown("Jump"))
{
fJumpPressedRemember = fJumpPressedRememberTime;
}
if ((fJumpPressedRemember > 0) && isGrounded())
{
fJumpPressedRemember = 0;
rb.velocity = new Vector2(rb.velocity.x, JumpForce);
}
UpdateAnimation();
}
private bool isGrounded()
{
return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
}
void OnCollisionEnter2D(Collision2D spikeCol)
{
if (spikeCol.gameObject.tag.Equals("DamageDealer") == true)
{
Instantiate(player, new Vector3(-10, 1, 0), Quaternion.identity);
Destroy(player);
}
if (spikeCol.gameObject.tag.Equals("End") == true)
{
SceneManager.LoadScene("Level2", LoadSceneMode.Single);
}
if (spikeCol.gameObject.tag.Equals("End2") == true)
{
SceneManager.LoadScene("Level1", LoadSceneMode.Single);
}
}
private void UpdateAnimation()
{
if (dirX > 0f)
{
anim.SetBool("running", true);
sprite.flipX = false;
}
else if (dirX < 0f)
{
anim.SetBool("running", true);
sprite.flipX = true;
}
else
{
anim.SetBool("running", false);
}
}
}
It's supposed to instantiate once after dying. I don't know what's happening, but it only started after I added animation code, so I think that's the issue. Thanks to anyone who can help!
OnCollisionEnter2D is called twice.
Reasons why this can be described here: Why is OnCollisionEnter getting called twice?
You can add instantiate method double call check, like this:
private bool isInited;
void OnCollisionEnter2D(Collision2D spikeCol)
{
if (spikeCol.gameObject.tag.Equals("DamageDealer") == true)
{
if (isInited)
{
return;
}
isInited = true;
Instantiate(player, new Vector3(-10, 1, 0), Quaternion.identity);
Destroy(player);
}
...
}

How to select an object to be picked up whenever the camera is pointed to that object?

I'm trying to pick up objects in unity. I have a gameObject called LookObject. Whenever the camera points to an object, the name of that object will be stored in LookObject, then when I press Space the object gets picked up. it is working but not completely. The issue I'm facing is that when I look at an object then look at another direction, the lookObject still shows the name of the object I was looking at (it doesn't update).
please see this image:
as shown in the image, the reticle is not pointing to the object. but it is still showing Cube as the Look Object.
Here is PlayerInteractions class:
GameObject[] targetObjects;
List<GameObject> targetObjectsList;
[Header("InteractableInfo")]
public float sphereCastRadius = 0.5f;
public int interactableLayerIndex;
private Vector3 raycastPos;
public GameObject lookObject;
private PhysicsObjects physicsObject;
private Camera mainCamera;
public GameObject winUI;
private InteractiveObjects interactiveObjects;
[Header("Pickup")]
[SerializeField] private Transform pickupParent;
public GameObject currentlyPickedUpObject;
private Rigidbody pickupRB;
[Header("ObjectFollow")]
[SerializeField] private float minSpeed = 0;
[SerializeField] private float maxSpeed = 300f;
[SerializeField] private float maxDistance = 8f;
private float currentSpeed = 0f;
private float currentDist = 0f;
[Header("Rotation")]
public float rotationSpeed = 100f;
// Quaternion lookRot;
[SerializeField] GameObject TargetsCanvas;
static bool strikeThrough = false;
private void Start()
{
mainCamera = Camera.main;
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
targetObjectsList = new List<GameObject>();
foreach (var obj in targetObjects)
{
var mytext = CreateText(TargetsCanvas.transform);
mytext.text = "• Find The " + obj.name;
Debug.Log(""+ obj.name);
}
}
//A simple visualization of the point we're following in the scene view
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(pickupParent.position, 0.5f);
}
void Update()
{
//Here we check if we're currently looking at an interactable object
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
//if we press the button of choice
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (currentlyPickedUpObject == null)
{
//and we are looking an interactable object
if (lookObject != null )
{
PickUpObject();
if (!targetObjectsList.Contains(lookObject.gameObject))
{
targetObjectsList.Add(lookObject.gameObject);
if (targetObjectsList.Count == targetObjects.Length)
{
// Time.timeScale = 0f;
// winUI.SetActive(true);
// SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
// Time.timeScale = 1f;
}
}
}
}
//if we press the pickup button and have something, we drop it
else
{
BreakConnection();
}
}
}
private void FixedUpdate()
{
if (currentlyPickedUpObject != null)
{
currentDist = Vector3.Distance(pickupParent.position, pickupRB.position);
currentSpeed = Mathf.SmoothStep(minSpeed, maxSpeed, currentDist / maxDistance);
currentSpeed *= Time.fixedDeltaTime;
Vector3 direction = pickupParent.position - pickupRB.position;
pickupRB.velocity = direction.normalized * currentSpeed;
//Rotation//
// lookRot = Quaternion.LookRotation(mainCamera.transform.position - pickupRB.position);
// lookRot = Quaternion.Slerp(mainCamera.transform.rotation, lookRot, rotationSpeed * Time.fixedDeltaTime);
// pickupRB.MoveRotation(lookRot);
}
}
//Release the object
public void BreakConnection()
{
pickupRB.constraints = RigidbodyConstraints.None;
currentlyPickedUpObject = null;
lookObject = null;
physicsObject.pickedUp = false;
currentDist = 0;
}
public void PickUpObject()
{
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
Here is the code attached to objects:
public float waitOnPickup = 0.2f;
public float breakForce = 35f;
[HideInInspector] public bool pickedUp = false;
[HideInInspector] public PlayerInteractions playerInteractions;
private void OnCollisionEnter(Collision collision)
{
if (pickedUp)
{
if (collision.relativeVelocity.magnitude > breakForce)
{
playerInteractions.BreakConnection();
}
}
}
//this is used to prevent the connection from breaking when you just picked up the object as it sometimes fires a collision with the ground or whatever it is touching
public IEnumerator PickUp()
{
yield return new WaitForSecondsRealtime(waitOnPickup);
pickedUp = true;
}
Here is an image of the object inspector:
how can I make it accurately showing the objects I'm looking at?
A simple fix for this would be to set lookObject to null if the SphereCast returns false since that would indicate you are no longer looking at a valid object. Simply adding else lookObject = null; to the end of the first if statement in your Update() method should do the trick.

When I hit E,and the raycast doesnt hit an object,I get this error message:NullReferenceException:Object reference not set to an instance of an object [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Player : MonoBehaviour
{
[SerializeField] private DialogueUI dialogueUI;
public Vector3 raypositionup = new Vector3(5, 0, 0);
public Vector3 raypositiondown = new Vector3(5, 0, 0);
private float MoveSpeed = 7f;
public Animator animator;
public DialogueUI DialogueUI => dialogueUI;
public Interactable Interactable { get; set; }
bool move = false;
bool movefoward = false;
public Rigidbody2D rb;
Vector2 movement;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (DialogueUI.IsOpen) return;
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown(KeyCode.W))
{
move = true;
movefoward = false;
animator.SetBool("Lookingfoward", true);
}
if (Input.GetKeyDown(KeyCode.S))
{
movefoward = true;
move = false;
animator.SetBool("Lookingfoward", false);
}
if (Input.GetKey(KeyCode.LeftShift))
{
MoveSpeed = 10f;
}
else
{
MoveSpeed = 7f;
}
if (Input.GetKeyDown(KeyCode.Return))
{
if (Interactable != null)
{
Interactable.Interact(this);
}
}
if (move == true)
{
if (Input.GetKey(KeyCode.E))
{
Debug.DrawRay(transform.position + raypositionup, transform.up * 2f, Color.red);
RaycastHit2D hit = Physics2D.Raycast(transform.position + raypositionup , transform.up, 2f);
if (hit.collider.CompareTag("Object"))
{
Debug.Log("Poop");
hit.transform.GetComponent<SpriteRenderer>().color = Color.red;
}
if (hit.collider == null)
{
return;
}
}
}
}
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * MoveSpeed * Time.fixedDeltaTime);
}
}
Ive tried several different fixes and none seem to work, such as another if statement, else statements, and even reworking the entire raycast system. If anyone knows a solution that would be just great. Also, any criticism on my code is okay and endorsed, im new to coding and want any opportunity I can get to clean up my code as best as possible.
You have code for return if collider is null, but not catch if null on compare:
if (hit.collider == null)
{
return;
}
elseif (hit.collider.CompareTag("Object")) //No catched if null
{
Debug.Log("Poop");
hit.transform.GetComponent<SpriteRenderer>().color = Color.red;
}

MissingComponentException: There is no 'SpriteRenderer' attached to the "Balletjes(Clone)" game object, but a script is trying to access it

I get this error:
"MissingComponentException: There is no 'SpriteRenderer' attached to the "Balletjes(Clone)" game object, but a script is trying to access it."
I would like to assign the name of a sprite to the variable goldCoinPopUp. I searched a lot but nothing worked. Can anyone help me please?
ItemDragHandler
public class ItemDragHandler : MonoBehaviour, IDragHandler, IEndDragHandler
{
public GameObject prefab;
Vector2 original;
public void OnDrag(PointerEventData eventData)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100);
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))
{
if (hit.collider.gameObject.name == "Terrain")
{
transform.gameObject.SetActive(false);
GameObject obj = Instantiate(prefab,hit.point, Quaternion.EulerAngles(0f, 3f, 0f)); // 3d object
obj.AddComponent<GenerateResources>(); // link to GenerateResources script
Vector3 img = new Vector3(0, 8.66f, 0);
obj.transform.position += img;
}
else
{
transform.localPosition = original;
}
}
else
{
transform.localPosition = original;
}
}
void Start ()
{
original = transform.localPosition;
}
}
GenerateResources
public class GenerateRessources : MonoBehaviour
{
private int ressourcesInBuilding;
public int valueWhenCollect = 10;
private float timerExtraRessource;
public float generateRate = 4;
public SpriteRenderer goldCoinPopUp;
private GameObject currentGameObject;
private GameObject Inventory;
void Start ()
{
goldCoinPopUp = GetComponent<SpriteRenderer>();
goldCoinPopUp.enabled = false;
currentGameObject = gameObject.GetComponent<GameObject>();
Inventory = GameObject.Find("Inventory");
}
void Update ()
{
AddResource();
getResources();
}
private void AddResource()
{
timerExtraRessource += Time.deltaTime;
if (timerExtraRessource >= generateRate)
{
ressourcesInBuilding++;
timerExtraRessource = 0;
if (ressourcesInBuilding >= valueWhenCollect)
{
goldCoinPopUp.enabled = true;
}
}
}
private void getResources()
{
if (Input.touchCount > 0)
{
Touch touch = Input.touches[0];
if (touch.phase == TouchPhase.Began)
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100f))
{
if (hit.transform.gameObject != null)
{
}
}
}
}
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100f))
{
if (hit.transform.gameObject != currentGameObject)
{
if (goldCoinPopUp.enabled == true)
{
goldCoinPopUp.enabled = false;
Inventory.GetComponent<GameController>().Gold += ressourcesInBuilding;
ressourcesInBuilding = 0;
}
}
}
}
}
}
Your code depends on SpriteRenderer being present, while your prefab does not have one. You can work around this problem by adding the following attribute before your class definiton
[RequireComponent(typeof(SpriteRenderer))]
public class GenerateRessources : MonoBehaviour
However the automatically added SpriteRenderer will have no sprite attached, hence a better solution would be to make sure your prefab does indeed have a reasonable sprite attached