任务系统一半多,商城筛选功能,一些细节和免费包
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
public static class DailyTaskEventHub
|
||||
{
|
||||
public static void ReportLogin()
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.Login,
|
||||
amount = 1f
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportPlaySong()
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.PlaySong,
|
||||
amount = 1f
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportFullCombo()
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.FullCombo,
|
||||
amount = 1f
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportTotalScore(float totalScore)
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.TotalScore,
|
||||
amount = totalScore
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportUseItem(int amount)
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.UseItem,
|
||||
amount = amount
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportSpendCoins(int amount)
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.SpendCoins,
|
||||
amount = amount
|
||||
});
|
||||
}
|
||||
|
||||
public static void ReportGameDuration(float durationSeconds)
|
||||
{
|
||||
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.GameDuration,
|
||||
amount = durationSeconds
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14861f485792abe459d3724786b183f8
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Serializable]
|
||||
public class DailyTaskRuntimeEntry
|
||||
{
|
||||
public string taskID;
|
||||
public float progress;
|
||||
public bool isCompleted;
|
||||
public bool isClaimed;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DailyTaskSaveData
|
||||
{
|
||||
public string dateKey;
|
||||
public int refreshUsedCount;
|
||||
public List<DailyTaskRuntimeEntry> activeTasks = new List<DailyTaskRuntimeEntry>();
|
||||
public List<DailyTaskAccumulatedProgress> accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
|
||||
}
|
||||
|
||||
public struct DailyTaskEventData
|
||||
{
|
||||
public userTasksPool.TaskType taskType;
|
||||
public float amount;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DailyTaskAccumulatedProgress
|
||||
{
|
||||
public int taskType;
|
||||
public float progress;
|
||||
}
|
||||
|
||||
public sealed class DailyTaskViewData
|
||||
{
|
||||
public userTasksPool.TaskDefinition definition;
|
||||
public DailyTaskRuntimeEntry runtimeEntry;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03c6abfd01940964aa67c37ecf92af16
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
public static class DailyTaskSaveService
|
||||
{
|
||||
private static string SaveFilePath
|
||||
{
|
||||
get { return Path.Combine(Application.persistentDataPath, "daily_tasks.json"); }
|
||||
}
|
||||
|
||||
public static DailyTaskSaveData Load()
|
||||
{
|
||||
if (!File.Exists(SaveFilePath))
|
||||
{
|
||||
return new DailyTaskSaveData();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(SaveFilePath);
|
||||
var data = JsonUtility.FromJson<DailyTaskSaveData>(json);
|
||||
return data ?? new DailyTaskSaveData();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[DailyTaskSaveService] Load failed: {ex.Message}");
|
||||
return new DailyTaskSaveData();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(DailyTaskSaveData data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonUtility.ToJson(data, true);
|
||||
File.WriteAllText(SaveFilePath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[DailyTaskSaveService] Save failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abd7cb33db74f5f4096c58a5416f9b53
|
||||
@@ -0,0 +1,665 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public sealed class DailyTaskService : MonoBehaviour
|
||||
{
|
||||
public static DailyTaskService Instance { get; private set; }
|
||||
|
||||
public event Action OnTasksChanged;
|
||||
|
||||
private readonly Queue<DailyTaskEventData> pendingEvents = new Queue<DailyTaskEventData>();
|
||||
private readonly Dictionary<string, userTasksPool.TaskDefinition> definitionById = new Dictionary<string, userTasksPool.TaskDefinition>(StringComparer.Ordinal);
|
||||
|
||||
private DailyTaskSaveData saveData;
|
||||
private userTasksPool configuredTaskPool;
|
||||
private int configuredTaskMaxSize = 5;
|
||||
private int configuredRefreshMaxTimes = 3;
|
||||
private bool initialized;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
EnsureInstance();
|
||||
}
|
||||
|
||||
public static DailyTaskService EnsureInstance()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
var host = new GameObject("__daily_task_service");
|
||||
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
|
||||
DontDestroyOnLoad(host);
|
||||
Instance = host.AddComponent<DailyTaskService>();
|
||||
return Instance;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
InitializeStorageIfNeeded();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(userTasksPool taskPool, int taskMaxSize, int refreshMaxTimes)
|
||||
{
|
||||
configuredTaskPool = taskPool;
|
||||
configuredTaskMaxSize = Mathf.Max(1, taskMaxSize);
|
||||
configuredRefreshMaxTimes = Mathf.Max(0, refreshMaxTimes);
|
||||
RebuildDefinitionIndex();
|
||||
EnsureTodayTasks();
|
||||
FlushPendingEvents();
|
||||
NotifyTasksChanged();
|
||||
}
|
||||
|
||||
public IReadOnlyList<DailyTaskViewData> GetActiveTaskViews()
|
||||
{
|
||||
EnsureTodayTasks();
|
||||
|
||||
var result = new List<DailyTaskViewData>();
|
||||
if (saveData == null || saveData.activeTasks == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
for (int i = 0; i < saveData.activeTasks.Count; i++)
|
||||
{
|
||||
var runtimeEntry = saveData.activeTasks[i];
|
||||
if (runtimeEntry == null || string.IsNullOrEmpty(runtimeEntry.taskID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(new DailyTaskViewData
|
||||
{
|
||||
definition = definition,
|
||||
runtimeEntry = runtimeEntry
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ReportEvent(DailyTaskEventData eventData)
|
||||
{
|
||||
InitializeStorageIfNeeded();
|
||||
if (!IsConfigured())
|
||||
{
|
||||
pendingEvents.Enqueue(eventData);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureTodayTasks();
|
||||
float accumulatedIncrement = GetAccumulatedIncrement(eventData);
|
||||
if (accumulatedIncrement <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float accumulatedValue = AddAccumulatedProgress(eventData.taskType, accumulatedIncrement);
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0; i < saveData.activeTasks.Count; i++)
|
||||
{
|
||||
var runtimeEntry = saveData.activeTasks[i];
|
||||
if (runtimeEntry == null || runtimeEntry.isClaimed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsEventMatch(definition, eventData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float previousProgress = runtimeEntry.progress;
|
||||
bool previousCompleted = runtimeEntry.isCompleted;
|
||||
runtimeEntry.progress = GetInitialProgress(definition, accumulatedValue);
|
||||
runtimeEntry.isCompleted = runtimeEntry.progress >= Mathf.Max(0.0001f, definition.targetValue);
|
||||
|
||||
if (!Mathf.Approximately(previousProgress, runtimeEntry.progress) || previousCompleted != runtimeEntry.isCompleted)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Save();
|
||||
NotifyTasksChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRefreshTasks()
|
||||
{
|
||||
if (!IsConfigured())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureTodayTasks();
|
||||
if (saveData.refreshUsedCount >= configuredRefreshMaxTimes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
saveData.refreshUsedCount += 1;
|
||||
GenerateDailyTasks();
|
||||
Save();
|
||||
NotifyTasksChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetRemainingRefreshCount()
|
||||
{
|
||||
EnsureTodayTasks();
|
||||
return Mathf.Max(0, configuredRefreshMaxTimes - (saveData != null ? saveData.refreshUsedCount : 0));
|
||||
}
|
||||
|
||||
public bool TryClaimReward(string taskID, Player_SO playerData, out string failureMessage)
|
||||
{
|
||||
failureMessage = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(taskID))
|
||||
{
|
||||
failureMessage = "任务参数无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureTodayTasks();
|
||||
|
||||
DailyTaskRuntimeEntry runtimeEntry;
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!TryGetTaskPair(taskID, out runtimeEntry, out definition))
|
||||
{
|
||||
failureMessage = "未找到任务";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!runtimeEntry.isCompleted)
|
||||
{
|
||||
failureMessage = "任务尚未完成";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (runtimeEntry.isClaimed)
|
||||
{
|
||||
failureMessage = "奖励已领取";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGrantReward(definition, playerData, out failureMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
runtimeEntry.isClaimed = true;
|
||||
Save();
|
||||
NotifyTasksChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public int ClaimAllAvailableRewards(Player_SO playerData, out string failureMessage)
|
||||
{
|
||||
failureMessage = string.Empty;
|
||||
EnsureTodayTasks();
|
||||
|
||||
int claimedCount = 0;
|
||||
var taskIds = new List<string>();
|
||||
var views = GetActiveTaskViews();
|
||||
for (int i = 0; i < views.Count; i++)
|
||||
{
|
||||
var runtimeEntry = views[i].runtimeEntry;
|
||||
if (runtimeEntry == null || !runtimeEntry.isCompleted || runtimeEntry.isClaimed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
taskIds.Add(runtimeEntry.taskID);
|
||||
}
|
||||
|
||||
for (int i = 0; i < taskIds.Count; i++)
|
||||
{
|
||||
string claimError;
|
||||
if (!TryClaimReward(taskIds[i], playerData, out claimError))
|
||||
{
|
||||
if (claimedCount <= 0)
|
||||
{
|
||||
failureMessage = claimError;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
claimedCount += 1;
|
||||
}
|
||||
|
||||
if (claimedCount <= 0 && string.IsNullOrEmpty(failureMessage))
|
||||
{
|
||||
failureMessage = "暂无可领取奖励";
|
||||
}
|
||||
|
||||
return claimedCount;
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
if (string.Equals(scene.name, "UI_UI", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.Login,
|
||||
amount = 1f
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeStorageIfNeeded()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
saveData = DailyTaskSaveService.Load() ?? new DailyTaskSaveData();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void EnsureTodayTasks()
|
||||
{
|
||||
InitializeStorageIfNeeded();
|
||||
|
||||
if (!IsConfigured())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string todayKey = GetTodayKey();
|
||||
if (saveData == null)
|
||||
{
|
||||
saveData = new DailyTaskSaveData();
|
||||
}
|
||||
|
||||
bool needRegenerate = !string.Equals(saveData.dateKey, todayKey, StringComparison.Ordinal)
|
||||
|| saveData.activeTasks == null
|
||||
|| saveData.activeTasks.Count == 0;
|
||||
|
||||
if (!needRegenerate)
|
||||
{
|
||||
EnsureAccumulatedProgressCompatibility();
|
||||
return;
|
||||
}
|
||||
|
||||
saveData.dateKey = todayKey;
|
||||
saveData.refreshUsedCount = 0;
|
||||
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
|
||||
GenerateDailyTasks();
|
||||
Save();
|
||||
}
|
||||
|
||||
private void GenerateDailyTasks()
|
||||
{
|
||||
if (saveData == null)
|
||||
{
|
||||
saveData = new DailyTaskSaveData();
|
||||
}
|
||||
|
||||
saveData.activeTasks = new List<DailyTaskRuntimeEntry>();
|
||||
|
||||
var candidates = configuredTaskPool.tasks
|
||||
.Where(t => t != null && t.addToTaskPool && !string.IsNullOrWhiteSpace(t.taskID) && t.selectionWeight > 0)
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int pickCount = Mathf.Min(configuredTaskMaxSize, candidates.Count);
|
||||
var random = new System.Random(unchecked(GetTodayKey().GetHashCode() + saveData.refreshUsedCount * 397));
|
||||
var selectedDefinitions = new List<userTasksPool.TaskDefinition>();
|
||||
|
||||
while (selectedDefinitions.Count < pickCount && candidates.Count > 0)
|
||||
{
|
||||
int totalWeight = candidates.Sum(t => Mathf.Max(1, t.selectionWeight));
|
||||
int roll = random.Next(0, totalWeight);
|
||||
int cumulative = 0;
|
||||
int selectedIndex = 0;
|
||||
for (int i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
cumulative += Mathf.Max(1, candidates[i].selectionWeight);
|
||||
if (roll < cumulative)
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectedDefinitions.Add(candidates[selectedIndex]);
|
||||
candidates.RemoveAt(selectedIndex);
|
||||
}
|
||||
|
||||
for (int i = 0; i < selectedDefinitions.Count; i++)
|
||||
{
|
||||
var definition = selectedDefinitions[i];
|
||||
float currentProgress = GetInitialProgress(definition, GetAccumulatedProgress(definition.taskType));
|
||||
saveData.activeTasks.Add(new DailyTaskRuntimeEntry
|
||||
{
|
||||
taskID = definition.taskID,
|
||||
progress = currentProgress,
|
||||
isCompleted = currentProgress >= Mathf.Max(0.0001f, definition.targetValue),
|
||||
isClaimed = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsConfigured()
|
||||
{
|
||||
return configuredTaskPool != null && definitionById.Count > 0;
|
||||
}
|
||||
|
||||
private void RebuildDefinitionIndex()
|
||||
{
|
||||
definitionById.Clear();
|
||||
if (configuredTaskPool == null || configuredTaskPool.tasks == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < configuredTaskPool.tasks.Count; i++)
|
||||
{
|
||||
var definition = configuredTaskPool.tasks[i];
|
||||
if (definition == null || string.IsNullOrWhiteSpace(definition.taskID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!definitionById.ContainsKey(definition.taskID))
|
||||
{
|
||||
definitionById.Add(definition.taskID, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureAccumulatedProgressCompatibility()
|
||||
{
|
||||
if (saveData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (saveData.accumulatedProgress == null)
|
||||
{
|
||||
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
|
||||
}
|
||||
|
||||
if (saveData.accumulatedProgress.Count > 0 || saveData.activeTasks == null || saveData.activeTasks.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0; i < saveData.activeTasks.Count; i++)
|
||||
{
|
||||
var runtimeEntry = saveData.activeTasks[i];
|
||||
if (runtimeEntry == null || string.IsNullOrWhiteSpace(runtimeEntry.taskID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddAccumulatedProgress(definition.taskType, Mathf.Max(0f, runtimeEntry.progress));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushPendingEvents()
|
||||
{
|
||||
if (!IsConfigured())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (pendingEvents.Count > 0)
|
||||
{
|
||||
ReportEvent(pendingEvents.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
|
||||
{
|
||||
return definition != null && definition.taskType == eventData.taskType;
|
||||
}
|
||||
|
||||
private float AddAccumulatedProgress(userTasksPool.TaskType taskType, float amount)
|
||||
{
|
||||
if (saveData == null)
|
||||
{
|
||||
saveData = new DailyTaskSaveData();
|
||||
}
|
||||
|
||||
if (saveData.accumulatedProgress == null)
|
||||
{
|
||||
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
|
||||
}
|
||||
|
||||
int taskTypeValue = (int)taskType;
|
||||
for (int i = 0; i < saveData.accumulatedProgress.Count; i++)
|
||||
{
|
||||
var entry = saveData.accumulatedProgress[i];
|
||||
if (entry == null || entry.taskType != taskTypeValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.progress = Mathf.Max(0f, entry.progress + amount);
|
||||
return entry.progress;
|
||||
}
|
||||
|
||||
var newEntry = new DailyTaskAccumulatedProgress
|
||||
{
|
||||
taskType = taskTypeValue,
|
||||
progress = Mathf.Max(0f, amount)
|
||||
};
|
||||
saveData.accumulatedProgress.Add(newEntry);
|
||||
return newEntry.progress;
|
||||
}
|
||||
|
||||
private float GetAccumulatedProgress(userTasksPool.TaskType taskType)
|
||||
{
|
||||
if (saveData == null || saveData.accumulatedProgress == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
int taskTypeValue = (int)taskType;
|
||||
for (int i = 0; i < saveData.accumulatedProgress.Count; i++)
|
||||
{
|
||||
var entry = saveData.accumulatedProgress[i];
|
||||
if (entry == null || entry.taskType != taskTypeValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return Mathf.Max(0f, entry.progress);
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
private static float GetInitialProgress(userTasksPool.TaskDefinition definition, float accumulatedValue)
|
||||
{
|
||||
if (definition == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return Mathf.Min(Mathf.Max(0f, accumulatedValue), Mathf.Max(0.0001f, definition.targetValue));
|
||||
}
|
||||
|
||||
private bool TryGetTaskPair(string taskID, out DailyTaskRuntimeEntry runtimeEntry, out userTasksPool.TaskDefinition definition)
|
||||
{
|
||||
runtimeEntry = null;
|
||||
definition = null;
|
||||
|
||||
if (saveData == null || saveData.activeTasks == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < saveData.activeTasks.Count; i++)
|
||||
{
|
||||
var candidate = saveData.activeTasks[i];
|
||||
if (candidate == null || !string.Equals(candidate.taskID, taskID, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
userTasksPool.TaskDefinition matchedDefinition;
|
||||
if (!definitionById.TryGetValue(candidate.taskID, out matchedDefinition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
runtimeEntry = candidate;
|
||||
definition = matchedDefinition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGrantReward(userTasksPool.TaskDefinition definition, Player_SO playerData, out string failureMessage)
|
||||
{
|
||||
failureMessage = string.Empty;
|
||||
if (definition == null)
|
||||
{
|
||||
failureMessage = "任务定义无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (definition.rewardAmount <= 0)
|
||||
{
|
||||
failureMessage = "奖励数量无效";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (definition.rewardType)
|
||||
{
|
||||
case userTasksPool.RewardType.coins:
|
||||
{
|
||||
var wallet = PlayerEconomyLedger.EnsureInstance();
|
||||
if (playerData != null)
|
||||
{
|
||||
wallet.AttachPlayerData(playerData);
|
||||
}
|
||||
|
||||
wallet.AddCoins(definition.rewardAmount);
|
||||
return true;
|
||||
}
|
||||
case userTasksPool.RewardType.expBottle:
|
||||
{
|
||||
var bottleLedger = ExpBottleLedger.EnsureInstance();
|
||||
if (playerData != null)
|
||||
{
|
||||
bottleLedger.AttachPlayerData(playerData);
|
||||
}
|
||||
|
||||
bottleLedger.Add(definition.rewardExpBottleKind, definition.rewardAmount);
|
||||
return true;
|
||||
}
|
||||
case userTasksPool.RewardType.dushMaterial:
|
||||
{
|
||||
var materialLedger = DushMaterialLedger.EnsureInstance();
|
||||
if (playerData != null)
|
||||
{
|
||||
materialLedger.AttachPlayerData(playerData);
|
||||
}
|
||||
|
||||
materialLedger.Add(definition.rewardDushMaterialKind, definition.rewardAmount);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
failureMessage = "暂不支持该奖励类型";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetAccumulatedIncrement(DailyTaskEventData eventData)
|
||||
{
|
||||
switch (eventData.taskType)
|
||||
{
|
||||
case userTasksPool.TaskType.TotalScore:
|
||||
case userTasksPool.TaskType.SpendCoins:
|
||||
case userTasksPool.TaskType.GameDuration:
|
||||
return Mathf.Max(0f, eventData.amount);
|
||||
case userTasksPool.TaskType.Login:
|
||||
case userTasksPool.TaskType.PlaySong:
|
||||
case userTasksPool.TaskType.FullCombo:
|
||||
case userTasksPool.TaskType.UseItem:
|
||||
return Mathf.Max(1f, eventData.amount);
|
||||
default:
|
||||
return Mathf.Max(0f, eventData.amount);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetTodayKey()
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
private void Save()
|
||||
{
|
||||
DailyTaskSaveService.Save(saveData);
|
||||
}
|
||||
|
||||
private void NotifyTasksChanged()
|
||||
{
|
||||
if (OnTasksChanged != null)
|
||||
{
|
||||
OnTasksChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41ce491ff1399f340bd82a554499a880
|
||||
@@ -1,7 +1,6 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Bansonic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class dailyTaskManager : MonoBehaviour
|
||||
@@ -9,16 +8,21 @@ public class dailyTaskManager : MonoBehaviour
|
||||
[Header("任务池so")]
|
||||
public userTasksPool taskPool;
|
||||
|
||||
[Header("player data")]
|
||||
public Player_SO playerData;
|
||||
|
||||
[Header("今日进度")]
|
||||
public Slider todayProgressSlider;
|
||||
public Text todayProgressPercent;
|
||||
public Text todayProgressText;
|
||||
public Text notasksanymore;
|
||||
|
||||
|
||||
[Header("一键领取")]
|
||||
public Button oneKeyRewardBtn;
|
||||
|
||||
[Header("刷新任务")]
|
||||
public Button refreshTaskBtn;
|
||||
|
||||
[Header("任务配置")]
|
||||
[Tooltip("任务列表最大容量")]
|
||||
public int taskMaxSize = 5;
|
||||
@@ -28,4 +32,299 @@ public class dailyTaskManager : MonoBehaviour
|
||||
[Header("prefabs")]
|
||||
public GameObject dailyTaskItemPrefab;
|
||||
public Transform dailyTaskItemParent;
|
||||
|
||||
[Header("reward sprites")]
|
||||
public Sprite coinRewardSprite;
|
||||
|
||||
private readonly List<GameObject> spawnedTaskItems = new List<GameObject>();
|
||||
private readonly Dictionary<int, Sprite> rewardSpriteByItemId = new Dictionary<int, Sprite>();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
CacheRewardSpritesFromStoreItems();
|
||||
|
||||
var taskService = DailyTaskService.EnsureInstance();
|
||||
taskService.Configure(taskPool, taskMaxSize, refreshMaxTimes);
|
||||
taskService.OnTasksChanged += RefreshTaskUi;
|
||||
|
||||
if (oneKeyRewardBtn != null)
|
||||
{
|
||||
oneKeyRewardBtn.onClick.RemoveAllListeners();
|
||||
oneKeyRewardBtn.onClick.AddListener(OnOneKeyRewardClicked);
|
||||
}
|
||||
|
||||
if (refreshTaskBtn != null)
|
||||
{
|
||||
refreshTaskBtn.onClick.RemoveAllListeners();
|
||||
refreshTaskBtn.onClick.AddListener(OnRefreshTaskClicked);
|
||||
}
|
||||
|
||||
RefreshTaskUi();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (DailyTaskService.Instance != null)
|
||||
{
|
||||
DailyTaskService.Instance.OnTasksChanged -= RefreshTaskUi;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshTaskUi()
|
||||
{
|
||||
ClearSpawnedItems();
|
||||
|
||||
var tasks = DailyTaskService.EnsureInstance().GetActiveTaskViews();
|
||||
if (notasksanymore != null)
|
||||
{
|
||||
notasksanymore.gameObject.SetActive(tasks.Count == 0);
|
||||
notasksanymore.text = tasks.Count == 0 ? "今日暂无任务" : string.Empty;
|
||||
}
|
||||
|
||||
int completedCount = 0;
|
||||
for (int i = 0; i < tasks.Count; i++)
|
||||
{
|
||||
var taskView = tasks[i];
|
||||
if (taskView == null || taskView.definition == null || taskView.runtimeEntry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (taskView.runtimeEntry.isCompleted)
|
||||
{
|
||||
completedCount += 1;
|
||||
}
|
||||
|
||||
SpawnTaskItem(i, taskView);
|
||||
}
|
||||
|
||||
UpdateTodayProgress(tasks.Count, completedCount);
|
||||
RefreshOneKeyRewardButtonState(tasks);
|
||||
RefreshTaskButtonState();
|
||||
}
|
||||
|
||||
private void SpawnTaskItem(int index, DailyTaskViewData taskView)
|
||||
{
|
||||
if (dailyTaskItemPrefab == null || dailyTaskItemParent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var instance = Instantiate(dailyTaskItemPrefab, dailyTaskItemParent);
|
||||
spawnedTaskItems.Add(instance);
|
||||
|
||||
var controller = instance.GetComponent<taskPrefabController>();
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controller.Setup(taskView, index + 1, ResolveRewardSprite(taskView.definition), () => OnSingleRewardClicked(taskView.runtimeEntry.taskID));
|
||||
}
|
||||
|
||||
private void UpdateTodayProgress(int totalTaskCount, int completedCount)
|
||||
{
|
||||
float progress = totalTaskCount <= 0 ? 0f : (float)completedCount / totalTaskCount;
|
||||
|
||||
if (todayProgressSlider != null)
|
||||
{
|
||||
todayProgressSlider.value = progress;
|
||||
}
|
||||
|
||||
if (todayProgressPercent != null)
|
||||
{
|
||||
todayProgressPercent.text = (progress * 100f).ToString("F0") + "%";
|
||||
}
|
||||
|
||||
if (todayProgressText != null)
|
||||
{
|
||||
todayProgressText.text = completedCount + "/" + totalTaskCount;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSpawnedItems()
|
||||
{
|
||||
for (int i = spawnedTaskItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (spawnedTaskItems[i] != null)
|
||||
{
|
||||
Destroy(spawnedTaskItems[i]);
|
||||
}
|
||||
}
|
||||
|
||||
spawnedTaskItems.Clear();
|
||||
}
|
||||
|
||||
private void OnSingleRewardClicked(string taskID)
|
||||
{
|
||||
string failureMessage;
|
||||
if (!DailyTaskService.EnsureInstance().TryClaimReward(taskID, playerData, out failureMessage))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(failureMessage))
|
||||
{
|
||||
gNotice.warning.display(failureMessage);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshTaskUi();
|
||||
}
|
||||
|
||||
private void OnOneKeyRewardClicked()
|
||||
{
|
||||
string failureMessage;
|
||||
int claimedCount = DailyTaskService.EnsureInstance().ClaimAllAvailableRewards(playerData, out failureMessage);
|
||||
if (claimedCount <= 0)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(failureMessage))
|
||||
{
|
||||
gNotice.warning.display(failureMessage);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshTaskUi();
|
||||
}
|
||||
|
||||
private void RefreshOneKeyRewardButtonState(IReadOnlyList<DailyTaskViewData> tasks)
|
||||
{
|
||||
if (oneKeyRewardBtn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasClaimableReward = false;
|
||||
for (int i = 0; i < tasks.Count; i++)
|
||||
{
|
||||
var taskView = tasks[i];
|
||||
if (taskView == null || taskView.runtimeEntry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (taskView.runtimeEntry.isCompleted && !taskView.runtimeEntry.isClaimed)
|
||||
{
|
||||
hasClaimableReward = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
oneKeyRewardBtn.interactable = hasClaimableReward;
|
||||
}
|
||||
|
||||
private void OnRefreshTaskClicked()
|
||||
{
|
||||
if (taskPool == null)
|
||||
{
|
||||
gNotice.warning.display("未配置任务池");
|
||||
return;
|
||||
}
|
||||
|
||||
var taskService = DailyTaskService.EnsureInstance();
|
||||
if (!taskService.TryRefreshTasks())
|
||||
{
|
||||
if (taskService.GetRemainingRefreshCount() <= 0)
|
||||
{
|
||||
gNotice.warning.display("已达到今日刷新上限");
|
||||
}
|
||||
else
|
||||
{
|
||||
gNotice.warning.display("刷新失败");
|
||||
}
|
||||
|
||||
RefreshTaskButtonState();
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshTaskUi();
|
||||
}
|
||||
|
||||
private void RefreshTaskButtonState()
|
||||
{
|
||||
if (refreshTaskBtn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
refreshTaskBtn.interactable = taskPool != null && DailyTaskService.EnsureInstance().GetRemainingRefreshCount() > 0;
|
||||
}
|
||||
|
||||
private Sprite ResolveRewardSprite(userTasksPool.TaskDefinition definition)
|
||||
{
|
||||
if (definition == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (definition.rewardType)
|
||||
{
|
||||
case userTasksPool.RewardType.coins:
|
||||
return coinRewardSprite;
|
||||
case userTasksPool.RewardType.expBottle:
|
||||
return GetRewardSpriteByItemId(GetExpBottleRewardItemId(definition.rewardExpBottleKind));
|
||||
case userTasksPool.RewardType.dushMaterial:
|
||||
return GetRewardSpriteByItemId(GetDushMaterialRewardItemId(definition.rewardDushMaterialKind));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheRewardSpritesFromStoreItems()
|
||||
{
|
||||
rewardSpriteByItemId.Clear();
|
||||
|
||||
var rewardStoreItems = Resources.LoadAll<storeItemSO>("so/storeSO");
|
||||
for (int i = 0; i < rewardStoreItems.Length; i++)
|
||||
{
|
||||
var itemSO = rewardStoreItems[i];
|
||||
if (itemSO == null || itemSO.itemIcon == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardSpriteByItemId[itemSO.itemID] = itemSO.itemIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private Sprite GetRewardSpriteByItemId(int itemId)
|
||||
{
|
||||
if (itemId <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Sprite sprite;
|
||||
return rewardSpriteByItemId.TryGetValue(itemId, out sprite) ? sprite : null;
|
||||
}
|
||||
|
||||
private static int GetExpBottleRewardItemId(ExpBottleKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ExpBottleKind.Common: return 78001;
|
||||
case ExpBottleKind.Medium: return 78002;
|
||||
case ExpBottleKind.Superior: return 78003;
|
||||
case ExpBottleKind.Supreme: return 78004;
|
||||
case ExpBottleKind.Extraordinary: return 78005;
|
||||
case ExpBottleKind.Celestial: return 78006;
|
||||
case ExpBottleKind.RainAll: return 78011;
|
||||
case ExpBottleKind.AdvancedRainAll: return 78012;
|
||||
case ExpBottleKind.SuperRainAll: return 78013;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetDushMaterialRewardItemId(DushMaterialKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case DushMaterialKind.Material78021: return 78021;
|
||||
case DushMaterialKind.Material78022: return 78022;
|
||||
case DushMaterialKind.Material78023: return 78023;
|
||||
case DushMaterialKind.Material78024: return 78024;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,16 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
tasks:
|
||||
- taskID: 10101
|
||||
description: "\u767B\u5F55\u6E38\u620F"
|
||||
description: "\u767B\u5F55\u6E38\u620Fv50"
|
||||
taskType: 0
|
||||
refreshFrequency: 1
|
||||
targetValue: 1
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardAmount: 50
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10102
|
||||
description: "\u5B8C\u62103\u9996\u6B4C\u66F2"
|
||||
taskType: 1
|
||||
@@ -28,28 +31,62 @@ MonoBehaviour:
|
||||
targetValue: 3
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10103
|
||||
description: "\u5B8C\u6210\u4E00\u6B2199\u8FDE\u51FB"
|
||||
description: "\u8FBE\u62101\u6B21Full Combo"
|
||||
taskType: 2
|
||||
refreshFrequency: 1
|
||||
targetValue: 99
|
||||
targetValue: 1
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10104
|
||||
description: "\u8FBE\u5230\u4E00\u6B21\u603B\u52061000000"
|
||||
description: "\u7D2F\u8BA1\u83B7\u5F971000000\u5206"
|
||||
taskType: 3
|
||||
refreshFrequency: 1
|
||||
targetValue: 1000000
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardType: 1
|
||||
rewardAmount: 4
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10105
|
||||
description: "\u4F7F\u7528\u4E00\u6B21\u9644\u9B54\u4E4B\u74F6"
|
||||
description: "\u4F7F\u7528\u4EFB\u610F\u7C7B\u578B\u7ECF\u9A8C\u74F6\u4E00\u6B21"
|
||||
taskType: 5
|
||||
refreshFrequency: 1
|
||||
targetValue: 1
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
addToTaskPool: 0
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10106
|
||||
description: "\u82B1\u8D391000\u91D1\u5E01"
|
||||
taskType: 7
|
||||
refreshFrequency: 1
|
||||
targetValue: 1000
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
- taskID: 10107
|
||||
description: "\u6E38\u620F\u65F6\u957F\u8FBE\u523030\u5206\u949F"
|
||||
taskType: 8
|
||||
refreshFrequency: 1
|
||||
targetValue: 1800
|
||||
rewardType: 0
|
||||
rewardAmount: 0
|
||||
rewardExpBottleKind: 0
|
||||
rewardDushMaterialKind: 0
|
||||
addToTaskPool: 1
|
||||
selectionWeight: 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CreateAssetMenu(fileName = "NewUserTasksPool", menuName = "DailyTask/UserTasksPool")]
|
||||
public class userTasksPool : ScriptableObject
|
||||
@@ -10,7 +10,6 @@ public class userTasksPool : ScriptableObject
|
||||
[System.Serializable]
|
||||
public class TaskDefinition
|
||||
{
|
||||
|
||||
[Tooltip("Unique identifier for the task")]
|
||||
public string taskID;
|
||||
|
||||
@@ -22,20 +21,28 @@ public class userTasksPool : ScriptableObject
|
||||
public TaskType taskType;
|
||||
|
||||
[Tooltip("How often the task refreshes")]
|
||||
public RefreshFrequency refreshFrequency;
|
||||
public RefreshFrequency refreshFrequency = RefreshFrequency.DailyReset;
|
||||
|
||||
[Tooltip("Target value to complete the task (e.g., 100 combo, 3 songs)")]
|
||||
public float targetValue;
|
||||
[Tooltip("Target value to complete the task")]
|
||||
public float targetValue = 1f;
|
||||
|
||||
[Tooltip("Type of reward given upon completion")]
|
||||
public RewardType rewardType;
|
||||
|
||||
[Tooltip("Quantity of the reward")]
|
||||
public int rewardAmount;
|
||||
public int rewardAmount = 1;
|
||||
|
||||
[Tooltip("是否加入任务池")]
|
||||
[Tooltip("Used when rewardType is expBottle")]
|
||||
public ExpBottleKind rewardExpBottleKind = ExpBottleKind.Common;
|
||||
|
||||
[Tooltip("Used when rewardType is dushMaterial")]
|
||||
public DushMaterialKind rewardDushMaterialKind = DushMaterialKind.Material78021;
|
||||
|
||||
[Tooltip("Whether this task can be included in the random daily task pool")]
|
||||
public bool addToTaskPool = true;
|
||||
|
||||
[Tooltip("Weighted random selection weight")]
|
||||
public int selectionWeight = 1;
|
||||
}
|
||||
|
||||
public enum TaskType
|
||||
@@ -46,21 +53,22 @@ public class userTasksPool : ScriptableObject
|
||||
TotalScore,
|
||||
WatchStory,
|
||||
UseItem,
|
||||
ShareGame
|
||||
ShareGame,
|
||||
SpendCoins,
|
||||
GameDuration
|
||||
}
|
||||
|
||||
public enum RewardType
|
||||
{
|
||||
player_money,
|
||||
player_material,
|
||||
bottleOfEXP,
|
||||
playerEXP
|
||||
coins,
|
||||
expBottle,
|
||||
dushMaterial
|
||||
}
|
||||
|
||||
public enum RefreshFrequency
|
||||
{
|
||||
OnLogin,
|
||||
DailyReset,
|
||||
Never
|
||||
OnLogin,
|
||||
DailyReset,
|
||||
Never
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,8 +245,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 14.409, y: -0.56745}
|
||||
m_SizeDelta: {x: 29.618, y: 15.665}
|
||||
m_AnchoredPosition: {x: 12.13, y: 0}
|
||||
m_SizeDelta: {x: 34.1858, y: 20}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &602272132256342015
|
||||
CanvasRenderer:
|
||||
@@ -278,12 +278,12 @@ MonoBehaviour:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: dfa10de3e2846524089b5073175cd151, type: 3}
|
||||
m_FontSize: 14
|
||||
m_FontSize: 18
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 10
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 0
|
||||
m_Alignment: 3
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
@@ -325,7 +325,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -19.4, y: 0.6}
|
||||
m_SizeDelta: {x: 14, y: 18}
|
||||
m_SizeDelta: {x: 18, y: 18}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5147910286390164955
|
||||
CanvasRenderer:
|
||||
@@ -355,7 +355,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 4453427214848945594, guid: be41ed26869bfcf46b4c6b3acf9ec09e, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 30f736af053d2094e9406aa272c0725b, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
@@ -402,8 +402,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 171.90002, y: 1.1999512}
|
||||
m_SizeDelta: {x: 110, y: 47}
|
||||
m_AnchoredPosition: {x: 171.90002, y: -0.3146}
|
||||
m_SizeDelta: {x: 110, y: 50.0289}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5599503168244631389
|
||||
CanvasRenderer:
|
||||
@@ -468,7 +468,7 @@ MonoBehaviour:
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_DisabledColor: {r: 0.6509434, g: 0.6509434, b: 0.6509434, a: 1}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
@@ -498,6 +498,7 @@ GameObject:
|
||||
- component: {fileID: 1888209457113168138}
|
||||
- component: {fileID: 4060125231613820467}
|
||||
- component: {fileID: 5034613503719026902}
|
||||
- component: {fileID: 3223954077874542836}
|
||||
m_Layer: 5
|
||||
m_Name: progress
|
||||
m_TagString: Untagged
|
||||
@@ -516,13 +517,14 @@ RectTransform:
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Children:
|
||||
- {fileID: 8144451642442379881}
|
||||
m_Father: {fileID: 5109224055821985532}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 348, y: 55}
|
||||
m_AnchoredPosition: {x: 6.5, y: 0}
|
||||
m_SizeDelta: {x: 317, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4060125231613820467
|
||||
CanvasRenderer:
|
||||
@@ -545,23 +547,111 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.74294424, g: 1, b: 0.6556604, a: 1}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 4dcbef834d5273c41aee29d861169fd9, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: ad4148593b05d0f47980774815c325fe, type: 3}
|
||||
m_Type: 3
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 0
|
||||
m_FillAmount: 0
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &3223954077874542836
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4811890131364998009}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ShowMaskGraphic: 1
|
||||
--- !u!1 &4969152878334586126
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8144451642442379881}
|
||||
- component: {fileID: 8489452282027704886}
|
||||
- component: {fileID: 5843470283112251827}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8144451642442379881
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4969152878334586126}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1888209457113168138}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 13.8749, y: 0.000059128}
|
||||
m_SizeDelta: {x: 371.4091, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8489452282027704886
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4969152878334586126}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5843470283112251827
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4969152878334586126}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: f47d8a39555c75d4aa3ca75114e70bc4, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1.83
|
||||
--- !u!1 &6006229956337094046
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -746,8 +836,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -4.121, y: 1.7968}
|
||||
m_SizeDelta: {x: 255.338, y: 28.4246}
|
||||
m_AnchoredPosition: {x: -9.9906, y: 0}
|
||||
m_SizeDelta: {x: 267.0772, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3518498349447220834
|
||||
CanvasRenderer:
|
||||
@@ -779,7 +869,7 @@ MonoBehaviour:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: bc54bd51ea8b84448ba1b65311872862, type: 3}
|
||||
m_FontSize: 14
|
||||
m_FontSize: 15
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
@@ -863,7 +953,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0.375, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -74.6, y: 17}
|
||||
m_AnchoredPosition: {x: -76.8, y: 12.584}
|
||||
m_SizeDelta: {x: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &2318309606455167046
|
||||
@@ -921,8 +1011,8 @@ MonoBehaviour:
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 28
|
||||
m_fontSizeBase: 28
|
||||
m_fontSize: 20
|
||||
m_fontSizeBase: 20
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
@@ -999,8 +1089,8 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 348, y: 55}
|
||||
m_AnchoredPosition: {x: 6.5, y: 0}
|
||||
m_SizeDelta: {x: 317, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8024881274196398679
|
||||
CanvasRenderer:
|
||||
@@ -1030,8 +1120,8 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 4dcbef834d5273c41aee29d861169fd9, type: 3}
|
||||
m_Type: 0
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
@@ -1039,4 +1129,4 @@ MonoBehaviour:
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
m_PixelsPerUnitMultiplier: 0.7
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class taskPrefabController : MonoBehaviour
|
||||
{
|
||||
@@ -13,4 +14,53 @@ public class taskPrefabController : MonoBehaviour
|
||||
public Button rewardButton;
|
||||
public Image rewardImg;
|
||||
public Text rewardAmmount;
|
||||
|
||||
public void Setup(DailyTaskViewData taskView, int displayIndex, Sprite rewardSprite, UnityAction onRewardClicked)
|
||||
{
|
||||
if (taskView == null || taskView.definition == null || taskView.runtimeEntry == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (taskNumber != null)
|
||||
{
|
||||
taskNumber.text = displayIndex.ToString("00");
|
||||
}
|
||||
|
||||
if (taskText != null)
|
||||
{
|
||||
taskText.text = taskView.definition.description;
|
||||
}
|
||||
|
||||
if (taskProgress_fillAmount_img != null)
|
||||
{
|
||||
float targetValue = Mathf.Max(0.0001f, taskView.definition.targetValue);
|
||||
float clampedProgress = Mathf.Clamp(taskView.runtimeEntry.progress, 0f, targetValue);
|
||||
taskProgress_fillAmount_img.fillAmount = clampedProgress / targetValue;
|
||||
}
|
||||
|
||||
if (rewardImg != null)
|
||||
{
|
||||
rewardImg.sprite = rewardSprite;
|
||||
rewardImg.enabled = rewardSprite != null;
|
||||
}
|
||||
|
||||
if (rewardAmmount != null)
|
||||
{
|
||||
rewardAmmount.text = taskView.definition.rewardAmount.ToString();
|
||||
}
|
||||
|
||||
if (rewardButton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rewardButton.onClick.RemoveAllListeners();
|
||||
if (onRewardClicked != null)
|
||||
{
|
||||
rewardButton.onClick.AddListener(onRewardClicked);
|
||||
}
|
||||
|
||||
rewardButton.interactable = taskView.runtimeEntry.isCompleted && !taskView.runtimeEntry.isClaimed;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user