unity 3d 牧师与魔鬼小游戏(动作分离并添加裁判类)

一、动作分离

1.改进目的:

  • 将 CCAction 代码补充修改,按课件(第四章,作业2 “牧师与魔鬼 动作分离版”),编写“牧师与魔鬼游戏” 要求。
  • 集成 CCAction,使得动作管理从场景控制器中分离。
  • 利用接口消息传递机制,设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。实现游戏结束判定从场景控制器中分离。

我们在上次作业使用MVC代码架构制作了牧师与魔鬼小游戏,但是不难发现,场景控制器FirstController需要管理的事务过于繁杂,不利于代码管理。而我们现在要做的事情就是,建立一个动作管理器,使得动作管理从场景控制器中分离出来,优化结构,通过场景控制器把需要移动的游戏对象传递给动作管理器,让动作管理器去移动游戏对象。为此,我们需要把每个需要移动的游戏对象的移动方法提取出来,添加到动作管理器中由它来管理不同的移动方法。当动作很多或是需要做同样动作的游戏对象很多的时候,使用动作管理器可以让动作很容易管理,也提高了代码复用性。

        除此之外,我们还需要利用接口消息传递机制,设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。实现“游戏结束判定”从场景控制器中分离。

2.UML图

本次动作分离版牧师与魔鬼的UML图如下:

其中,ISceneController和IUserAction等接口设计、SSDirector场记和UserGUI用户交互的设计、ModelController和对应Model的设计基本与上次作业无异,主要是新增了SSAction动作基类和SSActionManager动过管理器基类并派生出两个子类CCMoveAction和CCActionManager,他们将实现和管理所有的动作方法(虽然我们在该游戏中只有平移这一动作),在该游戏例子中,在FirstController中声明一个CCActionManager动作管理器对象,需要移动对象时只需要声明一个CCMoveAction对象并添加到动作管理器对象即可实现动作从场景控制器中分离。而Judge类则将场景管理器的“判断游戏结束”功能方法分离出来,判断游戏结束,并通知场景管理器。
 

二、代码实现

Judge:

裁判类实际上是把原来FirstController中的UpdadeGameState()方法单独分离出来成为一个类

public class JudgeController{

    FirstController firstCtrl;

    public JudgeController(){
        firstCtrl = SSDirector.GetInstance().CurrentSceneController as FirstController;
    }

    //判断游戏状态
    public int UpdadeGameState(){
        if(firstCtrl.gameState != FirstController.PLAYING) return firstCtrl.gameState;
        //判断是否失败
        int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
        foreach(RoleController r in firstCtrl.RoleCtrl){
            rolePos[r.roleType, r.roleState]++;
        }
        if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) || 
           (rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) || 
           (rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){
            return FirstController.FAILED;
        }  
        //判断是否成功
        foreach(RoleController r in firstCtrl.RoleCtrl){
            if(r.roleType == 0 && r.roleState != FirstController.RIGHTLAND){
                return FirstController.PLAYING; 
            }
        }
        return FirstController.WIN;
    }
}

在上一篇文章中,游戏对象的移动由MoveController和Move共同管理,这样做的坏处是在FirstController中仍然保留有一小部分的用于管理对象运动的代码。在动作分离版的代码中,管理动作的代码被分解成以下三部分,CCActionManager用于管理所有动作,CCMoveAction用于管理“移动”这种动作,Move则是移动这个动作的实体。

 在CCActionManager中主要实现了MoveRole()和MoveBoat()两个函数。通过调用CCMoveAction中的MoveTo和MoveSeqcenceTo来实现两种不同形式的移动效果。

CCMoveAction:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCMoveAction
{
    GameObject moveObject;
    
    public bool IsMoving(){
        return(this.moveObject != null && this.moveObject.GetComponent<Move>().isMoving == true);
    }

    public void MoveTo(GameObject moveObject, Vector3 destination){
        Move test;
        this.moveObject = moveObject;
        if (!moveObject.TryGetComponent<Move>(out test)) {
            moveObject.AddComponent<Move>();
        }
        this.moveObject.GetComponent<Move>().moveAction = this;
        this.moveObject.GetComponent<Move>().destination = destination;
        this.moveObject.GetComponent<Move>().moveMode = Move.single;

    }

    public void MoveSequenceTo(GameObject moveObject, Vector3 destination){
        Move test;
        this.moveObject = moveObject;
        if (!moveObject.TryGetComponent<Move>(out test)) {
            moveObject.AddComponent<Move>();
        }
        this.moveObject.GetComponent<Move>().moveAction = this;
        this.moveObject.GetComponent<Move>().destination = destination;
        this.moveObject.GetComponent<Move>().moveMode = Move.sequence;
    }
}

