Box collider doesn't catch mouse button press event - c#

I have a cupboard with 2 colliders - one for cupboard and one for it's box. When I press on the box, I want to open/close it. It worked fine, but now by some reason it only work when I press on the edge on the box. When click on the center, it don't work.
Video: https://youtu.be/OozsAi7KNzs
Here is the code, which play animation (open/close cupboard), when I press on the box:
public Animation[] animations;
public string[] animationName;
public bool playOneDirection; // should revert animation speed after second playing?
public AudioSource myAudioOpen;
public AudioSource myAudioClose;
private bool isDoorClosed;
private bool isAimationReadyToPlay = true;
private Collider thisCollider;
public void Start()
{
thisCollider = GetComponent<Collider>();
}
void Update ()
{
if (Input.GetButton("Fire1"))
if(DoPlayerLookAtButton() && isAimationReadyToPlay)
OpenCloseDoor();
}
bool DoPlayerLookAtButton()
{
RaycastHit _hit;
Ray _ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
bool isHit = Physics.Raycast(_ray, out _hit, 1.5f);
if (isHit && _hit.collider == thisCollider) return true;
else return false;
}
public void OpenCloseDoor()
{
if (!isDoorClosed) // Play animation with normal speed
{
myAudioOpen.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = 1.0f;
animations[i].Play();
}
}
if(playOneDirection)
return;
if (isDoorClosed) // Play animation with revert speed
{
myAudioClose.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = -1.0f;
animations[i][animationName[i]].time = animations[i][animationName[i]].length;
animations[i].Play();
}
}
StartCoroutine("DelayBetweenAnimations");
isDoorClosed = !isDoorClosed;
}
IEnumerator DelayBetweenAnimations()
{
isAimationReadyToPlay = false;
yield return new WaitForSeconds(0.5f);
isAimationReadyToPlay = true;
}

Your cupboard has 2 colliders, but you are only checking for one of them. If there is some kind of overlap then it could be fiddly to click the correct one. If you just want to be able to click anywhere on the game object change your code like so...
//From
//if (isHit && _hit.collider == thisCollider) return true;
//To
if (isHit && _hit.transform.gameObject == this.gameObject) return true;
Add a layer mask for your player and ensure your Physics.Raycast excludes that layer, to avoid the cast from hitting yourself. See here

I've made the main camera starting from the center of the player, so raycast hits player's collider. I made it trying to fix the bug when camera can go throuth the wall like on the screen below.
Raycast can be seen passing through the player and did not reach the box

Related

Input.GetKeyDown calls a coroutine to put in 1st person but it blocks my character's movements

