There's Started, Performed and Cancelled, but what if I want to detect if I'm holding down the button on a Gamepad. So basically, how do i rewrite this to a gamepad with the new input system.
private void Jump()
{
if (isTouchingGround == true && Input.GetKeyDown(KeyCode.Space))
{
isJumping = true;
jumpTimeCounter = jumpTime;
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
}
if (Input.GetKey(KeyCode.Space) && isJumping == true)
{
if (jumpTimeCounter > 0)
{
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
}
There is no equivalent!
You have to Bind all Inputs to events and then process the inputs in the methods you assign to these events. This is because the new input system does not directly use Keys to make it work on multiple devices (keyboard, controller, phone, ...) without adding more code
See here.
The new input system exposes a Gamepad object with buttons each button is a
ButtonControl that exposes accessors for the button state:
Input.GetKeyDown(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.wasPressedThisFrame
Input.GetKey(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.isPressed
Input.GetKeyUp(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.wasReleasedThisFrame
Or if you want keyboard use Keyboard.
That lets you circumvent the new input system's configuration and hardcode your control scheme.
However, for supporting multiple input devices, you'd create a control scheme and use accessors on it:
Input.GetButton("fire") ~= m_Controls.fire.ReadValue<bool>()
Input.GetButtonUp("fire") ~= m_Controls.fire.triggered
Input.GetButtonDown("fire") ~= m_Controls.fire.triggered
triggered doesn't work quite like Input.GetButtonUp/Down. You'd configure the "fire" input for when during a press it should fire. You can set an input in your control scheme to be a Tap or SlowTap (ugh) and if it's a Tap, then triggered happens if it was released within the threshold time whereas SlowTaps are triggered if it's held for the threshold time.
You can also check which phase of press the input is in:
m_Controls.fire.phase == InputActionPhase.Started
If you go for this control scheme setup, see also this post about bad defaults.
I'd rewrite your code something like this (untested) and configure "jump" as a SlowTap with immediate activation:
void Jump()
{
// maybe need to clear isJumping if isTouchingGround?
if (isJumping)
{
if (m_Controls.gameplay.jump.ReadValue<bool>() && jumpTimeCounter > 0)
{
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
// Use triggered to avoid jumping again when we hold button on landing
else if (isTouchingGround && m_Controls.gameplay.jump.triggered)
{
isJumping = true;
jumpTimeCounter = jumpTime;
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
}
}
(But I'm not particularly advanced with the new input system.)
I'd recommend playing around with the simple demo sample that can be imported from the package window. See how the different ways of using the system work.
#if !ENABLE_LEGACY_INPUT_MANAGER
static Dictionary<KeyCode, Key> lookup;
#endif
static bool GetKeyDown(KeyCode key)
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetKeyDown(key);
#else
if (lookup == null)
{
lookup = new Dictionary<KeyCode, Key>();
foreach (KeyCode keyCode in Enum.GetValues(typeof(KeyCode)))
{
var textVersion = keyCode.ToString();
if (Enum.TryParse<Key>(textVersion, true, out var value))
lookup[keyCode] = value;
}
lookup[KeyCode.Return] = Key.Enter;
lookup[KeyCode.KeypadEnter] = Key.NumpadEnter;
}
return Keyboard.current[lookup[key]].wasPressedThisFrame;
#endif
}
Related
This is a weirdly phrased question...and I don't know how to explain it well. I'm making a ledge hang mechanic for my game I'm making, you press either of the directions to get onto the ledge, but you also press the directions to get off of the ledge. This means while you're trying to get on the ledge, he instantly falls off. Making a fixed wait time also doesn't feel good...I'm thinking that it would set the axis to 0 until the button is let go, but the problem is that if it's 0, the button is let go. It's a conundrum I can't solve.
Here is the code that detects movements pressed on the left or right arrow keys.
mx = Input.GetAxisRaw("Horizontal");
//Detects if the left or right arrow keys are pressed, and turns it into a number.
Here's some of the code that detects movement while you're hanging There's already a problem...there's an "if hanging" statement right before that's supposed to restrict movement until you let go or jump, but since moving is supposed to ALSO get you out of the hanging, it makes this point moot. I could really just use different buttons, like restricting it to just the jump button or down key (since that uses a different variable), but I want to see if a solution is possible.
if (IsGrabbing == true)
{
rb.velocity = new Vector2(0f, 0f);
rb.gravityScale = 0f;
// mx2 detects vertical movement. This is still a problem, if you're pressing down before you hang, it'll instantly hang instead of waiting for another press, but it's not as bad because you don't press down to hang on a ledge
if (mx != 0 || mx2 < 0)
{
//Just the code that makes you let go and unable to hang, this is reset when you land.
IsGrabbing = false;
anim.SetBool("IsHanging", false);
redXSize = 10f;
redYSize = 10f;
greenXSize = 0f;
greenYSize = 0f;
rb.gravityScale = 5f;
}
I've tried many things, a lot of "while" loops I've tried just crash the game. Does anyone know how to fix this issue? I'll provide all the code if needed.
Edit: With the help of the reply, I've found the solution! There needs to be a flag set like this, where by default it's false. However, it also only checks if it's false, and the else statement means it's true, not to mention the only way to set it to true bypasses that gate anyways. This gives the result needed! Also, while "while" loops seem to break it, adding a condition to every check to not do it while grabbing seemed to fix it. Thanks, everyone!
if (IsGrabbing == true)
{
if (mx != 0 && InitiateGrab == false)
{
InitiateGrab = false;
}
else
{
InitiateGrab = true;
}
rb.velocity = new Vector2(0f, 0f);
rb.gravityScale = 0f;
if (mx != 0 && InitiateGrab == true || mx2 < 0 && InitiateGrab == true)
{
IsGrabbing = false;
InitiateGrab = false;
anim.SetBool("IsHanging", false);
anim.SetBool("IsRising", false);
redXSize = 10f;
redYSize = 10f;
greenXSize = 0f;
greenYSize = 0f;
rb.gravityScale = 5f;
}
}
Since you don't have the code. I can't exactly help you out with the specifics, but I still understand what you're asking and can still help you out. Here is the "sample" code that I'd use in your case:
bool isHanging = false;
void Update()
{
if (Input.GetKeyDown("space") && !isHanging)
{
isHanging = true;
// Use ur code to hang onto the ledge.
} else if (Input.GetKeyDown("space") && !isHanging)
{
isHanging = false;
// Use ur code to get off the ledge.
}
}
bool CheckIfHanging()
{
if(isHanging) { return false; }
else return true;
}
Essentially, this causes a toggle effect where when you press the spacebar AND are close enough to the ledge, you set the bool to true, and you hang onto the ledge and went you want to get off it, you set the isHanging to false, and run your code to jump off the ledge. This does have the effect of making ur controls a toggle control however. If you're looking for a "press" control, where you hold onto a key to hold onto the ledge, and then let go when you press off, then maybe try this:
void Update()
{
if (Input.GetKeyDown("space"))
{
// Hang onto ledge
} else if (Input.GetKeyUp("space"))
{
// Let go of ledge.
}
}
Hope I could help!
(P.S. please put ur code in next time so that I can help you out more!)
Quick update on my answer since u posted ur code. Maybe try changing ur movement code to something like this
bool isHanging = false;
int speed; [SerializeField]
void MoveCharacter()
{
if (isHanging) return;
else mx = speed * Input.GetAxisRaw("Horizontal");
}
Essentially, this still allows you to move with your arrow keys, but will essentially stop your movement code if you're hanging. Also from ur previous comment, you said that you apparently can't use Input.GetKeyDown/Up for your left/right arrows? I'd recommend using KeyCode if that's the case, that should have the input for all btns in ur keyboard.
bool HangLedge()
{
if (!isHanging && Input.GetKeyDown(KeyCode.DownArrow)) return true;
else if (!isHanging && Input.GetKeyDown(KeyCode.DownArrow)) return false;
}
// Set ur ledge code and movement code to take this bool, and run the code when it's either false/true.
Hope I could clarify!
I'm developing a search game, where players must look for certain objects. Whenever the targeted object is found and has been picked up, the player wins and go to the next level. I tagged the targeted objects as "TargetObj". I successfully implemented this when there is only one object to look for. I want to modify my code to include cases where there is more than one object to look for. Here is my code :
public void someFunction() {
//if we press the button of choice
if (Input.GetKeyDown(KeyCode.Space)) {
//and we're not holding anything
if (currentlyPickedUpObject == null) {
//and we are looking an interactable object
if (lookObject != null) {
PickUpObject();
}
} else { //if we press the pickup button and have something, we drop it
BreakConnection();
}
}
}
/* ommitted lines */
public void PickUpObject() {
if (GameObject.FindGameObjectsWithTag("TargetObj").Length == 1 & lookObject.tag == "TargetObj") {
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
winUI.SetActive(true);
Time.timeScale = 0f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
Time.timeScale = 1f;
} else if (GameObject.FindGameObjectsWithTag("TargetObj").Length > 1) {
} else {
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
}
I added this line, to check if there is more than one object to look for.
else if (GameObject.FindGameObjectsWithTag("TargetObj").Length > 1)
How to implement this (if the player picked up all objects of tag "TargetObj", go to next level.)?
A fast way of doing this is to keep a counter of picked objects. Then, if the counter is equal with the number of objects with the "TargetObj" tag, then the player wins. As snippet you can have something like this:
Gameobject[] targetObjects; // an array where you will keep your objects with "TargetObj" tag
List<GameObject> targetObjectsList;
void Start()
{
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
targetObjectsList = new List<GameObject>();
}
.
.
.
// In your method (You didn't put all your code so I will use your snippet)
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (currentlyPickedUpObject == null)
{
//and we are looking an interactable object
if (lookObject != null )
{
PickUpObject();
// I suppose that "lookObject" is the gameobject that you want to pickup. If not, replase this variable with the right gameobject.
if(!targetObjectsList.Contains(lookObject.gameObject))
{
targetObjectsList.Add(lookObject.gameObject);
if (targetObjectsList.Count == targetObjects.Length)
{
//Finish the game
winUI.SetActive(true);
Time.timeScale = 0f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
Time.timeScale = 1f;
}
}
}
}
}
//if we press the pickup button and have something, we drop it
else
{
BreakConnection();
}
}
Then you modify your PickUpObject method, just to pick and drop objects.
I'm sorry if I miss something. I wrote this without an editor and I didn't test the code, so please tell me if is something that I miss.
Using Unity3d 2018.2.1 and C#
Problem: StartTimer not starting when user clicks screen, touch not being registered so timer never starts.
In FixedUpdate() im looking at when the user touches the screen to start a timer.
If the user hasn't touched the screen again and it has been 15 seconds then do something. If the user touches the screen before the 15 seconds, then restart the timer.
public float uiStartTime = 0f;
public float uiCurrentTime = 0f;
public float uiMaxTime = 15f;
private void FixedUpdate()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Ended)
{
uiStartTime = Time.time;
}
}
uiCurrentTime = Time.time;
if (uiStartTime - uiCurrentTime == uiMaxTime)
{
// Reset Timer
uiStartTime = 0f;
uiCurrentTime = 0f;
// Do Something
}
}
Problem: StartTimer not starting when user clicks screen, touch not
being registered so timer never starts.Im using a mouse to test but
will deploy for mobile
That's the main issue here. Of-course you still have some logical error in your code but timer shouldn't start since you're using mouse to test the Input.touchCount and Input.GetTouch API. These two API only work on the mobile device since they use touch instead of mouse.
If you want to use them in the Editor, use Unity Remote 5. Download it, on your mobile device, enable it in the Editor by going to Edit->Project Settings->Editor then connect your device to your computer. You should be able to use the touch API in your Editor without having to build it. See this post for more information about Unity Remote 5.
If you want the code to be more compatible with both mouse and touch then make a simple function that wraps around the touch and mouse API. You can then use the combination of UNITY_STANDALONE and UNITY_EDITOR preprocessor directives to detect when you are not running on a mobile platform.
Here is a simple mobile/desktop touch wrapper that works without Unity Remote 5:
bool ScreenTouched(out TouchPhase touchPhase)
{
#if UNITY_STANDALONE || UNITY_EDITOR
//DESKTOP COMPUTERS
if (Input.GetMouseButtonDown(0))
{
touchPhase = TouchPhase.Began;
return true;
}
if (Input.GetMouseButtonUp(0))
{
touchPhase = TouchPhase.Ended;
return true;
}
touchPhase = TouchPhase.Canceled;
return false;
#else
//MOBILE DEVICES
if (Input.touchCount > 0)
{
touchPhase = Input.GetTouch(0).phase;
return true;
}
touchPhase = TouchPhase.Canceled;
return false;
#endif
}
Below is how to use it to get what you want in your question:
public float uiStartTime = 0;
public float uiMaxTime = 15f;
private bool timerRunning = false;
private void Update()
{
//Increment timer if it's running
if (timerRunning)
uiStartTime += Time.deltaTime;
TouchPhase touchPhase;
if (ScreenTouched(out touchPhase))
{
//Check for keypres and start timer if it's not running
if (touchPhase == TouchPhase.Ended && !timerRunning)
{
timerRunning = true;
}
//If the user touches the screen before the 15 seconds, then restart the timer.
else if (touchPhase == TouchPhase.Ended && timerRunning && uiStartTime < uiMaxTime)
{
uiStartTime = 0f;
Debug.Log("Timer Reset");
}
}
//If the user hasn't touched the screen again and it has been 15 seconds then do something.
if (uiStartTime >= uiMaxTime)
{
// Do Something
Debug.Log("Timer not touched for 15 seconds");
}
}
Your equality will essentially never be true when using floats. uiStartime - uiCurrentTime is also giving you a negative number.
Instead do:
if (uiCurrentTime - uiStartTime >= uiMaxTime)
My goal is simple, like a lot of applications, I want to make my cursor invisible after a certain time of inactivity of my user.
My solution doesn't seem really optimized, here's the algorithm :
void Start {
Timer t = new Timer(5000);//Create a timer of 5 second
t.start();
}
void Update {
if(MouseMoved){
t.reset();
}
timeLeft = t.deltaTime;
if ( timeLeft == 5000 )
{
Cursor.visible = false;
}
}
I really don't like to check at every frame if the mouse is moved, I'd prefer that my mouse moved trigger something, but I'm lost here, is anyone have a better solution ?
This should be the good way to achieve the task, using coroutine, without any memory issue:
private Coroutine co_HideCursor;
void Update()
{
if (Input.GetAxis("Mouse X") == 0 && (Input.GetAxis("Mouse Y") == 0))
{
if (co_HideCursor == null)
{
co_HideCursor = StartCoroutine(HideCursor());
}
}
else
{
if (co_HideCursor != null)
{
StopCoroutine(co_HideCursor);
co_HideCursor = null;
Cursor.visible = true;
}
}
}
private IEnumerator HideCursor()
{
yield return new WaitForSeconds(3);
Cursor.visible = false;
}
There are two ways to check for things like this outside of the update loop, that I know of:
1 - Custom Events.
Writing your own custom event would allow you to call something like "OnMouseMove()" outside of the update function and it would only execute when the mouse cursor's position changes.
2 - Coroutines
Creating a separate coroutine would allow you to perform this check less frequently. To do this, you make an IEnumerator and put your mouse movement logic in there. Something like:
IEnumerator MouseMovement()
{
while(true)
{
if(MouseMoved)
{
//Do stuff
}
yield return new WaitForSeconds(1f);
}
}
That coroutine would perform the check once every second while it is running. You would start the coroutine by saying:
StartCoroutine(MouseMovement());
And to stop it, you call
StopCoroutine(MouseMovement());
If you Start it when the timer reaches 0 and stop it when the cursor is moved, you can also prevent the coroutine from running all the time, only using it while the cursor is inactive.
I have tried every which way possible to get this to work with no success. I have a swipe script and a game/control script. I am trying to reference the swipe control in my control script as the means to control my character. So far I am getting only one error from within Unity.
Assets/RuinRunStarterKit/Scripts/csTempleRun.cs(307,25): error CS1501: No overload for method UpdatePlayer' takes0' arguments
I've tried adding TouchDetector.enTouchType within the parenthesis of UpdatePlayer but that gives me three errors rather than just one. I've contacted numerous people and visited numerous sites looking for answers but have failed to yield a working result. I am not educated in programming. I have only researched online to figure out how to do something as far as programming goes. The second script I have here is from an Asset I purchased on the Unity Asset Store. I have contacted them for support for adding swipe controls and they emailed me the touch script but failed to tell me how to implement it. I just want to play this game on my phone with swipe as the controls. This is for personal use. I would really really appreciate it if someone could help me achieve the desired results. Again, I want to be able to control the character using swipe. Thank you. Here are my scripts:
TouchDetector.cs;
using UnityEngine;
using System.Collections;
public class TouchDetector : MonoBehaviour {
public delegate void deTouchEvent
(enTouchType touchType);
public static event
deTouchEvent
evTouchEvent;
public enum enTouchType
{
SwipeLeft,
SwipeRight,
SwipeDown,
SwipeUp,
}
void Start ()
{
}
void Update ()
{
if (evTouchEvent == null)
return;
if (Input.GetKeyDown(KeyCode.UpArrow )) evTouchEvent(enTouchType.SwipeUp );
if (Input.GetKeyDown(KeyCode.DownArrow )) evTouchEvent(enTouchType.SwipeDown );
if (Input.GetKeyDown(KeyCode.LeftArrow )) evTouchEvent(enTouchType.SwipeLeft );
if (Input.GetKeyDown(KeyCode.RightArrow)) evTouchEvent(enTouchType.SwipeRight);
if (Input.touchCount > 0)
{
foreach (Touch t in Input.touches)
{
Vector3 swipe = t.deltaPosition * t.deltaTime;
if (swipe.y > 0.5f) evTouchEvent(enTouchType.SwipeUp );
if (swipe.y < -0.5f) evTouchEvent(enTouchType.SwipeDown );
if (swipe.x > 0.5f) evTouchEvent(enTouchType.SwipeRight);
if (swipe.x < -0.5f) evTouchEvent(enTouchType.SwipeLeft );
}
}
}
}
csTempleRun.cs;
void Update ()
{
UpdatePlayer();
if (m_player != null)
{
// Make the camera follow the player (if active)
SmoothFollow sF = (SmoothFollow)Camera.mainCamera.GetComponent(typeof(SmoothFollow));
sF.target = m_player.transform;
// Check for collisions with interactive objects
UpdateCoins();
UpdateMines();
// Dynamically update the track
CreateNewCellsIfNeeded(false);
}
}
private void UpdatePlayer(TouchDetector.enTouchType T)
{
// if the player is dead (replaced with ragdoll) then exit since none of this code should fire.
if (m_player == null)
{
return;
}
// Gradually increase the players' running speed, and update the animation to match.
m_playerRunSpeed += Time.deltaTime * 0.005f;
m_playerRunSpeed = Mathf.Clamp(m_playerRunSpeed, 0.5f, 3.0f);
m_player.animation["run"].speed = m_playerRunSpeed * 2.0f;
// ****************************************************************************************
// INPUT
// Player can only turn if they are not already sliding / jumping.
// Equally, sliding / jumping are mutually exclusive.
if (Input.GetKeyDown (KeyCode.LeftArrow)&& m_playerJump <= -1.0f && m_playerSlide <=0.0f)
{
if (m_playerDirection == enCellDir.North) m_playerNextDirection = enCellDir.West;
if (m_playerDirection == enCellDir.East ) m_playerNextDirection = enCellDir.North;
if (m_playerDirection == enCellDir.South) m_playerNextDirection = enCellDir.East;
if (m_playerDirection == enCellDir.West ) m_playerNextDirection = enCellDir.South;
}
if (Input.GetKeyDown(KeyCode.RightArrow) && m_playerJump <= -1.0f && m_playerSlide <=0.0f)
{
if (m_playerDirection == enCellDir.North) m_playerNextDirection = enCellDir.East;
if (m_playerDirection == enCellDir.East ) m_playerNextDirection = enCellDir.South;
if (m_playerDirection == enCellDir.South) m_playerNextDirection = enCellDir.West;
if (m_playerDirection == enCellDir.West ) m_playerNextDirection = enCellDir.North;
}
if (T==TouchDetector.enTouchType.SwipeDown && m_playerJump <= -1.0f && m_playerSlide <=0.0f)
{
m_playerSlide = 1.0f;
m_player.animation.Play("slide_fake");
}
if ((Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.Space)) && m_playerJump <= -1.0f && m_playerSlide <=0.0f)
{
AudioSource.PlayClipAtPoint(m_jumpAudio, m_player.transform.position);
m_playerJump = 1.0f;
m_playerYvel = 0.0f;
m_player.animation.Play("jump");
}
I hope I'm not doing anything wrong by posting this script but I feel I need to post this in order to get the help I need. You'll notice in the csTempleRun.cs script that I replaced one of the KeyCode calls with TouchDetector.enTouchType.SwipeDown. Yet I am still getting an error. Thank you in advance for anyone's help. Thank you for your time as well.
Look at your error - No overload for method UpdatePlayer' takes0' arguments. It means that you need to supply an additional data to your method. Which data you need to supply intellisense can tell you.
In your code:
void Update ()
{
UpdatePlayer();
...
You call UpdatePlayer() with zero arguments. However, UpdatePlayer needs an argument:
private void UpdatePlayer(TouchDetector.enTouchType T)
{
So, when you call UpdatePlayer(), you need to send an object of the type TouchDetector.enTouchType as parameter. That would be one of the following:
SwipeLeft
SwipeRight
SwipeDown
SwipeUp