Могу поделиться с вами своим скриптом.
Используется csharp
using UnityEngine;
using System.Collections;
public class CharacterPhysics : MonoBehaviour {
public Transform[] child_pos;
public GUIText debug_text1;
private CharacterController controller;
private CapsuleCollider capsule;
// гравитация
public bool gravityOn {
get { return isChar ? _gravityOn : rigidbody.useGravity; }
set { if (isChar) _gravityOn = value; else rigidbody.useGravity = value; }
}
private bool _gravityOn = true;
public float gravity = 9.81f; // искусственная гравитация
// физика
private CollisionFlags flags;
public bool grounded, upcollide, colside;
public bool i_stick;
// контроллер
private bool isChar; // используется CharacterController иначе - Rigidbody
public bool IsChar {
get { return isChar; }
}
public float cRadius {
get { return isChar ? controller.radius : capsule.radius; }
}
public float cHeight {
get { return isChar ? controller.height : capsule.height; }
set { if (isChar) controller.height = value; else capsule.height = value; }
}
public Vector3 cCenter {
get { return isChar ? controller.center : capsule.center; }
set { if (isChar) controller.center = value; else capsule.center = value; }
}
public float cCenterY {
get { return (isChar ? controller.center.y : capsule.center.y); }
set {
if (isChar) controller.center = new Vector3(controller.center.x, value, controller.center.z);
else capsule.center = new Vector3(capsule.center.x, value, capsule.center.z);
}
}
// состояние персонажа
public Vector3 velocity {
get { return isChar ? (side*rightDirection + vert*Vector3.up) : (rigidbody.velocity); }
}
public float vert {
set {
if (isChar) _vert = value; else rigidbody.velocity = new Vector3(rigidbody.velocity.x, value, rigidbody.velocity.z);
}
get { return isChar ? _vert : rigidbody.velocity.y; }
}
public float _vert, side;
//[NonSerialized]
public Vector3 rightDirection { // прямое направление движения
get { return _rightDirection; }
set { _rightDirection = value.normalized; }
}
private Vector3 _rightDirection;
// Запрет перемещения
private bool freeze;
public bool IsFreeze {
get { return freeze; }
}
public void Freeze()
{
freeze = true; Stop();
}
public void Unfreeze()
{
freeze = false;
}
public void Stop() {
if (isChar) { vert = 0.0f; side = 0.0f; } else rigidbody.velocity = Vector3.zero;
}
// = = = = = = = = = = = = = = =
uint i, child_num;
private Vector3[] child_offset;
void Awake() {
// начальное положение
rightDirection = transform.forward;
CheckIsChar(); // определяем тип контроллера
//
if (!isChar) child_num = (uint)child_pos.Length;
child_offset = new Vector3[child_num];
for (i = 0; i < child_num; i++) child_offset[i] = child_pos[i].localPosition;
}
public void CheckIsChar() {
controller = GetComponent<CharacterController>();
capsule = GetComponent<CapsuleCollider>();
if (controller) { isChar = true; return; };
if (rigidbody && capsule) { isChar = false; return; }
Debug.LogError("Object no have CharacterController or Capsule+Rigidbody");
}
void Start() {
if (!isChar) rigidbody.freezeRotation = true; // отключаем вращение Rigidbody
}
void Update() {
// тащим детей за собой
if (!isChar) {
for (i = 0; i < child_num; i++) child_pos[i].position = transform.position + child_offset[i];
}
// ПЕРЕМЕЩЕНИЕ
if (!freeze && (side != 0.000f || vert != 0.000f)) {
if (isChar)
// с контроллером
{
Vector3 transit = Vector3.zero;
if (side != 0.0f) transit += rightDirection * side * Time.deltaTime;
if (vert != 0.0f) transit += transform.up * vert * Time.deltaTime;
controller.Move(transit);
}
// без контроллера
else {
if (!i_stick) rigidbody.velocity = side*rightDirection+Vector3.up*vert;
else rigidbody.velocity = Vector3.up*vert; // если застряли, то не двигаемся по горизонтали
}
}
// применяем гравитацию
if (isChar && gravityOn) {
if (!grounded) vert -= gravity * Time.deltaTime;
//else vert = -gravity * Time.deltaTime; // прижимаем к земле, чтобы поднялся флаг isGrounded
else vert = -0.05f;
}
if (isChar) {
flags = controller.collisionFlags;
grounded = controller.isGrounded;
upcollide = (flags & CollisionFlags.Above) != 0;
colside = (flags & CollisionFlags.Sides) != 0;
}
// отладочная информация
if (debug_text1 != null) debug_text1.text = DebugText(false);
}
Vector3 collideDirection;
float vel_magnitude;
void OnCollisionEnter(Collision other) {
if (isChar) return;
collideDirection = other.contacts[0].normal;
if (Vector3.Project(Vector3.up, collideDirection).magnitude >= 0.975) { grounded = true; i_stick = false; }
//upcollide = (Vector3.Project(-Vector3.up, other.contacts[0].normal).magnitude >= 0.85);
if (Vector3.Project(transform.forward, collideDirection).magnitude >= 0.85) colside = true;
}
void OnCollisionExit(Collision other) {
if (isChar) return;
collideDirection = other.contacts[0].normal;
if (Vector3.Project(Vector3.up, collideDirection).magnitude >= 0.975) grounded = false;
//upcollide = (Vector3.Project(-Vector3.up, other.contacts[0].normal).magnitude >= 0.85);
if (Vector3.Project(transform.forward, collideDirection).magnitude >= 0.85) colside = false;
}
void OnCollisionStay(Collision other) {
//Debug.Log("Contacts = "+other.contacts.Length);
if (other.contacts.Length == 0) return;
collideDirection = other.contacts[0].normal;
if (!grounded && Vector3.Project(transform.forward, collideDirection).magnitude >= 0.85) {
transform.position += collideDirection * Mathf.Clamp(0.5f*other.relativeVelocity.magnitude*Time.fixedDeltaTime, 0.05f, 0.5f);
i_stick = true; // застряли
Debug.Log("OnCollisionStay_colside");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// вывод на экран инфы о состоянии персонажа
string DebugText(bool inline)
{
char endl = inline ? ' ' : '\n';
string text = "";
text += (grounded ? "ground" : "inair");
text += endl+"gravity: "+(gravityOn ? "ON" : "OFF");
text += endl+"velocity = "+(isChar ? controller.velocity : rigidbody.velocity);
return text;
}
public void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position+rightDirection);
}
/* Расширение редактора - смена CC на Rigidbody+Capsule и наоборот */
// непереносимые параметры
private float oldCCSkin, oldSOffset;
// переносимые параметры
float _radius, _height; Vector3 _center;
[ExecuteInEditMode]
[ContextMenu ("Switch CC <-> Rigidbody")]
public void SwitchCharMode()
{
CheckIsChar();
if (!isChar) { // CC
_radius = capsule.radius; _height = capsule.height; _center = capsule.center;
controller = gameObject.AddComponent<CharacterController>();
DestroyImmediate (capsule); capsule = null;
DestroyImmediate (rigidbody);
controller.radius = _radius; controller.height = _height; controller.center = _center;
}
else { // Rigidbody
_radius = controller.radius; _height = controller.height; _center = controller.center;
DestroyImmediate (controller); controller = null;
capsule = gameObject.AddComponent<CapsuleCollider>();
gameObject.AddComponent<Rigidbody>();
capsule.radius = _radius; capsule.height = _height; capsule.center = _center;
}
CheckIsChar();
}
}
Он работает как с CharacterController, так и с CapsuleCollider+Rigidbody.
Единственное отличие - второй вариант может передвигаться по движ. платформам, но иногда при сильном прыжке все-таки застревает в препятствиях.
Что вам надо знать:
rightDirection - прямое направление движения, куда перс будет двигаться при положительном значении side. Нужно установить этот вектор в в инспекторе или в Awake другим скриптом.
vert, side - вертикальная и горизонтальная скорость движения. Меняйте их другим скриптом для управления движением персонажем.
velocity - текущая скорость движения. Предназначено для чтения
gravityOn - вкл/выкл. гравитацию.
Freeze(), Unfreeze() - отключает работу контроллера и включает обратно.
В контекстном меню (шестеренка в строке имени компонента) есть пункт "Switch CC <-> Rigidbody", к-й худо-бедно заменяет CC на Rididbody и Capsule Collider с теми же параметрами и обратно.
Кстати, при работе с CharacterController заметил такую особенность. Если внутри OnControllerColliderHit двигать коллайдер через Move, то игра зависает в бесконечном цикле и падает от переполнения стека. Поэтому так делать нельзя.
Нужна помощь? Сами, сами, сами, сами, сами... делаем все сами