I am trying make a functionality to my game so that the object with containerTransform can move 720f left or right depending upon the touch inputs. I have used Vector3.MoveTowards() but, it shows a **glitchy back and forth movement whenever I swipe right or left, instead of proper transition 720f right or left. I am not sure where my logic went wrong. Here are the complete codes. I am seeking your help. Thank you
private Vector2 startPosition;
private Vector2 endPosition;
public Transform containerTransform;
public float speed;
public float SoftZone = 20f;
//soft zone is the distance upto which the swipe wont work, so swipe length less than it wont trigger the function;
private bool SwipeLeft;
private bool SwipeRight;
private bool boolean;
private Vector3 currentLocation;
private Vector3 endLocation;
void Start()
{
currentLocation = containerTransform.position;
endLocation = containerTransform.position;
}
void Update()
{
if(SwipeLeft) {
containerTransform.position = Vector3.MoveTowards(
currentLocation,
endLocation,
Time.deltaTime * speed
);
if(containerTransform.position == endLocation) {
SwipeLeft = false;
currentLocation = endLocation;
print("swipeleft ends");
}
}
if(SwipeRight) {
containerTransform.position = Vector3.MoveTowards(
currentLocation,
endLocation,
Time.deltaTime * speed
);
if(containerTransform.position == endLocation) {
SwipeRight = false;
currentLocation = endLocation;
print("swiperight ends");
}
}
SwipeCheck ();
}
void SwipeCheck () {
/*if (!SwipeConfirmed){*/
foreach (Touch touch in Input.touches)
{
if(touch.phase == TouchPhase.Began)
{
startPosition = touch.position;
endPosition = touch.position;
boolean = true;
}
if (touch.phase == TouchPhase.Moved)
{
endPosition = touch.position;
}
if (touch.phase == TouchPhase.Ended ||
touch.phase == TouchPhase.Canceled &&
boolean == true)
{
if (startPosition.x - endPosition.x >= SoftZone)
{
SwipeLeft = true;
print("left");
endLocation += new Vector3(
endLocation.x - 720f,
endLocation.y,
endLocation.z
);
}
else if(startPosition.x - endPosition.x <= -SoftZone)
{
SwipeRight = true;
print("right");
endLocation += new Vector3(
endLocation.x + 720f,
endLocation.y,
endLocation.z
);
boolean = false;
}
}
}
}
First of all, Touch.position gives you
The position of the touch in pixel coordinates.
In order to use them for transform.position, you have to convert them to world space 3D coordinates using Camera.ScreenToWorldPoint like
var worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, distanceToCamera);
Where distanceToCamera is a value you have to define/calculate to determine how far the point should be in front of the camera.
Note
If you use this often as in Update, you should store the Camera and reuse the reference like this:
private Camera cam;
private void Awake ()
{
cam = Camera.main;
}
//...
var worldPosition = cam.ScreenToWorldPoint( ... );
The main issue
You are using
Vector3.MoveTowards(currentLocation, endLocation, speed * Time.deltaTime);
This will always start a new movement from currentLocation so you never move more than one step/frame away from the currentLocation.
Instead, use the actual current position of the object
containerTransform.position = Vector3.MoveTowards(containerTransform.position, endPosition, speed * Time.deltaTime);
Your code, SwipeLeft and SwipeRight actually do the same thing, so you could just remove one of the blocks.
if (Swipe)
{
containerTransform.position = Vector3.MoveTowards(containerTransform.position, endPosition, speed * Time.deltaTime);
if (containerTransform.position == endLocation)
{
Swipe = false;
currentLocation = endLocation;
print("swipe ends");
}
}
and only set Swipe = true;
However, I would recommend using Coroutines instead of doing stuff in Update since they are better to control using StartCoroutine and StopCoroutine and passing the value you in you don't need the local variables currentLocation and endLocation.
It could look like
// Makes sure only one scrolling process is running at a time
private bool isScrolling;
private IEnumerator MoveTo(Vector3 targetPosition)
{
if (isScrolling) yield break;
isScrolling = true;
while (containerTransform.position != targetPosition)
{
containerTransform.position = Vector3.MoveTowards(containerTransform.position, endPosition, speed * Time.deltaTime);
yield return null;
}
isScrolling = false;
print("Swipe ends");
}
If you rather would like to swipe within a certain time regardless how far the distance is you could use Vector3.Lerp instead
public float swipeDuration;
private IEnumerator MoveTo(Vector3 targetPosition)
{
if (isScrolling) yield break;
isScrolling = true;
var currentPosition = containerTransform.position;
var timePassed = 0.0f;
while (timePassed < swipeDuration)
{
var lerpFactor = timePassed / swipeDuration;
containerTransform.position = Vector3.Lerp(currentPosition, endPosition, lerpFactor);
yield return null;
}
// To be sure set a fixed end position
containerTransform.position = endPosition;
isScrolling = false;
print("Swipe ends");
}
Adobt your code in SwipeCheck() to use the new coroutine.
StartCoroutine (MoveTo(endLocationWorldPoint));
Related
I am creating a clone of Billiards and I cannot work out how to stop the player from interacting with and launching the ball while it is still moving; below is my attempt. I have placed an if statement that checks whether the ball is moving on both the mouseDrag and mouseUp functions. I have also tried using isSleeping() but this caused the ball to not move at all.
If possible I would like to apply this method to all of the balls and not just the cue ball; so that all balls have to have stopped before any actions may happen. This is currently in my "player" script, if I should move it a GameManager script please let me know.
private void Update()
{
speed = rb.velocity.magnitude;
if (speed < 0.5)
{
rb.velocity = new Vector3(0, 0, 0);
}
}
void OnMouseDrag()
{
if (speed == 0)
{
mousePointB.GetComponent<SpriteRenderer>().enabled = true;
currDistance = Vector3.Distance(mousePointA.transform.position, transform.position);
if (currDistance <= 3f)
{
spaceLimit = currDistance;
}
else
{
spaceLimit = maxDistance;
}
shootPower = Mathf.Abs(spaceLimit) * shootPowervar;
Vector3 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
mousePointB.transform.position = transform.position + ((dimxy / difference) * currDistance * -1);
mousePointB.transform.position = new Vector3(mousePointB.transform.position.x, mousePointB.transform.position.y, -0.8f);
shootDirection = Vector3.Normalize(mousePointA.transform.position - transform.position);
}
else
{
}
}
void OnMouseUp()
{
if (speed == 0)
{
mousePointB.GetComponent<SpriteRenderer>().enabled = false;
arrow.GetComponent<SpriteRenderer>().enabled = false;
circle.GetComponent<SpriteRenderer>().enabled = false;
Vector3 push = shootDirection * shootPower * -1;
GetComponent<Rigidbody2D>().AddForce(push, ForceMode2D.Impulse);
}
else
{
}
}
if your test speed == 0 is not functional, you could record the last position of the ball, and set a boolean isMoving = true, if actual position of ball is different thant the last, then record the lastposition and so on
Vector3 LastPosition;
bool isMoving;
void Update()
{
isMoving = BallPosition == LastPosition;
:
:
}
void OnMOuseDrag()
{
if(!isMoving) return;
}
void OnMOuseUp()
{
if(!isMoving) return;
}
My intention is to make my character wall jump/climb/slide, I got the sliding part working fine, but if he jumps while wall sliding, he should "bounce" back to the wall, the problem is that I can't balance the forces. In all tutorial I saw, it is simply a matter of detecting if the character is wall sliding, and if he is and he jumps, then you add a force oposite to the wall.
This is not working for me, because if I add enough force to make him jump, he goes way too fast and the player can barely see he jumped, he just sees that the character is now higher on the wall. If I add a smaller amount of force, it isn't enough to make a considerable jump and the player would have to hit space a thousand times to make him go up a few centimeters on the wall.
Any help is appreciated, I already tried a lot of things, even tried to freeze the controls, set gravity scale to 0 and make the character fo to the right points using MoveTowards, that is how desperate I am.
I'm also really new to Unity so I might be missing something really simple.
Here is a gif showing the character's behavior:
https://imgur.com/a/TgUHzP6
And here is the relevant parts of my character's script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TheBot : MonoBehaviour {
public float speed;
public int jumpForce;
public Transform groundCheck;
public Transform meleeCheck;
public Transform bulletSpawner;
public LayerMask layerGround;
public float meleeCoolDown;
public float meleeDamage;
private Rigidbody2D body;
private Animator anim;
private Dash dashController;
private Shooter shotController;
private float unloadWaitingTime = 3;
private float idleGunTime = 0;
private bool facingRight = true;
private bool onGround = true;
private bool jumping = false;
private bool attacking = false;
private bool dead = false;
private bool isGunLoaded = false;
private bool isGunLoading = false;
private bool isGunUnloading = false;
private bool takingDamage = false;
private bool dashing = false;
private bool isWallSliding = false;
private float wallJumpTime = 0f;
private Vector3[] wallJumpControlPoint;
// Use this for initialization
void Start () {
body = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
dashController = GetComponent<Dash>();
shotController = GetComponent<Shooter>();
}
// Update is called once per frame
void Update () {
PlayAnimations();
CheckIfGrounded();
checkIfWallSliding();
dashing = dashController.IsDashing();
if (Input.GetButtonDown("Jump") && (onGround || isWallSliding) && !isGunLoading && !jumping && !takingDamage){
jumping = true;
wallJumpControlPoint = new Vector3[3];
wallJumpControlPoint[0] = body.position;
wallJumpControlPoint[1] = new Vector3(body.position.x +4, body.position.y + 2);
wallJumpControlPoint[2] = new Vector3(body.position.x, body.position.y + 4);
}
if (Input.GetButtonDown("Melee") && !attacking && !isGunLoading){
Attack();
}
if(Input.GetButtonDown("Ranged") && !attacking && !isGunLoading && onGround){
Shoot();
}
if(Input.GetButtonDown("Dash") && !attacking && !isGunLoading && onGround){
dashController.DashTo(facingRight? Dash.RIGHT : Dash.LEFT);
}
if(isGunLoaded){
idleGunTime += Time.deltaTime;
if (idleGunTime >= unloadWaitingTime){
UnloadGun();
}
}
}
void FixedUpdate(){
if(!takingDamage){
float move = Input.GetAxis("Horizontal");
//while charachter is wall sliding, slowly fall
if (isWallSliding){
body.velocity = new Vector2(body.velocity.x, -0.7f);
}
if(!dashing){
if(onGround){
//if not dashing on on ground, walk with normal speed
body.velocity = new Vector2(move * speed, body.velocity.y);
} else {
//if character is not on ground, reduce the speed so he doesn't jump too far away
body.velocity = new Vector2(move * (speed * 0.7f), body.velocity.y);
}
}
if((move < 0 && facingRight) || (move > 0 && !facingRight) ){
//control direction character is facing
Flip();
}
if (jumping){
if(isWallSliding){
body.velocity = new Vector2(30, 20);
} else {
body.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
if(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.LeftArrow)){
//if is moving while jumping, reduce jump height
body.velocity = new Vector2(body.velocity.x, body.velocity.y*0.8f);
}
onGround = false;
jumping = false;
}
}
}
void CheckIfGrounded(){
onGround = false;
Collider2D[] collisionResults = new Collider2D[2];
int objectsBeneath = Physics2D.OverlapBoxNonAlloc(groundCheck.position, new Vector2(0.9f, 0.3f), 0.0f, collisionResults, layerGround);
for (int i=0; i <objectsBeneath; i++ ){
if (!GameObject.ReferenceEquals(gameObject, collisionResults[i].gameObject)){
onGround = true;
}
}
}
void checkIfWallSliding(){
if (!onGround){
RaycastHit2D[] ray = new RaycastHit2D[1];
int totalRayHits = Physics2D.LinecastNonAlloc(bulletSpawner.position, body.position, ray, 1 << LayerMask.NameToLayer("SolidGround"));
bool wallFound = totalRayHits > 0 && ray[0].collider.gameObject.tag == "SolidGround";
isWallSliding = wallFound && ( (facingRight && Input.GetKey(KeyCode.RightArrow)) || (!facingRight && Input.GetKey(KeyCode.LeftArrow))) ;
} else {
isWallSliding = false;
if (body.velocity.y > 10){
body.velocity = new Vector2(body.velocity.x, 5);
}
}
}
public void Die(){
dead = true;
}
}
As you've attempted before, you will need to reduce your horizontal jump acceleration/velocity on the jump.
When you wall jump, you'll be pressing towards the wall. And as your code is currently, while you are in the air, your horizontal velocity is set to be in the direction you press. This makes any horizontal movement from the wall jump very hard to see, unless it's large enough to push you very far in one frame.
This (as well as the change we discussed in the comments) is why your previous attempts with low walljump magnitudes didn't work.
To fix this, you have to change how your air control works. One way of going about it is making it add a clamped modifier to your horizontal velocity instead of setting it directly to the target velocity.
if(!dashing){
if(onGround){
//if not dashing on on ground, walk with normal speed
body.velocity = new Vector2(move * speed, body.velocity.y);
} else {
//if character is not on ground, reduce the speed so he doesn't jump too far away
float airControlAccelerationLimit = 0.5f; // Higher = more responsive air control
float airSpeedModifier = 0.7f; // the 0.7f in your code, affects max air speed
float targetHorizVelocity = move
* speed
* airSpeedModifier; // How fast we are trying to move horizontally
float targetHorizChange = targetHorizVelocity
- body.velocity.x; // How much we want to change the horizontal velocity
float horizChange = Mathf.Clamp(
targetHorizChange ,
-airControlAccelerationLimit ,
airControlAccelerationLimit ); // How much we are limiting ourselves
// to changing the horizontal velocity
body.velocity = new Vector2(body.velocity.x + horizChange, body.velocity.y);
}
}
Here it is, in your code, along with making sure we only update velocity once OR use AddForce per FixedUpdate call. And we also change the wallsliding slowdown code to only activate if the player is about to go down faster than the wallslide speed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TheBot : MonoBehaviour {
public float speed;
public int jumpForce;
public Transform groundCheck;
public Transform meleeCheck;
public Transform bulletSpawner;
public LayerMask layerGround;
public float meleeCoolDown;
public float meleeDamage;
private Rigidbody2D body;
private Animator anim;
private Dash dashController;
private Shooter shotController;
private float unloadWaitingTime = 3;
private float idleGunTime = 0;
private bool facingRight = true;
private bool onGround = true;
private bool jumping = false;
private bool attacking = false;
private bool dead = false;
private bool isGunLoaded = false;
private bool isGunLoading = false;
private bool isGunUnloading = false;
private bool takingDamage = false;
private bool dashing = false;
private bool isWallSliding = false;
private float wallJumpTime = 0f;
private Vector3[] wallJumpControlPoint;
// Use this for initialization
void Start () {
body = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
dashController = GetComponent<Dash>();
shotController = GetComponent<Shooter>();
}
// Update is called once per frame
void Update () {
PlayAnimations();
CheckIfGrounded();
checkIfWallSliding();
dashing = dashController.IsDashing();
if (Input.GetButtonDown("Jump") && (onGround || isWallSliding) && !isGunLoading && !jumping && !takingDamage){
jumping = true;
wallJumpControlPoint = new Vector3[3];
wallJumpControlPoint[0] = body.position;
wallJumpControlPoint[1] = new Vector3(body.position.x +4, body.position.y + 2);
wallJumpControlPoint[2] = new Vector3(body.position.x, body.position.y + 4);
}
if (Input.GetButtonDown("Melee") && !attacking && !isGunLoading){
Attack();
}
if(Input.GetButtonDown("Ranged") && !attacking && !isGunLoading && onGround){
Shoot();
}
if(Input.GetButtonDown("Dash") && !attacking && !isGunLoading && onGround){
dashController.DashTo(facingRight? Dash.RIGHT : Dash.LEFT);
}
if(isGunLoaded){
idleGunTime += Time.deltaTime;
if (idleGunTime >= unloadWaitingTime){
UnloadGun();
}
}
}
void FixedUpdate(){
if(!takingDamage){
float move = Input.GetAxis("Horizontal");
//while charachter is wall sliding, slowly fall
if (isWallSliding && !jumping && body.velocity.y < -0.7f){
body.velocity = new Vector2(body.velocity.x, -0.7f)
}
if(!dashing){
if(onGround){
//if not dashing on on ground, walk with normal speed
body.velocity = new Vector2(move * speed, body.velocity.y);
} else {
//if character is not on ground, reduce the speed so he doesn't jump too far away
float airControlAccelerationLimit = 0.5f; // Higher = more responsive air control
float airSpeedModifier = 0.7f; // the 0.7f in your code, affects max air speed
float targetHorizVelocity = move
* speed
* airSpeedModifier; // How fast we are trying to move horizontally
float targetHorizChange = targetHorizVelocity
- body.velocity.x; // How much we want to change the horizontal velocity
float horizChange = Mathf.Clamp(
targetHorizChange ,
-airControlAccelerationLimit ,
airControlAccelerationLimit ); // How much we are limiting ourselves
// to changing the horizontal velocity
body.velocity = new Vector2(body.velocity.x + horizChange, body.velocity.y);
}
}
if((move < 0 && facingRight) || (move > 0 && !facingRight) ){
//control direction character is facing
Flip();
}
if (jumping){
if(isWallSliding){
body.velocity = new Vector2(body.velocity.x + 0.25f * jumpForce, jumpForce);
} else {
body.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
if(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.LeftArrow)){
//if is moving while jumping, reduce jump height
body.velocity = new Vector2(body.velocity.x, body.velocity.y*0.8f);
}
onGround = false;
jumping = false;
}
}
}
void CheckIfGrounded(){
onGround = false;
Collider2D[] collisionResults = new Collider2D[2];
int objectsBeneath = Physics2D.OverlapBoxNonAlloc(groundCheck.position, new Vector2(0.9f, 0.3f), 0.0f, collisionResults, layerGround);
for (int i=0; i <objectsBeneath; i++ ){
if (!GameObject.ReferenceEquals(gameObject, collisionResults[i].gameObject)){
onGround = true;
}
}
}
void checkIfWallSliding(){
if (!onGround){
RaycastHit2D[] ray = new RaycastHit2D[1];
int totalRayHits = Physics2D.LinecastNonAlloc(bulletSpawner.position, body.position, ray, 1 << LayerMask.NameToLayer("SolidGround"));
bool wallFound = totalRayHits > 0 && ray[0].collider.gameObject.tag == "SolidGround";
isWallSliding = wallFound && ( (facingRight && Input.GetKey(KeyCode.RightArrow)) || (!facingRight && Input.GetKey(KeyCode.LeftArrow))) ;
} else {
isWallSliding = false;
}
}
public void Die(){
dead = true;
}
}
Hey guys my question is in the title. Basically when I press play everything is OK, the ball starts to go up without shaking, but when I start swiping to change position it starts shaking.
I already tried different things like changing to FixedUpdate() or resetting the player, but it doesn't change. Btw there is no animation on the ball. Can you help me?
Here is the script with the swipe parameters :
public class Swipe : MonoBehaviour
{
private const float DEADZONE = 100.0f;
public static Swipe Instance { set; get; }
private bool tap, swipeLeft, swipeRight, swipeUp, swipeDown;
private Vector2 startTouch, swipeDelta;
public bool Tap { get { return tap; } }
public Vector2 SwipeDelta { get { return swipeDelta; } }
public bool SwipeLeft { get { return swipeLeft; } }
public bool SwipeRight { get { return swipeRight; } }
public bool SwipeUp { get { return swipeUp; } }
public bool SwipeDown { get { return swipeDown; } }
private void Awake()
{
Instance = this;
}
private void Update()
{
// Reseting all the booleans
tap = swipeLeft = swipeRight = swipeDown = swipeUp = false;
#region Stadalone Inputs
if (Input.GetMouseButtonDown(0))
{
tap = true;
startTouch = Input.mousePosition;
}
else if (Input.GetMouseButtonUp(0))
{
startTouch = swipeDelta = Vector2.zero;
}
#endregion
#region Mobile Inputs
if (Input.touches.Length != 0)
{
if (Input.touches[0].phase == TouchPhase.Began)
{
tap = true;
startTouch = Input.mousePosition;
}
else if (Input.touches[0].phase == TouchPhase.Ended || Input.touches[0].phase == TouchPhase.Canceled)
{
startTouch = swipeDelta = Vector2.zero;
}
}
#endregion
// Calculate The Distance
swipeDelta = Vector2.zero;
if (startTouch != Vector2.zero)
{
if (Input.touches.Length != 0)
{
swipeDelta = Input.touches[0].position - startTouch;
}
else if (Input.GetMouseButton(0))
{
swipeDelta = (Vector2)Input.mousePosition - startTouch;
}
}
// Did we cross the deadzone ?
if (swipeDelta.magnitude > DEADZONE)
{
// Which direction ?
float x = swipeDelta.x;
float y = swipeDelta.y;
if (Mathf.Abs(x) > Mathf.Abs(y))
{
// Left or Right
if (x < 0)
swipeLeft = true;
else
swipeRight = true;
}
else
{
// Up or Down
if (y < 0)
swipeDown = true;
else
swipeUp = true;
}
startTouch = swipeDelta = Vector2.zero;
}
}
}
And this the part of the script in Update() or (FixedUpdate(), it doesn't change anything) used to make it move one side or the other on the player :
// Gather the inputs in which tube we should be
if (Swipe.Instance.SwipeLeft)
MoveTube(false);
if (Swipe.Instance.SwipeRight)
MoveTube(true);
// Calculate where we should be in the future
Vector3 targetPosition = transform.position.y * Vector3.up;
if (desiredTube == 0)
targetPosition += Vector3.left * TUBE_DISTANCE;
else if (desiredTube == 2)
targetPosition += Vector3.right * TUBE_DISTANCE;
// Let's calculate our move delta
Vector3 moveVector = Vector3.zero;
moveVector.x = (targetPosition - transform.position).normalized.x * speed;
moveVector.y = speed;
// Move the ball
controller.Move(moveVector * Time.deltaTime);
}
private void MoveTube(bool goingRight)
{
desiredTube += (goingRight) ? 1 : -1;
desiredTube = Mathf.Clamp(desiredTube, 0, 2);
}
I believe you may be having issue here:
// Gather the inputs in which tube we should be
if (Swipe.Instance.SwipeLeft)
MoveTube(false);
if (Swipe.Instance.SwipeRight)
MoveTube(true);
Since Update() is invoked once per frame, this means this will be checked several times per second. So it may be changing the position of the player several times per second, giving this shaking effect you mention.
What you could try is to restrict the times the player can swipe per second.
private float SwipeRate = 0.1f;
private float NextSwipe = 0.0f;
Update(){
if (Swipe.Instance.SwipeLeft && Time.time > NextSwipe)
{
NextSwipe = Time.time+SwipeRate;
MoveTube(false);
}
if (Swipe.Instance.SwipeRight && Time.time > NextSwipe)
{
NextSwipe = Time.time+SwipeRate;
MoveTube(true);
}
}
Edit:
private void MoveTube(bool goingRight)
{
desiredTube += (goingRight) ? 1 : -1;
desiredTube = Mathf.Clamp(desiredTube, 0, 2);
// Calculate where we should be in the future
Vector3 targetPosition = transform.position.y * Vector3.up;
if (desiredTube == 0)
targetPosition += Vector3.left * TUBE_DISTANCE;
else if (desiredTube == 2)
targetPosition += Vector3.right * TUBE_DISTANCE;
// Let's calculate our move delta
Vector3 moveVector = Vector3.zero;
moveVector.x = (targetPosition - transform.position).normalized.x * speed;
moveVector.y = speed;
// Move the ball
controller.Move(moveVector * Time.deltaTime);
}
I found how to solve the problem. So basically I was using the same speed variable to move my ball on the x and y axis and it seems that it created some “switch problem” for unity. So I just created 2 different variables for the speed and I solved the bug like this.
Hi i have a scenario in which i need to limit the angle rotation for 3rd person controller.Present when i press left and right input keys it is turning 90degrees. I wish to rotate 45degrees. Could someone help me,at which line that angle rotation is happening.
Here my code :
public class ThirdPersonShooter : MonoBehaviour
{
public AnimationClip idleAnimation;
public AnimationClip walkAnimation;
public AnimationClip runAnimation;
public AnimationClip jumpPoseAnimation;
public float walkMaxAnimationSpeed = 0.75F;
public float trotMaxAnimationSpeed = 1F;
public float runMaxAnimationSpeed = 1F;
public float jumpAnimationSpeed = 1F;
public float landAnimationSpeed = 1F;
private Animation _animation;
enum CharacterState
{
Idle = 0,
Walking = 1,
Trotting = 2,
Running = 3,
Jumping = 4,
}
private CharacterState _characterState;
// The speed when walking
public float walkSpeed = 2.0F;
// after trotAfterSeconds of walking we trot with trotSpeed
public float trotSpeed = 4.0F;
// when pressing "Fire3" button (cmd) we start running
public float runSpeed = 6.0F;
public float inAirControlAcceleration = 3.0F;
// How high do we jump when pressing jump and letting go immediately
public float jumpHeight = 0.5F;
// The gravity for the character
public float gravity = 20.0F;
// The gravity in controlled descent mode
public float speedSmoothing = 10.0F;
public float rotateSpeed = 500.0F;
public float trotAfterSeconds = 3.0F;
public bool canJump = true;
private float jumpRepeatTime = 0.05F;
private float jumpTimeout = 0.15F;
private float groundedTimeout = 0.25F;
// The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
private float lockCameraTimer = 0.0F;
// The current move direction in x-z
private Vector3 moveDirection = Vector3.zero;
// The current vertical speed
private float verticalSpeed = 0.0F;
// The current x-z move speed
private float moveSpeed = 0.0F;
// The last collision flags returned from controller.Move
private CollisionFlags collisionFlags;
// Are we jumping? (Initiated with jump button and not grounded yet)
private bool jumping = false;
private bool jumpingReachedApex = false;
// Are we moving backwards (This locks the camera to not do a 180 degree spin)
private bool movingBack = false;
// Is the user pressing any keys?
private bool isMoving = false;
// When did the user start walking (Used for going into trot after a while)
private float walkTimeStart = 0.0F;
// Last time the jump button was clicked down
private float lastJumpButtonTime = -10.0F;
// Last time we performed a jump
private float lastJumpTime = -1.0F;
// the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
private float lastJumpStartHeight = 0.0F;
private Vector3 inAirVelocity = Vector3.zero;
private float lastGroundedTime = 0.0F;
private bool isControllable = true;
// Use this for initialization
void Awake()
{
moveDirection = transform.TransformDirection(Vector3.forward);
_animation = GetComponent<Animation>();
if (!_animation)
Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
/*
public AnimationClip idleAnimation;
public AnimationClip walkAnimation;
public AnimationClip runAnimation;
public AnimationClip jumpPoseAnimation;
*/
if (!idleAnimation)
{
_animation = null;
Debug.Log("No idle animation found. Turning off animations.");
}
if (!walkAnimation)
{
_animation = null;
Debug.Log("No walk animation found. Turning off animations.");
}
if (!runAnimation)
{
_animation = null;
Debug.Log("No run animation found. Turning off animations.");
}
if (!jumpPoseAnimation && canJump) {
_animation = null;
Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");
}
}
void UpdateSmoothedMovementDirection()
{
Transform cameraTransform = Camera.main.transform;
bool grounded = IsGrounded();
// Forward vector relative to the camera along the x-z plane
Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
forward.y = 0;
forward = forward.normalized;
// Right vector relative to the camera
// Always orthogonal to the forward vector
Vector3 right = new Vector3(forward.z, 0, -forward.x);
float v = Input.GetAxisRaw("Vertical");
float h = Input.GetAxisRaw("Horizontal");
// Are we moving backwards or looking backwards
if (v < -0.2f)
movingBack = true;
else
movingBack = false;
bool wasMoving = isMoving;
isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
// Target direction relative to the camera
Vector3 targetDirection = h * right + v * forward;
// Grounded controls
if (grounded)
{
// Lock camera for short period when transitioning moving standing still
lockCameraTimer += Time.deltaTime;
if (isMoving != wasMoving)
lockCameraTimer = 0.0f;
// We store speed and direction seperately,
// so that when the character stands still we still have a valid forward direction
// moveDirection is always normalized, and we only update it if there is user input.
if (targetDirection != Vector3.zero)
{
// If we are really slow, just snap to the target direction
if (moveSpeed < walkSpeed * 0.9f && grounded)
{
moveDirection = targetDirection.normalized;
}
// Otherwise smoothly turn towards it
else
{
moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
moveDirection = moveDirection.normalized;
}
}
// Smooth the speed based on the current target direction
float curSmooth = speedSmoothing * Time.deltaTime;
// Choose target speed
//* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
_characterState = CharacterState.Idle;
// Pick speed modifier
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
targetSpeed *= runSpeed;
_characterState = CharacterState.Running;
}
else if (Time.time - trotAfterSeconds > walkTimeStart)
{
targetSpeed *= trotSpeed;
_characterState = CharacterState.Trotting;
}
else
{
targetSpeed *= walkSpeed;
_characterState = CharacterState.Walking;
}
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
// Reset walk time start when we slow down
if (moveSpeed < walkSpeed * 0.3f)
walkTimeStart = Time.time;
}
// In air controls
else
{
// Lock camera while in air
if (jumping)
lockCameraTimer = 0.0f;
if (isMoving)
inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
}
}
void ApplyJumping()
{
// Prevent jumping too fast after each other
if (lastJumpTime + jumpRepeatTime > Time.time)
return;
if (IsGrounded())
{
// Jump
// - Only when pressing the button down
// - With a timeout so you can press the button slightly before landing
if (canJump && Time.time < lastJumpButtonTime + jumpTimeout) {
verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
}
}
void ApplyGravity()
{
if (isControllable) // don't move player at all if not controllable.
{
// Apply gravity
bool jumpButton = Input.GetButton("Jump");
// When we reach the apex of the jump we send out a message
if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)
{
jumpingReachedApex = true;
SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
}
if (IsGrounded())
verticalSpeed = 0.0f;
else
verticalSpeed -= gravity * Time.deltaTime;
}
}
float CalculateJumpVerticalSpeed(float targetJumpHeight)
{
// From the jump height and gravity we deduce the upwards speed
// for the character to reach at the apex.
return Mathf.Sqrt(2 * targetJumpHeight * gravity);
}
void DidJump()
{
jumping = true;
jumpingReachedApex = false;
lastJumpTime = Time.time;
//lastJumpStartHeight = transform.position.y;
lastJumpButtonTime = -10;
_characterState = CharacterState.Jumping;
}
void Update()
{
if (!isControllable)
{
// kill all inputs if not controllable.
Input.ResetInputAxes();
}
if (Input.GetButtonDown("Jump"))
{
lastJumpButtonTime = Time.time;
}
UpdateSmoothedMovementDirection();
// Apply gravity
// - extra power jump modifies gravity
// - controlledDescent mode modifies gravity
ApplyGravity();
// Apply jumping logic
ApplyJumping();
// Calculate actual motion
Vector3 movement = moveDirection * moveSpeed + new Vector3(0, verticalSpeed, 0) + inAirVelocity;
movement *= Time.deltaTime;
// Move the controller
CharacterController controller = GetComponent<CharacterController>();
collisionFlags = controller.Move(movement);
// ANIMATION sector
if (_animation)
{
if (_characterState == CharacterState.Jumping)
{
if (!jumpingReachedApex)
{
_animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
_animation.CrossFade(jumpPoseAnimation.name);
}
else
{
_animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
_animation.CrossFade(jumpPoseAnimation.name);
}
}
else
{
if (controller.velocity.sqrMagnitude < 0.1f)
{
_animation.CrossFade(idleAnimation.name);
}
else
{
if (_characterState == CharacterState.Running)
{
_animation[runAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, runMaxAnimationSpeed);
_animation.CrossFade(runAnimation.name);
}
else if (_characterState == CharacterState.Trotting)
{
_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, trotMaxAnimationSpeed);
_animation.CrossFade(walkAnimation.name);
}
else if (_characterState == CharacterState.Walking)
{
_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, walkMaxAnimationSpeed);
_animation.CrossFade(walkAnimation.name);
}
}
}
}
// ANIMATION sector
// Set rotation to the move direction
if (IsGrounded())
{
transform.rotation = Quaternion.LookRotation(moveDirection);
}
else
{
Vector3 xzMove = movement;
xzMove.y = 0;
if (xzMove.sqrMagnitude > 0.001f)
{
transform.rotation = Quaternion.LookRotation(xzMove);
}
}
// We are in jump mode but just became grounded
if (IsGrounded())
{
lastGroundedTime = Time.time;
inAirVelocity = Vector3.zero;
if (jumping)
{
jumping = false;
SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
}
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
// Debug.DrawRay(hit.point, hit.normal);
if (hit.moveDirection.y > 0.01f)
return;
}
float GetSpeed()
{
return moveSpeed;
}
public bool IsJumping()
{
return jumping;
}
bool IsGrounded()
{
return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
}
Vector3 GetDirection()
{
return moveDirection;
}
public bool IsMovingBackwards()
{
return movingBack;
}
public float GetLockCameraTimer()
{
return lockCameraTimer;
}
bool IsMoving()
{
return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
}
bool HasJumpReachedApex()
{
return jumpingReachedApex;
}
bool IsGroundedWithTimeout()
{
return lastGroundedTime + groundedTimeout > Time.time;
}
void Reset()
{
gameObject.tag = "Player";
}
}
There are two lines under Update() that handle rotation:
transform.rotation = Quaternion.LookRotation(moveDirection);
and
transform.rotation = Quaternion.LookRotation(xzMove);
Follow the variables moveDirection and xzMove to see how the values are actually determined. It should be pretty straight forward. Hint: moveDirection located in the function UpdateSmoothedMovementDirection(). moveDirection is also used to calculate xzMove using movement. The rest I leave up to you.
Edit: The section of code that actually processes movement is inside UpdateSmoothedMovementDirection(). Look down to if (grounded) --> if (targetDirection != Vector3.zero). It is this area that processes movement. It is the if (targetDirection < walkSpeed * 0.9f && grounded) where movement snaps at 90 degrees. This is the intended function.
I swapped that code out with the code in the else statement, and at slow speeds the avatar moved more naturally. The avatar will only walk at 45 degrees when either both horizontal and vertical are toggled simultaneously. In other words, pressing up + left, or down + right, etc.
If you don't want the snap behavior, just use the code in the else statement and delete the conditional statements. Then your avatar will always turn smoothly.
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.