最近有一个需求,是分享图片。之前做过一次类似的功能,但是使用setPixels来做的。在这里由于需要动态设置的信息过多,不便于使用此方法,此方法在文章最后列出。
1 通过camera渲染
大概是 玩家在游戏中达成某些要求后会解锁成就,游戏内部已经有成就的显示界面,需要把成就的信息分享出去。但是问题主要在于分享出去的图并非游戏内显示的图,并且尺寸不是游戏内设置的分辨率。而且由于在PC、安卓和IOS等各个平台不同以及市面上各种机型分辨率的原因 ,导致分享出去的图片不是有多余空白部分 就是显示不完全只发送出了一部分。
最后解决成功,先说下思路。
由于需要有分享功能的界面在游戏内的很多场景都要用到,所以在游戏内新建了一个canvas和用于渲染他的camera 并在游戏过程中让他保持不销毁,设置相机渲染层级为最低,并将canvas scaler设置如下
将需要分享的信息先按照规则显示在此隐藏的canvas上,在此canvas上得到要分享出的图片,再将图片保存到本地并进行分享。
然后是具体实现的代码
首先要的得到运行设备的分辨率,与正常显示的分辨率相比的缩放比例 m_multiplyNum 防止出现在720P、全面屏 或者宽屏手机上出现截取的图片出现上面的问题
float m_multiplyNum = 1;
void Awake()
{
float standardPhoneNum = 1080f / 1920f; //16:9 尺寸
float phoneNum = (float)Screen.width / (float)Screen.height;
m_canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
//宽屏,pad等
if (phoneNum > standardPhoneNum)
{
m_multiplyNum = m_camera.pixelHeight/ 1920f;
}
else //全面屏 mix p20 iphonex等
{
m_multiplyNum = m_camera.pixelWidth / 1080f;
}
}
在拿到要显示的信息并在你的canvas内对要现实的信息处理完成之后 执行以下代码
这里我是用背板image作为父物体,可以根据自己的实际需求修改
public Camera m_camera;
private RenderTexture m_renderTexture;
//ima为要分享的图片,分享时要保证此canvas下只打开要分享的相关物体
private string StartRender(Image ima)
{
m_camera.enabled = true;
//分局平台的不同设置分享图片显示的位置
//pc或者iphone的起始点位于坐上
#if UNITY_EDITOR || UNITY_IPHONE
ima.GetComponent().SetPivot(PivotPresets.TopLeft);
ima.GetComponent().SetAnchor(AnchorPresets.TopLeft, 0, 0);
#elif UNITY_ANDROID //安卓位于左下
ima.GetComponent().SetPivot(PivotPresets.BottomLeft);
ima.GetComponent().SetAnchor(AnchorPresets.BottomLeft, 0, 0);
#endif
Rect rect = ima.GetComponent().rect;
rect.width = rect.width ;
rect.height = rect.height;
//首次执行创建renderTexture,大小为相机渲染出的canvas的像素,并设置相机targetTexture
if (m_renderTexture == null)
{
m_renderTexture = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 24);
m_renderTexture.Create();
m_camera.targetTexture = m_renderTexture;
}
m_camera.Render();
RenderTexture.active = m_renderTexture;
//创建要分享出的texture2d 大小为ima的rect的宽、高乘之前计算的缩放比例
Texture2D t = new Texture2D((int)(rect.width * m_multiplyNum), (int)(rect.height * m_multiplyNum), TextureFormat.ARGB32, true);
//读取renderTexture内贴图大小的像素内保存在贴图内
t.ReadPixels(new Rect(0, 0, t.width, t.height), 0, 0);
//图片的名称,命名为分享的时间
string name = System.DateTime.Now.ToString().Replace("/", "_");
name = name.Replace(" ", "_");
name = name.Replace(":", "_");
//获取图片保存的路径
string path = Application.persistentDataPath + "/sharePic/" + name + ".png";
string directoryPath = Application.persistentDataPath + "/sharePic";
//如果 com.xxx.xxx 下不存在 sharePic路径 创建该文件夹
if (!Directory.Exists(directoryPath))
{
System.IO.Directory.CreateDirectory(directoryPath);
}
if (!File.Exists(path))
{
var bytes = t.EncodeToPNG();
File.WriteAllBytes(path, bytes);
}
#if !UNITY_EDITOR
//防止全面屏手机穿透看到 如果不关闭会导致屏幕最下方出现此图片
m_camera.enabled = false;
#endif
//返回保存图片的路径
return path;
}
代码比较简单并且注释写的比较详细,就不再过多解释了 主要麻烦在适配的测试上
如果在使用过程中适配遇到了问题,可以留言遇到问题的平台 设备 以及对应型号的屏幕分辨率
图上的代码设置UGUI锚点的点这里 代码设置UGUI锚点
2 SetPixels
这个方法是之前做的一个 简单的功能,只是单纯用来在给固定图片在固定位置添加游戏水印和二维码
代码如下:
public enum WaterMarkConrner
{
uperLeft,
uperRight,
lowerLeft,
lowerRight
}
///
///
/// 背景图
/// 需要覆盖上的图
/// 添加的位置
/// dis 为图片的对应conrner 与 conrner的距离 若WaterMarkConrner 选的是lowerright
则是地板图的右下角距离覆盖图的右下角的像素距离
///
public static Texture2D TextureOverlay(Texture2D backTextrue, Texture2D upTeture, WaterMarkConrner w, Vector2 dis)
{
Color[] colors = upTeture.GetPixels();
int startX = 0, starty = 0;
switch (w)
{
case WaterMarkConrner.lowerLeft:
startX = 0 + (int)dis.x; starty = 0 + (int)dis.y;
break;
case WaterMarkConrner.lowerRight:
startX = backTextrue.width - upTeture.width - (int)dis.x; starty = 0 + (int)dis.y;
break;
case WaterMarkConrner.uperLeft:
startX = 0 + (int)dis.x; starty = backTextrue.height - upTeture.height - (int)dis.y;
break;
case WaterMarkConrner.uperRight:
startX = backTextrue.width - upTeture.width - (int)dis.x; starty = backTextrue.height - upTeture.height - (int)dis.y;
break;
}
for (int i = 0; i < upTeture.width; i++)
{
for (int j = 0; j < upTeture.height; j++)
{
int x = i + startX, y = j + starty;
if (upTeture.GetPixel(i, j).a == 0)
{
Color c = backTextrue.GetPixel(x, y);
colors[upTeture.width * j + i] = c;
}
// 透明通道添加
else if (upTeture.GetPixel(i, j).a < 0.5)
{
Color c = backTextrue.GetPixel(x, y);
colors[upTeture.width * j + i] = (c * c.a + upTeture.GetPixel(i, j) * (1 - (upTeture.GetPixel(i, j).a))) / 2;
}
}
}
backTextrue.SetPixels(startX, starty, upTeture.width, upTeture.height, colors);
backTextrue.Apply();
return backTextrue;
}