UE4 | جرد متعددة اللاعبين # 1 | مستودع البيانات على DataAsset




مجموعة البيانات في هذه المقالة سأحاول الكشف عن معنى ومنهجية إنشاء DataAsset ، كمستودع لأنواع مختلفة من البيانات ، وفي حالتنا هي مكتبة للممثلين ومعلماتهم.




مقدمة صغيرة للتخطي

لاتخاذ قرار بإنشاء لعبة منذ حوالي عامين ، ساعدني أنني تعثرت بطريق الخطأ على معلومات حول Unreal Engine 4 وقرأت كم هي رائعة وبسيطة. في الواقع ، من الصعب جدًا على الشخص الذي لا يعرف كيفية كتابة التعليمات البرمجية (لا يهم لغة البرمجة في هذا السياق) إنشاء شيء أكثر تعقيدًا من تعديل صغير لمجموعة قياسية من قطع العمل من المحرك. لذلك ، نمت تدريجياً الرغبة الأولية في صنع لعبة ضخمة ، مع نمو المعرفة حول واقع هذا المشروع ، إلى هواية. يبدو أن رفع جميع طبقات تطوير اللعبة ، بدءًا من النمذجة ثلاثية الأبعاد والرسوم المتحركة ، وحتى كتابة التعليمات البرمجية ، أمرًا ممكنًا بالنسبة لشخص واحد. ومع ذلك ، هذا تمرين جيد للدماغ.


لماذا قررت أن تكتب شيئاً؟ .. ربما بسبب ذلك. أن الكتيبات المقدمة إما تعطي معرفة سطحية للغاية (وهناك معظمها) ، أو حتى بالنسبة لمهنيين للغاية وتحتوي فقط على تعليمات عامة.


من الأفضل دائمًا البدء من البداية. لا يمكنني القول أنني أفعل ذلك دائمًا ، لكني سأحاول أن أصرح بأكبر قدر ممكن من الاتساق.


بالطبع ، من الأفضل البدء بالهيكل ، ولكن للأسف ، مع وجود صندوق مغلق بأدوات ، من الصعب جدًا فهم ما يمكن بناؤه بالضبط بمساعدتهم. لذا دعنا نفتح هذا الصندوق ونرى ما بداخله.




السؤال الأول للإجابة. لماذا DataAsset ؟


  1. في كثير من الأحيان في المقالات والبرامج التعليمية يمكنك مشاهدة استخدام DataTable . لماذا هذا سيء؟ إذا قمت بتخزين عنوان لمخطط معين ، فعند إعادة تسميته أو نقله إلى مجلد آخر ، ستضطر إلى تغيير هذا العنوان يدويًا. توافق - غير مريح؟ مع DataAsset ، لن يحدث هذا. سيتم تحديث جميع الاتصالات تلقائيا. إذا كنت واثقًا تمامًا في هيكل مشروعك لسنوات قادمة ، فيمكنك بالطبع استخدام الجداول.
  2. الميزة الثانية التي لا يمكن إنكارها هي القدرة على تخزين أنواع البيانات المعقدة ، مثل الهياكل ( الهيكل ).

الآن القليل عن العيوب النسبية. في الواقع ، أرى واحدة فقط. هذه هي الحاجة لكتابة كود C ++ .


إذا كنت قد فهمت بالفعل أنه بدون العمل مع الرمز ، فلن تفعل أي شيء ملحمي ، فهذا ليس عيبًا ، ولكنه ميزة.


وتجدر الإشارة إلى أن هناك حلاً واحدًا - لاستخدام الفاعل كمخزن. لكن مثل هذا التطبيق يبدو وكأنه العذر الأخير لعدم الرغبة في تعلم لغة C ++ ، ويحمل إمكانية أن ينتهي به الأمر في طريق مسدود في المستقبل.
إذا كنت مقتنعًا بأن كل ما تحتاجه لمشروعك يمكن القيام به على مخطط ، استخدم الجداول.




الآن بعد أن كنت تعتقد بالفعل أن DataAsset جيد ، فكر في كيفية إنشائه لمشروعك.


بالنسبة لأولئك الذين لا يزالون "ليسوا في الخزان"
هناك وصف مفصل للغاية للخطوات والصور في منتدى اللغة الروسية مخصص لـ UE4 . مجرد جوجل "UE4 DataAsset" ل . لقد أتقن أساسيات هذا الدليل قبل حوالي عام.

بادئ ذي بدء ، قم بإنشاء فئة C ++ ، مثل الطفل من UDataAsset .
(كل الكود أدناه مأخوذ من مشروعي الذي لم يولد بعد. ما عليك سوى إعادة تسمية الأسماء كما تريد.)


