I have build a driver simulator using Unity and I use as steering wheel the Logitech G29 controller.
So in my project to break and throttle I configured this:
Vertical1 is used to Throttle function and Vertical2 is used to Break function. This configuration are working now.
Now I need to configure also another controller (HC1 3DRap). This is an Hand Controller. So I checked it on windows device and I can see this:
Rotation Axis X and Rotation Axis Y have a value in sleep mode (without press the two levels).
Now I need to integrate also this new Controller in my project. So I try to make this:
In this mode if I try to check value of Y axis value with the follow code ( in this moment I cannot press the levers) :
Debug.Log("Input debug frenata: " + Input.GetAxis("Vertical2"));
I can display this:
If I try to press a levers, I can display this values
In this mode with thie new controller join on the system I m not able to run the car, because I think that there is every time the break pressed.
Could you suggest me, how can I fixed this bug ?
I run into a similar problem some time ago. I found out, that the axis I was actually using was not the one I expected.
Let's say you have a joystick and have a separate "POV-Stick" on it. When you use the POV-Stick you might be moving the whole joystick and therefore change the main axis of the joystick. If you are just watching the main axis input, it looks like that is the input of your POV-Stick, but actually isn't. So make sure the input you read is the correct one.
Then you have another problem: Not every joystick, steer etc. is mapping it's inputs to the same axis. So if you buy 2 more devices, they might be on a different axis as well. If you try to handle that on your own, you go crazy.
There is a unity forum about that topic (and other related problems).
And I found that there are some unity plugins, that could probably solve your problem:
https://github.com/speps/XInputDotNet
https://github.com/JISyed/Unity-XboxCtrlrInput
I hope you can solve your problem with these inputs (please let us know, if you do).
It seems that since you're seeing values even when the input device is not being used, that you'll need to "zero" the device and use dead zones like you might for a joystick or controller.
In the Player or Input script, in OnEnable/Awake/Start (whichever works best for you), set the "zero" value for the device in a field. You can run the assignment of the Vertical2 zero value in a method, and then allow the player to recalibrate during play with a button that invokes that function.:
private float _vertical2Zero = 0.0f;
void Start()
{
this.CalibrateVertical2Zero();
// ... more code ...
}
private void CalibrateVertical2Zero()
{
this._vertical2Zero = Input.GetAxis("Vertical2")
{
Then, when you're checking the value later, test it against the "zero" value, and apply some deadzones if desired:
private float _vertical2Deadzone = 0.05f;
void HandleInput()
{
float newVertical2Value = Input.GetAxis("Vertical2");
bool vertical2Low = newVertical2Value <= ( this._vertical2Zero - _vertical2Deadzone );
bool vertical2High = newVertical2Value >= ( this._vertical2Zero + _vertical2Deadzone );
if( vertical2Low || vertical2High )
{
// Input detected on Vertical2, accounting for the zero and deadzone
}
}
This can help with your problem.
How to manage input in Unity
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// Spacebar was pressed
}
if (Input.GetMouseButtonDown(0))
{
// Left mouse was pressed
}
}
The new Input System
using UnityEngine;
using UnityEngine.InputSystem;
public class ReportMousePosition : MonoBehaviour
{
void Update()
{
Vector2 mousePosition = Mouse.current.position.ReadValue();
if(Keyboard.current.anyKey.wasPressedThisFrame)
{
Debug.Log("A key was pressed");
}
if (Gamepad.current.aButton.wasPressedThisFrame)
{
Debug.Log("A button was pressed");
}
}
}
Hope it helped
Related
So, I've been trying to learn Unity these past couple of weeks. As a starter project I decided to try and replicate the mobile game Pop the Lock. Right now, I'm having some problems with my keyboard inputs that I just can't figure out how to solve.
This is how my game screen looks: GameScreen. The red bar is the Player and the yellow circle is the Target. As the game progresses, the Player rotates around the ring towards the Target.
Basically, if the Player and the Target are touching AND the Player presses the Space Key at the exact same time, the Player is supposed to gain a point. The Target is also supposed to be destroyed, and a new Target is supposed to randomly be spawned in somewhere on the game screen. At the moment, this system works ... most of the time. About 80% of the time my code operates as it should, but around 20% of the time my code doesn't register when player presses the space key as the two collide. Here's my code:
public class Target: MonoBehaviour {
public GameObject target;
void Update () {
if (Input.GetKeyDown("space")) {
Debug.Log("SPACE PRESSED!!");
}
}
private void OnTriggerEnter2D (Collider2D collision) {
Debug.Log("Collision!");
}
private void OnTriggerStay2D(Collider2D other) {
// This is the part that sometimes isn't registering:
if (Input.GetKeyDown("space")) {
Debug.Log("HIT!!");
Score.score++;
// Code to spawn new Target on random place in the ring:
// Seems to be working as intended:
float distance = 2.034822f;
float x = Random.Range(-2f, 2f);
float y = Mathf.Pow(distance,2) - Mathf.Pow(x,2);
y = Mathf.Sqrt(y);
float[] options = {y, -y};
int randomIndex = Random.Range(0, 2);
y = options[randomIndex];
Vector3 vector = new Vector3(x, y, 0);
GameObject newTarget = Instantiate(target, vector, Quaternion.identity);
Destroy(gameObject);
}
}
}
As you can see I have Log statements that print something every time the player and the target are touching, every time the space key is pressed, and every time the space key is pressed while they are touching. This is how the console looks like when everything is working : Image One. This is what the console looks like when my code isn't working : Image Two.
So even when it isn't working, the collision and the key press are still registered at the exact same time. But for some reason the hit itself isn't registered (so the if condition isn't passed). Because of this I'm quite confident that it's not just input delay or me pressing the key at the wrong time. As I mentioned above, this only happens about 20% of the time, which makes it even more confusing to me. The Target has a trigger collider2D and it also has a dynamic RigidBody2D with gravity scale set to 0 (as I was told it should). Any help would be greatly appreciated.
(How my collider and rigidbody look: Image)
Something you can do is to set a flag to becomes true while you are pressing the key in the update loop, so the update loop will convert to something like:
private bool isSpacePressed = false;
update() {
isSpacePressed = false;
if (Input.GetKeyDown(KeyCode.Space)){
isSpacePressed = true;
}
}
so every loop the flag will be set to false except if you are pressing the space bar and the OnTriggerStay2D while become something like
OnTriggerStay2D () {
if(isSpacePressed){
.. do magic..
}
}
Look that I replace the Input.GetKeyDown('space') to Input.GetKeyDown(KeyCode.Space) I recommend using this one to avoid typing errors
I'm just starting out please excuse vast ignorance.
I'm writing a c# script in unity as part of the essentials training. I'm doing the 3d audio module and I thought I'd try and get a little bit fancier than the scope of this particular lesson which is supposed to be having an object fly through a window in a pre-built scene and make a 3d sound as it moves.
I wanted to make the movement of the object conditional upon a player moving close to it in 3d space. I figured out how to trigger the movement of an object in a script with an if statement that changes the transform parameters of the object the script is attached to when a 'distanceFromObject' variable is < 2. It works, however the script runs in the update section of the script which runs once every frame. This means that the object's transform parameters are changed every frame as expected but of course stops doing so when the distance between the object that's moving and the player exceeds 2.
I see the mistake I've made because if the object moves away when the player gets close then it will inevitably eventually move far enough away that the distanceFromObject variable will grow bigger than 2 whereupon it stops and just hovers in place. I don't know how to fix it though.
I need the script to check the distance between the object and the player every frame so that it will trigger the instance the player gets close enough, and when they get close enough, I need the object to move away, however once it has been triggered to move, I need the object to continue moving, but the script to stop checking what the distance is anymore.
The script looks like this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyOff : MonoBehaviour
{
public Vector3 rotateChange;
public Vector3 positionChange;
public float distanceFromObject;
public GameObject character;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
Use flags instead of writing your logic in the if statement :
public class FlyOff : MonoBehaviour
{
// fields removed for more readability
// use a flag that's set to true/false
private bool isCloseEnough = false;
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
// set the flag to true when player is close enough
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
// even if the player gets far, the flag will remain true
if (isCloseEnough)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
You can even apply the opposite logic to stop the object to move away when it has reach a certain distance :
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
else if (distanceFromObject > SomeValue)
{
isCloseEnough = false;
}
If I understand correctly you could just add a bool flag and set it once you are close enough. Then you can start moving and skip further distance checks but keep moving forever.
private bool flyAway;
void Update()
{
if(!flyAway)
{
distanceFromObject = Vector3.Distance(character.transform.position, transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
flyAway = true;
}
}
else
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
In general: Avoid using print every frame! Even if you user doesn't see the log in a built app it is still causing overhead!
I want to make a door which opens and closes by pressing a button ( "O" for open and "C" for close). Tried with this:
using System.Collections;
using UnityEngine;
public class DoorScript : MonoBehaviour {
public float speed;
public float angle;
public Vector3 direction;
// use this for initializaton
void start() {
angle = transform.eulerAngles.y;
}
// Update is called once per frame
void Update() {
if (Mathf.Round(transform.eulerAngles.y) != angle) {
//rotate our door
transform.Rotate(direction * speed);
}
if (Input.GetKeyDown(KeyCode.O)) {
angle = 90;
direction = Vector3.up;
}
if (Input.GetKeyDown(KeyCode.C)) {
angle = 0;
direction = -Vector3.up;
}
}
}
But it did not works how I want. I mean: You can be far away, if you press "O" the door will open anyway. How can I implement an action range? I mean, you will need to be a little bit closer to the door interact with it?
I also need to say that in the game will me aprox. 40 doors. I need for each other a custom script?
The code is in C#. I have a little bit of coding experience, but i canĀ“t solve this.
Thanks
One way to do this is to use Colliders, RigidBodies and OnTrigger calls. Here is a manual page that outlines the usage.
https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
For your implementation, the door would have the Example script (call it something other than "Example") and the player would be the sphere (although you would create the player outside of this script). When the player is near a door, the door will know it by keeping track of the presence of the player through the OnTrigger calls. Then, when the key is pressed, the door will react if the user is nearby. Every door would have a script like this.
What breaks is the scrubbing slider, it recognises a click, but it does not move. I have interactable enable.
I'm having to reimport all assets to get it to work again but even now this isn't a sure bet it'll fix it.
This code was working perfectly fine about a week ago, I've made little changes to it since.
A lot of it is commented so I can learn that different parts of the code. Feel free to delete it if it bothers you.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider scrubSlider; //This is the slider that controls the current position of the animation. Values have to be 0 to 1 for normalized time of the animation, called later in the script
public Slider speedSlider; //This is the slider that controls the playback speed of the animation
public Animator animator; //Animator that is attached to the model that we are looking to control the animation of
public float playbackSpeedAdjustment = 0.5f; //This is a variable that can be easily adjusted to change the total playback speed of the animation. If it's too fast make smaller and vice versa
public Text currentDateText;
public int monthsInProject;
private float rememberTheSpeedBecauseWeMightNeedIt; //Float needed to keep the speed of the animation between pausing and playing
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime; //float a new value animationTime that is equal to the animators animation state then converted to normalized time
//Debug.Log("animationTime (normalized) is " + animationTime); //logging the normalized time to be able to store it for the scrubbing slider. Doesn't need to be logged for this to work, good to log it to make sure that it's working
scrubSlider.value = animationTime; //making the slider value equal to the now logged normalized time of the animation state
}
public void ScrubSliderChanged(float ScrubSliderchangedValue) // this value has to be floated so that the scrubbing slider can be attached to in the inspector to be able to change the current frame of the animation
{
animator.Play("Take 001", -1, scrubSlider.normalizedValue);
}
public void SpeedSliderChanged(float SpeedSliderchangedValue) //value is floated to be able to change the speed of the animation playback
{
animator.speed = speedSlider.normalizedValue * playbackSpeedAdjustment; // here the speed is multiplied by the adjustment value set in the editor just in case the playback speed needs to be changed outside of normalized values
}
public void UserClickedPauseButton()
{
if (animator.speed > 0f)
{
// we need to pause animator.speed
rememberTheSpeedBecauseWeMightNeedIt = animator.speed;
animator.speed = 0f;
}
else
{
// we need to "unpause"
animator.speed = rememberTheSpeedBecauseWeMightNeedIt;
}
}
public void UserClickedBackButton()
{
scrubSlider.value = scrubSlider.value - (1f / monthsInProject);
}
public void UserClickedForwardButton()
{
scrubSlider.value = scrubSlider.value + (1f / monthsInProject);
}
public void UserClickedStartButton()
{
scrubSlider.value = 0;
}
public void UserClickedEndButton()
{
scrubSlider.value = 1;
}
}
Many thanks for all your help.
I'm pretty sure the problem is this. In Update you are doing something like this:
scrubSlider.value = animationTime
that means that every frame, "NO MATTER WHAT", YOU ARE SETTING THE SLIDER POSITION, to, where the animation is. If the user is trying to move the slider - you are moving it right back, that same frame!
It's not so easy to solve this problem. Unity did not include a trivial built-in function for this. You need a separate script which sits on the slider which determines whether or not the handle is being held down. You have to use the OnPointerDown and OnPointerUp handlers.
How to use pointer handlers in modern Unity:
Step one, make this small script called Teste.cs
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class Teste:MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public Slider theSlider;
public bool sliderIsBeingHeldDown;
public void OnPointerDown(PointerEventData data)
{
sliderIsBeingHeldDown = true;
Debug.Log("holding down!");
}
public void OnPointerUp(PointerEventData data)
{
sliderIsBeingHeldDown = false;
Debug.Log("holding up!");
}
}
Don't forget the declaration...
Teste:MonoBehaviour, IPointerDownHandler, IPointerUpHandler
rather than the usual MonoBehavior.
Drag that script on to your UI.Slider.
Note that in general these only work when they are "on" the thing in question.
Run, and try clicking up and down on the slider button - check the console. It works right? Now you have to use that variable inside NewAnimController
(1) Add a public variable to NewAnimController,
public Teste teste;
(2) Be sure to drag to connect that variable in Inspector
(3) Now you can refer to teste.sliderIsBeingHeldDown to see if that slider is being held down. So instead of doing this every frame...
scrubSlider.value = animationTime;
you will have to do this every frame...
if (teste.sliderIsBeingHeldDown)
Debug.Log("don't try to work against the user!");
else
scrubSlider.value = animationTime;
I hope that works! I think that's the simplest way to know when the slider is being held-down.
You really chose a difficult project for your first project! Hell!
It's worth noting that you could also put the slider-adjusting code (I mean to say your actual code for the scrubber) "inside" the script that is actually on the slider -- but I wouldn't worry about that for now. Your solution as stands is good.
Note depending on how you implement it, you may need to make it pause more elegantly when you hold down the slider handle, but before moving the slider handle. To achieve this, one approach is arrange to call UserClickedPauseButton() or a similar concept, when, the Down/Up routines shown here in "Teste", are called. Conversely you could just not bother with ScrubSliderChanged, and instead do the whole job inside code that runs whenever the handle is "down".
(In general, you'd almost certainly use UnityEvent here to make a solid reusable solution for a slider such as this for use with something like a scrubber.)
I've been working on a Breakout-style project recently and wanted to make it so that once a user enters the 'cheat mode', they'll be able to move the ball in their desired direction.
Here's what my code looks like: https://gist.github.com/anonymous/7589f4f141888c3c32c7
I've included the return; in my ball class due to the fact that if I don't, the program will not recognise the changes in the X and Y. So basically, my problem is, I want the program to ignore the existing x and y speeds and use the ones provided by the user if the cheat mode is enabled. (Meaning if the user enters the cheat mode, the program should stop the ball where it last was, in terms of x and y coordinates, and let the user move it)
Example of what I mean:
Player enters cheat mode -> Presses Up.
- Ball keeps moving up until it hits the top of the form and then should bounce back in the opposite direction to which it hit the wall.
How do I implement this into my program? I used the 'if statement' for the boolean cheat mode and the return function to exit the moveBall procedure (which ignores the xSpeed and ySpeed but ultimately stops the ball from moving on it's own completely, so the player has to keep clicking Up to move the ball up, otherwise it stays in place.
EDIT:
Okay, I've fixed the issue where the ball wouldn't move, I just added a few more variables for the X and Y change of the ball.
Here's what my code looks like now:
public void moveBall()
{
if (Form1.cheatModeClicked == true)
{
ballRec.X += cheatX;
ballRec.Y -= cheatY;
}
else
{
ballRec.X += xSpeed;
ballRec.Y -= ySpeed;
}
}
However, the ball collision still doesn't want to work, and I don't know why?
It sounds like you need to add a method to the ball class to allow the ball's speed to be set externally. Then you can call that method when keys are pressed, depending on the cheat mode state. The cheat mode state should be stored outside the ball class anyway. The ball itself should only know about ball things and not what a non-ball concept such as cheat mode is.
Here is some pseudo code, I might have missed a few things in the description but hopefully it helps
class Form
{
bool _inCheatMode = false;
Ball ball;
void EnterCheatMode()
{
_inCheatMode = true;
ball.SetSpeed(0, 0); //stop the ball
}
void ExitCheatMode()
{
_inCheatMode = false;
}
void KeyDown(Keys key)
{
if (_inCheatMode)
{
if (key == Keys.Up)
ball.SetSpeed(0, -1);
}
}
}
class Ball
{
public void SetSpeed(x, y)
{
}
}