More States

        if (playerFighter.GetCurrentShieldConfig()) description = "ShieldBlock";
        else if (playerFighter.GetCurrentWeaponConfig().IsTwoHanded) description = "2HLongBlock";
        else if (playerFighter.GetCurrentWeaponConfig() != null)  description = "1HBlock";

tried that before, no avail :sweat_smile: (now it’s the exact opposite. Everyone triggers the shield defence animation)

I’m guessing impact happens less and less the higher the level gets…?

^^^

OK so… I noticed something. Remember when we were creating the shield and we had that bug of an empty shield? Yup, that shield still “invisibly” exists. In other words, everytime we unequip the shield, we do it visually, but it’s never permanently gone. All we got is a “base shield” that acts as a placeholder, giving off the idea that it’s an empty hand, but it’s not (the base shield is invisibly sitting there). This is how I dealt with it, in ‘fighter.cs’ (back then):

        public void EquipShield (ShieldConfig shield) {
            DestroyOffHandItem();
            currentShield = shield;
            currentEquippedShield.value = AttachShield(shield);
        }

        private Shield AttachShield(ShieldConfig shield) {
            return shield.SpawnEquipableItem(rightHandTransform, leftHandTransform);
        }

        private void DestroyOffHandItem() {
            Shield offHandWeapon = leftHandTransform.GetComponentInChildren<Shield>();
            if (offHandWeapon == null) return;
            Destroy(offHandWeapon.gameObject);
        }

(what’s the point of ‘DestroyOffHandItem()’…?! Do we set the parent to null? Any additional steps we need to take to fix this…?!)

I noticed the issue because placing debuggers in this function (check below), everytime I play it after taking that shield off tells me I have a shield on (although I don’t). Once you put the shield on for the first time, the problem starts, and there’s no way back:

    public string GetBlockHashName()
    {
        string description = "";
        var playerFighter = GameObject.FindWithTag("Player").GetComponent<Fighter>();
        
        if (playerFighter.GetCurrentShieldConfig()) {
            Debug.Log("You have a shield in-hand");
            description = "ShieldBlock";
        }
        
        if (playerFighter.GetCurrentWeaponConfig().IsTwoHanded) {
        Debug.Log("You have a two-handed weapon");
        description = "2HLongBlock";
        }

        if (playerFighter.GetCurrentWeaponConfig() != null && !playerFighter.GetCurrentWeaponConfig().IsTwoHanded && playerFighter.GetBaseShield())
        {
            Debug.Log("You have a weapon, single-handed, and no shield, so 1-Handed Block");
            description = "1HBlock";
        }
        return description;
    }

The question now is, how do we put this into consideration when swapping between the animations? (and why does wielding weapons call the debug in ‘EquipShield()’?)

Seems to me we’re not null-checking the shield here… but this should be as simple as EquipShield(null),

        public void EquipShield (ShieldConfig shield) {
            DestroyOffHandItem();
            currentShield = shield;
            if(currentShield) currentEquippedShield.value = AttachShield(shield);
            else currentEquippedShield.value=null;
        }

and… that did solve a major and unforeseen problem, but it wasn’t the final bug solution yet (without a small tweak). You’d be surprised if I told you what was causing this major headache though (P.S: I solved it, at 2:29 AM… the usual time for me to solve major bugs :stuck_out_tongue: - my mind was racing so I couldn’t sleep (it’s a common thing for me…)). Here is the bug:

// in 'Fighter.cs':
    private void UpdateWeapon() {

        var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
        var shield = equipment.GetItemInSlot(EquipLocation.Shield) as ShieldConfig;

        /* if (weapon == null) EquipWeapon(defaultWeapon);
        else EquipWeapon(weapon); */

        EquipWeapon(weapon != null ? weapon : defaultWeapon);
        // EquipShield(shield != null ? shield : baseShield); // THIS LINE
        EquipShield(shield != null ? shield : null);

    }

Where I wrote “THIS LINE” is where the bug was. As you may have noticed, if the shield was null, we were not equipping a “null” on. Instead, we were putting on the invisible “base” shield (the fictional solution we made to hide shields when 2h weapons were activated). Replacing it with a real null (i.e: make the hand empty) solves the problem :slight_smile:

and this is the final Block Hash Getter:

    public string GetBlockHashName()
    {
        string description = "";
        var playerFighter = GameObject.FindGameObjectWithTag("Player").GetComponent<Fighter>();

        if (playerFighter.GetCurrentWeaponConfig() != null && !playerFighter.GetCurrentWeaponConfig().IsTwoHanded && playerFighter.GetCurrentShieldConfig() == null)
        {
            Debug.Log("You have a weapon, single-handed, and no shield, so 1-Handed Block");
            description = "1HBlock";
            return description;
        }

        if (playerFighter.GetCurrentShieldConfig()) {
        Debug.Log("You have a shield in-hand");
        description = "ShieldBlock";
        }
        
        if (playerFighter.GetCurrentWeaponConfig().IsTwoHanded) {
        Debug.Log("You have a two-handed weapon");
        description = "2HLongBlock";
        }


        return description;
    }

I can’t find a single hand-cross animation to display a block event for hands-free weapons, so I’m skipping that for now (until I need it)

ANYWAY, I will need help with the next state, the dodging one because even in the Third Person Course I got that completely wrong, and it never worked for me… I’ll go watch the lectures again in the morning and keep you updated on where I get stuck :slight_smile: (I’ll do my best to minimize the requests as we speak)

JUST WAIT and I’ll do the dodging state up proper, but I have a couple sections to wrap up (throttling, then jumping, falling, then dodge, then a blocking state that will be much more realistic (because even blocking doesn’t mean you take no damage or have no consequences)

1 Like

alas, I see a roadmap :stuck_out_tongue: (Jumping, Falling and Blocking should be very similar to mine though, right? RIGHT? RIGHT?! - I don’t want to re-write the entire thing) :slight_smile: - hoping to see Throttling states soon though

Anyway if that’s the case, then I’ll go work on other major systems (honestly was kinda thinking of swimming states)

Swimming states I have no plans on doing, not on the roadmap, and once I’m done with what’s on the map, the saving system overhaul is next, and that will be a major task.

oweeee we aren’t seeing ranged attacks anytime soon :stuck_out_tongue: - I’ll try swimming alone and see what I can come up with :slight_smile: (it’s literally just an outward raycast, and animations to match)

I’ll be returning to do more afterwards, but swimming isn’t in the game plan at all right now. Ranged Weapons, and Abilities are, however.

amazing (because one of my abilities bugged out, but I haven’t touched abilities in so long, so it doesn’t matter right now :sweat_smile:)

not the best start for me… I still can’t get the raycast to detect the water layer for some reason:

// in "PlayerFreeLookState.Tick()":

            // Debug.DrawRay(stateMachine.transform.position + new Vector3(0, 1.5f, 0), Vector3.forward, Color.red);
            if (IsSwimming()) 
            {
                Debug.Log("Player Entered Swimming Mode");
// if true, we shall switch to the swimming state
            }

// 'isSwimming():'
        bool IsSwimming() 
        {
            RaycastHit hit;
            if (Physics.Raycast(stateMachine.transform.position + new Vector3(0, 1.5f, 0), Vector3.forward, out hit, 10f, LayerMask.GetMask("Water"))) 
            {
                Debug.Log("Water Found");
                return true;
            }
            return false;
        }

I placed a ‘Debug.DrawRay()’ to see the raycast being emitted, but to my surprise… it works, but for god knows what reason (I checked the layer on my water, it is assigned to water. I also checked the project settings, water is connected to all sorts of layers), the debugger of either the ‘IsSwimming()’ and the one in tick under the true swim check won’t work…

The idea is simple, if the raycast hits the water layer, switch to the swimming state

It seems like you are reinventing the wheel here. Why not make a water detector that derives from RangeFinder. Adding a Swimming State should be similar to all the other states. That’s the beauty of using a state machine.

because if you’re on a surface and you detect water a few feet below you, the last thing I want is a swimming airborne animation :slight_smile: - currently trying to pass time by re-designing my game world though (mainly because I will need Level Streaming in the future, xD)

That’s not entirely true. You could create a RangeFinder with a small collider (say .4 radius), and place it at 0,1.5,0
Then it would only be true if the character was up to his hips in water

1 Like

OK if we’re taking that path, what will the generic inheritance of the RangeFinder refer to? :thinking: (I haven’t thought of that tbh… and now that I do, I have no idea :sweat_smile:)

If I go the ‘ResourceGathering’ path and do a ‘Swimming’ script, who do I put that script on, the player? (more importantly, what does that script contain?)

And suddenly, I have A LOT of questions… :sweat_smile: (hence why I want the Raycast path)

The simplest way to manage water is to set box collider for the water. When the RangeFinder OnTriggerEnters the water, you’re in water and should start swimming.When the RangeFinder finds you’ve left the water, you should fire an event and stop swimming. Just create a Water class that implements the ITarget interface.

OK I’m not exactly sure if what I did was right or wrong, but I wanted to give it a try. So here’s what I did:

I made a box collider, but when we enter or exit that, things don’t work out…

By “OnTriggerEnter()”, I assumed we were adding and removing targets (I honestly started peaking at ‘ResourceFinder.cs’ at this point in time), so I did that…:

    protected override void AddTarget(Water target)
    {
        base.AddTarget(target);
        target.OnWaterFound += RemoveTarget;
        Debug.Log($"WaterFinder: Adding {target.name}");
    }

    protected override void RemoveTarget(Water target)
    {
        base.RemoveTarget(target);
        target.OnWaterFound -= RemoveTarget;
        Debug.Log($"WaterFinder: Removing {target.name}");
    }

did that too, check above

I had no clue what to put in ‘IsValid()’ for that one, so I kept this class extremely simple:

using RPG.Core;
using UnityEngine;

public class Water : MonoBehaviour, ITarget
{

    public event System.Action <Water> OnWaterFound;

    bool ITarget.IsValid()
    {
        OnWaterFound?.Invoke(this);
        Debug.Log("Entering Water...");
        return true;
    }

}

and that was my entire ‘Water.cs’ script. For my ‘WaterFinder.cs’, here’s what I did:

using System.Linq;
using RPG.Core;
using UnityEngine;

public class WaterFinder : RangeFinder<Water>
{
    public Water GetNearestWater() 
    {
        CurrentTarget = Targets.OrderBy(w => Vector3.Distance(transform.position, w.transform.position)).FirstOrDefault();
        return CurrentTarget;
    }

    protected override void AddTarget(Water target)
    {
        base.AddTarget(target);
        target.OnWaterFound += RemoveTarget;
        Debug.Log($"WaterFinder: Adding {target.name}");
    }

    protected override void RemoveTarget(Water target)
    {
        base.RemoveTarget(target);
        target.OnWaterFound -= RemoveTarget;
        Debug.Log($"WaterFinder: Removing {target.name}");
    }

}

At this point in time, I don’t want to know why it doesn’t work as much as I want to know if I’m even on the right track or not :sweat_smile:

I will also have to mention that ‘WaterFinder.cs’ went on another trigger placed on the player, and ‘Water.cs’ went on the Box Collider (on the water) that detects the player

You’re on the right track.
In this case, the OnWaterFound is unneeded (and sort of useless as you have it now anyways). Where we use these events in the other RangeFinders, it’s a bandaid for the fact that OnTriggerExit is never called if a collider leaves the trigger zone by being destroyed. So, for example, in Pickup, the pickup could leave the RangeFinder’s collider by the RangeFinder moving away, or by the Player picking up the item. If the picked up the Pickup, then we fire that event so that the target can be removed. Same with enemies. If an enemy rudely dies on us, the RangeFinder won’t know that until we rule out the target by subscribing to the Health’s OnDie.

The water should never be destroyed, so the event is not needed. Calling it in IsValid() makes little sense. If you were to call it, it would be in OnDestroy().

using RPG.Core;
using UnityEngine;

public class Water : MonoBehaviour, ITarget
{

    public event System.Action <Water> OnWaterFound;

    bool ITarget.IsValid()
    {
        Debug.Log("Entering Water...");    // Never gets called... :/
        return true;
    }

    private void OnDestroy() 
    {
    OnWaterFound?.Invoke(this);
    }

}

So ‘Water.cs’ is something like this…? Any other changes I need to make? :slight_smile: (I’m re-reading all you mentioned above to try and grasp what was being said)

EDIT: OHHH I GET WHAT YOU WERE SAYING NOW… So these events are basically the escape route that is used rather than ‘OnTriggerExit()’, to ensure that we can quit an event, but under “weird” circumstances (for “OnTriggerExit()”, that is… totally normal for us advanced human beings), like an enemy death or equivalent…?! (Basically I’m assuming ‘OnTriggerExit()’ won’t be able to detect that, hence we use the exit events) OK I’m a little rusty on ideas today, so please bear with me because I’m trying my best here :slight_smile:

Anyway, what other changes do we need to make? (It’s still not working)

LOL I got Malbers’ Horse to be able to swim and not my main character yet… :stuck_out_tongue:

Privacy & Terms