Unity中SpriteRender实现广告牌效果

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

需要做的:场景中的节点如图
游戏对象节点层次

SpriteRender中引用到的广告牌格式:
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(即只有两个广告牌,就是后面的广告慢慢显示出来,前面的慢慢完全消失:
单张广告牌gif动画

UnitySpriteRenderer组件并不支持直接添加点击事件,因此你需要将SpriteRenderer嵌套在一个支持点击事件的对象,例如UI Button或Collider。 以下是让SpriteRenderer对象支持点击事件的一种方法: 1. 将SpriteRenderer嵌套在一个UI Button对象。 2. 为UI Button添加一个OnClick事件。 3. 在OnClick事件编写处理SpriteRenderer点击事件的代码。 下面是一个示例代码,演示如何在UnitySpriteRenderer对象添加点击事件: ``` using UnityEngine; using UnityEngine.UI; public class ClickableSpriteRenderer : MonoBehaviour { public SpriteRenderer spriteRenderer; private void Start() { var button = GetComponent<Button>(); if (button != null) { button.onClick.AddListener(OnButtonClick); } } private void OnButtonClick() { RaycastHit hit; if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) { if (hit.collider.gameObject == spriteRenderer.gameObject) { Debug.Log("SpriteRenderer clicked!"); } } } } ``` 在这个示例,我们首先将SpriteRenderer对象嵌套在了一个UI Button对象,然后编写了一个ClickableSpriteRenderer脚本组件,将其添加到UI Button对象上。在Start方法,我们获取到Button组件并为其添加OnClick事件的监听器。在OnClick事件,我们使用Physics.Raycast方法来检测鼠标点击位置是否与SpriteRenderer对象相交,并在点击SpriteRenderer时输出一条消息。 请注意,在这个示例我们使用了Physics.Raycast方法来检测鼠标点击位置,因此我们需要在场景添加一个Collider组件以确保点击事件被正确检测到。你也可以使用其他方法来检测点击事件,例如使用碰撞器(Collider)或射线(Ray)等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值