在Unity3D中加载外部图片的两种方法

2015年10月14日 13:34 0 点赞 0 评论 更新于 2025-11-21 19:11

作者:秦元培 博客地址:http://qinyuanpei.com 文章出处:http://blog.csdn.net/qinyuanpei/article/details/48262583

各位朋友大家好,我是秦元培,欢迎大家关注我的博客。最近在项目开发过程中遇到一个需求:玩家在游戏过程中可以实时存档,存档时会保存当前游戏进度,同时截取当前游戏画面并加载到游戏存档界面。下次进入游戏时,读取本地存档图片并加载到游戏界面。这在单机游戏中是常见功能,主要有两个关键点:

  1. 截取游戏画面:大家可以在《Unity3D游戏开发之截屏保存精彩瞬间》这篇文章中找到答案。
  2. 从本地加载图片:由于要保证可读可写,传统的Resources.Load()方式和AssetBundle方式均无法实现该功能。

那么,如何从外部加载图片到游戏中呢?本文将介绍两种实现方法。

喜闻乐见的WWW方式

WWW方式是我们比较熟悉的一种方式。通过WWW可以从网络上加载文本、图片、音频等内容,同时它也支持加载本地外部(相对于应用程序)资源,因为WWW支持httpfile两种协议。我们通常接触的WWW默认是指http协议,下面重点介绍file协议,该协议可用于访问本地资源(使用绝对路径)。

例如,若要加载文件D:\TestFile\pic001.png,对应的C#脚本如下:

//请求WWW
WWW www = new WWW("file://D:\\TestFile\\pic001.png");
yield return www;
if(www != null && string.IsNullOrEmpty(www.error))
{
//获取Texture
Texture texture = www.texture;
//更多操作...
}

需要注意的是,代码中出现了yield return结构,这表示使用了协程。因此,在项目中需要使用StartCoroutine等协程相关方法来调用这些协程。虽然在Unity3D中使用协程较为简单,但如果随意使用协程而不注意维护,原本简单的代码可能会带来很多问题。

亘古不变的传统IO方式

下面介绍传统的IO方式。既然是传统IO方式,主要就是各种IO流的处理。以下是具体代码:

//创建文件读取流
FileStream fileStream = new FileStream(screen, FileMode.Open, FileAccess.Read);
fileStream.Seek(0, SeekOrigin.Begin);
//创建文件长度缓冲区
byte[] bytes = new byte[fileStream.Length];
//读取文件
fileStream.Read(bytes, 0, (int)fileStream.Length);
//释放文件读取流
fileStream.Close();
fileStream.Dispose();
fileStream = null;

//创建Texture
int width = 800;
int height = 640;
Texture2D texture = new Texture2D(width, height);
texture.LoadImage(bytes);

使用这种方式读取图片文件时,主要是将图片文件转化为byte[]数组,再利用Texture2DLoadImage方法将其转化为Unity3D中的Texture2D。这种方法在创建过程中需要传入图片的大小,上述代码创建了一张800X640的图片。经过研究发现,这种方式加载外部图片相对于使用WWW加载外部图片效率更高,因此推荐大家在遇到类似需求时使用这种方式。

将外部图片加载到游戏界面

解决了从外部加载图片到Unity3D的问题后,我们还需要将读取到的图片加载到游戏界面。例如,当使用UGUI时,UGUI中的Image控件需要一个Sprite作为填充内容,这就需要将Texture转化为Sprite。以下是一个简单的例子:

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

public class TestLoading : MonoBehaviour
{
/// <summary>
/// Image控件
/// </summary>
private Image image;

void Start ()
{
image = this.transform.Find("Image").GetComponent<Image>();

//为不同的按钮绑定不同的事件
this.transform.Find("LoadByWWW").GetComponent<Button>().onClick.AddListener
(
delegate(){LoadByWWW();}
);

this.transform.Find("LoadByIO").GetComponent<Button>().onClick.AddListener
(
delegate(){LoadByIO();}
);
}

/// <summary>
/// 以IO方式进行加载
/// </summary>
private void LoadByIO()
{
double startTime = (double)Time.time;
//创建文件读取流
FileStream fileStream = new FileStream("D:\\test.jpg", FileMode.Open, FileAccess.Read);
fileStream.Seek(0, SeekOrigin.Begin);
//创建文件长度缓冲区
byte[] bytes = new byte[fileStream.Length];
//读取文件
fileStream.Read(bytes, 0, (int)fileStream.Length);
//释放文件读取流
fileStream.Close();
fileStream.Dispose();
fileStream = null;

//创建Texture
int width = 300;
int height = 372;
Texture2D texture = new Texture2D(width, height);
texture.LoadImage(bytes);

//创建Sprite
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
image.sprite = sprite;

startTime = (double)Time.time - startTime;
Debug.Log("IO加载用时:" + startTime);
}

/// <summary>
/// 以WWW方式进行加载
/// </summary>
private void LoadByWWW()
{
StartCoroutine(Load());
}

IEnumerator Load()
{
double startTime = (double)Time.time;
//请求WWW
WWW www = new WWW("file://D:\\test.jpg");
yield return www;
if(www != null && string.IsNullOrEmpty(www.error))
{
//获取Texture
Texture2D texture = www.texture;

//创建Sprite
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
image.sprite = sprite;

startTime = (double)Time.time - startTime;
Debug.Log("WWW加载用时:" + startTime);
}
}
}

运行程序后可以发现,两种方式都能成功加载图片。为了对比两种方式的执行效率,在脚本中加入了相关代码。通过对比发现,使用IO方式加载一张227k的图片用时为0s,而使用WWW方式加载用时为0.0185s。因此,传统的IO方式效率更高,建议大家在遇到这类问题时优先使用这种方式。

好了,今天的内容就介绍到这里。

作者信息

洞悉

洞悉

共发布了 3994 篇文章