Controls menu in Unity - choose buttons - c#

I am trying to add a menu in my 2D unity game where the user can specify which controls they prefer to use in the game. Right now I have a script where the code for the main character movements are specified as follows:
private void Update()
{
// Set the new direction based on the current input
if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)) {
movement.SetDirection(Vector2.up);
}
else if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow)) {
movement.SetDirection(Vector2.down);
}
else if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.LeftArrow)) {
movement.SetDirection(Vector2.left);
}
else if (Input.GetKeyDown(KeyCode.D) || Input.GetKeyDown(KeyCode.RightArrow)) {
movement.SetDirection(Vector2.right);
}
// Rotate the main character to face the movement direction
float angle = Mathf.Atan2(movement.direction.y, movement.direction.x);
transform.rotation = Quaternion.AngleAxis(angle * Mathf.Rad2Deg, Vector3.forward);
}
Instead of for example Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow) I'd like to listen to what the user has typed in, in a "controls" menu. However, I don't know how this menu should be connected to the script.
Does anyone have any suggestions on how this can be done or if there is some website/video that I can watch where this is implemented?
Thanks in advance,
N

Ah, key binding. There is no easy way of adding keybinds.
You would have to loop through all the keycodes and see if any are pressed. It isn't efficient but it works in this context.
foreach(KeyCode kcode in Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKey(kcode))
{
// Set key to given keycode.
return;
}
}
After that, you should save what key the user pressed into a file or into PlayerPrefs.
When the player starts the game again, just read it and set it back.

Related

How to delay the else statement without coroutine in unity c#?

I have a joystick display picture for my game. Currently, when the player touches the screen the image disappears and when the player is not touching the screen, it reappears. I wrote that using an if else statement.
if (indicator.inputIndicator.x != 0)
{
joystick.SetActive(false);
}
else
{
joystick.SetActive(true);
}
The problem is, I want the image to reappear after some time like 2 seconds. I want to delay the "else", but I do not want to use a coroutine. I want "else" to work after 2 seconds since the player takes his hand off the screen but I couldn't figure out how to do it. any help will be great.
Setting a timer is a pretty common problem you have to solve in Unity. One basic approach is to have a variable that you add Time.deltaTime every update. That way you can tell how long it has been since some condition was met.
Every Update iteration that meets the condition, add Time.deltaTime to the variable. If at some point the condition fails, reset the variable to 0. Then you can just base your joystick.SetActive() call on the value of your variable.
For example, your script might become:
float thresholdTimeToShowPrompt = 2;
// By starting at the threshold, the image is hidden at the start until a touch
float timeSincePlayerTouch = 2;
void Update()
{
// Rather than calling SetActive directly, just update the timer
if (indicator.inputIndicator.x != 0)
{
timeSincePlayerTouch = 0;
}
else
{
timeSincePlayerTouch += Time.deltaTime;
}
// Now we can base visibility on the time since the last user touch
bool shouldShowIcon = timeSincePlayerTouch >= thresholdTimeToShowPrompt;
// Only call SetActive when needed, in case of overhead
if (shouldShowIcon && !joystick.activeSelf)
{
joystick.SetActive(true);
}
else if (!shouldShowIcon && joystick.activeSelf)
{
joystick.SetActive(false);
}
}

Unity attack once during animation

Since I am still very beginner question maybe dumb but I am really cant find any solution. I am creating 3rd person adventure game and trying to implement enemy attack. The problem is that I cannot implement it in a way that enemy do damage only once during attack animation. In my code alreadyAttacked bool is changing to false only when the transitions between animations happens. However I want to reset this value everytime when the attack animation starts or finish.
void FixedUpdate()
{
playerInSightRange = Physics.CheckSphere(transform.position, sightRange, playerMask);
playerInAttackRange = Physics.CheckSphere(transform.position, attackRange, playerMask);
if (!playerInSightRange && !playerInAttackRange) Patroling();
if (playerInSightRange && !playerInAttackRange) Chasing();
if (playerInSightRange && playerInAttackRange) Attacking();
}
private void Attacking()
{
animator.SetInteger("Condition", 2);
agent.SetDestination(player.position);
if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 0.4f
&& animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 0.6f
&& alreadyAttacked == false)
{
player.GetComponent<Health>().healthValue -= damage / 100f;
alreadyAttacked = true;
}
if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 0.7f )
{
alreadyAttacked = false;
}
}
You might rather want to look into Animation Events.
Without having to query states from the Animator you can rather simply invoke certain events directly from your animation state itself!
Simply rather make it
public void CauseDamage()
{
if(player) player.GetComponent<Health>().healthValue -= damage / 100f;
}
and then in your animation itself add an Event
and select the method you want to call in the Event's inspector.
Then everytime your animation passes that key frame the event will be Invoked exactly once.

Unity Prevent playing a sound for all objects with the same name

