Keyboard input works on some occasions but not others - c#

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

Related

Unity Input controller about new Hand Controller

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

Letting The Character Traverse Between Art Canvases When There Are Intersecting Sides in Unity2D

I am creating a video game where the character has to travel within literal art canvases (the ones that you use for painting) to reach the end goal.
Note that the "canvas" I am referring is not the UI element, but the actual canvases you would see in real life.
I based my code off the concepts of portals. This may not be the most efficient way of dealing with it and I will consider all advices.
This is my current code:
public Transform PortalB;
public CharacterController2D Player;
public GameObject PlayerObject;
private GameObject CloneTemporary;
public Queue<GameObject> Clones = new Queue<GameObject>();
private bool isCreated = false;
// Use this for initialization
void Start () {
Clones.Enqueue(PlayerObject);
}
// Update is called once per frame
void Update () {
// Portal adapts to the player's current position
if (Player == null) {
Player = GameObject.FindWithTag("Player").GetComponent<CharacterController2D>();
PlayerObject = GameObject.FindWithTag("Player");
}
}
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Player") {
if (Vector2.Distance(transform.position, other.transform.position) > 0.7f) {
if (!isCreated) {
CloneTemporary = Instantiate(PlayerObject, new Vector2 (PortalB.position.x, PortalB.position.y), Quaternion.identity) as GameObject;
Clones.Enqueue(CloneTemporary);
isCreated = true;
}
}
}
}
void OnTriggerExit2D (Collider2D other) {
if (Vector2.Distance(transform.position, other.transform.position) > 0.7f) {
print(Clones.Count);
if (Clones.Count > 1) {
UnityEngine.Object.Destroy(Clones.Dequeue());
}
}
isCreated = false;
}
The "original character" will collide with the portal's box collider and create a copy on the other end of the portal. The box collider is annotated in red in the first image below. Note that the sizes are exactly the same.
Note that it technically is not a portal, but since it's a box which brings a character from one place to another, I might as well call it a portal.
Once the original leaves the box collider, it will get deleted, and the clone will then become the "original".
I am using a queue system to determine which "clone" gets deleted first.
There are a few problems with this:
It is very inefficient. I have to create portals manually for every intersection point in the canvases. Imagine a level full of canvases, and imagine a game with full of levels...
When it touches the portal, a duplicate will get spawned by the original character. There are three characters, but the Clones.Count only registers two.
I am not sure how well the code will work for vertical traverses.
When the character crosses the portal, it should be able to turn back. In this code, the character would glitch through the floor if he were to turn back and not get deleted by the OnExit function. I suspect this has something to do with the size of the portal, but I can foresee that even if it were to be bigger, the character would immediately disappear if he turns back.
I think the OnTriggerEnter function gets activated when the character is teleported on the other side. This might have an effect on the errors I just stated, but it may cause more in the near future.

Jumping onto a platform

I am making a 2D platform game in Untiy for android and i am having some issues with a section of code i have. When i jump onto a platform the first time i can land onto the platform but when i jump again i fall through the platform. i have it so the box collider is inactive if the player is less then the height of the platform and active when the player is higher then the platform. I thought the box collider was to small and it was just missing the collider so i have tried different sizes of colliders and i have tried adjusting different heights at which it activates. Also when i set the height to low the player does a double jump. So what am i doing wrong?
public class Rock : MonoBehaviour
{
private BoxCollider2D platform;
private PlayerScript player;
public float height;
void Awake() {
player = GameObject.Find("Player").GetComponent<PlayerScript>();
platform = GetComponent<BoxCollider2D>();
}
void Start () {
platform.enabled = false;
}
// Update is called once per frame
void Update () {
if(player.transform.position.y > height){
platform.enabled = true;
} else if(player.transform.position.y < height){
platform.enabled = false;
}
}
}
Could it be that you're not covering the case where player.transform.position.y == height?
I can see that you're checking for greater / smaller than height but not equality. This could lead to unwanted behavior like the one you're describing.
Let me know if this was the problem.
This code actually worked. The issue ended up being in my playerscript where I had added a chunk of code where I was having a bug if you held the jump button down the player would get stuck in the jump animation.
void OnCollisionStay2D(Collision2D target) {
if (target.gameObject.tag == "Ground") {
grounded = true;
anim.SetBool("Jump",false);
}
}
public void Jump () {
if(grounded){
grounded = false;
anim.SetBool("Jump",true);
myBody.velocity = new Vector2(myBody.velocity.x, 0);
myBody.AddForce( new Vector2(0, jumpForce));
}
}
After some troubleshooting and going and removing pieces of code trying to see why I doubled jump I finally came across this piece then when I was trying with the touch controls instead of with the keyboard I noticed I am not actually able to hold the jump button down like you can with a keyboard so I didn't really need this piece of code. So I was my own worst enemy this time.

Using ConstantForce to simulate wind

