1. 程式人生 > >Unity截圖

Unity截圖

如何將區域性螢幕儲存為PNG格式的圖片。我這裡的實現方式是:使用一張2D的紋理貼圖,通過該貼圖讀取螢幕的畫素資訊並將之轉換為PNG格式圖片。就一句話的原理,估計有的讀者就範暈了,不知道我在講什麼,不過,我下邊通過程式碼的講解就”柳暗花明”了,哈哈。

既然是使用一張2D紋理, 那麼是不是需要創建出一張2D紋理,使用Texture2D類的建構函式就可以,該建構函式原型是static function Texture2D (width : int, height : int, format : TextureFormat, mipmap : bool) : Texture2D ; 
可以看到通過該建構函式可以指定該2d貼圖的寬,號,格式等,具體的含義有興趣的讀者可以翻閱unity指令碼手冊,這裡不過多闡述,因為一則比較簡單,二則這不是我們的重點。

那麼好了,2D貼圖有了,我們就可以讀取螢幕的區域性畫素資訊儲存在該貼圖中了,我們可以使用Texture2D 類中的ReadPixels來獲取,該函式的原型是: 
function ReadPixels (source : Rect, destX : int, destY : int, recalculateMipMaps : bool = true) : void 
官方的解釋是: 
1.Read screen pixels into the saved texture data. 
2.This will copy a rectangular pixel area from the currently active RenderTexture or the view (specified by /source/) into the position defined by destX and destY. Both coordinates use pixel space - (0,0) is lower left 
Unity聖典中的翻譯是: 
1.讀取螢幕畫素資訊並存儲為紋理資料. 
2.這將從當前處於啟用狀態的 RenderTexture 或檢視(由/source/指定)複製一個由destX和destY指定的矩形畫素區域。這兩個座標使用畫素空間座標 (0,0)是螢幕左下角。

不知道你們有沒有看懂,反正我是沒明白什麼意思,2個點怎麼能指定一個矩形區域呢,著實想不明白,有木有同感。555~ 在此還求大神指導迷津.

不過文章還得繼續,我既然已經在寫這篇文章,說明,哥是胸有成足的哦…..我是我自己的哥,哈哈哈!!!

那我們繼續,引數不知道什麼意思,功能總知道的吧,沒錯,我們使用創建出來的2D紋理貼圖物件來呼叫ReadPixels()函式,可以知道:引數中所對應的矩形區域的畫素資訊就被寫入到該紋理貼圖物件中了。然後,我們如何將之儲存為PNG格式的圖片的呢?同樣Texture2D類還給我提供了一個將2D紋理轉換為PNG的方法,該方法的原型是: 
function EncodeToPNG () : byte[] 
可以看到,該方法返回的是一個位元組陣列,也就是將我們的紋理畫素資訊儲存在該位元組陣列中,到此還沒喲接觸,我們還必須將位元組陣列中的資訊入讀檔案,方法有很多可以實現,不過我這裡使用File類下的WriteAllBytes方法(注意,使用file類需要包含該類的空間,要使用using System.IO),簡單粗暴,封裝後的語言使用就是這麼方便,不要自己手動關閉資源。ok,到此,原理已經大體的講過了,其中就一個點沒有闡述,就是ReadPixel函式的引數。那,這就是我們下邊將要講述的事情.我們先上程式碼,在程式碼中我們一一試驗並解釋( 這樣,我在上程式碼前,先上一張我的場景執行畫面,其實就3個Cube和一個plane其他都是燈光) 


這是更個Game遊戲檢視的畫面,有點粗糙,不過,這不是我們的重點,只是當演示而已罷了 
OK, 再上程式碼

using UnityEngine;
using System.Collections;
using System.IO;

public class printScreen : MonoBehaviour
{

  private Texture2D screenShot;

  private bool shoot = false;

  void Start()
  {
//例項化一張你到透明通道的大小為256*256的貼圖
    screenShot = new Texture2D(256,256,TextureFormat.RGB24, false);

  }

  void Update()
  {
  //點選滑鼠左鍵的時候 截圖並儲存為png圖片
    if (Input.GetKeyUp(KeyCode.Mouse0))
    {
      StartCoroutine(CaptureScreenshot());
    }
  }

  void OnGUI()
  {
  //將截出的圖片以填充的方式繪製到螢幕中Rect(10,10,256,256)的區域內
    if (shoot)
    {
      GUI.DrawTexture(new Rect(10,10,256,256),screenShot,ScaleMode.StretchToFill);
    }
  }

  IEnumerator CaptureScreenshot()
  {
  //只在每一幀渲染完成後才讀取螢幕資訊
    yield return new WaitForEndOfFrame();

    //讀取螢幕畫素資訊並存儲為紋理資料
    screenShot.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
    screenShot.Apply();// 這一句必須有,畫素資訊並沒有儲存在2D紋理貼圖中

    //讀取將這些紋理資料,成一個png圖片檔案
    byte[] bytes = screenShot.EncodeToPNG();
    string filename = Application.dataPath +"/"+Time.time.ToString()+ ".png";

//寫入檔案 並且指定路徑,因為使用該函式寫入檔案,同名的檔案會被覆蓋,所以,在路徑中使用Time.time只是為了不出現同名檔案而已, 沒有什麼實際的意義,只是當作個測試 
    File.WriteAllBytes(filename, bytes);
    shoot = true;
  }
}


我們可以看到ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0),2D貼圖的大小為256*256,我們先運行遊戲,看看截出的圖片是樣的效果 
 
可以看到的是圖片的大小是256*256,是從Game試圖的左下角為(0,0)截圖大為Screen.width*Screen.height,而由於我們的紋理大小是256*256,所以,只要我們紋理大小足夠大,我們就是可以儲存足夠大的螢幕資訊,當然,這個我們待會在試驗。那麼,有的讀者可能說了,我如果不想要從Game檢視的左下角(0,0)出截圖在儲存為圖片呢,比如說想從螢幕中間的位置開始截圖256*256大小的螢幕,那怎麼辦呢?很好,我們可以修改ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0)中的Rect,OK,我們再來做試驗,將之改為ReadPixels(new Rect(Screen.width*0.5f,Screen.height*0.5f,256,256),0,0),然後,我們執行看看到底是不是這樣子的 
 
對比一下原圖,我這裡將原圖做了一下標記如圖 

這樣做了一下標記,不言而名了吧,這裡總結一下,ReadPixels函式中的第一個引數Rect型別的引數我個人理解代表的是:讀取的螢幕畫素矩形區域是『以Game檢視的左下角為原點的矩形區域』。 
ok,是不是有種豁然開朗的感覺,還沒完,感覺完了,但實際上真沒完,怎麼沒完呢,沒錯,就是後面兩個引數又代表什麼呢,兩個int型的引數,一個是destX,一個是destY,啥話不說,直接試驗,我們在上邊的基礎上修改ReadPixels的引數為ReadPixels(new Rect(0,0,Screen.width,Screen.height),10,10), 就是第二個和第三個引數改為10,執行,我們看看 
 
和上邊的圖片對比,可以看到,圖片大小依然是256*256,但是有了一個10*10的 空白,這就足以說明,destX和destY是在引數1即Rect所指定的區域上進行的偏移。

到此,基本上的引數已經講完,只有剩最後一個bool型別的引數,可以不用去管它按預設的就好了。