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

2023-12-16 11:10

本文主要是介绍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();}//准备运行一个动作,将动作初始化,并加入到waitingAddpublic 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 updateprotected 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);elsemid_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 updatepublic virtual void Start(){throw new System.NotImplementedException();}// Update is called once per framepublic 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 updatepublic override void Start(){}// Update is called once per framepublic 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(){//如果有,则释放原有的GameObjectfor(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: // LEFTLANDif(!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: // RIGHTLANDif(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: //BOATif(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;  //上面的情况都不符合,则说明所有角色都安全到达右岸,winreturn;}}

三、运行

        首先新建一个空物体。

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

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

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

这篇关于unity小游戏-牧师与魔鬼-动作分离版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/500246

相关文章

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

请解释Java Web应用中的前后端分离是什么?它有哪些好处?什么是Java Web中的Servlet过滤器?它有什么作用?

请解释Java Web应用中的前后端分离是什么?它有哪些好处? Java Web应用中的前后端分离 在Java Web应用中,前后端分离是一种开发模式,它将传统Web开发中紧密耦合的前端(用户界面)和后端(服务器端逻辑)代码进行分离,使得它们能够独立开发、测试、部署和维护。在这种模式下,前端通常通过HTTP请求与后端进行数据交换,后端则负责业务逻辑处理、数据库交互以及向前端提供RESTful

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj