Hello Unity Community,
I’m currently developing a game and I’m facing an issue where my player character is knocked back excessively when colliding with enemies. This problem occurs specifically when the enemy hits the player with their attack while the player simultaneously strikes the enemy. I suspect that the issue lies within the scripts that manage player movement and enemy attack logic.
Issue Description:
When my player character (controlled by the PlayerMoment script) collides with an enemy (handled by the ColliderAttackScript), the player is pushed back too far, making the gameplay feel unnatural.
Relevant Scripts
Here are the two scripts I’m working with:
- PlayerMoment Script:
using System.Collections;
using UnityEngine;
public class PlayerMoment : MonoBehaviour
{
// Movement Variables
[Header("Horizontal Movement Settings")]
public float moveSpeed; // The speed at which the player moves
private float moveDirectionX; // Horizontal input value
private float moveDirectionY; // Vertical input value
[Header("Knockback-System")]
[SerializeField] private float knockbackPower; // Multiplier for knockback force
private float knockbackPowerX = 7f; // Knockback force in X direction
private float knockbackPowerY = 0.8f; // Knockback force in Y direction
[HideInInspector] public bool isKnockedBack = false; // Check if the player is currently knocked back
[HideInInspector] public bool allowKnockback = true; // Allow knockback?
[Header("Jump Settings Player")]
[SerializeField] public float JumpForce; // Jump force value
[SerializeField] private Transform groundCheck; // Position for ground check
[SerializeField] private float groundCheckY = 0.2f; // Distance to check for ground
[SerializeField] private LayerMask WhatIsGround; // Layer mask for ground detection
[SerializeField] private float groundCheckX; // Horizontal offset for ground check
[Header("Important-System")]
public Rigidbody2D rb; // Rigidbody component for physics
Animator anim; // Animator component for animations
private TrailRenderer _trailRenderer; // Trail renderer for visual effect
[Header("GameObject-Referenzes")]
public GameObject RedKnight; // Reference to the red knight enemy
public GameObject BarsPosition; // Reference to the Bars enemy position
void Start()
{
rb = GetComponent<Rigidbody2D>(); // Get the Rigidbody2D component
anim = GetComponent<Animator>(); // Get the Animator component
_trailRenderer = GetComponent<TrailRenderer>(); // Get the TrailRenderer component
}
void Update()
{
if (!isKnockedBack) // Check if the player is not currently knocked back
{
GetInput(); // Get player input
Move(); // Move the player
Jump(); // Handle jumping
Flip(); // Flip the player's sprite based on direction
}
}
public void KnockbackSystem(Collision2D collision)
{
if (collision.gameObject.CompareTag("BarsEnemy")) // Check if colliding with Bars enemy
{
float direction = Mathf.Sign(transform.position.x - BarsPosition.transform.position.x);
Vector2 knockbackForce = new Vector2(direction * knockbackPowerX, knockbackPowerY * knockbackPower);
rb.AddForce(knockbackForce, ForceMode2D.Impulse); // Apply knockback force
isKnockedBack = true; // Set knockback status
StartCoroutine(ResetKnockback()); // Start coroutine to reset knockback
}
if (collision.gameObject.CompareTag("KnightRed")) // Check if colliding with red knight
{
if (allowKnockback == true)
{
float direction = Mathf.Sign(transform.position.x - RedKnight.transform.position.x);
Vector2 knockbackForce = new Vector2(direction * knockbackPowerX, knockbackPowerY * knockbackPower);
rb.AddForce(knockbackForce, ForceMode2D.Impulse); // Apply knockback force
isKnockedBack = true; // Set knockback status
StartCoroutine(ResetKnockback()); // Start coroutine to reset knockback
allowKnockback = false; // Prevent further knockback until reset
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
KnockbackSystem(collision); // Call the knockback system on collision
}
void GetInput()
{
moveDirectionX = Input.GetAxisRaw("Horizontal"); // Get horizontal input
moveDirectionY = Input.GetAxisRaw("Vertical"); // Get vertical input
}
void Move()
{
rb.velocity = new Vector2(moveSpeed * moveDirectionX, rb.velocity.y); // Move the player
anim.SetBool("isWalking", rb.velocity.x != 0 && Grounded()); // Set walking animation
}
void Flip()
{
if (moveDirectionX < 0) // If moving left
{
transform.localScale = new Vector2(-4, transform.localScale.y); // Flip the sprite
}
else if (moveDirectionX > 0) // If moving right
{
transform.localScale = new Vector2(4, transform.localScale.y); // Flip the sprite
}
}
public bool Grounded()
{
// Check if the player is grounded
return Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckY, WhatIsGround)
|| Physics2D.Raycast(groundCheck.position + new Vector3(groundCheckX, 0, 0), Vector2.down, groundCheckY, WhatIsGround)
|| Physics2D.Raycast(groundCheck.position + new Vector3(groundCheckX, 0, 0), Vector2.down, groundCheckY, WhatIsGround);
}
private void Jump()
{
if (Input.GetButtonUp("Jump") && (rb.velocity.y > 0))
{
rb.velocity = new Vector2(rb.velocity.x, 0); // Stop jump if button released
}
if (Input.GetButtonDown("Jump") && Grounded())
{
rb.velocity = new Vector2(rb.velocity.x, JumpForce); // Apply jump force
}
anim.SetBool("isJumping", !Grounded() || Input.GetButtonDown("Jump")); // Set jump animation
}
private IEnumerator ResetKnockback()
{
yield return new WaitForSeconds(0.5f); // Wait 0.5 seconds
isKnockedBack = false; // Reset knockback status
}
void OnDrawGizmos()
{
Gizmos.color = Color.red; // Set color to red
Gizmos.DrawWireSphere(groundCheck.position, groundCheckY); // Draw ground check sphere
}
}
ColliderAttackScript:
using System.Collections;
using UnityEngine;
public class ColliderAttackScript : MonoBehaviour
{
[Header("Important-Referenzes")]
private Rigidbody2D PlayerRb; // Reference to player's Rigidbody2D
private Animator RedKnightAnimation; // Reference to the enemy's Animator
public LayerMask PlayerLayer; // Layer mask for player detection
[Header("GameObject-Referenzes-And-Scripts")]
public GameObject AttackPoint; // Point of attack for collision detection
public GameObject player; // Reference to the player
public GameObject PlayerPosition; // Reference to player's position
private PlayerMoment playerMomentScript; // Reference to PlayerMoment script
private PlayerHealthSystem PlayerHealthScripts; // Reference to PlayerHealthSystem script
[Header("Attack-System")]
[SerializeField] private float attackRadius; // Radius for attack detection
[SerializeField] private int AttackDamage; // Damage inflicted on the player
[SerializeField] private float KnockbackMultiplier; // Multiplier for knockback force
private float knockbackPowerX = 7f; // Knockback force in X direction
private float KnockbackPowerY = 0.5f; // Knockback force in Y direction
void Start()
{
if (player != null)
{
RedKnightAnimation = player.GetComponent<Animator>(); // Get Animator component
}
PlayerHealthScripts = FindObjectOfType<PlayerHealthSystem>(); // Get PlayerHealthSystem
playerMomentScript = FindObjectOfType<PlayerMoment>(); // Get PlayerMoment script
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (player != null && collision.gameObject.CompareTag("Player")) // Check if colliding with player
{
RedKnightAnimation.SetTrigger("isAttacking"); // Trigger attack animation
AttacktSystem(); // Call the attack system
}
}
public void AttacktSystem()
{
if (AttackPoint == null) return; // Exit if AttackPoint is null
int combinedLayerMask = PlayerLayer; // Combine layer masks
Collider2D[] PlayerHit = Physics2D.OverlapCircleAll(AttackPoint.transform.position, attackRadius, combinedLayerMask); // Detect players in the attack radius
foreach (Collider2D playerGameObject in PlayerHit) // Iterate through hit players
{
PlayerHealthSystem playerHealthSystem = playerGameObject.GetComponent<PlayerHealthSystem>(); // Get PlayerHealthSystem
if (playerHealthSystem != null)
{
playerHealthSystem.TakeDamage(AttackDamage); // Inflict damage on player
playerMomentScript.allowKnockback = true; // Allow knockback for the player
float direction = Mathf.Sign(player.transform.position.x - playerGameObject.transform.position.x); // Determine direction for knockback
Vector2 knockbackForce = new Vector2(direction * knockbackPowerX * KnockbackMultiplier, KnockbackPowerY * KnockbackMultiplier);
playerGameObject.GetComponent<Rigidbody2D>().AddForce(knockbackForce, ForceMode2D.Impulse); // Apply knockback force
StartCoroutine(ResetKnockback()); // Start coroutine to reset knockback
}
}
}
IEnumerator ResetKnockback()
{
yield return new WaitForSeconds(0.5f); // Wait 0.5 seconds
playerMomentScript.allowKnockback = false; // Prevent further knockback until reset
}
void OnDrawGizmos()
{
Gizmos.color = Color.red; // Set color to red for visibility
Gizmos.DrawWireSphere(AttackPoint.transform.position, attackRadius); // Draw attack radius
}
}
Observations:
- The excessive knockback occurs specifically when the enemy attacks the player and both characters collide at the same time.
- It appears that the combined knockback forces from both the player’s and enemy’s scripts are too strong.
- I am considering using
Mathf.Clamp()to limit the knockback force but am unsure how to implement this correctly
Questions
- What could be the cause of the excessive knockback?
- How can I effectively clamp the knockback force to prevent this issue?
I would appreciate any insights or suggestions to resolve this problem. Thank you!