2017年5月15日 星期一

Unity內用C#來進行複製檔案的方式,直接File copy或Stream

當要預先在外部放置資源,等程式執行後才載入時,會視需要檢測資源檔案是否存在、移動、複製和刪除等,這邊便來記錄一下操控外部檔案進行複製的方式。

自己會做的有兩種,很簡單直接的File.Copy、和有點麻煩的FileStream方式。

首先來看File.Copy的方式,一行就輕鬆愉快地搞定;可是如果檔案很大時,那整個程式就會卡在那邊,畫面看起來就會像當機狀態,所以只適用於小型檔案和在本機端時:
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class XXX: MonoBehaviour {

    void Start()
    {
        if(File.Exists("D:/aaa.mp4"))
        {
            File.Copy("D:/aaa.mp4", "D:/bbb.mp4");
        }
    }

    void Update()
    {

    }
}
然後是FileStream的方式,雖然撰寫比較麻煩,但由於是另外執行,所以不會影響到程式裡其他部分的執行,是比較好的圓融方式,而實際上的複製速度會比File.Copy慢,適用於大型檔案和非本機端時:
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class XXX: MonoBehaviour {

    void Start()
    {
        if(File.Exists("D:/aaa.mp4"))
        {
            StartCoroutine(DownloadMovie());
        }
    }

    void Update()
    {

    }

    IEnumerator DownloadMovie()
    {
        FileStream fromFileStream = null;
        FileStream toFileStream = null;
        byte[] buffer = new byte[32768];
        int read;

        fromFileStream = new FileStream("D:/aaa.mp4", FileMode.Open);
        toFileStream = new FileStream("D:/bbb.mp4", FileMode.Create);

        while ((read = fromFileStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            toFileStream.Write(buffer, 0, read);
              
            yield return new WaitForSeconds(0.01f);
        }

        fromFileStream.Close();
        toFileStream.Close();
    }
}
其他還有很多方式,這裡只是笨笨的我所摸過後會用的兩種。

Unity內用C#尋找比對在Dictionary內Structure的屬性資料

Dictionary可以塞各種不同格式的資料,我比較常用的是放Structure,這樣可以把一個個體的各種屬性資料都放在一塊;但當要條件式比對或尋找裡面的某一個屬性資料時,就有點麻煩,因此在這裡做一個記錄。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class XXX : MonoBehaviour {

    public struct playerDataStruct
    {
        public string nickName;
        public int age;
        public int score;
    }

    private Dictionary<string, playerDataStruct> playerDataDictionary = new Dictionary<string, playerDataStruct>();
    private playerDataStruct tempPlayerDataStruct = new playerDataStruct();

    void Start ()
    {
        tempPlayerDataStruct.nickName = "Warrior";
        tempPlayerDataStruct.age = 18;
        tempPlayerDataStruct.score = 1000;

        playerDataDictionary.Add("John", tempPlayerDataStruct);

        tempPlayerDataStruct.nickName = "Fighter";
        tempPlayerDataStruct.age = 20;
        tempPlayerDataStruct.score = 2000;

        playerDataDictionary.Add("Peter", tempPlayerDataStruct);

        //----------------------------------------------------------------------------------------------------------------

        if (playerDataDictionary.Values.Any(x => x.age >= 20) == true)
        {
            Debug.Log("本遊戲有大於20歲以上的玩家。");
        }

        Debug.Log("本遊戲內小於20歲的玩家第一人為:" + playerDataDictionary.Where(x => x.Value.age < 20).Select(x => x.Key).FirstOrDefault());
    }

    void Update ()
    {

    }
}
一般比較常見的情況有:
1.使用Value去逆向尋找Key。
2.判斷此Dictionary內是否包含該Key或是該Value。
3.直接尋找或比對Structure內的某一個屬性。

自己所知的對應方式:

依據條件把找到Key的全都取出
playerDataDictionary.Where(x => x.Value.age < 20).Select(x => x.Key)

取出找到的第一個Key
playerDataDictionary.Where(x => x.Value.age > 20).Select(x => x.Key).FirstOrDefault())
playerDataDictionary.FirstOrDefault(x => x.age >= 20).Key

判斷有無該Key包含在內
playerDataDictionary.ContainsKey("Peter")

判斷有無該Value的屬性包含在內
playerDataDictionary.Values.Any(x => x.age >= 20)

2017年5月9日 星期二

Unity內使用桌面的虛擬鍵盤

虛擬鍵盤一般用在觸控螢幕上,也就是觸控電視和手機平板等,當需要讓使用者輸入的時候就很方便,因此在這記錄一下呼叫虛擬鍵盤的做法,之前在網路上找到並在專案中拿來使用的。

首先是創造一個C#的Script,裡面內容直接如下,可以直接複製貼上:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class VirtualKeyboardController : MonoBehaviour {

    void Start ()
    {

    }

    void Update ()
    {

    }

    public class VirtualKeyboard
    {
        [DllImport("user32")]
        static extern IntPtr FindWindow(String sClassName, String sAppName);

        [DllImport("user32")]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        private static Process _onScreenKeyboardProcess = null;

        //Show the touch keyboard (tabtip.exe).
        public void ShowTouchKeyboard()
        {
            ExternalCall("C:\\Program Files\\Common Files\\Microsoft Shared\\ink\\tabtip.exe", null, false);
            //ExternalCall("TABTIP", null, false);
        }

        //Hide the touch keyboard (tabtip.exe).
        public void HideTouchKeyboard()
        {
            uint WM_SYSCOMMAND = 274;
            int SC_CLOSE = 61536;
            IntPtr ptr = FindWindow("IPTip_Main_Window", null);
            PostMessage(ptr, WM_SYSCOMMAND, SC_CLOSE, 0);
        }

        //Show the on screen keyboard (osk.exe).
        public void ShowOnScreenKeyboard()
        {
            //ExternalCall("C:\\Windows\\system32\\osk.exe", null, false);

            if (_onScreenKeyboardProcess == null || _onScreenKeyboardProcess.HasExited)
                _onScreenKeyboardProcess = ExternalCall("OSK", null, false);
        }

        // Hide the on screen keyboard (osk.exe).
        public void HideOnScreenKeyboard()
        {
            if (_onScreenKeyboardProcess != null && !_onScreenKeyboardProcess.HasExited)
                _onScreenKeyboardProcess.Kill();
        }

        /// <summary>
        /// Set size and location of the OSK.exe keyboard, via registry changes.  Messy, but only known method.
        /// </summary>
        /// <param name='rect'>
        /// Rect.
        /// </param>
        public void RepositionOnScreenKeyboard(Rect rect)
        {
            ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowLeft /t REG_DWORD /d " + (int)rect.x + " /f", true);
            ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowTop /t REG_DWORD /d " + (int)rect.y + " /f", true);
            ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowWidth /t REG_DWORD /d " + (int)rect.width + " /f", true);
            ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowHeight /t REG_DWORD /d " + (int)rect.height + " /f", true);
        }

        private static Process ExternalCall(string filename, string arguments, bool hideWindow)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = filename;
            startInfo.Arguments = arguments;

            // if just command, we do not want to see the console displayed
            if (hideWindow)
            {
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = true;
            }

            Process process = new Process();
            process.StartInfo = startInfo;
            process.Start();

            return process;
        }
    }
}
然後在其他地方來做呼叫或關閉,當然虛擬鍵盤呼叫出來後,也可以直接按鍵盤右上角的X來關閉:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class XXX : MonoBehaviour {

    void Start ()
    {
        VirtualKeyboardController.VirtualKeyboard keyboard = new VirtualKeyboardController.VirtualKeyboard();

        keyboard.ShowOnScreenKeyboard();

        keyboard.HideOnScreenKeyboard();
    }

    void Update ()
    {

    }
}
這樣就可以呼叫和關閉螢幕虛擬鍵盤了,網路上有人說這招在Win 10後就沒辦法叫了,因為Microsoft不再放出虛擬鍵盤的控制權,我自己在Win 10使用是無礙啦......