how to take button hold as an input in Unity C# - c#

I am trying to create a situation where I can take "hold 'E' to interact" type of input. My first thoughts are, start a timer after the button has been held down. After the timer reaches 700 ms, take the action. But the problem, is I don't know how to write that in c# since I am new in this language. Any other approach is also appreciated.
I am adding pseudocode here
//pseudocode
//inside the update function
if (e is pressed)
{
start timer;
if (timer.time == 700)
{
Debug.Log("E is pressed for quite some time");
}
}

What I would do is use a coroutine.
When the user presses down the button, the coroutine would start and if the user stops pressing the button the coroutine would stop.
To do this, your code could look something like:
using System.Collections;
[SerializeField] int time;
void Update ()
{
if (Input.GetKeyDown(KeyCode.E)) StartCoroutine(Action());
if (Input.GetKeyUp(KeyCode.E)) StopCoroutine(Action());
}
IEnumerator Action ()
{
yield return new WaitForSeconds(time);
// do stuff
}
Thanks to derHugo for spotting an error in my example code - its now been edited.
Of course there are other ways to go about what you want to do, but I'm thinking a coroutine is your best option because it will then be super easy to add some feedback to the user when they are holding down, in your case, 'E'. For example, you could fill an image to create an effect similar to this:
And, well, even if you don't want to do that - coroutines are still a good option.
Hope this helps!

Related

Parameter" does not exist warning

The Context:
I'm currently creating a melee combo system for my 2D RPG. The first time a player attacks will trigger the first attack animation and if the player chooses to attack a second time, it ill trigger the second attack animation. I got the system to work by calling an attack function that passes a string parameter depending on which animation I need to be played.
So the ComboOrder function will be called every frame to order the animations and once the player presses the "E" key, the Attack function will be called with either "Attack-1" or "Attack-2".
The Problem: The code and animations work fine however, after many changes to other parts of the game I noticed, I get a warning every time I press the "E" key. What I changed was completely unrelated ti this so I don't see a connection from that to the warning.
Parameter" does not exist
Solutions I have tried: I've searched the web for answers, however, none of them fit my issue. It seems that Unity thinks I'm passing the string " which would indicate a syntax error but I've double-checked my code and I can't find anything of sorts. I've also heard that it has been a bug in Unity but they never told how to fix it. Is there anything I can do if that were the case?
Here's the code:
void ComboOrder() {
if (comboIndex == 1)
{
if (Input.GetKeyDown(KeyCode.E)) // Attack Input
{
Attack("Attack-1");
comboIndex++;
}
} else if (comboIndex == 2) {
if (Input.GetKeyDown(KeyCode.E)) // Attack Input
{
Attack("Attack-2");
comboIndex--;
}
}
}
public void Attack(string attack)
{
animator.SetTrigger(attack);
}
And here are the Animator settings for the player:
So after not being able to work on the game for about two weeks, I came back to take a closer look and realized it was what you said, there was an animation event calling the function without passing a parameter and since it was not coded, I never caught it. Thank you to everybody who helped.

Passing a class that use a method as a member

