1 (edited by xFTLxKingPhoenix 2017-01-03 18:32:18)

Topic: Parry as a move

Is there any way to make a move that acts as a parry?

I want to create a parry  using the move editor as parrying is not a mechanic I want to use for every character

But I also don't want the parry to be a simple button press. I want it to be an actual move with input sequence.

I also want to create a reflect

Eternal Rift Studios
    Current Projects:
         Destined Soels
Always happy to help when possible. If its something pretty minor ill just help you out. But i do commissions as well. Hit me up if you need a commission.

Share

Thumbs up Thumbs down

Re: Parry as a move

I would like to see this functionality as well. Toying with the idea of trying some sword dueling samurai style...

Share

Thumbs up Thumbs down

Re: Parry as a move

I will need the same thing, any progress on your end?

Share

Thumbs up Thumbs down

Re: Parry as a move

Nope. sad

Share

Thumbs up Thumbs down

5 (edited by EvilDogGames 2017-09-10 21:22:48)

Re: Parry as a move

Off the bat I imagine the parry move could be a move with invincible frames or an armor properties
And the move would have hitboxes and hurt boxes that matches the parry pose, and that move on hit would have an enemy override to perform the move on the other player, kinda like the throws are done, with the attempt, the confirm and the reaction.
That's roughly toward where I will attempt this armor mechanic.

Share

Thumbs up Thumbs down

Re: Parry as a move

I have working solution so far, I changed a bit of code to have a new Hit Confirm Type called Parry it tests like a hit but behaves like a Throw with the opponent override and all that. I set it as an invincible move for the first 10 frames and the hurt box is basically inside the character during the same 10 frames so as soon as an attack comes in, you'll hit the guy and the "throw" like behaviour will kick in.  The confirm move is fast and the reaction move is slower so you can parry and combo during the reaction.

Share

Thumbs up +1 Thumbs down

Re: Parry as a move

Furthermore, I created a new TestCollision function that tests hurt boxes against hurt boxes, since I only want the parry move to "hit" incoming hurt boxes, not hit boxes that can happen to enter the parry zone just based on the animation.
If anyone is interested I can post a more detailed explanation of the changes I made.

Share

Thumbs up Thumbs down

Re: Parry as a move

EvilDogGames wrote:

I have working solution so far, I changed a bit of code to have a new Hit Confirm Type called Parry it tests like a hit but behaves like a Throw with the opponent override and all that. I set it as an invincible move for the first 10 frames and the hurt box is basically inside the character during the same 10 frames so as soon as an attack comes in, you'll hit the guy and the "throw" like behaviour will kick in.  The confirm move is fast and the reaction move is slower so you can parry and combo during the reaction.

That sounds awesome! If you ever feel like sharing more details or even a tutorial for a good parry that would be really cool. One of my envisioned projects, if I ever get to it will have samurai slashing and parrying.

Share

Thumbs up Thumbs down

Re: Parry as a move

Let's see if i can share it quick

First thing is to add the Parry type in the HitConfirmType enum in MoveInfo.cs
It becomes

public enum HitConfirmType {
    Hit,
    Throw,
    Parry
}

It will also become available in the inspector to set your move's hit type as a Parry hit

In MoveEditorWindow.cs, this line

castingValues.Add(new Vector3(hit.activeFramesBegin, hit.activeFramesEnds, (hit.hitConfirmType == HitConfirmType.Throw ? 1: 0)));

Becomes this line

castingValues.Add(new Vector3(hit.activeFramesBegin, hit.activeFramesEnds, (hit.hitConfirmType == HitConfirmType.Throw || hit.hitConfirmType == HitConfirmType.Parry ? 1: 0)));

¸

That does....something....

This line

else if (moveInfo.hits[i].hitConfirmType == HitConfirmType.Throw)

Becomes this, to let the move editor show the throw confirm move and stuff for a parry, which essentially work as a throw (attempt, confirm, reaction)

else if (moveInfo.hits[i].hitConfirmType == HitConfirmType.Throw || moveInfo.hits[i].hitConfirmType == HitConfirmType.Parry)

This line

if (moveInfo.moveClassification.hitConfirmType == HitConfirmType.Throw)

Becomes this, for some reason smile