/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #pragma once /* Includes from Engine */ #include "Engine/DataAsset.h" #include "Engine/Texture2D.h" #include "GameplayTagContainer.h" /* Includes from Dreampax */ //no includes #include "DreampaxItemsDataAsset.generated.h" UCLASS(BlueprintType) class DREAMPAX_API UDreampaxItemsDataAsset : public UDataAsset { GENERATED_BODY() } 

الآن ، على أساس هذه الفئة ، يمكنك إنشاء مخطط بأمان ، ولكن من السابق لأوانه القيام بذلك ... حتى الآن هو مجرد دمية. على الرغم من ذلك ، لاحظ أنه تم بالفعل تضمين شوائب للقوام والأسماء.




من هذه اللحظة ، تبدأ في إنشاء هيكل المستودع الخاص بك. سيتم إعادة بنائه عدة مرات ، لذلك أوصي بشدة بعدم ملء مساحة التخزين الخاصة بك على الفور. ثلاثة إلى خمسة عناصر ، في عناصر جرد حالتنا ، كافية للاختبارات. في بعض الأحيان ، بعد التجميع ، يمكن أن يكون مخططك فارغًا تمامًا ، وهو أمر غير سار للغاية إذا كنت قد قمت بالفعل بملء اثني عشر أو وظيفتين.


يمكنك إنشاء بنية مباشرة في ملف الرأس ، لأن في هذه الحالة ، من غير المرجح أن يتم تطبيقه في مكان آخر. عادة ، أفضل جعله كملف رأس منفصل “SrtuctName.h” ، وربطه عند الضرورة.


في حالتي ، يبدو هذا
 USTRUCT(BlueprintType) struct FItemsDatabase { GENERATED_USTRUCT_BODY() /* Storage for any float constant data */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TMap<FGameplayTag, float> ItemData; /* Gameplay tag container to store the properties */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") FGameplayTagContainer ItemPropertyTags; /* Texture for showing in the inventory */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") UTexture2D* IconTexture; /* The class put on the Mesh on the character */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxOutfitActor> ItemOutfitClass; /* The class to spawn the Mesh in the level then it is dropped */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxPickupActor> ItemPickupClass; //TODO internal call functions }; 

كن حسابات مع TMap . غير منسوخ! في هذه الحالة ، لا يهم.


يرجى ملاحظة أنني لا أستخدم FName . وفقا للاتجاهات الحديثة ، يعتبر استخدام FGameplayTag أكثر صحة ، لأنه يقلل بشكل كبير من خطر الخطأ ولديه العديد من المزايا التي ستكون مفيدة في وقت لاحق.


GameplayTag في المحرر
علامة اللعب

تم تصدير DataTable لـ GameplayTag من Excel
DataTable

من الممارسات الجيدة أيضًا تحديد وظائف استدعاء المتغيرات في البنية ، مثل GetSomething () . على ما يبدو ، ما زلت بحاجة إلى العمل على تربيتي ، نظرًا لأنها موجودة في قاعدة البيانات هذه ، لم أجري مثل هذه المكالمة حتى الآن.


إليك كيفية القيام بذلك باستخدام قاعدة بيانات أخرى كمثال.
 USTRUCT(BlueprintType) struct FBlocksDatabase { GENERATED_USTRUCT_BODY() /* The class put on the Mesh for the building block */ UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") TSubclassOf<class ADreampaxBuildingBlock> BuildingBlockClass; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") FVector DefaultSize; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") FVector SizeLimits; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") TArray<class UMaterialInterface *> BlockMaterials; FORCEINLINE TSubclassOf<class ADreampaxBuildingBlock> * GetBuildingBlockClass() { return &BuildingBlockClass; } FORCEINLINE FVector GetDefaultSize() { return DefaultSize; } FORCEINLINE FVector GetSizeLimits() { return SizeLimits; } FORCEINLINE TArray<class UMaterialInterface *> GetBlockMaterials() { return BlockMaterials; } }; 

وأهم نقطة هي إعلان قاعدة البيانات:


 UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") TMap<FGameplayTag, FItemsDatabase> ItemsDataBase; 

يمكنك الآن إنشاء مخططنا وتعبئته.


مثال لملء DataAsset
مثال لملء DataAsset

ولكن قبل ذلك ، سنكتب المزيد من وظائف الاتصال حتى نتمكن من تلقي البيانات من قاعدة البيانات.