Move:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    public static int single = 0;
    public static int sequence = 1;


    public bool isMoving;
    bool initialized;
    public int moveMode;
    public bool doneMoving;

    public float speed = 5;


    int n_seq;
    public Vector3[] desseq;
    public Vector3 destination;
    public CCMoveAction moveAction;


    public Move(){
        n_seq = 0;
        isMoving = false;
        initialized = false;
        moveMode = -1;
    }

    void Update()
    {
        if(moveMode == -1) return;
        if(!initialized){
            if(moveMode == single){
                desseq = new Vector3[1];
                desseq[0] = destination;
            }
            else if(moveMode == sequence){
                desseq = new Vector3[3];
                desseq[0] = transform.localPosition + new Vector3(0, 1, 0);
                desseq[1] = destination + new Vector3(0, 1, 0);
                desseq[2] = destination;
            }
            else{
                Debug.Log("ERROR!");
            }
            initialized = true;
        }     
        isMoving = true;
        if(n_seq >= desseq.Length){
            n_seq = 0;
            moveMode = -1;
            initialized = false;
            isMoving = false;
            return;
        }
        if(transform.localPosition == desseq[n_seq]){
            n_seq += 1;
            return;
        }
        transform.localPosition = Vector3.MoveTowards(transform.localPosition, desseq[n_seq], speed * Time.deltaTime);
    }
}

CCActionManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager
{
    public CCMoveAction moveBoatAction;
    public CCMoveAction moveRoleAction;

    public FirstController controller;

    public CCActionManager(){
        controller = SSDirector.GetInstance().CurrentSceneController as FirstController;
        controller.actionManager = this;

        moveBoatAction = new CCMoveAction();
        moveRoleAction = new CCMoveAction();
    }


    public bool IsMoving(){
        return moveRoleAction.IsMoving() || moveBoatAction.IsMoving();
    }


    public void MoveRole(BoatController BoatCtrl, RoleController RoleCtrl, int destination, int seat){
        Vector3 finalPos;
        if(destination == FirstController.RIGHTLAND){
            finalPos = Position.roleRightPos[seat];
        }
        else if(destination == FirstController.LEFTLAND){
            finalPos = Position.roleLeftPos[seat];
        }
        else{
            if(BoatCtrl.onLeftside){
                finalPos = Position.seatLeftPos[seat];
            }
            else{
                finalPos = Position.seatRightPos[seat];
            }
        }
        moveRoleAction.MoveSequenceTo(RoleCtrl.GetModelGameObject(), finalPos);

    }


    public void MoveBoat(BoatController BoatCtrl, int destination){
        if(destination == FirstController.RIGHTLAND){
            moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatRightPos);
            for(int i = 0; i < 3; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])];
                    moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatRightPos[i]);
                }
            }
        }
        else{
            moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);
            for(int i = 0; i < 3; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])];
                    moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatLeftPos[i]);
                }
            }
        }
    }
}

FirstController:

