using System; using System.Collections; using UnityEngine; using UnityEngine.Events; [RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(CapsuleCollider2D))] public class CharacterController2D : MonoBehaviour { public enum Direction { left = -1, right = 1 } [SerializeField] Camera mainCamera; [SerializeField] float acceleration = 40f; [SerializeField][Range(0, .3f)] float smoothing = .05f; [SerializeField] float gravityScale = 1.5f; [SerializeField] float jumpForce = 50f; [SerializeField] int maxJumpCount = 1; [SerializeField] bool airControl = true; internal UnityEvent OnIdleEvent; internal UnityEvent OnFlip; internal UnityEvent OnJump; internal UnityEvent OnLand; internal UnityEvent OnFall; public Direction direction = Direction.right; public Vector2 velocity = Vector2.zero; public int jumpCount = 0; public bool isGrounded = false; public Vector3 cameraPos; public Rigidbody2D _rigidBody; public CapsuleCollider2D _collider; public Transform _transform; void Awake() { _rigidBody = GetComponent(); _collider = GetComponent(); OnIdleEvent ??= new UnityEvent(); OnFlip ??= new UnityEvent(); OnJump ??= new UnityEvent(); OnFall ??= new UnityEvent(); OnLand ??= new UnityEvent(); } // Use this for initialization void Start() { _transform = transform; _rigidBody.freezeRotation = true; _rigidBody.collisionDetectionMode = CollisionDetectionMode2D.Continuous; _rigidBody.gravityScale = gravityScale; direction = (Direction)(_transform.localScale.x / Mathf.Abs(_transform.localScale.x)); jumpCount = maxJumpCount; if (mainCamera) cameraPos = mainCamera.transform.position; OnFlip.AddListener(HandleFlip); OnJump.AddListener(HandleJump); OnFall.AddListener(HandleFall); OnLand.AddListener(HandleLand); } void Update() { if (isGrounded || airControl) HandleHorzInput(Input.GetAxisRaw("Horizontal")); // Changing this to `canJump` or `jumpCount > 0` would allow for things like double jumping. if (jumpCount > 0 && Input.GetButtonDown("Jump")) OnJump.Invoke(); if (mainCamera) { var targetPos = new Vector3(_transform.position.x, cameraPos.y, cameraPos.z); mainCamera.transform.position = Vector3.Lerp(mainCamera.transform.position, targetPos, 0.25f); } } void FixedUpdate() { CheckCollision(); } public void Move(float magnitude) { Vector3 targetVelocity = new Vector2(magnitude, _rigidBody.velocity.y); _rigidBody.velocity = Vector2.SmoothDamp(_rigidBody.velocity, targetVelocity, ref velocity, smoothing); } void CheckCollision() { var evnt = isGrounded ? OnFall : OnLand; float colliderRadius = _collider.size.x * 0.4f * Mathf.Abs(transform.localScale.x); Vector3 groundCheckPos = new Vector3(_collider.bounds.size.x * 0.5f, colliderRadius * 0.9f, 0) + _collider.bounds.min; Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, colliderRadius); if (Array.Exists(colliders, c => c != _collider)) { Debug.Log(Array.Find(colliders, c => c != _collider)); evnt.Invoke(); } } void HandleHorzInput(float horzInput) { Move(horzInput * acceleration); if (horzInput * (int)direction < 0) OnFlip.Invoke(); } void HandleFlip() { Debug.Log("Flipping"); Vector3 theScale = transform.localScale; theScale.x *= -1; transform.localScale = theScale; direction = direction == Direction.left ? Direction.right : Direction.left; } void HandleJump() { Debug.Log("Jumping"); jumpCount--; _rigidBody.AddForce(Vector2.up * jumpForce); OnFall.Invoke(); } void HandleFall() { Debug.Log("Falling"); isGrounded = false; } void HandleLand() { Debug.Log("Landing"); isGrounded = true; jumpCount = maxJumpCount; } }