Unity Animations - c#

I am trying to change character animations using a script, based on key inputs, but Unity seems to only play the default "standing idle" animation and occasionally switching to the "crouched idle", is there a different way to handle animations or am I just doing the script wrong? Here is my script as it currently stands
using UnityEngine;
using System.Collections;
public class CharacterControl : MonoBehaviour {
private Animator animator;
public bool crouched;
private string sc;
// Use this for initialization
void Start () {
animator = GetComponent<Animator> ();
}
// Update is called once per frame
void Update () {
if (crouched == true) {
sc = "crouch";
} else {
sc = "standing";
}
animator.Play (sc + "_idle");
if (Input.GetButton ("Fire3")) {
if (crouched == false) {
crouched = true;
} else {
crouched = false;
}
}
}
}

Try replacing
if (Input.GetButton ("Fire3")) {
if (crouched == false) {
crouched = true;
} else {
crouched = false;
}
}
with
if (Input.GetButton ("Fire3")) {
crouched = true;
} else {
crouched = false;
}
Now, when you hold down the "Fire3" button, your character should crouch, and when you release it he/she should stand again
Also a suggestion: Put the other code in the function (if (crouched == true) ... animator.Play (sc + "idle"); after this code (the Input.GetButton check). That way, your character should instantly start crouching the same frame that the button is pressed; otherwise, he/she will the frame after
Explanation
Input.GetButton will return true while you're pressing down (in the middle of clicking or touching) on the button each frame. Each time Update is called (around 1/60th of a second) your code will check if you're pressing down, and toggle crouched. When you click/tap the button, you'll likely be pressing down for a few frames, so crouched will switch from true to false, back and forth, a few times. In some cases (when you press down for an odd number of frames) crouched will be switched, but in other cases (when you press down for an even number of frames) crouched will stay as it was before you clicked on the button, preventing your character from crouching, or standing if he was crouching before.
Source: From the official API: Input.GetButton

I would strongly suggest using animation states and transit from one to another when you get an input. Checkout my answer at: Unity 2D animation running partially

Yes you can do the toggle action like this
void Update()
{
if(Input.MouseButtonDown(2))
{
crouched = true;
}
if(Input.MouseButtonUp(2))
{
crouched = false;
}
}

You can try this code:
crouched = Input.GetButtonDown("Fire3") ? true : false;

Related

I'm trying to make an animation that can always happen when clicking a button after entering a trigger

I'm making a game with unity 2022 and was struggling to make this animator [bool turn true] after entering a trigger and pressing "E". and then go false after still holding "E" still or not pressing it. also am struggling with Get Key stuff. it isn't working mate and I have been trying for hours at a time with struggles so yeah. this is my code. it's basically someone pressing a button (the object in unity I'm using), making it click until you click it again, please help this is for a school summer project!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Fixedpress : MonoBehaviour
{
public Animator Tributton;
bool YUIp = false;
// Start is called before the first frame update
void OnTriggerEnter(Collider other)
{
if(other.tag == "player")
{
YUIp = true;
}
}
void OnTriggerStay(Collider other)
{
if (other.tag == "Player" && YUIp == true)
{
if (Input.GetKeyDown(KeyCode.E))
{
Tributton.SetBool("Fiveyon", true);
Debug.Log("THY");
}
else if (Input.GetKey(KeyCode.E))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THe");
}
else if (Input.GetKeyUp(KeyCode.Return))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THane");
}
}
}
void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
YUIp = false;
}
}
}
I mostly need help with the press e code stuff to make work so yeah :D
thank you
sorry for the bad grammar its 3 am rn
I think I got it now.
You want
Player has to be in the trigger in order to start the animation
Pressing E should trigger the animation only ONCE
I would no use a Bool parameter in that case but rather a Trigger and then Animator.SetTrigger.
Your transitions should be somewhat like e.g.
Idle State --condition-Fiveyon--> Your Animation
Your Animation --exit-time-no-further-condition--> Idle State
Then you could probably do something like e.g.
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
private Coroutine routine;
private void OnTriggerEnter(Collider other)
{
// not sure if needed right now, some of the events are triggered even on disabled components
if(!enabled) return;
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if(!other.CompareTag("player")) return;
// just in case to prevent concurrent routines
if(routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if(!other.CompareTag("player")) return;
// when exiting the trigger stop the routine so later button press is not handled
if(routine != null) StopCoroutine(routine);
}
}

