StrangeIoc框架-Unity

1、导入StrangeIoc框架

Asset Store 搜 StrangeIoc 下载 导入api

2、StrangeIoC的流程图


3、实战

    新建一个Unity3D工程,3D的,用StrangeIoc框架去实现咱们最原始的打砖块!服务器

    Project文件:框架

        

Scripts-> Framework存的全是咱们框架相关的C#脚本文件ide

01-StrangeIoc是StrangeIoc场景下的普通C#脚本即与框架无关,或关系不大this

1.ROOT文件下的框架脚本


StrangeIocContextView对应着spa

StrangeIocMVCSContextView对应着3d

代码以下:code

StrangeIocContextView
orm

using strange.extensions.context.impl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StrangeIocContextView : ContextView {

    /// <summary>
    /// ContextView绑定MVCSContext
    /// </summary>
    private void Awake()
    {
        this.context = new StrangeIocMVCSContextView(this);
    }
}
StrangeIocMVCSContextView
using strange.extensions.context.api;
using strange.extensions.context.impl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StrangeIocMVCSContextView : MVCSContext {

    /// <summary>
    /// 构造MVCSContext须要ContextView
    /// </summary>
    /// <param name="view"></param>
    public StrangeIocMVCSContextView(MonoBehaviour view):base(view)
    {

    }

    /// <summary>
    /// 初始化捆绑关系(类与类,接口与类,消息(enum)与类之间)
    /// </summary>
    protected override void mapBindings()
    {
        //inject
        injectionBinder.Bind<ILocalDataService>().To<LocalDataService>().ToSingleton();
        injectionBinder.Bind<GameModel>().To<GameModel>().ToSingleton();                

        //command
        commandBinder.Bind(CommandEvent.RequestScore).To<RequestScoreCommand>();//这样就能够用全局发送器的Dispatch方法发送RequestScore消息,去到RequestScoreCommand处理了
        commandBinder.Bind(CommandEvent.HitCube).To<HitCubeCommand>();
        //START消息是启动StrangeIoc框架时发送的,这样会触发StartCommand中的Execute方法
        commandBinder.Bind(ContextEvent.START).To<StartCommand>().Once();

        //mediator
        mediationBinder.Bind<PlayerView>().To<PlayerMediator>();//这样Mediator便可直接使用Inject注入方式获取到View      
    }

}

先不用管方法中的具体内容究竟是什么意思,看方法前面的注释去好好地理解一下,咱们这样就实现了对象

将StrangeIocContextView挂载到一个空物体身上,空物体随你命名。(必定要作这一步!)

这样,咱们每次启动游戏的时候就由StrangeIocContextView启动了StrangeIoc框架,能够发现咱们就只是在Awake写了一条代码而已,即这条代码

this.context = new StrangeIocMVCSContextView(this);

意思是将ContextView与MVCSContextView捆绑,同时也实例化MVCSContextView.整个框架主要仍是靠MVCSContextView来管理的。ContextView只是启动框架的。

2.Model文件夹的框架脚本:


存游戏数据的普通脚本:

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

public class GameModel  {

	public int Score { get; set; }//咱们须要一个分数来显示集中球体后的得分,假设击中就有一分

}

3.View文件夹的框架脚本:

   

PlayerView是一个视图类,继承于框架提供的View,挂载于主摄像机Main Camera身上,做用是射球,与PlayerMediator交互更新分数。(View继承于Monobehaviour,因此它能够挂载到游戏物体身上)

PlayerMediator是一个Mediator类,继承于框架提供的Mediator,它不能挂载到游戏物体上,它充当PlayerView的“传话人”

MediatorMessage是一个枚举类,保存了Mediator消息类型。(其实这个我不发代码,大家也能懂,就是个枚举类嘛)

下面的很难一口气讲解完,须要配合其余的框架脚本解释,不过,只要你跟着思路走就没什么难度。

代码以下:

PlayerView

using strange.extensions.dispatcher.eventdispatcher.api;
using strange.extensions.mediation.impl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerView : View {
    //局部发送器:PlayerView利用它与PlayerMediator交互,在下面的HitCube方法中使用到,传话给Mediator,执行Mediator的HitCub方法 具体流程下面说
    [Inject]  //Inject注入,框架特性之一,使用了该特性的属性,都是框架自动赋值的,目前,我所知的只有发送器是不须要捆绑关系的,也就是内置捆绑好了。
    public IEventDispatcher dispatcher { get; set; }//注意必定要是属性,才能用[Inject]注入的方法给它赋值

    public GameObject bullet;
    public float speed;
    public Text scoreText;//直接外部拖拽赋值

    protected override void Start()
    {
        base.Start();             
    }
    //功能:点击鼠标左击时,射线检测到Cube方块,实例化子弹,给子弹一个冲击力Impulse,方向是dir(这个你们都懂吧,A-B 就是B指向A的向量)
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray;
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray,out hit))
            {
                if(hit.collider.tag=="Cube")
                {                    
                    GameObject bulletGo = Instantiate(bullet, transform.position, Quaternion.identity);
                    bulletGo.GetComponent<Bullet>().cameraGo = this.gameObject;
                    Vector3 dir = hit.transform.position - transform.position;//获取从摄像机指向射线撞击点的方向,下面还要对dir作一步标准化normalized
                    bulletGo.GetComponent<Rigidbody>().AddForce(dir.normalized * speed, ForceMode.Impulse);//Impulse是冲击力
                }
            }
        }
    }
    public void UpdateScoreText(int score)//更新分数
    {
        scoreText.text = score.ToString();
    }
    public void HitCube()//子弹撞到Cube后 子弹会调用该方法
    {
        //发送 撞击到方块 事件
        dispatcher.Dispatch(MediatorMessage.CallPlayerMediator_HitCube);
        //Debug.Log("PlayerViewHitCube");
    }
}

PlayerMediator

using strange.extensions.context.api;
using strange.extensions.dispatcher.eventdispatcher.api;
using strange.extensions.mediation.impl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMediator : Mediator {

    //建立一个全局Dispatcher发送器
    [Inject(ContextKeys.CONTEXT_DISPATCHER)]
    public IEventDispatcher Dispatcher { get; set; }

    [Inject]
    public PlayerView PlayerView { get; set; } //咱们是用注入的方法获得PlayerView的,注意咱们在StrangeIocMVCSContextView对PlayerView与PlayerMediator作了捆绑才有这个效果

    public override void PreRegister()
    {
        //Debug.Log("PlayerMediator捆绑以前");
    }

    public override void OnRegister()
    {
        //Debug.Log("PlayerMediator捆绑以后");//这个方法是StrangeIocMVCSContextView里的mapBindings的mediationBinder.Bind<PlayerView>().To<PlayerMediator>();执行后会马上执行
        Dispatcher.AddListener(MediatorMessage.CallPlayerMediator_UpdateScore, UpdateScore);//全局发送器注册一个消息事件,消息是第一个参数,事件是第二个参数(局部发送器同样道理),这里就是让其余类可以直接调用UpdateScore方法。
        PlayerView.dispatcher.AddListener(MediatorMessage.CallPlayerMediator_HitCube,HitCube);//给PlayerView的局部发送器添加一个监听,消息是第一个参数,事件是第二个参数,这样PlayerView就可以发送这个消息,去调用PlayerMediator的HitCube方法了,是否是颇有意思!彻底隔离全靠消息。
        //发送 AddScore事件
        Dispatcher.Dispatch(CommandEvent.RequestScore);//用全局发送器的Dispatch方法发送一个消息,事件是什么呢?好!让咱们回到StrangeIocContextView类的mapBindings方法是否是有个捆绑是关于RequestScore消息捆绑的,它对应事件是RequestScoreCommand    
    }
    public override void OnRemove()
    {
        //Debug.Log("PlayerMediator移除以前");
        Dispatcher.RemoveListener(MediatorMessage.CallPlayerMediator_UpdateScore, UpdateScore); //销毁了当前Mediator,咱们也应该把全局发送器的相关监听给销毁。
    }
    private void UpdateScore(IEvent ie)
    {       
        PlayerView.UpdateScoreText((int)ie.data);
    }
    public void HitCube()
    {
        //发送 碰撞方块事件
        Dispatcher.Dispatch(CommandEvent.HitCube);
        //Debug.Log("PlayerMediatorHitCube");
    }
}

 4.Command文件夹的框架脚本:


