Forum begins after the advertisement:
[Part 3] My recoil wont effect the player
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 3] My recoil wont effect the player
- This topic has 2 replies, 3 voices, and was last updated 4 weeks ago by
Terence.
-
AuthorPosts
-
March 6, 2025 at 11:22 am #17393::
This is my code but it wont let my character recoil back when hitting a enemy also in the inspection bar it ticks recoilX and recoilY but never unticks them or recoils my character would apprieciate some help
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { [Header("Horizontal Movement Settings")] [SerializeField] private float Walkspeed = 1; [Space(5)] [Header("Vertical Movement Settings")] [SerializeField] private float Jumpforce = 45; private float jumpBufferCounter = 0; [SerializeField] private float jumpBufferFrames; private float coyoteTimeCounter = 0; [SerializeField] private float coyoteTime; private int airJumpCounter = 0; [SerializeField] private int maxAirJumps; [Space(5)] [Header("Ground Check Settings")] [SerializeField] private Transform groundCheckPoint; [SerializeField] private float groundCheckY = 0.2f; [SerializeField] private float groundCheckX = 0.5f; [SerializeField] private LayerMask whatIsGround; [Space(5)] [Header("Dash Settings")] [SerializeField] private float dashSpeed; [SerializeField] private float dashTime; [SerializeField] private float dashCooldown; [SerializeField] GameObject dashEffect; [Space(5)] [Header("Attacking")] bool attack = false; float timeBetweenAttack, timeSinceAttack; [SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea; [SerializeField] LayerMask attackableLayer; [SerializeField] float damage; [SerializeField] GameObject slashEffect; [Space(5)] [Header("Recoiling")] [SerializeField] int recoilXSteps = 5; [SerializeField] int recoilYSteps = 5; [SerializeField] float recoilXSpeed = 100; [SerializeField] float recoilYSpeed = 100; int stepsXRecoiled, stepsYRecoiled; PlayerStateList pState; private Rigidbody2D rb; private float xAxis, yAxis; private float gravity; private bool canDash; private bool dashed; Animator anim; public static PlayerController Instance; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } } // Start is called before the first frame update void Start() { rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); pState = GetComponent<PlayerStateList>(); gravity = rb.gravityScale; canDash = true; // Ensure dashing is enabled at the start } private void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea); Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea); Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea); } // Update is called once per frame void Update() { GetInputs(); UpdateJumpVariables(); if (pState.dashing) return; Flip(); Move(); Jump(); StartDash(); Attack(); } void GetInputs() { xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); attack = Input.GetMouseButtonDown(0); } void Flip() { if (xAxis < 0) { transform.localScale = new Vector2(-1, transform.localScale.y); pState.lookingRight = false; } else if (xAxis > 0) { transform.localScale = new Vector2(1, transform.localScale.y); pState.lookingRight = true; } } private void Move() { rb.velocity = new Vector2(Walkspeed * xAxis, rb.velocity.y); anim.SetBool("Walking", rb.velocity.x != 0 && Grounded()); } void StartDash() { if (Input.GetButtonDown("Dash")) { if (!canDash) { } else if (dashed) { } else { StartCoroutine(Dash()); dashed = true; } } if (Grounded()) { dashed = false; } } IEnumerator Dash() { canDash = false; // Disable dashing until cooldown is finished pState.dashing = true; anim.SetTrigger("Dashing"); rb.gravityScale = 0; rb.velocity = new Vector2(transform.localScale.x * dashSpeed, 0); if(Grounded()) Instantiate(dashEffect, transform); yield return new WaitForSeconds(dashTime); // Dash duration pState.dashing = false; // Stop the dash animation anim.ResetTrigger("Dashing"); // Reset the dash trigger in case it was still set rb.gravityScale = gravity; pState.dashing = false; yield return new WaitForSeconds(dashCooldown); // Dash cooldown time canDash = true; // Re-enable dashing after cooldown } void Attack() { timeSinceAttack += Time.deltaTime; if(attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attacking"); if(yAxis == 0 || yAxis < 0 && Grounded()) { Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, recoilXSpeed); Instantiate(slashEffect, SideAttackTransform); } else if(yAxis > 0) { Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, recoilYSpeed); SlashEffectAtAngle(slashEffect, 80, UpAttackTransform); } else if(yAxis < 0 && !Grounded()) { Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, recoilYSpeed); SlashEffectAtAngle(slashEffect, -90, DownAttackTransform); } } } void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength) { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); if(objectsToHit.Length > 0) { _recoilDir = true; } for(int i = 0; i < objectsToHit.Length; i++) { if(objectsToHit[i].GetComponent<Enemy>() !=null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, (transform.position - objectsToHit[i].transform.position).normalized, _recoilStrength); } } } void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform) { _slashEffect = Instantiate(_slashEffect, _attackTransform); _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle); _slashEffect.transform.localScale = new Vector2(transform.localScale.x, _attackTransform.localScale.y); } void Recoil() { if(pState.recoilingX) { if(pState.lookingRight) { rb.velocity = new Vector2 (-recoilXSpeed, 0); } else { rb.velocity = new Vector2 (recoilXSpeed, 0); } } if(pState.recoilingY) { rb.gravityScale = 0; if(yAxis < 0 ) { rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed); } else { rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed); } airJumpCounter = 0; } else { rb.gravityScale = gravity; } //stop recoil if(pState.recoilingX && stepsXRecoiled < recoilXSteps) { stepsXRecoiled++; } else { StopRecoilX(); } if(pState.recoilingY && stepsYRecoiled < recoilYSteps) { stepsYRecoiled++; } else { StopRecoilY(); } if(Grounded()) { StopRecoilY(); } } void StopRecoilX() { stepsXRecoiled = 0; pState.recoilingX = false; } void StopRecoilY() { stepsYRecoiled = 0; pState.recoilingY = false; } public bool Grounded() { if (Physics2D.Raycast(groundCheckPoint.position, Vector2.down, groundCheckY, whatIsGround) || Physics2D.Raycast(groundCheckPoint.position + new Vector3(-groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround) || Physics2D.Raycast(groundCheckPoint.position + new Vector3(groundCheckX, 0, 0), Vector2.down, groundCheckY, whatIsGround)) { return true; } else { return false; } } void Jump() { if (Input.GetButtonUp("Jump") && rb.velocity.y > 0) { rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); // Reduce jump height pState.jumping = false; } if (jumpBufferCounter > 0 && coyoteTimeCounter > 0) { rb.velocity = new Vector3(rb.velocity.x, Jumpforce); pState.jumping = true; } else if (!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump")) { pState.jumping = true; airJumpCounter++; rb.velocity = new Vector3(rb.velocity.x, Jumpforce); } anim.SetBool("Jumping", !Grounded()); } void UpdateJumpVariables() { if (Grounded()) { pState.jumping = false; coyoteTimeCounter = coyoteTime; airJumpCounter = 0; jumpBufferCounter = 0; // Reset jump buffer when grounded } else { coyoteTimeCounter -= Time.deltaTime; } if (Input.GetButtonDown("Jump")) { jumpBufferCounter = jumpBufferFrames; } else { jumpBufferCounter = jumpBufferCounter - Time.deltaTime *10; } } }
March 6, 2025 at 11:31 am #17394::It looks like you’re trying to implement a recoil effect for your character when attacking an enemy. However, it seems that the recoil is not being executed correctly, as you mentioned that the
recoilX
andrecoilY
flags are not being unticked or reset as expected.Here are a few suggestions to help you troubleshoot and fix the recoil behavior in your code:
-
Ensure Recoil is Called: You need to call the
Recoil()
method in theUpdate()
function. Currently, it seems that this method isn’t being called anywhere in your code. Add a call toRecoil()
in theUpdate()
method after checking for inputs and before other actions:void Update() { GetInputs(); UpdateJumpVariables(); Recoil(); // Call Recoil here if (pState.dashing) return; Flip(); Move(); Jump(); StartDash(); Attack(); }
-
Resetting the Recoil Flags: Ensure that the recoil flags (
pState.recoilingX
andpState.recoilingY
) are being set tofalse
after the recoil steps have been exhausted. You already haveStopRecoilX()
andStopRecoilY()
methods for this, but you should make sure they are getting called correctly. -
Check Attack Logic: In your
Hit()
method, you set_recoilDir
totrue
if an enemy is hit. Ensure that this flag is correctly being passed back to thePlayerStateList
and that it impacts the recoil behavior:if(objectsToHit.Length > 0) { _recoilDir = true; // Here you should set the corresponding state in PlayerStateList pState.recoilingX = true; // Example of setting recoiling state pState.recoilingY = true; // Also set this if needed }
-
Check Conditions in Recoil Method: In the
Recoil
method, you should ensure that the logic for dealing withstepsXRecoiled
andstepsYRecoiled
is functioning correctly. If recoil steps are not being incremented, check the conditions under which they are being updated. -
Debugging: Add some debug statements in critical parts of your code to help identify where the logic might be failing. For instance:
void Recoil() { if (pState.recoilingX) { Debug.Log("Recoiling X"); // Existing recoil logic... } if (pState.recoilingY) { Debug.Log("Recoiling Y"); // Existing recoil logic... } }
-
Adjusting Physics Settings: Ensure that the Rigidbody2D settings (like gravity scale and collision detection) are set up correctly. Sometimes physics configurations can also affect how movement is applied.
By following these suggestions, you should be able to better diagnose why your recoil isn’t working as intended and get it functioning properly. If you continue to face issues, consider narrowing down the problem further by isolating the recoil logic and testing it separately.
has upvoted this post. March 6, 2025 at 1:03 pm #17395::I put recoil into the update function and I got debug log to check if it recoils and it doesnt come up with anything
March 6, 2025 at 1:06 pm #17396::It sounds like you are trying to implement a recoil effect in your update function, but you’re not seeing the expected debug logs. Here are a few troubleshooting steps and tips that may help you identify the issue:
-
Check Your Update Function: Make sure that your update function is being called every frame. If it’s not, the recoil logic inside it won’t execute.
function update() { // Your recoil logic here console.log("Update function called"); } function gameLoop() { update(); requestAnimationFrame(gameLoop); } gameLoop();
-
Verify Recoil Logic: Ensure that your recoil logic is correctly implemented. If recoil is based on certain conditions (like player input), make sure those conditions are being met.
let recoilAmount = 0; function update() { if (isShooting) { // Replace with your shooting condition recoilAmount += 1; // Adjust this value as needed console.log("Recoil applied: ", recoilAmount); } }
-
Debug Logging: Confirm that your debug log statements are correctly placed. If they are inside a conditional block, they will only execute if that condition is true.
-
Recoil Reset Logic: If you are applying recoil and then resetting it, ensure the reset logic is working properly. If recoil is reset too quickly, you might not see any output.
-
Console Settings: Make sure your console is set up to show all log messages. Sometimes, certain log levels may be filtered out.
-
Test with Simple Conditions: For debugging, you can simplify your conditions to ensure that your logging works as expected. For example, log every frame regardless of whether the player is shooting.
function update() { console.log("Update called"); if (isShooting) { console.log("Shooting detected"); recoilAmount += 1; console.log("Recoil applied: ", recoilAmount); } }
-
Check for Errors: Look for any errors in the console that may be stopping the execution of your script. An error in a different part of your code could prevent the update function from running properly.
If you’ve gone through these steps and are still having issues, feel free to share your relevant code snippets, and I can help you troubleshoot further!
March 6, 2025 at 2:53 pm #17398::@joshkelly, try to add the following messages, and let me know if the messages print on your console when you get hit:
void Recoil() { if(pState.recoilingX) { print("Recoiling X condition met"); if(pState.lookingRight) { rb.velocity = new Vector2 (-recoilXSpeed, 0); } else { rb.velocity = new Vector2 (recoilXSpeed, 0); } } if(pState.recoilingY) { print("Recoiling Y condition met"); rb.gravityScale = 0; if(yAxis < 0 ) { rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed); } else { rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed); } airJumpCounter = 0; } else { rb.gravityScale = gravity; } //stop recoil if(pState.recoilingX && stepsXRecoiled < recoilXSteps) { stepsXRecoiled++; } else { StopRecoilX(); } if(pState.recoilingY && stepsYRecoiled < recoilYSteps) { stepsYRecoiled++; } else { StopRecoilY(); } if(Grounded()) { StopRecoilY(); } }
-
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: