Forum begins after the advertisement:
[Part 3] Issue with Slash Effect
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 3] Issue with Slash Effect
- This topic has 11 replies, 3 voices, and was last updated 3 days, 3 hours ago by
Sean Ng.
-
AuthorPosts
-
May 23, 2026 at 5:34 am #19652::
Here is my code:
using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; 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 int jumpBufferCounter = 0; [SerializeField] private int 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; PlayerStateList pState; private Rigidbody2D rb; private float xAxis, yAxis; private float gravity; Animator anim; private bool canDash = true; private bool dashed; public static PlayerController Instance; void Awake() { if(Instance != null && Instance != this) { Destroy(this.gameObject); } else { Instance = this; } } // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { pState = GetComponent<PlayerStateList>(); rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); gravity = rb.gravityScale; } 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); } else if (xAxis > 0) { transform.localScale = new Vector2(1, transform.localScale.y); } } private void Move() { rb.linearVelocity = new Vector2(walkSpeed * xAxis, rb.linearVelocity.y); anim.SetBool("Walking", rb.linearVelocity.x != 0 && Grounded()); } void StartDash() { if (Input.GetButtonDown("Dash") && canDash && !dashed) { StartCoroutine(Dash()); dashed = true; } if (Grounded()) { dashed = false; } } IEnumerator Dash() { canDash = false; pState.dashing = true; anim.SetTrigger("Dashing"); rb.gravityScale = 0; rb.linearVelocity = new Vector2(transform.localScale.x * dashSpeed, 0); if (Grounded()) Instantiate(dashEffect, transform); yield return new WaitForSeconds(dashTime); rb.gravityScale = gravity; pState.dashing = false; yield return new WaitForSeconds(dashCooldown); canDash = true; } void Attack() { timeSinceAttack += Time.deltaTime; if (attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attacking"); if (yAxis == 0 || yAxis < 0 && Grounded()) { Hit(SideAttackTransform, SideAttackArea); Instantiate(slashEffect, SideAttackTransform); } else if (yAxis > 0) { Hit(UpAttackTransform, UpAttackArea); SlashEffectAtAngle(slashEffect, 90, UpAttackTransform); } else if (yAxis < 0 && !Grounded()) { Hit(DownAttackTransform, DownAttackArea); SlashEffectAtAngle(slashEffect, -90, DownAttackTransform); } } } private void Hit(Transform _attackTransform, Vector2 _attackArea) { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); if(objectsToHit.Length > 0) { Debug.Log("hit"); } for (int i = 0; i < objectsToHit.Length; i++) { if (objectsToHit[i].GetComponent<Enemy>() != null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage); } } } 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, transform.localScale.y); } 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.linearVelocity.y > 0) { rb.linearVelocity = new Vector2(rb.linearVelocity.x,0); pState.jumping = false; } if (!pState.jumping) { if (jumpBufferCounter > 0 && coyoteTimeCounter > 0) { rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce); pState.jumping = true; } else if (!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump")) { pState.jumping = true; airJumpCounter++; rb.linearVelocity = new Vector3(rb.linearVelocity.x, jumpForce); } } anim.SetBool("Jumping", !Grounded()); } void UpdateJumpVariables() { if(Grounded()) { pState.jumping = false; coyoteTimeCounter = coyoteTime; airJumpCounter = 0; } else { coyoteTimeCounter -= Time.deltaTime; } if (Input.GetButtonDown("Jump")) { jumpBufferCounter = jumpBufferFrames; } else { jumpBufferCounter--; } } }May 23, 2026 at 5:03 pm #19653::Can you show me how the pivot is set for your slash effect? Open the sprite in the Sprite Editor, select the Sprite and take a screenshot of it.
May 24, 2026 at 12:39 am #19654May 24, 2026 at 10:05 pm #19656::Do you see the Pivot dropdown in the bottom right corner of your screenshot? Set the pivot to Left Center and see if it fixes your issue.
If that still doesn’t work, upload your project onto GitHub (by following the video below), then post the link here.
May 26, 2026 at 11:21 am #19675May 26, 2026 at 12:29 pm #19677::Are you using github desktop when setting up the project folder, as you should not need to use command prompt.
After you make a project folder through github desktop you just need to drag 3 files from github project folder into you unity project folder, from github desktop you’ll need to change location to your unity project folder and it will show and changes, you’ll just need to commit then send the link and we can see the problem.
the 3 files are .gitattributes, .gitignore and .git, .git is hidden set your file explorer to show hidden items and you can drag it.
May 27, 2026 at 5:56 am #19684May 27, 2026 at 5:57 am #19685May 27, 2026 at 6:08 am #19686::Oh, I figured out the problem. Here is the link to the GitHub repository: https://github.com/kartikeydino/Metroidvania-Teresquall-Git
May 27, 2026 at 12:33 pm #19687::Hi there again, for the slash effect is your issue with the flow of the animation due to scaling? If so you just need to change the Sprite Mode for each to Single then replace the sprites within the slash animation.
View post on imgur.com
The reason why it doesn’t flow smoothly is because each sprite is a different size, when you enter sprite editor you will see unity automatically slice it but it will cut as small as possible leaving out the transparent parts, when making animation you want to have each sprite the same size even is there is alot of empty space, as most sprite animation when created follow a template size to make it more easily.
May 28, 2026 at 9:29 am #19691::That is not my issue. My issue is that whenever I’m jumping, or falling, the attacks always happen at the side.
May 28, 2026 at 10:25 am #19692 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: