实现效果:




最终搭建场景如下:

通过ScriptableObject做一个配置文件,里面存储每日签到的奖励类型和数量。创建代码命名为MonthSignAwardData,继承修改为ScriptableObject,并给其添加CreateAssetMenu编辑器拓展。
完整代码如下:
using System;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "MonthSignAwardData", menuName = "GameData/MonthSignAwardData", order = 1)]
public class MonthSignAwardData : ScriptableObject
{
public List<AwardItem> awardDataList = new List<AwardItem>();
}
[Serializable]
public class AwardItem
{
///
/// 奖励类型 0:金币,1:钻石
///
public CurrencyType awardType;
// 奖励数量
public int awardCount;
}
创建完成此代码就可以在文件中右键 --> Create --> GameData --> MonthSignAwardData。创建出一个MonthSignAwardData配置文件,然后手动编辑一下奖励内容就可以了。

PS:我这里为了方便演示后面加载,将配置文件直接存储到Resources文件夹下了。
逻辑也比较简单,通过PlayerPrefs存储一个上次签到日期,存储一个上传签到索引。然后通过上次签到日期和本地日期比较判断今日是否已签到。
完整逻辑如下:
using System;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
public class MonthSignIn : MonoBehaviour
{
[Header("奖励图")]
[SerializeField] private Sprite[] Spr_AwardSprites;
[Header("模版")]
[SerializeField] private GameObject Obj_Item;
[Header("内容父物体")]
[SerializeField] private Transform Tran_Context;
[Header("关闭按钮")]
[SerializeField] private Button Btn_Close;
[Header("内容列表")]
[SerializeField] private List<SignItem> signList;
// 配置文件
private MonthSignAwardData _signAwardData;
// 上次签到索引
private int LastSignIndex
{
get { return PlayerPrefs.GetInt("CzhenyaGameDataLastSignIndex", 0); }
set { PlayerPrefs.SetInt("CzhenyaGameDataLastSignIndex", value); }
}
// 上次签到日期
private string LastSignDate
{
get { return PlayerPrefs.GetString("CzhenyaGameDataLastSignDate", "yy-MM-dd"); }
set { PlayerPrefs.SetString("CzhenyaGameDataLastSignDate", value); }
}
void Start()
{
Obj_Item.gameObject.SetActive(false);
_signAwardData = Resources.Load<MonthSignAwardData>("MonthSignAwardData");
signList = new List<SignItem>();
for (int i = 0; i < _signAwardData.awardDataList.Count; i++)
{
Transform itemTrans = Instantiate(Obj_Item, Tran_Context).transform;
itemTrans.gameObject.SetActive(true);
SignItem signItem = new SignItem();
signItem.Img_Context = itemTrans.GetChild(0).GetComponent<Image>();
signItem.Text_ContextNum = itemTrans.GetChild(1).GetComponent<Text>();
signItem.Btn_Receive = itemTrans.GetChild(2).GetComponent<Button>();
signItem.Obj_Mask = itemTrans.GetChild(3).gameObject;
signItem.Btn_Receive.onClick.AddListener(OnClickReceive);
int awardCount = _signAwardData.awardDataList[i].awardCount;
int spriteIndex = (int)_signAwardData.awardDataList[i].awardType;
signItem.Img_Context.sprite = Spr_AwardSprites[spriteIndex];
//signItem.Img_Context.SetNativeSize();
signItem.Text_ContextNum.text = "x" + awardCount;
signList.Add(signItem);
}
Btn_Close.onClick.AddListener(OnClickClose);
UpdateSignStatus();
}
///
/// 更新签到状态
///
void UpdateSignStatus()
{
for (int i = 0; i < signList.Count; i++)
{
signList[i].Btn_Receive.gameObject.SetActive(false);
signList[i].Obj_Mask.SetActive(i < LastSignIndex);
}
// 今天没签
if (!LastSignDate.Contains(DateTime.Today.ToString("MM/dd/yyyy")))
{
signList[LastSignIndex].Btn_Receive.gameObject.SetActive(true);
}
}
///
/// 点击签到 -- 领取签到奖励
///
void OnClickReceive()
{
int curIndex = LastSignIndex;
// todo... 恭喜获得
Debug.Log("当前领取id:" + curIndex);
if (LastSignIndex >= 27)
{
LastSignIndex = 0;
}
else
{
LastSignIndex++;
}
LastSignDate = DateTime.Today.ToString("MM/dd/yyyy");
UpdateSignStatus();
}
///
/// 关闭面板
///
void OnClickClose()
{
gameObject.SetActive(false);
}
}
// 签到内容
public class SignItem
{
public Image Img_Context;
public Text Text_ContextNum;
public Button Btn_Receive;
public GameObject Obj_Mask;
}
public enum CurrencyType
{
///
/// 金币
///
Gold,
///
/// 钻石
///
Gam,
}
将上面代码挂载到SignPanel,然后将公开变量挨个赋值:

将下面代码添加到MonthSignIn类中,即可实现开篇看到的入场动画效果:
private void OnEnable()
{
PlayDoAni();
}
// 播放进入动画
public void PlayDoAni()
{
StartCoroutine(DoAni());
}
// 入场动画
IEnumerator DoAni()
{
for (int i = 1; i < Tran_Context.childCount; i++)
{
Tran_Context.GetChild(i).gameObject.SetActive(false);
}
for (int i = 1; i < Tran_Context.childCount; i++)
{
Tran_Context.GetChild(i).gameObject.SetActive(true);
Tran_Context.GetChild(i).transform.localScale = Vector3.zero;
Tran_Context.GetChild(i).DOScale(Vector3.one, 0.2f).SetEase(Ease.OutBack);
if (i % 3 == 0)
{
yield return new WaitForSeconds(0.06f);
}
}
}
源码和步骤都在上面分享过了,若还有什么不明白的,可以点击链接下载,积分不够的童鞋关注下方卡片公号,回复:“月签到” 即可获得Demo源码~