How to increment/decrement only by one when Ray hits - c#

So, the game is to find a difference. When ray hits a collider of hidden object, it is supposed to add a score only of one and reduce a private integer differences value by one. But when the ray hits an object collider it is constantly incrementing/decrementing while the ray is hitting the collider. How can i change that? It is in the following method:
void Update()
{
camRay = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(camRay.origin, camRay.direction, Color.red, 1);
if (Input.touchCount > 0)
{
if (Physics.Raycast(camRay, out result))
{
/*{
obj = result.collider.gameObject;
score = score + 1;
differences= differences - 1;
score1.text = " " + score;
predmet.Play();
}*/
if (result.collider.CompareTag("Slike") && !hitOnce)
{
score +=1;
differences-= 1;
score1.text = " " + score;
predmet.Play();
}
else
{
hitOnce = false;
}
if (differences == 0)
{
WinPanel.SetActive(true);
}
}
}
}

When you first hit the object, you could remove or disable it's collider component. This should prevent it from being hit by more Raycasts.
On first hit, something like:
result.collider.enabled = false;

Related

want to pick up tools or guns but want to pick only one at a time in unity c#

want to pick up only one gun at a time but the player is picking both guns when I press the button so someone please solve this issue I want to pick up only one gun when the player presses a particular key but when the player drops the gun down then the only player can pick another gun
public void pickup() //this is a function when player presses to pick up guns
{
if (Input.GetKeyDown(KeyCode.E) && Vector3.Distance(s.transform.position, transform.position) <= 5 && dontpickup)
{
//var a = gameObject.FindWithTag("player");
transform.SetParent(s);
Vector3 temp = transform.position;
temp.x = player.transform.position.x;
temp.y = player.transform.position.y;
transform.position = temp + new Vector3(4, 0, 0);
Rigidbody2D rb = GetComponent<Rigidbody2D>();
rb.isKinematic = true;
Collider2D c = GetComponent<Collider2D>();
c.isTrigger = true;
needdropdown = true;
}
}
public void dropdown() //this is a function when player presses to drop down the guns
{
if (Input.GetKeyDown(KeyCode.Q) && needdropdown)
{
//var a = gameObject.FindWithTag("player");
transform.SetParent(null);
Rigidbody2D rb = GetComponent<Rigidbody2D>();
rb.isKinematic = false;
Collider2D c = GetComponent<Collider2D>();
c.isTrigger = false;
}
}
void OnTriggerStay2D(Collider2D trig)
{
if(f.gameObject != null && gameObject.tag == "player")
{
dontpickup = true;
}
}
You can add variable, something like _gunPickedUpTimeStamp. When player picks up gun assign value DateTime.Now.
And add condition that is evaluated every time when player picks up some gun. Something like
if ((_gunPickedUpTimeStamp - DateTime.Now) > TimeSpan.FromSeconds(5)) {
do something...
_gunPickedUpTimeStamp = DateTime.Now
}
I guess you should modify the value of dontpickup in these two methods:
public void pickup() //this is a function when player presses to pick up guns
{
if (Input.GetKeyDown(KeyCode.E) && Vector3.Distance(s.transform.position, transform.position) <= 5 && dontpickup)
{
//before code
needdropdown = true;
dontpickup = false;
}
}
public void dropdown() //this is a function when player presses to drop down the guns
{
if (Input.GetKeyDown(KeyCode.Q) && needdropdown)
{
//before code
needdropdown = false;
dontpickup = true;
}
}

How to rotate the cube I click instead of all cubes rotating at the same time?

