2016年4月7日星期四

[Unity3D]用uFrame的Kernel在場景間修改玩家狀態和數據


在uFrame 1.6的 Example Project裏有一個UserManagementSystem的系統(WinCondition這個eEnum是我之後加的,下文會再討論)



public class UserManagementService : UserManagementServiceBase
{
    [Inject("LocalUser")] public UserViewModel LocalUser;

    public override void Setup()
    {
        base.Setup();
  // for develop only
  LocalUser.AuthorizationState = AuthorizationState.Authorized;
        //LocalUser.AuthorizationState = AuthorizationState.Unauthorized;
    }

    public void AuthorizeLocalUser(string Username, string Password)
    {
        if (Username == "uframe" && Password == "uframe")
        {
            Debug.Log("authorized in service");     
            LocalUser.AuthorizationState = AuthorizationState.Authorized;
        }
    }
}

這是處理Example Project內一開始登入的部份,就是確認登入成功後把AuthorizationState這個Enum轉成Authorized,方便在其他腳本檢查玩家的狀態。

我在想,是不是可以用UserManagementSystem內UserViewModel(我把這個instance命名為LocalUser, 上圖左上角)儲存玩家各種設定和資料,然後在不同場景不同的View 或 Controller的腳本把LocalUser取出來用或修改設定呢?

簡單說個例子,其中一個想做的功能就是在主畫面(有一個SetBattleScreenView)按不同作戰/勝利模式的按鈕,會修改LocalUser中WinCondition的設定,在進入作戰畫面後(另一個場景scene),而作戰畫面會檢查玩家在用哪個勝利模式(就是WinCondition),而運行不同的代碼。



(如果大家不了解uFrame MVVM模式可以來這裏了解一下:http://isaacforfun.blogspot.hk/2015/06/uframeflappy-brid.html)

但在uFrame的框架下,像UserManagementService腳本內用[Inject("LocalUser")]這個功能抓取LocalUser這個MVVM,只能在繼承了uFrame Service/ServiceBase才可以用!要是我在其他的View或Controller 的腳本是沒辦法用Inject抓取LocalUser的!

我苦惱了 OTL

後來我發覺了uFrame MVVM在版本1.6,新增Kernel的偉大!先簡單理解,Kernel就是一個在不同場景都會存在的東西,你能在Kernel內建立不同的Service,但這次先不討論這個。
(關於uFrame內的Kernel,可以來這裏認識一下:http://isaacforfun.blogspot.hk/2015/07/unity3duframe-16.html)

這次主要想說,其實Kernel一直都在儲存LocalUser這個UserViewModel!透過Kernel,我們可以用以下代碼在View,  ViewModel, Controller 或uFrameComponent 的腳本把LocalUser抓下來用


public UserViewModel LocalUser;

LocalUser =  uFrameKernel.Container.Resolve("LocalUser");


在SetBattleScreenView這個腳本﹐在InitializeViewModel先把LocalUser抓下來(記得在SetBattleScreenView的inspector激活Initialise ViewModel),然後按下Mission1Button這個按鈕,修改LocalUser的WinCondition,並轉換場景


protected override void InitializeViewModel(uFrame.MVVM.ViewModel model) 
{
     base.InitializeViewModel(model);
     // NOTE: this method is only invoked if the 'Initialize ViewModel' is checked in the inspector.
     // var vm = model as SetBattleScreenViewModel;
     // This method is invoked when applying the data from the inspector to the viewmodel.  Add any view-specific customizations here.
   LocalUser = uFrameKernel.Container.Resolve("LocalUser");
   Debug.Log (LocalUser == null ? "LocalUser is null" : LocalUser.Identifier);
}


public override void Bind() 
{
     base.Bind();
     // Use this.SetBattleScreen to access the viewmodel.
     // Use this method to subscribe to the view-model.
     // Any designer bindings are created in the base implementation.

    this.BindButtonToHandler (Mission1Button, () => {
      if (LocalUser != null)
         LocalUser.WinCondition = WinCondition.Tower;
  
     //Move from MainMmenuScreen to MainGameScene
     ChangetoMainGame();
    });
}

轉換到作戰畫面後,因為我是用繼承uFrameComponent的腳本處理作戰場景的邏輯,所以要在KernelLoaded內抓取LocalUser。因為KernelLoaded 之後才會Initialize ViewModel,所以在View或Controller內直接在Initialize ViewModel裏抓Kernel內的東西就好了


public MainGameRootViewModel MainGameVM;
public UserViewModel LocalUser;

// Call when Kernel have been loaded
public override void KernelLoaded()
{
  base.KernelLoaded();
  LocalUser =  uFrameKernel.Container.Resolve<UserViewModel>("LocalUser");
  //對呀,我也可以這樣抓取作作戰系統(MainGameSystem)的ViewModel
  MainGameVM = uFrameKernel.Container.Resolve<MainGameRootViewModel>("MainGameRoot");

  MainGameVM.WinCondition = LocalUser.WinCondition;
}

得放多一次流程圖,事就這樣成了!



題外話:

近幾個月uFrame的支援變得不足,後來uFrame製作人親身在Slack(uFrame的群組)跟我們說自己的身體狀況欠佳,可以更新和支援比較慢,並說到最壞打算是Open Source uFrame的所有內容

今日製作人再跟我們說將會Open Source uFrame的所有內容,並在準備一份正式的通知和解釋背後的原因,這是一個沉重的決定......

然後剛剛有人發現uFrame MVVM和ECS已經是免費了
uFrame MVVM
uFrame ECS

待官方資料出來後,我再寫一篇個人感想吧,希望製作人身體健康吧~

1 則留言: