Help with 2D Action J-RPG Collisions

Hi!, I’m trying to recreate some sort of Action JRPG Battle (Sword of Mana, Legend of Zelda: A link to the past), but when I try to make colissions only the first one works, after that one it seems like you have to leave the collision area and re-enter on it for another collision to work…

I’m currently using the “OnTriggerEnter2D” method but is not working correctly.
I have already tried the “OnTriggerExit2D” but it works pretty much the same but inverted…
If I use the method “OnTriggerStay2D” a lot of collisions take place in a single attack, and the objective dies in a single hit.

I have recorded a video to show the actual behavior:

And here’s the code of the Player:

using UnityEngine;
using System.Collections;

public class PlayerControllerV3 : MonoBehaviour
{
    Animator anim;
    Rigidbody2D rb;

    private SpriteRenderer playerRenderer;

    bool isAttacking;
    bool isWalking;

    public BoxCollider2D attackTrigger;
    public float attackTriggerSize;

    public Vector2 speedVector;
    public int life;
    public int magic;

    private float attackTimer;
    public float attackCoolDown;
    public float damageAnimDuration;


    // Use this for initialization
    void Start ()
    {
        anim = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
        playerRenderer = GetComponent<SpriteRenderer>();
        anim.SetFloat("x", 1);

        attackTrigger.enabled = false;
    }

    //Update is called once per frame
    void Update()
    {
        // ***PLAYER DIES***
        if (life < 1)
        {
            //Call GameOver
            Destroy(gameObject);
        }

        // *** MOVEMENT ***
        float input_x = Input.GetAxisRaw("Horizontal");
        float input_y = Input.GetAxisRaw("Vertical");
        //if the absolute value of "input_x" OR "input_y" is greater than 0 the player IS walking
        if (Mathf.Abs(input_x) + Mathf.Abs(input_y) > 0)
        {
            isWalking = true;
            playerMoves(input_x, input_y);
        }
        else
        {
            isWalking = false;
            anim.SetBool("isWalking", isWalking);
            rb.velocity = new Vector2(0,0);
        }

        // *** PLAYER ATTACK START ***
        if (Input.GetKeyDown("j") && isAttacking == false)
        {
            playerStartsAttack();
        }
        // *** PLAYER ATTACK FINISHES ***
        if (isAttacking)
        {
            playerFinishesAttack();
        }
    }

    // Player Moves
    void playerMoves(float in_x,float in_y)
    {
        anim.SetBool("isWalking", isWalking);
        //detect direction and normalize
        Vector2 movement = new Vector2(in_x,in_y).normalized;
        //Add speed
        movement.x *= speedVector.x;
        movement.y *= speedVector.y;
        //Add time-syncronizing with UnityEngine
        movement *= Time.deltaTime;
        //Add movement speed to the rigidbody
        rb.velocity = movement;

        //show anims
        anim.SetFloat("x", in_x);
        anim.SetFloat("y", in_y);
    }

    // Player Starts Attack
    void playerStartsAttack()
    {
        rb.velocity = new Vector2(0,0);
        isAttacking = true;
        attackTimer = attackCoolDown;

        if(anim.GetFloat("x") < 0) //LEFT
        {
            attackTrigger.offset = new Vector2( attackTriggerSize*-1 , attackTriggerSize*0 );
            attackTrigger.enabled = true;
        }
        else if(anim.GetFloat("x") > 0) //RIGHT
        {
            attackTrigger.offset = new Vector2( attackTriggerSize*1 , attackTriggerSize*0 );
            attackTrigger.enabled = true;
        }
        else if(anim.GetFloat("y") > 0) //UP
        {
            attackTrigger.offset = new Vector2( attackTriggerSize*0 , attackTriggerSize*1 );
            attackTrigger.enabled = true;
        }
        else if(anim.GetFloat("y") < 0) //DOWN
        {
            attackTrigger.offset = new Vector2( attackTriggerSize*0 , attackTriggerSize*-1 );
            attackTrigger.enabled = true;
        }
    }

    // Player Ends Attack
    void playerFinishesAttack()
    {
        if (isAttacking)
        {
            if(attackTimer > 0)
            {
                rb.velocity = new Vector2(0, 0);
                attackTimer -= Time.deltaTime;
            }
            else //attack has ended
            {
                isAttacking = false;
                attackTrigger.enabled = false;
            }
        }
        anim.SetBool("isAttacking",isAttacking);
    }

    // Player receives damage
    IEnumerator playerDamage(int damage)
    {
        life -= damage;
        playerRenderer.color = new Color (255,0,0,255);
        yield return new WaitForSeconds(damageAnimDuration);
        playerRenderer.color = new Color(255, 255, 255, 255);
        yield break;
    }
}
using UnityEngine;
using System.Collections;