I've been learning C# for the past 6 months or so and am currently stuck on a turn-based battle system for a game.
I have tried a couple of different things and watched loads of videos about it but I didn't find what I was looking for. Maybe it's impossible but here it is. Also, I'm not looking for someone to write the code or anything, I am mainly looking for pointers.
switch (currentBattleState)
{
case (BattleState.WAIT):
{
if (heroesToManage.Count > 0)
currentBattleState = BattleState.ACTION_SELECT;
break;
}
case (BattleState.ACTION_SELECT):
{
foreach(Button button in buttons)
button.interactable = true;
break;
}
}
This is the current status of the battle state machine. I have two buttons at the moment, one that will attack and one that will open a spellbook to choose which spell to cast.
What I would need is something like this pseudocode:
void OnClicked()
{
ChooseSpell(); // If it's the "magic" button
ChooseTarget();
// And this Action will pass the target to the Attack() or Cast()
// function on the HeroStateMachine
listOfActions.Add(new Action(origin, target, Attack() or Cast())
}
and then when each Action is processed by the BattleStateMachine, like so, again in pseudocode because I have no idea how to do it:
void ProcessAction(Function action, int index = 0)
{
// This would result in Cast(origin, target) or Attack(origin, target)
action(listOfAction[index].origin, listOfAction[index].target);
}
// And then call it like this ?
ProcessAction(listOfActions[0].function)
we remove it from the list so, basically, this would be a first-in first-out type of thing.
The issue is that I haven't found a way to do that. So far, the videos I've watched are creating a derived class for every single type of attack, spell or item (all derived from a BaseAction)... Which I don't want to do since this seems like really wet code... Also, I wouldn't want to create a whole new class every time I want to add something to the game...
As I said, I'm not looking for full code but so far you've all been helpful so maybe just a pointer to what I could use to do what I want? Or just tell me if I'm approaching this whole thing from the wrong angle.

Does 'yield return new WaitForSeconds(4.0f)' have to charge?

I am making a First Person Shooter.
I want that my gun reloads, there plays an animation and after 4 seconds the number of bullets will be displayed. But sometimes it does and sometimes it doesn't wait and displays the bullets immediately without playing the animation.
IEnumerator waitForReload()
{
if (reloadNow==true && !gun.GetComponent<Animation>().IsPlaying("Reload"))
{
animator.SetInteger ("Anumber", reload);
yield return new WaitForSeconds (4.0f);
reloadNow = false;
displayBullets ();
}
}
I would advise against using the Animation component, you'll be after the Animator. They do similar things, except the Animator is Unity's newer and more improved version. Grabbing the Animation component and checking IsPlaying won't tell you whether the animation is playing, as the animation is being played through the Animator component.
First thing to check is your co-routines, how often are you firing them? The logic within is sound, however, you would ideally not start the co-routine again if you are reloading.
The yield in the co-routine is where the magic happens. Each time you yield, the function remembers it's location, so when you execute the function again it will pick up from where you left off. Each time you call StartCoroutine, a new routine is created.
So, if you accidentally start the co-routine again, you'll have multiple instances of it running, which is why it is potentially glitching out in certain places.
I'd recommend whacking some debugs in, put one in where you start the co-routine and one when you finish. If your console is getting spammed, then you know that you're starting it incorrectly and would either have to stop it from firing when it shouldn't or by starting the co-routine, and ending it if your checks say no.
if(reloading == false)
{
reloading = true;
StartCoroutine(waitForReload());
}
else
{
//do nothing
}
IEnumerator waitForReload()
{
//your custom reload logic here
reloading = false;
//set reloading to false so we can reload again later, after the time is up in your case
}
Something along those lines should give you the intended result.
For the yield command, here's a reference
https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
For the animator component:
https://docs.unity3d.com/ScriptReference/Animator.GetCurrentAnimatorClipInfo.html
This'll give you back the current animation clip that is playing, so you can check names that way.
Hopefully this'll lead you in the right direction.
I believe the problem occurs because your coroutine runs more than once, try this (assuming your animation is 4 seconds long)
IEnumerator waitForReload()
{
if (reloadNow)
{
animator.SetInteger ("Anumber", reload);
reloadNow = false;
yield return new WaitForSeconds (4.0f);
displayBullets ();
}
}
or preferably you can call the function that displays the bullets at the end of your animation

Avoid to use 'Update'?

I have a seperate script of time which I use to show time in my scene. It contains hour and minute and seconds variable.I want to do some specified work e.g., code execution on specified time and currently i am doing something like this. in Update. I am running a function which check continously check time variable in order to run an animation.
void Update()
{
checkTrainArriveTime();
}
void checkTrainArriveTime()
{
if (timeManager.GetComponent<Scale2>().hour == trainArriveTimeHour && timeManager.GetComponent<Scale2>().min == trainArriveTimeMin
&& isTrainArriveConditionExecute)
{
isTrainArriveConditionExecute = false;
PlayAnimationClip("Start");
}
else if (timeManager.GetComponent<Scale2>().min != trainArriveTimeMin)
{
isTrainArriveConditionExecute = true;
}
}
As Time will match this function will play the animation. Now I have 50 script attached to 50 different game Object. It is
working fine but It definitely not the right way to use Update Event. In my code, It is necessary to check time on every frame and
extra load on update. Is there any efficient way to do this Job?.
I can see your struggle. You are right, it is definitely not the best way forward.
The best option I can see here would be creating Animation Manager which is a singleton instance (there is only one instance allowed per application).
I would suggest moving your animation triggering logic to an Update method of AnimationManager.
Once you have done that. You will be able to access its instance calling AnimationManager.getInstance() method.
Next step is creating internal registry that would be nothing else than just a list of your registered game objects that you want to trigger animation for.
I don't know what exactly is your timeManager but I can imagine it is probably an instance of TimeManager controller that you drag and drop onto your public timeManager property. Consider turning it into singleton as well or at least moving assignment of timeManager.GetComponent<Scale2>() into Awake() method.
It is important to not to call GetComponent() method from inside of Update()', as it has an impact on performance.GetComponent` is quite expensive to call.
Hope it helps.

Check if object is active.Unity

I want to check if object is active and if it is I want to inactivate other objects. Here is my method. It is not the best one but I just want it to work. This method I have doesn't work right because it only runs once even though I have it in update(). Need some help here...
update: the problem is that when I choose the selection that selection becomes true and because another selection is already true this code is not working and right now I don't know how I can fix it.
public GameObject selectionTop, selectionBot, selectionSplash, selectionFloor;
private void Update() => Check();
public void Check()
{
if (selectionTop.activeSelf)
{
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionBot.activeSelf)
{
selectionTop.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionSplash.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionFloor.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
}
}
Update only gets called when the GameObject its script is on is activeInHierarchy and the script is enabled. My guess is that you have this script on one of the GameObjects that is being disabled and that is causing update not to get called.
So I would move this script to another game object that is not used one of the "selection" game objects so that update will always be called.
Use a variable to store what the current selection is.
Then when a new selection is made, hide the current selection, then call check.
or -
Make a function that hides all the items. Call that each time. Then set the items you want active.
I'm not sure if this is a silly answer, I've never worked with the game sdk but just looking at the code...
the
if(...)
{
}
else if (...)
{
}
I've always seen it written this way, but maybe it's just me, you can check if your code is executed both using the debugger and if it is not, you can use the Debug.Writeline to let something be printed inside the routines on your debug screen to see which code is executed.

Categories

Resources