Unity中SpriteRender实现广告牌效果

时间:2022-02-28 04:20:21

通过刷新修改SpriteRender组建来实现一个翻广告牌的效果,一般的资源原图最好是能两张或更多,实现起来就能类似广告牌那样展现,或者只有一张也可以单独作为一个显示图片的动画来用:
首先是游戏场景中对象的节点层次,这里的fatherObj分别为每张广告牌的父节点,有多少个广告牌就有多少个父节点,父节点的SpriteRender要直接引用图片资源,我以一张广告牌垂直分割为四张为例,如图为图片分割的效果示意图:
Unity中SpriteRender实现广告牌效果

需要做的:场景中的节点如图
Unity中SpriteRender实现广告牌效果

SpriteRender中引用到的广告牌格式:
Unity中SpriteRender实现广告牌效果

本文只贴一下比较关键的代码,既然翻广告牌的对象节点要求已经挫成这样了,代码结构也要继续挫下去,有点搞复杂了。我单独加了个类来存这样的结构:

public class AdCardItem
{
//对应途中的广告牌节点fatherObj
public GameObject father;
//存放广告牌碎片,对于fahterObj这个而言,需要把childObj1-childObj4都村进来,SubCardItem结构后面详写。
public List<SubCardItem> cardSubList = new List<SubCardItem>();
//维护多张图的时候才需要,其实单张图距用不到这个了,文本中暂不使用
public int order =0;
//多此一举了,其实是fatherObj的SpriteRender组件
public SpriteRender fatherRender;
public AdCardItem(GameObject father, List<SubCardItem> cardSubList,int order,SpriteRender fathRender)
{
this.father = father;
this.cardSubList = cardSubList;
this.order = order;
this.fathRender = fathRender;
}
}

//存储广告牌子块的结构
public class SubCardItem
{
public SpriteRender spriteRender;
/*这个字典其实是用来做缓存优化的,把广告牌的每张小图在不同比例时的显示都缓存了,其实也不会太多。即所有的广告牌翻过一次之后,所有的小碎片都能被缓存到,避免每次用小碎片都直接实例化的尴尬*/
public Dictionary<int,Sprite> sprDic;

//实例化的时候只需要传入碎片对象引用的SpriteRender组件
public SubCardItem(SpriteRender spriteRender1)
{
this.spriteRender = spriteRender1;
sprDic = new Dictionary<int,Sprite>();
}
}

接下来简单的写一行实例化的代码,以一张广告牌的为例:

public class AdCardUpdate:MonoBehaviour
{
List<AdCardItem> adCardList;
//暂定只有2张广告牌,对象示意图中的命名为场景的对象摆放分别为fatherObj0,fatherObj1
int CARD_COUNT = 2;
GameObject fathObj;//广告牌父节点
//假定大广告牌的总宽度为100
float SUB_WIDE = 100/4;
//此算法水平分割,因此所有子块和大图的显示高度一致,假定图片高100
float SUB_HEIGHT =50;

void Start()
{
adCardList = new List<AdCardItem>();
int subCount =4;//为碎片个数,此处的把广告牌分成了四块
for(int index = 0; index < CARD_COUNT; index ++)
{
List<SubCardItem> subList = new List<SubCardItem>();
for(int subIndex=0;subIndex<subCount;subIndex++)
{
string path = "fatherObj/"+"childObj"+(index+1).ToString();
if(GameObject.Find(path)!=null)
{
subList.Add(new
SubCardItem(GameObject.Find(path).GetComponent<SpriteRender>));
Transform father = GameObject.Find("fatherObj"+index);
if(father!=null)
{
adCardList.Add(new AdCardItem(father.gameObject,
subList,0,father.gameObject.GetComponet<SpriteRender>()));
}
}
}
}
}

//描述填充的百分比(%100后使用,当showRate=100表示碎片全部显示)
int showRate = 0;
int fullRate=100;
int forehandIndex
{
get
{
int temp = (bgIndex-1)<0?
(adCardList.Count-1):(bgIndex-1));
return temp<=0?0:temp;
}
set{}
}
int bgIndex=0;

void Update()
{
showRate++;
UpdateCard(forehandIndx,shoRate);

if(showRate>fullRate)
{
showRate =0;
//该翻下一张广告牌了
bgIndex++;
if(bgIndex>=adCardList.Count)
{
bgIndex=0;
}
//完全翻牌子了
ResetLayer();
}

}

///以下是刷新某张广告牌的详细实现
///其中,adIndex为adCardList中前景图的索引(慢慢消失的那张广告牌),后面的(bgIndex位置)慢慢全部显示出来
void UpdateCard(int adIndex,float scale,bool needReturn=false)
{
if(needRetunr) //希望传入scale=0,即前景图全不显示直接隐藏该广告牌父节点
{
adCardList[adIndex].father.SetActive(false);
return;
}
Rect fathRect =adCardList[adIndex].fathRender.sprite.textureRect;
Texture2D texture2D = adCardList[adIndex].fatherRender.sprite.texture;
float leftX = fathRect.x;
float topY = fathRect.y;
flot curWide = SUB_WIDE*scale*0.01f;//实际显示的碎片宽度
int scaleIndex=(int)scale;
for(int index=0;index<adCardList[adIndex].cardSubList.Count;index++)
{
SpriteRender render = adCardList[adIndex].cardSubList[index].spriteRender;
//尚未缓存的
if(!adCardList[adIndex].cardSubList[index].sprDic.ContainsKey(scaleInt))
{
//最关键的两行
Rect rect = new Rect(index*SUB_WIDE+leftX,topY,curWide,SUB_HEIGHT);
render.sprite=Sprite.Create(texture2D,rect,Vector2.zero);
adCardList[adIndex].cardSubList[index].sprDic.Add(scaleInt,render.sprite);
}
else
{
render.sprite =adCardList[adIndex].cardSubList[index].sprDic[scaleInt];
}
}

///每次翻了新的广告牌,重置层次
void ResetLayer()
{
adCardListp[bgIndex].father.SetActive(true);
adCardListp[bgIndex].father.transform.localPosition=
new Vector3(0,0,-0.1);

adCardListp[forehandIndex].father.SetActive(true);
adCardListp[forehandIndex].father.transform.localPosition=
new Vector3(0,0,-0.12); //被翻的牌子forehandIndex放到前面

//前后图片都按100%比例填充
UpdateCard(forehandIndex,fullRate);
UpdateCard(bIndex,fullRate);
}
}
}

其实,这个实现只是一个思路,纯手打,不能保证不报错,部分空对象报错的保护代码没有加上,实际运行中某些对报错的时候还是需要额外加限制保护的

最后附上一张2个fatherObj(即只有两个广告牌,就是后面的广告慢慢显示出来,前面的慢慢完全消失:
Unity中SpriteRender实现广告牌效果