2017年5月15日 星期一

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

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

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

首先來看File.Copy的方式,一行就輕鬆愉快地搞定;可是如果檔案很大時,那整個程式就會卡在那邊,畫面看起來就會像當機狀態,所以只適用於小型檔案和在本機端時:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using System.IO;
  4.  
  5. public class XXX: MonoBehaviour {
  6.  
  7.     void Start()
  8.     {
  9.         if(File.Exists("D:/aaa.mp4"))
  10.         {
  11.             File.Copy("D:/aaa.mp4", "D:/bbb.mp4");
  12.         }
  13.     }
  14.  
  15.     void Update()
  16.     {
  17.  
  18.     }
  19. }
然後是FileStream的方式,雖然撰寫比較麻煩,但由於是另外執行,所以不會影響到程式裡其他部分的執行,是比較好的圓融方式,而實際上的複製速度會比File.Copy慢,適用於大型檔案和非本機端時:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using System.IO;
  4.  
  5. public class XXX: MonoBehaviour {
  6.  
  7.     void Start()
  8.     {
  9.         if(File.Exists("D:/aaa.mp4"))
  10.         {
  11.             StartCoroutine(DownloadMovie());
  12.         }
  13.     }
  14.  
  15.     void Update()
  16.     {
  17.  
  18.     }
  19.  
  20.     IEnumerator DownloadMovie()
  21.     {
  22.         FileStream fromFileStream = null;
  23.         FileStream toFileStream = null;
  24.         byte[] buffer = new byte[32768];
  25.         int read;
  26.  
  27.         fromFileStream = new FileStream("D:/aaa.mp4", FileMode.Open);
  28.         toFileStream = new FileStream("D:/bbb.mp4", FileMode.Create);
  29.  
  30.         while ((read = fromFileStream.Read(buffer, 0, buffer.Length)) > 0)
  31.         {
  32.             toFileStream.Write(buffer, 0, read);
  33.              
  34.             yield return new WaitForSeconds(0.01f);
  35.         }
  36.  
  37.         fromFileStream.Close();
  38.         toFileStream.Close();
  39.     }
  40. }
其他還有很多方式,這裡只是笨笨的我所摸過後會用的兩種。

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

Dictionary可以塞各種不同格式的資料,我比較常用的是放Structure,這樣可以把一個個體的各種屬性資料都放在一塊;但當要條件式比對或尋找裡面的某一個屬性資料時,就有點麻煩,因此在這裡做一個記錄。
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.Linq;
  5.  
  6. public class XXX : MonoBehaviour {
  7.  
  8.     public struct playerDataStruct
  9.     {
  10.         public string nickName;
  11.         public int age;
  12.         public int score;
  13.     }
  14.  
  15.     private Dictionary<string, playerDataStruct> playerDataDictionary = new Dictionary<string, playerDataStruct>();
  16.     private playerDataStruct tempPlayerDataStruct = new playerDataStruct();
  17.  
  18.     void Start ()
  19.     {
  20.         tempPlayerDataStruct.nickName = "Warrior";
  21.         tempPlayerDataStruct.age = 18;
  22.         tempPlayerDataStruct.score = 1000;
  23.  
  24.         playerDataDictionary.Add("John", tempPlayerDataStruct);
  25.  
  26.         tempPlayerDataStruct.nickName = "Fighter";
  27.         tempPlayerDataStruct.age = 20;
  28.         tempPlayerDataStruct.score = 2000;
  29.  
  30.         playerDataDictionary.Add("Peter", tempPlayerDataStruct);
  31.  
  32.         //----------------------------------------------------------------------------------------------------------------
  33.  
  34.         if (playerDataDictionary.Values.Any(x => x.age >= 20) == true)
  35.         {
  36.             Debug.Log("本遊戲有大於20歲以上的玩家。");
  37.         }
  38.  
  39.         Debug.Log("本遊戲內小於20歲的玩家第一人為:" + playerDataDictionary.Where(x => x.Value.age < 20).Select(x => x.Key).FirstOrDefault());
  40.     }
  41.  
  42.     void Update ()
  43.     {
  44.  
  45.     }
  46. }
