1 任务数据持久化
1.1 创建新的save类,并创建相应变量和函数
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "QuestSaveData.generated.h"
struct FQuestNodeData;
struct FQuestStructure;
/**
*
*/
UCLASS()
class ARPG_CPLUS_API UQuestSaveData : public USaveGame
{
GENERATED_BODY()
private:
UPROPERTY()
TArray<FQuestStructure> CurrentQuests{};
UPROPERTY()
TArray<FQuestNodeData> CurrentQuestNodesData{};
UPROPERTY()
TArray<FQuestStructure> CompletedQuests{};
public:
//GetterAndSetter
UFUNCTION(BlueprintCallable)
TArray<FQuestStructure> GetCurrentQuests();
UFUNCTION(BlueprintCallable)
void SetCurrentQuests(TArray<FQuestStructure>& Quests);
UFUNCTION(BlueprintCallable)
TArray<FQuestNodeData> GetCurrentQuestNodesData();
UFUNCTION(BlueprintCallable)
void SetCurrentQuestNodesData(TArray<FQuestNodeData>& QuestNodesData);
UFUNCTION(BlueprintCallable)
TArray<FQuestStructure> GetCompletedQuests();
UFUNCTION(BlueprintCallable)
void SetCompletedQuests(TArray<FQuestStructure>& Quests);
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Quest/QuestSaveData.h"
#include "Quest/QuestManagerComponent.h"
TArray<FQuestStructure> UQuestSaveData::GetCurrentQuests()
{
return CurrentQuests;
}
void UQuestSaveData::SetCurrentQuests(TArray<FQuestStructure>& Quests)
{
CurrentQuests = Quests;
}
TArray<FQuestNodeData> UQuestSaveData::GetCurrentQuestNodesData()
{
return CurrentQuestNodesData;
}
void UQuestSaveData::SetCurrentQuestNodesData(TArray<FQuestNodeData>& QuestNodesData)
{
CurrentQuestNodesData = QuestNodesData;
}
TArray<FQuestStructure> UQuestSaveData::GetCompletedQuests()
{
return CompletedQuests;
}
void UQuestSaveData::SetCompletedQuests(TArray<FQuestStructure>& Quests)
{
CompletedQuests = Quests;
}
1.2 管理存储函数
bool UQuestManagerComponent::LoadData()
{
bool bIsSaveGameExist=UGameplayStatics::DoesSaveGameExist(FString("QuestSaveData"), 0);
if(!bIsSaveGameExist)return false;
UQuestSaveData* LoadedQuestSaveData= Cast<UQuestSaveData>(UGameplayStatics::LoadGameFromSlot(FString("QuestSaveData"), 0));
if(!LoadedQuestSaveData)return false;
CurrentQuests=LoadedQuestSaveData->GetCurrentQuests();
CurrentQuestNodesData=LoadedQuestSaveData->GetCurrentQuestNodesData();
CompletedQuests=LoadedQuestSaveData->GetCompletedQuests();
return true;
}
bool UQuestManagerComponent::SaveData()
{
UQuestSaveData* Save = Cast<UQuestSaveData>(UGameplayStatics::CreateSaveGameObject(UQuestSaveData::StaticClass()));
if (Save)
{
Save->SetCurrentQuests(CurrentQuests);
Save->SetCurrentQuestNodesData(CurrentQuestNodesData);
Save->SetCompletedQuests(CompletedQuests);
bool bIsSaveSuccess=UGameplayStatics::SaveGameToSlot(Save, FString("QuestSaveData"), 0);
if(bIsSaveSuccess)
{
UE_LOG(LogTemp, Warning, TEXT("UQuestManagerComponent-->Save Success"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("UQuestManagerComponent-->Save Failed"));
}
return bIsSaveSuccess;
}
return false;
}
2 对外提供修改指定任务节点的Value的接口
2.1 三个check函数
bool UQuestManagerComponent::CheckIsExistQuestById(int QuestId)const
{
for(const auto& Quest:CurrentQuests)
{
if(Quest.QuestID==QuestId)
{
return true;
}
}
return false;
}
bool UQuestManagerComponent::CheckIsExistQuestNodeDataById(int NodeId)const
{
for(const auto& QuestNodeData:CurrentQuestNodesData)
{
if(QuestNodeData.NodeID==NodeId)
{
return true;
}
}
return false;
}
bool UQuestManagerComponent::CheckIsExistQuestConditionByIndex(int NodeId,int index)const
{
for(const auto& QuestNodeData:CurrentQuestNodesData)
{
if(QuestNodeData.NodeID==NodeId)
{
if(index<QuestNodeData.Conditions.Num()&&index>=0)
{
return true;
}
return false;
}
}
return false;
}
2.2 对外修改接口
//默认加等于Value,不做数值限制,由调用者设置限制
bool UQuestManagerComponent::ModQuestConditionValue(int QuestNodeId,int QuestConditionIndex,int Value)
{
if(!(CheckIsExistQuestNodeDataById(QuestNodeId)&&CheckIsExistQuestConditionByIndex(QuestNodeId,QuestConditionIndex)))
{
return false;
}
int NodeDataIndex{GetQuestNodeDataIndexByNodeID(QuestNodeId)};
CurrentQuestNodesData[NodeDataIndex].Conditions[QuestConditionIndex].CurrentValue+=Value;
//UE_LOG(LogTemp,Warning,TEXT("QuestManagerComponent-->ModQuestConditionValue,Value=%f"),CurrentQuestNodesData[NodeDataIndex].Conditions[QuestConditionIndex].CurrentValue);
return true;
}
2.3 获取物品相关任务节点
TArray<int> UQuestManagerComponent::GetItemTypeQuestNodeInCurrentNode()const
{
TArray<int> NodesId{};
for(const auto& Quest:CurrentQuests)
{
if(CurrentQuestNodesData[GetQuestNodeDataIndexByNodeID(Quest.CurrentNodeID)].QuestType==EQuestType::Items)//若每个任务的当前节点类型等于Items
{
NodesId.AddUnique(Quest.CurrentNodeID);
}
}
return NodesId;
}
2.4 TArray<任务节点id>接收 所有物品相关的任务节点中 需要某个id物品的节点
TArray<int> UQuestManagerComponent::GetRelativeQuestNodeByItemId(TArray<int> ItemRelativeQuestNodes,int itemId)const
{
TArray<int> NodesId{};
for(const auto& NodeId:ItemRelativeQuestNodes)
{
for(const auto& QuestNodeData:CurrentQuestNodesData)
{
if(QuestNodeData.NodeID==NodeId)
{
//遍历当前节点的所有条件中是否存在指定itemid的条件
for(const auto& Condition:QuestNodeData.Conditions)
{
if(Condition.ItemID==itemId)
{
NodesId.AddUnique(NodeId);
break;
}
}
break;
}
}
}
return NodesId;
}
2.5 物品相关任务调用逻辑
获取物品相关任务节点->获取其中需要某个物品的节点->通过节点id,遍历节点下的condition找到对应的itemid的index->调用Mod函数->尝试提交节点对应的任务
最终实现的物品相关任务的修改函数:
(我知道写的很史但是以后会优化的,现在就一两个任务怎么遍历都没事)
bool UQuestManagerComponent::UpdateConditionAndTryCommit(int ItemId,int Value)
{
TArray<int> ItemTypeNode{GetItemTypeQuestNodeInCurrentNode()};
ItemTypeNode=GetRelativeQuestNodeByItemId(ItemTypeNode,ItemId);
for(const auto& Nodeid:ItemTypeNode)UE_LOG(LogTemp,Warning,TEXT("ItemTypeNode:id==%d"),Nodeid);
UE_LOG(LogTemp,Warning,TEXT("UpdateConditionAndTryCommit,1"));
bool bIsModified=false;
for(const auto& NodeId:ItemTypeNode )//1
{
int NodeDataIndex{GetQuestNodeDataIndexByNodeID(NodeId)};
UE_LOG(LogTemp,Warning,TEXT("UpdateConditionAndTryCommit,2"));
for(int index=0;index<CurrentQuestNodesData[NodeDataIndex].Conditions.Num();index++)
{
if(CurrentQuestNodesData[NodeDataIndex].Conditions[index].ItemID==ItemId)
{
if(ModQuestConditionValue(NodeId,index,Value))
bIsModified=true;
UE_LOG(LogTemp,Warning,TEXT("CurrentQuestNodesData[%d].Conditions[%d].ItemID==%d,CurrentValue==%f"),NodeDataIndex,index,CurrentQuestNodesData[NodeDataIndex].Conditions[index].ItemID,CurrentQuestNodesData[NodeDataIndex].Conditions[index].CurrentValue);
}
}
UE_LOG(LogTemp,Warning,TEXT("UpdateConditionAndTryCommit,3"));
for(const auto& Quest:CurrentQuests)
{
if(Quest.CurrentNodeID==NodeId)
{
TryCommitQuestCurrentNode(Quest.QuestID);
break;//要保证唯一任务包含此节点,不可继续遍历,TryCommit函数里的Complete函数会修改CurrentQuest
}
}
}
UE_LOG(LogTemp,Warning,TEXT("UpdateConditionAndTryCommit,4"));
return bIsModified;
}
3 任务奖励的获取
3.1 添加变量记录奖励物品
任务节点不设奖励,整个任务设奖励, 因此变量放在FQuestStructure里,数据类型TArray<FMyItemInfo>
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category="QuestStructure")
TArray<FMyItemInfo> Rewards{};
3.2 添加回调
void UQuestManagerComponent::OnCompleteQuest(int index)const
{
TArray<FMyItemInfo> RewardsCopy=CurrentQuests[index].Rewards;
AMyPlayer* Player=Cast<AMyPlayer>(GetOwner());
Player->QuestRewards=RewardsCopy;
Player->OnGetRewardsItems();
}
//MyPlayer.h
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite,Category = "MyPlayer|Quest")
TArray<FMyItemInfo> QuestRewards{};
UFUNCTION(BlueprintImplementableEvent)
void OnGetRewardsItems();
调用仓库的添加物品函数,这里默认加到仓库里
显示获得物品提示,并保存数据
测试效果:
4 将完成的任务记录
扩写完成任务回调即可
void UQuestManagerComponent::OnCompleteQuest(int index)
{
TArray<FMyItemInfo> RewardsCopy=CurrentQuests[index].Rewards;
AMyPlayer* Player=Cast<AMyPlayer>(GetOwner());
Player->QuestRewards=RewardsCopy;
Player->OnGetRewardsItems();
FQuestStructure CompletedQuest=CurrentQuests[index];
AddToCompletedQuestsWithoutRepeat(CompletedQuest);
}
5 优化UI显示
过程太繁琐,直接看效果: