UnityMVC框架编程核心思想
MVC 编程模式
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:
Model(模型)表示应用程序核心(比如数据库记录列表)。
View(视图)显示数据(数据库记录)。
Controller(控制器)处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。 通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。 通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。
视图
视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但一些新的技术已层出不穷,它们包括Adobe Flash和像XHTML,XML/XSL,WML等一些标识语言和Web services.
MVC好处是它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据是联机存储的还是一个雇员列表,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式。
模型
模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。例如它可能用像EJBs和ColdFusion Components这样的构件对象来处理数据库,被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
控制器
控制器接受用户的输入并调用模型和视图去完成用户的需求,所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据
https://blog.youkuaiyun.com/muyi_amen/article/details/54341065
MVC架构简介
MVC是一种架构设计模式,是一种设计理念。是为了达到分层设计的目的,从而使代码解耦,便于维护和代码的复用。MVC是3个单词的缩写,全称:Model-View-Controller(模型-视图-控制器)。
举一个例子,MVC就好比我们的鞋柜。当没有鞋柜的时候,鞋子是这样摆放的:
有了鞋柜之后,我们的鞋子是这样摆放的:
一眼就能看出,有了鞋柜之后,鞋子的摆放明显的整齐和有序很多,这样也很方便我们找到自己想穿的鞋子,不用将大量的时间花在寻找鞋子上。如果把我们的成千上万行代码和各种复杂的业务逻辑看作是各式各样的鞋子,那我们的MVC就是鞋柜。MVC让你的代码结构更加清晰明了。
没有使用MVC的时候,我们的代码结构如下:
上图那一坨“剪不断、理还乱”的乱麻就是你没有使用分层设计的代码结构。如果这时让你找你代码中的某一段逻辑估计是很费劲的,更别说将代码中的某一段代码进行复用或者替换了。
使用MVC分层设计之后,我们的代码结构如下:
上面的图示可能有点夸张,但是这样可能更好的理解。MVC其实就是提供一种规则,让你把相同类型的代码放在一起,这样就形成了层次,从而达到分层解耦、复用、便于测试和维护的目的。
以上说了一堆,其实就是想让大家理解MVC是什么,有什么作用。接下来,我们结合我们实际开发中的代码类型来解释一下MVC。
1、Model
模型层,可以简单理解就是数据层,用于提供数据。在项目中,(简单理解)一般把数据访问和操作,比如将对象关系映射这样的代码作为Model层,也就是对数据库的操作这一些列的代码作为Model层。比如代码中我们会写DAO和DTO类型的代码,那这个DAO和DTO我们可以理解为是属于Model层的代码。
2、View
视图层,就是UI界面,用于跟用户进行交互。一般所有的JSP、Html等页面就是View层。
3、Controller
控制层,Controller层的功能就是将Model和View层进行关联。比如View主要是显示数据的,但是数据又需要Model去访问,这样的话,View会先告诉Controller,然后Controller再告诉Model,Model请求完数据之后,再告诉View。这样View就可以显示数据了。如下图:
关于Spring MVC和Struts,与MVC的关系:
大家还记得在上面我举过的一个例子,MVC好比鞋柜。那Spring和Struts2只是不同牌子的鞋柜而已。并且Spring MVC和Struts2只是一个协助程序员更好实现MVC分层架构的框架而已。就是说,我们实现MVC不一定非要使用Spring或者struts2,自己按照MVC的理解,自己完成自己代码的分层也行。就好比自己在家用木棍自己制作一个鞋柜也照样可以把鞋子摆放整齐,当然,这样做的话首先要你有这样的一个木工技术。我们没有必要为了一个鞋柜,还要自己去学习木工技术,所以最好最快的方式就是去超市买一个鞋柜。
我们直接使用Spring mvc或者struts2来实现MVC,就是我们直接使用别人做好的东西,直接用。快捷、省时、省事、而且质量好。
最后:
其实现在除了MVC架构之外,还有MVP、MVVM等。
在实际项目中MVC更好的实现应该还多一个service层,用来处理业务逻辑。如下:
其中多出来的Service层,主要是用来处理复杂的业务逻辑,这样结构层次更加鲜明和简介。
FSM(有限状态机)
FSM官方WIki C# - FSMSystem.cs
http://wiki.unity3d.com/index.php/Finite_State_Machine
https://blog.youkuaiyun.com/silangquan/article/details/51155805
一个通用的有限状态机(FSM)框架
吃饭,睡觉,打豆豆
现在要实现一个游戏中的一个NPC的AI, 他只做三件事,吃饭,睡觉,打豆豆,最直接,最简答想到的代码应该是这样。
-
void Update()
-
{
-
if(Hungry)
-
{
-
Eat();
-
return;
-
}
-
if(Sleepy)
-
{
-
Sleep();
-
return;
-
}
-
if(Bored)
-
{
-
KickDD();
-
return;
-
}
-
}
这样的代码是Work,但是,当NPC的状态和行为不断复杂化的时候,慢慢的,你就会添加各种条件变量,慢慢的,你就会用上了各种switch case,慢慢的,判断层级越来越多,慢慢的....
这个时候你就需要一个状态机了。
FSM简介
FSM定义:
一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生。一个有限状态机在任何瞬间只能处在一种状态。
它的优点:
1.编程快速简单,2.易于调试,3.很少的计算开销,4.直觉性,5.灵活性。
简单的框架
主要的两个类,FSMState表示状态,FSMSystem里面维护了一个状态的列表,最后需要一个StateController作为状态的控制器。
代码清单
FSMState.cs
-
using UnityEngine;
-
using System;
-
using System.Collections.Generic;
-
using System.Collections;
-
public enum Transition
-
{
-
NullTransition = 0, // Use this transition to represent a non-existing transition in your system
-
SawPlayer,
-
LostPlayer,
-
NoHealth,
-
ReadytoAim,
-
ReadytoShot,
-
ReadytoIdle,
-
ReadytoAttack,
-
ReadytoChasing
-
}
-
public enum StateID
-
{
-
NullStateID = 0, // Use this ID to represent a non-existing State in your system
-
Idle,
-
Chasing, // jump
-
Attack,
-
Shooting,
-
Aiming,
-
BacktoIdle,//jump
-
Dead,
-
}
-
public abstract class FSMState{
-
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
-
protected StateID stateID;
-
public StateID ID { get { return stateID; } }
-
public void AddTransition(Transition trans, StateID id)
-
{
-
// Check if anyone of the args is invalid
-
if (trans == Transition.NullTransition)
-
{
-
Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
-
return;
-
}
-
if (id == StateID.NullStateID)
-
{
-
Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
-
return;
-
}
-
// Since this is a Deterministic FSM,
-
// check if the current transition was already inside the map
-
if (map.ContainsKey(trans))
-
{
-
Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
-
"Impossible to assign to another state");
-
return;
-
}
-
map.Add(trans, id);
-
}
-
/// <summary>
-
/// This method deletes a pair transition-state from this state's map.
-
/// If the transition was not inside the state's map, an ERROR message is printed.
-
/// </summary>
-
public void DeleteTransition(Transition trans)
-
{
-
// Check for NullTransition
-
if (trans == Transition.NullTransition)
-
{
-
Debug.LogError("FSMState ERROR: NullTransition is not allowed");
-
return;
-
}
-
// Check if the pair is inside the map before deleting
-
if (map.ContainsKey(trans))
-
{
-
map.Remove(trans);
-
return;
-
}
-
Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
-
" was not on the state's transition list");
-
}
-
/// <summary>
-
/// This method returns the new state the FSM should be if
-
/// this state receives a transition and
-
/// </summary>
-
public StateID GetOutputState(Transition trans)
-
{
-
// Check if the map has this transition
-
if (map.ContainsKey(trans))
-
{
-
return map[trans];
-
}
-
return StateID.NullStateID;
-
}
-
/// <summary>
-
/// This method is used to set up the State condition before entering it.
-
/// It is called automatically by the FSMSystem class before assigning it
-
/// to the current state.
-
/// </summary>
-
public virtual void DoBeforeEntering() { }
-
/// <summary>
-
/// This method is used to make anything necessary, as reseting variables
-
/// before the FSMSystem changes to another one. It is called automatically
-
/// by the FSMSystem before changing to a new state.
-
/// </summary>
-
public virtual void DoBeforeLeaving() { }
-
/// <summary>
-
/// This method decides if the state should transition to another on its list
-
/// NPC is a reference to the object that is controlled by this class
-
/// </summary>
-
public abstract void Reason(GameObject player, GameObject npc);
-
/// <summary>
-
/// This method controls the behavior of the NPC in the game World.
-
/// Every action, movement or communication the NPC does should be placed here
-
/// NPC is a reference to the object that is controlled by this class
-
/// </summary>
-
public abstract void Act(GameObject player, GameObject npc);
-
}
FSMSystem.cs
-
using UnityEngine;
-
using System.Collections;
-
using System.Collections.Generic;
-
public class FSMSystem{
-
private List<FSMState> states;
-
// The only way one can change the state of the FSM is by performing a transition
-
// Don't change the CurrentState directly
-
private StateID currentStateID;
-
public StateID CurrentStateID { get { return currentStateID; } }
-
private FSMState currentState;
-
public FSMState CurrentState { get { return currentState; } }
-
public StateID defaultState {set{defaultState = value;} get {return defaultState;}}
-
public void resetToDefaultState()
-
{
-
currentState = states[0];
-
currentStateID = states[0].ID;
-
/*for(int i =0; i< states.Count; i++)
-
{
-
if(states[i].ID == defaultState)
-
{
-
currentState = states[i];
-
currentStateID = states[i].ID;
-
}
-
}*/
-
}
-
public FSMSystem()
-
{
-
states = new List<FSMState>();
-
}
-
/// <summary>
-
/// This method places new states inside the FSM,
-
/// or prints an ERROR message if the state was already inside the List.
-
/// First state added is also the initial state.
-
/// </summary>
-
public void AddState(FSMState s)
-
{
-
// Check for Null reference before deleting
-
if (s == null)
-
{
-
Debug.LogError("FSM ERROR: Null reference is not allowed");
-
}
-
// First State inserted is also the Initial state,
-
// the state the machine is in when the simulation begins
-
if (states.Count == 0)
-
{
-
states.Add(s);
-
currentState = s;
-
currentStateID = s.ID;
-
return;
-
}
-
// Add the state to the List if it's not inside it
-
foreach (FSMState state in states)
-
{
-
if (state.ID == s.ID)
-
{
-
Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
-
" because state has already been added");
-
return;
-
}
-
}
-
states.Add(s);
-
}
-
/// <summary>
-
/// This method delete a state from the FSM List if it exists,
-
/// or prints an ERROR message if the state was not on the List.
-
/// </summary>
-
public void DeleteState(StateID id)
-
{
-
// Check for NullState before deleting
-
if (id == StateID.NullStateID)
-
{
-
Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
-
return;
-
}
-
// Search the List and delete the state if it's inside it
-
foreach (FSMState state in states)
-
{
-
if (state.ID == id)
-
{
-
states.Remove(state);
-
return;
-
}
-
}
-
Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
-
". It was not on the list of states");
-
}
-
/// <summary>
-
/// This method tries to change the state the FSM is in based on
-
/// the current state and the transition passed. If current state
-
/// doesn't have a target state for the transition passed,
-
/// an ERROR message is printed.
-
/// </summary>
-
public void PerformTransition(Transition trans)
-
{
-
// Check for NullTransition before changing the current state
-
if (trans == Transition.NullTransition)
-
{
-
Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
-
return;
-
}
-
// Check if the currentState has the transition passed as argument
-
StateID id = currentState.GetOutputState(trans);
-
if (id == StateID.NullStateID)
-
{
-
Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
-
" for transition " + trans.ToString());
-
return;
-
}
-
// Update the currentStateID and currentState
-
currentStateID = id;
-
foreach (FSMState state in states)
-
{
-
if (state.ID == currentStateID)
-
{
-
// Do the post processing of the state before setting the new one
-
currentState.DoBeforeLeaving();
-
currentState = state;
-
// Reset the state to its desired condition before it can reason or act
-
currentState.DoBeforeEntering();
-
break;
-
}
-
}
-
} // PerformTransition()
-
}
角色控制状态机
角色的控制器通常也是通过状态机来实现。
首先要定义出角色的各种状态已经状态间的转换条件,就像这样:
接下来就是用代码定义各种状态的执行逻辑,跳转条件等。有些复杂的游戏还有通过分层的概念来处理角色的。
下面是最简单的两个状态,Idle和Move。
IdleState.cs
-
using UnityEngine;
-
using System.Collections;
-
namespace CharacterFSM
-
{
-
public class IdleState : CharacterState
-
{
-
float horizontalMove;
-
float verticalMove;
-
public IdleState(Character _host)
-
{
-
host = _host;
-
stateID = CharacterStateID.Idle;
-
}
-
public override void HandleInput(MovementInput movementInput)
-
{
-
horizontalMove = movementInput.moveStrafe;
-
verticalMove = movementInput.moveForward;
-
}
-
public override void Act()
-
{
-
}
-
public override void Reason()
-
{
-
if (horizontalMove * horizontalMove + verticalMove * verticalMove < 0.1f)
-
{
-
return;
-
}
-
else
-
{
-
host.stateController.SetTransition(CharacterStateTransition.ToMove);
-
}
-
}
-
public override void DoBeforeEntering()
-
{
-
host.animator.SetBool("Static_b", true);
-
host.animator.SetFloat("Speed_f", 0);
-
}
-
}
-
}
MoveState.cs
-
using UnityEngine;
-
using System.Collections;
-
namespace CharacterFSM
-
{
-
public class MoveState : CharacterState
-
{
-
float stepDelta;
-
float stepMark;
-
public MoveState(Character _host)
-
{
-
stepMark = -1f;
-
stepDelta = 0.3f;
-
host = _host;
-
stateID = CharacterStateID.Move;
-
}
-
public override void HandleInput(MovementInput movementInput)
-
{
-
float maxSpeed = host.MaxSpeed * Mathf.Sqrt(movementInput.moveStrafe * movementInput.moveStrafe + movementInput.moveForward * movementInput.moveForward);
-
host.CurrentSpeed -= 2 * host.Acceleration * Time.deltaTime;
-
host.CurrentSpeed = Mathf.Max(maxSpeed, host.CurrentSpeed);
-
Vector2 tmp = new Vector2(movementInput.moveStrafe, movementInput.moveForward).normalized * host.CurrentSpeed;
-
host.CurrentVelocity = new Vector3(tmp.x, 0, tmp.y);
-
host.animationController.SetSpeed(host.CurrentSpeed);
-
}
-
public override void Act()
-
{
-
}
-
public override void Reason()
-
{
-
if(host.CurrentSpeed < 0.01f )
-
{
-
host.stateController.SetTransition(CharacterStateTransition.ToIdle);
-
}
-
}
-
public override void DoBeforeLeaving()
-
{
-
}
-
public override void DoBeforeEntering()
-
{
-
host.animationController.PerformMove();
-
}
-
}
-
}
还有一个比较重要的类,CharacterStateController
-
using UnityEngine;
-
using System.Collections;
-
using CharacterFSM;
-
public class CharacterStateController {
-
CharacterFSMSystem characterFsm;
-
Character character;
-
public CharacterStateController(Character _character)
-
{
-
character = _character;
-
}
-
public CharacterStateID GetCurrentStateID()
-
{
-
return characterFsm.CurrentStateID;
-
}
-
public void Init()
-
{
-
ConstructFSM();
-
}
-
// Update is called once per frame
-
public void Update () {
-
Debug.Log(GetCurrentStateID());
-
//Debug.Log(character.movementInput.moveForward + " " +character.movementInput.moveStrafe);
-
characterFsm.CurrentState.HandleInput(character.movementInput);
-
characterFsm.CurrentState.Reason();
-
characterFsm.CurrentState.Act();
-
}
-
public void SetTransition(CharacterStateTransition t)
-
{
-
if (characterFsm != null)
-
{
-
characterFsm.PerformTransition(t);
-
}
-
}
-
void ConstructFSM()
-
{
-
IdleState idleState = new IdleState(character);
-
idleState.AddTransition(CharacterStateTransition.ToMove, CharacterStateID.Move);
-
MoveState moveState = new MoveState(character);
-
moveState.AddTransition(CharacterStateTransition.ToIdle, CharacterStateID.Idle);
-
characterFsm = new CharacterFSMSystem();
-
characterFsm.AddState(idleState);
-
characterFsm.AddState(moveState);
-
}
-
}
这个类没必要声明称Monobehavior,只需要作为Character的一个成员来处理就可以了。运行的效果是这样的。