Giter VIP home page Giter VIP logo

behaviourtree-ai's Introduction

BehaviourTree、UtilityAI、FSM

基于ecs-framework开发的AI(BehaviourTree、UtilityAI、FSM)系统,一套已经非常完整的系统。教程较少,可以自行看源代码来学习。

目录结构

  • src 源目录
    • behaviourTree 行为树主目录
      • actions 动作是行为树的节点。比如: 播放动画,触发事件等。
      • composites Composites是行为树中的父节点,他们容纳一个或多个子节点,并以不同的方式执行。
      • conditionals 它们由IConditional接口标识。它们会检查游戏世界的某些情况,并返回成功或失败
      • decorators 装饰器可以通过各种方式修改子任务的行为,例如: 反转结果,运行知道失败等
    • utilityAI 实用AI主目录
      • actions AI执行的操作
      • considerations 列出评估和行为清单。计算一个分数,用数字表示Action的有效使用情况。
      • reasoners 从附加的Reasoner的事项列表中选择最佳的事项。AI的根源
    • core egret核心扩展
    • test 示例工程
      • utilityActions 实用AI示例目录

介绍

State Machine

它实现 状态作为对象 模式。 StateMachine为每个状态使用单独的类,因此对于更复杂的系统而言,它是更好的选择。

我们开始使用StateMachine来了解上下文的概念。 在编码中,上下文只是用于满足一般约束的类。 在Array中,字符串将是上下文类,即列表所基于的类。 使用所有其他的AI解决方案,您都可以指定上下文类。 它可能是您的敌人类,玩家类或包含与您的AI相关的任何信息(例如玩家,敌人列表,导航信息等)的帮助对象。

这是一个显示用法的简单示例(为简洁起见,省略了State子类):

  // 创建一个状态机,该状态机将使用SomeClass类型的对象作为焦点,并具有PatrollingState的初始状态 
  let machine = new SKStateMachine<SomeClass>( someClass, new PatrollingState() );
  
  // 我们现在可以添加任何其他状态 
  machine.addState(new AttackState());
  machine.addState(new ChaseState());
  
  // 通常在更新对象时调用此方法 
  machine.update(es.Time.deltaTime);
  
  // 改变状态。 状态机将自动创建并缓存该类的实例(在本例中为ChasingState) 
  machine.changeState<ChasingState>(ChasingState);

Behavior Trees

行为树由节点树组成。节点可以根据世界状态做出决策并执行操作。它包含一个BehaviorTreeBuilder类,它提供了一个用于设置行为树的API。BehaviorTreeBuilder是一种使行为树减少使用并快速启动的方法。

Composites

组合是行为树中的父节点。 他们有一个或多个子节点,并以不同的方式处理他们。

  • Sequence 一旦其子任务之一返回失败,则返回失败。 如果一个子任务返回成功,它将在树的下一帧顺序运行下一个子节点
  • Selector 一旦其子任务之一返回成功,则返回成功。 如果子任务返回失败,则它将在下一帧顺序运行下一个子任务。
  • Parallel 运行每个子节点直到子节点返回失败。它不同于Sequence仅在于它在每帧都会运行所有子节点
  • ParallelSelector 同Selector,除了它自身将在每帧都运行所有子节点
  • ParallelSequence 同Sequence,除了它自身将在每帧都运行所有子节点
  • RandomSequence 同Sequence,在执行前将子节点随机打乱后运行
  • RandomSelector 同Selector, 在执行前将子节点随机打乱后运行

Conditional

条件是成功/失败节点。 它们由IConditional接口标识。 他们检查您的游戏世界的某些状况,并返回成功或失败。 它们本质上是特定于游戏的,因此框架仅提供一个开箱即用的通用条件,以及包装Function的辅助条件,因此您不必为每个条件创建单独的类。

  • RandomProbability: 当随机概率高于指定的成功率时返回成功
  • ExecuteActionConditional: 包装一个Func并未做Conditional执行。用于原型设计和避免为简单的条件创建单独的类。

Decoration

装饰器是具有单个子任务的包装器任务。 他们可以通过多种方式修改子任务的行为,例如反转结果,运行直到失败等。

  • AlwaysFail: 无论子结果如何,总是返回失败
  • AlwaysSuccedd: 无论子结果如何,总是返回成功
  • ConditionalDecorator: 包装条件,并且仅在满足条件时才运行其子项。
  • Repeater: 重复其子任务指定次数
  • UntilFail: 继续执行其子任务,直到返回失败
  • UntilSuccess: 继续执行其子任务,直到返回成功
  • Inverter: 反转子结果

Action

动作是行为树的叶子节点。 例如播放动画,触发事件等。

  • ExecuteAction: 包装一个Func并将其作为动作执行。
  • WaitAction: 等待指定的时间
  • LogAction:将字符串记录到控制台用于调试。
  • BehaviorTreeReference:运行另一个行为树

使用文档

class AiComponent{
    private _tree: BehaviorTree<AiComponent>;
    public state: State = new State();
    private _distanceToNextLocation: number = 10;
    public update(){
        if (this._tree)
            this._tree.tick();
    }