So i am instantiating cubes and i want to rotate a single cube which i click, however when i click on the cube, all instantiated cubes rotate at the same time, I keep trying with boolean but no success. Any help would be appreciated. UNITY 2D
{
public int rotationDirection = -1; //-1 for clockwise
public int rotationStep = 5; //Should be less than 90
public bool noRotation;
// public GameObject cubeBox;
private Vector3 currentRotation, targetRotation;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null && hit.collider.tag == "colourBox" && noRotation == false)
{
Debug.Log("object clicked: " + hit.collider.tag);
RotateCube();
}
}
}
void RotateCube()
{
currentRotation = gameObject.transform.eulerAngles;
targetRotation.z = (currentRotation.z + (90 * rotationDirection));
StartCoroutine(objectRotationAnimation());
}
IEnumerator objectRotationAnimation()
{
currentRotation.z += (rotationStep * rotationDirection);
gameObject.transform.eulerAngles = currentRotation;
yield return new WaitForSeconds(0);
if (((int)currentRotation.z > (int)targetRotation.z && rotationDirection < 0))
{
StartCoroutine(objectRotationAnimation());
}
}
}
If I understand you correctly this script is present on all cubes.
The problem is that when you click with your mouse every single cube will create its own instance of a raycast. After that every cube will check if the hit.collider is not null, if its tag is colourBox and if noRotation equals false.
You in fact don't check anywhere what cube you just clicked on. You can simply add this check by comparing the instance ID of the gameobject the raycast collided with and the instance ID of the gameobject where the script runs.
if (hit.collider != null && hit.collider.tag == "colourBox" && noRotation == false)
{
// Check if the cube that was clicked was this cube
if (hit.collider.gameObject.GetInstanceID() == gameObject.GetInstanceID())
{
Debug.Log("object clicked: " + hit.collider.tag);
RotateCube();
}
}
This way every cube will compare its instance ID with the instance ID of the clicked object. Only the clicked object will return true in this if statement and thus runs the RotateCube method.
Used this instead of RayCastHit2D ....Deleted everything inside Update and created a new void.
private void OnMouseDown()
{
RotateCube();
}

Touch Dragging and Dropping onto Target

I have the following script attached to my game object:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TouchDragDrop : MonoBehaviour
{
// Components
private Rigidbody2D myRigidbody2D;
private CanvasGroup canvasGroup;
private Collider2D myCollider2D;
// Drop Zones
[SerializeField] public List<GameObject> dropZones = new List<GameObject>();
// Indicators
private bool isMoving = false;
private bool debuglog = true;
public bool isDropped = false;
// Numbers
private Vector2 touchPosition;
private float deltaX, deltaY;
private Vector2 oldVelocity;
// Start is called before the first frame update
void Start()
{
myRigidbody2D = GetComponent<Rigidbody2D>();
myCollider2D = GetComponent<Collider2D>();
canvasGroup = GetComponent<CanvasGroup>();
}
// Update is called once per frame
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
// touchPosition = touch.position;
touchPosition = Camera.main.ScreenToWorldPoint(touch.position);
switch (touch.phase)
{
case TouchPhase.Began: beginTouchMove(); break;
case TouchPhase.Moved: moveTouch(); break;
case TouchPhase.Stationary: stationaryTouch(); break;
case TouchPhase.Ended: endTouchMove(); break;
}
}
}
private void beginTouchMove()
{
if (myCollider2D == Physics2D.OverlapPoint(touchPosition))
{
if(debuglog) Debug.Log("Begin Touch # x: " + touchPosition.x.ToString() + " y: " + touchPosition.y.ToString());
//if(debuglog) Debug.Log("RigidBody: " + myRigidbody2D.position.ToString());
deltaX = touchPosition.x - transform.position.x;
deltaY = touchPosition.y - transform.position.y;
isMoving = true;
myRigidbody2D.bodyType = RigidbodyType2D.Kinematic;
oldVelocity = myRigidbody2D.velocity;
myRigidbody2D.velocity = new Vector2(0f, 0f);
}
}
private void moveTouch()
{
if (isMoving)
{
Vector2 newPosition = new Vector2(touchPosition.x - deltaX, touchPosition.y - deltaY);
//gameObject.transform.position = newPosition;
myRigidbody2D.MovePosition(newPosition);
//if(debuglog) Debug.Log("Touch Position: x: " + touchPosition.x.ToString() + " y: " + touchPosition.y.ToString());
//if(debuglog) Debug.Log("RigidBody: " + myRigidbody2D.position.ToString());
}
}
private void endTouchMove()
{
if (debuglog) Debug.Log("On End Touch");
canvasGroup.blocksRaycasts = true;
// Check drop zones for overlap
foreach(GameObject dropZone in dropZones)
{
if (debuglog) Debug.Log("Checking " + dropZone.name);
if(dropZone.GetComponent<Collider2D>() == Physics2D.OverlapPoint(myCollider2D.transform.position))
{
isDropped = true;
if (debuglog) Debug.Log("TOUCH: Found Collider");
dropZone.GetComponent<DropZone>().EndGameObjectLife(gameObject);
}
}
if(!isDropped) {
myRigidbody2D.bodyType = RigidbodyType2D.Dynamic;
myRigidbody2D.velocity = oldVelocity;
}
}
private void stationaryTouch()
{
//Debug.Log("Stationary Touch");
}
}
My problem is I dont know how to check that the Collider in gameObject has hit one of the colliders in the dropZones List. I dont get any errors, but I don't get the message "Touch: Found Collider" either. So the game doesnt acknoledge that the object has found its target.
What is the best way to go about this?
I tried doing
if(dropZone.GetComponent<Collider2D>() == Physics2D.OverlapPoint(touchPosition))
Which works to a point, but it does mean that the user can just touch the target without dragging the object and it'll recognize a drop. With the MouseEvents I have Ondrop, is there an equivilent for Touch?
To check if a collider hit other collider you can use Collider2D messages like OnCollisionEnter2D, OnCollisionExit2D, OnCollisionStay2D. There are similar messages if your collider is set to trigger like OnTriggerEnter2D.
Once you detect the collision check if the collider is in the dropZones list.

