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.
Related
I am trying to make a grappling hook more fluent but as of right now it is very choppy and does not have the right feel. It currently makes a line and pulls the player there. I have not tried anything yet because I am not even sure we're to start on fixing this. Here is all the grappling code below. `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SFPSC_PlayerMovement))] // PlayerMovement also requires Rigidbody
public class SFPSC_GrapplingHook : MonoBehaviour
{
public bool IsGrappling
{
get { return isGrappling; }
}
private SFPSC_PlayerMovement pm;
private Rigidbody rb;
private int segments;
private void Start()
{
segments = rope.segments;
pm = this.GetComponent<SFPSC_PlayerMovement>();
rb = this.GetComponent<Rigidbody>();
}
private bool isGrappling = false;
private void Update()
{
if (crossHairSpinningPart != null)
{
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
goto _else;
crossHairSpinningPart.gameObject.SetActive(true);
crossHairSpinningPart.Rotate(Vector3.forward * crossHairSpinSpeed * Time.deltaTime);
goto _out;
}
}
_else:
crossHairSpinningPart.gameObject.SetActive(false);
}
_out:
if (!isGrappling)
{
if (Input.GetKeyDown(SFPSC_KeyManager.Grapple))
Grapple();
return;
}
else
{
if (!Input.GetKey(SFPSC_KeyManager.Grapple))
UnGrapple();
GrappleUpdate();
return;
}
}
[Header("Properties")]
public float maxGrappleDistance = 100.0f;
public SFPSC_Rope rope;
public float maximumSpeed = 100.0f;
public float deceleration = 2500.0f; // This is how much the player is going to decelerate after stopped grappling
public float deceleratingTime = 1.4f; // This is the time the decelerating is going to act on the player after stopped grappling
public RectTransform crossHairSpinningPart;
public float crossHairSpinSpeed = 200.0f;
public float distanceToStop = 2.0f;
public LayerMask layerMask;
public float grappleCooldown = 1.0f;
private bool isBlocked = false;
private Transform location; // the grappled location
private RaycastHit hitInfo;
private string hitName;
public void Grapple()
{
if (isBlocked)
return;
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
return;
// We create a GameObject and we parent it to the grappled object.
// If we don't parent it to the object and the object moves the player is stuck only on one location instead of the moving object.
location = new GameObject().transform;//Instantiate(new GameObject(), hitInfo.point, Quaternion.identity).transform;
location.position = hitInfo.point;
location.parent = hitInfo.collider.transform;
if (decelerateTimer != 0.0f)
StopCoroutine(Decelerate());
pm.DisableMovement();
// Rope attaching
rope.segments = (int)((hitInfo.distance / maxGrappleDistance) * segments);
rope.Grapple(transform.position, hitInfo.point);
rb.useGravity = false;
isGrappling = true;
}
}
}
private Vector3 grappleForce;
public void UnGrapple()
{
if (!isGrappling)
return;
if (location != null)
Destroy(location.gameObject);
if (decelerateTimer == 0.0f)
StartCoroutine(Decelerate());
else
decelerateTimer = 0.0f;
pm.EnableMovement();
// Rope detaching
rope.UnGrapple();
Invoke("UnblockGrapple", grappleCooldown);
rb.useGravity = true;
isGrappling = false;
}
private void UnblockGrapple()
{
isBlocked = false;
}
private float decelerateTimer = 0.0f, max;
private IEnumerator Decelerate()
{
WaitForEndOfFrame wfeof = new WaitForEndOfFrame();
max = deceleratingTime * Mathf.Clamp01(targetDistance / 10.0f) * Mathf.Clamp01(rb.velocity.magnitude / 30.0f);
for (; decelerateTimer < max; decelerateTimer += Time.deltaTime)
{
rb.AddForce(-rb.velocity.normalized * deceleration * (1.0f - decelerateTimer / max) * Mathf.Clamp01(rb.velocity.sqrMagnitude / 400.0f) * Time.deltaTime, ForceMode.Acceleration);
yield return wfeof;
}
decelerateTimer = 0.0f;
}
private Vector3 dir;
private float speed = 0.0f, targetDistance;
private void GrappleUpdate()
{
if (location == null)
return;
targetDistance = Vector3.Distance(transform.position, location.position);
rope.segments = (int)((targetDistance / maxGrappleDistance) * segments);
dir = (location.position - transform.position).normalized;
rb.velocity = Vector3.Lerp(rb.velocity, dir * maximumSpeed * Mathf.Clamp01(targetDistance / (4.0f * distanceToStop)), Time.deltaTime);
// Rope updating
rope.UpdateStart(transform.position);
rope.UpdateGrapple();
}
private Vector3 ClampMag(Vector3 vec, float maxMag)
{
if (vec.sqrMagnitude > maxMag * maxMag)
vec = vec.normalized * maxMag;
return vec;
}
}
`
Try using FixedUpdate instead of Update for physics based work (basically all of your code in Update right now). Update is dependent on your computer's clock speed and refresh rate (more or less), and gets called at fairly irregular intervals, because the next update is called in the next frame, after the present frame has finished processing. FixedUpdate makes it frame-rate independent.
Also, you can cap your framerate using Application.targetFrameRate and cap it to a decent FPS.
You could also multiply your movement with Time.deltaTime for smoother movement, although this is a standard practice and yet debatable for use as a smoothing value.
I have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}
I have a ThirdPersonController in the Hierarchy.
Then i'm adding to it a Nav Mesh Agent.
The dragging to it the scrip in c#.
Then in the Inspector i'm adding in the script part the agent.
Then baking.
And it was working more or less with some stuttering but was working.
The character ThirdPersonController walked automatic to specific point in this case to a cylinder i have on my Terrain.
I can't figure out why first time/s it's working fine but then now i have this error.
This is the script i'm using for the automatic walk:
using System.Collections;
using UnityStandardAssets.Characters.ThirdPerson;
public class BasicAi : MonoBehaviour {
public NavMeshAgent agent;
public ThirdPersonCharacter character;
public enum State {
PATROL,
CHASE
}
public State state;
private bool alive;
// Variables for patrolling
public GameObject[] waypoints;
private int waypointInd = 0;
public float patrolSpeed = 0.5f;
// Variable for chasing
public float chaseSpeed = 1f;
public GameObject target;
// Use this for initialization
void Start () {
agent = GetComponent<NavMeshAgent> ();
character = GetComponent<ThirdPersonCharacter>();
agent.updatePosition = true;
agent.updateRotation = false;
state = BasicAi.State.PATROL;
alive = true;
StartCoroutine ("FSM");
}
IEnumerator FSM()
{
while (alive)
{
switch (state)
{
case State.PATROL:
Patrol ();
break;
case State.CHASE:
Chase ();
break;
}
yield return null;
}
}
void Patrol()
{
agent.speed = patrolSpeed;
if (Vector3.Distance (this.transform.position, waypoints [waypointInd].transform.position) >= 2) {
agent.SetDestination (waypoints [waypointInd].transform.position);
character.Move (agent.desiredVelocity, false, false);
} else if (Vector3.Distance (this.transform.position, waypoints [waypointInd].transform.position) <= 2) {
waypointInd += 1;
if (waypointInd > waypoints.Length) {
waypointInd = 0;
}
}
else
{
character.Move (Vector3.zero, false, false);
}
}
void Chase()
{
agent.speed = chaseSpeed;
agent.SetDestination (target.transform.position);
character.Move (agent.desiredVelocity, false, false);
}
void OnTriggerEnter(Collider coll)
{
if (coll.tag == "Player")
{
state = BasicAi.State.CHASE;
target = coll.gameObject;
}
}
// Update is called once per frame
void Update () {
}
And the exception error message is:
NullReferenceException: Object reference not set to an instance of an object
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.CheckGroundStatus () (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:215)
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.Move (Vector3 move, Boolean crouch, Boolean jump) (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:54)
BasicAi.Patrol () (at Assets/My Scripts/BasicAi.cs:72)
BasicAi+<FSM>c__Iterator0.MoveNext () (at Assets/My Scripts/BasicAi.cs:55)
UnityEngine.MonoBehaviour:StartCoroutine(String)
BasicAi:Start() (at Assets/My Scripts/BasicAi.cs:43)
When i click on the error message in the Console window it's going to the ThirdPersonCharacter script. The script is from in Unity.
The error is on the line:
m_Animator.applyRootMotion = true;
And this is the ThirdPersonCharacter script where the error is:
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Animator))]
public class ThirdPersonCharacter : MonoBehaviour
{
[SerializeField] float m_MovingTurnSpeed = 360;
[SerializeField] float m_StationaryTurnSpeed = 180;
[SerializeField] float m_JumpPower = 12f;
[Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f;
[SerializeField] float m_RunCycleLegOffset = 0.2f; //specific to the character in sample assets, will need to be modified to work with others
[SerializeField] float m_MoveSpeedMultiplier = 1f;
[SerializeField] float m_AnimSpeedMultiplier = 1f;
[SerializeField] float m_GroundCheckDistance = 0.1f;
Rigidbody m_Rigidbody;
Animator m_Animator;
bool m_IsGrounded;
float m_OrigGroundCheckDistance;
const float k_Half = 0.5f;
float m_TurnAmount;
float m_ForwardAmount;
Vector3 m_GroundNormal;
float m_CapsuleHeight;
Vector3 m_CapsuleCenter;
CapsuleCollider m_Capsule;
bool m_Crouching;
void Start()
{
m_Animator = GetComponent<Animator>();
m_Rigidbody = GetComponent<Rigidbody>();
m_Capsule = GetComponent<CapsuleCollider>();
m_CapsuleHeight = m_Capsule.height;
m_CapsuleCenter = m_Capsule.center;
m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ;
m_OrigGroundCheckDistance = m_GroundCheckDistance;
}
public void Move(Vector3 move, bool crouch, bool jump)
{
// convert the world relative moveInput vector into a local-relative
// turn amount and forward amount required to head in the desired
// direction.
if (move.magnitude > 1f) move.Normalize();
move = transform.InverseTransformDirection(move);
CheckGroundStatus();
move = Vector3.ProjectOnPlane(move, m_GroundNormal);
m_TurnAmount = Mathf.Atan2(move.x, move.z);
m_ForwardAmount = move.z;
ApplyExtraTurnRotation();
// control and velocity handling is different when grounded and airborne:
if (m_IsGrounded)
{
HandleGroundedMovement(crouch, jump);
}
else
{
HandleAirborneMovement();
}
ScaleCapsuleForCrouching(crouch);
PreventStandingInLowHeadroom();
// send input and other state parameters to the animator
UpdateAnimator(move);
}
void ScaleCapsuleForCrouching(bool crouch)
{
if (m_IsGrounded && crouch)
{
if (m_Crouching) return;
m_Capsule.height = m_Capsule.height / 2f;
m_Capsule.center = m_Capsule.center / 2f;
m_Crouching = true;
}
else
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
return;
}
m_Capsule.height = m_CapsuleHeight;
m_Capsule.center = m_CapsuleCenter;
m_Crouching = false;
}
}
void PreventStandingInLowHeadroom()
{
// prevent standing up in crouch-only zones
if (!m_Crouching)
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
}
}
}
void UpdateAnimator(Vector3 move)
{
// update the animator parameters
m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);
m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);
m_Animator.SetBool("Crouch", m_Crouching);
m_Animator.SetBool("OnGround", m_IsGrounded);
if (!m_IsGrounded)
{
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);
}
// calculate which leg is behind, so as to leave that leg trailing in the jump animation
// (This code is reliant on the specific run cycle offset in our animations,
// and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5)
float runCycle =
Mathf.Repeat(
m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1);
float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount;
if (m_IsGrounded)
{
m_Animator.SetFloat("JumpLeg", jumpLeg);
}
// the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector,
// which affects the movement speed because of the root motion.
if (m_IsGrounded && move.magnitude > 0)
{
m_Animator.speed = m_AnimSpeedMultiplier;
}
else
{
// don't use that while airborne
m_Animator.speed = 1;
}
}
void HandleAirborneMovement()
{
// apply extra gravity from multiplier:
Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity;
m_Rigidbody.AddForce(extraGravityForce);
m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f;
}
void HandleGroundedMovement(bool crouch, bool jump)
{
// check whether conditions are right to allow a jump:
if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded"))
{
// jump!
m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z);
m_IsGrounded = false;
m_Animator.applyRootMotion = false;
m_GroundCheckDistance = 0.1f;
}
}
void ApplyExtraTurnRotation()
{
// help the character turn faster (this is in addition to root rotation in the animation)
float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);
transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0);
}
public void OnAnimatorMove()
{
// we implement this function to override the default root motion.
// this allows us to modify the positional speed before it's applied.
if (m_IsGrounded && Time.deltaTime > 0)
{
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime;
// we preserve the existing y part of the current velocity.
v.y = m_Rigidbody.velocity.y;
m_Rigidbody.velocity = v;
}
}
void CheckGroundStatus()
{
RaycastHit hitInfo;
#if UNITY_EDITOR
// helper to visualise the ground check ray in the scene view
Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));
#endif
// 0.1f is a small offset to start the ray from inside the character
// it is also good to note that the transform position in the sample assets is at the base of the character
if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
{
m_GroundNormal = hitInfo.normal;
m_IsGrounded = true;
m_Animator.applyRootMotion = true;
}
else
{
m_IsGrounded = false;
m_GroundNormal = Vector3.up;
m_Animator.applyRootMotion = false;
}
}
}
}
Here is a video of the problem:
https://pbs.twimg.com/tweet_video/CPmbdpMWIAAUesX.mp4
The issue is whenever the ship flies in the direction of its shield, it seems to stop at regular intervals.
Here's the code for the ship and the shield:
public class MoveOnAxis : MonoBehaviour {
public float speed = 1.0f;
public float defaultDrag = 0.0f;
public float maxDrag = 3.0f;
public float maxAngularDrag = 3.0f;
public float maxTumble = 720;
public Transform shield;
private float distance;
private float hAxis;
private float deadzone = 0.15f;
private float aVel;
private Rigidbody2D rb;
// Use this for initialization
void Start ()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update ()
{
hAxis = Input.GetAxis("Horizontal");
aVel = rb.angularVelocity;
if(Mathf.Abs(hAxis) > 0.15f) //Controlls ship rotation
{
if(aVel > 10.0f) //if spinning out after a crash clockwise
{
if(aVel <= maxTumble && hAxis > deadzone)
{
rb.angularVelocity -= hAxis*speed*2;
}
else if(aVel < maxTumble && hAxis < -deadzone)
{
rb.angularVelocity -= hAxis*speed*2;
}
else if(aVel > maxTumble && hAxis < -deadzone)
{
rb.angularVelocity = maxTumble;
}
}
else if (aVel < -10.0f) //if spinning out of a crash anti-clockwise
{
if(aVel >= -maxTumble && hAxis < -deadzone)
{
rb.angularVelocity -= hAxis*speed*2;
}
else if(aVel > -maxTumble && hAxis > deadzone)
{
rb.angularVelocity -= hAxis*speed*2;
}
else if(aVel < -maxTumble && hAxis > deadzone)
{
rb.angularVelocity = -maxTumble;
}
}
else //if no physics angular momentum go back to using transform for rotation
{
rb.angularVelocity = 0;
transform.Rotate(0.0f, 0.0f, hAxis*-speed);
}
}
if(Input.GetAxis("triggerAxis") > 0.1f) //Controlls ship accelleration and decelleration
{
//distance = Mathf.Abs((transform.position - shield.position).magnitude);
//if (distance <= 1.0f && distance >= 0.6f)
//{
rb.AddForce(transform.up * speed * Input.GetAxis("triggerAxis"));
//}
//else if (distance < 0.6f || distance > 1.0f)
//{
// rb.velocity = new Vector2(0f,0f);
//}
}
if(Input.GetAxis("triggerAxis") < -0.1f)
{
rb.drag = maxDrag*-Input.GetAxis("triggerAxis");
rb.angularDrag = maxAngularDrag*-Input.GetAxis("triggerAxis");
}
else
{
rb.drag = defaultDrag;
rb.angularDrag = defaultDrag;
}
}
}
and
public class ShieldMovement : MonoBehaviour {
public Transform target; //player shield is attaced to
public float circSpeed = 0.1f; // Speed Shield moves around ship
private Vector3 direction = Vector3.up;
private float distance = 0.8f; // distance from player so it doesn't clip
private float deadzone = 0.15f;
private float separation;
private bool canMove = true;
private Rigidbody2D tRB;
private Rigidbody2D rb;
// Use this for initialization
void Start ()
{
tRB = target.GetComponent<Rigidbody2D>();
rb = GetComponent<Rigidbody2D>();
}
void OnCollisionEnter2D (Collision2D other)
{
canMove = false;
}
void OnCollisionStay2d(Collision2D other)
{
canMove = false;
}
void OnCollisionExit2D (Collision2D other)
{
canMove = true;
}
// Update is called once per frame
void Update () {
float rh = Input.GetAxisRaw("rightH");
float rv = Input.GetAxisRaw("rightV");
separation = Mathf.Abs((transform.position - target.position).magnitude);
if(Mathf.Abs(rh) > deadzone || Mathf.Abs(rv) > deadzone)
{
Vector3 targetDir = new Vector3(rh, rv, 0.0f);
direction = Vector3.Slerp(transform.position-target.position, targetDir, circSpeed);
}
Ray ray = new Ray(target.position, direction); // cast ray in direction of point on circle shield is to go
if(canMove)
{
transform.position = ray.GetPoint(distance); //move shield to the ray as far as the distance
}
else if(!canMove)
{
tRB.velocity = new Vector2(0.0f, 0.0f);
//rb.velocity = new Vector2(0.0f, 0.0f);
transform.position = transform.position;
}
if(separation != distance) //If the shield get torn away from the ship
{
transform.position = ray.GetPoint(distance); //move shield to the ray as far as the distance
}
float angleY = transform.position.y - target.position.y;
float angleX = -(transform.position.x - target.position.x);
float angle = Mathf.Atan2 (angleY, angleX) * Mathf.Rad2Deg-90; //Get angle
if(Mathf.Abs(rh) > deadzone || Mathf.Abs(rv) > deadzone)
{
transform.rotation = Quaternion.AngleAxis(angle,Vector3.forward*-1); // keep shield facing outwards in respect to player
}
}
}
I've tried taking out the stuff that stops moving the ship like tRB.velocity = new Vector2(0.0f, 0.0f); but it didn't make any difference at all. Any ideas on what I need to do here?
Edit
Removing the collider from the shield fixes the problem so it seems like somehow the shield is colliding with the ship even though it doesn't look like it and the raycast should keep it far enough away from the ship. Not sure where to go from here.
For physics system updates, it's best practice to put them in FixedUpdate() instead of Update(). The former can be used to change the velocity or add forces, and the latter can be used for getting the input from the user.
To prevent the ship and shield from colliding, use a different tag on each one, then compare the other.collider.tag to ignore those collisions.
// e.g. in ShieldMovement
void OnCollisionEnter2D (Collision2D other)
{
if (other.collider.tag != "Ship") {
canMove = false;
}
}
It seems the shield was colliding with the ship somehow, once I put them on separate non interacting layers the problem went away :D
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.