Neues Projekt
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
public class CelesteStyleController2D : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Rigidbody2D body;
|
||||
[SerializeField] private Collider2D col;
|
||||
[SerializeField] private Transform groundCheck;
|
||||
[SerializeField] private Transform wallCheck;
|
||||
[SerializeField] private LayerMask groundLayer;
|
||||
|
||||
[Header("Run")]
|
||||
[SerializeField] private float maxRunSpeed = 9f;
|
||||
[SerializeField] private float runAcceleration = 70f;
|
||||
[SerializeField] private float runDeceleration = 80f;
|
||||
[SerializeField] private float airAccelerationMultiplier = 0.65f;
|
||||
|
||||
[Header("Jump")]
|
||||
[SerializeField] private float jumpVelocity = 14f;
|
||||
[SerializeField] private float coyoteTime = 0.12f;
|
||||
[SerializeField] private float jumpBufferTime = 0.12f;
|
||||
[SerializeField] private float jumpCutMultiplier = 0.5f; // wenn Jump losgelassen wird
|
||||
|
||||
[Header("Gravity Feel")]
|
||||
[SerializeField] private float fallGravityMultiplier = 1.6f; // schneller fallen
|
||||
[SerializeField] private float lowJumpGravityMultiplier = 1.3f; // wenn Jump nicht gehalten wird
|
||||
|
||||
[Header("Checks")]
|
||||
[SerializeField] private Vector2 groundCheckSize = new Vector2(0.6f, 0.12f);
|
||||
[SerializeField] private float wallCheckDistance = 0.35f;
|
||||
|
||||
[Header("Wall Slide & Wall Jump")]
|
||||
[SerializeField] private bool enableWallSlide = true;
|
||||
[SerializeField] private float wallSlideMaxSpeed = 3.5f;
|
||||
[SerializeField] private float wallJumpX = 10f;
|
||||
[SerializeField] private float wallJumpY = 14f;
|
||||
[SerializeField] private float wallJumpInputLock = 0.15f; // kurz kein normaler Move nach Walljump
|
||||
[SerializeField] private bool requireMoveIntoWallForSlide = true; // Celeste-like: zum Sliden Richtung Wand halten
|
||||
|
||||
[Header("Dash (optional)")]
|
||||
[SerializeField] private bool enableDash = true;
|
||||
[SerializeField] private float dashSpeed = 18f;
|
||||
[SerializeField] private float dashTime = 0.16f;
|
||||
[SerializeField] private float dashCooldown = 0.05f; // kleine Pause nach Dash
|
||||
[SerializeField] private int airDashes = 1;
|
||||
|
||||
// input
|
||||
private float moveX;
|
||||
private bool jumpPressed;
|
||||
private bool jumpHeld;
|
||||
private bool dashPressed;
|
||||
|
||||
// state
|
||||
private bool grounded;
|
||||
private bool onWallLeft;
|
||||
private bool onWallRight;
|
||||
private bool wallSliding;
|
||||
private int wallDir; // -1 links, +1 rechts
|
||||
|
||||
private float coyoteCounter;
|
||||
private float jumpBufferCounter;
|
||||
|
||||
private float inputLockCounter;
|
||||
|
||||
// dash state
|
||||
private bool isDashing;
|
||||
private float dashCounter;
|
||||
private float dashCooldownCounter;
|
||||
private int dashesLeft;
|
||||
private float baseGravity;
|
||||
|
||||
private int facingDir = 1; // 1 = rechts, -1 = links
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
body = GetComponent<Rigidbody2D>();
|
||||
col = GetComponent<Collider2D>();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!body) body = GetComponent<Rigidbody2D>();
|
||||
if (!col) col = GetComponent<Collider2D>();
|
||||
baseGravity = body.gravityScale;
|
||||
dashesLeft = airDashes;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
ReadInput();
|
||||
UpdateChecks();
|
||||
UpdateTimers();
|
||||
HandleFlip();
|
||||
|
||||
// Jump buffer aktualisieren
|
||||
if (jumpPressed) jumpBufferCounter = jumpBufferTime;
|
||||
|
||||
// Dash
|
||||
if (enableDash && dashPressed && !isDashing && dashCooldownCounter <= 0f && dashesLeft > 0)
|
||||
{
|
||||
StartDash();
|
||||
}
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (isDashing)
|
||||
{
|
||||
dashCounter -= Time.fixedDeltaTime;
|
||||
if (dashCounter <= 0f) EndDash();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dashCooldownCounter > 0f)
|
||||
dashCooldownCounter -= Time.fixedDeltaTime;
|
||||
|
||||
ApplyHorizontalMovement();
|
||||
ApplyWallSlide();
|
||||
TryConsumeBufferedJump();
|
||||
ApplyBetterGravity();
|
||||
}
|
||||
|
||||
// ---------------- INPUT & CHECKS ----------------
|
||||
|
||||
private void ReadInput()
|
||||
{
|
||||
moveX = Input.GetAxisRaw("Horizontal");
|
||||
jumpPressed = Input.GetButtonDown("Jump");
|
||||
jumpHeld = Input.GetButton("Jump");
|
||||
dashPressed = Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKeyDown(KeyCode.X); // frei anpassbar
|
||||
}
|
||||
|
||||
private void UpdateChecks()
|
||||
{
|
||||
// Ground check (OverlapBox)
|
||||
grounded = Physics2D.OverlapBox(groundCheck.position, groundCheckSize, 0f, groundLayer);
|
||||
|
||||
// Wall check (Raycasts)
|
||||
onWallLeft = Physics2D.Raycast(wallCheck.position, Vector2.left, wallCheckDistance, groundLayer);
|
||||
onWallRight = Physics2D.Raycast(wallCheck.position, Vector2.right, wallCheckDistance, groundLayer);
|
||||
|
||||
wallDir = onWallRight ? 1 : (onWallLeft ? -1 : 0);
|
||||
|
||||
// Dash reset am Boden
|
||||
if (grounded)
|
||||
dashesLeft = airDashes;
|
||||
}
|
||||
|
||||
private void UpdateTimers()
|
||||
{
|
||||
// Coyote Time
|
||||
if (grounded) coyoteCounter = coyoteTime;
|
||||
else coyoteCounter -= Time.deltaTime;
|
||||
|
||||
// Jump Buffer
|
||||
if (jumpBufferCounter > 0f) jumpBufferCounter -= Time.deltaTime;
|
||||
|
||||
// Input Lock nach Walljump
|
||||
if (inputLockCounter > 0f) inputLockCounter -= Time.deltaTime;
|
||||
}
|
||||
|
||||
private void HandleFlip()
|
||||
{
|
||||
// Facing nur wenn Spieler wirklich l�uft
|
||||
if (moveX != 0 && !isDashing)
|
||||
{
|
||||
facingDir = (moveX > 0) ? 1 : -1;
|
||||
// Simple Flip: Scale X (alternativ SpriteRenderer.flipX)
|
||||
Vector3 s = transform.localScale;
|
||||
s.x = Mathf.Abs(s.x) * facingDir;
|
||||
transform.localScale = s;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- MOVEMENT ----------------
|
||||
|
||||
private void ApplyHorizontalMovement()
|
||||
{
|
||||
if (inputLockCounter > 0f) return;
|
||||
|
||||
float targetSpeed = moveX * maxRunSpeed;
|
||||
|
||||
float accel = (Mathf.Abs(targetSpeed) > 0.01f) ? runAcceleration : runDeceleration;
|
||||
if (!grounded) accel *= airAccelerationMultiplier;
|
||||
|
||||
float speedDiff = targetSpeed - body.linearVelocity.x;
|
||||
float movement = speedDiff * accel;
|
||||
|
||||
body.AddForce(Vector2.right * movement);
|
||||
|
||||
// Clamp, damit du nicht "durch AddForce" zu schnell wirst
|
||||
float clampedX = Mathf.Clamp(body.linearVelocity.x, -maxRunSpeed, maxRunSpeed);
|
||||
body.linearVelocity = new Vector2(clampedX, body.linearVelocity.y);
|
||||
}
|
||||
|
||||
// ---------------- JUMP LOGIC ----------------
|
||||
|
||||
private void TryConsumeBufferedJump()
|
||||
{
|
||||
if (jumpBufferCounter <= 0f) return;
|
||||
|
||||
// Priorit�t: Wenn wallSliding ? Walljump, sonst normal Jump (Coyote)
|
||||
if (wallSliding)
|
||||
{
|
||||
DoWallJump();
|
||||
jumpBufferCounter = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coyoteCounter > 0f)
|
||||
{
|
||||
DoJump();
|
||||
jumpBufferCounter = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoJump()
|
||||
{
|
||||
// konsistent: setze Y-Velocity (Celeste-like)
|
||||
body.linearVelocity = new Vector2(body.linearVelocity.x, jumpVelocity);
|
||||
coyoteCounter = 0f;
|
||||
}
|
||||
|
||||
private void DoWallJump()
|
||||
{
|
||||
// weg von Wand: wenn Wand rechts (+1), spring nach links (-1)
|
||||
Vector2 v = new Vector2(-wallDir * wallJumpX, wallJumpY);
|
||||
|
||||
body.linearVelocity = Vector2.zero;
|
||||
body.linearVelocity = v;
|
||||
|
||||
// kurzer Lock gegen Input "zieht dich zur�ck"
|
||||
inputLockCounter = wallJumpInputLock;
|
||||
|
||||
// Nach Walljump gilt Sprung als verbraucht, aber du kannst dashen
|
||||
coyoteCounter = 0f;
|
||||
}
|
||||
|
||||
// ---------------- WALL SLIDE ----------------
|
||||
|
||||
private void ApplyWallSlide()
|
||||
{
|
||||
wallSliding = false;
|
||||
if (!enableWallSlide) return;
|
||||
if (grounded) return;
|
||||
if (wallDir == 0) return;
|
||||
|
||||
// Optional: nur sliden, wenn Richtung zur Wand gedr�ckt wird
|
||||
if (requireMoveIntoWallForSlide)
|
||||
{
|
||||
if (moveX == 0) return;
|
||||
if (Mathf.Sign(moveX) != wallDir) return;
|
||||
}
|
||||
|
||||
// Nur sliden wenn man nach unten f�llt (Celeste-feel)
|
||||
if (body.linearVelocity.y < 0f)
|
||||
{
|
||||
wallSliding = true;
|
||||
float newY = Mathf.Max(body.linearVelocity.y, -wallSlideMaxSpeed);
|
||||
body.linearVelocity = new Vector2(body.linearVelocity.x, newY);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- GRAVITY FEEL ----------------
|
||||
|
||||
private void ApplyBetterGravity()
|
||||
{
|
||||
// Wenn man f�llt: schneller
|
||||
if (body.linearVelocity.y < 0f)
|
||||
{
|
||||
body.linearVelocity += Vector2.up * Physics2D.gravity.y * (fallGravityMultiplier - 1f) * Time.fixedDeltaTime;
|
||||
}
|
||||
// Wenn man aufsteigt, aber Jump nicht mehr gehalten wird: Jump "cut"
|
||||
else if (body.linearVelocity.y > 0f && !jumpHeld)
|
||||
{
|
||||
body.linearVelocity += Vector2.up * Physics2D.gravity.y * (lowJumpGravityMultiplier - 1f) * Time.fixedDeltaTime;
|
||||
}
|
||||
|
||||
// Optional: sofortiges jump cut beim Loslassen (noch snappier)
|
||||
if (!jumpHeld && body.linearVelocity.y > 0f)
|
||||
{
|
||||
body.linearVelocity = new Vector2(body.linearVelocity.x, body.linearVelocity.y * (1f - (1f - jumpCutMultiplier) * 0.02f));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- DASH ----------------
|
||||
|
||||
private void StartDash()
|
||||
{
|
||||
isDashing = true;
|
||||
dashCounter = dashTime;
|
||||
dashCooldownCounter = 0f;
|
||||
|
||||
dashesLeft--;
|
||||
|
||||
// Dash direction: Input, sonst facing
|
||||
Vector2 dir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
|
||||
if (dir.sqrMagnitude < 0.01f) dir = new Vector2(facingDir, 0f);
|
||||
dir.Normalize();
|
||||
|
||||
// w�hrend Dash keine Gravity
|
||||
body.gravityScale = 0f;
|
||||
body.linearVelocity = dir * dashSpeed;
|
||||
}
|
||||
|
||||
private void EndDash()
|
||||
{
|
||||
isDashing = false;
|
||||
dashCooldownCounter = dashCooldown;
|
||||
body.gravityScale = baseGravity;
|
||||
|
||||
// Optional: kleiner "carry", damit dash nicht abrupt stoppt
|
||||
body.linearVelocity = new Vector2(body.linearVelocity.x * 0.9f, body.linearVelocity.y);
|
||||
}
|
||||
|
||||
// ---------------- GIZMOS ----------------
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (groundCheck)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireCube(groundCheck.position, groundCheckSize);
|
||||
}
|
||||
|
||||
if (wallCheck)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawLine(wallCheck.position, wallCheck.position + Vector3.left * wallCheckDistance);
|
||||
Gizmos.DrawLine(wallCheck.position, wallCheck.position + Vector3.right * wallCheckDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user