UE求职Demo开发日志#24 继续完善任务系统

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显示

过程太繁琐,直接看效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值