if (moveInfo.moveClassification.hitConfirmType == HitConfirmType.Throw || moveInfo.moveClassification.hitConfirmType == HitConfirmType.Parry)

That takes care of the move setup and the move editor

Now in ControlsScript.cs

This

// Throw
                        }else if (hit.hitConfirmType == HitConfirmType.Throw){
                            CastMove(hit.throwMove, true);
                            return;

Becomes this, to make the parry work as a throw confirm when it connects

// Throw/Parry
                        }else if (hit.hitConfirmType == HitConfirmType.Throw || hit.hitConfirmType == HitConfirmType.Parry){
                            CastMove(hit.throwMove, true);
                            return;

This code

// Parry
                        }else if (opControlsScript.potentialParry > 0 
                                  && opControlsScript.currentMove == null 
                                  && hit.hitConfirmType != HitConfirmType.Throw 
                                  && opControlsScript.TestParryStances(hit.hitType)
                                  ){

Becomes this, to make normal UFE parries not consider parry hits just like it doesn't consider throw hits

// Parry
                        }else if (opControlsScript.potentialParry > 0 
                                  && opControlsScript.currentMove == null 
                                  && hit.hitConfirmType != HitConfirmType.Throw 
                                  && hit.hitConfirmType != HitConfirmType.Parry 
                                  && opControlsScript.TestParryStances(hit.hitType)
                                  ){

Now we're in the final file to modify: HitBoxesScript.cs

This code

public Vector3[] TestCollision(HurtBox[] hurtBoxes, HitConfirmType hitConfirmType) {
        if (isHit && hitConfirmType == HitConfirmType.Hit) return new Vector3[] { };

Becomes this, to make hit and parry collisions happen only once (I think)

public Vector3[] TestCollision(HurtBox[] hurtBoxes, HitConfirmType hitConfirmType) 
    {
        if(isHit && (hitConfirmType == HitConfirmType.Hit || hitConfirmType == HitConfirmType.Parry))
        {
            return new Vector3[] { };
        }

A few lines down, this code

        return HitBoxesScript.TestCollision(this.hitBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);

Becomes this, to call the new collision test for the parry, we'll want to test against active hurt boxes to parry an incoming attack and not any hit collider on the character that would enter the parry zone

// A parry will check against other hurt boxes, not hit boxes
        if(hitConfirmType == HitConfirmType.Parry)
        {
            return HitBoxesScript.TestCollision(this.activeHurtBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);
        }

        return HitBoxesScript.TestCollision(this.hitBoxes, hurtBoxes, hitConfirmType, controlsScript.mirror);

Now to display the active parry frames when editing the move (how throws are orange and hits are...some other color, Cyan)
This code

if (hitConfirmType == HitConfirmType.Throw){
                Gizmos.color = new Color(1f, .5f, 0);
            }else{
                Gizmos.color = Color.cyan;
            }

Becomes this, of course put whatever color you want, I put red, arbitrarily

if(hitConfirmType == HitConfirmType.Throw)
            {
                Gizmos.color = new Color(1f, .5f, 0);
            }
            else if (hitConfirmType == HitConfirmType.Parry)
            {
                Gizmos.color = new Color(1f, 0.0f, 0.0f);
            }
            else
            {
                Gizmos.color = Color.cyan;
            }

And finally, the actual new function to test collision for the parry on the opponent's active hurt boxes.

public static Vector3[] TestCollision(HurtBox[] activeHurtBoxes, HurtBox[] hurtBoxes, HitConfirmType hitConfirmType, int mirror) {
        if(activeHurtBoxes == null) return new Vector3[]{ };
        foreach (HurtBox activeHurtBox in activeHurtBoxes) {
            activeHurtBox.state = 0;
            //drawRect.Clear();
            foreach (HurtBox hurtBox in hurtBoxes) {
                Vector3 hurtBoxPosition = hurtBox.position;
                Vector3 activeHurtBoxPosition = activeHurtBox.position;
                float dist = 0;
                bool collisionConfirm = false;

                if (!UFE.config.detect3D_Hits){
                    hurtBoxPosition.z = -1;
                    activeHurtBoxPosition.z = -1;
                }

                if (hurtBox.shape == HitBoxShape.circle){
                    if (activeHurtBox.shape == HitBoxShape.circle){
                        dist = Vector3.Distance(hurtBoxPosition, activeHurtBoxPosition);
                        if (dist <= hurtBox.radius + activeHurtBox.radius) collisionConfirm = true;

                    }else if (activeHurtBox.shape == HitBoxShape.rectangle){
                        Rect activeHurtBoxRectanglePosition = new Rect(activeHurtBox.rect);
                        activeHurtBoxRectanglePosition.x *= -mirror;
                        activeHurtBoxRectanglePosition.width *= -mirror;
                        activeHurtBoxRectanglePosition.x += activeHurtBoxPosition.x;
                        activeHurtBoxRectanglePosition.y += activeHurtBoxPosition.y;

                        if (activeHurtBox.followXBounds) {
                            activeHurtBoxRectanglePosition.x = activeHurtBox.rendererBounds.x - (activeHurtBox.rect.width / 2);
                            activeHurtBoxRectanglePosition.width = (activeHurtBox.rendererBounds.width + activeHurtBox.rect.width) - activeHurtBox.rendererBounds.x;
                        }
                        if (activeHurtBox.followYBounds) {
                            activeHurtBoxRectanglePosition.y = activeHurtBox.rendererBounds.y - (activeHurtBox.rect.height / 2);
                            activeHurtBoxRectanglePosition.height = (activeHurtBox.rendererBounds.height + activeHurtBox.rect.height) - activeHurtBox.rendererBounds.y;
                        }

                        dist = distancePointToRectangle(hurtBoxPosition, activeHurtBoxRectanglePosition);
                        if (hurtBox.radius >= dist) collisionConfirm = true;


                        /*if (collisionConfirm && !hurtBox.isBlock) {
                            Debug.Log("------------------");
                            Debug.Log(hurtBoxPosition);
                            Debug.Log(activeHurtBox.bodyPart + " - " + activeHurtBoxRectanglePosition);
                            Debug.Log("xMin/xMax,yMin/yMax : " + activeHurtBoxRectanglePosition.xMin + "/" + activeHurtBoxRectanglePosition.xMax + ", " + activeHurtBoxRectanglePosition.yMin + "/" + activeHurtBoxRectanglePosition.yMax);
                            Debug.Log(hurtBox.radius + " >= " + dist + " = " + collisionConfirm);
                        }*/
                    }
                }else if (hurtBox.shape == HitBoxShape.rectangle){
                    /* Overlap doesn't work with negative width
                    Rect hurtBoxRectanglePosition = new Rect(hurtBox.rect);
                    hurtBoxRectanglePosition.x *= -mirror;
                    hurtBoxRectanglePosition.width *= -mirror;
                    hurtBoxRectanglePosition.x += hurtBoxPosition.x;
                    hurtBoxRectanglePosition.y += hurtBoxPosition.y;*/

                    float mirrorDiff = mirror < 0 ? hurtBox.rect.width : 0f;
                    Rect hurtBoxRectanglePosition = new Rect(((hurtBox.rect.x + mirrorDiff) * mirror) + hurtBoxPosition.x,
                        hurtBox.rect.y + hurtBoxPosition.y,
                        hurtBox.rect.width, hurtBox.rect.height);

                    if (activeHurtBox.shape == HitBoxShape.circle){

                        if (hurtBox.followXBounds){
                            hurtBoxRectanglePosition.x = hurtBox.rendererBounds.x - (hurtBox.rect.width/2);
                            hurtBoxRectanglePosition.width = (hurtBox.rendererBounds.width + hurtBox.rect.width) - hurtBox.rendererBounds.x;
                        }
                        if (hurtBox.followYBounds){
                            hurtBoxRectanglePosition.y = hurtBox.rendererBounds.y - (hurtBox.rect.height/2);
                            hurtBoxRectanglePosition.height = (hurtBox.rendererBounds.height + hurtBox.rect.height) - hurtBox.rendererBounds.y;
                        }

                        dist = distancePointToRectangle(activeHurtBoxPosition, hurtBoxRectanglePosition);
                        if (dist <= activeHurtBox.radius) collisionConfirm = true;

                    }else if (activeHurtBox.shape == HitBoxShape.rectangle){
                        /* Overlap doesn't work with negative width
                        Rect activeHurtBoxRectanglePosition = new Rect(activeHurtBox.rect);
                        activeHurtBoxRectanglePosition.x *= -mirror;
                        activeHurtBoxRectanglePosition.width *= -mirror;
                        activeHurtBoxRectanglePosition.x += activeHurtBoxPosition.x;
                        activeHurtBoxRectanglePosition.y += activeHurtBoxPosition.y;*/

                        mirrorDiff = mirror > 0 ? activeHurtBox.rect.width : 0f;
                        Rect activeHurtBoxRectanglePosition = new Rect(((activeHurtBox.rect.x + mirrorDiff) * - mirror) + activeHurtBoxPosition.x, 
                            activeHurtBox.rect.y + activeHurtBoxPosition.y, 
                            activeHurtBox.rect.width, activeHurtBox.rect.height);

                        if (activeHurtBox.followXBounds){
                            activeHurtBoxRectanglePosition.x = activeHurtBox.rendererBounds.x - (activeHurtBox.rect.width/2);
                            activeHurtBoxRectanglePosition.width = (activeHurtBox.rendererBounds.width + activeHurtBox.rect.width) - activeHurtBox.rendererBounds.x;
                        }
                        if (activeHurtBox.followYBounds){
                            activeHurtBoxRectanglePosition.y = activeHurtBox.rendererBounds.y - (activeHurtBox.rect.height/2);
                            activeHurtBoxRectanglePosition.height = (activeHurtBox.rendererBounds.height + activeHurtBox.rect.height) - activeHurtBox.rendererBounds.y;
                        }

                        if (hurtBox.followXBounds){
                            hurtBoxRectanglePosition.x = hurtBox.rendererBounds.x - (hurtBox.rect.width/2);
                            hurtBoxRectanglePosition.width = (hurtBox.rendererBounds.width + hurtBox.rect.width) - hurtBox.rendererBounds.x;
                        }
                        if (hurtBox.followYBounds){
                            hurtBoxRectanglePosition.y = hurtBox.rendererBounds.y - (hurtBox.rect.height/2);
                            hurtBoxRectanglePosition.height = (hurtBox.rendererBounds.height + hurtBox.rect.height) - hurtBox.rendererBounds.y;
                        }

                        if (hurtBoxRectanglePosition.Overlaps(activeHurtBoxRectanglePosition)) collisionConfirm = true;
                    }
                }

                if (collisionConfirm) {
                    activeHurtBox.state = 1;
                    return new Vector3[]{hurtBoxPosition, activeHurtBoxPosition, (hurtBoxPosition + activeHurtBoxPosition)/2};
                }
            }
        }

        foreach (HurtBox activeHurtBox in activeHurtBoxes) {
            if (activeHurtBox.state == 1) activeHurtBox.state = 0;
        }
        return new Vector3[]{ };
    }

Note that you have to add a "state" member in the HurtBox class at the top of the file we're currently editing

public int state{get;set;}

So as far as the moves, make your parry attempt that will have a hurt box covering your entire character with a Parry hit confirm type, make your move invincible during the active frames of the parry, so that if someone let the parry go a bit he can punish you for parrying for nothing. Set the parry confirm move as you would with a throw.
The rest is pretty much like setting up a normal throw with the confirm and the opponent override with the reaction move. There's just no tech to setup. When someone attacks your parry, the hurt boxes will clash and your parry will connect and do the confirm and reaction moves.
Makes sense? Let me know if you try it, works well for me so far.

Share

Thumbs up +1 Thumbs down

Re: Parry as a move

sooo Just stating.. Parry as a move is already possible without any code changes whatsoever


in hit confirm options set the move as counter hit.
set armor

during the chain move frames  if you are hit you can have the move cancel into whatever you want

Eternal Rift Studios
    Current Projects:
         Destined Soels
Always happy to help when possible. If its something pretty minor ill just help you out. But i do commissions as well. Hit me up if you need a commission.

Share

Thumbs up +1 Thumbs down

Re: Parry as a move

Oh that's interesting! Gonna try that method

Share

Thumbs up Thumbs down