Windows 10 UWP GamePad以及Http cookie實作

  • 218
  • 0
  • UAP
  • 2021-04-30

在UWP上使用GamePad ( JoyStick )和Http cookie共享給WebView的方式。

日前遇上要使用GamePad(JoyStick)遊戲手把的UWP開發問題,所以稍微研究了一下Windows.Gaming.Input的GamePad物件!

而這個GamePad物件有幾點要注意!

  1. 使用的是Xbox 360或是Xbox One的JoyStick為基礎,所以對於其他第三方的JoyStick會有相容性問題。
  2. GamePad的讀取是需要高即時性的,最好使用Dispatcher.TryRunAsync的時候搭配CoreDispatcherPriority.High。
  3. GamePad的讀取必須要在Window Focus的UWP app上。

稍微說一下關於JoyStick的問題,之前在Win32的Classic的APP開發上可以使用DirectInput(DirectX 8)全域的JoyStick的抓取在JoyStick的輸入事件。但是...從Direct X 9的版本開始MSFT就將DirectInput拔除換作XInput的API!至於DirectInput和XInput有何優劣就請自行Search一下啦~

開始進入正題,在UWP如何使用JoyStick ( Gamepad )的API,在一開始的時候reference Windows.Gaming.Input的namespace就可以使用GamePad的物件。Gamepad的物件有兩個事件GamepadAdded以及GamepadRemoved,分別就是當UWP的App偵測到有Gamepad插入(已經插入的狀態)和被拔除的狀態。

Gamepad.GamepadAdded += Gamepad_GamepadAdded;
Gamepad.GamepadRemoved += Gamepad_GamepadRemoved;

稍微要注意的是GamepadAdded是每次只要App launch的時候都需要抓取到目前UWP Client連接到的JoyStick數量!以下是Added和Removed的事件

private void Gamepad_GamepadAdded(object sender, Gamepad e)
{
  System.Diagnostics.Debug.WriteLine("Add");
  FirstGp = Gamepad.Gamepads.FirstOrDefault();
}

private void Gamepad_GamepadRemoved(object sender, Gamepad e)
{
  System.Diagnostics.Debug.WriteLine("Remove");
}

然後要取的CurrentReading的操作並顯示在UI的話大致Code如下

<Page
    x:Class="GamePadApp1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GamePadApp1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <x:Double x:Key="CircleSize">40</x:Double>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Ellipse x:Name="RedCircle" Fill="Red" Width="{StaticResource CircleSize}" Height="{StaticResource CircleSize}">
            <Ellipse.Transform3D>
                <CompositeTransform3D x:Name="CircleTransform3D"/>
            </Ellipse.Transform3D>
        </Ellipse>
    </Grid>
</Page>

先在XAML上面加入個Ellipse來表示Thumbstick(蘑菇頭)移動方向,並使用CompositerTransform3D來及時的顯示Transfrom。

private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
 var baseScale = 100;
 while(true)
 {
   await Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
   {
      var fgp = FirstGp.GetCurrentReading();
      CircleTransform3D.TranslateX = fgp.LeftThumbstickX * baseScale;
      CircleTransform3D.TranslateY = fgp.LeftThumbstickY * baseScale * -1;
   });
   await Task.Delay(TimeSpan.FromMilliseconds(5));
 }
}

接者在MainPage_Loaded的事件中使用Loop的方式不斷在First的Gamepad上抓取CurrentReading的數值後把左邊的Thumbstick的移動assign給在XAML上的Ellispe的CompositTransform。而為什麼Y軸需要乘一個 -1的是因為在Y的部分是對應到XAML的Composite屬性是上下顛倒的。這樣就可以看到磨菇頭在XAML上的移動。


接者第二個問題則是因為要做到Hybird的APP(web browser和native app有些超做需要整合)所以當如果APP進行登入後webview也需要能夠顯示為登入的狀態。

這邊使用的是Windows.Web.Http的HttpClient,而非System.Web.Http的喔! 因為如果使用System.Web下的HttpClient將無法達成共享Cookie的在App的方式。

先是Call Login的Async方法大致如下

public async Task<string> LoginAsync(string accountName, string accountPassword)
        {
            var accountObject = new JObject();
            var uriString = System.IO.Path.Combine(baseUriString, LoginApi);
            accountObject.Add(new JProperty("accountName", $"{accountName}"));
            accountObject.Add(new JProperty("accountPassword", $"{accountPassword}"));
            var content = JsonConvert.SerializeObject(accountObject);
            var resultStr = string.Empty;
            try
            {
                var filter = new HttpBaseProtocolFilter();
                filter.AllowAutoRedirect = true;
                filter.CacheControl.ReadBehavior = HttpCacheReadBehavior.Default;
                filter.CacheControl.WriteBehavior = HttpCacheWriteBehavior.Default;
                var cookieMgr = filter.CookieManager;
                using (var client = new HttpClient(filter))
                {
                    using (var response = await client.PostAsync(new Uri(uriString), new HttpStringContent(content, Windows.Storage.Streams.UnicodeEncoding.Utf8, mediaType)))
                    {
                        response.EnsureSuccessStatusCode();
                        var responseStr = await response.Content.ReadAsStringAsync();
                        resultStr = responseStr;
                        SetupCookie(resultStr, cookieMgr);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                resultStr = ex.Message;
            }
            return resultStr;
        }

先把account的Name以及Password做Json序列化,然後再使用HttpBaseProtocolFilter來設定給httpclient,然後以下是SetupCookie的寫法

private void SetupCookie(string rawJsonString, HttpCookieManager cookieManager)
{
  var accountObj = JsonConvert.DeserializeObject<UserAccountModel>(rawJsonString);
  var cookie = new HttpCookie("AccessKey", "Domain.com", "/");
  cookie.HttpOnly = false;
  cookie.Secure = false;
  cookie.Value = accountObj.accessKey;
  cookieManager.SetCookie(cookie, false);
}

這邊情境是把Server吐出來的Json String轉換成物件後把accessKey塞進cookie的值,接者在把Cookie設定在HttpCookieManager內。

UWP所提供的Cookie container可以在整個App的Life cycle都可以把Cookie變成共享的方式,當WebView進行Navigate的時候依然可以在APP內使用Cookie來帶相關資訊給Server端。