I have an issue when playing animation in Unity. I made a transaction and set a bool on the animation. The issue here is that if I have an animation on a loop it won't stop. If I unloop it, it would play only once. What I actually want from this is to play only when the player presses the button. Right now my code looks like this. I tried to set the bool to false in different places, but it didn't have the right effect. I would appreciate any idea. Thanks
private void Inflations()
{
inflationTimer -= Time.deltaTime;
//if hands are in mount area inflate
if (OVRInput.GetDown(OVRInput.Button.Any))
{
if (inflationTimer <= 0 && rightHand.inflated)
{
if (inflated)
{
inflationTimer = inflationSpawn;
inflated = false;
inflationAnimator.SetBool("Pressed", true);
//we can add inflations only if they are under 2 and if the button is pressed and released
inflations++;
}
else
{
inflationAnimator.SetBool("Pressed", false);
}
tempo.SetInflations(inflations);
}
}
if (OVRInput.GetUp(OVRInput.Button.Any))
{
if (!inflated)
{
inflated = true;
}
}
if (inflations >= 2)
{
inflationAnimator.SetBool("Pressed", false);
state = stueState.TakeInflator;
inflationsDone = true;
}
}
#ecco is right, you need two states for it
Without seeing your Animator Controller, you could use something like this;
//...
Stack<Action> _animations = new Stack<Action>();
private void Inflations()
{
// ...
if (OVRInput.GetDown(OVRInput.Button.Any))
{
if(inflated){
// your code
_animations.Push(() => inflationAnimator.SetBool("Pressed", true));
// ...
}
}
}
private void Update(){
if(_animations.Count > 0){
var currentAnimation = _animations.Pop();
currentAnimation();
}
}
This eliminates the need for a lot of if checks and is nice to use.
Note: I used lambda expression there for simplicity. You can read more about it in here. And you can read more about Stack containers here
The update function is gonna check if there are any animations in Stack, if there are, it will automatically execute it once and remove it from the stack.
Related
I have created a simple custom editor tool, which allows me to keep mouse position in a straight line. I require this to draw texture on a terrain in a straight line. Unfortunately, when I enable "Paint texture" tool in the terrain editor in inspector, my custom tool gets disabled and vice-versa. How can I keep both my custom tool and terrain paint tool enabled at once?
Custom tool selected but paint texture is deactivated-
Custom tool got deselected on paint texture selection-
Following is the OnToolGUI method
public override void OnToolGUI(EditorWindow window)
{
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
Event e = Event.current;
if (!(window is SceneView))
return;
if (!ToolManager.IsActiveTool(this))
return;
if (e.shift)
{
if (e.type == EventType.MouseDown)
{
if (e.button == 0)
{
downY = e.mousePosition.y;
}
}
if (e.type == EventType.MouseDrag)
{
if (e.button == 0)
{
e.mousePosition = new Vector2(e.mousePosition.x, downY);
Debug.Log("Mouse Position: " + e.mousePosition);
}
}
}
As mentioned in the comments I guess it simply is the nature of the tools that they are exclusive and you can only have one active at a time.
As alternative I would rather simply
enable/disable this via a general header menu entry
(optionally) store that decision persistent in EditorPrefs (pretty much like PlayerPrefs but for the editor itself)
and accordingly attach a listener to SceneView.duringSceneGui
This could look somewhat like e.g.
public static class StraightLineTool
{
// Used for the displayed menu labels
// and also simply (ab)used as the unique key for the EditorPrefs
private const string k_MenuName = "My Tools/Straight Line Tool";
private static float downY;
// Property for simplifying access and setting more centralized
private static bool IsEnabled
{
get => EditorPrefs.GetBool(k_MenuName, false);
set => EditorPrefs.SetBool(k_MenuName, value);
}
// method to be called when clicking the menu button
[MenuItem(k_MenuName)]
private static void ToggleEnabled()
{
IsEnabled = !IsEnabled;
ApplySettings();
}
// adding a checkmark when is enabled and simply always allow to click it
[MenuItem(k_MenuName, true)]
private static bool ToggleEnabledValidate()
{
Menu.SetChecked(k_MenuName, IsEnabled);
return true;
}
// Called on every project loading or code recompilation
[InitializeOnLoadMethod]
private static void Initialize()
{
EditorApplication.delayCall -= ApplySettings;
EditorApplication.delayCall += ApplySettings;
}
private static void ApplySettings()
{
// remove so only happening once
EditorApplication.delayCall -= ApplySettings;
SceneView.duringSceneGui -= OnSceneGUI;
if (IsEnabled)
{
// if enabled start listening
SceneView.duringSceneGui += OnSceneGUI;
}
}
// Callback listening to any SceneView.duringSceneGui
private static void OnSceneGUI(SceneView sceneView)
{
// Not sure tbh what this does or if you need it still in this approach
// HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
var currentEvent = Event.current;
if (currentEvent.shift)
{
if (currentEvent.type == EventType.MouseDown)
{
if (currentEvent.button == 0)
{
downY = currentEvent.mousePosition.y;
}
}
if (currentEvent.type == EventType.MouseDrag)
{
if (currentEvent.button == 0)
{
currentEvent.mousePosition = new Vector2(currentEvent.mousePosition.x, downY);
Debug.Log("Mouse Position: " + currentEvent.mousePosition);
}
}
}
}
}
=> using this the tool will be enabled/disabled persistent even when restarting Unity
Then using this as start point you can probably still try to somehow integrate this somewhere more nicely into the SceneView menus - but maybe that is also overkill ;)
In the game I am trying to make. When I am holding my fire button and I pick up a power up SuperShot, it continues to fire my regular laser unless I release my fire button and press it again. The same thing happens again when the power up ends. I have researched and I cant seem to figure out a way to check on where the power up is active while holding the key down.
private void Fire()
{
if (Input.GetButtonDown("Fire1"))
{
if (SuperShotIsActive)
{
superShotFiringCoroutine = StartCoroutine(SuperShotFireContinuously());
}
else if (!SuperShotIsActive)
{
firingCoroutine = StartCoroutine(FireContinuously());
}
}
if (Input.GetButtonUp("Fire1"))
{
StopCoroutine(firingCoroutine);
StopCoroutine(superShotFiringCoroutine);
}
}
You might be able to fix this by looking away from the button itself and into the code of the actual firing of the weapon.
//psuedocode-ish example. Just for the concept
//not necessarily the best idea to use a for loop but maybe it works for you.
FireContinuously()
{
for(int foo = 0; foo < ammo; foo++)
{
if(!SuperShotIsActive)
{
//normal firing code you have goes here
}
else
{
//supershot code goes here
}
}
}
In FireContinuously() (which I assume is a loop of some sort) you can add a check before each shot to see if the player now has the powerup and the necessary logic to change to the appropriate SuperShotFireContinuously(). Do the opposite for SuperShotFireContinuously() to change to FireContinuously().
I'm trying to make it so my toggle = !toggle doesn't just repeat OVER and OVER when you press it once because it kind of toggles on and off over and over if you hold it it just keeps toggling like 30 times a second and I want it to have a delay here is the gist of my code
private class AirJump_Patch
{
private static void Postfix (Locomotion.Player __instance)
{
List<InputDevice> list = new List<InputDevice>();
InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.HeldInHand |
InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right, list);
list[0].TryGetFeatureValue(CommonUsages.secondaryButton, out
Plugin.AirJump_Patch.secondaryButton);
if (Plugin.AirJump_Patch.secondaryButton)
{
Plugin.AirJump_Patch.jumpToggled = !Plugin.AirJump_Patch.jumpToggled;
}
if (!Plugin.AirJump_Patch.jumpToggled)
{
//DO CODE
}
ECT ECT.
I tried adding delay using StartCoroutine, doesn't work
I tried adding delay using simple C# wait things, doesn't work
This is in a private static void. I cannot change that. Is there a reasonably easy way to make the toggle not repeat 30 times a second with a 0.5 second delay or so?
Maybe you could use a simple timer field like
private float lastToggle;
and then
if(Time.time > lastToggle + 0.5f)
{
lastToggle = Time.time;
Plugin.AirJump_Patch.jumpToggled = !Plugin.AirJump_Patch.jumpToggled;
}
Alternatively you could actually handle it really only once e.g. like
private bool alreadyToggled;
if(Plugin.AirJump_Patch.secondaryButton)
{
if(!alreadyToggled)
{
Plugin.AirJump_Patch.jumpToggled = !Plugin.AirJump_Patch.jumpToggled;
alreadyToggled = true;
}
}
else
{
alreadyToggled = false;
}
now it is really only handled once per button press.
My ultimate goal is to only enable the photo capture button when a face is actually detected in the frame. Adding face detection using AVCaptureMetadataOutput is really simple, but I am having trouble finding a way to tell when the frame is empty. The AVCaptureMetadataOutput only has the one delegate method, DidOutputMetadataObjects and it is only called when the AVCaptureMetadataOutput detects an object, not when there is no object. I need a method that continually fires every second or so, or I need a way to check the detected objects within a timer.
I need something like:
IsFaceDetected () {
isButtonEnabled = metadataOutput.IsDetectingObject();
}
or
new Timer(repeat 1000ms) {
if (metadataOutput.DetectedObject == Object.Face) {
buttonEnabled == true;
} else {
buttonEnabled == false;
}
}
Has anyone discovered a way to make something like this work?
Well I was unable to really find a way to do what I wanted so I just resorted to adding a single use timer to the end to the end of the callback method to disable the button IF the callback method was not called again.
public void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput,
AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
{
// dispose of the existing timer if there is one
_timer?.Dispose();
if (metadataObjects.OfType<AVMetadataFaceObject>() != null) {
Button.Enabled = true;
} else {
Button.Enabled = false;
}
// have the timer disable the button if no face is detected again
_timer = new Timer(new TimerCallback((object state) => {
InvokeOnMainThread(() => {
Button.Enabled = true;
});
}), null, 150, Timeout.Infinite);
}
I want to know how to detect if the user has released the mouse button the first time, or the times after that:
Pseudo-Code:
if *first* (Input.GetMouseButtonUp(0))
{
do something
}
if *second, third, fourth..etc.* (Input.GetMouseButtonUp(0))
{
do something else
}
I really have no idea on how to accomplish this. I'm sure it's pretty simple though!
This is only an idea, but you could do this using a flag variable, like this:
private static bool WasFirstTimeReleased = false;
if (Input.GetMouseButtonUp(0))
{
if (!WasFirstTimeReleased)
{
WasFirstTimeRelease = true;
//do your stuff for first time
}
else
{
//do your stuff for all other times
}
}
Generally, you have to remember somewhere how many times button was released. Just create field in yours class:
private int clicks = 0;
then:
if (Input.GetMouseButtonUp(0))
{
if(clicks == 0)
{
// do something on first click
}
else
{
// do something on further click
}
clicks++;
}
If the object where you store clicks counter is created each time you press mouse button then mark counter using static word.
Keep track of mouse clicks:
int _leftUp;
void Update()
{
var leftUp = Input.GetMouseButtonUp(0);
if (leftUp) _leftUp++;
// etc ...
}
Easiest way to accomplish this would be to use a counter to check the number of times the user has released the button.
private int releaseCounter = 0;
And then in an if statement:
if (Input.GetMouseButtonUp(0)) {
releaseCounter++;
//If you know on which release you want the code to be executed,
//replace the x with that number.
if (releaseCounter == x) {
//your code here
}
//If you want the code to be executed at set intervals.
//replace the x with the interval number.
if(releaseCounter%x == 0) {
//your code here
}
}
Hope I helped.