public class PlayerAttackTrigger : MonoBehaviour
{
    public int damage = 20;

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.isTrigger != true && col.CompareTag("Enemy"))
        {
            col.SendMessageUpwards("enemyDamage", damage);
        }
    }
}

Any ideas?
Can anyone help me?, thanks in advance.

So is the collision suppose to make the enemy attack? Are you using the collision to detect if your close enough to do damage?
If just need to know if your close enough, have the OnTriggerEnter2D set a bool on your main Player that closeEnough is true. and have the OnTriggerExit2D have it set it to false.

Just a rundown
OnTriggerEnter — Fires one time when the two colliders first collide
OnTriggerStay – Fires every update as long as the two are still “collided”
OnTriggerExit – Fires one time when the two colliders pull apart.

So just Use enter to set some variables that you are in range of the enemy (or have the enemy start auto attacking)
then use Exit to stop the enemy autoattack, and set variables that you aren’t in range.

1 Like

Actually, it doesn’t work like that.
I enable the “PlayerAttackTrigger” when I press the “J” key in keyboard and enable the “EnemyAttackTrigger” on the Enemy when Player is in range.
So… The “PlayerAttackTrigger” collides with the Enemy “BoxCollider2D” and this should make a HIT (and it does) and send a message to take Damage from the Enemy, so far so good.
The problem starts here, if both, player and enemy stay in the same position and don’t move around the map, the sucessives collisions do not work.

This was the tutorial that I followed to make the melee attcks, in this video the sucessive collisions works good, I can’t understand why they don’t work in my case:

On TriggerExit you can use:
otherRigid.isKinematic = true;
otherRigid.isKinematic = false;
to toggle the other rigidbody to register the trigger next time. If that doesn’t work, try it on the player, or the sword if it has rigidbody. It is a workaround, but for me it works.

1 Like

Is the J key suppose to be a permanant toggle of I want to attack. Or just 1 swing?
Either way, it still sems like you can just have a few boolean values to get your design to work:
If J is an “auto attack” then has soon as the user presses j. just set some bool autoAttack = true;
Then in your OnTriggerEnter set 2 values:
bool InRange = true
GameObject enemy = the thing I collided with.

Then in your update function:
if (autoAttack == true && inRange == true)
AttackEnemy(enemy)

IF J is just a single attack. When they press J just check if InRange == true and attack that enemy.

Lastly, In OnTriggerExit
set InRange = false
Enemy = null

1 Like

I’ve tried this one, it’s not working.

It’s just 1 swing/hit/attacks, when I press the “J” key an animation that lasts 0.5 seconds take place, at the same time a “Trigger” is enabled for the same amount of time, if that trigger collides whit the boxcollider of the enemy then I send a “Message” to call the function “enemyDamage” and proceed to Subtract 20 HealthPoints.
The problem seems to be it only works the first time.

Another workaround - just move the trigger out of the screen for a frame and then back. Anyway there must be an error somewhere. You can try toggling the isTrigger property between attacks.

Then add a new variable to your player’

using System.Collections.Generic;

List<GameObect> enemiesInRange = new List<GameObject>;

Then everytime your OnTrigger Enter fires for the player, add that Enemy to the list
and Everytime your OnTriggerExit fires remove it from the list.

When you press J either send 20 damage to all the enemies in that list (AoE style)
or just the first Enemy on the List.

You have to detect if an enemy dies and is destroyed and remove it from the list as well.

I recommend you to use different systems for damage over time and visual collision. I mean, you can just raycast or do the physic overlap to check your enemy “can be hit” and then start doing damage over time paced as your animation, but I would’t use the physics to trigger damage. a lot of stuff can go wrong doing that, and you’ll end up doing a massive amount of work just for all the weird cases.

1 Like

I’m kind of a novice here and I find what you said to be true, it seems like the approach seems wrong, too much effort to make something work that should be simpler to implement.
Any good tutorials you can recommend on the subject?? (raycasting2D/taking damage from enemies/damage over time and animation coordination)

I’d go to paper, and sketch. BTW think on a system that “grabs an enemy” and then every X seconds does “amount of damage” to it. once you get the enemy in range you grab it as an objective in your player controller, and then in the update you check what’s your state: is it attaking enemy then you start doing damage to it and you do your animation. But keep the animation outside of the damage thing.

I don’t know a tutorial about this, but I made this for a tower defense game I did, and it was like this.

Also make sure to use player states to differentiate different situations, like “recovering”, “looking for enemy”, “attacking enemy” and so on…

Edit: Also remember to use the AnimationController to control your anims, this way once you trigger damage from your player controller you can tell the animation controller to move to the animation state you want. Look for the unity tutorials, I’d go over them once before going for full project, if you’re novice, look at them in detail as you’ll discover a lot of things and then you’ll go faster doing your own project.

1 Like