牧师与魔鬼动作分离版

LogicAdventurer
• 阅读 338

牧师与魔鬼

牧师与魔鬼动作分离版

一、动作分离和MVC的区别

动作分离和MVC是两种常用的软件架构设计模式,它们的区别在于实现方式和重点。

动作分离(AA)是一种轻量级架构,它基于观察者模式实现。应用程序由多个独立的模块组成,这些模块被设计为只负责将信息从一个模块传递到另一个,模块之间没有直接的耦合关系。当一个事件发生时,事件的产生者会通知所有观察者,观察者可以根据事件的类型对事件进行处理。动作分离的重点在于分离视图和模型,以及事件的处理。

MVC模式(Model-View-Controller)是一种更为复杂的架构,它将应用程序的三个基本部分进行分离:数据模型、用户界面和控制器。MVC模式的核心是控制器,它接受用户的交互请求,并负责调用合适的模型来处理请求,最后更新用户界面。MVC模式的重点在于分离模型、视图和控制器,以及实现数据的变更通知和视图的更新。

二、动作管理器的设计

牧师与魔鬼动作分离版

要求:
设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。

三、视频演示

实现效果与上一次实验效果一致,具体可看视频

四、代码解释

本次实验比上次多了裁判类

裁判类Judge:
裁判类实际上是把原来FirstController中的check_game_over单独分离出来成为一个类,而check_game_over中主要需要这三个对象的属性来进行判断:

    private CoastController fromCoast;
    private CoastController toCoast;
    private BoatController boat;

构造函数Judge用于初始化这三个成员变量。

public Judge(CoastController cfrom, CoastController cto, BoatController b){
        fromCoast = cfrom;
        toCoast = cto;
        boat = b;
    }

check_game_over()方法用于检查游戏是否结束。它首先统计起始岸和目标岸上的牧师和魔鬼的数量,然后根据一些规则判断游戏的结果。如果目标岸上的牧师和魔鬼的数量之和等于6,则游戏胜利,返回2;如果起始岸上的牧师数量小于魔鬼数量,并且牧师数量大于0,则游戏失败,返回1;如果目标岸上的牧师数量小于魔鬼数量,并且牧师数量大于0,则游戏失败,返回1;否则,游戏还未结束,返回0。

FristController:
FristController中创建了FirstSceneActionManager这个类的对象

private FirstSceneActionManager actionManager;

FirstSceneActionManager:

    public class FirstSceneActionManager:ActionManager {
        public void moveBoat(BoatController boat) {
            MoveToAction action = MoveToAction.getAction(boat.getDestination(), boat.movingSpeed);
            this.addAction(boat.getGameobj(), action, this);
        }

        public void moveCharacter(MyCharacterController characterCtrl, Vector3 destination) {
            Vector3 currentPos = characterCtrl.getPos();
            Vector3 middlePos = currentPos;
            if (destination.y > currentPos.y) {        //from low(boat) to high(coast)
                middlePos.y = destination.y;
            } else {    //from high(coast) to low(boat)
                middlePos.x = destination.x;
            }
            ObjAction action1 = MoveToAction.getAction(middlePos, characterCtrl.movingSpeed);
            ObjAction action2 = MoveToAction.getAction(destination, characterCtrl.movingSpeed);
            ObjAction seqAction = SequenceAction.getAction(1, 0, new List<ObjAction>{action1, action2});
            this.addAction(characterCtrl.getGameobj(), seqAction, this);
        }
    }

FirstController就是通过FirstSceneActionManager来管理所有动作,可以看到它里面包括了:

Awake()方法中,获取Director实例,并将当前的场景控制器设置为自身。
loadResources()方法中,实例化水面对象,并设置其位置和旋转。
loadCharacter()方法用于加载角色。
moveBoat()方法用于移动船只。
characterIsClicked()方法用于处理角色被点击的事件。
restart()方法用于重新开始游戏。它重置船只、岸边控制器和角色的状态。

相较于之前MVC架构,使用了actionManager的moveCharacter()方法来移动角色,

