镜像模块

2020/07/18 Mmo-Game

mirror镜像模块,主要学习AI逻辑

目录

什么是镜像?

机器人,模拟玩家。随机的名字,拥有主副侠客,侠客都是从表里配死的,境界、星级、属性。

镜像,拥有玩家性质的生物角色,可以从机器人身上拷贝生物性质,也可以直接从玩家身上拷贝生物性质。

类继承关系

1.类图

2.所有状态机抽象类AbstractAIState

/**
 * 状态机抽象类
 * 
 * @author wangguohao
 * @time 2017年7月6日 - 下午5:00:08
 */
public abstract class AbstractAIState<E extends AbstractCharacter>
{
    /**
    * 当前状态可转换的状态
    * 
    */
	protected EnumSet<StateEnum> transitionState = EnumSet.noneOf(StateEnum.class);
    
    /**
    * 判断是否可以进入此状态
    * @param e
    * @return
    */
	public boolean canEnter(E e)
    {
        return true;
    }

    /**
	* 进入当前状态要做的事
	* @param e
	* @return
	*/
	public abstract boolean enter(E e);

    /**
    * 判断是否可以退出此状态
    * @param e
    * @return
    */
    public boolean canExit(E e)
    {
        return true;
    }

	/**
	 * 退出当前状态要做的事
	 * @param e
	 * @return
	 */
	public abstract boolean exit(E e);

    ...

}

所有状态机的抽象父类,

镜像、npc、傀儡…状态机的实现都是继承自AbstractAIState。

AbstractAIState,

传入对象必须是继承AbstractCharacter,拥有生物类属性的对象。

抽象一些方法,判断是否可以进入此状态、进入当前状态要做的事…, 子类各自覆写实现。

3.镜像抽象类AbstractMirrorAIState

/**
 * 镜像基础AI状态类
 */
public abstract class AbstractMirrorAIState extends AbstractAIState<Mirror>
{
    /**
    * 检查目标是否继续有效
    * @param mirror
    * @return true=有效 false=失效
    */
    protected boolean checkTarget(Mirror mirror)
    {
        ...
    }
}

传入的对象是Mirror

将检查目标是否继续有效提到公共层

4.镜像AI状态类

/**
 * 镜像AI状态机 - Idle状态
 * <pre>
 *     可转换状态 :  Battle状态 、 Persuit状态
 *     状态机逻辑 :  1.如果镜像死亡或者不能攻击,将一直留在Idle状态
 *                   2.判断敌人的状态,如果死亡则一直停留在Idle状态
 *                   3.选择一个可释放技能,如果是刚开始需要顺序释放镜像的防守技能,放完一遍防守技能随机使用技能
 *                   4.根据随机的技能,判断和敌人的距离,不够则追击(转换到 Persuit状态),距离够则使用技能攻击( Battle状态)
 * </pre>
 */
public class MirrorIdleState extends AbstractMirrorAIState

空闲状态机,Idle状态

/**
 * 镜像追击状态
 */
public class MirrorPursuitState extends AbstractMirrorAIState

镜像追击状态

/**
 * 镜像AI状态 - 战斗状态
 * <p>
 * 主要负责释放技能,释放完技能就转换成Idle状态,释放技的过程中仍然在该状态
 * </p>
 */
public class MirrorBattleState extends AbstractMirrorAIState

战斗状态

/**
 * 特殊控制状态
 */
public class MirrorSpecialControlState extends AbstractMirrorAIState

特殊控制状态

5.状态机管理类BaseAIFsmManager

/**
 * 状态机管理类
 */
public class BaseAIFsmManager<T extends AbstractCharacter>
{
    /**
     * 当前状态
     */
    AbstractAIState<T> curState;

    /**
     * 转换状态  ( 当前帧不会转换,下一帧才转换)
     *
     * @param stateEnum
     */
    public boolean transtTo(StateEnum stateEnum)
    {
        ...
    }

