Rimworld Mod制作教程10 角色工作机制JobDriver

简介

RW中角色行为相关的部分由JobDriver实现。例如搬运,睡觉,屠宰

job与toil

首先有一个概念要理解,job与toil都是工作的意思,但关系是一个job包含多个toil。toil是工作中一个具体的环节步骤。比如搬运的过程,1.小人走到物体;2.抱起物体;3小人走到储存区;4.丢下物品
这四个步骤连续在一起称为一个job,每个步骤称为一个toil。

核心API

protected Toil CurToil; 返回当前进行的toil

protected LocalTargetInfo TargetA; 当前工作的执行者

protected LocalTargetInfo TargetB; 当前工作需要交互的目标
ps:虽然我们实际用到的是在这里插入图片描述
但实际是以枚举+索引的方式使用他们
在这里插入图片描述

public abstract bool TryMakePreToilReservations(bool errorOnFailed);
这是个抽象函数,我们的jobDriver要实现它, 在这个函数中我们要声明保留某个目标, 不让其他物体与之交互。比如小人正在屠宰一个生物,读条中,如果没有声明保留那个生物,这个时候可能另一个小人也走过来一起屠宰,当然RW里并没有协同工作一说,只是发生这种情况不符合RW开发者的预期,可能会出错。

public void DriverTick(); 具体工作在每一帧如何进行是在这个函数中完成的

protected override IEnumerable<Toil> MakeNewToils(); 我们一般都是重写这个函数来完成自己的工作,这个函数定义了工作内容。它返回一个Toil类型的泛型迭代器IEnumerable<Toil>,也就是把几个Toil装进一个迭代器中(可以简单理解为一个集合),只是获取每个toil的时候做了些特别的内容。

MakeNewToils示例

这个代码为了图省事方便,写的很烂,大家凑合看。

FailOn开头的函数是指你在这个job的过程中可能遇到一些情况导致job无法进行下去,判定为失败。

如果犯人没有死亡的话,这个job包含了4个toil:1.走过去;2交互1秒;3.解除保留 4.我们自己定义的一个步骤,触发了某个buff,并弹出提示

/// <summary>
/// 行为过程
/// </summary>
/// <returns></returns>
protected override IEnumerable<Toil> MakeNewToils()
{
    this.FailOnDestroyedOrNull(TargetIndex.A);
    this.FailOnDestroyedOrNull(TargetIndex.B);
    this.FailOnDespawnedNullOrForbidden(TargetIndex.A);//床被禁止使用
    this.FailOnAggroMentalStateAndHostile(TargetIndex.B);//B精神不正常
    yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.ClosestTouch).FailOnForbidden(TargetIndex.A);//走到dark家具旁边
    Pawn prisoner = (Pawn)Target;
    //捆绑操作
    if (!prisoner.Dead)
    {
        yield return Toils_General.WaitWith(TargetIndex.A, 60, true, true); //交互1秒
        yield return Toils_Reserve.Release(TargetIndex.B);
        //解除效果
        yield return new Toil
        {
            initAction = delegate ()
            {
                if (Thing != null)
                {
                    CompRemoveEffectBondageBed compUseEffect = Thing.TryGetComp<CompRemoveEffectBondageBed>();//解除束缚床效果
                    if (compUseEffect != null)
                    {
                        compUseEffect.DoEffect(prisoner);
                        MoteMaker.ThrowText(Target.PositionHeld.ToVector3(), Target.MapHeld, "SR_Release".Translate(), 4f);
                    }
                }
            },
            defaultCompleteMode = ToilCompleteMode.Instant
        };
    }
    yield break;
}

编写流程

1.我们定义自己的类继承JobDriver(或者某个继承了jobDriver分化更细的类)
2.重写MakeNewToils()定义这个工作的流程
3.在某个xml组件里注册这个JobDriver等待触发
在这里插入图片描述
或者我们做个UI窗口,点了就主动触发。xml也是某个组件以以下代码的方式触发的。

 Verse.AI.Job job = extraTarget.IsValid ? JobMaker.MakeJob(Job.JobDefOf.SR_Job_ReleaseBed, parent, extraTarget) : JobMaker.MakeJob(Job.JobDefOf.SR_Job_ReleaseBed, this.parent);
 job.count = 1;
 pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);

运行机制

小人的类Pawn中有个Job类,这个类里保存了一些相关的参数以及负责处理Job的Pawn_JobTracker类。
1.小人每帧会调用Tick(也可能不是每帧,有xml参数可以控制频率)
2.Tick中调用Pawn_JobTracker的JobTrackerTick函数。
在这里插入图片描述
3.Pawn_JobTracker存有当前进行的JobDriver
在这里插入图片描述
在自身JobTrackerTick()函数调用JobDriver的DriverTick函数
在这里插入图片描述
4.在DriverTick中调用ReadyForNextToil()触发我们为Toil定义的initAction回调函数

在这里插入图片描述
在这里插入图片描述
5.然后触发我们为Toil类定义的Toil的tickAction回调函数
在这里插入图片描述
initAction适合进入Toil工作流程开始瞬间能完成的内容,比如设置buff,削减血量之类的。
而耗时比较多的要放在tickAction里,等待协程处理。比如交互1秒,等待1秒,走路到某个点(每一帧坐标都会向目标点靠近,插值算法)
6.等待Toil结束,尝试下一个Toil,当全部Toil完成时即job完成。(当然你也可以强制中断job)

结语

就分享这么多,中间还是有很多细节省略掉了。在我的某个mod里有具体的调用可供参考一下
https://github.com/Shadowrabbit/SR_DarkArtist

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值