using UnityEngine;
using System.Collections;
public class HeroCamera : MonoBehaviour
{
public LayerMask collisionLayers = -1;
public float heroHeight = 2.0f;
public float heroDistance = 5.0f;
public float minDistance = 2.0f;
public float maxDistance = 10.0f;
public int zoomRate = 200;
public float zoomDampening = 5.0f;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public bool invertMouseY = false;
public float rotationDampening = 3.0f;
public float offsetFromWall = 0.1f;
public float fpsCamDist = -0.15f;
public bool useIdleOrbit = true;
public enum CameraState
{
FirstPerson,
ThirdPerson,
Orbit
}
public CameraState camState = CameraState.ThirdPerson;
public Transform cam = null;
Transform hero;
public Transform headBone = null;
public int minAngleY = -50;
public int maxAngleY = 60;
float xAngl = 0.0f;
float yAngl = 0.0f;
float curDist;
float desDist;
float finalDist;
HeroCtrl hCtrl;
public CamInput camInput;
[System.Serializable]
public class CamInput
{
public float mX = 0;
public float mY = 0;
public float mSW = 0;
public bool doFPS = false;
public bool do3rdP = false;
public bool doOrbit = false;
public bool doLShift = false;
}
//=================================================================================================================o
void Start ()
{
hero = transform;
hCtrl = GetComponent <HeroCtrl>() as HeroCtrl;
Vector3 angls = cam.eulerAngles;
xAngl = angls.x;
yAngl = angls.y;
curDist = heroDistance;
desDist = heroDistance;
finalDist = heroDistance;
//cam.camera.nearClipPlane = 0.01f;
//Screen.lockCursor = true;
Screen.showCursor = false;
// if no headbone search for it
if (headBone == null) {
Transform[] bones = GetComponentsInChildren <Transform>() as Transform[];
foreach (Transform t in bones) {
if (t.name == "head")
headBone = t;
}
}
}
//=================================================================================================================o
void IdleOrbit ()
{
if(hCtrl == null)
return;
if(hCtrl.v == 0 && hCtrl.h == 0)
{
hCtrl.canRotate = false;
camState = CameraState.Orbit;
}
else
{
if(camState == CameraState.Orbit)
{
Quaternion r = Quaternion.Euler(0,cam.eulerAngles.y,0);
hero.rotation = Quaternion.Lerp(hero.rotation, r, Time.deltaTime * 15);
hCtrl.canRotate = true;
camState = CameraState.ThirdPerson;
}
}
}
//=================================================================================================================o
void LateUpdate ()
{
// Cached Input
camInput.doFPS = Input.GetKeyDown ("1");
camInput.doOrbit = Input.GetKeyDown ("2");
camInput.do3rdP = Input.GetKeyDown ("3");
camInput.doLShift = Input.GetKey (KeyCode.LeftShift);
camInput.mX = Input.GetAxis ("Mouse X");
camInput.mY = Input.GetAxis ("Mouse Y");
camInput.mSW = Input.GetAxis ("Mouse ScrollWheel");
// 1,2,3 buttons for switching camera modi
if (camInput.doFPS)
{
// FirstPerson
cam.camera.fieldOfView = 80.0f;
camState = CameraState.FirstPerson;
}
else if (camInput.doOrbit)
{
// Orbit
cam.camera.fieldOfView = 70.0f;
camState = CameraState.Orbit;
}
else if (camInput.do3rdP)
{
// ThirdPerson
cam.camera.fieldOfView = 70.0f;
camState = CameraState.ThirdPerson;
}
else if(useIdleOrbit)
{
IdleOrbit();
}
// Camera states
switch (camState)
{
case CameraState.FirstPerson:
FirstPerson();
break;
case CameraState.ThirdPerson:
ThirdPerson();
break;
case CameraState.Orbit:
Orbit();
break;
}
}
//=================================================================================================================o
void FirstPerson ()
{
// Horizontal
xAngl = hero.eulerAngles.y;
// Vertical
yAngl = ClampAngle (yAngl, minAngleY /1.5f, maxAngleY /1.1f);
// Desired distance
desDist = fpsCamDist;
// Camera rotation
Quaternion camRot = Quaternion.Euler (yAngl, xAngl, 0);
// Camera position
Vector3 camPos = headBone.position - (cam.forward * desDist) - (cam.up * -heroHeight /4);
// Apply Y-mouse axis
if(invertMouseY)
yAngl += Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
else
yAngl -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
// Apply position and rotation
cam.rotation = camRot;
cam.position = camPos;
}
//=================================================================================================================o
void ThirdPerson ()
{
// Desired distance via mouse wheel
desDist -= camInput.mSW * Time.deltaTime * zoomRate * Mathf.Abs (desDist);
desDist = Mathf.Clamp (desDist, minDistance, maxDistance);
finalDist = desDist;
// Horizontal smooth rotation
xAngl = Mathf.LerpAngle (cam.eulerAngles.y, hero.eulerAngles.y, rotationDampening * Time.deltaTime);
// Vertical angle limitation
yAngl = ClampAngle (yAngl, minAngleY, maxAngleY);
// Camera rotation
Quaternion camRot = Quaternion.Euler (yAngl, xAngl, 0);
// Camera height
Vector3 headPos = new Vector3 (0, -heroHeight /1.2f, 0);
// Camera position
Vector3 camPos = hero.position - (camRot * Vector3.forward * desDist + headPos);
// Recalculate hero position
Vector3 trueHeroPos = new Vector3 (hero.position.x, hero.position.y + heroHeight, hero.position.z);
// Check for collision with Linecast
RaycastHit hit;
bool isOk = false;
if ( Physics.Linecast (trueHeroPos, camPos - Vector3.up + Vector3.forward, out hit, collisionLayers.value)) // slightly behind and below the camera
{
// Final distance
finalDist = Vector3.Distance (trueHeroPos, hit.point) - offsetFromWall;
isOk = true;
}
// Lerp current distance if not corrected
if ( !isOk || ( finalDist > curDist ) )
curDist = Mathf.Lerp (curDist, finalDist, Time.deltaTime * zoomDampening);
else
curDist = finalDist;
// Clamp current distance
//curDist = Mathf.Clamp (curDist, minDistance, maxDistance);
// Recalculate camera position
camPos = hero.position - (camRot * Vector3.forward * curDist + headPos);
// Left shift = no y rotation
if(!camInput.doLShift)
{
// Apply Y-mouse axis
if(invertMouseY)
yAngl += Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
else
yAngl -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
}
// Apply position and rotation
cam.rotation = camRot;
cam.position = camPos;
}
//=================================================================================================================o
void Orbit ()
{
// Desired distance via mouse wheel
desDist -= camInput.mSW * Time.deltaTime * zoomRate * Mathf.Abs (desDist);
desDist = Mathf.Clamp (desDist, minDistance, maxDistance);
finalDist = desDist;
// Horizontal smooth rotation
xAngl += camInput.mX * xSpeed * 0.02f;
// Vertical angle limitation
yAngl = ClampAngle (yAngl, minAngleY, maxAngleY);
// Camera rotation
Quaternion camRot = Quaternion.Euler (yAngl, xAngl, 0);
// Camera height
Vector3 headPos = new Vector3 (0, -heroHeight /1.2f, 0);
// Camera position
Vector3 camPos = hero.position - (camRot * Vector3.forward * desDist + headPos);
// Recalculate hero position
Vector3 trueHeroPos = new Vector3 (hero.position.x, hero.position.y + heroHeight, hero.position.z);
// Check if there is something between camera and character
RaycastHit hit;
bool isOk = false;
if ( Physics.Linecast (trueHeroPos, camPos, out hit, collisionLayers.value))
{
// Final distance
finalDist = Vector3.Distance (trueHeroPos, hit.point) - offsetFromWall;
isOk = true;
}
// Lerp current distance if not corrected
if ( !isOk || ( finalDist > curDist ) )
curDist = Mathf.Lerp (curDist, finalDist, Time.deltaTime * zoomDampening);
else
curDist = finalDist;
// Clamp current distance
//curDist = Mathf.Clamp (curDist, minDistance, maxDistance);
// Recalculate camera position
camPos = hero.position - (camRot * Vector3.forward * curDist + headPos);
// Left shift = no y rotation
if(!camInput.doLShift)
{
// Apply Y-mouse axis
if(invertMouseY)
yAngl += Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
else
yAngl -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
}
// Apply position and rotation
cam.rotation = camRot;
cam.position = camPos;
}
//=================================================================================================================o
// Clamp angle at 360deg
static float ClampAngle ( float angle, float min, float max )
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp (angle, min, max);
}
//=================================================================================================================o
}
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
public class HeroCtrl : MonoBehaviour
{
public LayerMask groundLayers = -1;
public LayerMask wallRunLayers = -1;
public bool canRotate = true;
public float rotSpeed = 90.0f;
public float baseDrag = 0;
public float animSpeed = 1.2f;
public float moveSpeed = 1.5f;
public bool canJump = true;
public float jumpHeight = 4.0f;
public float groundedDistance = 1.5f; // from capsule center
public float setFloatDampTime = 0.15f;
// Double Tap ----------------------- //
public bool canEvade = true;
public float doubleTapSpeed = 0.3f; // Time between the taps frames*2
bool isDoubleTap = false;
bool is2Fwd = false;
bool is2Back = false;
bool is2Left = false;
bool is2Right = false;
// WallRun ----------------------- //
public bool canWallRun = true;
public enum BaseState
{
Base,
Climb,
Swim,
Fly,
Combat,
PhysX
}
public BaseState baseState = BaseState.Base;
public enum WeaponState
{
None,
Sword,
Bow,
Rifle,
Pistol,
//Unarmed,
//Throw
}
public WeaponState weaponState = WeaponState.None;
// Possible Weapon sets ----------------------- //
public Weapons weapons;
[System.Serializable]
public class Weapons
{
public GameObject sword_Hand = null;
public GameObject sword_Holster = null;
public GameObject bow_Hand = null;
public GameObject bow_Holster = null;
public GameObject bow_Quiver = null;
public GameObject rifle_Hand = null;
public GameObject rifle_Holster = null;
public GameObject pistol_Hand = null;
public GameObject pistol_Holster = null;
}
// Animator state hash
//static int JUMP_Tree = Animator.StringToHash("AirTree.JumpTree");
bool grounded;
public bool Grounded { get { return grounded; } }
protected Animator a;
Transform hero;
Rigidbody rb;
CapsuleCollider col;
RaycastHit groundHit;
bool canNextWeapon = true;
bool canDrawHolster = true;
bool climbUp = false; // Up or Down?
bool climbLong = false; // Short or Long?
bool isClimb = false;
// Cached Input or AI ----------------------- //
[HideInInspector]
public float h;
[HideInInspector]
public float v;
[HideInInspector]
public float mX;
bool doJumpDown;
//bool doJump;
bool doAtk1Down;
bool doAtk1;
bool doAtk2Down;
bool doAtk2;
bool doFwd;
bool doBack;
bool doLeft;
bool doRight;
bool doNextWeapon;
bool doCombat;
bool doFly;
bool doClimb;
bool doWalk;
bool doSprint;
bool doSneak;
bool doLShift;
bool doDance1;
bool doDance2;
bool doDance3;
bool doPullLever;
bool doPushButton;
bool doThrow;
AnimatorStateInfo st;
// Anim Controllers ----------------------- //
public AnimControllers animCtrl;
[System.Serializable]
public class AnimControllers
{
public RuntimeAnimatorController baseC;
public RuntimeAnimatorController climb;
public RuntimeAnimatorController swim;
public RuntimeAnimatorController fly;
}
// Climb ----------------------- //
public bool canClimb = true;
public LayerMask climbLayers = 1 << 9; // Layer 9
public float climbSpeed = 0.7f;
public float climbCheckRadius = 0.4f;
public float climbCheckDistance = 3.0f;
public float climbOffsetToWall = 0.2f;
public float heightOffsetToEdge = 2.12f;
public float smoothDistanceSpeed = 2.0f;
public float cornerSideOffset = 0.4f;
public enum ClimbState
{
Climb,
Top,
Corner,
Wall,
Area,
Edge,
None
}
public ClimbState climbState = ClimbState.None;
Transform curT = null;
Vector3 nextPoint;
Quaternion rot;
float cacheDist = 0.0f;
bool reset = false;
bool isTop = false;
bool isOverhang = false;
bool jumpOffNext = false;
float cornerReach = 0.4f;
// Swim ----------------------- //
public bool canSwim = true;
public float swimSpeed = 2.5f;
public float diveRotSpeed = 1.0f;
public float waterDrag = 1.4f;
public float offsetToSurf = 1.6f;
public Vector3 liftVector;
GameObject cam;
float distY = 0.0f;
float curAngle = 0.0f;
Transform[] bones;
// Fly ----------------------- //
public bool canFly = true;
public float flySpeed = 7.5f;
public float flyRotSpeed = 1.0f;
public float flyDrag = 1.4f;
float groundTime = 0;
// PhysX --------------------- //
public bool canRagdoll = true;
public float startRagTime = 0.4f;
public float endRagForce = 0.1f;
public bool startRagdoll = false;
public bool endRagdoll = false;
// IK Feet Placement --------------------- //
public bool useIdleFeetPlacement = true;
// Assign your character's root bone here ----------------- //
public Transform rootBone = null;
//=================================================================================================================o
void Start ()
{
a = GetComponent<Animator>();
col = GetComponent<CapsuleCollider>();
hero = GetComponent<Transform>();
rb = GetComponent<Rigidbody>();
a.speed = animSpeed;
if(rootBone == null)
Debug.Log ("Root Bone not assigned! - Please assign your character's root bone to the Root Bone field");
if (a.layerCount > 1)
{
a.SetLayerWeight(1, 1.0f); // Leg layer, IK feet placement
}
// col.center = new Vector3(0, 1, 0);
// col.height = 2.0f;
rb.mass = 3.0f;
rb.constraints = RigidbodyConstraints.FreezeRotation;
Physics.IgnoreLayerCollision(8,9); // ignore player / climb collision
// Climb
cacheDist = climbCheckDistance;
// Swim
cam = GameObject.FindGameObjectWithTag("MainCamera"); // Your characters's camera tag
bones = GetComponentsInChildren<Transform> () as Transform[];
foreach (Transform t in bones)
{
if(t.rigidbody && t.rigidbody != rb)
t.rigidbody.isKinematic = true;
if(t.collider && t.collider != hero.collider)
t.collider.isTrigger = true;
}
liftVector = new Vector3 (0, 2.0f, 0);
// Enable correct weapon setup
StartCoroutine(NextWeapon());
}
//=================================================================================================================o
void OnAnimatorMove ()
{
// Set up for a rigidbody - set the RB position equal to the animator deltaPosition and increase by MoveSpeed
if(baseState == BaseState.Base || baseState == BaseState.Combat)
{
rb.position += a.deltaPosition * moveSpeed;
hero.rotation *= a.deltaRotation;
}
}
//=================================================================================================================o
void FixedUpdate ()
{
// Grab Input each frame --- Handy for your custom input setting and AI
doLShift = Input.GetKey(KeyCode.LeftShift);
mX = doLShift ? 0 : Input.GetAxis("Mouse X"); // Mouse X is 0 if leftShift is held down
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
doJumpDown = Input.GetButtonDown("Jump");
//doJump = Input.GetButton("Jump");
doAtk1Down = Input.GetMouseButtonDown(0);
doAtk1 = Input.GetMouseButton(0);
doAtk2Down = Input.GetMouseButtonDown(1);
doAtk2 = Input.GetMouseButton(1);
doFwd = Input.GetKeyDown(KeyCode.W);
doBack = Input.GetKeyDown(KeyCode.S);
doLeft = Input.GetKeyDown(KeyCode.A);
doRight = Input.GetKeyDown(KeyCode.D);
doNextWeapon = Input.GetKeyDown(KeyCode.Q);
doCombat = Input.GetKeyDown(KeyCode.C);
doFly = Input.GetKeyDown(KeyCode.Z);
doClimb = Input.GetKeyDown(KeyCode.E);
doWalk = Input.GetKeyDown(KeyCode.X);
doSprint = Input.GetKeyDown(KeyCode.LeftShift);
doSneak = Input.GetKeyDown(KeyCode.V);
doDance1 = Input.GetKeyDown(KeyCode.H);
doDance2 = Input.GetKeyDown(KeyCode.J);
doDance3 = Input.GetKeyDown(KeyCode.K);
doPullLever = Input.GetKeyDown(KeyCode.L);
doPushButton = Input.GetKeyDown(KeyCode.P);
doThrow = Input.GetKeyDown(KeyCode.G);
if(a)
{
grounded = Physics.Raycast (hero.position + hero.up * col.center.y,
hero.up * -1, out groundHit, groundedDistance, groundLayers);
// Set Animator parameters
a.SetFloat ("AxisY", v, setFloatDampTime, Time.deltaTime);
a.SetFloat("MouseX", mX, setFloatDampTime * 4, Time.deltaTime);
a.SetFloat("AxisX", h, setFloatDampTime, Time.deltaTime);
a.SetBool("Grounded", grounded);
switch(baseState)
{
case BaseState.Base:
_Base();
break;
case BaseState.Climb:
_Climb();
break;
case BaseState.Swim:
_Swim();
break;
case BaseState.Fly:
_Fly();
break;
case BaseState.Combat:
_Combat();
break;
case BaseState.PhysX:
_PhysX();
break;
}
}
}
//=================================================================================================================o
void LateUpdate ()
{
// Procedural animation
if(canSwim && baseState == BaseState.Swim)
{
DiveRotation(diveRotSpeed);
}
else if(canFly && baseState == BaseState.Fly)
{
DiveRotation(flyRotSpeed);
}
}
//=================================================================================================================o
//================================================Base=============================================================o
//=================================================================================================================o
void _Base ()
{
// Next Weapon
if(doNextWeapon && canNextWeapon)
{
StartCoroutine(NextWeapon());
}
// Combat Stance / Out
else if(doCombat && canDrawHolster)
{
// Coroutine draw motion finished -> switch
if(weaponState == WeaponState.None)
{
return;
}
/*else if(weaponState == WeaponState.Unarmed)
{
return;
}*/
else if(weaponState == WeaponState.Sword)
{
StartCoroutine(DrawHolster(0.3f,weapons.sword_Hand.renderer, weapons.sword_Holster.renderer));
a.SetBool("Sword", true);
baseState = BaseState.Combat;
}
else if(weaponState == WeaponState.Bow)
{
StartCoroutine(DrawHolster(0.3f,weapons.bow_Hand.renderer, weapons.bow_Holster.renderer));
a.SetBool("Bow", true);
baseState = BaseState.Combat;
}
else if(weaponState == WeaponState.Rifle)
{
StartCoroutine(DrawHolster(0.3f,weapons.rifle_Hand.renderer, weapons.rifle_Holster.renderer));
a.SetBool("Rifle", true);
baseState = BaseState.Combat;
}
else if(weaponState == WeaponState.Pistol)
{
StartCoroutine(DrawHolster(0.2f,weapons.pistol_Hand.renderer, weapons.pistol_Holster.renderer));
a.SetBool("Pistol", true);
baseState = BaseState.Combat;
}
}
// Dance
else if(doDance1)
{
a.SetBool("Dance", true);
a.SetInteger("RandomM", 1);
}
else if(doDance2)
{
a.SetBool("Dance", true);
a.SetInteger("RandomM", 2);
}
else if(doDance3)
{
a.SetBool("Dance", true);
a.SetInteger("RandomM", 3);
}
// Pull Push
else if(doPullLever)
{
a.SetBool("Pull", true);
}
else if(doPushButton)
{
a.SetBool("Push", true);
}
// Throw
else if(doThrow)
{
a.SetBool("Throw", true);
}
// Fly
else if(doFly)
{
if(!canFly)
return;
rb.useGravity = false;
a.SetBool("Jump", true);
rb.velocity = Vector3.up*5; // Up in fast
// Fly State
if(animCtrl.fly)
{
a.runtimeAnimatorController = animCtrl.fly;
StartCoroutine (JustHolster());
baseState = BaseState.Fly;
}
}
// Climb
else if(doClimb)
{
if(!canClimb)
return;
CheckClimb();
if(climbState != ClimbState.None)
{
// Climb State
if(animCtrl.climb)
{
a.runtimeAnimatorController = animCtrl.climb;
StartCoroutine (JustHolster());
baseState = BaseState.Climb; // Out
}
}
}
// Double Tap - Evade takes tapSpeed & coolDown in seconds
if(canEvade)
{
if(!isDoubleTap) StartCoroutine (DoubleTap (doubleTapSpeed, 1));
}
// Start Radoll modus
if(canRagdoll)
{
// When falling for time
if(st.IsTag("Fall") && a.enabled)
{
float nTime = st.normalizedTime;
if(nTime > startRagTime && !a.IsInTransition(0))
{
StartRagdoll();
}
}
else if(startRagdoll) // Manual switch
{
StartRagdoll();
}
}
// Current state info for layer Base
st = a.GetCurrentAnimatorStateInfo(0);
if(grounded)
{
if(doJumpDown && canJump && !st.IsTag("Jump") && !st.IsTag("Land"))
{
a.SetBool("Jump", true);
//add extra force to main jump
if(!st.IsTag("LedgeJump"))
rb.velocity = hero.up * jumpHeight;
// Start cooldown until we can jump again
//StartCoroutine (JumpCoolDown(0.5f));
}
// Don't slide
if(!rb.isKinematic)
rb.velocity = new Vector3(0, rb.velocity.y, 0);
// Extra rotation
if(canRotate)
{
if(!doLShift)
hero.Rotate(0, mX * rotSpeed/2 * Time.deltaTime, 0);
}
// Punch, Kick if weapon state is not = None
if(weaponState != WeaponState.None)
{
if(doAtk1Down && !st.IsName("PunchCombo.Punch1"))
{
a.SetBool("Attack1", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
else if(doAtk2Down && !st.IsName("KickCombo.Kick1"))
{
a.SetBool("Attack2", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
}
// Walk
if(doWalk)
{
if(!st.IsName("WalkTree.TreeW"))
{
a.SetBool("Walking", true);
a.SetBool("Sneaking", false); // RESET
a.SetBool("Sprinting", false); // RESET
}
else
{
a.SetBool("Walking", false); // RESET
}
}
// Sprint
else if(doSprint)
{
if(!st.IsName("SprintTree.TreeS"))
{
a.SetBool("Sprinting", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
else
{
a.SetBool("Sprinting", false); // RESET
}
}
// Sneak
else if(doSneak)
{
if(!st.IsName("SneakTree.TreeSn"))
{
a.SetBool("Sneaking", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
}
else
{
a.SetBool("Sneaking", false); // RESET
}
}
WallGround ();
// Balanceing trigger
if(groundHit.transform && groundHit.transform.gameObject.layer == 9)
{
// Layer 9 should be Climb
a.SetBool("Balancing", true);
}
else
a.SetBool("Balancing", false); // RESET
// -----------AirTime--------- //
if(!a.GetBool("CanLand")) // Very short air time
{
groundTime += Time.deltaTime;
if(groundTime >= 0.4f)
{
a.SetBool("CanLand", true);
}
}
else
groundTime = 0;
// -----------AirTime--------- //
}
else // In Air
{
// -----------AirTime--------- //
if(groundTime <= 0.3f)
{
groundTime += Time.deltaTime;
if(groundTime >= 0.2f)
{
a.SetBool("CanLand", true);
}
else
a.SetBool("CanLand", false);
}
// -----------AirTime--------- //
if(canRotate)
hero.Rotate(0, mX * rotSpeed/2 * Time.deltaTime, 0);
WallRun();
// After jumping off from climb state controller
if(jumpOffNext)
{
a.SetBool("Jump", true);
jumpOffNext = false;
}
}
// Resetting--------------------------------------------------
if(!a.IsInTransition(0))
{
if(st.IsTag("Jump") || st.IsTag("LedgeJump"))
{
// Reset our parameter to avoid looping
a.SetBool("Jump", false); // RESET LedgeJump
}
else if(st.IsTag("Dance"))
{
a.SetBool("Dance", false);
}
else if(st.IsTag("Action"))
{
a.SetBool("Pull", false);
a.SetBool("Push", false);
a.SetBool("Throw", false);
}
else if(st.IsTag("StandUp"))
{
a.SetInteger("RandomM", 3); // 0 or 1 are triggers
a.SetBool("StandUp", false); // RESET
}
if(st.IsName("PunchCombo.Punch1") && st.normalizedTime > 0.7f && !doAtk1)
{
// Reset our parameter to avoid looping
a.SetBool("Attack1", false); // RESET
}
else if(st.IsName("PunchCombo.Punch2"))
{
// Reset our parameter to avoid looping
a.SetBool("Attack1", false); // RESET
}
if(st.IsName("KickCombo.Kick1") && st.normalizedTime > 0.7f && !doAtk2)
{
// Reset our parameter to avoid looping
a.SetBool("Attack2", false); // RESET
}
else if(st.IsName("KickCombo.Kick2"))
{
// Reset our parameter to avoid looping
a.SetBool("Attack2", false); // RESET
}
if(st.IsTag("Evade"))
{
// Reset our parameter to avoid looping
a.SetBool("Evade_F", false); // RESET
a.SetBool("Evade_B", false); // RESET
a.SetBool("Evade_L", false); // RESET
a.SetBool("Evade_R", false); // RESET
}
if(st.IsTag("WallRun") && grounded) // No instant reset
{
a.SetBool("WallRunL", false); // RESET
a.SetBool("WallRunR", false); // RESET
a.SetBool("WallRunUp", false); // RESET
}
}
}
//=================================================================================================================o
// Facing a low object to jump over or a wall
void WallGround ()
{
if(v < 0.5f)
{
a.SetBool("WallLow", false); // RESET
a.SetBool("WallHigh", false); // RESET
return;
}
// roughly 2/3 of the characters height
bool wallHigh = Physics.Raycast (hero.position + hero.up*1.4f,
hero.forward, col.radius*1.6f, wallRunLayers);
// roughly the half of the characters height
bool wallLow = Physics.Raycast (hero.position + hero.up,
hero.forward, col.radius*1.5f, wallRunLayers)
&& !wallHigh;
if(doJumpDown)
{
a.SetInteger("RandomM", Random.Range(0,3));
}
a.SetBool("WallLow", wallLow);
a.SetBool("WallHigh", wallHigh);
}
//=================================================================================================================o
// Jump to a wall and run on it (Air)
void WallRun ()
{
if(!canWallRun) return;
if(v < 0.2f) return;
bool leftW = Physics.Raycast (hero.position + hero.up,
hero.right * -1 + hero.forward/4, col.radius + 0.2f, wallRunLayers);
bool rightW = Physics.Raycast (hero.position + hero.up,
hero.right + hero.forward/4, col.radius + 0.2f, wallRunLayers);
bool frontW = Physics.Raycast (hero.position+ hero.up,
hero.forward, col.radius + 0.2f, wallRunLayers);
if(!a.IsInTransition(0) && !rb.isKinematic)
{
if(leftW)
{
rb.velocity = hero.forward * Mathf.Abs(v) + hero.up*3;
if(!st.IsName("WallRun.RunL"))
{
a.SetBool("WallRunL", true);
StartCoroutine( WallRunCoolDown(3f)); // Exclude if not needed
}
if(doJumpDown)
{
a.SetBool("Jump", true);
}
}
else if(rightW)
{
rb.velocity = hero.forward * Mathf.Abs(v) + hero.up*3;
if(!st.IsName("WallRun.RunR"))
{
a.SetBool("WallRunR", true);
StartCoroutine( WallRunCoolDown(3f)); // Exclude if not needed
}
if(doJumpDown)
{
a.SetBool("Jump", true);
}
}
else if(frontW)
{
rb.velocity = hero.forward/2;
if(!st.IsName("WallRun.RunUp"))
{
a.SetBool("WallRunUp", true);
StartCoroutine( WallRunCoolDown(3f)); // Exclude if not needed
}
if(doJumpDown)
{
a.SetBool("Jump", true);
}
}
a.SetBool("WallRunL", leftW);
a.SetBool("WallRunR", rightW);
a.SetBool("WallRunUp", frontW);
}
}
//=================================================================================================================o
// Jump cool-down
IEnumerator JumpCoolDown (float sec)
{
canJump = true;
yield return new WaitForSeconds (sec);
canJump = false;
yield return new WaitForSeconds (sec);
canJump = true;
}
//=================================================================================================================o
// Wall run cool-down
IEnumerator WallRunCoolDown (float sec)
{
canWallRun = true;
yield return new WaitForSeconds (sec/4);
canWallRun = false;
a.SetBool("WallRunL", false); // RESET
a.SetBool("WallRunR", false); // RESET
a.SetBool("WallRunUp", false); // RESET
yield return new WaitForSeconds (sec);
canWallRun = true;
}
//=================================================================================================================o
IEnumerator JustHolster ()
{
// Holster now
if(weaponState == WeaponState.None)
{
yield break;
}
/*else if(weaponState == WeaponState.Unarmed)
{
yield break;
}*/
else if(weaponState == WeaponState.Sword)
{
yield return StartCoroutine(DrawHolster(0.3f,weapons.sword_Holster.renderer, weapons.sword_Hand.renderer));
a.SetBool("Sword", false);
yield break;
}
else if(weaponState == WeaponState.Bow)
{
yield return StartCoroutine(DrawHolster(0.6f,weapons.bow_Holster.renderer, weapons.bow_Hand.renderer));
a.SetBool("Bow", false);
yield break;
}
else if(weaponState == WeaponState.Rifle)
{
yield return StartCoroutine(DrawHolster(0.9f,weapons.rifle_Holster.renderer, weapons.rifle_Hand.renderer));
a.SetBool("Rifle", false);
yield break;
}
else if(weaponState == WeaponState.Pistol)
{
yield return StartCoroutine(DrawHolster(0.6f,weapons.pistol_Holster.renderer, weapons.pistol_Hand.renderer));
a.SetBool("Pistol", false);
yield break;
}
}
//=================================================================================================================o
IEnumerator DrawHolster (float sec, Renderer on, Renderer off)
{
canDrawHolster = false;
yield return new WaitForSeconds (sec);
on.enabled = true;
off.enabled = false;
yield return new WaitForSeconds (2); // Cool down
canDrawHolster = true;
}
//=================================================================================================================o
// Next Weapon + cool-down
IEnumerator NextWeapon ()
{
if(baseState == BaseState.Base)
{
canNextWeapon = false;
weaponState += 1; // Next
if(weaponState == WeaponState.Sword) // Skip if no weapon is assigned
{
if(weapons.sword_Holster == null) // No weapon - next
{
weaponState += 1;
}
else
{
if(weapons.sword_Hand) weapons.sword_Hand.renderer.enabled = false;
if(weapons.sword_Holster) weapons.sword_Holster.renderer.enabled = true; // On
if(weapons.bow_Hand) weapons.bow_Hand.renderer.enabled = false;
if(weapons.bow_Holster) weapons.bow_Holster.renderer.enabled = false;
if(weapons.bow_Quiver) weapons.bow_Quiver.renderer.enabled = false;
if(weapons.rifle_Hand) weapons.rifle_Hand.renderer.enabled = false;
if(weapons.rifle_Holster) weapons.rifle_Holster.renderer.enabled = false;
if(weapons.pistol_Hand) weapons.pistol_Hand.renderer.enabled = false;
if(weapons.pistol_Holster) weapons.pistol_Holster.renderer.enabled = false;
}
}
if(weaponState == WeaponState.Bow) // Skip if no weapon is assigned
{
if(weapons.bow_Holster == null) // No weapon - next
{
weaponState += 1;
}
else
{
if(weapons.sword_Hand) weapons.sword_Hand.renderer.enabled = false;
if(weapons.sword_Holster) weapons.sword_Holster.renderer.enabled = false;
if(weapons.bow_Hand) weapons.bow_Hand.renderer.enabled = false;
if(weapons.bow_Holster) weapons.bow_Holster.renderer.enabled = true; // On
if(weapons.bow_Quiver) weapons.bow_Quiver.renderer.enabled = true; // On
if(weapons.rifle_Hand) weapons.rifle_Hand.renderer.enabled = false;
if(weapons.rifle_Holster) weapons.rifle_Holster.renderer.enabled = false;
if(weapons.pistol_Hand) weapons.pistol_Hand.renderer.enabled = false;
if(weapons.pistol_Holster) weapons.pistol_Holster.renderer.enabled = false;
}
}
if(weaponState == WeaponState.Rifle) // Skip if no weapon is assigned
{
if(weapons.rifle_Holster == null) // No weapon - next
{
weaponState += 1;
}
else
{
if(weapons.sword_Hand) weapons.sword_Hand.renderer.enabled = false;
if(weapons.sword_Holster) weapons.sword_Holster.renderer.enabled = false;
if(weapons.bow_Hand) weapons.bow_Hand.renderer.enabled = false;
if(weapons.bow_Holster) weapons.bow_Holster.renderer.enabled = false;
if(weapons.bow_Quiver) weapons.bow_Quiver.renderer.enabled = false;
if(weapons.rifle_Hand) weapons.rifle_Hand.renderer.enabled = false;
if(weapons.rifle_Holster) weapons.rifle_Holster.renderer.enabled = true; // On
if(weapons.pistol_Hand) weapons.pistol_Hand.renderer.enabled = false;
if(weapons.pistol_Holster) weapons.pistol_Holster.renderer.enabled = false;
}
}
if(weaponState == WeaponState.Pistol) // Skip if no weapon is assigned
{
if(weapons.pistol_Holster == null) // No weapon - next
{
weaponState += 1;
}
else
{
if(weapons.sword_Hand) weapons.sword_Hand.renderer.enabled = false;
if(weapons.sword_Holster) weapons.sword_Holster.renderer.enabled = false;
if(weapons.bow_Hand) weapons.bow_Hand.renderer.enabled = false;
if(weapons.bow_Holster) weapons.bow_Holster.renderer.enabled = false;
if(weapons.bow_Quiver) weapons.bow_Quiver.renderer.enabled = false;
if(weapons.rifle_Hand) weapons.rifle_Hand.renderer.enabled = false;
if(weapons.rifle_Holster) weapons.rifle_Holster.renderer.enabled = false;
if(weapons.pistol_Hand) weapons.pistol_Hand.renderer.enabled = false;
if(weapons.pistol_Holster) weapons.pistol_Holster.renderer.enabled = true; // On
}
}
// No weapon
if(weaponState == WeaponState.None /*|| weaponState == WeaponState.Unarmed*/)
{
if(weapons.sword_Hand) weapons.sword_Hand.renderer.enabled = false;
if(weapons.sword_Holster) weapons.sword_Holster.renderer.enabled = false;
if(weapons.bow_Hand) weapons.bow_Hand.renderer.enabled = false;
if(weapons.bow_Holster) weapons.bow_Holster.renderer.enabled = false;
if(weapons.bow_Quiver) weapons.bow_Quiver.renderer.enabled = false;
if(weapons.rifle_Hand) weapons.rifle_Hand.renderer.enabled = false;
if(weapons.rifle_Holster) weapons.rifle_Holster.renderer.enabled = false;
if(weapons.pistol_Hand) weapons.pistol_Hand.renderer.enabled = false;
if(weapons.pistol_Holster) weapons.pistol_Holster.renderer.enabled = false;
}
// Last in the enum
if (weaponState > (WeaponState)System.Enum.GetValues(typeof(WeaponState)).Length-1)
weaponState = WeaponState.None; // Start at the first again
yield return new WaitForSeconds(0.3f); // Cool-down
canNextWeapon = true;
}
}
//=================================================================================================================o
IEnumerator DoubleForward (float dTapSpeed, float coolDown)
{
is2Fwd = true;
float t = 0;
yield return new WaitForSeconds(0.1f);
while (t < dTapSpeed)
{
t += Time.deltaTime;
yield return new WaitForSeconds(0.01f); // jitter stabilizing
if(doFwd)
{
a.SetBool("Evade_F", true);
yield return new WaitForSeconds(coolDown);
is2Fwd = false;
yield break;
}
}
is2Fwd = false;
}
IEnumerator DoubleBack (float dTapSpeed, float coolDown)
{
is2Back = true;
float t = 0;
yield return new WaitForSeconds(0.1f);
while (t < dTapSpeed)
{
t += Time.deltaTime;
yield return new WaitForSeconds(0.01f); // jitter stabilizing
if(doBack)
{
a.SetBool("Evade_B", true);
yield return new WaitForSeconds(coolDown);
is2Back = false;
yield break;
}
}
is2Back = false;
}
IEnumerator DoubleLeft (float dTapSpeed, float coolDown)
{
is2Left = true;
float t = 0;
yield return new WaitForSeconds(0.1f);
while (t < dTapSpeed)
{
t += Time.deltaTime;
yield return new WaitForSeconds(0.01f); // jitter stabilizing
if(doLeft)
{
a.SetBool("Evade_L", true);
yield return new WaitForSeconds(coolDown);
is2Left = false;
yield break;
}
}
is2Left = false;
}
IEnumerator DoubleRight (float dTapSpeed, float coolDown)
{
is2Right = true;
float t = 0;
yield return new WaitForSeconds(0.1f);
while (t < dTapSpeed)
{
t += Time.deltaTime;
yield return new WaitForSeconds(0.01f); // jitter stabilizing
if(doRight)
{
a.SetBool("Evade_R", true);
yield return new WaitForSeconds(coolDown);
is2Right = false;
yield break;
}
}
is2Right = false;
}
//=================================================================================================================o
IEnumerator DoubleTap (float tapSpeed, float coolDown)
{
isDoubleTap = true;
if(doFwd && !is2Fwd)
yield return StartCoroutine (DoubleForward (tapSpeed, coolDown));
else if(doBack && !is2Back)
yield return StartCoroutine (DoubleBack (tapSpeed, coolDown));
else if(doLeft && !is2Left)
yield return StartCoroutine (DoubleLeft (tapSpeed, coolDown));
else if(doRight && !is2Right)
yield return StartCoroutine (DoubleRight (tapSpeed, coolDown));
isDoubleTap = false;
}
//=================================================================================================================o
//==================================================Climb==========================================================o
//=================================================================================================================o
void _Climb ()
{
// Current state info for layer Base
st = a.GetCurrentAnimatorStateInfo(0);
// Switch climb states
switch (climbState)
{
case ClimbState.None:
if (!reset)
{
ExitClimb ();
}
CheckClimb();
break;
case ClimbState.Climb:
ClimbTo();
reset = false;
break;
case ClimbState.Top:
isTop = false;
PullUp();
reset = false;
break;
case ClimbState.Wall:
WallClimb();
reset = false;
break;
case ClimbState.Area:
ClimbTo();
reset = false;
break;
case ClimbState.Edge:
EdgeClimb();
reset = false;
break;
case ClimbState.Corner:
CornerLerp(climbSpeed);
reset = false;
break;
}
if(doJumpDown)
{
if(v != 0)
jumpOffNext = true;
else
jumpOffNext = false;
climbState = ClimbState.None;
}
if(!a.IsInTransition(0))
{
// Resetting--------------------------------------------------
if(st.IsTag("Jump"))
{
// Reset our parameter to avoid looping
a.SetBool("Jump", false); // RESET
}
if(st.IsTag("Oh"))
{
a.SetBool("Overhang", false); // RESET
isOverhang = false;
}
}
}
//=================================================================================================================o
// Look for objects to climb
void CheckClimb ()
{
if(!canClimb)
return;
if(doClimb && v > 0.2) // Up
{
// Reduce check distance/height while falling
if (!grounded && baseState == BaseState.Base)
{
climbCheckDistance = 0.4f;
}
else
climbCheckDistance = cacheDist;
Vector3 p1 = hero.position - (hero.up * -heightOffsetToEdge) + hero.forward;
Vector3 p2 = hero.position - (hero.up * -heightOffsetToEdge) - hero.forward;
RaycastHit hit;
// Hit nothing and not at edge -> Out
if(!Physics.CapsuleCast (p1, p2, climbCheckRadius, hero.up, out hit, climbCheckDistance, climbLayers))
return;
// If not almost facing the edge cancel/Out
if (Vector3.Angle (hero.right, hit.transform.right) >= 60.0f)
return;
if(isTop)
return;
if(curT != hit.transform)
{
curT = hit.transform;
nextPoint = hit.point;
}
hero.rotation = Quaternion.Euler(curT.eulerAngles.x, curT.eulerAngles.y, 0);
a.SetInteger("RandomM", Random.Range(0,3));
if(curT.name == "climbObject")
{
rb.isKinematic = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectOh")
{
rb.isKinematic = true;
isOverhang = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectTop")
{
rb.isKinematic = true;
isTop = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectTopOh")
{
rb.isKinematic = true;
isTop = true;
isOverhang = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbWall")
{
rb.isKinematic = true;
isTop = true;
if(grounded)
climbState = ClimbState.Wall;
else
climbState = ClimbState.Area;
}
else if(curT.name == "climbArea")
{
rb.isKinematic = true;
climbState = ClimbState.Area;
}
else
{
climbState = ClimbState.None;
}
isClimb = true;
// UP
climbUp = true;
// Long or Short ?
climbLong = hit.distance > 1 ? true : false;
}
else if(doClimb && v < -0.2) // Down
{
// Reduce check distance/height while falling
if (!grounded && baseState == BaseState.Base)
{
climbCheckDistance = 0.4f;
}
else
climbCheckDistance = cacheDist;
Vector3 p1 = hero.position - (hero.up * -heightOffsetToEdge) + hero.forward;
Vector3 p2 = hero.position - (hero.up * -heightOffsetToEdge) - hero.forward;
RaycastHit hit;
// Hit nothing and not at edge -> Out
if(!Physics.CapsuleCast (p1, p2, climbCheckRadius, -hero.up, out hit, climbCheckDistance, climbLayers))
return;
// If not almost facing the edge cancel/Out
if (Vector3.Angle (hero.right, hit.transform.right) >= 60.0f)
return;
if(curT != hit.transform)
{
curT = hit.transform;
nextPoint = hit.point;
}
hero.rotation = Quaternion.Euler(0,curT.eulerAngles.y,0);
isTop = false; // RESET
a.SetBool("Top", false); // RESET
a.SetInteger("RandomM", Random.Range(0,3));
if(curT.name == "climbObject")
{
rb.isKinematic = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectOh")
{
rb.isKinematic = true;
isOverhang = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectTop")
{
rb.isKinematic = true;
isTop = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbObjectTopOh")
{
rb.isKinematic = true;
isTop = true;
isOverhang = true;
climbState = ClimbState.Climb;
}
else if(curT.name == "climbWall")
{
climbState = ClimbState.None;
}
else if(curT.name == "climbArea")
{
rb.isKinematic = true;
climbState = ClimbState.Area;
}
else
{
climbState = ClimbState.None;
}
isClimb = true;
// Down
climbUp = false;
// Long or Short ?
climbLong = hit.distance > 1 ? true : false;
//print ("Down");
}
}
//=================================================================================================================o
// Reached the top, PullUp
void PullUp ()
{
if(st.IsTag("Top") && !a.IsInTransition(0))
{
float nT = st.normalizedTime;
if (nT <= 1.0f)
{
if(nT <= 0.4f) // Step up
{
hero.Translate(Vector3.up * Time.deltaTime * climbSpeed *8.0f);
}
else // Step forward
{
if(nT <= 0.6f)
hero.Translate(Vector3.forward * Time.deltaTime * climbSpeed *2.5f);
else if(nT >= 0.6f && rb.isKinematic) // fall early
rb.isKinematic = false;
if(!rb.isKinematic)
rb.velocity = new Vector3(0, rb.velocity.y, 0);
}
}
else // Animation is finished
climbState = ClimbState.None; // Out
}
}
//=================================================================================================================o
void EdgeClimb ()
{
Vector3 relPoint = curT.InverseTransformPoint (hero.position);
MatchP(Mathf.Abs(relPoint.z), climbOffsetToWall, Vector3.forward, Vector3.back);
MatchP(Mathf.Abs(relPoint.y), heightOffsetToEdge, Vector3.up, Vector3.down);
if(isOverhang)
{
a.SetBool("Overhang", true);
}
if(!st.IsTag("Oh") && !a.IsInTransition(0)) // not if overhanging
{
// Horizontal climbing
if(h != 0.0f)
{
Vector3 origin = hero.position - (hero.forward * -climbOffsetToWall) - (hero.up * -heightOffsetToEdge);
if(h > 0.0f) // Check right
{
RaycastHit hit;
if(Physics.SphereCast (origin, 0.2f, hero.right, out hit, 0.4f, climbLayers))
{
nextPoint = hit.point;
curT = hit.transform;
a.SetBool("CornerR", true);
cornerReach = 0.4f;
climbState = ClimbState.Corner; // Out
}
// Edge speed interval, min - max speed.
float edgeSpeed = st.normalizedTime % 1 < 0.5f ? 0.9f : 0.3f;
// Apply
hero.Translate( 1 * Time.deltaTime * edgeSpeed, 0, 0, curT );
}
else // Check left
{
RaycastHit hit;
if(Physics.SphereCast (origin, 0.2f, -hero.right, out hit, 0.4f, climbLayers))
{
nextPoint = hit.point;
curT = hit.transform;
a.SetBool("CornerL", true);
cornerReach = -0.4f;
climbState = ClimbState.Corner; // Out
}
// Edge speed interval, min - max speed.
float edgeSpeed = st.normalizedTime % 1 < 0.5f ? 0.3f : 0.9f;
// Apply
hero.Translate( -1 * Time.deltaTime * edgeSpeed, 0, 0, curT );
}
}
// Top
if(isTop && doClimb)
{
a.SetBool("Top", true);
climbState = ClimbState.Top; // Out
}
}
// Right End
if (relPoint.x >= 0.44f)
{
a.SetBool("HangR", true);
if (relPoint.x >= 0.5f)
climbState = ClimbState.None; // Out
}
// Left End
else if (relPoint.x <= -0.44f)
{
a.SetBool("HangL", true);
if (relPoint.x <= -0.5f)
climbState = ClimbState.None; // Out
}
else
{
a.SetBool("HangL", false); // RESET
a.SetBool("HangR", false); // RESET
// While not hanging, preparing or climbing
if((!st.IsTag("Pre") || !st.IsTag("Climb")) && !a.IsInTransition(0))
CheckClimb(); // Find next
}
}
//=================================================================================================================o
void WallClimb ()
{
if(grounded && v < 0) // On ground and going down
{
climbState = ClimbState.None;
}
Vector3 relV = curT.InverseTransformPoint(hero.position);
Vector3 inputVec = hero.position;
float distY = Mathf.Abs(relV.y);
float distZ = Mathf.Abs(relV.z);
a.SetBool("WallClimb", true);
// Limit is almost the edge
if (distY >= heightOffsetToEdge * 1.1f)
{
MatchP (distZ, climbOffsetToWall, Vector3.forward, Vector3.back);
// Input
if (v != 0.0f)
{
if (v > 0.0f)
{
inputVec = hero.TransformPoint( Vector3.up );
}
else
{
inputVec = hero.TransformPoint( Vector3.down );
if (!Physics.Raycast(hero.position, hero.forward, climbOffsetToWall, climbLayers) || grounded)
{
climbState = ClimbState.None;
}
}
// Apply movement
hero.position = Vector3.Lerp( hero.position, inputVec, Time.deltaTime * climbSpeed * 1.3f);
}
else // Stop if there's no input
{
inputVec = hero.TransformPoint( Vector3.zero );
}
}
else // Reached the edge
{
a.SetBool("WallClimb", false); // RESET
climbState = ClimbState.Edge; // Out
}
}
//=================================================================================================================o
void ClimbTo ()
{
// When entering Climb state
if(isClimb)
{
if(climbUp)
{
if(climbLong)
a.SetBool("Climb", true);
else
a.SetBool("ClimbShort", true);
isClimb = false;
}
else
{
if(climbLong)
a.SetBool("ClimbDown", true);
else
a.SetBool("ClimbDownShort", true);
isClimb = false;
}
}
if(grounded && v < 0) // On ground and going down
{
climbState = ClimbState.None;
}
if(!st.IsTag("Pre"))
{
Vector3 edgePos = nextPoint + ((hero.up * -heightOffsetToEdge) - (hero.forward * climbOffsetToWall));
// Apply lift up
hero.position = Vector3.Lerp (hero.position, edgePos, Time.deltaTime * climbSpeed * 11);
if(Vector3.Distance (hero.position, edgePos) <= 0.1f)
{
a.SetBool("Climb", false); // RESET
a.SetBool("ClimbShort", false); // RESET
a.SetBool("ClimbDown", false); // RESET
a.SetBool("ClimbDownShort", false); // RESET
if(isOverhang)
a.SetBool("Overhang", true);
if(curT.name == "climbArea" || curT.name == "climbWall")
{
climbState = ClimbState.Wall; // Out
}
else
climbState = ClimbState.Edge; // Out
}
}
}
//=================================================================================================================o
// At corner change climb handle
void CornerLerp (float speed)
{
Vector3 tVec = nextPoint -
(hero.right * -cornerReach) - (hero.forward * climbOffsetToWall) - (hero.up * heightOffsetToEdge);
float angleTo = Vector3.Angle (hero.right, curT.transform.right);
// Faster if angle is rel small
speed = angleTo <= 15.0f ? speed *= 15 : speed *= 4;
// Avoid hanging
a.SetBool("HangL", false); // RESET
a.SetBool("HangR", false); // RESET
// Apply
hero.position = Vector3.Lerp (hero.position, tVec, Time.deltaTime * speed);
hero.rotation = Quaternion.Lerp (hero.rotation, curT.rotation, Time.deltaTime * speed);
// Shift inward a bit
if (angleTo <= 0.01f)
{
Vector3 relPoint = curT.InverseTransformPoint (hero.position);
MatchP (Mathf.Abs(relPoint.z), climbOffsetToWall, Vector3.forward, Vector3.back);
a.SetBool("CornerL", false); // RESET
a.SetBool("CornerR", false); // RESET
climbState = ClimbState.Edge; // Out
}
}
//=================================================================================================================o
void MatchP (float dist, float desiredDist, Vector3 positive, Vector3 negative)
{
if (dist.ToString("f2") != desiredDist.ToString("f2"))
{
float s = climbSpeed * Time.deltaTime;
if (dist > desiredDist) // forward
{
if (dist > (desiredDist + 0.1f))
s *= smoothDistanceSpeed * 2; // Far, move faster
else
s /= smoothDistanceSpeed /1.5f; // Near, move slower
hero.Translate( positive * s );
}
else if (dist < desiredDist) // backward
{
if (dist < (desiredDist + 0.1f))
s *= smoothDistanceSpeed * 2; // Far, move faster
else
s /= smoothDistanceSpeed /1.5f; // Near, move slower
hero.Translate( negative * s );
}
}
}
//=================================================================================================================o
void ExitClimb ()
{
// Base State
if(animCtrl.climb)
{
rb.isKinematic = false;
isTop = false; // RESET
isOverhang = false; // RESET
hero.rotation = Quaternion.Euler(0, curT.eulerAngles.y, 0);
curT = null;
climbState = ClimbState.None;
a.runtimeAnimatorController = animCtrl.baseC;
baseState = BaseState.Base;
reset = true; // Out
}
}
//=================================================================================================================o
//================================================Swim=============================================================o
//=================================================================================================================o
// Movement & Rotation
void _Swim ()
{
float dTS = distY - hero.position.y;
Floating( dTS, liftVector);
a.SetFloat("WaterLevel", dTS);
rb.drag = waterDrag;
// Rotation, if mouse movement
if(mX != 0.0f)
{
hero.Rotate (0, mX * Time.fixedDeltaTime * diveRotSpeed * 55, 0, Space.Self);
}
// If input
if(h != 0.0f || v != 0.0f)
{
float speed = swimSpeed;
// Movement Vector
if(dTS >= offsetToSurf) // Under surface
{
Vector3 swimInput = hero.right * h + cam.transform.forward * v;
rb.velocity = swimInput.normalized * speed;
}
else if(grounded && dTS < offsetToSurf) // Walking in/out of the water
{
Vector3 swimInput = hero.right * h + hero.forward * v;
rb.velocity = swimInput.normalized * speed;
}
else // Above the surface
{
Vector3 swimInput = hero.right * h + hero.forward * v + liftVector;
Floating(dTS, swimInput);
}
}
}
//=================================================================================================================o
void DiveRotation (float speed) // Procedural animation for LateUpdate
{
float targetAngle = Mathf.Abs (Vector3.Angle (cam.transform.forward, hero.up));
// stay upwards till targetAngle is in scope
if(targetAngle < 150.0f)
{
targetAngle -= 90;
}
else if(targetAngle >= 150.0f)
{
targetAngle = 140.0f;
}
if(grounded)
{
targetAngle = 0.0f;
}
curAngle = Mathf.Lerp (curAngle, targetAngle, Time.deltaTime * speed);
// Update our current rotation
if(curAngle > 5.0f)
{
// Apply rotation, in this case the local Y axis of the rootBone
rootBone.Rotate (0, curAngle, 0, Space.Self);
}
}
//=================================================================================================================o
// Water behaviour
void Floating (float distToSurf, Vector3 liftVec)
{
if(distToSurf > offsetToSurf) // Under water
{
rb.velocity = Vector3.Lerp(rb.velocity, liftVec, Time.deltaTime *2);
}
else
{
rb.velocity = Vector3.Lerp(rb.velocity, -liftVec, Time.deltaTime *2);
}
}
//=================================================================================================================o
// Trigger of the water system
void OnTriggerEnter (Collider c)
{
if(c.name == "WaterTrigger")
{
if(!canSwim || baseState == BaseState.PhysX)
return;
distY = c.bounds.max.y;
rb.useGravity = false;
rb.velocity = Vector3.down*5; // Dive in fast
// Swim State
if(animCtrl.swim)
{
a.runtimeAnimatorController = animCtrl.swim;
StartCoroutine (JustHolster());
baseState = BaseState.Swim; // Out
}
}
}
void OnTriggerExit (Collider c)
{
if(c.name == "WaterTrigger")
{
if(!canSwim || baseState == BaseState.PhysX)
return;
distY = 0.0f;
rb.useGravity = true;
// Base State
if(animCtrl.baseC)
{
rb.drag = baseDrag;
a.runtimeAnimatorController = animCtrl.baseC;
baseState = BaseState.Base; // Out
}
}
}
//=================================================================================================================o
//================================================Fly==============================================================o
//=================================================================================================================o
void _Fly ()
{
rb.drag = flyDrag;
// Rotation, if mouse movement
if(mX != 0.0f)
{
hero.Rotate (0, mX * Time.fixedDeltaTime * diveRotSpeed * 55, 0, Space.Self);
}
// Leave Flying
if(doFly)
{
rb.useGravity = true;
// Base State
if(animCtrl.baseC)
{
rb.drag = baseDrag;
a.runtimeAnimatorController = animCtrl.baseC;
baseState = BaseState.Base; // Out
}
}
// If input
if(h != 0.0f || v != 0.0f)
{
// Movement Vector
Vector3 flyVec = hero.right * h
+ cam.transform.forward * v ;
// Lift if grounded
if(grounded)
{
groundTime += Time.deltaTime;
if(groundTime > 0.6f)
{
rb.velocity = Vector3.Lerp(Vector3.zero, Vector3.up * 22, groundTime);
groundTime = 0;
}
}
else
{
rb.velocity = flyVec.normalized * flySpeed;
}
}
}
//=================================================================================================================o
//===============================================Combat============================================================o
//=================================================================================================================o
void _Combat ()
{
// Combat Stance / Out
if(doCombat && canDrawHolster)
{
// Coroutine draw motion finished -> switch
if(weaponState == WeaponState.None)
{
return;
}
/*else if(weaponState == WeaponState.Unarmed)
{
return;
}*/
else if(weaponState == WeaponState.Sword)
{
StartCoroutine(DrawHolster(0.3f,weapons.sword_Holster.renderer, weapons.sword_Hand.renderer));
a.SetBool("Sword", false);
baseState = BaseState.Base;
}
else if(weaponState == WeaponState.Bow)
{
StartCoroutine(DrawHolster(0.6f,weapons.bow_Holster.renderer, weapons.bow_Hand.renderer));
a.SetBool("Bow", false);
baseState = BaseState.Base;
}
else if(weaponState == WeaponState.Rifle)
{
StartCoroutine(DrawHolster(0.9f,weapons.rifle_Holster.renderer, weapons.rifle_Hand.renderer));
a.SetBool("Rifle", false);
baseState = BaseState.Base;
}
else if(weaponState == WeaponState.Pistol)
{
StartCoroutine(DrawHolster(0.6f,weapons.pistol_Holster.renderer, weapons.pistol_Hand.renderer));
a.SetBool("Pistol", false);
baseState = BaseState.Base;
}
}
// Double Tap - Evade takes tapSpeed & coolDown in seconds
if(canEvade)
{
if(!isDoubleTap) StartCoroutine (DoubleTap (doubleTapSpeed, 1));
}
// Current state info for layer Base
st = a.GetCurrentAnimatorStateInfo(0);
if(grounded)
{
if(doJumpDown /*&& canJump */&& !st.IsTag("Jump") && !st.IsTag("Land"))
{
a.SetBool("Jump", true);
//add extra force to main jump
rb.velocity = hero.up * jumpHeight;
// Start cooldown until we can jump again
//StartCoroutine (JumpCoolDown(0.5f));
}
// Don't slide
if(!rb.isKinematic)
rb.velocity = new Vector3(0,rb.velocity.y,0);
// Extra rotation
if(canRotate)
{
if(!doLShift)
hero.Rotate(0,mX * rotSpeed/2 * Time.deltaTime,0);
}
// Punch, Kick
if(doAtk1Down && !st.IsTag("Attack1"))
{
a.SetBool("Attack1", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
else if(doAtk2Down && !st.IsTag("Attack2"))
{
a.SetBool("Attack2", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
// Walk
if(doWalk)
{
if(!st.IsTag("WalkTree"))//IsName("WalkTree.TreeW"))
{
a.SetBool("Walking", true);
a.SetBool("Sneaking", false); // RESET
a.SetBool("Sprinting", false); // RESET
}
else
{
a.SetBool("Walking", false); // RESET
}
}
// Sprint
else if(doSprint)
{
if(!st.IsTag("SprintTree"))
{
a.SetBool("Sprinting", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sneaking", false); // RESET
}
else
{
a.SetBool("Sprinting", false); // RESET
}
}
// Sneak
else if(doSneak)
{
if(!st.IsTag("SneakTree"))
{
a.SetBool("Sneaking", true);
a.SetBool("Walking", false); // RESET
a.SetBool("Sprinting", false); // RESET
}
else
{
a.SetBool("Sneaking", false); // RESET
}
}
WallGround ();
// -----------AirTime--------- //
if(!a.GetBool("CanLand")) // Very short air time
{
groundTime += Time.deltaTime;
if(groundTime >= 0.4f)
{
a.SetBool("CanLand", true);
}
}
else
groundTime = 0;
// -----------AirTime--------- //
}
else // In Air
{
// -----------AirTime--------- //
if(groundTime <= 0.3f)
{
groundTime += Time.deltaTime;
if(groundTime >= 0.2f)
{
a.SetBool("CanLand", true);
}
else
a.SetBool("CanLand", false);
}
// -----------AirTime--------- //
if(canRotate)
hero.Rotate(0,mX * rotSpeed/2 * Time.deltaTime,0);
WallRun();
}
// Resetting--------------------------------------------------
if(!a.IsInTransition(0))
{
if(st.IsTag("Jump") || st.IsTag("LedgeJump"))
{
// Reset our parameter to avoid looping
a.SetBool("Jump", false); // RESET
}
if(st.IsTag("Shoot") && !doAtk1)
{
// Reset our parameter to avoid looping
a.SetBool("Attack1", false); // RESET
}
if((st.IsName("SwordCombo_L.slash1")
|| st.IsName("SwordCombo_L.slash2")) && st.normalizedTime > 0.4f && !doAtk1)
{
// Reset our parameter to avoid looping
a.SetBool("Attack1", false); // RESET
}
else if(st.IsName("SwordCombo_L.slash2.5"))
{
// Reset our parameter to avoid looping
a.SetBool("Attack1", false); // RESET
}
if((st.IsName("SwordCombo_R.slash3")
|| st.IsName("SwordCombo_R.slash4")) && st.normalizedTime > 0.4f && !doAtk2)
{
// Reset our parameter to avoid looping
a.SetBool("Attack2", false); // RESET
}
else if(st.IsName("SwordCombo_R.slash5"))
{
// Reset our parameter to avoid looping
a.SetBool("Attack2", false); // RESET
}
if(st.IsTag("Evade"))
{
// Reset our parameter to avoid looping
a.SetBool("Evade_F", false); // RESET
a.SetBool("Evade_B", false); // RESET
a.SetBool("Evade_L", false); // RESET
a.SetBool("Evade_R", false); // RESET
}
if(st.IsTag("WallRun") && grounded) // No instant reset
{
a.SetBool("WallRunL", false); // RESET
a.SetBool("WallRunR", false); // RESET
a.SetBool("WallRunUp", false); // RESET
}
}
}
//=================================================================================================================o
//================================================PhysX============================================================o
//=================================================================================================================o
void _PhysX ()
{
// End - back to Base state
if(endRagdoll)
{
EndRagdoll(1); // Manual front
}
// Stay at Ragdoll position
Vector3 pos = rootBone.position;
hero.position = Vector3.Lerp (hero.position, pos, Time.deltaTime * 100.0f);
rootBone.position = pos;
float velM = rootBone.rigidbody.velocity.magnitude;
// Stop condition
if(velM <= endRagForce && grounded)
{
Vector3 targetDir = rootBone.forward - hero.up;
float targetAngle = Mathf.Abs(Vector3.Angle (targetDir, rootBone.forward));
velM = 0;
// Pick animation based on angle
if (targetAngle >= 40.0f) // Back
{
a.SetInteger("RandomM", 0);
MainRotationRagDoll (2);
EndRagdoll(0); // Out
}
else if (targetAngle <= 40.0f) // Front
{
rootBone.rigidbody.velocity = Vector3.zero;
a.SetInteger("RandomM", 1);
MainRotationRagDoll (-2);
EndRagdoll(1); // Out
}
}
else if(doClimb) // End ragdoll manually
{
MainRotationRagDoll (-2);
EndRagdoll(1); // Out
}
}
//=================================================================================================================o
// Start ragdoll modus
void StartRagdoll ()
{
startRagdoll = true;
a.cullingMode = AnimatorCullingMode.AlwaysAnimate;
a.enabled = false;
// Velocity with H and V axis
Vector3 vec = rb.velocity + hero.forward *v*2 + hero.right *h*2;
vec.y -= 3; // Extra down force
rb.isKinematic = true;
Physics.IgnoreLayerCollision(8,8); // No self collision
int i;
for (i=0; i<bones.Length; i++)
{
if (bones[i].rigidbody && bones[i] != hero.root)
{
bones[i].rigidbody.isKinematic = false;
bones[i].collider.isTrigger = false;
bones[i].rigidbody.drag = 0.0f;
bones[i].rigidbody.velocity = vec;
}
}
startRagdoll = false;
StartCoroutine (JustHolster());
baseState = BaseState.PhysX;
}
//=================================================================================================================o
// End ragdoll modus
void EndRagdoll (int s)
{
rb.isKinematic = false;
int i;
for (i=0; i<bones.Length; i++)
{
if (bones[i] != hero.root && bones[i].rigidbody)
{
bones[i].rigidbody.isKinematic = true;
bones[i].collider.isTrigger = true;
}
}
a.enabled = true;
a.cullingMode = AnimatorCullingMode.BasedOnRenderers;
a.SetBool("StandUp", true);
a.SetInteger("RandomM", s);
endRagdoll = false;
baseState = BaseState.Base;
}
//=================================================================================================================o
// Recover rotation before STandUp()
void MainRotationRagDoll (float f)
{
Vector3 v = rootBone.position + (rootBone.right * f); //root local up axis
v.y = hero.position.y;
hero.LookAt(v);
}
//=================================================================================================================o
//============================================Feet Placement=======================================================o
//=================================================================================================================o
void FeetPlacementIK () // Unity Pro only!
{
if(!useIdleFeetPlacement)
{
if(a.GetLayerWeight(1) != 0)
a.SetLayerWeight(1, 0); // Set Leglayer weight to 0
return;
}
if(!grounded)
return;
// Cancel if out of MoveTree State
if (!st.IsTag("MoveTree"))
return;
float LegLayerW = a.GetLayerWeight(1);
// Axis movement
if(v != 0 || h != 0)
{
if(LegLayerW > 0)
a.SetLayerWeight(1, LegLayerW - (Time.deltaTime + 0.2f));
}
else // Idle
{
if(LegLayerW < 1)
a.SetLayerWeight(1, LegLayerW + (Time.deltaTime + 0.1f));
}
if(LegLayerW > 0.1f)
{
RaycastHit rL;
RaycastHit rR;
Vector3 leftP = (hero.position + hero.up) - hero.right/4.5f;
Vector3 rightP = (hero.position + hero.up) + hero.right/4.5f;
if(!Physics.Raycast(leftP, Vector3.down, out rL, 2, groundLayers))
return;
if(!Physics.Raycast(rightP, Vector3.down, out rR, 2, groundLayers))
return;
float dif = Mathf.Abs(rL.distance - rR.distance);
if(dif < 0.05f) // If groundheight difference is bigger than...
return;
a.SetIKPositionWeight(AvatarIKGoal.LeftFoot, LegLayerW);
a.SetIKPositionWeight(AvatarIKGoal.RightFoot, LegLayerW);
Vector3 rheight = Vector3.Lerp(Vector3.zero, new Vector3(0, dif, 0), LegLayerW);
a.bodyPosition -= rheight;
Vector3 feetHeight = new Vector3(0, 0.09f, 0);
Vector3 leftFootPos = rL.point + feetHeight;
Vector3 rightFootPos = rR.point + feetHeight;
a.SetIKPosition(AvatarIKGoal.LeftFoot,leftFootPos);
a.SetIKPosition(AvatarIKGoal.RightFoot,rightFootPos);
}
}
//=================================================================================================================o
//================================================Aiming===========================================================o
//=================================================================================================================o
void AimLookIK () // Unity Pro only!
{
if(st.IsTag("Evade_F"))
return;
if(baseState == BaseState.Combat)
{
// Camera forward(Z) is aim direction
Vector3 targetDir = cam.transform.position + cam.transform.forward * 22;
a.SetLookAtPosition (targetDir);
a.SetLookAtWeight(1, 0.5f, 0.7f, 0.0f, 0.5f);
}
}
//=================================================================================================================o
void OnAnimatorIK (int layerIndex) // Unity Pro only!
{
if(layerIndex == 1)
{
FeetPlacementIK ();
}
if(layerIndex == 2)
{
AimLookIK ();
}
}
}
这个插件代码很简单,只有2个类,一个控制跟随人物的摄像头,一个是控制玩家化身动作的类,但功能确是意想不到的强大,我们放大下看看下设定
我们看下其实无论什么样的地板都是一个模型上面绑了一个碰撞盒子,这个是千古不变的(在u3d中),
1) WallRun layers : The layer(s) on which the character could perform a wall-run
人物最基本的行走动作,不需要这个层有过多的设定这个图中,其他的选项
1) CanRotate : Should the player be able to rotate itself (turn on spot)
可以旋转
Rotation Speed :How fast does he rotate.
旋转速度
2) BaseDrag : The rigidbody air drag we should have when not flying and swimming.
不是飞行和游泳时候的拖拽
3) AnimSpeed : The overall animation speed
总体动画速度
6) Move Speed : how fast we a physically moving.
移动速度(物理上的)
7) Can Jump : Should the player be able to jump.
可否跳跃
8) Jump Height : The force or power we use to jump.
跳高极限
Grounded Distance : A raycast that shoots from ourcapsule colliders center down to check if we're grounded. This is the distancewe check
接地距离:一个光线投射,从我们的胶囊碰撞器中心停机检查如果我们接地。这是距离我们检查
10)
Set Float Damp Time : The animator receivesaxises from our input, these are damped by this value.
设阻尼时间:动画接收我们的输入,用这个时间来控制衰减
11)
Can Evade : Should the character be able to evade.
可以躲避,让角色可以避开
double Tap Speed : The time window the player has toperform a double tap
双击速度:时间窗玩家执行双击
13) Can WallRun : Should the character beable to run-wall
可以墙上跑
14) Base State : the state we are currentlyin like : Climbing, Combat, Swimming etc.
基本状态,包括日常,游泳,飞行,战斗,攀岩
Weapon State : Which weapon is currentlyequipped.
当前武器的状态
Weapons : Weapon sets we can equip per hand
.武器,我们装备在手上的武器至于模型预制件中如何装备武器,我们后面再说,demo1.0中人物都是没拿武器的,
AnimCtrl : Animation Controller we use to animate or/and move the character.
注意这里遮罩的出现
我们看这个tree,其实这个插件有很大一部分是移植于另外一个插件,不过我们不管他,只要会用就可以了
我们看下动作是如何控制的,看了这些在比较上面的代码,是不是很简单,就是个
<span style="white-space:pre"> </span>a.SetBool("Grounded", grounded);
grounded = Physics.Raycast (hero.position + hero.up * col.center.y,
<span style="white-space:pre"> </span>hero.up * -1, out groundHit, groundedDistance, groundLayers);<pre name="code" class="csharp">
<img src="https://img-blog.csdn.net/20150502105042475?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3dvcmRmaXNoeDgy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /><img src="https://img-blog.csdn.net/20150502105048029?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3dvcmRmaXNoeDgy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
同理,这些实现都是一样的,如果有一个很好的动画师,完全可以自己写一个这样的插件出来,
Can Climb : Should the character be able to climb.
可以攀爬,
ClimbLayers : The layer we can climb on.如果想要攀爬,那么我们看看场景中有什么特殊的设置
看见了吧,其实u3d的新动画系统可以活用ik手柄来作出复杂的动作,比如钻洞,攀岩等等,具体实现不是我们demo里面要考虑的,这里就先不说了,我们看下攀爬的参数
20) Climb Speed : How fast can we climb physically.
攀爬速度
21) Climb Check Radius : How thick is the ray we use tofind the next climb spot
攀爬半径检测
22) Climb Check Distance : How far we look for climbspots.
攀爬距离检测
23) Climb Offset From Wall : Hero's forward distancefrom the wall while climbing. Useful when adjusting the system to yourindividual character proportions.
攀爬的偏移量,在调整人物比例是很有用
24) Climb Offset To Edge : The Upwards directiondistance to the climb spot. Made to adjust the system to different characterheights and arm lengths.
攀爬边缘偏移:为了调整系统的性质不同身高和臂长。
25) Smooth Distance Speed : this damps or speeds theclimbing process, dependent on the distance to the current spot.
光滑时距离速度:用于潮湿光滑控制。
26) Corner Side Offset : This value determines at whichpoint the character starts to hang on one hand until he slips off the edge oncethe end of a climb spot is reached.
角偏移:悬挂状态。
27) Climb State : The climb state we are in. None if notcurrently climbing.
28) Can Swim :Should the character be able to swim.
可否游泳
29) Swim Speed : How fast we swim.
游泳速度
30) Dive Rotation Speed : How fastthe procedural dive animation rotates.
潜水旋转速度
31)Water Drag : The rigidbody dragwhile in the water.
水阻力,刚体在水中的物理特性
32) Offset to Surface : Distance fromcapsule center to waters surface.
33) Lift Vector : The waters lift vector or lift direction(buoyancy)
34) Can Fly : Should the character be able to Fly.
可以飞行
35) Fly Speed : How fast we can fly.
飞行速度
36) Fly Rotation Speed : How fast the procedural fly animation rotates.
飞行动画旋转速度
37) Fly drag : The rigidbody drag while flying.
飞行拖动
38) Can Ragdoll : Should the character be able to turn into aragdoll (controlled by PhysX).
人偶化
39) Start Ragdoll Time : How long we must fall to turn in aragdoll.
开始人偶化时间
40) End Ragdoll Force : How much velocity must the rigidbody(root) have to trigger the ragdoll's end
结束人偶化
41) Start Ragdoll : Let you trigger the ragdoll modusmanually or by script.
.开始人偶
42) End Ragdoll : Let you end the ragdoll modus manually orby script.
结束人偶
43) Use Idle Feet Placement : Should the character's feet beplaced correctly on uneven ground. (IK Unity Pro only)
使用空闲时脚的位置,让人物脚正确吸附在地面上
因为太多了,所以这里我们分到下一篇来结束。。