In this script, I call a coroutine using Input; this coroutine allows that if we press F5, change the view of our character that we see in 3rd person mode to 1st person mode and if we press F5 again, it will replace the view. Only, by using input.GetKey and not input.GetKeyDown the coroutine will read several times which gives an ugly result. But when I use Input.GetKeyDown, and I'm in 1st person mode (the basic view is in 3rd person) my character's movements are blocked: I can't move forward, backward, jump etc... error in the script ?
If you want to test on unity, just download the file on this link https://github.com/TUTOUNITYFR/unitypackages-jeu-survie-2022-tufr/blob/main/Episode01/personnage-et-environnement.unitypackage then in the AimBehaviourBasic script, replace all the code with:
`
using UnityEngine;
using System.Collections;
// AimBehaviour inherits from GenericBehaviour. This class corresponds to aim and strafe behaviour.
public class AimBehaviourBasic : GenericBehaviour
{
public Texture2D crosshair; // Crosshair texture.
public float aimTurnSmoothing = 0.15f; // Speed of turn response when aiming to match camera facing.
public Vector3 aimPivotOffset = new Vector3(0.5f, 1.2f, 0f); // Offset to repoint the camera when aiming.
public Vector3 aimCamOffset = new Vector3(0f, 0.4f, -0.7f); // Offset to relocate the camera when aiming.
private int aimBool; // Animator variable related to aiming.
public bool aim; // Boolean to determine whether or not the player is aiming.
// Start is always called after any Awake functions.
void Start ()
{
// Set up the references.
aimBool = Animator.StringToHash("Aim");
}
// Update is used to set features regardless the active behaviour.
void Update ()
{
// Activate/deactivate aim by input.
if (Input.GetKeyDown (KeyCode.F5) && !aim)
{
StartCoroutine(ToggleAimOn());
}
else
if (Input.GetKeyDown (KeyCode.F5) && aim)
{
StartCoroutine(ToggleAimOff());
}
}
// Co-rountine to start aiming mode with delay.
private IEnumerator ToggleAimOn()
{
yield return new WaitForSeconds(0.05f);
// Aiming is not possible.
if (behaviourManager.GetTempLockStatus(this.behaviourCode) || behaviourManager.IsOverriding(this))
yield return false;
// Start aiming.
else
{
aim = true;
int signal = 1;
yield return new WaitForSeconds(0.1f);
aimCamOffset.x = Mathf.Abs(aimCamOffset.x) * signal;
aimPivotOffset.x = Mathf.Abs(aimPivotOffset.x) * signal;
yield return new WaitForSeconds(0.1f);
behaviourManager.GetAnim.SetFloat(speedFloat, 0);
// This state overrides the active one.
behaviourManager.OverrideWithBehaviour(this);
}
}
// Co-rountine to end aiming mode with delay.
private IEnumerator ToggleAimOff()
{
aim = false;
yield return new WaitForSeconds(0.3f);
behaviourManager.GetCamScript.ResetTargetOffsets();
behaviourManager.GetCamScript.ResetMaxVerticalAngle();
yield return new WaitForSeconds(0.05f);
behaviourManager.RevokeOverridingBehaviour(this);
}
// LocalFixedUpdate overrides the virtual function of the base class.
public override void LocalFixedUpdate()
{
// Set camera position and orientation to the aim mode parameters.
if(aim)
{
behaviourManager.GetCamScript.SetTargetOffsets (aimPivotOffset, aimCamOffset);
}
}
// LocalLateUpdate: manager is called here to set player rotation after camera rotates, avoiding flickering.
public override void LocalLateUpdate()
{
AimManagement();
}
// Handle aim parameters when aiming is active.
void AimManagement()
{
// Deal with the player orientation when aiming.
Rotating();
}
// Rotate the player to match correct orientation, according to camera.
void Rotating()
{
// Always rotates the player according to the camera horizontal rotation in aim mode.
Quaternion targetRotation = Quaternion.Euler(0, behaviourManager.GetCamScript.GetH, 0);
float minSpeed = Quaternion.Angle(transform.rotation, targetRotation) * aimTurnSmoothing;
// Rotate entire player to face camera.
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, minSpeed * Time.deltaTime);
}
// Draw the crosshair when aiming.
void OnGUI ()
{
if (crosshair)
{
float mag = behaviourManager.GetCamScript.GetCurrentPivotMagnitude(aimPivotOffset);
GUI.DrawTexture(new Rect(Screen.width / 2 - (crosshair.width * 0.5f),
Screen.height / 2 - (crosshair.height * 0.5f),
crosshair.width, crosshair.height), crosshair);
}
}
}
`
Now you can test, press play and then press F5: the camera will change position but if you try to move (les touches : Z,Q,S,D, escape or arrowUp, arrowDown etc..) absolutely nothing will happen: here is my problem... Sorry for my bad English I am French ; )
Using coroutines like this will mess up your code because both coroutines can run almost at the same time, for example:
If you press F5 the first time ToggleAimOn() will be executed but before it get's finished you can press F5 again and call ToggleAimOff(), and since coroutines are async, that means they can run at the same time so it will create a real messed up behaviour
What you can try is to have another flag beside aim that will check if a coroutine is running like this:
private bool IsCoroutineActive = false;
void Update ()
{
// Activate/deactivate aim by input.
if (Input.GetKeyDown (KeyCode.F5) && !IsCoroutineActive && !aim)
{
StartCoroutine(ToggleAimOn());
}
else
if (Input.GetKeyDown (KeyCode.F5) && !IsCoroutineActive && aim)
{
StartCoroutine(ToggleAimOff());
}
}
// Co-rountine to start aiming mode with delay.
private IEnumerator ToggleAimOn()
{
IsCoroutineActive = true;
yield return new WaitForSeconds(0.05f);
// Aiming is not possible.
if (behaviourManager.GetTempLockStatus(this.behaviourCode) || behaviourManager.IsOverriding(this))
{
IsCoroutineActive = false;
yield return false;
}
// Start aiming.
else
{
aim = true;
int signal = 1;
yield return new WaitForSeconds(0.1f);
aimCamOffset.x = Mathf.Abs(aimCamOffset.x) * signal;
aimPivotOffset.x = Mathf.Abs(aimPivotOffset.x) * signal;
yield return new WaitForSeconds(0.1f);
behaviourManager.GetAnim.SetFloat(speedFloat, 0);
// This state overrides the active one.
behaviourManager.OverrideWithBehaviour(this);
}
IsCoroutineActive = false;
}
// Co-rountine to end aiming mode with delay.
private IEnumerator ToggleAimOff()
{
IsCoroutineActive = true;
aim = false;
yield return new WaitForSeconds(0.3f);
behaviourManager.GetCamScript.ResetTargetOffsets();
behaviourManager.GetCamScript.ResetMaxVerticalAngle();
yield return new WaitForSeconds(0.05f);
behaviourManager.RevokeOverridingBehaviour(this);
IsCoroutineActive = false;
}