动作管理类ActionManager:
动作分离是一种设计原则,它将游戏中的行为逻辑与显示逻辑分离开来,使得二者可以独立变化。在游戏开发中,动作分离可以使代码更加模块化和可复用,提高开发效率和代码可维护性。
所以在本次实现中去掉baseCode中Moveable这个类,而选用动作管理器ActionManager来实现。
具体来说,动作分离将游戏中的行为逻辑抽象为一个个独立的动作(Action),每个动作都有自己的执行逻辑和完成条件。游戏对象(如角色、道具等)通过执行不同的动作来实现不同的行为。这样,游戏中的行为逻辑就可以独立于游戏对象的实现,使得行为逻辑可以被复用和扩展。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Com.Mygame;


namespace Com.Mygame
{
    public class ActionManager: MonoBehaviour, ActionCallback {
        private Dictionary<int, ObjAction> actions = new Dictionary<int, ObjAction>();
        private List<ObjAction> waitingToAdd = new List<ObjAction>();
        private List<int> watingToDelete = new List<int>();

        protected void Update() {
            foreach(ObjAction ac in waitingToAdd) {
                actions[ac.GetInstanceID()] = ac;
            }
            waitingToAdd.Clear();

            foreach(KeyValuePair<int, ObjAction> kv in actions) {
                ObjAction ac = kv.Value;
                if (ac.destroy) {
                    watingToDelete.Add(ac.GetInstanceID());
                } else if (ac.enable) {
                    ac.Update();
                }
            }

            foreach(int key in watingToDelete) {
                ObjAction ac = actions[key];
                actions.Remove(key);
                DestroyObject(ac);
            }
            watingToDelete.Clear();
        }

        public void addAction(GameObject gameObject, ObjAction action, ActionCallback whoToNotify) {
            action.gameObject = gameObject;
            action.transform = gameObject.transform;
            action.whoToNotify = whoToNotify;
            waitingToAdd.Add(action);
            action.Start();
        }
        public void actionDone(ObjAction source) {}
    }
}

动作基类Objection:
ActionManager与ObjAction
ActionManager是动作对象管理器的基类,实现了所有动作的基本管理。而ObjAction是所有动作的基类,ActionManager就是通过ObjAction这个接口来管理动作的。

namespace Com.Mygame
{
    public class ObjAction : ScriptableObject
    {

        public bool enable = true;
        public bool destroy = false;

        public GameObject gameObject;
        public Transform transform;
        public ActionCallback whoToNotify;

        public virtual void Start()
        {
            throw new System.NotImplementedException();
        }

        public virtual void Update()
        {
            throw new System.NotImplementedException();
        }
    }

    
}

Start():一个虚拟方法,表示动作的开始执行。在子类中可以重写该方法以实现具体的动作逻辑。
Update():一个虚拟方法,表示动作的更新。在子类中可以重写该方法以实现动作的持续更新逻辑。
代码中的ObjAction类并没有实现具体的动作逻辑,而是提供了一个基类,供其他具体的动作类继承并实现自己的动作逻辑。

五、参考资料

学习Unity(7)小游戏架构改进——实现动作管理器
Unity3D游戏编程-牧师与恶魔 动作分离版

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
梦
5年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
4年前
AI 科学家带你快速 Get 人工智能最热技术
!(https://pic3.zhimg.com/80/v2af9f6637b50b09be60b00a42f3812d5e_1440w.jpg)日前,京东智联云与贪心学院联合举办的人工智能前沿技
Stella981 Stella981
4年前
Noark入门之异步事件
引入异步事件主要是为了各模块的解耦,每当完成一个动作时,向系统发布一个事件,由关心的模块自己监听处理,可选择同步处理,异步处理,延迟处理。何时发布事件,当其他模块关心此动作时<br比如获得道具时,任务系统模块要判定完成进度,BI模块需要上报等等都可以监听此事件,已达模块解耦0x00事件源一个实现xyz.noark.core.event
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这