Unity访问者模式

在实际的游戏项目中,怪物往往作为底层对象长期稳定存在,但围绕怪物的各种行为逻辑(攻击、技能、结算规则)却会随着玩法不断变化,如何在不频繁修改怪物代码的前提下扩展新逻辑,是一个常见但容易被忽视的问题。

1.定义两个接口IMonsterVisitor和IMonster,一个表示访问者,他需要实现对各个对象的访问方法。然后一个访问对象,他需要实现应答方法,当别人访问它的时候它需要执行的方法。

cs 复制代码
public interface IMonsterVisitor
{
    void Visit(Slime slime);
    void Visit(Goblin goblin);
    void Visit(Boss boss);
    void Visit(Wolf  wolf);
}

public interface IMonster
{
    void Accept(IMonsterVisitor visitor);
}

2.定义4个怪物脚本Boss,Goblin,Slime,Wolf。他们分别实现了应答方法,接收一个访问者,然后引导访问者去调用对应的方法。这两个方法效果是等价的

public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);

public void Accept(IMonsterVisitor visitor)

{

visitor.Visit(this);

}

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Monster;

public class Boss : MonoBehaviour, IMonster
{
    public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);
}
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Monster;

public class Goblin : MonoBehaviour, IMonster
{
    public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);
}
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Monster;

public class Slime : MonoBehaviour, IMonster
{
    public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);
}
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static Monster;

public class Wolf : MonoBehaviour, IMonster
{
    public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);
}

3.创建一个玩家访问者继承访问者接口,并实现访问者接口里面的所有方法访问,这样玩家就能知道攻击到不同的怪物该执行什么逻辑。

cs 复制代码
using UnityEngine;
using static Monster;

public class PlayerAttackVisitor : IMonsterVisitor
{
    public void Visit(Slime slime)
    {
        Debug.Log("Slime 受到伤害 + 弹跳");
    }

    public void Visit(Goblin goblin)
    {
        Debug.Log("Goblin 受到伤害 + 掉落金币");
    }

    public void Visit(Boss boss)
    {
        Debug.Log("Boss 受到伤害 + 进入狂暴状态");
    }
    public void Visit(Wolf wolf)
    {
        Debug.Log("狼被击中 → 流血效果 + 吼叫");
    }
}

4.创建一个玩家,当玩家碰到怪物后会尝试通过怪物接口去获取被访者的应答方法。

visitor = new PlayerAttackVisitor();相当于新建一个访问者。

monster.Accept(visitor);//相当于说那个怪物过来一下我有问题要问你

怪物中的这个方法public void Accept(IMonsterVisitor visitor) => visitor.Visit(this);//"我不关心你是什么怪物,你自己决定该怎么被我处理"

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAttack : MonoBehaviour
{
    PlayerAttackVisitor visitor;

    void Start()
    {
         visitor = new PlayerAttackVisitor();
    }
    private void OnCollisionEnter2D(Collision2D other)
    {
        if (other.gameObject.TryGetComponent<IMonster>(out var monster))
        {
            monster.Accept(visitor);
        }
    }
}

创建场景将玩家PlayerAttack加到玩家身上,并创建4个怪物,并且将怪物脚本加到对应怪物身上。

运行游戏拖动玩家,就会发现玩家碰撞到怪物执行了不同的方法

5.这样以后如果需要添加新的访问者十分容易,但是要添加新的被访问者需要修改所有的访问者接口,相当于你去不同的医院体检假如每个人的体检项目都一样,那么医生作为访问者那么只需要按照流程问你问题就行了,但是来了一个特殊的病人,只有修改访问者的接口了,相当于医生要去学习新的理论知识。

一句话总结访问者模式:

一句话总结访问者模式:当"对象结构稳定,但操作经常变化"时,把操作抽离成访问者,让对象自己决定"我该调用你哪个方法"。

还有一种常见场景是:当怪物逻辑来自第三方库或他人代码时,可以通过访问者模式在尽量不修改原有代码的前提下扩展新功能。

相关推荐
警醒与鞭策7 分钟前
【无标题】
android·unity·性能优化·游戏引擎·perforce
zhchyun20082 小时前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(10)
ui·unity·游戏引擎
地狱为王6 小时前
Unity使用NVIDIA的Audio2Face驱动口型
unity·游戏引擎
ebiobiz8 小时前
STM32F4 Unity 单元测试与多次测试失败率统计详细步骤
stm32·unity·单元测试
一线灵13 小时前
跨平台游戏引擎 Axmol-2.11.5 发布
游戏引擎·lua
淡海水13 小时前
16-01-C#数据结构源码-附录A-术语表
unity·c#·游戏引擎·il2cpp
淡海水14 小时前
16-03-C#数据结构源码-附录C-参考资源
unity·c#·游戏引擎·il2cpp
天神哥哥啊15 小时前
unity联调注意事项-安卓
android·unity·游戏引擎
天神哥哥啊15 小时前
unity联调注意事项-IOS
unity·ios·游戏引擎
可爱系程序猿1 天前
Unity 材质预览才弹 d3dcompiler_47.dll?先分清编辑器 SDK、着色器缓存和玩家包
程序人生·3d·unity·电脑·材质