unity小游戏-牧师与魔鬼-动作分离版

一、回顾

上一次实现了MVC版本,也就是model、view、controller分离的版本。在MVC版本的controller里面,实现了对model的动作控制。在动作种类增多、单一动作需要被复用、某个动作由多个动作联合实现 等等情况下,仍旧在controller里面实现动作逻辑就显得冗余且难以管理。因此,将动作的具体实现逻辑从controller中提取出来,放到Action模块里面定义和管理,同时controller增加对Action模块内的动作管理器的调用,可以较好的解决上述困扰。顾名思义,这个船新版本就是“动作分离版”。

二、实现

旧的scrpits结构:

新的scripts结构:

      1、Action:

               SSActionManager.cs:

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

public class SSActionManager : MonoBehaviour
{
    //动作集,以字典形式存在
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    //等待被加入的动作队列(动作即将开始)
    private List<SSAction> waitingAdd = new List<SSAction>();
    //等待被删除的动作队列(动作已完成)
    private List<int> waitingDelete = new List<int>();

    protected void Update()
    {
        //将waitingAdd中的动作保存
        foreach (SSAction ac in waitingAdd)
            actions[ac.GetInstanceID()] = ac;
        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();
            }
        }

        //销毁waitingDelete中的动作
        foreach (int key in waitingDelete)
        {
            SSAction ac = actions[key];
            actions.Remove(key);
            Destroy(ac);
        }
        waitingDelete.Clear();
    }

    //准备运行一个动作,将动作初始化,并加入到waitingAdd
    public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback manager)
    {
        action.gameObject = gameObject;
        action.transform = gameObject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }

    // Start is called before the first frame update
    protected void Start()
    {

    }

}

                对应的继承于SSActionManager的是 CCActionManager(动作管理器实现)
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback
{
    //是否正在运动
    private bool isMoving = false;
    //船移动动作类
    public CCMoveToAction moveBoatAction;
    //人移动动作类(需要组合)
    public CCSequenceAction moveRoleAction;
    //控制器
    public FirstController controller;

    protected new void Start()
    {
        UnityEngine.Debug.Log("CCActionManager");
        controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
        controller.actionManager = this;
    }

    public bool IsMoving()
    {
        return isMoving;
    }

    //移动船
    public void MoveBoat(GameObject boat, Vector3 target, float speed)
    {
        if (isMoving)
            return;
        isMoving = true;
        moveBoatAction = CCMoveToAction.GetSSAction(target, speed);
        this.RunAction(boat, moveBoatAction, this);
    }

    //移动人
    public void MoveRole(GameObject role, Vector3 destination, int speed)
    {
        Vector3 mid_destination;
        if (role.transform.localPosition.y > destination.y)
            mid_destination = new Vector3(destination.x, role.transform.localPosition.y, destination.z);
        else
            mid_destination = new Vector3(role.transform.localPosition.x, destination.y, destination.z);

        if (isMoving)
            return;
        isMoving = true;
        moveRoleAction = CCSequenceAction.GetSSAction(0, 0, new List<SSAction> { CCMoveToAction.GetSSAction(mid_destination, speed), CCMoveToAction.GetSSAction(destination, speed) });
        this.RunAction(role, moveRoleAction, this);
    }

    //回调函数
    public void SSActionEvent(SSAction source,
    SSActionEventType events = SSActionEventType.Completed,
    int intParam = 0,
    string strParam = null,
    Object objectParam = null)
    {
        isMoving = false;
    }
}

                SSAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject                                                    
{
    public bool enable = true;
    public bool destroy = false;

    public GameObject gameObject { get; set; }
    public Transform transform { get; set; }
    public ISSActionCallback callback { get; set; }

    protected SSAction()
    {

    }

    // Start is called before the first frame update
    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }

    // Update is called once per frame
    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
}

                 对应的继承于SSAction的是CCMoveToAction(单个动作)、CCSequenceAction(组合动作)。    --(动作定义)

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

public class CCMoveToAction : SSAction
{
    //目的地
    public Vector3 target;
    //速度
    public float speed;

    private CCMoveToAction()
    {

    }

