Why won't Unity SetActive execute within Input.GetButtonDown()? - c#

I'm completely stumped by this one. I have a book in game and when you are looking in its general direction while within a certain distance of it and press the Action button (e or left mouse click), it's supposed to display a UI panel (bookPage). Here's my code for it.
void Update () {
Vector3 direction = player.transform.position - this.transform.position;
angle = Vector3.Angle (direction, player.transform.forward);
distance = direction.magnitude;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf) {
if(Input.GetButtonDown("Action")) {
bookPage.SetActive (true);
}
}
if (bookPage.activeSelf && Input.GetButtonDown("Action")) {
bookPage.SetActive (false);
}
}
This doesn't work. Specifically, the line to set the page to active doesn't work. If it's open, it will close correctly. If I copy and paste bookPage.SetActive (true);
anywhere within this method besides within if(Input.getButtonDown("Action")){}, it will make the bookPage active. If I put Debug.Log("message"); within that if statement though, it will show up in the console. It's just the one line that sets bookPage to active that isn't working. I have no idea why. I've tried moving it to a different method then calling it and doing the same but using a Coroutine. Neither worked. I also tried using other keys which did work, but I need it to work with the action key. It's also worth noting that the action key works in other usage. I already use it to open doors. Has anyone else run into a problem like this?

When you call SetActive(true), activeSelf will return true for your second condition which will invoke SetActive(false) immediately after.
Change this;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf)
{
if (Input.GetButtonDown("Action"))
{
bookPage.SetActive(true);
}
}
if (bookPage.activeSelf && Input.GetButtonDown("Action"))
{
bookPage.SetActive(false);
}
To this;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf)
{
if (Input.GetButtonDown("Action"))
{
bookPage.SetActive(true);
}
}
else if (bookPage.activeSelf && Input.GetButtonDown("Action"))
{
bookPage.SetActive(false);
}

Related

