执行游戏动作的模式


11

在游戏中执行各种动作是否存在一种公认的模式?玩家执行动作的方式,以及AI可能执行的动作,例如移动,攻击,自毁等。

我目前有一个抽象的BaseAction,它使用.NET泛型来指定由各种操作返回的不同对象。所有这些都以类似于Command的模式实现,在该模式中,每个动作都对自己负责并完成其所需的全部工作。

我之所以这么抽象,是因为我可以只有一个ActionHandler,并且AI可以将实施baseAction的不同操作排队。而且之所以通用,是因为不同的动作可以返回与该动作相关的结果信息(因为不同的动作在游戏中可能具有完全不同的结果),以及一些常见的beforeAction和afterAction实现。

那么...是否有更可接受的方式来执行此操作,或者听起来还不错吗?


听起来不错,问题是您所说的排队是什么意思?大多数游戏都有非常快速的反应?“人工智能可以让不同的行动排队”
AturSams 2012年

好点子。没有队列。它只需要知道是否很忙,否则就可以执行操作。
Arkiliknam 2012年

Answers:


18

我认为没有一种公认的方法可以实现这一概念,但是我真的很想分享平时在游戏中的处理方式。它是Command设计模式和Composite设计模式的组合

我有一个抽象的动作基本类,它只不过是围绕Update被调用的每个框架的方法的包装,以及一个Finished指示动作何时完成运行的标志。

abstract class Action
{
    abstract void Update(float elapsed);
    bool Finished;
}

我还使用复合设计模式来创建一种能够托管和执行其他操作的操作类型。这也是一个抽象类。归结为:

abstract class CompositeAction : Action
{
    void Add(Action action) { Actions.Add(action); }
    List<Action> Actions;
}

然后,我有两种复合动作的实现,一种用于并行执行,一种用于顺序执行。但其优点在于,由于并行和顺序本身就是动作,因此可以将它们组合在一起以创建更复杂的执行流。

class Parallel : CompositeAction
{
    override void Update(float elapsed) 
    {
        Actions.ForEach(a=> a.Update(elapsed));
        Actions.RemoveAll(a => a.Finished);
        Finished = Actions.Count == 0;
    }
}

和一个控制顺序动作的动作。

class Sequence : CompositeAction
{
    override void Update(float elapsed) 
    {
        if (Actions.Count > 0) 
        {
            Actions[0].Update(elapsed);
            if (Actions[0].Finished)
                Actions.RemoveAt(0);
        }
        Finished = Actions.Count == 0;
    }
 }

完成此操作后,只需创建具体的动作实现,并使用ParallelSequence动作来控制执行流程即可。我将以一个示例结尾:

// Create a parallel action to work as an action manager
Parallel actionManager = new Parallel();

// Send character1 to destination
Sequence actionGroup1 = new Sequence();
actionGroup1.Add(new MoveAction(character1, destination));
actionGroup1.Add(new TalkAction(character1, "Arrived at destination!"));
actionManager.Add(actionGroup1);

// Make character2 use a potion on himself
Sequence actionGroup2 = new Sequence();
actionGroup2.Add(new RemoveItemAction(character2, ItemType.Potion));
actionGroup2.Add(new SetHealthAction(character2, character2.MaxHealth));
actionGroup2.Add(new TalkAction(character2, "I feel better now!"));
actionManager.Add(actionGroup2);

// Every frame update the action manager
actionManager.Update(elapsed);

在此之前,我已经成功地使用了该系统来驱动图形冒险中的所有游戏玩法,但它可能几乎可以用于任何事情。添加其他类型的复合操作(用于创建执行循环和条件)也很简单。


这看起来是一个非常好的解决方案。出于好奇,您如何让UI知道要绘制什么?您的游戏对象(例如角色)是否包含用于标识渲染目的的状态,还是动作本身能够做到这一点?
Arkiliknam 2012年

1
通常我的行动只会改变状态的实体,并渲染输出的任何变化发生的后果是状态的变化,而不是由行为本身的手段。例如,使用即时模式渲染器,不需要额外的步骤,因为该Draw方法已经建立在实体状态之上,并且更改是自动的。在类似Flash的保留模式渲染器中,您可以使用可观察的模式将对实体的更改传播到显示对象,或者在实体本身内部手动进行连接。
David Gouveia 2012年

1
在第一种情况下,假设您的Character类具有一个Position属性,以及一个Draw读取当前值Positionis并在其中绘制正确图像的方法。在这种情况下,您只需要更新该值即可Position在屏幕上自动看到结果。
David Gouveia 2012年

1
第二种情况是,当您Character具有Position属性,但将渲染委派给某种Sprite对象时,该对象将由场景图或其他东西自动渲染。在这种情况下,您必须确保角色的位置和精灵的位置始终保持同步,这需要做更多的工作。尽管如此,无论哪种情况,我都不明白为什么动作管理器应该与此有关。:)
David Gouveia 2012年

1
两种方法都有优点和缺点。.我在2D游戏中使用了第二种方法,但有时后悔,因为使所有内容保持同步要复杂得多。但是也有一些优点,例如,当尝试检测单击哪个实体或不应该绘制哪个实体时,因为将要呈现的所有内容都包含在同一数据结构中,而不是分散在N个实体类型之间。
David Gouveia 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.