the problem is that when I click on one object, all other objects (with the same name as that object) make a sound.
This is the code I'm using:
void OnMouseOver()
{
if (Input.GetMouseButton(0))
{
if (this.gameObject.name == "door")
{
anim.SetBool("open", !(anim.GetBool("open")));
SoundManagerScript.PlaySound("door");
}
if (this.gameObject.name == "window")
{
anim.SetBool("open", !(anim.GetBool("open")));
SoundManagerScript.PlaySound("window");
}
}
}
How to prevent playing a sound for all objects?
Converting my comment to an answer. The problem in the script is related to user input.
Input.GetMouseButton returns whether the given mouse button is held down. Therefore, your if statement is true while mouse is held down. This is the reason why you have some many sounds playing continuously because you Input.GetMouseButton returns true on several frames during the interaction .
Instead Input.GetMouseButtonDown should be used. Because it returns true on the frame the user pressed the given mouse button. It will return false when mouse is held down. Therefore, it will return true only once when mouse is pressed.
you could always try and set up an array of these objects that contain the sound and then turn them off all at the same time.
public gameObject[] itemsWithSound;
for(int i = 0; i < itemsWithSound.length; i++)
{
itemsWithSound.SoundManagerScript.StopSound();
}

Get Movement Direction of Mouse

I know this has been Asked but in my Case i dont know if i Can use the MouseEventArgs since its a Rendering / Input Loop where Im trying to use it Here is the Whole Rendering / Input Loop, I can only Move with the Keyboard which is Rather inconvinient.
public void update()
{
//--do raycast--//
raycast();
//=========================//
//=====take user input=====//
//=========================//
KeyboardState state = Keyboard.GetState();
bool lArrowKeyDown = state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A);
//MouseLeft
if (lArrowKeyDown)
{
rotate(rotSpeed);
}
//MouseRight
bool rArrowKeyDown = state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D);
if (rArrowKeyDown)
{
rotate(-rotSpeed);
}
//MouseUp
bool uArrowKeyDown = state.IsKeyDown(Keys.Up) || state.IsKeyDown(Keys.W);
bool shift = state.IsKeyDown(Keys.LeftShift);
if (uArrowKeyDown)
{
move(moveSpeed);
}
if (uArrowKeyDown && shift)
{
move(moveSpeed+0.01);
}
bool dArrowKeyDown = state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S);
//MouseDown
if (dArrowKeyDown)
{
move(-moveSpeed);
}
//=========================//
//=====user input end======//
//=========================//
}
This is the Whole Rendering / Input Loop, Would it be Possible to add a MouseEventArgs? Im unsure of if a MouseEventArgs Loops too when this Loops. Could someone help me Here?
I’m not entirely sure what you are asking for, but if you want to get movement direction from a mouse event, unless the mouse event has a last position and new position property, you need to keep track of the last position (x and y value) and use it with the current position to calculate the angle.
For calculating the angle, see
Calculating the angle between the line defined by two points
I can provide a better answer with more information.

Unity3d How to detect taps on android?