Unity does not update NavMeshAgent destination in coroutine

I'm working on an AI script in Unity 5 mostly constructed of coroutines to move around an AI object in my game. It actually works pretty well and is frame independent that way. I'm trying to avoid cluttering the Update function of the class.
However, I'm trying to create a function called wander, where the AI hangs around and randomly chooses a couple of Vector3s in the area of the waypoint and travel to each of them. After that the AI should go to the next waypoint and do the same thing into infinity basically. Collision detection and all that sort is for later parts. I want to get the navigating part fixed first.
void Start(){
agent = GetComponent<NavMeshAgent> ();
collisionRange = GetComponent<CapsuleCollider> ();
collisionRange.radius = detectionRadius;
if (waypoints.Length > 0) {
StartCoroutine (patrol ());
} else {
StartCoroutine (idle (idleTime));
}
}
IEnumerator patrol(){
agent.SetDestination(waypoints[waypointIndex].position);
Debug.Log ("Patrol started, moving to " + agent.destination);
while (agent.pathPending) {
Debug.Log ("not having path");
yield return null;
}
Debug.Log ("I have a path, distance is " + agent.remainingDistance);
while (float.Epsilon < agent.remainingDistance) {
//Debug.Log ("Moving...");
yield return null;
}
StartCoroutine (nextWaypoint ());
}
IEnumerator idle(int time){
Debug.Log ("Idleing for "+ time + " seconds");
agent.Stop ();
yield return new WaitForSeconds(time);
if(waypoints.Length > 2){
agent.Resume ();
StartCoroutine(patrol());
}
}
IEnumerator wander(){
agent.Stop ();
Debug.Log ("Going to look around here for a while.");
Vector3[] points = new Vector3[wanderPoints];
for (int i = 0; i < wanderPoints; i++) {
agent.Stop ();
Vector3 point = Random.insideUnitSphere * wanderRadius;
point.y = transform.position.y;
points [i] = point;
}
agent.ResetPath ();
agent.SetDestination(points[0]);
agent.Resume ();
Debug.Log ("point: " + points [0]);
Debug.Log ("Destination: " + agent.destination);
while (float.Epsilon < agent.remainingDistance) {
Debug.Log ("Moving...");
yield return null;
}
//StartCoroutine(patrol());
yield return null;
}
IEnumerator nextWaypoint(){
Debug.Log ("Arrived at my waypoint at " + transform.position);
if (waypointIndex < waypoints.Length -1) {
waypointIndex +=1;
} else {
waypointIndex = 0;
}
StartCoroutine(wander ());
yield return null;
}
If I swap the wander function with the idle function in nextWaypoint, everything works as expected, but this bit will never work:
agent.ResetPath ();
agent.SetDestination(points[0]);
agent.Resume ();
Debug.Log ("point: " + points [0]);
Debug.Log ("Destination: " + agent.destination);
This is a bit of test code (manually setting only 1 position to go point[0], but it never will travel to that destination. SetDestination will never get updated to the points I want to set them. I've tried calculating paths (NavMeshPath) up front and everything but the destination path will not change or reset weirdly enough. I've also had the while (float.Epsilon < agent.remainingDistance) loop in the wander function as well, but with no luck since it will remain in that loop forever since there's no path to go to.
I might be missing something here, or my logic is wrong in this case. Hopefully someone could give me a little push or maybe some extra debugging options, because I have no idea why the destination doesn't get updated in my wander function.
Consider using triggers on waypoints instead, as this will be even less taxing for the system. Below is an example that will allow you to have a variable number of waypoints easily, by getting all WayPoint type children from a parent transform. The only Coroutine that is used is for idle as you said you didn't want your Update function cluttered.
I've also exposed a couple of additional variables, including a min and max idle time, and a patrol chance, which should be set to a value less than or equal to 1, representing a percentage chance of patrolling vs idle. You can also choose a min and max number of patrol points for the agent to navigate to.
In RandomActivity you can also see that there is error handling for infinitely idle (no WayPoint children under parent). The agent will also not include the current Waypoint in it's list of points to navigate to, and will not navigate to a point already navigated to during it's current patrol (every time an element is added to m_targets, it's removed from m_availableTargets).
AgentController.cs
[RequireComponent(typeof(NavMeshAgent))]
public class AgentController : MonoBehaviour
{
[SerializeField]
private Transform m_waypointParent;
[SerializeField]
private float m_minIdle;
[SerializeField]
private float m_maxIdle;
[SerializeField]
private float m_patrolChance;
[SerializeField]
private int m_minPatrolPoints;
[SerializeField]
private int m_maxPatrolPoints;
private Waypoint[] m_waypoints;
private List<Waypoint> m_availableTargets;
private List<Waypoint> m_targets;
private Waypoint m_tempWaypoint;
private int m_currentTargetIndex;
private NavMeshAgent m_navMeshAgent;
public void Start()
{
m_waypoints = m_waypointParent.GetComponentsInChildren<Waypoint>();
m_targets = new List<Waypoint>();
m_navMeshAgent = GetComponent<NavMeshAgent>();
RandomActivity();
}
private void RandomActivity()
{
if (m_waypoints.Length == 0)
{
Debug.Log("Enemy will idle forever (no waypoints found)");
StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
return;
}
if(Random.Range(0f, 1f) <= m_patrolChance)
{
//Available waypoints
m_availableTargets = new List<Waypoint>(m_waypoints);
//Remove currentpoint
if(m_targets.Count > 0)
m_availableTargets.Remove(m_targets[m_targets.Count - 1]);
//Reset list
m_targets.Clear();
m_currentTargetIndex = -1;
//Add patrol points
for (int i = 0; i < Random.Range(m_minPatrolPoints, m_maxPatrolPoints + 1); i++)
{
m_tempWaypoint = m_availableTargets[Random.Range(0, m_availableTargets.Count)];
m_targets.Add(m_tempWaypoint);
m_availableTargets.Remove(m_tempWaypoint);
}
NextWaypoint(null);
}
else
StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
}
public void NextWaypoint(Waypoint p_waypoint)
{
//Collided with current waypoint target?
if ((m_currentTargetIndex == -1) || (p_waypoint == m_targets[m_currentTargetIndex]))
{
m_currentTargetIndex++;
if (m_currentTargetIndex == m_targets.Count)
RandomActivity();
else
{
Debug.Log("Target: " + (m_currentTargetIndex + 1) + "/" + m_targets.Count + " (" + m_targets[m_currentTargetIndex].transform.position + ")");
m_navMeshAgent.SetDestination(m_targets[m_currentTargetIndex].transform.position);
}
}
}
private IEnumerator Idle(float p_time)
{
Debug.Log("Idling for " + p_time + "s");
yield return new WaitForSeconds(p_time);
RandomActivity();
}
}
Note that for this to work, I have created a tag called Enemy so as to easily differentiate between enemies and any other tiggers in your game.
WayPoint.cs
[RequireComponent(typeof(BoxCollider))]
public class Waypoint : MonoBehaviour
{
public void OnTriggerEnter(Collider p_collider)
{
if (p_collider.tag == "Enemy")
p_collider.GetComponent<AgentController>().NextWaypoint(this);
}
}
I know this is an old post but hope this helps someone.

Number is randomizing repeatedly per click

I am currently working on a mini-game that revolves around numbers randomizing and score being allocated depending on whether the number displayed is a multiple of 'x' in this case 3.
However, I have an issue, because for some reason whenever the player clicks to randomize, it randomizes several times completely throwing my score system.
So far, I have attempted to solve this issue through the use of:
yield return new WaitForEndOfFrame();
However, this proved to be for naught as it did not help.
If any of you could shed some light on why this is happening it would be deeply appreciated.
Code in use
void Awake ()
{
randomNumber = Random.Range (0, 36);
}
void Start()
{
mycam = Camera.main;
}
void FixedUpdate()
{
StartCoroutine(Selection ());
thisAnswer.text = randomNumber.ToString ();
}
// Update is called once per frame
void Update ()
{
if (CorrectCount == 5)
{
PlayerPrefs.SetInt ("MiniScore", miniScore);
Destroy (GameObject.Find ("Killswitch"));
}
}
IEnumerator Selection ()
{
Ray ray = mycam.ScreenPointToRay (Input.mousePosition);
if (Input.GetKey (KeyCode.Mouse0))
{
if (Physics.Raycast (ray, out hit))
{
if(hit.transform.tag == "answer")
{
if (System.Convert.ToInt32(thisAnswer.text) % 3 == 0)
{
miniScore = miniScore + 100;
CorrectCount = CorrectCount + 1;
yield return new WaitForEndOfFrame();
randomNumber = Random.Range (0, 36);
Debug.Log (miniScore.ToString());
}
else if (System.Convert.ToInt32(thisAnswer.text) % 3 != 0)
{
if (miniScore > 50)
{
miniScore = miniScore - 50;
}
else if (miniScore < 50)
{
miniScore = 0;
}
Debug.Log (miniScore.ToString ());
yield return new WaitForEndOfFrame();
randomNumber = Random.Range (0, 36);
}
}
}
}
}
I suspect whats happening here is that this line of code
if (Input.GetKey (KeyCode.Mouse0))
is getting executing several times a once the user presses the mouse button - GetKey will return true as long as the button is down (which is will be for several frames - So you could try
if (Input.GetKeyUp (KeyCode.Mouse0))
This should only fire once when the mouse button is released.
Check out
http://docs.unity3d.com/ScriptReference/Input.GetKeyUp.html

Categories