I'm trying to create a simple platformer. I have triggers set around where if the player collides, ConstantForce is enabled on the player, and the player is pushed in a chosen direction (depending on the trigger). This works relatively well with pushing the player left and right, but where I'm having an issue is when the player is pushed up on the Y axis. The player hovers for a bit but each time he drops, he begins to go lower and lower- eventually exiting the trigger. I want him to maintain a certain height in air unless he Exits the trigger. Increasing ConstantForce only makes him fly higher, decreasing his mass has a similar effect.
private ConstantForce getConstantForce;
public GameObject findingThePlayer;
public Vector3 forceDirection = new Vector3();
void Start()
{
//Getting the ConstantForce component from player
findingThePlayer = GameObject.Find("pig_test");
getConstantForce = findingThePlayer.GetComponent<ConstantForce>();
}
void OnTriggerStay()
{
//turning on the players ConstantForce
getConstantForce.enabled = true;
//we set the vector value in editor, depending which way we want the wind to blow
getConstantForce.constantForce.force = forceDirection;
}
void OnTriggerExit()
{
//When player exits trigger, ConstantForce is disabled
getConstantForce.enabled = false;
}
}
Hacky way
void OnTriggerStay()
{
//turning on the players ConstantForce
getConstantForce.enabled = true;
//we set the vector value in editor, depending which way we want the wind to blow
getConstantForce.constantForce.force = forceDirection;
//Once you reach the height you want, set the body to be kinematic,
// that way forces will not longer have any effect
findingThePlayer.rigidbody.isKinematic = true;
}
void OnTriggerExit()
{
//When player exits trigger, ConstantForce is disabled
getConstantForce.enabled = false;
//And lets make it a physics rigidbody again on exit
findingThePlayer.rigidbody.isKinematic = false;
}
Another Method is to play with the gravity values until it feels right. You can do this by going into Edit -> Project Settings -> Physics

Moving Platform works most of the time, but occasionally falls through and/or is jittery

I'm writing a 2D game and I'm trying to get moving platforms to work. After doing some previous investigation, I have it ALMOST working. The idea is to have 2 platform objects with colliders: 1 a visible object, the other an invisible object with isTrigger set (since the player would just go through a trigger). The code for the Moving Platform child (the trigger one) is set here.
using UnityEngine;
using System.Collections;
public class MovingPlatformChild : MonoBehaviour
{
public string parentPlatform = "";
void Start ()
{
transform.parent = GameObject.Find(parentPlatform).transform;
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerEnter(Collider playerObject)
{
Debug.Log ("enter moving platform");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent = gameObject.transform;
}
}
int i = 0;
void OnTriggerStay(Collider playerObject)
{
Debug.Log ("stay" + i++);
if(playerObject.transform.position.y >= transform.position.y)
{
playerObject.transform.parent = gameObject.transform;
}
else
{
playerObject.transform.parent=null;
}
}
void OnTriggerExit(Collider playerObject)
{
Debug.Log ("EXIT");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent=null;
}
}
}
The Start() function just makes it a child of the visible platform. This can probably be done right in the Unity editor as well, instead of through code.
The OnTriggerEnter function adds the player object as a child of the trigger platform object, which is a child of the visible platform. So they should all move together.
The OnTriggerStay is an attempt to verify that this remains true only while the player is on the top of the platform. While the player is within the trigger, if the player is on top of the platform, then it remains attached. Otherwise, it's not. This is so that nothing happens on the bottom end.
The OnTriggerExit function just removes the player object as a child when it exits the trigger.
This is somewhat working (but we know somewhat isn't good enough). It works sometimes, but the player will be very jittery. Also, on the way down while standing atop the platform, the TriggerStay function doesn't appear to be called (implying the player is no longer within the trigger). This is observed through my Debug "stay" statement. Finally, sometimes the player will also fall straight through the platform.
What in this code would allow the player to fall through the platform, or be so jittery on the way up? Am I missing something crucial? If you need any more code, please let me know.
Below is the code for the movement of the non-trigger platform (the parent of the trigger platform and in an identical position). I will also share the Player's Update function after that.
void Start ()
{
origY = transform.position.y;
useSpeed = -directionSpeed;
}
// Update is called once per frame
void Update ()
{
if(origY - transform.position.y > distance)
{
useSpeed = directionSpeed; //flip direction
}
else if(origY - transform.position.y < -distance)
{
useSpeed = -directionSpeed; //flip direction
}
transform.Translate(0,useSpeed*Time.deltaTime,0);
}
And now the player code:
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
float rotation = Input.GetAxis("Horizontal");
if(controller.isGrounded)
{
moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0);
moveDirection = transform.TransformDirection(moveDirection);
//running code
if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
{ running = true; }
else
{ running = false; }
moveDirection *= running ? runningSpeed : walkingSpeed; //set speed
//jump code
if(Input.GetButtonDown("Jump"))
{
//moveDirection.y = jumpHeight;
jump ();
}
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
EDIT: I've added the specifications for the platforms and player in this imgur album:
http://imgur.com/a/IxgyS
This largely depends on the height of your trigger box, but it's worth looking into. Within your TriggerStay, you've got an IF statement concerning the player y coordinates. If the trigger box is fairly large and the platform's speed fast enough, on the way up and between update ticks the player Y coords could momentarily be smaller than the trigger Y coords. This would lead to him losing the parentage, only to regain it a few ticks later. This might be the cause of the 'jittering'.
The problem I was having included
The moving platform was written using Translate. I rewrote it using a rigidbody and the rigidbody.Move function. This didn't immediately help, but...
I realized the CharacterMotor script (Unity provides this) that I had attached to the player included moving platform support. I set the MovementTransfer value to PermaLocked, and also unchecked the "Use FixedUpdate" box on the script, and it now works 99% of the time. I've had one time where I did a particular behaviour and slipped through, but I can't recreate it.
Hope this helps anyone who might be looking for an answer!

Categories

Resources