I only recently got into Unity, and I have made a character movement system; everything is working besides from sprinting. I have the user use "Left Shift" to sprint, although it seems sprinting never turns off. I'm not quite sure the reasoning as to why this happens or what part of code could be fixed to accommodate for this, as such, I have attached the source file of movement.
Here is the paste: https://pastebin.com/S1h5pEwq
In particular, this is the movement function:
void Movement()
{
Vector2 axis = new Vector2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")) * walkSpeed;
Vector3 forward = new Vector3(-Camera.main.transform.right.z, 0.0f, Camera.main.transform.right.x);
Vector3 wishDirection = (forward * axis.x + Camera.main.transform.right * axis.y + Vector3.up * rb.velocity.y);
rb.velocity = wishDirection;
if (Input.GetKey(KeyCode.LeftShift))
{
isSprinting = true;
}
else
{
isSprinting = false;
}
if (isSprinting == true)
{
walkSpeed = 10.0f;
}
}
More specifically, my goal is to have Sprint be activated strictly while the key is pressed.
Try this:
void Movement()
{
Vector2 axis = new Vector2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")) * walkSpeed;
Vector3 forward = new Vector3(-Camera.main.transform.right.z, 0.0f, Camera.main.transform.right.x);
Vector3 wishDirection = (forward * axis.x + Camera.main.transform.right * axis.y + Vector3.up * rb.velocity.y);
rb.velocity = wishDirection;
if (Input.GetKey(KeyCode.LeftShift))
{
isSprinting = true;
}
else
{
isSprinting = false;
}
if (isSprinting == true)
{
walkSpeed = 10.0f;
}
else
{
walkSpeed = 0.0f
}
}
The problem in your script was that you were not resetting the speed.
If(Input.GetKeyUp(KeyCode.LeftShift)){
isSprinting = false;
}
This will do, you didn't check when the player stopped pressing shift.
Related
I'm writing some code to create a Minecraft Quake like game but I have an issue with the jump mecanic. When I'm stuck to a wall the player jump to high (see the video).
I use a Rigidbody for the physics and I modify that velocity to move the player. There is a Physic Material on the player's Collider with no consideration for friction or bouncing.
If you have ideas to fix the bug or an alternative to work around the problem, I'm interested.
How it looks like
Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum PlayerMovementState {
Sneak,
Walk,
Run
}
public class PlayerControls : MonoBehaviour
{
Rigidbody rb;
Vector3 velocity, desiredVelocity;
PlayerMovementState moveState;
float groundDistance;
[SerializeField]
bool forwardAir, backAir, rightAir, leftAir;
[SerializeField]
LayerMask groundLayer;
[SerializeField]
bool onGround;
bool desiredJump;
float jumpHeight = 1.0f;
private void Awake() {
rb = GetComponent<Rigidbody>();
moveState = PlayerMovementState.Walk;
groundDistance = GetComponentInChildren<Collider>().bounds.extents.y;
}
private void Update() {
Vector2 playerInputs = Vector2.ClampMagnitude(
new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
), 1.0f
);
if (Input.GetKey(KeyCode.LeftShift)) moveState = PlayerMovementState.Sneak;
else if (Input.GetKey(KeyCode.LeftControl)) moveState = PlayerMovementState.Run;
else moveState = PlayerMovementState.Walk;
float speed = moveState == PlayerMovementState.Run ? 10f : (
moveState == PlayerMovementState.Sneak ? 2f : 5f
);
RaycastGround();
onGround = !forwardAir && !backAir && !rightAir && !leftAir;
if (Input.GetButtonDown("Jump")) desiredJump = true;
if (moveState == PlayerMovementState.Sneak)
{
if (forwardAir && playerInputs.y > 0) playerInputs.y = 0f;
if (backAir && playerInputs.y < 0) playerInputs.y = 0f;
if (rightAir && playerInputs.x > 0) playerInputs.x = 0f;
if (leftAir && playerInputs.x < 0) playerInputs.x = 0f;
}
desiredVelocity =
(transform.forward * playerInputs.y + transform.right * playerInputs.x) * speed;
}
private void FixedUpdate() {
velocity = rb.velocity;
float acceleration = 10;
velocity.x = Mathf.MoveTowards(velocity.x, desiredVelocity.x, acceleration);
velocity.z = Mathf.MoveTowards(velocity.z, desiredVelocity.z, acceleration);
if (desiredJump && onGround)
{
desiredJump = false;
float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
velocity.y += jumpSpeed;
}
rb.velocity = velocity;
desiredJump = false;
}
void RaycastGround()
{
forwardAir = !(Physics.Raycast(
transform.position + Vector3.forward * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
backAir = !(Physics.Raycast(
transform.position - Vector3.forward * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
rightAir = !(Physics.Raycast(
transform.position + Vector3.right * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
leftAir = !(Physics.Raycast(
transform.position - Vector3.right * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
}
}
Very likely the problem is that the script thinks it's still grounded while it is jumping upwards along the wall.
Depending on what feeling you want, either fix the raycasts such that they only trigger when you are standing directly on top of an object, or you check if the y part of your velocity is <= 0 for your onGround variable.
I did not find a solution to my problem but I found a workaround anyway.
By detecting the walls around the player, I prevent him from moving in the direction of the wall which prevents him from being stuck on it and having this bug when he jumps.
(It means that my problem is not resolved and that I am still looking for a solution)
Video
...
float wallDistance;
...
[SerializeField]
bool forwardWall, backWall, rightWall, leftWall;
...
SpherecastWall();
...
if (forwardWall && playerInputs.y > 0) playerInputs.y = 0f;
if (backWall && playerInputs.y < 0) playerInputs.y = 0f;
if (rightWall && playerInputs.x > 0) playerInputs.x = 0f;
if (leftWall && playerInputs.x < 0) playerInputs.x = 0f;
void SpherecastWall() {
forwardWall = (Physics.SphereCast(
new Ray(transform.position, Vector3.forward),
wallDistance,
.2f,
groundLayer
));
backWall = (Physics.SphereCast(
new Ray(transform.position, -Vector3.forward),
wallDistance,
.2f,
groundLayer
));
rightWall = (Physics.SphereCast(
new Ray(transform.position, Vector3.right),
wallDistance,
.2f,
groundLayer
));
leftWall = (Physics.SphereCast(
new Ray(transform.position, -Vector3.right),
wallDistance,
.2f,
groundLayer
));
}
I think that's because the spheres center gets over the corner of the wall. So when you apply a force the sphere will be pushed over it.
Maybe you could replace the sphere collider of your player with a capsule or a square collider.
I'm currently working on a pushing system and I'm having some issues. The player uses a CharacterController which can interact with Rigidbodies in the scene. Currently this works as follows: When a Raycast hits a pushable object in front of the player and presses the push button he will push the object, which all works fine. The issue occurs when the Player is slightly turned away from the pushable and starts pushing. I.e. when the character is off by like 10 degrees, but the ray still hits. The concept I had in mind was calculating the angle of the raycast that hits the pushable cube and rotating the character controller towards the pushable by that angle. The problem is I have no clue how to achieve this. I hope someone can help me with this concept. If anyone has any other concepts or ideas feel free to share them :)
Edit: I've added some scripts
The Character controller part:
if (inputDir != Vector2.zero)
{
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
}
float targetSpeed = ((running) ? runSpeed : movementSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude;
The Raycast setup and pushstates as off now:
if (Physics.Raycast(middle, (forward), out hit, distanceForPush))
{
if (Input.GetButton("Push") && hit.collider.tag == "PushableLight")
{
anim.SetBool("isPushing", true);
pushForce = playerPushForceLight;
movementSpeed = pushSpeedLight;
hit.transform.SendMessage("HitByPlayer", null, SendMessageOptions.DontRequireReceiver);
if (controller.velocity == Vector3.zero)
{
anim.SetFloat("pushSpeedAnim", 0f);
}
else
{
anim.SetFloat("pushSpeedAnim", 1f);
}
}
else if (Input.GetButton("Push") && hit.collider.tag == "PushableHeavy")
{
anim.SetBool("isPushing", true);
pushForce = playerPushForceHeavy;
movementSpeed = pushSpeedHeavy;
hit.transform.SendMessage("HitByPlayer", null, SendMessageOptions.DontRequireReceiver);
if (controller.velocity == Vector3.zero)
{
anim.SetFloat("pushSpeedAnim", 0f);
}
else
{
anim.SetFloat("pushSpeedAnim", 1f);
}
}
else
{
anim.SetBool("isPushing", false);
pushForce = 0f;
movementSpeed = walkSpeed;
hit.transform.SendMessage("HitStopped", null, SendMessageOptions.DontRequireReceiver);
}
}
else
{
anim.SetBool("isPushing", false);
}
AnimatorStateInfo stateInfo = anim.GetCurrentAnimatorStateInfo(0);
if (stateInfo.fullPathHash == pushStateHash)
{
turnSmoothTime = maxTurnSmoothTimePushing;
}
else
{
turnSmoothTime = 0.1f;
}
}
I would try to align the player with the hit's collision normal.
transform.rotation = Quaternion.LookRotation(-hit.normal, Vector3.up);
This will probably only work well for boxes, though.
The base is a red cube.
The spaceship is moving already when the game start.
When I click/press the L button the spaceship rotates to face the base and starts moving to it but then when it's getting close to the base it's behaving unexpectedly and the spaceship starts rolling around the base nonstop.
What I want is to make the landing automatic like this youtube video of blender.
I don't want the graphics but the way it's landing.
Blender landing spaceship
And this is a short video clip showing my spaceship when it's start landing:
Landing test
This is the script i'm using for controlling the spaceship and the landing part should be automatic.
The script is attached to the spaceship.
using UnityEngine;
using System.Collections;
public class ControlShip : MonoBehaviour {
public int rotationSpeed = 75;
public int movementspeed = 10;
public int thrust = 10;
public float RotationSpeed = 5;
private bool isPKeyDown = false;
private float acceleration = .0f;
private Vector3 previousPosition = Vector3.zero;
private Rigidbody _rigidbody;
private bool landing = false;
private Vector3 originPosition;
private Vector3 lastPosition;
private const float minDistance = 0.2f;
private Transform baseTarget;
// Use this for initialization
void Start () {
baseTarget = GameObject.Find("Base").transform;
originPosition = transform.position;
_rigidbody = GetComponent<Rigidbody>();
Debug.Log("Acc Speed: " + thrust);
}
// Update is called once per frame
void Update()
{
if (landing == false)
{
var v3 = new Vector3(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal"), 0.0f);
transform.Rotate(v3 * rotationSpeed * Time.deltaTime);
transform.position += transform.forward * Time.deltaTime * movementspeed;
if (Input.GetKey(KeyCode.Z))
transform.Rotate(Vector3.forward * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.R))
transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.P))
{
isPKeyDown = Input.GetKey(KeyCode.P);
float distance = Vector3.Distance(previousPosition, transform.position);
acceleration = distance / Mathf.Pow(Time.deltaTime, 2);
previousPosition = transform.position;
_rigidbody.AddRelativeForce(0f, 0f, thrust, ForceMode.Acceleration);
}
}
else
{
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
}
if (landed == true)
TakeOff();
if (Input.GetKey(KeyCode.L))
{
landing = true;
lastPosition = transform.position;
}
}
void OnTriggerEnter(Collider other)
{
if (landing == true && other.gameObject.name == "Base")
{
StartCoroutine(Landed());
}
}
bool landed = false;
IEnumerator Landed()
{
yield return new WaitForSeconds(5);
Debug.Log("Landed");
landed = true;
}
private void TakeOff()
{
if (transform.position != originPosition)
{
_rigidbody.AddForce(transform.up * 10);
}
if ((transform.position - originPosition).sqrMagnitude <= (1f * 1f))
{
landed = false;
_rigidbody.useGravity = false;
}
}
void OnGUI()
{
if (isPKeyDown)
{
GUI.Label(new Rect(100, 100, 200, 200), "Acc Speed: " + acceleration);
}
}
}
This is the part of the landing, should be the part of the landing:
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
The spaceship have two components: Rigidbody, Use Gravity set to true. And a Box Collider.
The Base have a box collider component.
You appear to have a box collider on your vehicle and it looks as though it is colliding with the terrain when your code tries to bring it in for a landing. Try switching the collider to be a trigger(tick option on collider component).
Then try it again, as this will not cause physical collisions. If it works or fails for a totally different reason you know this is the cause or a contributing factor.
EDIT: It is worth also noting that when trying to achieve this kind of effect it can be much easier to trigger an animation than try to achieve it in physics based code. Unity offers some great animation controllers and you can call an animation when you need to land since you are taking control away from the player anyway.
While doing this you could turn off the collider so you don't get any strange collision then turn it on when the ship needs to take off, provided you need that of course.
Hope it helps.
Currently I am working on some player movements. The main idea of my player moves only horizontal and vertical, not diagonal. I couldn't find any reasonable solution for this problem. I really don't want to use rigidbody or character.controller for now. The other thing I want to achieve is, when I pressed multiple direction keys, I want my player move directly to last pressed direction. Here is my code:
using UnityEngine;
using System.Collections;
public class controller : MonoBehaviour {
public int movementspeed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.A)) {
//ratation
transform.localEulerAngles = new Vector3(0,270,0);
//move
transform.Translate (transform.right * movementspeed * Time.deltaTime);
}
else if(Input.GetKey (KeyCode.D)) {
//ratation
transform.localEulerAngles = new Vector3(0,90,0);
//move
transform.Translate (transform.right *(-1)* movementspeed * Time.deltaTime);
}
else if(Input.GetKey (KeyCode.S)) {
//ratation
transform.localEulerAngles = new Vector3(0,180,0);
//move
transform.Translate (transform.forward * (-1) * movementspeed * Time.deltaTime);
}
else if (Input.GetKey (KeyCode.W)) {
//ratation
transform.localEulerAngles = new Vector3(0,0,0);
//move
transform.Translate (transform.forward * movementspeed * Time.deltaTime);
}
else if (Input.GetKey (KeyCode.A)) {
//ratation
transform.localEulerAngles = new Vector3(0,270,0);
//move
transform.Translate (transform.right * movementspeed * Time.deltaTime);
}
}
}
The structure of your selection statement will not give you this functionality. As it will check the first if, then if false, it will check the next else if, and so on. So if I hold down A, no matter what I press, I will always only reach the A part of the code.
What I would do if I were you, is to add another layer on top of this. Only detect key down events, and set matching variables. When you set these, disable the others. Then use these variables for moving. Like this:
bool left, right, up, down;
void CheckInput() {
if (Input.GetKeyDown(KeyCode.W) {
up = true;
left = right = down = false;
}
if (Input.GetKeyDown(KeyCode.S) {
down = true;
left = right = up = false;
}
if (Input.GetKeyDown(KeyCode.A) {
left = true;
right = up = down = false;
}
if (Input.GetKeyDown(KeyCode.D) {
right= true;
left= up = down = false;
}
//And then do matching OnKeyUp events to set them false
}
void Update() {
CheckInput();
if (left)
//Move left
if (right)
//Move right
//etc.
}
I'm working on a project where I'm trying to make my character move by jumping at an angle. Right now during the frame updates, the character will pivot back and forth and when the right key is pressed, they will jump. This code causes them to jump at an angle, but they always return to their original position.
Additionally, I have two characters who start on opposite sides of the stage, but when I start the game they teleport to the same position. I've spent a lot of time reviewing my code but I can't seem to get this to work. Any help you can provide?
using UnityEngine;
using System.Collections;
public class Freg : MonoBehaviour {
public GameObject Tounge;
public float gravity;
public float tempScale = 1;
public float MaxJump = 8f;
public float MinJump = 0.1f;
static float yVector = 0;
static float xVector = 0;
static bool grounded = true;
bool isleft = false;
Vector3 farthestleft;
Vector3 farthestright;
// Use this for initialization
void Start () {
farthestleft = new Vector3 (-33.7f, 50.2f, 24.8f);
farthestright = new Vector3 (22.56f, 54.83f, -15.12f);
}
void OnTriggerEnter (Collider other) {
if (other.GetComponent<Collider> ().tag == "Ground") {
grounded = true;
yVector = 0;
//xVector = 0;
Vector3 onGround = new Vector3 (transform.position.x, -4.86f, transform.position.z);
transform.position = onGround;
} else
grounded = false;
}
// Update is called once per frame
void Update () {
/*if (Input.GetKey (KeyCode.UpArrow) == true) {
Tounge.transform.localScale.Set (1, 0.5f, 1);
} else {
Tounge.transform.localScale.Set (1, 1, 1);
}*/
if (grounded == false) {
yVector -= gravity;
}
if (Input.GetKeyDown (KeyCode.UpArrow) == true && grounded == true) {
MinJump += 0.5f;
} else if (MinJump > 0.1f){
yVector += MinJump;
xVector += MinJump;
MinJump = 0.1f;
grounded = false;
}
Vector3 stuff = new Vector3 (transform.localPosition.y + xVector, transform.position.y + yVector, transform.position.z);
transform.position = stuff;
float t = Mathf.PingPong (Time.time * 0.5f * 2.0f, 1.0f);
transform.eulerAngles = Vector3.Lerp (farthestright, farthestleft, t);
}
}
it looks like you should update the current position during the if statements, rather than after that way on each update, the actual position is moving based on the decision rather than just the end of the loop.