Anyone know how i can stop this from flicking basically I'm doing a raycast and if it hits the crafting table it it shows up a text saying press e to craft but it is flickering cause its in the void update function but if anyone knows a work around that would be nice thanks!
if(hit.collider.tag == "CraftingTable")
{
if(textIsOn)
{
noshowcraftingtext();
}
else
{
showcraftingtext();
}
}
void showcraftingtext()
{
textIsOn = true;
pressEToShowCraftingTableUI.SetActive(true);
}
void noshowcraftingtext()
{
textIsOn = false;
pressEToShowCraftingTableUI.SetActive(false);
}
The main problem of your code is that you are trying to change the state after state was changed.
That's why updated isn't the case in a big project, because all such things will lead to unpredictable or logically conflicted behavior, to avoid this you should use Data driven approach instead.
But to fix exactly your issue, you need to have a function, which isn't based on the state of the object, but returns what should happen right now.
There isn't much about what's going on inside your game, but I can guess that you are firing a raycast with mouse and then checking is it craftable or not.
public void Raycaster : MonoBehavior {
public void YourClickMethod()
{
if(hit.collider.tag == "CraftingTable")
{
hit.collider.GetComponent<UserInputReceiver>().SetClicked(this);
}
}
}
And on the object which is receiving your raycast you should add this:
public class UserInputReceiver : MonoBehavior {
private bool _isEnabled = false;
//set this inside inspector
public GameObject ObjectToEnable;
private Raycaster _currentSender = null
public void SetClicked(Raycaster sender){
_isEnabled = !_isEnabled;
_currentSender = sender;
}
public void Update(){
if(_currentSender != null && _isEnabled) {
_isEnabled = Vector3.Distance(transform.position, _currentSender.transform.position) < 1f; //set the threshold based on your unit system
}
//or another object, you can put it inside public field and enable him
//gameObject.SetActive(_isEnabled);
objectToEnable.SetActive(_isEnabled); //here you can pass a reference through inspector for your canvas
}
}
This is how you can outstand this issue with a short way.
This code will set the flag based on the received input and the distance. If you need to keep enabled text forever, than remove distance check than it will enable only with a second click, without distance dependency.
Related
My goal is to write one script that I can use on different game objects and it should have specific variables tied to it on that game object only without affecting other scripts in the process.
For example, if I take this script and put it on two game objects each game object should have their own unique variable value in that same script.
If my question is not clear enough, I'm more than happy to elaborate further.
I have a good understanding of the Unity Editor, however, I'm pretty new to C# so I don't think it's unreasonable that I made a rookie mistake somewhere in my code.
The way I've got things setup is that I have two separate scripts:
Fighting controls the values like the Team, Health, Attack Damage, Cool Down, Cooling down and Snap
TrigDetect controls the detection of a trigger being activated as a result of an enemy entering the trigger radius.
The problem I'm currently having lies in the TrigDetect script I guess.
It should also be noted that an empty attached to each game object in question contains both of these scripts and is tagged as "Troop".
TrigDetect
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrigDetect : MonoBehaviour
{
//public GameObject[] Enemy;
bool once = false;
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Troop"))
{
//Debug.Log("Entered");
}
}
void OnTriggerExit(Collider other)
{
if (other.CompareTag("Troop"))
{
//Debug.Log("Exitted");
}
}
void OnTriggerStay(Collider other)
{
if (other.CompareTag("Troop"))
{
Fighting self = GetComponent<Fighting>();
GameObject g = GameObject.Find("Detection");
Fighting fScript = g.GetComponent<Fighting>();
//Enemy = GameObject.FindGameObjectsWithTag("Troop");
//Debug.Log("Staying");
//Debug.Log(Enemy);
//Debug.Log(self.Health);
//Debug.Log(fScript.Health);
if (once == false)
{
Debug.Log("I am the team:" + self.Team);
Debug.Log("I have detected the team:" + fScript.Team);
once = true;
}
if (self.Team != fScript.Team)
{
if (self.CoolingDown == false)
{
self.CoolingDown = true;
fScript.Health -= self.AttackDamage;
}
else
{
self.CoolDown -= Time.deltaTime;
if (self.CoolDown <= 0)
{
self.CoolingDown = false;
self.CoolDown = self.original;
}
}
}
}
}
}
Fighting
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fighting : MonoBehaviour
{
public int Team = 1;
public int Health = 100;
public int AttackDamage = 10;
public float CoolDown = 2;
public float original = 2;
public bool CoolingDown = false;
public bool Snap = false;
// Update is called once per frame
void Update () {
if (Snap == true || Health <= 0)
{
//Destroy(gameObject, .5f);
Destroy(transform.parent.gameObject);
}
if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject);
}
}
The expected result when I move one game object into the trigger radius of the other is that they should both start subtracting Health from each other based on the AttackDamage value. They should do this every time the CoolingDown value is false. When an attack is executed, it's flipped to true and a timer starts, when the timer is done it's flipped back to false.
However, upon moving the two objects into each other's radius', the first object has its health taken away as expected and then proceeds to do nothing until it's health reaches 0 then it dies because of the object attacking it. The object attacking is successfully attacking the other object but, is still not being affected by the object it's attacking.
Basically, Find(name) only returns the first instance of anything by that name, thus your g = Find(name) is almost guaranteed to never be the object related to your trigger/collision condition. The OnTriggerStay(Collider other) already gives you the 'other' collider that's in your trigger zone, so use it. :)
Replace this:
GameObject g = GameObject.Find("Detection");
Fighting fScript = g.GetComponent<Fighting>();
with this:
Fighting fScript = other.GetComponent<Fighting>();
To your question header:
Every instaced (non-static) value is allways unique to the according component and thereby to the according GameObject it is attached to. You might want to refrase the question because this is actually not your issue.
The problem is that when you do
GameObject.Find("Detection");
it actually finds the same object both times: Namely the first one in the hierarchy. So in one of of the two components you find your own empty object and skip the rest in
if(self.Team != FScript.Team)
.. you could try to use
other.Find("Detection");
instead to only search in the according context .. However, you should not use Find at all!
It is very performance intense
You should allways reuse references and not search them over and over again
You don't need it in your case
Since you say both scripts are attached to the same object you can simply use
GetComponent<Fighting>();
and you can do so already in Awake and reuse the reference instead:
private Fighting myFighting;
private void Awake()
{
myFighting = GetComponent<Fighting>();
}
Than for the collision you don't have to use Find either because you already have the reference of the object you collide with: other.gameObject. I don't know your entire setup but you can search for the component either downwards in the hierachy
// the flag true is sued to also find inactive gameObjects and components
// leave it without parameters if you don't want this
var otherFighting = other.GetComponentInChildren<Fighting>(true);
or searcg upwards in the hierachy
var otherFighting = other.GetComponentInParent<Fighting>(true);
or if you already know you collide exactly with the correct GameObject anyway simply use
var otherFighting = other.GetComponent<Fighting>();
I will use the latter in my example.
Than cheking the health all the time in Update is a huge perfomance issue. You should rather have a method e.g. TakeDamage and do your check only if your health is actually changed:
Fighting
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fighting : MonoBehaviour
{
public int Team = 1;
public int Health = 100;
public int AttackDamage = 10;
public float CoolDown = 2;
public float original = 2;
// you don't need that flag see below
//public bool CoolingDown = false;
public bool Snap = false;
private void Update()
{
// you might also put this in a callback instead of update at some point later
if(Snap == true)
{
Destroy(transform.parent.gameObject);
}
// Note: this also makes not muh sense because if you destroyed
// the parent than you cannot instantiate it again!
// use a prefab instead
if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject);
}
public void TakeDamge(int DamageAmount)
{
Health -= DamageAmount;
if (Health > 0) return;
Destroy(transform.parent.gameObject);
}
}
Another performance issue in general: Even if Start, Update etc are empty, if they are present in your script Unity will call them. So if you don't use them then completely remove them to avoid that useless overhead.
So I would have
TrigDetect
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrigDetect : MonoBehaviour
{
bool once = false;
private Fighting myFighting;
private void Awake()
{
myFighting = GetComponent<Fighting>();
}
void OnTriggerStay(Collider other)
{
// if wrong tag do nothing
if (!other.CompareTag("Troop")) return;
Fighting fScript = other.GetComponent<Fighting>();
// here you should add a check
if(!fScript)
{
// By using the other.gameObject as context you can find with which object
// you collided exactly by clicking on the Log
Debug.LogError("Something went wrong: Could not get the Fighting component of other", other.gameObject);
}
if (!once)
{
Debug.Log("I am the team:" + self.Team);
Debug.Log("I have detected the team:" + fScript.Team);
once = true;
}
// if same team do nothing
if (self.Team == fScript.Team) return;
// you don't need the CoolingDown bool at all:
self.CoolDown -= Time.deltaTime;
// if still cooling down do nothing
if(self.CoolDown > 0) return;
fScript.TakeDamage(self.AttackDamage);
self.CoolDown = self.original;
}
}
I've used Unity for quite some time, but never tried the editor. Today I decided to try to make an EditorWindow to help me with shortcuts with my development.
What I want is to click on a button on my Window to turn on and off a specific light in my scene.
Added the code and an image of the Window below.
I am clueless on how to proceed. Any help would be appreciated a lot!
public class DevTools : EditorWindow
{
public Object workLight;
[MenuItem("Window/DevTools")]
public static void ShowWindow()
{
GetWindow<DevTools>("Development Tools");
}
private void OnGUI()
{
GUILayout.Label("This is the development tools.", EditorStyles.boldLabel);
workLight = EditorGUILayout.ObjectField(workLight, typeof(object), true);
if (GUILayout.Button("Working Light"))
{
if(workLight == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
}
}
}
}
This is my current Window
Well, you need a reference to the object to proceed with turning it on and off. You can either add a field to the editor and manually choose the object from the scene or dynamically find it through GameObject.Find.
I don't really know Unity (never used it) but it looks pretty straightforward.
The EditorFieldForObject method seems to take a second type param that denotes which types you can select using the field in the GUI - I'm not sure if you can make this a GameObject or not, but there are basically two approaches that should work:
Leave your code as is and cast the object (in OnGUI method):
var gameObject = workLight as GameObject;
if(gameObject == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
var light = gameObject.GetComponent<Light>();
if(light == null)
{
ShowNotification(new GUIContent("Selected object is not a light"));
return;
}
// Not sure if this is the correct way to disable a light but you can figure it out
light.enabled = false;
// or light.enabled = !light.enabled to toggle state
}
The other way is to make the editor GUI box look for GameObject instances in the first place:
public GameObject workLight;
....
workLight = EditorGUILayout.ObjectField(workLight, typeof(GameObject), true);
But I'm not sure if this is possible or not (as I've said, I don't know Unity)
Edit: apparently you need to use GameObject.SetActive(boolean) to toggle the
state of an object
Since you want to toggle, you can use the GameObject.activeSelf field which gives you the active true/false flag on the current object
So this should work:
light.SetActive(!light.activeSelf);
Thanks a lot Charleh!
With a little bit of rewriting, this seem to work. Not sure if it is optimized perfectly, but it works without a hitch (atm) :p.
public class DevTools : EditorWindow
{
Object workLight = default;
[MenuItem("Window/DevTools")]
public static void ShowWindow()
{
GetWindow<DevTools>("Development Tools");
}
private void OnGUI()
{
GUILayout.Label("This is the development tools.", EditorStyles.boldLabel);
if (GUILayout.Button("Working Light"))
{
if (workLight == null)
{
ShowNotification(new GUIContent("No light selected"));
}
else
{
var light = GameObject.Find("Work Light");
if (light == null)
{
ShowNotification(new GUIContent("Selected object is not a light"));
return;
}
else
{
light.gameObject.GetComponent<Light>().enabled = !light.gameObject.GetComponent<Light>().enabled;
}
}
}
}
}
How would you find the time since a certain variable was changed? Take for example a boolean variable, how would you find the time since it was last changed? I want to use the boolean variable as a trigger (activating the trigger when it's true), but only after an exact, constant time (such as 0.5s) has passed since it was changed to true (it can only be changed from false to true).
Here is the code I have:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class hitRegistration : MonoBehaviour
{
AudioSource hitSound;
private bool hitState = false;
// Use this for initialization
void Start()
{
hitSound = gameObject.GetComponent<AudioSource>();
}
void OnMouseOver()
{
Debug.Log("Mouse is over game object.");
if (Input.GetKey(KeyCode.X) && hitState == false)
{
hitSound.Play();
hitState = true;
}
}
private void OnMouseExit()
{
Debug.Log("Mouse is no longer over game object.");
if (hitState == true)
{
// sound clip gets cut if the cursor leaves before its finished.
Destroy(gameObject);
}
}
// Update is called once per frame
void Update()
{
}
}
"OnMouseOver()" Is simply a function that is called when the mouse is placed over the game object in question. I want to delay destroying the game object until a certain time has passed.
First off, as noted in a comment, you are probably trying to solve this problem the wrong way and you are probably asking an "XY" question -- a question where you are asking a question about a proposed bad solution instead of asking a question about the actual problem you face.
To answer the question you actually asked, for better or worse: there is no way to associate behaviours with reading or writing a variable in C#, but you can associate behaviours with a property:
private bool hitState; // The "backing store".
private bool HitState
{
get
{
return hitState;
}
set
{
hitState = value;
}
}
You would then use HitState rather than hitState throughout the rest of your class.
Now you can add whatever logic you want that happens when the property is read or written:
private DateTime hitStateTime = default(DateTime);
private bool hitState; // The "backing store".
private bool HitState
{
get
{
return hitState;
}
set
{
hitState = value;
hitStateSet = DateTime.Now;
}
}
Now you know when it was set. And so on.
Unless you really need to keep track of how much time has passed on each single frame, one way to do what you are asking for is to use Unity Coroutines.
A coroutine is a method that runs in parallel with the main thread. To solve your question, you can first create a coroutine in the same script, that waits and then does the thing you want to have delayed. A couroutine in Unity is a method that takes up to one parameter and has an IEnumerator return type. You use yield return WaitForSeconds(t); inside the coroutine to have it delay for t seconds.
Then, once it's time to die, check if the mouse is currently hovering over the object with isHovered (set in your OnMouseOver/OnMouseExit methods). If it is, keep a note that it's time to die. If it isn't, then it can die immediately.
IEnumerator WaitToDie(float delaySeconds)
{
yield return new WaitForSeconds(delaySeconds);
// If the mouse is on the object, let OnMouseExit know we're ready to die
if (isHovered)
{
readyToDie = true;
}
// Otherwise, just die
else
{
Destroy(gameObject)
}
}
And then inside your OnMouseOver code, run the coroutine after starting the sound
void OnMouseOver()
{
isHovered = true;
Debug.Log("Mouse is over game object.");
if (Input.GetKey(KeyCode.X) && !hitState)
{
hitState = true;
hitSound.Play();
// we want to delay for half a second before processing the hit.
float delaySeconds = 0.5;
IEnumerator coroutine = WaitToDie(delaySeconds);
StartCoroutine(coroutine);
}
}
And in your OnMouseExit, let everything know that you're done hovering and check if it's past time to die or not.
private void OnMouseExit()
{
isHovered = false;
Debug.Log("Mouse is no longer over game object.");
if (readyToDie) {
Destroy(gameObject);
}
}
Altogether this code will have the object die when both the mouse is off the object AND the time has elapsed.
As a sidenote, I think you might want to revisit how you are checking for a hit, unless your really want to trigger from the player holding X and then moving the mouse over the object. If you intend to trigger any time X is pressed down while the mouse is on top, you might want to put the check in Update and check Input.GetKey(KeyCode.X) && !hitState && isHovered
keep a seperate variable(DateTime) and call it lastUpdate. then be sure to set it to DateTime.Now, each time the bool you're tracking is updated. then when you need to see how long its been you can just subtract:
DateTime lengthOfTime = DateTime.Now-lastUpdate;
from lengthOfTime you can now access how many days, hours, minutes, and/or seconds have passed.
im on my phone so take it easy on my pseudo-code.
good luck
i have two objects(the same character, but, in different functions) which i want change the character when animation stop and when it runs triggered by a click. For example, I have the Kick_Player where there is the animation triggered by the click, and when the Kick_Player ends the animation, i want it automatically changes to Player_Stopped. The poses are different each other, 'cause this i need to do these changes.
I tried something with this.MyAnimator.GetCurrentAnimatorStateInfo(0).IsName("My_Animation") but, i got unsuccessful tries. Is there a way to do that ?
public class TapController : MonoBehaviour {
Animator Anim;
public GameObject CharacterToController; //Kick_Player
public GameObject CharacterToBeStopped; //Player_Stopped
void Start(){
Anim = CharacterToController.GetComponent<Animator>();
CharacterToBeStopped.SetActive(false);
}
void Update(){
if(input.GetMouseButtonDown(0)){
if(!CharacterToController.activeSelf){
CharacterToController.SetActive(true);
}
Anim.Play("Kick_Ball");
if(!this.Anim.GetCurrentAnimatorStateInfo(0).IsName("Kick_Ball") {
CharacterToController.SetActive(false);
CharacterToBeStopped.SetActive(true);
}
}
}
I made this code to test, but it doesn't work
Using the IsName function requires that you prefix the base layer name of the animation state before the actual animation state.
The default base name is usually "Base Layer"
if(!this.Anim.GetCurrentAnimatorStateInfo(0).IsName("Base Layer.Kick_Ball")
Note that you have to do that outside your if(input.GetMouseButtonDown(0)){ otherwise that will never get chance to be checked.
I've seen reports of IsName not working for some people so if you do that but still have issues, consider doing it another way.
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (!CharacterToController.activeSelf)
{
CharacterToController.SetActive(true);
}
Anim.Play("Kick_Ball");
StartCoroutine(PlayAndWaitForAnim(Anim, "Kick_Ball"));
}
}
const string animBaseLayer = "Base Layer";
int animHash = Animator.StringToHash(animBaseLayer + ".Kick_Ball");
public IEnumerator PlayAndWaitForAnim(Animator targetAnim, string stateName)
{
targetAnim.Play(stateName);
//Wait until we enter the current state
while (targetAnim.GetCurrentAnimatorStateInfo(0).fullPathHash != animHash)
{
yield return null;
}
float counter = 0;
float waitTime = targetAnim.GetCurrentAnimatorStateInfo(0).length;
//Now, Wait until the current state is done playing
while (counter < (waitTime))
{
counter += Time.deltaTime;
yield return null;
}
//Done playing. Do something below!
Debug.Log("Done Playing");
CharacterToController.SetActive(false);
CharacterToBeStopped.SetActive(true);
}
I am attempting to make a physics sandbox-type game for the Vive, but the velocity of an object is completely reset and just begins falling when you let go of an object you were previously holding, making throwing impossible.
The system I am using currently will disable gravity on an object, and disable colliders of on an object when you pick it up. It will also child the object to your controller, making it like holding the object. When you let go of the button to release the object, it will enable gravity, enable colliders, and then set its parent object to null. This works to pick up and release an object, but it does not work at all for throwing objects. I have played around with timing and order of the components of the code, and nothing works.
Is there any way to find the velocity of an object and the directional velocity, without the object using gravity? Velocity doesn't work if gravity is disabled.
Here is my code:
using UnityEngine;
using System.Collections;
public class WandController : MonoBehaviour
{
//Basic Controller tracking stuff
private Valve.VR.EVRButtonId gripButton = Valve.VR.EVRButtonId.k_EButton_Grip;
public bool gripButtonDown = false;
public bool gripButtonUp = false;
public bool gripButtonPressed = false;
private Valve.VR.EVRButtonId triggerButton = Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger;
public bool triggerButtonDown = false;
public bool triggerButtonUp = false;
public bool triggerButtonPressed = false;
private SteamVR_Controller.Device controller { get { return SteamVR_Controller.Input((int)trackedObj.index); } }
private SteamVR_TrackedObject trackedObj;
//Game Variables
public GameObject wouldSelect; //What is in the select zone, has tiny script for the zone that sets the newest triggerenter to this variable
public GameObject isHolding; //When you hold something, it goes from wouldselect to isholding
public bool holding = false;
public GameObject holdingZone; //The holding zone, also where objects go if they are picked up
// Use this for initialization
void Start()
{
trackedObj = GetComponent<SteamVR_TrackedObject>();
}
// Update is called once per frame
void Update()
{
//Basic Controller configuration & button management stuff
if (controller == null)
{
Debug.Log("Controller not initialized");
return;
}
gripButtonDown = controller.GetPressDown(gripButton);
gripButtonUp = controller.GetPressUp(gripButton);
gripButtonPressed = controller.GetPress(gripButton);
triggerButtonDown = controller.GetPressDown(triggerButton);
triggerButtonUp = controller.GetPressUp(triggerButton);
triggerButtonPressed = controller.GetPress(triggerButton);
if (gripButtonDown)
{
Debug.Log("Grip Button was just pressed");
}
if (gripButtonUp)
{
Debug.Log("Grip Button was just unpressed");
}
if (triggerButtonDown)
{
Debug.Log("Trigger Button was just pressed");
}
if (triggerButtonUp)
{
Debug.Log("Trigger Button was just unpressed");
}
//Calling void that allows you to grab
CanGrab();
}
void CanGrab ()
{
if(wouldSelect != null && wouldSelect.tag == "Object" && triggerButtonDown == true && holding == false)
{
wouldSelect.GetComponent<Collider>().enabled = false;
wouldSelect.GetComponent<Rigidbody>().useGravity = false;
isHolding = wouldSelect;
wouldSelect.transform.SetParent(this.transform);
wouldSelect.transform.position = holdingZone.transform.position;
holding = true;
}
if(holding == true && triggerButtonUp == true)
{
wouldSelect.GetComponent<Collider>().enabled = true;
isHolding.GetComponent<Rigidbody>().useGravity = true;
isHolding.transform.SetParent(null);
holding = false;
wouldSelect = null;
isHolding = null;
}
}
}
I had a similar issue and solved it by:
tracking the position of the object at every frame, and storing it as lastPosition
when the object is let go, using (transform.position - lastPosition) to give me a rough estimate of the velocity of that object
From there, you can add an impulse force if you have a rigidbody, etc, I found a thread with some details on the various ways Unity does it -- http://answers.unity3d.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html
I did a number of throwing mechanics in VR using the same parameters you had (Setting the object as a child of your controller, disabling gravity, disabling colliders).
The way I did it was to record the position of the object on the previous and current frame, and take the difference between them as the velocity. However, there are three main factors to consider:
There might be some jitter in the tracking of the controller, and sometimes the object does not fly in the direction of throw.
There might be a lag time between the user pressing/letting go of the throwing button and the button press/release getting recorded, and so the object is thrown only towards the falling arc of the throw. (This happens a lot in my playtests)
There is a peak force during a throw during which an object gains the most velocity for its flight, and sometimes people let go of the object a split second after. This is rather inconsequential, but you should definitely consider if you want very realistic throwing (Take a look at The Lab demo and you will know what I mean)
My implementation:
Record the last x frames (for me the sweet spot is between 10-15 frames) for the object's position.
Take the difference in the first and last frame in the window and use that to calculate the velocity.
transform.velocity = position[n] - position[0];
If I want a slightly more accurate implementation, I'll calculate the force of the throw by taking the differences in velocities for adjacent frames. If there is a time window between the peak force and the release of the object, do not take the velocities in the later half of the window.
For example, if I decide to record the last 10 frames of a throw, and there are 4 frames between the peak force and the release, I will take frame current-12 to current-2, instead of frame current-10 to current for the velocity.