DreampaxItemsDataAsset.h
 /// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #pragma once /* Includes from Engine */ #include "Engine/DataAsset.h" #include "Engine/Texture2D.h" #include "GameplayTagContainer.h" /* Includes from Dreampax */ //no includes #include "DreampaxItemsDataAsset.generated.h" USTRUCT(BlueprintType) struct FItemsDatabase { GENERATED_USTRUCT_BODY() /* Storage for any float constant data */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TMap<FGameplayTag, float> ItemData; /* Gameplay tag container to store the properties */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") FGameplayTagContainer ItemPropertyTags; /* Texture for showing in the inventory */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") UTexture2D* IconTexture; /* The class put on the Mesh on the character */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxOutfitActor> ItemOutfitClass; /* The class to spawn the Mesh in the level then it is dropped */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxPickupActor> ItemPickupClass; //TODO internal call functions }; UCLASS(BlueprintType) class DREAMPAX_API UDreampaxItemsDataAsset : public UDataAsset { GENERATED_BODY() protected: /* This GameplayTag is used to find a Max size of the stack for the Item. This tag can be missed in the ItemData */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") FGameplayTag DefaultGameplayTagForMaxSizeOfStack; /* This is the main Database for all Items. It contains constant common variables */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") TMap<FGameplayTag, FItemsDatabase> ItemsDataBase; public: FORCEINLINE TMap<FGameplayTag, float> * GetItemData(const FGameplayTag &ItemNameTag); FORCEINLINE FGameplayTagContainer * GetItemPropertyTags(const FGameplayTag &ItemNameTag); /* Used in the widget */ UFUNCTION(BlueprintCallable, Category = "ItemDatabase") FORCEINLINE UTexture2D * GetItemIconTexture(const FGameplayTag & ItemNameTag) const; FORCEINLINE TSubclassOf<class ADreampaxOutfitActor> * GetItemOutfitClass(const FGameplayTag & ItemNameTag); FORCEINLINE TSubclassOf<class ADreampaxPickupActor> * GetItemPickupClass(const FGameplayTag & ItemNameTag); int GetItemMaxStackSize(const FGameplayTag & ItemNameTag); FORCEINLINE bool ItemIsFound(const FGameplayTag & ItemNameTag) const; }; 

DreampaxItemsDataAsset.pp
 /// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #include "DreampaxItemsDataAsset.h" /* Includes from Engine */ // no includes /* Includes from Dreampax */ // no includes TMap<FGameplayTag, float>* UDreampaxItemsDataAsset::GetItemData(const FGameplayTag & ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemData; } FGameplayTagContainer * UDreampaxItemsDataAsset::GetItemPropertyTags(const FGameplayTag & ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemPropertyTags; } UTexture2D* UDreampaxItemsDataAsset::GetItemIconTexture(const FGameplayTag &ItemNameTag) const { if (ItemNameTag.IsValid()) { return ItemsDataBase.Find(ItemNameTag)->IconTexture; } return nullptr; } TSubclassOf<class ADreampaxOutfitActor>* UDreampaxItemsDataAsset::GetItemOutfitClass(const FGameplayTag &ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemOutfitClass; } TSubclassOf<class ADreampaxPickupActor>* UDreampaxItemsDataAsset::GetItemPickupClass(const FGameplayTag &ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemPickupClass; } int UDreampaxItemsDataAsset::GetItemMaxStackSize(const FGameplayTag & ItemNameTag) { // if DefaultGameplayTagForMaxSizeOfStack is missed return 1 for all items if (!DefaultGameplayTagForMaxSizeOfStack.IsValid()) { return 1; } int MaxStackSize = floor(GetItemData(ItemNameTag)->FindRef(DefaultGameplayTagForMaxSizeOfStack)); if (MaxStackSize > 0) { return MaxStackSize; } // if Tag for MaxStackSize is "0" return 1 return 1; } bool UDreampaxItemsDataAsset::ItemIsFound(const FGameplayTag & ItemNameTag) const { if (ItemsDataBase.Find(ItemNameTag)) { return true; } return false; } 

لا يوجد شيء من اللاعبين المتعددين. ولكن هذه هي الخطوة الأولى التي تم اتخاذها في الاتجاه الصحيح.


في المقالة التالية سوف أتحدث عن طرق ربط DataAsset (نعم ، وأي مخطط ) لقراءة البيانات في C ++ ، وإظهار أيهما الأكثر صحة.


إذا كانت لديك أسئلة أو اقتراحات للكشف عن أي جانب بمزيد من التفصيل ، يرجى الكتابة في التعليقات.

Source: https://habr.com/ru/post/ar418913/


All Articles