#60 - Simple character controller for Unity
Date: 2019-05-11 12:00 - c#
A simple character controller for unity which allows to have different abilities.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(Animator))]
public class Character : Interactable {
public delegate void DeathEvent();
public bool IsPlayerControlled = false;
protected List<Ability> abilities = new List<Ability>();
public bool PreventMovement = false;
public virtual bool CanMove {
get { return !PreventMovement && !IsDead; }
}
public bool IsGrounded = true;
public float DragGround = 10.0f;
public float DragAir = 1.0f;
public Vector3 Velocity = Vector3.zero;
public Vector3 Motion = Vector3.zero;
private bool isDead;
public bool IsDead {
get { return isDead; }
set {
if (value != isDead) {
isDead = value;
if (isDead && OnDeath != null)
OnDeath();
}
}
}
public event DeathEvent OnDeath;
public CharacterController CharacterController { get; private set; }
public Animator Animator { get; private set; }
public Transform CharacterCamera;
public T GetAbility<T>() where T: Ability {
foreach(Ability ability in abilities) {
if (typeof(T).IsAssignableFrom(ability.GetType()))
return (T) ability;
}
return null;
}
public T[] GetAbilities<T>() where T: Ability {
List<Ability> abilitiesOfType = new List<Ability>();
foreach(Ability ability in abilities) {
if (typeof(T).IsAssignableFrom(ability.GetType()))
abilitiesOfType.Add(ability);
}
return (T[]) abilitiesOfType.ToArray();
}
protected void Awake() {
Type abilityType = typeof(Ability);
PropertyInfo characterField = abilityType.GetProperty("character", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo[] fields = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach(FieldInfo field in fields) {
if (field.FieldType.IsSubclassOf(typeof(Ability))) {
Ability ability = (Ability) field.GetValue(this);
if (ability == null) {
Debug.Log(string.Format("Ability {0} is null in object {1} with name", field.FieldType.Name, this, field.Name));
}
characterField.SetValue(ability, this, null);
abilities.Add(ability);
}
}
CharacterController = GetComponent<CharacterController>();
Animator = GetComponent<Animator>();
}
public void Move(Vector3 movement) {
this.Motion += movement;
}
protected void FixedUpdate() {
Motion = Vector3.zero;
if (Velocity.x != 0 || Velocity.z != 0) {
float dragCoef = IsGrounded ? DragGround : DragAir;
float dragMultiplier = 1 - dragCoef * Time.fixedDeltaTime;
Velocity.Set(Velocity.x * dragMultiplier, Velocity.y, Velocity.z * dragMultiplier);
if (Mathf.Abs(Velocity.x) < 0.1) Velocity.x = 0;
if (Mathf.Abs(Velocity.z) < 0.1) Velocity.z = 0;
}
Velocity += Physics.gravity * Time.fixedDeltaTime;
foreach(Ability ability in abilities) {
if (!ability.IsExecuting && ability.Enabled && ability.CanStartAbility()) {
ability.Start();
}
if (ability.IsExecuting) {
ability.Update();
}
if ((ability.IsExecuting && ability.CanStopAbility()) || !ability.Enabled) {
ability.Stop();
}
}
Motion += Velocity;
if (!IsDead)
CharacterController.Move(Motion * Time.fixedDeltaTime);
if (IsGrounded && !Physics.Raycast(transform.position, -transform.up, 0.20f)) {
IsGrounded = false;
}
if (!IsGrounded && CharacterController.isGrounded){
IsGrounded = true;
}
if (IsGrounded) {
Velocity.y = 0;
}
float forwardMovement = Vector3.Project(Motion, transform.forward).magnitude;
float rightMovement = Vector3.Project(Motion, transform.right).magnitude;
this.Animator.SetBool("OnGround", IsGrounded);
this.Animator.SetFloat("Forward", forwardMovement, 0.05f, Time.fixedDeltaTime);
this.Animator.SetFloat("Right", rightMovement, 0.05f, Time.fixedDeltaTime);
if (IsGrounded)
this.Animator.SetFloat("Jump", 0.0f);
else
this.Animator.SetFloat("Jump", Velocity.y, 0.1f, Time.fixedDeltaTime);
float forwardValue = this.Animator.GetFloat("Forward");
if (forwardValue != 0.0f && Mathf.Abs(forwardValue) < 0.05f)
this.Animator.SetFloat("Forward", 0.0f);
float rightValue = this.Animator.GetFloat("Right");
if (rightValue != 0.0f && Mathf.Abs(rightValue) < 0.05f)
this.Animator.SetFloat("Right", 0.0f);
}
public override bool CanInteract(GameObject caller) {
return false;
}
public override void OnInteract(GameObject other)
{
}
}