I'm new to unity, and I'm trying to build a menu to a game, but I can't seem to be able to use a key press to activate the button.
public class MenuCamControl : MonoBehaviour
{
public Transform currentMount;
public float speed = 0.1f;
public float zoom = 0.1f;
void Update()
{
transform.position = Vector3.Lerp(transform.position, currentMount.position, speed);
transform.rotation = Quaternion.Slerp(transform.rotation, currentMount.rotation, speed);
}
public void SetMount(Transform newMonut)
{
currentMount = newMonut;
}
}
Edit: I added the code I have right now, which is a animation.
Edit2: This is where the animation leads image
Use Input.anyKey and a flag to determine when the first key is pressed to not trigger it multiple times.
public class Example: MonoBehaviour
{
private bool keyPressed = false;
void Update()
{
if (Input.anyKey && !keyPressed)
{
keyPressed = true;
}
else
{
// put your camera move code here
}
}
}
This method also picks up any mouse input as well. If you truly just want any keyboard input and not any mouse input to continue I can update my answer.
You do not need a UI button for this. Just a function to track if an input occurs and another to handle starting or loading your game. If you want the game to start when a button is clicked, you would just need to assign the onClick listener to a function either in code or in the inspector.
If you add what code you'd like to run or what you'd like to do after the click I can update my snippet.
Edit: Here is how you would combine my snippet and your current code. You can also use a Coroutine, but it is not needed.
public class MenuCamControl : MonoBehaviour
{
public Transform currentMount;
public float speed = 0.1f;
public float zoom = 0.1f;
private bool keyPressed = false;
void Update()
{
if (Input.anyKey && !keyPressed)
{
keyPressed = true;
}
else
{
transform.position = Vector3.Lerp(transform.position, currentMount.position, speed);
transform.rotation = Quaternion.Slerp(transform.rotation, currentMount.rotation, speed);
}
}
// I am not sure what this does, it currently is not called?
public void SetMount(Transform newMonut)
{
currentMount = newMonut;
}
}
With a Coroutine, it could look something like
void Update()
{
if (Input.anyKey && !keyPressed)
{
keyPressed = true;
if(referenceToCoroutine == null)
referenceToCoroutine = StartCoroutine(DoCameraAnimation());
}
}
private IEnumerator DoCameraAnimation()
{
...
}
You would need to change your current camera animation slightly to work in a Coroutine.
Related
I have a rebind script in my game that looks like this:
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.EventSystems;
using TMPro;
public class RebindButton : MonoBehaviour
{
[SerializeField] private InputActionReference inputActionRef;
[SerializeField] private TMP_Text buttonText;
[SerializeField] private int actionIndex = 0;
private InputActionRebindingExtensions.RebindingOperation rebindingOperation;
private void Start()
{
buttonText.text = InputControlPath.ToHumanReadableString(inputActionRef.action.bindings[actionIndex].effectivePath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
}
public void StartRebinding()
{
if (inputActionRef.action.bindings[actionIndex].isPartOfComposite)
{
Debug.Log(inputActionRef.action.bindings[actionIndex].path);
}
rebindingOperation = inputActionRef.action.PerformInteractiveRebinding(actionIndex)
.WithCancelingThrough("<Keyboard>/escape")
.OnMatchWaitForAnother(0.1f)
.OnCancel(operation => RebindCancel())
.OnComplete(operation => RebindComplete())
.Start();
}
private void RebindComplete()
{
Debug.Log(inputActionRef.action.bindings[actionIndex].path);
buttonText.text = InputControlPath.ToHumanReadableString(inputActionRef.action.bindings[actionIndex].effectivePath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
rebindingOperation.Dispose();
FindObjectOfType<EventSystem>().SetSelectedGameObject(null);
}
private void RebindCancel()
{
rebindingOperation.Dispose();
FindObjectOfType<EventSystem>().SetSelectedGameObject(null);
}
}
But all bindings that I change keep the same action path as before, even though their inputaction.callback button changing according to rebinding. That makes impossible to change my composite Vector2 movement binding. Vector2 changing only when I press button that showed in the action path. My movements set to WASD. When I changing it to the arrows my hero starting to move only when I press one of the arrows and one of WASD buttons. His direction changes according to the WASD buttons I press. Here is some movement script code:
void Update()
{
if (moveButtonIsHeld)
{
direction = playerInput.PlayerMovement.Movement.ReadValue<Vector2>();
areTwoKeysPressed = direction.x != 0 && direction.y != 0;
Move();
}
else
playerComponent.animator.SetLayerWeight(1, 0);
}
public void SetStateMoveButton(InputAction.CallbackContext context)
{
if (context.performed)
moveButtonIsHeld = true;
if (context.canceled)
moveButtonIsHeld = false;
}
public void Move()
{
transform.Translate(direction * speed * Time.deltaTime);
if (direction.x != 0 || direction.y != 0)
SetAnimatorMovement(direction);
}
Also I would like to ask how to get actionIndex in the script. Now I'm setting it manually in the inspector.
I'm trying to do RTS style movement with single selection only and without navmeshagent. When I click while another unit is already moving, its getting stopped and new one moving to same the point. Is there any way to seperate their hit points? Like I click to unit and order it to the point and then I click another unit and order him to another point.
Here is the video: demo
private Camera mycam;
private RaycastHit hit;
public Vector3 tf;
public LayerMask ground;
private bool move;
public static UnitMove instance;
private void Awake()
{
instance = this;
}
private void Start()
{
mycam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButtonDown(1) && haveSelected())
{
if (UnitClick.Instance.selectChanged)
{
getMouseRay();
}
}
if (move)
{
SetDestination(UnitClick.Instance.selectedUnit.transform.position, hit);
}
}
private void SetDestination(Vector3 unitPos, RaycastHit hit)
{
move = true;
if (Vector3.Distance(UnitClick.Instance.selectedUnit.transform.position, hit.point) > 0.5f)
{
UnitClick.Instance.selectedUnit.transform.position = Vector3.MoveTowards(UnitClick.Instance.selectedUnit.transform.position,
new Vector3(hit.point.x, UnitClick.Instance.selectedUnit.transform.position.y, hit.point.z), 1f * Time.deltaTime);
}
}
public void getMouseRay()
{
Ray ray = mycam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, ground))
{
move = true;
}
}
private bool haveSelected()
{
if (UnitSelection.Instance.unitSelected.Count > 0)
{
return true;
}
else
{
return false;
}
}
}
Looks to me like rather than just setting a destination, the SetDestination() function actually moves UnitClick.Instance.selectedUnit every frame. Once you click another unit, UnitClick.Instance.selectedUnit presumably changes to the newly selected unit, meaning every object’s SetDestination() code is always moving the same single object.
You’ll want to do something like save the destination in an instance variable when SetDestination() is first called and then have the move code change either:
change transform.position if the script is attached to the object you want to move
or if not: save the current UnitClick.Instance.selectedUnit in an instance variable when SetDestination() is first called and then change its position in the move code.
Edit: Here’s an attempt to show what I mean but note that this assumes the script is attached to the GameObject you want to move. It also preserves your existing system of setting the destination every frame while the unit is selected (it may be more desirable to update it only when the mouse is clicked?)
private Camera mycam;
private RaycastHit hit;
public Vector3 tf;
public LayerMask ground;
private bool move;
private Vector3 destination;
public static UnitMove instance;
private void Awake()
{
instance = this;
}
private void Start()
{
mycam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButtonDown(1) && haveSelected())
{
if (UnitClick.Instance.selectChanged)
{
getMouseRay();
}
}
if (move)
{
if( transform == UnitClick.Instance.selectedUnit.transform ) // If we are currently selected
SetDestination(/*UnitClick.Instance.selectedUnit.transform.position,*/ hit); // Update our destination
// If ‘move’ is true, move towards our destination whether or not we are currently selected
if (Vector3.Distance(transform.position, destination) > 0.5f)
transform.position = Vector3.MoveTowards(transform.position, destination, 1f * Time.deltaTime);
else
move = false; // This part is a guess at what you want — remove if it causes problems.
}
}
private void SetDestination(/*Vector3 unitPos,*/ RaycastHit hit)
{
move = true;
destination = new Vector3(hit.point.x, transform.position.y, hit.point.z);
}
public void getMouseRay()
{
Ray ray = mycam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, ground))
{
move = true;
}
}
private bool haveSelected()
{
if (UnitSelection.Instance.unitSelected.Count > 0)
{
return true;
}
else
{
return false;
}
}
I have 1 script to PlayerMovement and one for powerUp I the power-up code I reference player movement to change the speed and change the bool named timer to true and I write that in log and when I touch the paper the speed doesn't change and the timer don't turn to true but in the log, its say that is yes
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private float TargetPos;
public float Speed;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector2(TargetPos, transform.position.y);
}
public void right()
{
TargetPos = transform.position.x + Speed * Time.deltaTime;
}
public void left()
{
TargetPos = transform.position.x - Speed * Time.deltaTime;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Powrups : MonoBehaviour
{
public PlayerMovement pm;
public float PowerUpActiveTime;
public float StartPowerUpActiveTime;
public float peperSpeed;
float NormalSpeed;
bool timer;
bool timerover;
private void OnTriggerEnter2D(Collider2D col)
{
if (col.name == "peper")
{
pm.Speed = peperSpeed;
timer = true;
Debug.Log("timerOn");
Debug.Log(pm.Speed);
Debug.Log(timer);
}
}
private void Update()
{
while(timer)
{
GameObject Pause = GameObject.Find("Pause");
PauseScript pausescript = Pause.GetComponent<PauseScript>();
if (!pausescript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timerover = true;
}
if (timerover)
{
timer = false;
}
}
}
if (timerover)
{
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
}
}
Your mistake is that while loop.
You are lucky that until now you probably have tested this always while not being in pause mode ;)
This while would completely freeze your app and the entire Unity Editor!
In general be extremely careful with while loops and nested conditions like here, where the exit condition might never be fulfilled!
What happens currently is that you are not in pause mode so this while loop gets activated and runs until timer is set to false .. completely within one single frame. That is the reason why to you it seems that the value is never true.
What you rather want anyway is that code block be executed once per frame.
And in particular in a frame based application like Unity also have some performance impacts in mind.
You shouldn't use Find and GetComponent repeatedly within Update but store and re-use the results.
So your code should rather be
// If possible already drag this in via the Inspector
[SerializeField] private PauseScript _pauseScript;
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
// Get this ONCE as fallback on runtime
if(!_pauseScript)
{
_pauseScript = GameObject.Find("Pause"). GetComponent<PauseScript>();
// Or simply use
//_pauseScript = FindObjectOfType<_pauseScript>();
}
}
private void Update()
{
if(timer)
{
if (!_pauseScript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timer = false:
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
}
}
Besides all that, you should rather not let an external power-up control your player values. I would rather go the other way round and have your player object have a component which checks into which power-up items you run and react to it accordingly.
So your power-up itself would actually only be a trigger without any clue if or how exactly the player will be influenced by it.
In my project I have a Ground Plane Stage with an child object. Now I want to rotate this object with a UI Button. If i hold the button down it should rotate and if I release the button the rotation should stop. Unfortunately I´m not able to do this.
This is my script:
public class RotateObject : MonoBehaviour
{
public float rotationSpeed = 45f;
public bool isPressed = false;
public void TogglePressed(bool value)
{
isPressed = !isPressed;
} //edit added missing Brace
void Update()
{
if (isPressed)
{
transform.Rotate(Vector3.up * rotationSpeed * Time.deltaTime);
}
}
}
I added Event Trigger with Pointer Down and Pointer Up to the button.
The script is attached to my gameObject and the TogglePressedfunction is linked to Pointer Down with the checkbox checked and to Pointer Up, here checkbox is not checked.
If I test it in Unity and click the button, the inspector shows that it works, but if I upload it to my phone, there is no rotation of the object.
EDIT:
Input.GetMouseButtonDown()
void Update()
{
if (isPressed && Input.GetMouseButtonDown(0))
{
// Same Code
}
}
Input.GetButtonDown()
void Update()
{
if(isPressed && Input.GetButtonDown("Fire1"))
{
//Same Code
}
}
EDIT2:
This is my script i use now:
public class RoatateObject : MonoBehaviour
{
public float rotationSpeed = 45f;
void Update()
{
if(Input.GetMouseButtonDown(0))
{
transform.Rotate(Vector3.up * rotationSpeed * Time.deltaTime);
}
}
}
i dont know exactly if pointerdown is recognized on phones, do you have tried Input.GetMouseButtonDown() (also trigger on phones)?
maybe to speed up your testing try unity remote to test local instead of uploading to your phone
Hello in my below code i am updating my camera position using UI Buttons which is working fine what i want is to do this same process but by pressing right arrow key like if i press right arrow key camera will change its position to point A then stops there and when again i press the same arrow key the camera will change its position to point B and as in the code i have a different function to be called on different ui button so on thanks here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cam : MonoBehaviour
{
[Header("Locations where camera will update its position step by step")]
public Transform handleview;
public Transform needle1view;
public Transform wallview;
public Transform handletwoview;
public Transform needle2view;
public Transform switchview;
public Transform lastswitchview;
public GameObject Animatebtn;
Animator animatebtnanim;
[Header("UI Buttons")]
public GameObject inspectionbtn;
public GameObject animatebtn;
public GameObject step2btn;
public GameObject step3btn;
public GameObject step4btn;
public GameObject step5btn;
public GameObject step6btn;
public GameObject step7btn;
[Header("Inspection Views")]
public Transform startview;
public Transform handle1view;
public Transform motorview;
public Transform handle2view;
[Header("Move Boolean")]
public bool move = false;
[Header("Speed At Which Cam Moves")]
public float speed;
[Header("Current View/position Of Camera")]
Transform currentVIEW;
[Header("Current Angel Of Camera")]
Vector3 currentangel;
[Header("FieldofView of Camera ")]
public float camFieldOFview = 24f;
public int track = 0;
// Use this for initialization
void Start()
{
Camera.main.fieldOfView = camFieldOFview;
animatebtnanim = Animatebtn.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (move)
{
//float step = speed * Time.deltaTime;
//transform.position = Vector3.MoveTowards(transform.position, currentVIEW.position, step);
transform.position = Vector3.Lerp(transform.position, currentVIEW.position, Time.deltaTime * speed);
currentangel = new Vector3(Mathf.LerpAngle(transform.rotation.eulerAngles.x, currentVIEW.transform.rotation.eulerAngles.x, Time.deltaTime * speed),
Mathf.LerpAngle(transform.rotation.eulerAngles.y, currentVIEW.transform.rotation.eulerAngles.y, Time.deltaTime * speed),
Mathf.LerpAngle(transform.rotation.eulerAngles.z, currentVIEW.transform.rotation.eulerAngles.z, Time.deltaTime * speed));
transform.eulerAngles = currentangel;
}
//if (Input.GetKey(KeyCode.RightArrow))
//{
// if (track == 7)
// track = 1;
// if (track == 1)
// {
// moveTOstartVIEW();
// move = true;
// }
// else if (track == 2)
// {
// moveTOhandleONEview();
// move = true;
// }
// track += 1;
//}
}
// this function will lerp camera to startview location
public void moveTOstartVIEW()
{
currentVIEW = startview;
move = true;
}
public void moveTOhandleONEview()
{
currentVIEW = handle1view;
move = true;
animatebtnanim.SetBool("start", true);
animatebtnanim.SetBool("move", false);
}
public void moveTOmotorview()
{
currentVIEW = motorview;
move = true;
}
public void moveTOhandleTWOview()
{
currentVIEW = handle2view;
move = true;
}
public void Handleview()
{
currentVIEW = handleview;
inspectionbtn.SetActive(false);
animatebtn.SetActive(false);
move = true;
}
public void Needleoneview()
{
currentVIEW = needle1view;
step2btn.SetActive(false);
move = true;
}
public void Wallview()
{
currentVIEW = wallview;
move = true;
step3btn.SetActive(false);
}
public void Handletwoview()
{
currentVIEW = handletwoview;
move = true;
step4btn.SetActive(false);
}
public void Needletwoview()
{
currentVIEW = needle2view;
move = true;
step5btn.SetActive(false);
}
public void Switchview()
{
currentVIEW = switchview;
move = true;
step6btn.SetActive(false);
}
public void lastSwitchview()
{
currentVIEW = lastswitchview;
move = true;
step7btn.SetActive(false);
}
}
As I understood you have a usual UI.Button component and now want to do the same thing on a certain keyboard key as this button would do in onClick.
Solution 1: Extending the Button
I would simply invoke the Button's onClick event after getting a certain KeyCode by putting the following component right next to the Button component on your button objects (not on the camera)
using UnityEngine;
// make sure you are not accidentely using
// UnityEngine.Experimental.UIElements.Button
using UnityEngine.UI;
[RequireComponent(typeof(Button))]
public class KeyboardButton : MonoBehaviour
{
// Which key should this Button react to?
// Select this in the inspector for each Button
public KeyCode ReactToKey;
private Button _button;
private void Awake()
{
_button = GetComponent<Button>();
}
// Wait for the defined key
private void Update()
{
// If key not pressed do nothing
if (!Input.GetKeyDown(ReactToKey)) return;
// This simply tells the button to execute it's onClick event
// So it does exactly the same as if you would have clicked it in the UI
_button.onClick.Invoke();
}
}
Solution 2: Replacing the Button
Alternatively if you do not want to use a Button anymore at all you can add your own UnityEvent e.g. OnPress to the above script instead
using UnityEngine;
using UnityEngine.Events;
public class KeyboardButton : MonoBehaviour
{
// Which key should this Button react to?
// Select this in the inspector for each Button
public KeyCode ReactToKey;
// reference the target methods here just as
// you would do with the Button's onClick
public UnityEvent OnPress;
// Wait for the defined key
private void Update()
{
// If key not pressed do nothing
if (!Input.GetKeyDown(ReactToKey)) return;
OnPress.Invoke();
}
}