    //生产函数(工厂模式)
    public static CCMoveToAction GetSSAction(Vector3 target, float speed)
    {
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    // Start is called before the first frame update
    public override void Start()
    {
        
    }

    // Update is called once per frame
    public override void Update()
    {
        //判断是否符合移动条件
        if (this.gameObject == null || this.transform.localPosition == target)
        {
            this.destroy = true;
            this.callback.SSActionEvent(this);
            return;
        }
        //移动
        this.transform.localPosition = Vector3.MoveTowards(this.transform.localPosition, target, speed * Time.deltaTime);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCSequenceAction : SSAction, ISSActionCallback
{
    //动作序列
    public List<SSAction> sequence;
    //重复次数
    public int repeat = -1;
    //动作开始指针
    public int start = 0;

    //生产函数(工厂模式)
    public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence)
    {
        CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
        action.repeat = repeat;
        action.start = start;
        action.sequence = sequence;
        return action;
    }

    //对序列中的动作进行初始化
    public override void Start()
    {
        foreach (SSAction action in sequence)
        {
            action.gameObject = this.gameObject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
    }

    //运行序列中的动作
    public override void Update()
    {
        if (sequence.Count == 0)
            return;
        if (start < sequence.Count)
        {
            sequence[start].Update();
        }
    }

    //回调处理,当有动作完成时触发
    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Completed,
        int Param = 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);
            }
        }
    }

    void OnDestroy()
    {

    }
}

        为了实现controller(FirstController)与动作管理器之间的交互(后者告诉前者一些信息),需要实现回调函数:(ISSActionCallback.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType:int {Started, Completed}
public interface ISSActionCallback
{
    //回调函数
    void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Completed,
        int intParam = 0,
        string strParam = null,
        Object objectParam = null);
}
2、FirstController、JudgeController

        首先需要对firstcontroller进行修改:
        把原来使用MoveController的部分去掉,因为move(动作)已经从controller里面分离出来了。取而代之的是:1、CCActionManager。2、一个裁判JudgeController(下面讲)

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 JudgeController JudgeCtrl;
    public BoatController BoatCtrl;
    public RoleController[] RoleCtrl = new RoleController[6];
    public LandController[] LandCtrl = new LandController[2];
    public MoveController MoveCtrl;

    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();

        this.gameObject.AddComponent<UserGUI>();
        this.gameObject.AddComponent<CCActionManager>();
        this.gameObject.AddComponent<JudgeController>();
    }
    public void JudgeCallback(int gameState){
        this.gameState=gameState;

    } 
    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();
        JudgeCtrl = new JudgeController();

        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();
        //MoveCtrl = new MoveController();
        //开始游戏
        gameState = PLAYING;
    }

    //将角色的ID转换成数组的下标
    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() || BoatCtrl.isEmpty()) return;//MoveCtrl.IsMoving() 
        //CheckAndSetGameState();
        if(BoatCtrl.onLeftside){
            //MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatRightPos);
            for(int i = 0; i < BoatCtrl.seatNum; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
                    UnityEngine.Debug.Log(r.GetModelGameObject().transform.parent==null);

                    r.GetModelGameObject().transform.parent = BoatCtrl.GetModelGameObject().transform;
                    //MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatRightPos[i]);
                    //actionManager.MoveRole(r.GetModelGameObject(),Position.seatRightPos[i],5);
                }
            }
            actionManager.MoveBoat(BoatCtrl.GetModelGameObject(),Position.boatRightPos,5);
        }
        else{
            actionManager.MoveBoat(BoatCtrl.GetModelGameObject(),Position.boatLeftPos,5);
            //MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);
            for(int i = 0; i < BoatCtrl.seatNum; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
                    r.GetModelGameObject().transform.parent = BoatCtrl.GetModelGameObject().transform;
                    //actionManager.MoveRole(r.GetModelGameObject(),Position.seatLeftPos[i],5);
                    //MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatLeftPos[i]);
                }
            } 
        }
        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 || (BoatCtrl.getEmptySeat() == -1)) return;//增加判断getEmptySeat :
                LandCtrl[0].LeaveLand(id);                                         //如果上不了船,就直接返回,而
                seat = BoatCtrl.embark(id);                                        //不用改动role、land、boat的位置参数
                RoleCtrl[num].GoTo(BOAT);
                if(seat == -1) return;   //增加判断getEmptySeat后,这句应该可以略去
                actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.seatLeftPos[seat],5);
                //MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatLeftPos[seat]);
                break;
            case 1: // RIGHTLAND
                if(BoatCtrl.onLeftside || (BoatCtrl.getEmptySeat() == -1)) return;//同上
                LandCtrl[1].LeaveLand(id);
                seat = BoatCtrl.embark(id);
                RoleCtrl[num].GoTo(BOAT);
                if(seat == -1) return;
                actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.seatRightPos[seat],5);
                //MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatRightPos[seat]);
                break;
            case 2: //BOAT
                if(BoatCtrl.onLeftside){
                    RoleCtrl[num].GetModelGameObject().transform.parent=null;
                    seat = LandCtrl[0].getEmptySeat();
                    BoatCtrl.disembark(id);
                    LandCtrl[0].GoOnLand(id);
                    RoleCtrl[num].GoTo(LEFTLAND);
                    actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.roleLeftPos[seat],5);
                    //MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleLeftPos[seat]);
                }
                else{
                    
                    //UnityEngine.Debug.Log("mid_d");
                    RoleCtrl[num].GetModelGameObject().transform.parent=null;//LandCtrl[1].GetModelGameObject().transform;
                    seat = LandCtrl[1].getEmptySeat();
                    BoatCtrl.disembark(id);
                    LandCtrl[1].GoOnLand(id);
                    RoleCtrl[num].GoTo(RIGHTLAND);
                    //CheckAndSetGameState();//新添加一个在这里,在最后一个角色上右岸之后判定成功
                    actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.roleRightPos[seat],5);
                    //MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleRightPos[seat]);
                }
                break;
            default: break;
        }
    }

    //判断游戏状态
    // public void CheckAndSetGameState(){
    //     if(gameState != PLAYING) return;
    //     //判断是否失败
    //     int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
    //     foreach(RoleController r in RoleCtrl){
    //         rolePos[r.roleType, r.roleState]++;  //roletype: 0天使、1魔鬼
    //     }                                        //rolestate:0左岸、1右岸、2船上
    //     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])){  //船上最多只能承载两个角色时,这行可以删去
    //         gameState = FAILED;
    //         return;
    //     }  
    //     //判断是否成功
    //     foreach(RoleController r in RoleCtrl){
    //         if(r.roleState != RIGHTLAND){//存在角色未到达右岸,则直接返回 //r.roleType == 0 && 
    //             UnityEngine.Debug.Log("这里");
    //             return;
    //         }
    //     }
    //     UnityEngine.Debug.Log("其实是这里");
    //     gameState = WIN;  //上面的情况都不符合,则说明所有角色都安全到达右岸,win
    //     return;
    // }

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

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

        JudgeController:

        JudgeController的作用其实是原来FirstController对游戏状态的判断的那一部分逻辑的实现。

