Lab6:魔鬼与牧师 -v2

本次实验的任务为:在Lab5已完成的内容上进行改进。改进要求如下:

        1.将所有动作(上下船、船的移动)分离出来
        2.设计一个裁判类,能够判定游戏是否结束并通知给场景控制器Controller。

若要查看Lab5中的牧师与魔鬼游戏设计,请点击:

牧师与魔鬼-v1

以下是改进内容:

一,动作分离

        相较于初始版本中将动作设计为一个class的方式(public class Move : MonoBehaviour),本次改进中将所有动作都分离封装到另一个单独的 C# script中,命名为Action.cs。


1.所有动作的父类

在这里,我们需要先设计好两个动作(移动船和移动角色)的父类,以便后续使用。代码如下:

public class SSAction : ScriptableObject
{
    public bool enable = true;                      // 是否正在进行此动作
    public bool destroy = false;                    // 用于在动作执行完毕之后销毁该动作对象
    public GameObject gameobject;                   // 执行动作的实际对象(船或角色)
    public Transform transform;                     // 动作对象的transform
    public ISSActionCallback callback;              // 回调函数
    protected SSAction() { }                        // protected保证SSAction不会被new

    // 子类可以使用这两个函数
    public virtual void Start() {                   
        throw new System.NotImplementedException();
    }

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

其中ISSActionCallback将会在后续实现。

2.点到点移动的动作类

该动作子类继承于上面的动作父类,设计用于单段直线运动,参数仅有目的地坐标和速度。

public class SSMoveToAction : SSAction                        // 移动
{
    public Vector3 target;        // 移动到的目的地
    public float speed;           // 移动的速度

    private SSMoveToAction() { }
    public static SSMoveToAction GetSSAction(Vector3 target, float speed) {
        // 让unity自己创建一个MoveToAction实例,并自己回收
        SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    public override void Update() {
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
        if (this.transform.position == target) {
            this.destroy = true;
            this.callback.SSActionEvent(this);   //告诉动作管理或动作组合这个动作已完成
        }
    }

    public override void Start() {
        // 移动动作建立时候不做任何事情
    }
}

3.动作的队列——用于多段移动的存储

该动作子类继承于上面的动作父类,设计用于角色移动这样的多段移动。

public class SequenceAction : SSAction, ISSActionCallback
{
    public List<SSAction> sequence;    // 动作的列表
    public int repeat = -1;            // 判定是否做完列表中的所有动作
    public int start = 0;              // 当前做的动作的索引

    public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence) {
        // 让unity自己创建一个SequenceAction实例
        SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();
        action.repeat = repeat;
        action.sequence = sequence;
        action.start = start;
        return action;
    }

    public override void Update() {
        if (sequence.Count == 0) 
            return;
        if (start < sequence.Count)
            sequence[start].Update();     // 在Update函数中会有start++
    }

    public void SSActionEvent(SSAction source, int intParam = 0, string strParam = null, Object objectParam = null) {
        source.destroy = false;          
        this.start++;
        if (this.start >= sequence.Count) {
            this.start = 0;
            if (repeat > 0) 
                repeat--;
            if (repeat == 0) {                     // 整个组合动作都做完了
                this.destroy = true;               // 删除整个组合动作
                this.callback.SSActionEvent(this); // 告诉组合动作的管理对象组合做完了
            }
        }
    }

    public override void Start() {
        foreach (SSAction action in sequence) {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;                // 组合动作中每个动作的回调是这个组合动作
            action.Start();
        }
    }

    void OnDestroy()  {
        // 如果组合动作做完第一个动作突然不要它继续做了,那么后面的具体的动作需要被释放
    }
}

4.回调函数

这个函数初始化为接口,被动作的父类初始化为一个参数,而函数在动作队列、动作管理器这些类中重构,用处在于让 动作 向 动作队列/动作管理器 发送信息,告知动作已结束。

public interface ISSActionCallback
{
    void SSActionEvent(SSAction source, /*SSActionEventType events = SSActionEventType.Competeted,*/
        int intParam = 0, string strParam = null, Object objectParam = null);
}

5.动作管理器和游戏管理器

动作管理器如下:

public class SSActionManager : MonoBehaviour, ISSActionCallback                      // action管理器
{

    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();    // 将执行的动作的字典集合,int为key,SSAction为value
    private List<SSAction> waitingAdd = new List<SSAction>();                       // 等待去执行的动作列表
    private List<int> waitingDelete = new List<int>();                              // 等待删除的动作的key                

