您的位置:首页 > 移动开发 > Unity3D

Unity3D截取界面任意位置生成图片并保存

2013-03-04 10:57 274 查看
从事Unity3D开发工作有小一年了吧。这一年里积累了很多开发的经验,做了很多有用的和没有用的东西。

今天开了这个博客,希望能把这些东西慢慢记录下来。

一来可以当做自己的学习笔记见证自己的成长,二来可以和广大的开发工作者一起交流,学习经验。

这次研究的是一个屏幕截取并保存的功能。先上图。

1.Ctrl + Alt + X 开始截图。





2.Enter键执行截屏并弹出保存框。





3.截取后保存的图片。






大致的思路是这样:

1.GL类执行界面选框的显示。

2.用Texture2D类设置新的像素块颜色。

3.System.Windows.forms.dll弹出保存对话框,保存图片文件并打开。

难点在于GL框的绘制逻辑以及新的图片生成时屏幕坐标的计算。

代码如下:

ScreenShot类

using UnityEngine;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using System.Text;

//截屏工具
public class ScreenShot : MonoBehaviour
{
//选框材质
public Material mat;
//初始位置
Vector3 startPos = Vector3.zero;
//结束位置
Vector3 endPos = Vector3.zero;
//是否绘制
bool isDraw = false;
//是否清除
bool isClear = true;

void Update ()
{
//左键按下开始绘制
if(Input.GetMouseButtonDown(0))
{
isDraw = true;
isClear = true;
startPos = Input.mousePosition;
}
//左键松开GL框悬停
if(Input.GetMouseButtonUp(0))
{
//鼠标原位置点击不做GL绘制
if(endPos == startPos)
{
isDraw = false;
}
isClear = false;
}
//右键按下重新绘制
if(Input.GetMouseButtonDown(1))
{
isDraw = false;
enabled = false;
}
//Enter键按下执行截屏
if(Input.GetKeyDown(KeyCode.Return) && isDraw && !isClear)
{
isDraw = false;
StartCoroutine(DoScreenShot(new Vector2(startPos.x,UnityEngine.Screen.height - startPos.y),new Vector2(endPos.x,UnityEngine.Screen.height - endPos.y)));
enabled = false;
}
}

void OnGUI ()
{
//文本提示
if(!isDraw)
{
GUI.Label(new Rect(Input.mousePosition.x,UnityEngine.Screen.height - Input.mousePosition.y,100,100),"    请框选截图范围");
}
if(isDraw && !isClear && endPos != startPos)
{
GUI.Label(new Rect(Input.mousePosition.x,UnityEngine.Screen.height - Input.mousePosition.y,100,100),"    按下Enter键截屏");
}
}

void OnPostRender ()
{
//绘制GL线框
if(!isDraw)return;
if(isClear)
endPos = Input.mousePosition;

GL.PushMatrix();
if(!mat)return;
mat.SetPass(0);
GL.LoadPixelMatrix();
//绘制GL填充面
GL.Begin(GL.QUADS);
GL.Color(new  Color(Color.yellow.r,Color.yellow.g,Color.yellow.b,0.1f));
GL.Vertex3(startPos.x,startPos.y,0);
GL.Vertex3(endPos.x,startPos.y,0);
GL.Vertex3(endPos.x,endPos.y,0);
GL.Vertex3(startPos.x,endPos.y,0);
GL.End();
//绘制GL边框线
GL.Begin(GL.LINES);
GL.Color(Color.red);
GL.Vertex3(startPos.x,startPos.y,0);
GL.Vertex3(endPos.x,startPos.y,0);
GL.Vertex3(endPos.x,startPos.y,0);
GL.Vertex3(endPos.x,endPos.y,0);
GL.Vertex3(endPos.x,endPos.y,0);
GL.Vertex3(startPos.x,endPos.y,0);
GL.Vertex3(startPos.x,endPos.y,0);
GL.Vertex3(startPos.x,startPos.y,0);
GL.End();
GL.PopMatrix();
}

//截屏
IEnumerator DoScreenShot (Vector2 startPos,Vector2 endPos)
{
Debug.Log(new Rect(startPos.x < endPos.x ? startPos.x : endPos.x,startPos.y < endPos.y ? UnityEngine.Screen.height - endPos.y : UnityEngine.Screen.height - startPos.y,Mathf.Abs(endPos.x - startPos.x - 2),Mathf.Abs(endPos.y - startPos.y - 2)));
yield return new WaitForSeconds(1);
Texture2D tex = new Texture2D((int)Mathf.Abs(endPos.x - startPos.x - 2),(int)Mathf.Abs(endPos.y - startPos.y - 2),TextureFormat.RGB24,false);
tex.ReadPixels (new Rect(startPos.x < endPos.x ? startPos.x : endPos.x,startPos.y < endPos.y ? UnityEngine.Screen.height - endPos.y : UnityEngine.Screen.height - startPos.y,Mathf.Abs(endPos.x - startPos.x - 2),Mathf.Abs(endPos.y - startPos.y - 2)),0,0);
tex.Apply();
Destroy(tex);
SaveScreenShot(tex);
}

//保存文件
void SaveScreenShot (Texture2D tex)
{
//先将文件保存在工程目录下(其实可以不用这么做)
byte[] bytes = tex.EncodeToPNG();
string filename = System.DateTime.Now.Year.ToString("0000") + System.DateTime.Now.Month.ToString("00") + System.DateTime.Now.Day.ToString("00") + System.DateTime.Now.Hour.ToString("00") + System.DateTime.Now.Minute.ToString("00") + System.DateTime.Now.Second.ToString("00") + Random.Range(0,9999).ToString("0000");
string oldFilePath = UnityEngine.Application.dataPath + "/" + filename + ".png";
FileStream f =new FileStream(oldFilePath,FileMode.Create);
f.Write(bytes,0,bytes.Length);
f.Flush();
f.Close();
System.GC.Collect();
Resources.UnloadUnusedAssets();

//弹出文件保存对话框,调用的是WINDOWS的DLL.
SaveFileDialog dialog = new SaveFileDialog();
dialog.Filter = "png|*.png";
string newFilePath = "";
if (dialog.ShowDialog() == DialogResult.OK)
{
newFilePath = dialog.FileName;
}
if(newFilePath != "")
{
if(File.Exists(newFilePath))
{
File.Delete(newFilePath);
}
File.Move(oldFilePath, newFilePath);
UnityEngine.Application.OpenURL(newFilePath);
}
else
{
File.Delete(oldFilePath);
}
}
}


ScreenShotControl类

using UnityEngine;
using System.Collections;

//截屏控制
public class ScreenShotControl : MonoBehaviour
{
ScreenShot screenshot;

// Use this for initialization
void Start ()
{
screenshot = GetComponent<ScreenShot>();
}

// Update is called once per frame
void Update ()
{

}

void OnGUI ()
{
//Ctrl + Alt + X 执行截屏
if(Event.current.Equals(Event.KeyboardEvent("^&X")))
{
screenshot.enabled = true;
}
}
}


阐述几个开发中的问题:

1.调用Windows的DLL,需要在Unity里更改一下设置。

File -> Build Settings -> Player Settings ->Other Settings -> Optimization -> Api Compatibility Level 更改为 .NET 2.0。



2.在执行完截屏操作后,虽然能够成功生成图片,但是会报一个错。


ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame.

如果有高手知道具体的解决办法,请指教一下。

3.程序的代码结构写的不是太好,请高手指正。

工程包地址:http://www.vdisk.cn/down/index/12475143
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