与动作分离类似,将这部分逻辑实现(游戏什么时候Win、Fail等等)分离出来,放到裁判类JudgeController里面,减少firstcontroller的“负担”

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

public class JudgeController : MonoBehaviour{

    public FirstController firstCtrl;

    void Start(){
        UnityEngine.Debug.Log("judgeControl here start");
        firstCtrl = (FirstController)SSDirector.GetInstance().CurrentSceneController;
    }

    //判断游戏状态
    void Update(){
        if(firstCtrl.gameState != FirstController.PLAYING) return;
        //判断是否失败
        int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
        foreach(RoleController r in firstCtrl.RoleCtrl){
            rolePos[r.roleType, r.roleState]++;  //roletype: 0天使、1魔鬼
        }                                        //rolestate:0左岸、1右岸、2船上
        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])){  //船上最多只能承载两个角色时,这行可以删去
            
            firstCtrl.JudgeCallback(FirstController.FAILED);
            //firstCtrl.gameState = FAILED;
            return;
        }  
        //判断是否成功
        foreach(RoleController r in firstCtrl.RoleCtrl){
            if(r.roleState != FirstController.RIGHTLAND){//存在角色未到达右岸,则直接返回 //r.roleType == 0 && 
                UnityEngine.Debug.Log("这里");
                return;
            }
        }
        UnityEngine.Debug.Log("其实是这里");
        firstCtrl.JudgeCallback(FirstController.WIN);
        //firstCtrl.gameState = WIN;  //上面的情况都不符合,则说明所有角色都安全到达右岸,win
        return;
    }

}

三、运行

        首先新建一个空物体。

        与上一个不同,需要将:JudgeController、CCActionManager、FirstController都拖到空物体上。

然后将View模块的UserGUI拖到场景Camara中。

        视频链接:unity小游戏牧师与魔鬼-动作分离版_哔哩哔哩bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
综合小区管理系统管理系统按照操作主体分为管理员和用户。管理员的功能包括报修管理、车位管理、车位分配管理、出入管理、字典管理、房屋管理、物业费缴纳管理、公告管理、物业人员投诉管理、我的私信管理、物业人员管理、用户管理、管理员管理。用户的功能包括管理部门以及部门岗位信息,管理招聘信息,培训信息,薪资信息等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 综合小区管理系统管理系统可以提高综合小区管理系统信息管理问题的解决效率,优化综合小区管理系统信息处理流程,保证综合小区管理系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理综合小区管理系统信息,包括出入管理,报修管理,报修管理,物业费缴纳等,可以管理操作员。 出入管理界面,管理员在出入管理界面中可以对界面中显示,可以对招聘信息的招聘状态进行查看,可以添加新的招聘信息等。报修管理界面,管理员在报修管理界面中查看奖罚种类信息,奖罚描述信息,新增奖惩信息等。车位管理界面,管理员在车位管理界面中新增。公告管理界面,管理员在公告管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值