    /**
     * Tick
     */
    public void tick(int interval)
    {
        if (curState == null)
        {
            return;
        }
        curState.tick(owner, interval);
    }
}

主要的成员属性和方法,缓存了一个当前状态,转换状态,tick

例如:MirrorAiModule模块缓存了一个BaseAIFsmManager状态机管理类,

BaseAIFsmManager缓存当前状态,根据当前状态进行tick。

AI流程

1.调用mirror.init();

BPCopySceneFightArenaGame
@Override
public void onActorEnter(Actor actor)
{
    ...
    ...
    //初始化镜像数据
    Mirror mirror = new Mirror(mirrorData);
    mirror.init();

    //添加镜像到场景
    addBPObject(mirror);
    ...
    ...

}

比武小游戏场景,玩家添加进场景是。

同时,mirror.init()将镜像初始化,addBPObject添加到场景。

2.镜像AI模块初始化

MirrorAiModule:

@Override
public void init()
{
    //顺序释放技能队列 (第一个侠客先按配招放一遍技能,然后随机放;第二个侠客直接随机放)
    sequenceSkillQueue = new BPIntArrayDeque();
    //初始化状态机管理类
    aiFsmManager = new BaseAIFsmManager<>(getMirror());

    //初始化状态机为空闲状态机,并添加状态机列表
    List<AbstractAIState<Mirror>> states = new ArrayList<>();
    states.add(MirrorIdleState.getInstance());
    states.add(MirrorBattleState.getInstance());
    states.add(MirrorPursuitState.getInstance());
    states.add(MirrorSpecialControlState.getInstance());
    aiFsmManager.init(StateEnum.IDLE, states);

    //初始化强控制参数
    aiSpecialControlParam = AISpeicalcontrolParamFactory.create();
    aiSpecialControlParam.init();

    //是否释放完了顺序技能 默认为true
    this.hasSequenceSkillDone = true;
    //上次普攻是否是子技能(非蓄力技能)
    lastNormalSkillIsSequence = false;
    //上次的技能序号
    lastSkillIndex = 0;

}

顺序释放技能队列 (第一个侠客先按配招放一遍技能,然后随机放;第二个侠客直接随机放)

初始化状态机管理类

初始化状态机为空闲状态机,并添加状态机列表

初始化强控制参数,是否释放完了顺序技能,上次普攻是否是子技能(非蓄力技能),上次的技能序号

3.激活镜像AI

AbstractBPCopySceneFightArena:

/**
* 状态进入 : 战斗
*/
@Override
protected void onEnterStateFight()
{
    //是否打镜像
    if (isMirrorFight)
    {
        Mirror mirror = getMirror();
        if (mirror != null)
        {
            // 设置敌人objectId(镜像的敌人一定是挑战者)
            mirror.getMirrorAiModule().setEnemyObjectId(this.getChallengerObjectId());

            // 激活镜像的AI
            mirror.getMirrorAiModule().activeAI();
        }
    }
}

设置敌人objectId(镜像的敌人一定是挑战者)

激活镜像的AI,镜像AI逻辑开始tick

4.开始tick

MirrorAiModule:

@Override
public void tick(int interval)
{
    if (!active)
    {
        return;
    }

    tickRemain -= interval;
    if (tickRemain > 0)
    {
        return;
    }

    //tick间隔时间
    tickRemain = TICK_INTERVAL;

    // 状态机管理类tick
    aiFsmManager.tick(totalInterval);
}

减少tick频率,

执行aiFsmManager.tick(totalInterval);

5.镜像上来状态的idle状态,idle状态的tick

MirrorIdleState:

@Override
public void tickImpl(Mirror mirror, int interval)
{
    ...
    ...
    // 判断敌人是否存在
    MirrorAiModule mirrorAiModule = mirror.getMirrorAiModule();
    //敌人的ObjectID
    int enemyObjectId = mirrorAiModule.getEnemyObjectId();
    AbstractCharacter targetObject = mirror.getCharacter(enemyObjectId);

}

Search

    Table of Contents