    protected void Update() {
        foreach (SSAction ac in waitingAdd) {
            actions[ac.GetInstanceID()] = ac;                                      // 获取动作实例的ID作为key
        }
        waitingAdd.Clear();

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

        foreach (int key in waitingDelete) {
            SSAction ac = actions[key];
            actions.Remove(key);
            Destroy(ac);
        }
        waitingDelete.Clear();
    }

    public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager) {
        action.gameobject = gameobject;
        action.transform = gameobject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }

    public void SSActionEvent(SSAction source, /*SSActionEventType events = SSActionEventType.Competeted,*/
        int intParam = 0, string strParam = null, Object objectParam = null) {
        // 牧师与魔鬼的游戏对象移动完成后就没有下一个要做的动作了,所以回调函数为空
    }
}

游戏管理器如下:

public class MySceneActionManager : SSActionManager  // 游戏管理器
{

    private SSMoveToAction moveBoatToEndOrStart;     // 用于移动船到对岸
    private SequenceAction moveRoleToLandorBoat;     // 用于移动角色

    public Controllor sceneController;

    protected void Start() {
        sceneController = (Controllor)SSDirector.GetInstance().CurrentScenceController;
        sceneController.actionManager = this;
    }
    public void moveBoat(GameObject boat, Vector3 target, float speed) {
        moveBoatToEndOrStart = SSMoveToAction.GetSSAction(target, speed);
        this.RunAction(boat, moveBoatToEndOrStart, this);
    }

    public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos, float speed) {
        SSAction action1 = SSMoveToAction.GetSSAction(middle_pos, speed);
        SSAction action2 = SSMoveToAction.GetSSAction(end_pos, speed);
        moveRoleToLandorBoat = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 });
        this.RunAction(role, moveRoleToLandorBoat, this);
    }
}

至此,动作的分离就设计好啦,只需要在场景控制器Controller中调用以上方法,即可操作游戏。

二,裁判类的设计

裁判类的作用即为判定游戏是否开始、是否已结束等状态,并将游戏状态返回给场景控制器Controller的usergui.sign这一参数中,从而使usergui其显示对应内容。代码如下:

public class Judger
{
    LandModel start_land;    // 初始化两岸和船对应的变量
    LandModel end_land;      // 用于对岸上和船上的牧师和魔鬼计数
    BoatModel boat;          // 从而判定游戏状态

    //将游戏内初始化好的两岸和船赋值给裁判类的变量
    public Judger(LandModel bl, LandModel el, BoatModel b) {
        start_land = bl;
        end_land = el;
        boat = b;
    }

    public int Check() {
        int start_priest = (start_land.GetRoleNum())[0];
        int start_devil = (start_land.GetRoleNum())[1];
        int end_priest = (end_land.GetRoleNum())[0];
        int end_devil = (end_land.GetRoleNum())[1];

        if (end_priest + end_devil == 6)        // 游戏胜利
            return 3;

        int[] boat_role_num = boat.GetRoleNum();
        if (boat.GetBoatSign() == 1) {          // 船在开始岸时,开始岸的计数要包括船上
            start_priest += boat_role_num[0];
            start_devil += boat_role_num[1];
        } else {                                // 船在结束岸时,结束岸的计数要包括船上
            end_priest += boat_role_num[0];
            end_devil += boat_role_num[1];
        }

        if ((start_priest > 0 && start_priest < start_devil) || (end_priest > 0 && end_priest < end_devil)) { //游戏失败
            return 2;
        }

        return 1;                               //游戏正在进行中
    }
}

三.总结

以上就是动作分离版牧师和魔鬼的核心改动内容。完整内容请看以下链接:

3D游戏设计: 3D游戏设计仓库 (gitee.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值