Good day! With grief in half, I wrote a code that works, but with crutches.
Question: What is the correct way to prevent the player from moving when attacking?
Now I take the attack bool in the animator, when I press the mouse button, I switch the bool to the true, make a delay of 1.5 seconds and return it to false. But when you press quickly, all the animations are mixed up and you get a mess.
Also one of the controversial ways to translate the player's speed to 0 when attacking. What is the correct way to prevent the player from moving during the attack animation?
Animator
void Update () {
// WASD
float Horizontal = Input.GetAxis("Horizontal");
float Vertical = Input.GetAxis("Vertical");
Vector3 move = Quaternion.Euler (0, playerCamera.transform.eulerAngles.y, 0) * new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
controller.Move(move * Time.deltaTime * playerSpeed);
if (move != Vector3.zero && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack"))
{
gameObject.transform.forward = move;
animator.SetBool("Walk", true);
}
else
{
animator.SetBool("Walk", false);
}
if(Input.GetMouseButtonDown(0))
{
Attack();
}
}
private void Attack() {
animator.SetBool("Attack", true);
playerSpeed = 0f;
Debug.Log("Attack");
Invoke("AttackEnd", 1.5f);
}
private void AttackEnd()
{
animator.SetBool("Attack", false);
playerSpeed = 1f;
}
Related
How can I make this script work, or another alternative player movement script? This basic movement script in Unity doesn't do anything when i press WASD for some reason. I am a beginner at C# and unity and I kind of need an alternative player movement script that actually works.
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
transform.Translate(Vector3.forward);
if (Input.GetKeyDown(KeyCode.S))
transform.Translate(Vector3.back);
if (Input.GetKeyDown(KeyCode.A))
transform.Translate(Vector3.left);
if (Input.GetKeyDown(KeyCode.D))
transform.Translate(Vector3.right);
I recommend using the GetAxis("Horizontal") and GetAxis("Vertical") methods which maps wasd and arrow keys by default to help slim down your big if tree.
Below is a small example from an old unversity project of mine.
Note: the "* speed * Time.deltaTime" as this is used to help translate as time/frames move on, speed is just a multiplier in this case.
public float speed = 10.0f;
private float translation;
private float straffe;
// Use this for initialization
void Start()
{
// turn off the cursor
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
// Input.GetAxis() is used to get the user's input
// You can further set it on Unity. (Edit, Project Settings, Input)
translation = Input.GetAxis("Vertical") * speed * Time.deltaTime;
straffe = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
transform.Translate(straffe, 0, translation);
if (Input.GetKeyDown("escape"))
{
// turn on the cursor
Cursor.lockState = CursorLockMode.None;
}
}
Input.GetKeyDown checks if you pressed the w key on that frame. You need Input.GetKey. That checks if you are holding
You should use GetAxis("Horizontal") & GetAxis("Vertical");
Here is my basic code for my projects default movement:
//movement
Rigidbody rb;
float xInput;
float yInput;
public float speed;
public float defSpeed;
public float sprint;
public bool isGrounded = true;
public float jump;
// Start is called before the first frame update
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Start()
{
}
// Update is called once per frame
void Update()
{
xInput = Input.GetAxis("Horizontal");
yInput = Input.GetAxis("Vertical");
//Things is still can't understand
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
float cameraRot = Camera.main.transform.rotation.eulerAngles.y;
rb.position += Quaternion.Euler(0, cameraRot, 0) * input * speed * Time.deltaTime;
//sprint
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = sprint;
}
else if (Input.GetKeyUp(KeyCode.LeftShift))
{
speed = defSpeed;
}
//jump
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
rb.AddForce(0, jump, 0);
//Debug.Log("Jump!");
}
Hope this helps:)
hi there Iam trying learn how to use unity animation system i create function in my code to control animation triggers i only have two walk and attack its work good in 4 normal directions plus i create avatar to mix animation with layers and all works good from my perspective
my problem is when the character face a diagonal direction the attack animation wont trigger as you can see in the gif i attached and whats make me really wonder is when the character face a specific direction which is the up arrow + right arrow the trigger work with this diagonal direction otherwise the attack animation is never work with any other diagonal direction
i tried to create diagonal bool to control this but its not work i tried also to create double transitions on the animation its also not work
public class MovementController : MonoBehaviour
{
Vector3 _CharacterDirection;
Quaternion _CharacterRotation=Quaternion.identity;
Animator _CharacterAnim;
Rigidbody _CharacterRigidbody;
[SerializeField]
public float TurnSpeed;
bool _IsWalking, Isdiagonal;
void Start()
{
_CharacterAnim = GetComponent<Animator>();
_CharacterRigidbody = GetComponent<Rigidbody>();
}
void Update()
{
CharacterActions();
}
void FixedUpdate()
{
CharacterMovement();
}
void CharacterMovement()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
_CharacterDirection.Set(h, 0, v);
_CharacterDirection.Normalize();
bool _IsHorizontalChange = !Mathf.Approximately(h, 0f);
bool _IsVerticalChange = !Mathf.Approximately(v, 0);
_IsWalking = _IsHorizontalChange || _IsVerticalChange;
//Isdiagonal = _IsHorizontalChange && _IsVerticalChange ? true : false;
_CharacterAnim.SetBool("IsWalking", _IsWalking);
Vector3 _DesairdForward = Vector3.RotateTowards(this.transform.forward, _CharacterDirection, TurnSpeed * Time.deltaTime, 0);
_CharacterRotation = Quaternion.LookRotation(_DesairdForward);
_CharacterRigidbody.MovePosition(_CharacterRigidbody.position + _CharacterDirection * Time.deltaTime);
_CharacterRigidbody.MoveRotation(_CharacterRotation);
}
void CharacterActions()
{
if (Input.GetKeyDown(KeyCode.Space))
{
_CharacterAnim.SetTrigger("SpellCast");
}
else if(Input.GetKeyDown(KeyCode.Space) && _IsWalking)
{
_CharacterAnim.SetTrigger("SpellCast");
}
}
The grounded state for my character controller flickers on and off constantly at what seems to be every frame. From what I know, it's supposed to check if the player is grounded through player.isGrounded, but something else is moving it back up.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCharacterController: MonoBehaviour {
static Animator anim;
public bool walking;
public GameObject playerModel, Hero;
//Transforms
public Transform playerCam, character, centerPoint;
private Vector3 moveDirection;
//character controller declaration
CharacterController player;
//Mouse Rotation
private float rotX, rotY;
//Mouse Y Position
public float mouseYPosition = 1f;
//Mouse Sensitivity
public float Sensitivity = 10f;
//Mouse Zoom
private float zoom;
public float zoomSpeed = 2;
//Clamping Zoom
public float zoomMin = -2f;
public float zoomMax = -10f;
public float rotationSpeed = 5f;
//Move Front Back left & Right
private float moveFB, moveLR;
//Movement Speed
public float Speed = 2f;
//Velocity of Gravity
public float verticalVelocity;
//Jump Distance
public float jumpDist = 5f;
//Multiple Jumps
int jumpTimes;
//To use with Dialogue Manager
public DialogueManager DiagM;
public AudioClip jumpSound;
public AudioClip HurtSound;
public AudioClip PunchSound;
AudioSource audioSource;
//knockback
public float knockBackForce;
public float knockBackTime;
private float knockBackCounter;
// Use this for initialization
void Start ()
{
//character controller
player = GameObject.Find("Player").GetComponent<CharacterController> ();
StartCoroutine(MyCoroutine(character));
anim = GetComponent<Animator>();
//mouse zoom
zoom = -3;
centerPoint.transform.position = playerCam.transform.position;
centerPoint.transform.parent = null;
audioSource = GetComponent<AudioSource>();
}
IEnumerator MyCoroutine (Transform character)
{
if (player.isGrounded == true)
{
anim.SetBool("isFalling",false);
//anim.SetBool("isIdling", true);
yield return new WaitForSeconds(0);
}
}
// Update is called once per frame
void Update ()
{
//Mouse Zoom Input
zoom += Input.GetAxis ("Mouse ScrollWheel") * zoomSpeed;
if (zoom > zoomMin)
zoom = zoomMin;
if (zoom < zoomMax)
zoom = zoomMax;
//Mouse Camera Input
playerCam.transform.localPosition = new Vector3 (0, 0, zoom);
//Mouse Rotation
rotX += Input.GetAxis ("Mouse X") * Sensitivity;
rotY -= Input.GetAxis ("Mouse Y") * Sensitivity;
//Clamp Camera
rotY = Mathf.Clamp (rotY, -60f, 60f);
playerCam.LookAt (centerPoint);
centerPoint.localRotation = Quaternion.Euler (rotY, rotX, 0);
//Movement Speed
if (knockBackCounter <= 0)
{
moveDirection = (transform.forward * Input.GetAxis("Vertical")) + (transform.right * Input.GetAxis("Horizontal"));
moveDirection = moveDirection * Speed;
moveDirection.y = verticalVelocity;
player.Move(moveDirection * Time.deltaTime);
//Movement Rotation
centerPoint.position = new Vector3 (character.position.x, character.position.y + mouseYPosition, character.position.z);
//knockback disable
//Movement Input
if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
{
transform.rotation = Quaternion.Euler(0f, centerPoint.rotation.eulerAngles.y, 0f);
Quaternion turnAngle = Quaternion.LookRotation(new Vector3(moveDirection.x, 0f, moveDirection.z));
playerModel.transform.rotation = Quaternion.Slerp(playerModel.transform.rotation, turnAngle, Time.deltaTime * rotationSpeed);
if (player.isGrounded == true)
{
anim.SetBool("isWalking", true);
anim.Play("Running");
}
}
else
{
StartCoroutine(MyCoroutine(character));
}
if (Input.GetButtonDown("LHand"))
{
audioSource.PlayOneShot(PunchSound, 1F);
anim.Play("RPunch");
}
if (player.isGrounded == true)
{
jumpTimes = 0;
//verticalVelocity = -Physics.gravity.y * Time.deltaTime;
verticalVelocity = 0;
}
else
{
verticalVelocity += Physics.gravity.y * Time.deltaTime;
anim.SetBool("isWalking", false);
anim.SetBool("isFalling", true);
}
if (jumpTimes < 1)
{
if (Input.GetButtonDown("Jump"))
{
verticalVelocity += jumpDist;
anim.Play("Jump");
audioSource.PlayOneShot(jumpSound, 1F);
jumpTimes += 1;
}
}
}
else
{
knockBackCounter -= Time.deltaTime;
}
}
public void Knockback(Vector3 direction)
{
knockBackCounter = knockBackTime;
anim.Play("Jump");
audioSource.PlayOneShot(HurtSound, 50F);
moveDirection = direction * knockBackForce;
moveDirection.y = knockBackForce;
}
}
It looks like it has to do with the verticalVelocity lines, but so far I have only tried setting verticalVelocity = 0 and that works until I have actually moved the character. What could I change to stop the flickering?
Probably it is already solved, but the reason for that is that if you are using Character Controller you should apply gravity ALL the time to the character.
When the character collides with a object, it actually enters a little bit inside this object, then Unity pushes the character back away from the object, until it is no longer touching it. At this point, your gravity starts acting again, and re initiziling the cycle.
You need to apply gravity 100% of the time to create enough force to "balance" this fight with the floor. Could be a smaller "gravity" like 1. No need to be your gravity variable.
Also, on top of that, I like to add a "Coyote time", and make my on IsGrounded() method, as follows:
public bool IsGrounded()
{
return CoyoteTime < CoyoteTimeMax;
}
public void CoyoteControl()
{
if (CharController.isGrounded)
{
CoyoteTime = 0;
}
else
{
CoyoteTime += Time.deltaTime;
}
}
And then I call the CoyoteControl() on Update(), and I can call IsGrounded() whenever I need.
On the inspector I usually set the CoyoteTimeMax to 0.1 and it makes falls more smooth.
As per you comment. You should not determine if your player is grounded by checking an animation parameter. The best way is to use a RayCast(). So what you have to do:
Create a Layer named Ground, and add all the platforms in your
scene to that layer.
Create a bool variable
i.e
bool isGrounded;
Create a function to check if the character is grounded
Something like:
bool checkGrounded(){
return Physics.Raycast(transform.position, Vector3.down, 2f, 1 << LayerMask.NameToLayer("Ground")));
}
In this answer you can read about the involved parameters in the Raycast
Finally inside the update check if the player is grounded or not
Something like:
void Update(){
isGrounded = checkGrounded();
}
I have found that the isGrounded check can change over the course of the Update() function if you are checking it multiple times. Assigning it to a variable at the beginning of the function may solve the flickering issue.
void Update()
{
bool isGrounded = characterController.isGrounded;
...
Below is a copy of my player movement script which contains functions to animate my character moving left and right, jumping and shooting his bow. I have also been able to get my character to transition from shooting his bow to being in an alert animation, but I am wondering how to write the code so that for 5 seconds after I shoot my bow I will be alert before going back to my default "idle" animation.
I would also like my character to be able to walk around (i.e. transition the alert animation to the walk animation) while being alert, but if he stops moving he goes back to alert. Currently my character will go back to idle if he walks during his alert stance. The script below, specifically lines 98-102 (my "playerAlert" function) present another problem in that my character cannot perform his "shoot" animation after this function has occurred, but I do not know/can't wrap my head around how to code what I mentioned above.
I am using Unity 5.6.
I appreciate any help you guys may be able to give me.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class player_1_controller : MonoBehaviour {
Rigidbody2D myRB;
Animator myAnim;
bool facingRight;
//movement variables
public float maxSpeed;
//jumping variables
bool grounded = false;
float groundCheckRadius = 0.2f;
public LayerMask groundLayer;
public Transform groundCheck;
public float jumpHeight;
//arrow shooting variables
bool firing = true;
public float arrowShoot;
public Transform arrowTip;
public GameObject arrow;
public float fireRate = 0.5f;
public float nextFire = 0f;
// Use this for initialization
void Start () {
myRB = GetComponent<Rigidbody2D> ();
myAnim = GetComponent<Animator> ();
facingRight = true;
}
// Update is called once per frame
void Update () {
//player jump
if (grounded && Input.GetButtonDown ("Jump")) {
grounded = false;
myAnim.SetBool ("isGrounded", grounded);
myRB.AddForce (new Vector2 (0, jumpHeight));
}
//player shooting
if (grounded && Input.GetButtonDown ("Fire1")) {
myAnim.SetBool ("arrowShoot", firing);
Invoke ("fireArrow", 1);
}
}
void FixedUpdate() {
//player movement
float move = Input.GetAxis ("Horizontal");
myAnim.SetFloat ("speed", Mathf.Abs (move));
myRB.velocity = new Vector2 (move * maxSpeed, myRB.velocity.y);
if (move > 0 && !facingRight) {
flip ();
} else if (move < 0 && facingRight) {
flip ();
}
//player jump; check if we are grounded - if not, then we are falling
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
myAnim.SetBool ("isGrounded", grounded);
myAnim.SetFloat ("verticalSpeed", myRB.velocity.y);
}
void flip () {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
void fireArrow() {
if (Time.time > nextFire) {
nextFire = Time.time + fireRate;
if (facingRight) {
Instantiate (arrow, arrowTip.position, Quaternion.Euler (new Vector3 (0, 0, 0)));
} else if (!facingRight) {
Instantiate (arrow, arrowTip.position, Quaternion.Euler (new Vector3 (0, 0, 180)));
}
}
playerAlert ();
}
void playerAlert () {
firing = false;
myAnim.SetBool ("arrowShoot", firing);
}
}
Invoke can help you here. excerpt:
void Start()
{
Invoke("LaunchProjectile", 2);
}
void LaunchProjectile()
{
Rigidbody instance = Instantiate(projectile);
instance.velocity = Random.insideUnitSphere * 5;
}
You can use that to call(invoke) the "back to idle" function after 5 seconds.
Have you considered implementing it inside the animator?
You could create a transition from your Alert animation to your Idle animation, with a fixed exit time of 5 seconds.
This way, after 5 seconds of being in the Alert animation, it will transition to the Idle animation.
I am trying to make a script where i can move a ball, horizontal and vertical. I managed to get that working.
But now I want to make my ball "jump". I ended with script below, but now my ball just get launched like a rocket xD
Can anyone help me out
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
public float speed;
public float jumpSpeed;
public GUIText countText;
public GUIText winText;
private int count;
void Start()
{
count = 0;
SetCountText();
winText.text = " ";
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3 (moveHorizontal, 0, moveVertical);
Vector3 jump = new Vector3 (0, jumpSpeed, 0);
GetComponent<Rigidbody>().AddForce (movement * speed * Time.deltaTime);
if (Input.GetButtonDown ("Jump"));
GetComponent<Rigidbody>().AddForce (jump * jumpSpeed * Time.deltaTime);
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PickUp") {
other.gameObject.SetActive(false);
count = count +1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count >= 10)
{
winText.text = "YOU WIN!";
}
}
}
Jumping does not work with adding a continuous force on an object. You will have to apply a single impulse to the object once when the jump button is first pressed. This impulse will also not include a time factor, because it is applied only once. So you would get something like this:
bool jumping;
if (Input.GetButtonDown ("Jump") && !this.jumping);
{
GetComponent<Rigidbody>().AddForce (jumpForce * new Vector3(0,1,0));
this.jumping = true;
}
Also note that in your example you are multiplying the upward unit vector by the jumpspeed twice. Once in the jump vector initialization and then once in the AddForce method.
Of course, you will also have to make sure that gravity applies to pull the object back down (and if the object hits the ground, reset the jump bool.
In general, depending on what kind of game you are making, it is easier to just set the velocity of the object yourself and don't work with the Unity physics engine to do some simple moving around.
There is an error in your code in the function FixedUpdate:
if (Input.GetButtonDown ("Jump"));
in that way you are applying a force on your object at every frame because the semicolon is excluding the row below from the condition. By removing the semicolon you will have a correct if implementation in case of a jump, as long as on your RigidBody component UseGravity is enabled.
if (Input.GetButtonDown ("Jump"))
GetComponent<Rigidbody>().AddForce (jump * jumpSpeed * Time.deltaTime);
Hope it helps.
Thanks everyone, it was a great help. I now have a jumping character. One who can only jump when grounded.
public bool IsGrounded;
void OnCollisionStay (Collision collisionInfo)
{
IsGrounded = true;
}
void OnCollisionExit (Collision collisionInfo)
{
IsGrounded = false;
}
if (Input.GetButtonDown ("Jump") && IsGrounded)
{
GetComponent<Rigidbody>().velocity = new Vector3(0, 10, 0);
}