The cursor doesn't lock back in FPS Camera

I am making a FPS Game. So, when I start the game, the cursor is locked and I can not see it.
When I press Escape key, now, I am able to see the Cursor and I can move it around and interact with it on my screen and I can not control the game camera now. That is fine. Now, I press Escape key again, and now, I can resume controlling the camera in game, but I can still see the cursor and I can move the camera in game while making the cursor interact with all the stuff even outside of my game window.
The Code
void Update()
{
LockAndUnlockCursor();
if(Cursor.lockState == CursorLockMode.Locked)
{
LookAround();
}
}
void LockAndUnlockCursor()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
if(Cursor.lockState == CursorLockMode.Locked)
{
Cursor.lockState = CursorLockMode.None;
}
else if (Cursor.lockState == CursorLockMode.None)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}```
In the editor this tends to bug out, you have to constantly set it in Update() by saving them into variables.
bool isCursorLocked;
Update() {
if (Input.GetKeyDown(blah))
isCursorLocked = !isCursorLocked;
if (isCursorLocked)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
else
{
// etc
just use variables and always set the cursor lock every Update(). If you don't want this code running always then you could use #if UNITY_EDITOR in a way to only set it every single update for unity editor

Message not being disabled when not in collider

I am trying to display a message saying 'Press E to talk to NPC' when the player is collided with the NPC collider and when the player is not collided with the NPC the message is disabled. The message does appear upon collision but it does not disabled when there are no collisions I have tried so many things but nothing seem to work. Can anyone help? HERE IS MY CODE AND SOME THINGS I HAVE TRIED:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Task_7 : MonoBehaviour
{
public GameObject PressEmsg;
//public bool isNearNPC = true;
// Start is called before the first frame update
void Start()
{
PressEmsg.gameObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, 5f);
//bool isNearNPC = false;
//we are looping in the array hitColliders
foreach(Collider collider in nearbyColliders)
{
if(collider.gameObject.tag == "NPC")
{
PressEmsg.gameObject.SetActive(true);
print("NPC DETECTED");
//isNearNPC = true;
}
else
{
PressEmsg.gameObject.SetActive(false);
print("NPC NOT DETECTED");
}
/*
else if(collider.gameObject.tag != "NPC")
{
PressEmsg.gameObject.SetActive(false);
print("NPC NOT DETECTED");
}
*/
}
/*foreach(Collider collider1 in notnearbyColliders)
{
if(collider1.gameObject.tag != "NPC")
{
PressEmsg.gameObject.SetActive(false);
print("NPC NOT DETECTED");
}
}
*/
}
}
If you have no collisions at all you will not have any iteration of your loop. So the deactivate will not happen, except you have a nearby object without the tag NPC.
Further you are iterating through all the nearby objects and check for each of them if it has the tag NPC. So the loop fully depends on the order in which the colliders are iterated. It might e.g. happen that you first have a hit that has the tag, then you have a second hit that doesn't => you falsely deactivate the object again.
You should rather use Linq Any like e.g.
using System.Linq;
...
void Update()
{
var nearbyColliders = Physics.OverlapSphere(transform.position, 5f);
// This will be true if any of the nearbyColliders has the tag "NPC"
// If there are no colliders this will automatically be false accordingly
var detected = nearbyColliders.Any(collider => collider.CompareTag("NPC"));
// Basically this equals somewhat doing
//var detected = false;
//foreach(var collider in nearbyColliders)
//{
// if(collider.CompareTag("NPC"))
// {
// detected = true;
// break;
// }
//}
PressEmsg.gameObject.SetActive(detected);
print(detected ? "NPC detected" : "NPC not detected");
}
In general for performance reasons avoid logging in Update! Even though your users don't see the log it is still done and is quite expensive.
Note: Typed on smartphone but I hope the idea gets clear
It seems like if you don't have any collisions, you won't get into your for loop.
Before looping over the found colliders I would default the message to be not active, but I'd use a variable so I only actually call the message state method once:
bool isNearNpc = false;
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, 5f);
foreach(Collider collider in nearbyColliders)
{
if(collider.gameObject.tag == "NPC")
{
print("NPC DETECTED");
isNearNpc = true;
}
}
PressEmsg.gameObject.SetActive(isMessageActive);
print($"NPC DETECTED: { isNearNpc }");

can't use WaitForSeconds with OnMouseDown

I used OnMouseDown() to deactivate an object but i want the object to activate again in a few seconds. I have used WaitForSeconds() for other things but this time it just doesn't work
this is what i could gather by researching (the deactivating part works fine):
void Start()
{
StartCoroutine(wait());
}
void Update(){}
void OnMouseDown()
{
gameObject.SetActive(false);
}
IEnumarator wait()
{
yield return new WaitForSeconds(3);
gameObject.SetActive(true);
}
There are too many reasons your code isn't work right. You are doing it backwards. Your coroutine starts immediately when your program starts because wait() is called from the Start() function. When it starts, it pauses for 3 seconds and set your GameObject to SetActive(true);
If your GameObject is already visible to the screen, your code wont do anything because SetActive(true) will be called even when it is visible. If you fail to press/click on the screen before that 3 seconds, you wont be able to see SetActive(true); because your coroutine code would have finished running by that time.
Also, if you disable a GameObject, the coroutine attached to it will stop. The solution is to create a reference of the GameObject you want to disable then use that reference to disable and enable it from another script without problems.
Since provided a code, I fixed/re-wrote it for you. I replaced the OnMouseDown fucntion with something more robust.
All you have to do is create an empty GameObject. Attach this script to that empty GameObject. Then drag and drop that GameObject you want to disable and enable to the this "Game Object To Disable" slot in this script, from the Editor.
Do NOT attach this script to that GameObject you want to disable and enable.
Tested with cube and it worked.
using UnityEngine;
using System.Collections;
public class ALITEST: MonoBehaviour
{
//Reference to the GameObject you want to Disable/Enable
//Drag the Object you want to disable here(From the Editor)
public GameObject gameObjectToDisable;
void Start()
{
}
void Update()
{
//Keep checking if mouse is pressed
checkMouseClick();
}
//Code that checks when the mouse is pressed down(Replaces OnMouseDown function)
void checkMouseClick()
{
//Check if mouse button is pressed
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo))
{
//Check if the object clicked is that object
if (hitInfo.collider.gameObject == gameObjectToDisable)
{
Debug.Log("Cube hit");
StartCoroutine(wait()); //Call the function to Enable/Disable stuff
}
}
}
}
//This value is used to make sure that the coroutine is not called again while is it already running(fixes many bugs too)
private bool isRunning = false;
IEnumerator wait(float secondsToWait = 3)
{
//Exit coroutine while it is already running
if (isRunning)
{
yield break; //Exit
}
isRunning = true;
//Exit coroutine if gameObjectToDisable is not assigned/null
if (gameObjectToDisable == null)
{
Debug.Log("GAME OBJECT NOT ATTACHED");
isRunning = false;
yield break; //Exit
}
gameObjectToDisable.SetActive(false);
//Wait for x amount of Seconds
yield return new WaitForSeconds(secondsToWait);
//Exit coroutine if gameObjectToDisable is not assigned/null
if (gameObjectToDisable == null)
{
Debug.Log("GAME OBJECT NOT ATTACHED");
isRunning = false;
yield break; //Exit
}
gameObjectToDisable.SetActive(true);
isRunning = false;
}
}
Because you're calling StartCoroutine() in Start(), your coroutine will resume 3 seconds after the component is started. You want to call StartCoroutine(wait()) in OnMouseDown() so the GameObject will become active after that.
You cannot deactivate GameObject and continue Coroutine on it. If you deactivate GameObject that has runing Coroutine it will be stoped.
So if you want to do it right, you need Coroutine runing on other GameObject and from there actvating this GameObject.
If you need more help, ask.

Jump Button in Unity for Canvas

I am looking for a code or script in C# or Java to make my cube tagged as a Player jump, in below script.
I have written some code and attached it to a button on canvas, but the problem is when I press and hold the button, it keeps jumping and makes an infinitly high jump.
Here is my script written in C#
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class DownstateButton : Button
{
public void Update()
{
//A public function in the selectable class which button inherits from.
if(IsPressed())
{
WhilePressed();
}
}
public void WhilePressed()
{
var player =GameObject.FindWithTag("Player");
player.transform.Translate(0, 1, 0);
//It was for jump
}
}
Hook the PointerDown (called when the button is pressed down) and PointerUp (button has been let go again) events from the UIButton and weight the translation of the position with Time.deltaTime, and you should be good to go. (player.transform.Translate(0,1 * Time.deltaTime, 0), optionally multiply it with another factor for speed modulation.) References: http://unity3d.com/learn/tutorials/modules/beginner/ui/ui-events-and-event-triggers
EDIT: Yeah, some example code. First, I have an EventTrigger component on the button. I use this sothat I can hook the PointerDown and PointerUp events as described before. It looks like this in the inspector:
(Use the "Add New Event Type" button to redirect the event calls.)
Then, I have this script on the button. The code speaks for itself.
using UnityEngine;
using UnityEngine.EventSystems;
public class JumpButton : MonoBehaviour {
private bool shouldJump = false;
// Update is called once per frame
void Update () {
//Find the player
var player = GameObject.FindGameObjectWithTag("Player");
//No player? exit out.
if (player == null)
return;
//Is the jump button currently being pressed?
if (shouldJump)
{
//Translate it upwards with time.
player.transform.Translate(new Vector3(0, Time.deltaTime * 5, 0));
//Make sure the Rigidbody is kinematic, or gravity will pull us down again
if (player.GetComponent<Rigidbody>().isKinematic == false)
player.GetComponent<Rigidbody>().isKinematic = true;
}
//Not jumping anymore? reset the Rigidbody.
else
player.GetComponent<Rigidbody>().isKinematic = false;
}
//When the button is being pressed down, this function is called.
public void ButtonPressedDown(BaseEventData e)
{
shouldJump = true;
}
//When the button is released again, this function is called.
public void ButtonPressedUp(BaseEventData e)
{
shouldJump = false;
}
}
The thing with switching to a kinematic rigidbody is optional, though. Also the speed can be adjusted with the multiplicative constant in the Translate() call. I tested this code with a standard cube, that has the Player tag and a Rigidbody on it, and it works fine.
Happy coding.
You could use a unity coroutine.
At the start of the routine, you'd set (for example) "isJumping" (a bool), and then before you start your loop bit, you'd check if you are jumping by checking 'isJumping'.
If not "isJumping", set isJumping to true, do your jump, and then on the completion of the routine, set isJumping to false.
//untested (uncompiled) code written on the fly
bool isJumping = false;
IEnumerator doJump()
{
if (!isJumping) {
isJumping = true;
// do jump (probably a loop)
while (jumpCondition) {
// jump instructions
yield return
}
//unset isJumping, inside 'if' but after yield return
isJumping = false
}
}
Note : code after yield return in a coroutine will only (probably) be run once, and only run as the coroutine exists (because no more yielding means the coroutine is at an end)

Categories

Resources