在FirstController中就创建Judge类对象和CCActionManager类对象,由judge判断游戏输赢,由CCActionManager执行游戏对象的移动动作,其他与之前无异

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
    public static int LEFTLAND = 0;
    public static int RIGHTLAND = 1;
    public static int BOAT = 2;

    public static int PRIEST = 0;
    public static int DEVIL = 1;

    public static int PLAYING = 0;
    public static int WIN = 1;
    public static int FAILED = 2;

    public CCActionManager actionManager;

    public BoatController BoatCtrl;
    public RoleController[] RoleCtrl = new RoleController[6];
    public LandController[] LandCtrl = new LandController[2];
    public JudgeController JudgeCtrl;

    public int[] rolesID = new int[6]{0,1,2,3,4,5};
    public int gameState;

    void Awake(){
        SSDirector director = SSDirector.GetInstance();
        director.CurrentSceneController = this;
        director.CurrentSceneController.Initialize();
    }

    public void Initialize(){
        //如果有,则释放原有的GameObject
        for(int i = 0; i < 6; i++){
            if(RoleCtrl[i] != null){
                Destroy(RoleCtrl[i].GetModelGameObject());
            }
        }
        for(int i = 0; i < 2; i++){
            if(LandCtrl[i] != null){
                Destroy(LandCtrl[i].GetModelGameObject());
            }
        }
        if(BoatCtrl != null){
            Destroy(BoatCtrl.GetModelGameObject());
        }
        // 加载控制器和模型
        BoatCtrl = new BoatController();
        BoatCtrl.CreateModel();

        for(int i = 0; i < 6; i++){
            int roleType = (i < 3) ? PRIEST : DEVIL;
            RoleCtrl[i] = new RoleController(roleType, rolesID[i]);
            RoleCtrl[i].CreateModel();
        }
        LandCtrl[0] = new LandController(LEFTLAND, rolesID);
        LandCtrl[1] = new LandController(RIGHTLAND, rolesID);
        LandCtrl[0].CreateModel();
        LandCtrl[1].CreateModel();
        JudgeCtrl = new JudgeController();

        actionManager = new CCActionManager();
        
        
        //开始游戏
        gameState = PLAYING;
    }

    //将角色的ID转换成数组的下标
    public int IDToNumber(int ID){
        for(int i = 0; i < 6; i++){
            if(rolesID[i] == ID){
                return i;
            }
        }
        return -1;
    }

    //点击船时执行
    public void MoveBoat(){
        if(gameState != PLAYING || actionManager.IsMoving()) return;
        gameState = JudgeCtrl.UpdadeGameState();
        if(BoatCtrl.onLeftside){
            actionManager.MoveBoat(BoatCtrl, RIGHTLAND);
        }
        else{
            actionManager.MoveBoat(BoatCtrl, LEFTLAND);
        }
        BoatCtrl.onLeftside = !BoatCtrl.onLeftside;
    }

    //点击角色时执行
    public void MoveRole(int id){
        int num = IDToNumber(id);
        if(gameState != PLAYING || actionManager.IsMoving()) return;
        int seat;
        switch(RoleCtrl[num].roleState){
            case 0: // LEFTLAND
                if(!BoatCtrl.onLeftside) return;
                seat = BoatCtrl.embark(id);
                if(seat == -1) return;
                LandCtrl[0].LeaveLand(id);
                RoleCtrl[num].GoTo(BOAT);
                actionManager.MoveRole(BoatCtrl, RoleCtrl[num], BOAT, seat);
                break;
            case 1: // RIGHTLAND
                if(BoatCtrl.onLeftside) return;
                seat = BoatCtrl.embark(id);
                if(seat == -1) return;
                LandCtrl[1].LeaveLand(id);
                RoleCtrl[num].GoTo(BOAT);
                actionManager.MoveRole(BoatCtrl, RoleCtrl[num], BOAT, seat);
                break;
            case 2: //BOAT
                if(BoatCtrl.onLeftside){
                    seat = LandCtrl[0].getEmptySeat();
                    BoatCtrl.disembark(id);
                    LandCtrl[0].GoOnLand(id);
                    RoleCtrl[num].GoTo(LEFTLAND);
                    actionManager.MoveRole(BoatCtrl, RoleCtrl[num], LEFTLAND, seat);
                }
                else{
                    seat = LandCtrl[1].getEmptySeat();
                    BoatCtrl.disembark(id);
                    LandCtrl[1].GoOnLand(id);
                    RoleCtrl[num].GoTo(RIGHTLAND);
                    actionManager.MoveRole(BoatCtrl, RoleCtrl[num], RIGHTLAND, seat);
                }
                break;
            default: break;
        }
    }

    //Reset按钮执行的功能
    public void Restart(){
        Initialize();
        gameState = PLAYING;
    }

    //获取游戏当前状态
    public int GetGameState(){
        return gameState;
    }
}

三、游戏展示

游戏展示视频:unity 3d游戏编程作业 牧师与魔鬼动作分离版 (bilibili.com)

代码地址:冯威彬/unity 3d - 码云 - 开源中国 (gitee.com)

参考师兄文章,感谢师兄!Unity3D小游戏——牧师与魔鬼(动作分离版) - LoongChan - 博客园 (cnblogs.com)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值