RequestScoreCommand

using strange.extensions.command.impl;
using strange.extensions.dispatcher.eventdispatcher.api;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//继承于EventCommand 是可以用它里面定义的一个全局发送器,全局发送器是全局惟一的。
public class RequestScoreCommand : EventCommand {
    
    [Inject]
    public GameModel gameModel { get; set; }//注入方式,咱们捆绑了这个GameModel,因此咱们会自动地用这个GameModel来注入给这个gameModel对象,并且是全局惟一的。


    [Inject]
    public ILocalDataService localDataService { get; set; }//注入,同上,在捆绑处的To<LocalDataService>() 会注入Bind<ILocalDataService>()里面。好好地理解我这一句话,To注入到Bind

    //用全局发送器发送它捆绑的消息后会调用该方法
    public override void Execute()
    {
        Retain();//保持该Command的存在,只要没有Release(),那么就不会销毁该Command
        localDataService.dispatcher.AddListener(CommandMessage.CallRequestScoreCommand, OnCompleteRequestScore);//localDataService的局部发送器注册监听
        //从本地服务器获取分数请求
        localDataService.RequestScore();
    }


    //从本地服务器获取到了分数以后的操做
    private void OnCompleteRequestScore(IEvent ie)
    {
        localDataService.dispatcher.RemoveListener(CommandMessage.CallRequestScoreCommand, OnCompleteRequestScore);
        int score = (int)ie.data;
        gameModel.Score = score;
        dispatcher.Dispatch(MediatorMessage.CallPlayerMediator_UpdateScore, score);
        Release();
    }
}

5.Service文件夹的框架脚本:


ILocalDataService:

using strange.extensions.dispatcher.eventdispatcher.api;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ILocalDataService  {

    void RequestScore();
    void OnRequestScore(int score);
    void UpdateScore(int score);

    IEventDispatcher dispatcher { get; set; }
}

LocalDataService:

using System.Collections;
using System.Collections.Generic;
using strange.extensions.dispatcher.eventdispatcher.api;
using UnityEngine;
using System.IO;
public class LocalDataService : ILocalDataService {

    [Inject]
    public GameModel gameModel { get; set; }

    //局部发送器,用于返回Command
    [Inject]
    public IEventDispatcher dispatcher { get; set; }
   
    //请求本地文本分数
    public void RequestScore()
    {
        int score = 0;
        string path = Application.dataPath + "/Resources/" + GameStatic.PlayerData + ".txt";
        //从本地加载分数  
        if (File.Exists(path))
        {
            string str = Resources.Load<TextAsset>(GameStatic.PlayerData).text;
            if (string.IsNullOrEmpty(str)==false)
            {
                score = int.Parse(str);
            }
        }
        else
        {
            Debug.LogWarning("本地保存文件不存在,正在帮您生成中");
            File.WriteAllText(path, "0");
            score = 0;
        }
        //拿到分数以后要作的事情
        OnRequestScore(score);
    }
    public void OnRequestScore(int score)
    {
        //发送回去RequestScoreCommand,把分数做为参数发送回去
        dispatcher.Dispatch(CommandMessage.CallRequestScoreCommand, score);
    }

    /// <summary>
    /// 更新本地文本分数
    /// </summary>
    public void UpdateScore(int score)
    {
        string path = Application.dataPath + "/Resources/" + GameStatic.PlayerData + ".txt";
        File.WriteAllText(path, score.ToString());       
    }
  
}

上面的一大坨代码,用框架流程图来解释一下,咱们到底干了什么事情!


还有一个没画出来,更新GameModel的Score,你能理解上面的,基本上,就OK了。服务器我随便弄了个脚原本作的,终结!