State Machine for PlayerMotor

Discuss coding questions, pull requests, and implementation details.
Post Reply
User avatar
MeteoricDragon
Posts: 141
Joined: Mon Feb 12, 2018 8:23 pm

State Machine for PlayerMotor

Post by MeteoricDragon »

I've adapted some code I found online for the purpose of defining states and commands for a state machine in the PlayerMotor rewrite. @Interkarma can you provide a list of states and commands you'd like?

Code: Select all

using UnityEngine;
using System;
using System.Collections;
using DaggerfallConnect;
using System.Collections.Generic;

namespace DaggerfallWorkshop.Game
{
    public enum TraversalState
    {
    	Canceled,
        Frozen,
        Ground, // Moving on ground
        Water, // Moving along the water
        Air, // Moves in the air
        Climbing // Moves along walls
    }
    public enum TraversalCommand
    {
        CancelMovement,
        Freeze,
        Walk,
        Swim,
        Fly,
        Climb
    }
    public class TraversalProcess : MonoBehaviour
    {
        class StateTransition
        {
            readonly TraversalState traversalState;
            readonly TraversalCommand command;

            public StateTransition(TraversalState state, TraversalCommand comm)
            {
                traversalState = state;
                command = comm;
            }

            public override int GetHashCode()
            {
                return 17 + 31 * traversalState.GetHashCode() + 31 * command.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                StateTransition other = obj as StateTransition;
                return other != null && this.traversalState == other.traversalState && this.command == other.command;
            }
        }

        Dictionary<StateTransition, TraversalState> transitions;
        public TraversalState MyState { get; private set; }

        public TraversalProcess()
        {
            MyState = TraversalState.Frozen;
            transitions = new Dictionary<StateTransition, TraversalState>
            {
                { new StateTransition(TraversalState.Frozen, TraversalCommand.Walk), TraversalState.Ground },
                { new StateTransition(TraversalState.Frozen, TraversalCommand.Swim), TraversalState.Water },
                // add more transitions that can occur with traversal commands, and show which state it is changed to with that command.  
            };
        }

        public TraversalState GetNext(TraversalCommand command)
        {
            StateTransition transition = new StateTransition(MyState, command);
            TraversalState nextState;
            if (!transitions.TryGetValue(transition, out nextState))
                throw new Exception("Invalid transition: " + MyState + " -> " + command);
            return nextState;
        }

        public TraversalState MoveNext(TraversalCommand command)
        {
            MyState = GetNext(command);
            return MyState;
        }
    }
}

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: State Machine for PlayerMotor

Post by Interkarma »

Here are my personal notes on the player motor upgrade I had in mind.
New Motor Requirements
Better fit through tight spots (e.g. Privateer's Hold stairs and Sentinel Castle ramp)
Doesn't get stuck on misplaced single-sided geometry
Doesn't fall through cracks or fail static geometry collision
No self-intersection from ray towards loot/levers by feet
Perfect ray alignment for levers, etc.
Does not fall to death while moving down steep ramps
Cannot climb cave walls where not appropriate
Freeze motor on teleport

Multiple states:
  • Movement
  • Crouching
  • Climbing
  • Levitation
  • Swimming
  • Riding (horse/cart)
  • Crush (below lowering platform)
  • Moving platform support
  • Ceiling hit support (no clip through low ceilings)
  • Smooth crouch
Might be required to remove CharacterController and create a custom setup.
Right now, you've already tackled a few items on this list, and I'm happy with your work. If you'd like to continue with state-based approach that will be OK with me. The main process will be deciding which states to use at which times. Generally speaking, almost every state can move into almost any other state in Daggerfall. It's not that sophisticated as far as movement systems are concerned. And most of the existing work should make for a good starting point.

I'm not ready to tackle the engineering side of character controller and face some of the issues here yet. Please only focus on the movement handling side of things for now and we can loop back to look at the more sticky physics-based issues later (like getting stuck on malformed single-sided geometry). I have a few ways in mind to fix these, I'm just not ready to approach yet.

I already have a fix for ray alignment, this just isn't something I've gotten around to adding yet. So you can pretty much ignore the physics items on the list. :)

Post Reply