一、案例说明
《Flappy Bird》是一款由来自越南的独立[游戏开发者]Dong Nguyen所开发的作品,它的玩法极为简单,开发技术也容易,但它却在上线的短短几天之内几乎占据了80%的欧美手机游戏用户,并且最终获得四千万的融资。笔者以这款简单的游戏作为载体,深入浅出理解PureMVC框架思想,并且将这种框架思想应用在以后项目开发中去。
二、框架视图
三、关键代码
1、逻辑控制层 Control
Ctrl_StartGameCommond
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_StartGameCommond : MacroCommand
{
protected override void InitializeMacroCommand()
{
//注册模型与视图Command
AddSubCommand(typeof(Ctrl_RigistModelAndViewCommand));
//注册重新开始command
AddSubCommand(typeof(Ctrl_ReStartGameCommond));
}
}
}
Ctrl_EndGameCommand
using System.Collections;
using System.Collections.Generic;
using PureMVC.Interfaces;
using PureMVC.Patterns;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_EndGameCommand :SimpleCommand{
public override void Execute(INotification notification)
{
//停止脚本运行。
StopScriptRuning();
//关闭当前UI窗体,回到“玩家指导”UI窗体
CloseCurrentUIForm();
//保存当前最高分数
Model_GameDataProxy gameData=Facade.RetrieveProxy(Model_GameDataProxy.NAME) as Model_GameDataProxy;
gameData.SaveHightestScores();
//重置游戏时间
gameData.ResetGameTime();
}
/// <summary>
/// 停止脚本运行
/// </summary>
private void StopScriptRuning()
{
//主角停止运行
GameObject.FindGameObjectWithTag(ProjectConsts.Player).GetComponent<Ctrl_HeroControl>().StopGame();
//得到层级视图根节点。
GameObject goEnviromentRoot = GameObject.Find(ProjectConsts.MainGameScenes);
//管道组停止运行
UnityHelper.FindTheChildNode(goEnviromentRoot, "GamePipes").GetComponent<Ctrl_PipesMoving>().StopGame();
//获取时间脚本,然后停止运行
goEnviromentRoot.GetComponent<Ctrl_GetTime>().StopGame();
//“金币”道具停止
for (int i = 1; i <= 3; i++)
{
//道具开始运行
UnityHelper.FindTheChildNode(goEnviromentRoot, "Pipe" + i + "_Trigger").GetComponent<Ctrl_Golds>().StopGame();
}
}
// 关闭当前UI窗体,回到“玩家指导”UI窗体
private void CloseCurrentUIForm()
{
UIManager.GetInstance().CloseUIForms(ProjectConsts.GamePlayingUIForm);
}
}
}
Ctrl_ReStartGameCommond
using System.Collections;
using System.Collections.Generic;
using PureMVC.Interfaces;
using PureMVC.Patterns;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_ReStartGameCommond : SimpleCommand
{
public override void Execute(INotification notification)
{
//主角重新运行
GameObject.FindGameObjectWithTag(ProjectConsts.Player).GetComponent<Ctrl_HeroControl>().StartGame();
//得到层级视图根节点。
GameObject goEnviromentRoot = GameObject.Find(ProjectConsts.MainGameScenes);
//管道组启动运行
UnityHelper.FindTheChildNode(goEnviromentRoot, "GamePipes").GetComponent<Ctrl_PipesMoving>().StartGame();
//获取时间脚本
goEnviromentRoot.GetComponent<Ctrl_GetTime>().StartGame();
//“金币”道具启动
for (int i =1; i <=3; i++)
{
//道具开始运行
UnityHelper.FindTheChildNode(goEnviromentRoot, "Pipe"+i+"_Trigger").GetComponent<Ctrl_Golds>().StartGame();
}
}
}
}
Ctrl_RigistModelAndViewCommand
using System.Collections;
using System.Collections.Generic;
using PureMVC.Interfaces;
using PureMVC.Patterns;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_RigistModelAndViewCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
Facade.RegisterProxy(new Model_GameDataProxy());
Facade.RegisterMediator(new Vew_GamePlayingMediator());
}
}
}
Ctrl_GetTime
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_GetTime : MonoBehaviour {
//模型代理
public Model_GameDataProxy dataProxy = null;
//游戏是否开始
private bool IsStartGame = false;
/// <summary>
/// 游戏开始
/// </summary>
public void StartGame()
{
//得到模型层类的对象实例
//dataProxy = Facade.Instance.RetrieveProxy(Model_GameDataProxy.NAME) as Model_GameDataProxy;
IsStartGame = true;
}
/// <summary>
/// 游戏结束
/// </summary>
public void StopGame()
{
IsStartGame = false;
}
void Start () {
//得到模型层类的对象实例
dataProxy = Facade.Instance.RetrieveProxy(Model_GameDataProxy.NAME) as Model_GameDataProxy;
//启动协程,每隔一秒种,往PureMVC 模型层发送一条消息。
StartCoroutine("GetTime");
}
/// <summary>
/// 协程,得到时间
/// </summary>
/// <returns></returns>
private IEnumerator GetTime()
{
yield return new WaitForEndOfFrame();
while (true)
{
yield return new WaitForSeconds(1F);
if (dataProxy != null && IsStartGame)
{
//调用(Mediator)模型层方法
dataProxy.AddGameTime();
}
}
}
}
}
Ctrl_Golds
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_Golds : MonoBehaviour {
//模型代理
private Model_GameDataProxy _proxyObj;
//是否开始
private bool _IsStartGame = false;
/// <summary>
/// 开始游戏
/// </summary>
public void StartGame()
{
_proxyObj = Facade.Instance.RetrieveProxy(Model_GameDataProxy.NAME) as Model_GameDataProxy;
//开始游戏
_IsStartGame = true;
}
/// <summary>
/// 结束游戏
/// </summary>
public void StopGame()
{
_IsStartGame = false;
}
/// <summary>
/// 触发检测
/// </summary>
/// <param name="collision"></param>
public void OnTriggerEnter2D(Collider2D collision)
{
if (_IsStartGame)
{
if (collision.gameObject.tag==ProjectConsts.Player)
{
_proxyObj.AddScores();
}
}
}
}
}
Ctrl_BirdControl
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PureMVCDemo
{
[RequireComponent(typeof(Rigidbody2D))]
public class Ctrl_BirdControl : MonoBehaviour {
//升力
public float floUPPower = 3F;
//2D刚体
private Rigidbody2D rd2D;
//主角原始位置
private Vector2 _VecHeroOriginalPosition;
//游戏是否开始
private bool _IsGameStart = false;
//游戏开始
public void StartGame()
{
_IsGameStart = true;
//启用2D刚体
this.gameObject.GetComponent<Rigidbody2D>().isKinematic = false;
//恢复小鸟的原始方位
this.gameObject.transform.position = _VecHeroOriginalPosition;
}
//游戏结束
public void StopGame()
{
_IsGameStart = false;
//恢复小鸟的原始方位
//this.gameObject.transform.position = _VecHeroOriginalPosition;
//禁用2D刚体。
DisableRigibody2D();
}
void Start () {
//保存原始的方位
_VecHeroOriginalPosition = this.gameObject.transform.position;
//获取2D刚体
rd2D = this.gameObject.GetComponent<Rigidbody2D>();
//禁用2D刚体。
DisableRigibody2D();
}
/// <summary>
/// 接收玩家的输入
/// </summary>
void Update()
{
if(_IsGameStart)
{
if (Input.GetButton(ProjectConsts.Fire1))
{
rd2D.velocity = Vector2.up*floUPPower;
}
}
}
//禁用2D刚体。
private void DisableRigibody2D()
{
this.gameObject.GetComponent<Rigidbody2D>().isKinematic = true;
}
}
}
Ctrl_LandMoving
using System.Collections;
using System.Collections.Generic;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_LandMoving : MonoBehaviour
{
public float floMovingSpeed = 1F; //陆地移动速度
private Vector2 _VecOriginalPostion; //陆地原始位置
//测试
//private int intTestNumber = 0;
void Start ()
{
//Log.Write("普通脚本LandMoving=" + this.GetHashCode());
//保存陆地原始位置
_VecOriginalPostion = this.gameObject.transform.position;
}
void Update()
{
//intTestNumber++;
//Log.Write("LandMoving.cs/Update() intTestNumber" + intTestNumber);
//陆地循环移动,到了“临界值”,回复原始方位
if (this.gameObject.transform.position.x<-5F)
{
this.gameObject.transform.position = _VecOriginalPostion;
}
//移动
this.gameObject.transform.Translate(Vector2.left*Time.deltaTime*floMovingSpeed);
}
}
}
Ctrl_PipeAndLand
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using UnityEngine;
namespace PureMVCDemo
{
[RequireComponent(typeof(BoxCollider2D))]
public class Ctrl_PipeAndLand : MonoBehaviour {
/// <summary>
/// 2D碰撞检测
/// </summary>
/// <param name="collision"></param>
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag==ProjectConsts.Player)
{
//通过PureMVC的通知,游戏结束。
Facade.Instance.SendNotification(ProjectConsts.Reg_EndGameCommand);
}
}
}
}
Ctrl_PipesMoving
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PureMVCDemo
{
public class Ctrl_PipesMoving : MonoBehaviour {
public float floMovingSpeed = 1F; //移动速度
private Vector2 _VecOriginalPostion; //原始位置
private bool _IsStartGame = false; //是否开始游戏
//游戏开始
public void StartGame()
{
_IsStartGame = true;
}
//游戏结束
public void StopGame()
{
_IsStartGame = false;
//管道复位
ResetPipesPosition();
}
void Start()
{
//保存原始位置
_VecOriginalPostion = this.gameObject.transform.position;
}
void Update()
{
if (_IsStartGame)
{
//陆地循环移动,到了“临界值”,回复原始方位
if (this.gameObject.transform.position.x < -20F)
{
this.gameObject.transform.position = _VecOriginalPostion;
}
//移动
this.gameObject.transform.Translate(Vector2.left * Time.deltaTime * floMovingSpeed);
}
}
/// <summary>
/// 管道复位
/// </summary>
private void ResetPipesPosition()
{
this.gameObject.transform.position = _VecOriginalPostion;
}
}
}
2、数据模型层 Model
Model_GameData
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PureMVCDemo
{
public class Model_GameData {
//游戏时间
public int GameTime { set; get; }
//游戏分数
public int Scores { set; get; }
//游戏最高分数
public int HightestScores { set; get; }
}
}
Model_GameDataProxy
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class Model_GameDataProxy:Proxy
{
//类的名称
public new const string NAME = "Model_GameDataProxy";
//游戏数据实体类
private Model_GameData _GameData;
//测试
private int intTestNumber = 0;
public Model_GameDataProxy():base(NAME)
{
Log.Write("代理类 Model_GameDataProxy GetHashCode=" + this.GetHashCode());
_GameData=new Model_GameData();
//得到最高分数
_GameData.HightestScores = PlayerPrefs.GetInt(ProjectConsts.GameHighestScores);
}
//增加游戏的时间
public void AddGameTime()
{
//intTestNumber++;
//Log.Write("代理类 intTestNumber=" + intTestNumber);
++_GameData.GameTime;
//数值发送到视图层。
SendNotification(ProjectConsts.Msg_DisplayGameInfo, _GameData);
}
/// <summary>
/// 重置游戏时间
/// </summary>
public void ResetGameTime()
{
_GameData.GameTime = 0;
}
//增加分数
public void AddScores()
{
++_GameData.Scores;
//更新最高分数
GetHightestScores();
}
//得到最高分数
public int GetHightestScores()
{
if (_GameData.Scores>_GameData.HightestScores)
{
_GameData.HightestScores = _GameData.Scores;
}
return _GameData.HightestScores;
}
//保存最高分数
public void SaveHightestScores()
{
if (_GameData.HightestScores > PlayerPrefs.GetInt(ProjectConsts.GameHighestScores))
{
PlayerPrefs.SetInt(ProjectConsts.GameHighestScores, _GameData.HightestScores);
}
}
}
}
3、视图交互层
Vew_GamePlayingMediator
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.Mime;
using Boo.Lang;
using PureMVC.Interfaces;
using PureMVC.Patterns;
using SUIFW;
using UnityEngine;
using UnityEngine.UI;
namespace PureMVCDemo
{
public class Vew_GamePlayingMediator : Mediator
{
public new const string NAME = "Vew_GamePlayingMediator";
//控件定义
private Text _TxtGameTime; //游戏时间
private Text _TxtShowGameTime; //显示游戏时间
private Text _TxtGameScore; //游戏分数
private Text _TxtShowGameScore; //显示最高分数
private Text _TxtGameHighestScore; //游戏最高分数
private Text _TxtShowGameHighestScore; //显示游戏最高分数
/// <summary>
/// 构造函数
/// </summary>
public Vew_GamePlayingMediator():base(NAME)
{
Log.Write("Mediator hashcode="+this.GetHashCode());
}
/// <summary>
/// 初始化字段
/// </summary>
private void InitMediatorFiled()
{
//得到层级视图根节点
GameObject goRootCanvase = GameObject.Find("Canvas(Clone)");
//得到文字控件
_TxtGameTime = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtTime");
_TxtGameScore = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtScores");
_TxtGameHighestScore = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtHighScores");
_TxtGameTime.text = "时间:";
_TxtGameScore.text = "分数:";
_TxtGameHighestScore.text = "最高:";
//得到文字控件数值显示
_TxtShowGameTime = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtTimeShow");
_TxtShowGameScore = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtScoresShow");
_TxtShowGameHighestScore = UnityHelper.GetTheChildNodeComponetScripts<Text>(goRootCanvase, "TxtHighScoresShow");
}
/// <summary>
/// 允许可以接收的消息列表
/// </summary>
/// <returns></returns>
public override IList<string> ListNotificationInterests()
{
IList<string> liResult=new System.Collections.Generic.List<string>();
liResult.Add(ProjectConsts.Msg_DisplayGameInfo);
liResult.Add(ProjectConsts.Msg_InitGamePlayingMediatorFiled);
return liResult;
}
/// <summary>
/// 处理接收到的消息列表
/// </summary>
/// <param name="notification"></param>
public override void HandleNotification(INotification notification)
{
Model_GameData gameData = null;
switch (notification.Name)
{
case ProjectConsts.Msg_InitGamePlayingMediatorFiled:
InitMediatorFiled();
break;
case ProjectConsts.Msg_DisplayGameInfo:
gameData = notification.Body as Model_GameData;
if (gameData!=null)
{
if (_TxtShowGameTime && _TxtShowGameScore && _TxtShowGameHighestScore)
{
_TxtShowGameTime.text = gameData.GameTime.ToString();
_TxtShowGameScore.text = gameData.Scores.ToString();
_TxtShowGameHighestScore.text = gameData.HightestScores.ToString();
}
}
break;
default:
break;
}
}
}
}
StartUIForm
using System.Collections;
using System.Collections.Generic;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class StartUIForm : BaseUIForm {
void Awake()
{
//按钮注册
RigisterButtonObjectEvent("ImgBackground",p=>
OpenUIForm(ProjectConsts.GameGuideUIForm)
);
}
void Start()
{
//启动MVC 框架
new ApplicationFacade();
}
}
}
GameGuideUIForm
using System.Collections;
using System.Collections.Generic;
using PureMVC.Patterns;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class GameGuideUIForm : BaseUIForm {
void Awake()
{
//本窗体类型
CurrentUIType.UIForms_ShowMode = UIFormShowMode.HideOther;
//注册按钮事件
RigisterButtonObjectEvent("BtnGuideOK", p =>
{
OpenUIForm(ProjectConsts.GamePlayingUIForm);
//MVC 启动命令
Facade.Instance.SendNotification(ProjectConsts.Reg_StartGameCommand);
//对视图层字段初始化
Facade.Instance.SendNotification(ProjectConsts.Msg_InitGamePlayingMediatorFiled);
}
);
}
}
}
GamePlayingUIForm
using System.Collections;
using System.Collections.Generic;
using SUIFW;
using UnityEngine;
namespace PureMVCDemo
{
public class GamePlayingUIForm : BaseUIForm {
void Awake () {
//UI窗体类型
CurrentUIType.UIForms_ShowMode = UIFormShowMode.HideOther;
}
}
}
四、效果展示
五、PureMVC源码下载地址
[https://github.com/PureMVC/puremvc-csharp-standard-framework](https://github.com/PureMVC/puremvc-csharp-standard-framework)