Currently I am struggling to work out how to create a simple bit of code that will tell me if a tap has happened, allowing me to get the position of taps. Having looked through many different articles I am rather stumped as Touch.tapCount that is often suggested only works for ios. Also, i have tried to use Touch.deltaTime and/or Touch.deltaPosition to detect a tap but have failed.
I define a tap as having
A very short period between the finger initially touching and finally exiting the phone
Little or no movement
Thanks for reading this, I hope its clear and precise and if any detail or assistance is required on my behalf in order for your to answer feel free to ask. Any assistance is gratefully received. Thanks.
Note - I work in C#
First you have to find out if your Android device indeed can register several touches. If you have a newer device, this shouldn't be a problem. I'm going to assume that your device can, and if it can not - you'll soon find out soon enough.
let's start with the update method.
void Update() {
// Nothing at the moment
}
What we first want to do is register touches. We can do this by putting a foreach inside, checking for touches in Input.touches. Like this:
void Update() {
foreach (Touch touch in Input.touches) {
}
}
By doing this, we are always checking how many touches there are currently on the screen. What we can now do is check by fingerId, and if fingerId == 0, 1, 2... run some code. Here's what we got now:
void Update() {
foreach (Touch touch in Input.touches) {
if (touch.fingerId == 0) {
// Finger 1 is touching! (remember, we count from 0)
}
if (touch.fingerId == 1) {
// finger 2 is touching! Huzzah!
}
}
}
We're great so far! What we now want to do is detect the motion we want. In our case, we wanted taps, right? That should work perfectly with TouchPhase Began, and Ended. There's also TouchPhase.Moved, but we don't need that now.
if (touch.fingerId == 0) {
if (Input.GetTouch(0).phase == TouchPhase.Began) {
Debug.Log("First finger entered!");
}
if (Input.GetTouch(0).phase == TouchPhase.Ended) {
Debug.Log("First finger left.");
}
}
Here we are checking the phase of the corresponding finger. If you run that now, you should be able to see the messages in the console whenever your first touch enters, as well as leaves the screen. This can be done with several touches, so here's the 'whole' script:
void Update() {
foreach (Touch touch in Input.touches) {
if (touch.fingerId == 0) {
if (Input.GetTouch(0).phase == TouchPhase.Began) {
Debug.Log("First finger entered!");
}
if (Input.GetTouch(0).phase == TouchPhase.Ended) {
Debug.Log("First finger left.");
}
}
if (touch.fingerId == 1) {
if (Input.GetTouch(1).phase == TouchPhase.Began) {
Debug.Log("Second finger entered!");
}
if (Input.GetTouch(1).phase == TouchPhase.Ended) {
Debug.Log("Second finger left.");
}
}
}
}
I'm hoping this will help you. I'm fairly new at this myself, so if we're lucky - someone with more experience can come and help. I'm confident that this could be written a lot cleaner. Just remember that if you build it, you can't see the console messages. Check out Unity Remote if you haven't already. Good luck! :)
After doing some searching (Googling "unity mobile detect tap"), I found this as the top search result. #Mothil's answer gets us very close to the solution and OP's answer solves the question. However, OP's answer does not account for multiple taps. Also tracking the count of started touches, ended touches, and moved touches (e.g. int SCount, MCount, ECount) is not necessary.
Instead we can directly loop through each touch and track each individual touch by its fingerId as #mothil did. For this we'll need two arrays to track a tap's characteristics: 1) short time delay, and 2) no movement. In the arrays below, the array index is the fingerId.
private float[] timeTouchBegan;
private bool[] touchDidMove;
We'll also need one more variable to store our desired tap time threshold. In my tests, I found that a threshold of 0.2f works pretty well.
private float tapTimeThreshold = 0.2f;
In the Start() function, we'll initialize this to hold 10 elements for 10 touches. This can be modified to however many touches desired.
In the Update() function, we loop through each touch. If in TouchPhase.Began then we set the timeTouchBegan[fingerIndex] to the current time; we also set touchDidMove[fingerIndex] to false.
If in TouchPhase.Moved, we set touchDidMove[fingerIndex] to true.
Finally, in TouchPhase.Ended, we can calculate the tap time.
float tapTime = Time.time - timeTouchBegan[fingerIndex];
If the tap time is less than the threshold and the touch did not move then we have a verified tap.
if (tapTime <= tapTimeThreshold && touchDidMove[fingerIndex] == false)
{
// Tap detected at touch.position
}
Complete Script
Here's the full class:
public class TapManager : MonoBehaviour
{
private float[] timeTouchBegan;
private bool[] touchDidMove;
private float tapTimeThreshold = 0.2f;
void Start()
{
timeTouchBegan = new float[10];
touchDidMove = new bool[10];
}
private void Update()
{
// Touches
foreach (Touch touch in Input.touches)
{
int fingerIndex = touch.fingerId;
if (touch.phase == TouchPhase.Began)
{
Debug.Log("Finger #" + fingerIndex.ToString() + " entered!");
timeTouchBegan[fingerIndex] = Time.time;
touchDidMove[fingerIndex] = false;
}
if (touch.phase == TouchPhase.Moved)
{
Debug.Log("Finger #" + fingerIndex.ToString() + " moved!");
touchDidMove[fingerIndex] = true;
}
if (touch.phase == TouchPhase.Ended)
{
float tapTime = Time.time - timeTouchBegan[fingerIndex];
Debug.Log("Finger #" + fingerIndex.ToString() + " left. Tap time: " + tapTime.ToString());
if (tapTime <= tapTimeThreshold && touchDidMove[fingerIndex] == false)
{
Debug.Log("Finger #" + fingerIndex.ToString() + " TAP DETECTED at: " + touch.position.ToString());
}
}
}
}
}
Unity Tests
I tested this in my game using Unity Remote. In the screenshot below, you can see my debug console logs. I did a four finger tap. You can see that fingers 0 through 3 entered and left without any movement detected. A tap was detected for each finger and each tap's location was printed to the console.
simple yet effective, jsut add the engine at top UI and add code in update dune
enter image description here
I am pretty sure that tapCount will work on Android.
In my scenario, what handles input is not deriving from a MonoBehaviour. Instead its just an object I pass around. I wont supply the full class, but just what handles phone touches/taps.
if (Input.touchCount > i)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
}
if (Input.GetTouch(i).phase == TouchPhase.Canceled)
{
}
if (Input.GetTouch(i).phase == TouchPhase.Ended)
{
}
if (Input.GetTouch(i).phase == TouchPhase.Moved)
{
}
if (Input.GetTouch(i).phase == TouchPhase.Stationary)
{
}
}
The i is because I want to know how many touches the user has, but this should get you going at least.
Edit: I forgot to mention, you might want this if statement chunk inside a coroutine.
https://docs.unity3d.com/Manual/Coroutines.html
Hope this helps.

Categories