using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]
[RequireComponent(typeof(PlatformEffector2D))]
public class CharacterController2D : MonoBehaviour
{
public float maxSpeed = 3.0f;
public float jumpHeight = 6.0f;
public float gravityScale = 1.5f;
float moveDirection = 0.0f;
float collisionCheckRadius = 0.0f;
bool facingRight = true;
bool isGrounded = false;
bool isJumping = false;
Rigidbody2D rigidBody;
CapsuleCollider2D mainCollider;
PlatformEffector2D platformEffector;
Vector3 cameraPos;
public Camera mainCamera;
void Start()
{
rigidBody = GetComponent<Rigidbody2D>();
mainCollider = GetComponent<CapsuleCollider2D>();
platformEffector = GetComponent<PlatformEffector2D>();
rigidBody.freezeRotation = true;
// Freeze rotation: Prevents the Rigidbody2D from rotating due to collisions or physics forces.
rigidBody.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
// Collision Detection Mode: Continuous - Rigidbody2D will use continuous collision detection
// to prevent fast-moving objects from passing through colliders.
rigidBody.gravityScale = gravityScale;
mainCollider.usedByEffector = true;
platformEffector.useOneWay = false;
platformEffector.useSideFriction = false;
// PlatformEffector없이 Collider만 사용하면 캐릭터가 점프 후 벽에 붙는 효과가 발생한다. 마찰력 때문이다.
// platformEffector.useSideFriction을 false로 설정하면 벽에 붙는 효과를 제거할 수 있다.
facingRight = transform.localScale.x > 0.0f;
collisionCheckRadius = mainCollider.size.x * 0.6f * Mathf.Abs(transform.localScale.x);
if (mainCamera)
{
cameraPos = mainCamera.transform.position;
}
}
void Update()
{
//if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) && (isGrounded || Mathf.Abs(rigidBody.linearVelocity.x) > 0.01f))
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) // 위 조건문으로 변경하면, 점프만 했을때 좌우로 움직이지 않게 된다.
{
moveDirection = Input.GetKey(KeyCode.A) ? -1 : 1;
}
else if (isGrounded || rigidBody.linearVelocity.magnitude < 0.01f)
{
moveDirection = 0.0f;
}
// Jumping logic. 키 입력 확인이 FixedUpdate()에서 처리되면 제대로 감지되지 않을때가 있다.
if (Input.GetKeyDown(KeyCode.W) && isGrounded)
{
isJumping = true;
}
// Change facing direction
if (moveDirection != 0)
{
if (moveDirection > 0 && !facingRight)
{
facingRight = true;
transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
}
else if (moveDirection < 0 && facingRight)
{
facingRight = false;
transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
}
}
}
void FixedUpdate()
{
Vector3 groundCheckPos = mainCollider.bounds.min + new Vector3(mainCollider.size.x * 0.5f, mainCollider.size.x * 0.5f, 0.0f);
// Check if player is grounded. OverlapCircleAll(): Get a list of all Colliders that fall within a circular area.
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, collisionCheckRadius);
isGrounded = false;
// 플레이어 콜라이더가 아니면 벽 또는 바닥 콜라이더라 가정하고 점프 할 수 있도록 충돌체크한다.
// 벽을 타고 멀티 점프가 가능하다.
if (colliders.Length > 0)
{
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i] != mainCollider)
{
isGrounded = true;
break;
}
}
}
if (isJumping)
{
// Apply jump force
rigidBody.linearVelocity = new Vector2(rigidBody.linearVelocity.x, jumpHeight);
//rigidBody.AddForce(new Vector2(0.0f, jumpHeight * rigidBody.mass), ForceMode2D.Impulse);
isJumping = false;
}
else
{
// Apply movement velocity
rigidBody.linearVelocity = new Vector2(moveDirection * maxSpeed, rigidBody.linearVelocity.y);
}
// OverlapCircleAll() 이 커버하는 범위를 시각적으로 표시하기 위해 디버그 라인 그리기.
Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(0.0f, collisionCheckRadius, 0.0f), isGrounded ? Color.green : Color.red);
Debug.DrawLine(groundCheckPos, groundCheckPos + new Vector3(collisionCheckRadius * (facingRight ? 1 : -1), 0.0f, 0.0f), isGrounded ? Color.green : Color.red);
}
private void LateUpdate()
{
// Ensure the camera follows the player smoothly
if (mainCamera)
{
// 카메라의 y, z 좌표는 고정하고, x 좌표만 플레이어의 x 좌표로 설정.
mainCamera.transform.position = new Vector3(transform.position.x, cameraPos.y, cameraPos.z);
}
}
}