(Unity, 2D, C#) How can I make the player let go of a button before needing to press it again?

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!

if statement not exiting properly C# and Unity

I am trying to get my 'player' to change directions with my below script
however, it is contently staying stuck in the first 2 if statements of my 'void Update()'
I am trying to use these two scripts (1. https://pastebin.com/AGLatvUD (I wrote this one) and 2. https://pastebin.com/2XA3w04w)
I am attempting to use CharacterController2D to move my player with specified points and actions
Don't know if this is the right place to ask but I figured I'd try!!
void Update() // not sure if should have more in 'FixedUpdate' or others (maybe?)
{
if (isRight && transform.position.x > currentPoint.position.x) // flipping the character -- I'm pretty sure I can use TestCharacterController2D to do this for me, this is comparing the player's 'transform'
{
moveSpeed = -0.25f; // tells controller to head in the left direction
isRight = false; // no longer facing right
}
if (!isRight && transform.position.x < currentPoint.position.x) // reverse of above
{
moveSpeed = -0.25f; // tells controller to head in the right direction
isRight = true; // no longer facing left
}
if (transform.position == currentPoint.position) // checks to see if player is at 'currentPoint'
{
pause = true; // starts the pause sequenece
if (pause) // when the movement is pause do the the following
{
animator.SetFloat("Speed", 0); // player stops moving -- works!
if (maxPause <= 100) // checks to see if still paused
{
Debug.Log(maxPause);
maxPause--; // reduce pause amount (working way out of loop)
if (maxPause < 0) // found 'maxPause' was going to far below zero
maxPause = 0;
}
if (maxPause == 0) // when 'maxPause' timer has finished
{
pointsSelection++; // move to netx point
maxPause = 100; // reset 'maxPause' timer
pause = false; // resume 'transform.position == currentPoint.position' process
}
}
if (pointsSelection == points.Length) // makes sure 'pointsSelection' doesn't go out of bounds
pointsSelection = 0; // start the player's movement process over again
}
else // not sure if requried
Debug.Log("removed pause = false");
any help would be appreciated!!!
Thank you very much!!
I am an amateur (obviously :))
littlejiver
There's nothing that sets the character's position exactly to currentPoint. There are a number of ways to solve this. One way is to check if the character is near the currentPoint instead of exactly on it.
if (Vector2.Distance(transform.position, currentPoint.position) < 0.1f) {
pause = true; // starts the pause sequenece
...

Reloading Unity Scene doesn't work

I'm using this code to fade in a CanvasGroup at the beginning of my scene loading, and then if the player loses I want to fade out the scene and reload it from the beginning.
void Update () {
if (Time.time < 1 && fade.alpha >= 0)
fade.alpha = .99f - Time.time;
if (fadeOut)
fade.alpha += .02f;
if (fade.alpha >= 1)
{
Debug.Log("Change scene");
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
The problem is that when it hits this it shows the gamescene twice in the hierarchy, and neither one on the screen. I'll upload a picture below. When it does this is also hits the Debug.Log repeatedly. Does this not work if it's the only scene in the project? Only thing I can think of.
I've updated the method to include a boolean to ensure it SHOULD only run one time. Somehow it is still running countless times until I end the unity editor. The new update method is this :
void Update () {
if (Time.time < 1 && fade.alpha >= 0)
fade.alpha = .99f - Time.time;
if (fadeOut)
fade.alpha += .02f;
if (fade.alpha >= 1 && !reloadStarted)
{
Debug.Log("Change scene");
reloadStarted = true;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
I guess Update is executed more rapidly than the scene is reloaded. So it starts to reload on every update. Introduce a bool property to indicate, that reload is already started.
if (fade.alpha >= 1 && !isReloadStarted)
{
Debug.Log("Change scene");
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
isReloadStarted = true;
}
Furthermore according to your description, you might want to reload when fade.alpha is less or equal to 0.
fade.alpha <= 0
I ended up refactoring the code and implementing this in a completely different way. I still don't know why it was acting so weird.

How do I access specific raycast hits in a 'for' loop?

I have a loop that draws 4 raycasts from the bottom of my character, which are used to detect collision with objects. My issue is that if raycast #1 and raycast #4 are colliding with different objects they return the values from both objects.
I'd like to make it so that I look for a hit on raycast #4 only, and if that doesn't return a hit then I check raycast #3, etc. Once I have a hit I will check for the value of the object with which the raycast is colliding. I tried using RaycastHits[], but I believe this is intended to be used when you want to analyze multiple hits within all raycasts, not just a single raycast.
for (int i = 0; i < VerticalRayCount; i++)
{
Vector2 rayOrigin = (directionY == -1) ? RaycastOrigin.bottomLeft : RaycastOrigin.topLeft;
rayOrigin += Vector2.right * (VerticalRaySpacing * i + deltaMovement.x);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);
Debug.DrawRay(rayOrigin, Vector2.up * directionY * rayLength, Color.red);
if (directionY == -1)
{
if (directionX == 1)
{
if (i == VerticalRayCount -1)
{
if (hit)
{
if (currentPlatform != hit.collider.gameObject)
{
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
currentPlatform = hit.collider.gameObject;
}
}
}
else if (i == 0)
{
if (hit)
{
if (currentPlatform != hit.collider.gameObject)
{
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
currentPlatform = hit.collider.gameObject;
}
}
}
}
}
}
I see two problems in your code:
In your for-loop currentPlatform gets allways overwritten by the last hit.
But since you check if(currentPlatform != hit.collider.gameObject) it will allways switch between the last two hits if there is more than one. This happens because it hits the first one and sets it. Than the second hit is not equal to the first one so it gets overwritten. So the next time the currentPlatform is still the second hit and gets again overwritten with the first hit of the new loop and always goes on like this.
To avoid the second remove those if(currentPlatform != hit.collidergameObject).
And you should add break; after a hit so the for loop doesn't continue with the other raycasts but rather exits the for loop immediately. So you get only one hit and don't overwrite them.
...
if(hit)
{
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
currentPlatform = hit.collider.gameObject;
// here add break so the for loop is not continued
break;
}
...
On this way currentPlattform allways has the value of the first hit.

Logic for detecting swipe direction

Basically what I am trying to do is create some code that will register when a touch has swiped across the screen, so I can move my character by touch controls.
What I have written only seems to work sometimes, and when it does it makes the character move multiple times.
Here is my code :
if (Input.touches [0].phase == TouchPhase.Ended) {
Debug.Log (Input.touches [0].deltaPosition);
if (Input.touches[0].deltaPosition.y > Input.touches[0].deltaPosition.x || Input.touches[0].deltaPosition.y > (-Input.touches[0].deltaPosition.x))
movePlayer ("Up");
if ((-Input.touches[0].deltaPosition.y) > Input.touches[0].deltaPosition.x || (-Input.touches[0].deltaPosition.y) > (-Input.touches[0].deltaPosition.x))
movePlayer ("Down");
if ((-Input.touches[0].deltaPosition.x) > Input.touches[0].deltaPosition.y || (-Input.touches[0].deltaPosition.x) > (-Input.touches[0].deltaPosition.y))
movePlayer ("Left");
if (Input.touches[0].deltaPosition.x > Input.touches[0].deltaPosition.y || Input.touches[0].deltaPosition.x > (-Input.touches[0].deltaPosition.y))
movePlayer ("Right");
}
Any advice would be really helpful
Once you've made a decision, you don't need to run anymore tests, so this would be an ideal time for using else if and else
if()
{
}
else if()
{
}
else
{
}
to ensure that only a single case could execute.
However... I think your testing is flawed. Wouldn't the following be more appropriate?
var dp = Input.touches[0].deltaPosition;
if(Math.Abs(dp.x) > Math.Abs(dp.y))
{
//gesture had more horizonal movement
if(dp.x < 0)
{
//left
}
else
{
//right
}
}
else
{
//gesture had more vertical movement
if(dp.y < 0)
{
//up
}
else
{
//down
}
}
I wrote this simple tutorial wich is basically 7 lines of code and works like a charm. Easy and simple.
You just have to use te build in Unity event system instead to write long and useless code.
No need to use an update or fixed update.

Categories

Resources