    public start(){
        let builder = BehaviorTreeBuilder.begin(this);

        builder.selector(AbortTypes.Self);

        // 睡觉最重要
        builder.conditionalDecoratorR(m => m.state.fatigue >= State.MAX_FATIGUE, false);
        builder.sequence(AbortTypes.LowerPriority)
            .logAction("-- 累了,准备回家")
            .action(m => m.goToLocation(Locate.Home))
            .logAction("-- 准备上床")
            .action(m => m.sleep())
            .endComposite();

        // 喝水第二重要
        builder.conditionalDecoratorR(m => m.state.thirst >= State.MAX_THIRST, false);
        builder.sequence(AbortTypes.LowerPriority)
            .logAction("-- 渴了! 准备喝水")
            .action(m => m.goToLocation(Locate.Saloon))
            .logAction("-- 开始喝水")
            .action(m => m.drink())
            .endComposite();

        // 存钱第三重要
        builder.conditionalDecoratorR(m => m.state.gold >= State.MAX_GOLD, false);
        builder.sequence(AbortTypes.LowerPriority)
            .logAction( "--- 背包满了,准备去银行存钱." )
            .action( m => m.goToLocation( Locate.Bank ) )
            .logAction( "--- 开始存钱!" )
            .action( m => m.depositGold() )
            .endComposite();

        // 赚钱最后
        builder.sequence()
            .action(m => m.goToLocation(Locate.Mine))
            .logAction("-- 开始挖矿!")
            .action(m => m.digForGold())
            .endComposite();

        builder.endComposite();

        this._tree = builder.build();
    }

    private digForGold(): TaskStatus{
        console.log(`开始金币增加: ${this.state.gold}.`);
        this.state.gold++;
        this.state.fatigue++;
        this.state.thirst++;

        if( this.state.gold >= State.MAX_GOLD )
            return TaskStatus.Failure;

        return TaskStatus.Running;
    }

    private drink(): TaskStatus{
        console.log(`开始喝水, 口渴程度: ${this.state.thirst}`);

        if( this.state.thirst == 0 )
            return TaskStatus.Success;

        this.state.thirst--;
        return TaskStatus.Running;
    }

    private sleep(): TaskStatus{
        console.log(`开始睡觉, 当前疲惫值: ${this.state.fatigue}`);

        if (this.state.fatigue == 0)
            return TaskStatus.Success;
        
        this.state.fatigue--;
        return TaskStatus.Running;
    }

    private goToLocation(location: Locate): TaskStatus{
        console.log(`前往目的地: ${location}. 距离: ${this._distanceToNextLocation}`);

        if (location != this.state.currentLocation){
            this._distanceToNextLocation--; 
            if (this._distanceToNextLocation == 0){
                this.state.fatigue ++;
                this.state.currentLocation = location;
                this._distanceToNextLocation = Math.floor(Random.range(2, 8));
                return TaskStatus.Success;
            }

            return TaskStatus.Running;
        }

        return TaskStatus.Success;
    }

    private depositGold(): TaskStatus{
        this.state.goldInBank += this.state.gold;
        this.state.gold = 0;

        console.log(`存钱进入银行. 当前存款 ${this.state.goldInBank}`);

        return TaskStatus.Success;
    }
}
class State{
    public static MAX_FATIGUE: number = 10;
    public static MAX_GOLD = 8;
    public static MAX_THIRST = 5;

    public fatigue: number = 0;
    public thirst: number = 0;
    public gold: number = 0;
    public goldInBank: number = 0;
    public currentLocation: Locate = Locate.Home;
}

enum Locate{
    Home,
    InTransit,
    Mine,
    Saloon,
    Bank
}

开始行为树

this.aiComponent = new AiComponent();
this.aiComponent.start();

最后还需要对aiComponent进行派发update事件更新

this.aiComponent.update();

Utility Based AI

游戏效用理论。 最复杂的AI解决方案。 最适合在其计分系统最有效的动态环境中使用。 基于实用程序的AI更适用于AI可以采取大量潜在竞争行为的情况,例如在RTS中。

Reasoner

从附加在Reasoner上的考虑因素列表中选择最佳考虑因素。一个实用AI的根。

Consideration

拥有一个评估和一个行动的列表。计算一个分数,用数字表示其行动的效用。

Appraisal

可以将一个或多个评估添加到Appraisal中。 他们计算并返回其使用代价的得分。

Action

当一个特定的考虑因素被选中时,AI执行的行动。

依赖库

ecs-framework

behaviourtree-ai's People

Contributors

esengine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

behaviourtree-ai's Issues

isIConditional函数判断是不是有问题?

var isIConditional = (props: any): props is IConditional => typeof (props as IConditional)['update'] !== 'undefined';
编译成js代码如下
var isIConditional = function (props) { return typeof props['update'] !== 'undefined'; };
只检查了函数是否具有update函数。Behavior里自带了update函数,也就是所有继承自Behavior的函数都会返回true。并不能判断是不是条件节点吧?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.