一般比較常見的情況有:
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,裡面內容直接如下,可以直接複製貼上:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using System.Diagnostics;
  6. using System.Runtime.InteropServices;
  7.  
  8. public class VirtualKeyboardController : MonoBehaviour {
  9.  
  10.     void Start ()
  11.     {
  12.  
  13.     }
  14.  
  15.     void Update ()
  16.     {
  17.  
  18.     }
  19.  
  20.     public class VirtualKeyboard
  21.     {
  22.         [DllImport("user32")]
  23.         static extern IntPtr FindWindow(String sClassName, String sAppName);
  24.  
  25.         [DllImport("user32")]
  26.         static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
  27.  
  28.         private static Process _onScreenKeyboardProcess = null;
  29.  
  30.         //Show the touch keyboard (tabtip.exe).
  31.         public void ShowTouchKeyboard()
  32.         {
  33.             ExternalCall("C:\\Program Files\\Common Files\\Microsoft Shared\\ink\\tabtip.exe", null, false);
  34.             //ExternalCall("TABTIP", null, false);
  35.         }
  36.  
  37.         //Hide the touch keyboard (tabtip.exe).
  38.         public void HideTouchKeyboard()
  39.         {
  40.             uint WM_SYSCOMMAND = 274;
  41.             int SC_CLOSE = 61536;
  42.             IntPtr ptr = FindWindow("IPTip_Main_Window", null);
  43.             PostMessage(ptr, WM_SYSCOMMAND, SC_CLOSE, 0);
  44.         }
  45.  
  46.         //Show the on screen keyboard (osk.exe).
  47.         public void ShowOnScreenKeyboard()
  48.         {
  49.             //ExternalCall("C:\\Windows\\system32\\osk.exe", null, false);
  50.  
  51.             if (_onScreenKeyboardProcess == null || _onScreenKeyboardProcess.HasExited)
  52.                 _onScreenKeyboardProcess = ExternalCall("OSK", null, false);
  53.         }
  54.  
  55.         // Hide the on screen keyboard (osk.exe).
  56.         public void HideOnScreenKeyboard()
  57.         {
  58.             if (_onScreenKeyboardProcess != null && !_onScreenKeyboardProcess.HasExited)
  59.                 _onScreenKeyboardProcess.Kill();
  60.         }
  61.  
  62.         /// <summary>
  63.         /// Set size and location of the OSK.exe keyboard, via registry changes.  Messy, but only known method.
  64.         /// </summary>
  65.         /// <param name='rect'>
  66.         /// Rect.
  67.         /// </param>
  68.         public void RepositionOnScreenKeyboard(Rect rect)
  69.         {
  70.             ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowLeft /t REG_DWORD /d " + (int)rect.x + " /f", true);
  71.             ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowTop /t REG_DWORD /d " + (int)rect.y + " /f", true);
  72.             ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowWidth /t REG_DWORD /d " + (int)rect.width + " /f", true);
  73.             ExternalCall("REG", @"ADD HKCU\Software\Microsoft\Osk /v WindowHeight /t REG_DWORD /d " + (int)rect.height + " /f", true);
  74.         }
  75.  
  76.         private static Process ExternalCall(string filename, string arguments, bool hideWindow)
  77.         {
  78.             ProcessStartInfo startInfo = new ProcessStartInfo();
  79.             startInfo.FileName = filename;
  80.             startInfo.Arguments = arguments;
  81.  
  82.             // if just command, we do not want to see the console displayed
  83.             if (hideWindow)
  84.             {
  85.                 startInfo.RedirectStandardOutput = true;
  86.                 startInfo.RedirectStandardError = true;
  87.                 startInfo.UseShellExecute = false;
  88.                 startInfo.CreateNoWindow = true;
  89.             }
  90.  
  91.             Process process = new Process();
  92.             process.StartInfo = startInfo;
  93.             process.Start();
  94.  
  95.             return process;
  96.         }
  97.     }
  98. }
然後在其他地方來做呼叫或關閉,當然虛擬鍵盤呼叫出來後,也可以直接按鍵盤右上角的X來關閉:
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. public class XXX : MonoBehaviour {
  6.  
  7.     void Start ()
  8.     {
  9.         VirtualKeyboardController.VirtualKeyboard keyboard = new VirtualKeyboardController.VirtualKeyboard();
  10.  
  11.         keyboard.ShowOnScreenKeyboard();
  12.  
  13.         keyboard.HideOnScreenKeyboard();
  14.     }
  15.  
  16.     void Update ()
  17.     {
  18.  
  19.     }
  20. }
這樣就可以呼叫和關閉螢幕虛擬鍵盤了,網路上有人說這招在Win 10後就沒辦法叫了,因為Microsoft不再放出虛擬鍵盤的控制權,我自己在Win 10使用是無礙啦......