OverlapBox sometimes not working as expected, Unity 2d

I am using an overlap box to detect whether an arrow hits the ground or an enemy, the arrow hitting the enemy works, it's that often the arrow will just pass through the ground, not sure if I am missing something.
Heres my code for detecting the ground or enemy.
private void FixedUpdate()
{
damageHit = Physics2D.OverlapBox(damagePosition.position, damageSize, whatIsEnemy);
groundHit = Physics2D.OverlapBox(damagePosition.position, damageSize, whatIsGround);
if (!hasHitGround)
{
if (damageHit)
{
Collider2D[] detectedObjects = Physics2D.OverlapBoxAll(damagePosition.position, damageSize, whatIsEnemy);
foreach (Collider2D collider in detectedObjects)
{
IDamageable damageable = collider.GetComponent<IDamageable>();
if (damageable != null)
{
damageable.Damage(weaponData.attackDamage);
Destroy(gameObject);
}
IKnockbackable knockbackable = collider.GetComponent<IKnockbackable>();
if (knockbackable != null)
{
knockbackable.KnockBack(weaponData.knockbackAngle, weaponData.knockbackStrength, (int)Mathf.Sign(transform.rotation.y));
}
}
}
if (groundHit)
{
rb.gravityScale = 0f;
rb.velocity = Vector2.zero;
hasHitGround = true;
Destroy(gameObject, 10f);
}
else if (Mathf.Abs(xStartPos - transform.position.x) >= travelDistance && !isGravityOn)
{
isGravityOn = true;
rb.gravityScale = gravity;
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(damagePosition.position, damageSize) ;
}
I thought that the hitbox may have been to small and it would just pass through wall due to not hitting it on any frame. After making the hitbox larger it still did not help, here you can see that even though the overlap box is overlaping the collider the arrow will continue on.

How to switch between the OnTriggerExit/Enter logic depending on the situation?

The script is attached to two gameobjects.
One it's colliding area the collider is big enough so when the game start the player is already inside the collider area and then when he exit the area everything is working fine.
The problem is when I attached the object to another gameobject with collider but this time the collider s smaller and he is inside the bigger collider so now the player is entering the smaller collider and not first time exiting. but I want in both case to make the same effect.
If the player exit the collider he slow down wait then turn around and move inside back.
The same I want to make when the player getting closer to the fire flames slow down wait turn around and move back in it's just with the flames the player entering the collider area and then exit while in the bigger collider he first exit then enter.
Screenshot of the two colliders areas. The small one on the left is the one the player entering first and not exiting. The game start when the player is in the bigger collider area.
And the script :
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.Characters.ThirdPerson;
public class DistanceCheck : MonoBehaviour
{
public Transform targetToRotateTowards;
public Transform colliderArea;
public float lerpDuration;
public float rotationSpeed;
[TextArea(1, 2)]
public string textToShow;
public GameObject descriptionTextImage;
public TextMeshProUGUI text;
public ThirdPersonUserControl thirdPersonUserControl;
private Animator anim;
private float timeElapsed = 0;
private float startValue = 1;
private float endValue = 0;
private float valueToLerp = 0;
private bool startRotating = false;
private bool slowOnBack = true;
private bool exited = false;
private Vector3 exitPosition;
private float distance;
void Start()
{
anim = transform.GetComponent<Animator>();
}
private void FixedUpdate()
{
if (startRotating)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(targetToRotateTowards.position - transform.position),
rotationSpeed * Time.deltaTime);
}
if (exitPosition != new Vector3(0, 0, 0) && slowOnBack)
{
distance = Vector3.Distance(transform.position, exitPosition);
}
if (distance > 5 && slowOnBack)
{
slowOnBack = false;
StartCoroutine(SlowDown());
}
}
private void OnTriggerExit(Collider other)
{
if (other.name == colliderArea.name)
{
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
}
private void OnTriggerEnter(Collider other)
{
if (other.name == colliderArea.name)
{
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
}
IEnumerator SlowDown()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
if (exited)
{
yield return new WaitForSeconds(3f);
startRotating = true;
StartCoroutine(SpeedUp());
}
if (slowOnBack == false)
{
thirdPersonUserControl.enabled = true;
}
}
IEnumerator SpeedUp()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
}
}
Two problems I'm facing right now :
The smaller collider the player enter it then exit and the bigger collider the player exit it then enter so I need to change somehow the OnTriggerExit/Enter behavior logic in the script. but how to make the logic ?
Maybe it's better to make the script to be on the player object only and make it some how generic so I can drag to it many colliders and to make the effect in each one of the colliders the problem is how to make a text field for each collider area ? Now because I attach the script to each collider object I have one colliderArea variable but if I want to make the script only attached to the player I need to change the colliderArea variable to a List<Transform> collidersAreas and then how to create a text field/area to each collider area in the List ?
I think I would do this by creating two tags, NoExit and NoEntry. Once the tags are created you can set the tag on the GameObjects that hold your colliders. Then you can check for tags in the OnTriggerEnter and OnTriggerExit and act accordingly.
I can't tell for sure which functionality you've got written is for which case, or if it's both - everything looks muddled. Ultimately what you should be going for is a function like RepositionPlayer, that moves them back to before they're violating the no entry or no exit rules, and then some kind of OnPlayerRepositioned event that restores their control.
I'll leave it up to you to split out your functionality, but in general I'd look to do something like the following:
private void OnTriggerExit(Collider other)
{
if (other.tag == "NoExit")
{
RepositionPlayer();
}
else if(other.tag == "NoEntry")
{
OnPlayerRepositioned();
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "NoExit")
{
OnPlayerRepositioned();
}
else if(other.tag == "NoEntry")
{
RepositionPlayer();
}
}
And again here it's not clear to me what you're trying to do to reposition the player, but it looks like it's something like:
private void RepositionPlayer()
{
// Stuff that needs to happen to reposition the player
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
private void OnPlayerRepositioned()
{
// stuff you need to do to clear the "repositioning" status
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
Splitting up the logic like this makes it easier to both read and maintain.

OnCollisionExit is called, but behaves strangely

I'm developing a 3d FPS game in Unity. At the moment I'm implementing a wall-running mechanic. The logic behind this is very simple - if player is pushing forward, and not grounded, and touching a wall, I constrain the Y/Z direction (but player can still run forward as I ignore the X direction though) and turn off gravity. It seems to work fine, a little bit clumsy but ok for me. Except, when the wall is left behind player is still able to run in mid-air until he runs out of inertia (here's the example: https://imgur.com/a/LtbWs9J). Here's the code:
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class WallRunning : MonoBehaviour
{
public AudioClip audioClip;
private CharacterMovement cm;
private Rigidbody rb;
private bool isJumping;
public bool isWall;
private bool playAudio;
private AudioSource audioSource;
public float energyLimit = 3.5f;
private void Start()
{
//Get attached components so we can interact with them in our script.
cm = GetComponent<CharacterMovement>();
rb = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
private void FixedUpdate()
{
bool jumpPressed = Input.GetButtonDown("Jump");
float verticalAxis = Input.GetAxis("Vertical");
//Check if the controller is grounded.
if (cm.Grounded)
{
isJumping = false;
isWall = false;
}
//Has the jump button been pressed.
if (jumpPressed)
{
StartCoroutine(Jumping());
}
//If we are pushing forward, and not grounded, and touching a wall.
if (verticalAxis > 0 && isJumping && isWall)
{
StartCoroutine(Energy());
//We constrain the Y/Z direction to defy gravity and move off the wall.
//But we can still run forward as we ignore the X direction.
rb.useGravity = false;
rb.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotation;
//We also telegraph to the player by playing a sound effect on contact.
if (audioClip != null && playAudio == true)
{
audioSource.PlayOneShot(audioClip);
//We block more audio being played while we are on the wall.
playAudio = false;
}
}
else
{
StopCoroutine(Energy());
//We need to make sure we can play audio again when touching the wall.
playAudio = true;
rb.useGravity = true;
rb.constraints = RigidbodyConstraints.FreezeRotation;
}
}
void OnCollisionEnter(Collision other)
{
//Are we touching a wall object?
if (other.gameObject.CompareTag("Walls"))
{
isWall = true;
}
}
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (!other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}
IEnumerator Jumping()
{
//Check for 5 frames after the jump button is pressed.
int frameCount = 0;
while (frameCount < 5)
{
frameCount++;
//Are we airbourne in those 5 frames?
if (!cm.Grounded)
{
isJumping = true;
}
yield return null;
}
}
IEnumerator Energy()
{
yield return new WaitForSeconds(energyLimit);
isWall = false;
}
}
Notice: walls have box colliders on them ("Is Trigger" checkbox is unchecked), and player has non-kinematic rigidbody and capsule collider attached. Walls aren't marked as "static" and assigned to Default layer, while player is assigned to the Player layer.
What am I doing wrong? I'm sure I screwed up with the code, but can't figure out the problem.
Replace
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (!other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}
With
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}

unity raycast , on raycast leave , how to ? c#

i have a problem understanding how to keep a reference to an object that has previously been hit by a raycast .
for example i can have a raycast script put on the camera of my 1rst person controller going from the camera position to the forwad vector * some value
this script is attached to the camera
public class raycast : MonoBehaviour {
float lenthRay = 10.0f;
Vector3 originePos;
Vector3 dir;
RaycastHit hitinfo;
GameObject hitten;
bool isHitting;
Color beforC;
int selectionLayer = 9;
void Update () {
originePos = Camera.main.transform.position;
dir = Camera.main.transform.forward * lenthRay;
Debug.DrawRay(originePos, dir, Color.blue);
if (Physics.Raycast(originePos, dir, out hitinfo, lenthRay , selectionLayer)) {
hitten = hitinfo.transform.gameObject;
MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
beforC = tmp.material.color;
tmp.material.color = Color.black;
}
//hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
print(hitten.name);
}
}
it is working great , except if i try to access the GameObject Hitten outside my if condition (like the print print(hitten.name))
i get this error before hitting an object from the right layer :
NullReferenceException: Object reference not set to an instance of an object
raycast.Update () (at Assets/raycast.cs:30)
then when i hit the object it is ok
but the problem is , i dont understand how i can change back the object color to its original color (beforC) after turning it to Color.black when the ray exit the object
this is what i try to do in the commented line , but i just get the same error than with the print , and nothing is turning black .
i have tried this :
originePos = Camera.main.transform.position;
dir = Camera.main.transform.forward * lenthRay;
Debug.DrawRay(originePos, dir, Color.blue);
isHitting = Physics.Raycast (originePos, dir, out hitinfo, lenthRay, selectionLayer);
if (isHitting) {
hitten = hitinfo.transform.gameObject;
MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
beforC = tmp.material.color;
tmp.material.color = Color.black;
}
if(!isHitting){
hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
print(hitten.name);
}
but it is not working either
can you help me understand the logic i should be using
thanks in advance
I had this same need, when I was trying to detect when a wall was obstructing the player. I had never used a Raycast (or Linecast) before, and was surprised there wasn't a built in method to detect 'Enter', 'Stay', and 'Leave' events.
So I created this simple class to handle the details for me. I didn't bother creating any class constructors, and just exposed the properties as public. It handles both Raycasts and Linecasts.
This class tracks your objects each frame for you once you set it up using the properties.
Here's some sample code to demonstrate potential usage (the code I use for my wall detection):
private RayCaster wallRay;
void Start() {
wallRay = new RayCaster();
wallRay.OnRayEnter += WallRay_OnEnter;
wallRay.OnRayExit += WallRay_OnExit;
wallRay.LayerMask = RayCaster.GetLayerMask("Wall");
wallRay.StartTransform = camera.transform;
wallRay.EndTransform = PlayerManager.Player.transform;
}
void Update() {
wallRay.CastLine();
}
void WallRay_OnEnter(Collider collider) {
// Fade OUT wall section [Needs DOTween (free) installed]
collider.gameObject.renderer.material.DOFade(0.65f, 0.2f);
}
void WallRay_OnExit(Collider collider) {
// Fade IN wall section
collider.gameObject.renderer.material.DOFade(1f, 0.2f);
}
Here's the RayCaster class:
using System;
using System.Collections;
using UnityEngine;
public class RayCaster {
public Transform StartTransform;
public Transform EndTransform;
public Vector3 Direction;
public float RayLength;
public int LayerMask = 0;
public event Action<Collider> OnRayEnter;
public event Action<Collider> OnRayStay;
public event Action<Collider> OnRayExit;
Collider previous;
RaycastHit hit = new RaycastHit();
public bool CastRay() {
Physics.Raycast(StartTransform.position, Direction, out hit, RayLength, LayerMask);
ProcessCollision(hit.collider);
return hit.collider != null ? true : false;
}
public bool CastLine() {
Physics.Linecast(StartTransform.position, EndTransform.position, out hit, LayerMask);
ProcessCollision(hit.collider);
return hit.collider != null ? true : false;
}
private void ProcessCollision(Collider current) {
// No collision this frame.
if (current == null) {
// But there was an object hit last frame.
if (previous != null) {
DoEvent(OnRayExit, previous);
}
}
// The object is the same as last frame.
else if (previous == current) {
DoEvent(OnRayStay, current);
}
// The object is different than last frame.
else if (previous != null) {
DoEvent(OnRayExit, previous);
DoEvent(OnRayEnter, current);
}
// There was no object hit last frame.
else {
DoEvent(OnRayEnter, current);
}
// Remember this object for comparing with next frame.
previous = current;
}
private void DoEvent(Action<Collider> action, Collider collider) {
if (action != null) {
action(collider);
}
}
public static int GetLayerMask(string layerName, int existingMask=0) {
int layer = LayerMask.NameToLayer(layerName);
return existingMask | (1 << layer);
}
}
If your question is how to access the last object hit by your raycast then I suggest creating a global variable where you can store it.
Instead of setting the local variable in your method you can then set the global variable when you raycast. This way you can always access the object until you hit a new one(because this one is now stored in your global variable)
EDIT: In the event that you want to be able to keep track of all the targets that you have ever raycast I suggest making an global array where you store each item, appending them as you hit a new target.
so i did it with the mouse and it works
you have to put your object on the right layer first (9th here)
you middle mouse click on a object to change its color (here black), and right click anywhere (on the object or not) to change back its original color
only one object has its color changed at any moment , (lets call this state "selected")
when you "select" the next object by middle mouse clicking on it wile one is already "selected", it'll change the first one to its original color , as it is now "unselected"
public class raycast : MonoBehaviour {
float lenthRay = 10.0f;
Vector3 originePos;
Vector3 dir;
RaycastHit hitinfo;
GameObject hitten;
bool isHitting;
Color beforC;
int selectionLayer = 9;
bool alreadyHitten =false;
void Update () {
originePos = Camera.main.transform.position;
dir = Camera.main.transform.forward * lenthRay;
Debug.DrawRay(originePos, dir, Color.blue);
if (Input.GetMouseButtonDown (2)) {
isHitting = Physics.Raycast (originePos, dir, out hitinfo, lenthRay, selectionLayer);
if(isHitting) {
if(hitinfo.transform.gameObject == null){
hitten= null;
}
if(hitten != null && hitinfo.transform.gameObject == hitten){
alreadyHitten = true;
}
if(hitten != null && hitinfo.transform.gameObject != hitten){
alreadyHitten = false;
hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
hitten = hitinfo.transform.gameObject;
}
hitten = hitinfo.transform.gameObject;
if(hitten != null && !alreadyHitten){
print (hitten.name);
MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
beforC = tmp.material.color;
tmp.material.color = Color.black;
}
}
}
if (Input.GetMouseButtonDown (1)) {
if(hitten != null){
alreadyHitten = false;
hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
hitten = null;
}
}
}
}

Categories