Compare commits
3 Commits
c90b46f430
...
15a213a5c4
| Author | SHA1 | Date | |
|---|---|---|---|
| 15a213a5c4 | |||
| 090bd16c67 | |||
| 4bb809b8e3 |
@ -27,4 +27,5 @@ r.DefaultFeature.MotionBlur=False
|
||||
+PropertyRedirects=(OldName="/Script/BusyRabbit.TerrainTileSetConfig.TerrainTileMapping",NewName="/Script/BusyRabbit.TerrainTileSetConfig.n")
|
||||
+PropertyRedirects=(OldName="/Script/BusyRabbit.TerrainLayerComponent.TerrainLayers",NewName="/Script/BusyRabbit.TerrainLayerComponent.TerrainMeshes")
|
||||
+ClassRedirects=(OldName="/Script/BusyRabbit.LevelPlayerController",NewName="/Script/BusyRabbit.LevelPlayerController")
|
||||
+ClassRedirects=(OldName="/Script/BusyRabbit.StaticResource",NewName="/Script/BusyRabbit.BusyStaticResource")
|
||||
|
||||
|
||||
Binary file not shown.
BIN
Content/Blueprint/Level/Actor/RoamingCamera.uasset
Normal file
BIN
Content/Blueprint/Level/Actor/RoamingCamera.uasset
Normal file
Binary file not shown.
Binary file not shown.
BIN
Content/Blueprint/Level/Actor/Static/BP_Campsite.uasset
Normal file
BIN
Content/Blueprint/Level/Actor/Static/BP_Campsite.uasset
Normal file
Binary file not shown.
BIN
Content/Blueprint/Level/Actor/Static/BP_Tree.uasset
Normal file
BIN
Content/Blueprint/Level/Actor/Static/BP_Tree.uasset
Normal file
Binary file not shown.
Binary file not shown.
BIN
Content/Blueprint/Level/GameMode/BP_BusyLevelGameState.uasset
Normal file
BIN
Content/Blueprint/Level/GameMode/BP_BusyLevelGameState.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Data/Input/Level/IA_CameraDetach.uasset
Normal file
BIN
Content/Data/Input/Level/IA_CameraDetach.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3
Content/Lua/@types/BusyGameplayLibrary.d.lua
Normal file
3
Content/Lua/@types/BusyGameplayLibrary.d.lua
Normal file
@ -0,0 +1,3 @@
|
||||
--- @class BusyGameplayLibrary
|
||||
--- @field K2_GetWorld fun(obj:table):table
|
||||
local BusyGameplayLibrary = {}
|
||||
@ -27,3 +27,9 @@ slua = {
|
||||
KismetSystemLibrary = {
|
||||
K2_ClearTimerHandle = function() end
|
||||
}
|
||||
|
||||
|
||||
--- @class GameplayStatics
|
||||
--- @field GetPlayerController fun(uobj:table,idx:number):table
|
||||
--- @field GetGameState fun(uobj:table):table
|
||||
local GameplayStatics
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
local BusyPlayerRole = {}
|
||||
|
||||
function BusyPlayerRole:UpdateMoveDirection(InDirection)
|
||||
if(InDirection.Y > 0) then
|
||||
self["SpineAnimationComponent"]:SetSkin("front/move")
|
||||
else
|
||||
self["SpineAnimationComponent"]:SetSkin("back/move")
|
||||
end
|
||||
end
|
||||
|
||||
return Class(nil, nil, BusyPlayerRole)
|
||||
4
Content/Lua/Level/Actor/LevelFoxRole.lua
Normal file
4
Content/Lua/Level/Actor/LevelFoxRole.lua
Normal file
@ -0,0 +1,4 @@
|
||||
local LevelFoxRole = {}
|
||||
|
||||
|
||||
return Class(nil, nil, LevelFoxRole)
|
||||
58
Content/Lua/Level/Actor/LevelRabbitRole.lua
Normal file
58
Content/Lua/Level/Actor/LevelRabbitRole.lua
Normal file
@ -0,0 +1,58 @@
|
||||
local LevelRabbitRole = {}
|
||||
|
||||
function LevelRabbitRole:ctor()
|
||||
self.last_animation = "back/move"
|
||||
end
|
||||
|
||||
function LevelRabbitRole:ReceiveBeginPlay()
|
||||
self["SpineAnimationComponent"]:SetSkin("back/move");
|
||||
self["SpineAnimationComponent"]:SetAnimation(0, "animation", true);
|
||||
end
|
||||
|
||||
|
||||
function LevelRabbitRole:OnMove(location)
|
||||
-- 控制器移动相应函数
|
||||
self["MovementComponent"]:MoveTo(location)
|
||||
end
|
||||
|
||||
|
||||
function LevelRabbitRole:UpdateMoveDirection(InDirection)
|
||||
-- 运动组件更新方向响应函数
|
||||
local cur_animation
|
||||
if(InDirection.Y >= 0) then
|
||||
cur_animation = "front/move"
|
||||
else
|
||||
cur_animation = "back/move"
|
||||
end
|
||||
if cur_animation ~= self.last_animation then
|
||||
self["SpineAnimationComponent"]:SetSkin(cur_animation)
|
||||
self["SpineAnimationComponent"]:SetSlotsToSetupPose()
|
||||
self.last_animation = cur_animation
|
||||
end
|
||||
end
|
||||
|
||||
function LevelRabbitRole:OnDetachCamera()
|
||||
print(self, "LevelRabbitRole.OnDetachCamera")
|
||||
|
||||
-- 禁用弹簧臂的附着
|
||||
self["SpringArmComponent"].bEnableCameraRotationLag = true
|
||||
self["SpringArmComponent"].CameraRotationLagSpeed = 0
|
||||
-- 保持当前位置,停止跟随
|
||||
self["SpringArmComponent"].bInheritPitch = true
|
||||
self["SpringArmComponent"].bInheritYaw = true
|
||||
self["SpringArmComponent"].bInheritRoll = true
|
||||
|
||||
-- (Pitch=0.000000,Yaw=-90.000000,Roll=0.000000)
|
||||
end
|
||||
|
||||
function LevelRabbitRole:OnReattachCamera()
|
||||
print(self, "LevelRabbitRole.OnReattachCamera")
|
||||
end
|
||||
|
||||
|
||||
function LevelRabbitRole:OnMoveCamera(direction)
|
||||
print(self, "LevelRabbitRole.OnMoveCamera", direction.X, direction.Y)
|
||||
end
|
||||
|
||||
|
||||
return Class(nil, nil, LevelRabbitRole)
|
||||
4
Content/Lua/Level/Actor/Static/Campsite.lua
Normal file
4
Content/Lua/Level/Actor/Static/Campsite.lua
Normal file
@ -0,0 +1,4 @@
|
||||
local Campsite = {}
|
||||
|
||||
|
||||
return Class(nil, nil, Campsite)
|
||||
36
Content/Lua/Level/LevelGameMode.lua
Normal file
36
Content/Lua/Level/LevelGameMode.lua
Normal file
@ -0,0 +1,36 @@
|
||||
local Vector = import("Vector")
|
||||
local GameplayStatics = import("GameplayStatics")
|
||||
local BusyGameplayLibrary = import("BusyGameplayLibrary")
|
||||
|
||||
--- 保留到以后做联机内容时拓展
|
||||
--- @class LevelGameMode
|
||||
--- @field GameMapActorClass table
|
||||
local LevelGameMode = {}
|
||||
|
||||
function LevelGameMode:ctor()
|
||||
self.map_actor = nil
|
||||
end
|
||||
|
||||
function LevelGameMode:ReceiveBeginPlay()
|
||||
print("LevelGameMode:ReceiveBeginPlay", self.GameMapActorClass)
|
||||
local world = BusyGameplayLibrary.K2_GetWorld(self)
|
||||
local game_state = GameplayStatics.GetGameState(self) --- @type LevelGameState
|
||||
|
||||
if self.GameMapActorClass then
|
||||
local map_actor = world:SpawnActor(self.GameMapActorClass, Vector(), nil, nil)
|
||||
game_state:SetGameMapActor(map_actor)
|
||||
end
|
||||
end
|
||||
|
||||
-- function LevelGameMode:K2_PostLogin(new_player_controller)
|
||||
-- local new_player_state = new_player_controller.PlayerState
|
||||
-- local role = new_player_state:CreateRoleRoster(new_player_controller)
|
||||
-- local new_pos = FVector()
|
||||
-- new_pos.X = 500
|
||||
-- new_pos.Y = 500
|
||||
-- new_pos.Z = 50
|
||||
-- role:K2_SetActorLocation(new_pos, true, nil, false)
|
||||
-- new_player_controller:Possess(role)
|
||||
-- end
|
||||
|
||||
return Class(nil, nil, LevelGameMode)
|
||||
9
Content/Lua/Level/LevelGameState.lua
Normal file
9
Content/Lua/Level/LevelGameState.lua
Normal file
@ -0,0 +1,9 @@
|
||||
--- @class LevelGameState
|
||||
local LevelGameState = {}
|
||||
|
||||
function LevelGameState:SetGameMapActor(game_map_actor)
|
||||
self.GameMapActor = game_map_actor
|
||||
end
|
||||
|
||||
|
||||
return Class(nil, nil, LevelGameState)
|
||||
12
Content/Lua/Level/LevelPlayerState.lua
Normal file
12
Content/Lua/Level/LevelPlayerState.lua
Normal file
@ -0,0 +1,12 @@
|
||||
local GameplayStatics = import("GameplayStatics")
|
||||
|
||||
local LevelPlayerState = {}
|
||||
|
||||
function LevelPlayerState:ReceiveBeginPlay()
|
||||
local pc = GameplayStatics.GetPlayerController(self, 0)
|
||||
local role = self:CreateRoleRoster(pc)
|
||||
print("LevelPlayerState:ReceiveBeginPlay", role, self:HasAuthority())
|
||||
pc:Possess(role)
|
||||
end
|
||||
|
||||
return Class(nil, nil, LevelPlayerState)
|
||||
BIN
Content/Resource/Spine/PlacedItems/Fire/Fire.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Fire/Fire.uasset
Normal file
Binary file not shown.
BIN
Content/Resource/Spine/PlacedItems/Fire/FireData.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Fire/FireData.uasset
Normal file
Binary file not shown.
BIN
Content/Resource/Spine/PlacedItems/Fire/Textures/Fire.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Fire/Textures/Fire.uasset
Normal file
Binary file not shown.
BIN
Content/Resource/Spine/PlacedItems/Tree/Textures/Tree.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Tree/Textures/Tree.uasset
Normal file
Binary file not shown.
BIN
Content/Resource/Spine/PlacedItems/Tree/Tree.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Tree/Tree.uasset
Normal file
Binary file not shown.
BIN
Content/Resource/Spine/PlacedItems/Tree/TreeData.uasset
Normal file
BIN
Content/Resource/Spine/PlacedItems/Tree/TreeData.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -16,7 +16,6 @@ void UBusyActorManagerSubSystem::OnWorldBeginPlay(UWorld& InWorld) {
|
||||
}
|
||||
|
||||
void UBusyActorManagerSubSystem::Deinitialize(){
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Luaʵ<61>ֵĺ<D6B5><C4BA><EFBFBD><EFBFBD>ܲ<EFBFBD><DCB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȳ<EFBFBD><C8B2>ṩ<EFBFBD><E1B9A9><EFBFBD>ٵ<EFBFBD>lua<75>ӿڰ<D3BF>
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
@ -26,7 +25,7 @@ FString UBusyActorManagerSubSystem::GetLuaFilePath_Implementation() const{
|
||||
|
||||
bool UBusyActorManagerSubSystem::GetLevelBaseConfig(FBusyLevelBaseConfig& config){
|
||||
FBusyLevelBaseConfig* Config;
|
||||
UDataTable *DataTable = UBusyGamePlayLibrary::GetGameDataTable("LevelBaseConfig");
|
||||
UDataTable *DataTable = UBusyGameplayLibrary::GetGameDataTable("LevelBaseConfig");
|
||||
if (!DataTable) return false;
|
||||
|
||||
Config = DataTable->FindRow<FBusyLevelBaseConfig>(
|
||||
|
||||
@ -11,7 +11,7 @@ static inline const UBusyDataAsset* GetGameAsset() {
|
||||
|
||||
template<typename RowStruct>
|
||||
static bool GetTableConfig(const FString& TableName, const FName& RowName, RowStruct& RowData) {
|
||||
UDataTable* Table = UBusyGamePlayLibrary::GetGameDataTable(TableName);
|
||||
UDataTable* Table = UBusyGameplayLibrary::GetGameDataTable(TableName);
|
||||
RowStruct* Config = Table->FindRow<RowStruct>(
|
||||
RowName,
|
||||
*FString::Printf(TEXT("GetTableConfig, %s"), *RowName.ToString()),
|
||||
@ -26,83 +26,59 @@ static bool GetTableConfig(const FString& TableName, const FName& RowName, RowSt
|
||||
}
|
||||
}
|
||||
|
||||
UDataTable* UBusyGamePlayLibrary::GetGameDataTable(const FString& TableName){
|
||||
UDataTable* UBusyGameplayLibrary::GetGameDataTable(const FString& TableName){
|
||||
const UBusyDataAsset* GameAsset = GetGameAsset();
|
||||
if (!GameAsset) return nullptr;
|
||||
auto Table = (GameAsset->DataTableMapping.Find(TableName));
|
||||
return Table ? Table->Get() : nullptr;
|
||||
}
|
||||
|
||||
UClass* UBusyGamePlayLibrary::GetGameClass(const FString& ClassName){
|
||||
UClass* UBusyGameplayLibrary::GetGameClass(const FString& ClassName){
|
||||
const UBusyDataAsset* GameAsset = GetGameAsset();
|
||||
if (!GameAsset) return nullptr;
|
||||
auto Class = (GameAsset->ClassPathMapping.Find(ClassName));
|
||||
return Class ? Class->Get() : nullptr;
|
||||
}
|
||||
|
||||
UClass* UBusyGamePlayLibrary::GetGameUIClass(const FString& ClassName){
|
||||
UClass* UBusyGameplayLibrary::GetGameUIClass(const FString& ClassName){
|
||||
const UBusyDataAsset* GameAsset = GetGameAsset();
|
||||
if (!GameAsset) return nullptr;
|
||||
auto Class = (GameAsset->UIPathMapping.Find(ClassName));
|
||||
return Class ? Class->Get() : nullptr;
|
||||
}
|
||||
|
||||
UWorld* UBusyGamePlayLibrary::K2_GetWorld(UObject* obj){
|
||||
return Cast<UWorld>(obj->GetOuter());
|
||||
UWorld* UBusyGameplayLibrary::K2_GetWorld(const UObject* UObj){
|
||||
return UObj->GetWorld();
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetLevelBaseConfig(const FName& RowName, FBusyLevelBaseConfig& RowData){
|
||||
bool UBusyGameplayLibrary::GetLevelBaseConfig(const FName& RowName, FBusyLevelBaseConfig& RowData){
|
||||
return GetTableConfig<FBusyLevelBaseConfig>(TEXT("LevelBaseConfig"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetLevelItemConfig(const FName& RowName, FBusyLevelItemConfig& RowData){
|
||||
bool UBusyGameplayLibrary::GetLevelItemConfig(const FName& RowName, FBusyLevelItemConfig& RowData){
|
||||
return GetTableConfig<FBusyLevelItemConfig>(TEXT("LevelItems"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetRoleConfig(const FName& RowName, FBusyRoleConfig& RowData){
|
||||
bool UBusyGameplayLibrary::GetRoleConfig(const FName& RowName, FBusyRoleConfig& RowData){
|
||||
return GetTableConfig<FBusyRoleConfig>(TEXT("RoleConfig"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetItemResourceConfig(const FName& RowName, FBusyLevelItemResourceConfig& RowData){
|
||||
bool UBusyGameplayLibrary::GetItemResourceConfig(const FName& RowName, FBusyLevelItemResourceConfig& RowData){
|
||||
return GetTableConfig<FBusyLevelItemResourceConfig>(TEXT("LevelItemResource"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetLevelItemDescription(const FName& RowName, FBusyLevelItemDescription& RowData){
|
||||
bool UBusyGameplayLibrary::GetLevelItemDescription(const FName& RowName, FBusyLevelItemDescription& RowData){
|
||||
return GetTableConfig<FBusyLevelItemDescription>(TEXT("LevelItemDesc"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetHomelandItemDescription(const FName& RowName, FBusyHomelandItemDescription& RowData){
|
||||
bool UBusyGameplayLibrary::GetHomelandItemDescription(const FName& RowName, FBusyHomelandItemDescription& RowData){
|
||||
return GetTableConfig<FBusyHomelandItemDescription>(TEXT("HomelandItemDesc"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetItemDescription(const FName& RowName, FBusyItemDescription& RowData){
|
||||
bool UBusyGameplayLibrary::GetItemDescription(const FName& RowName, FBusyItemDescription& RowData){
|
||||
return GetTableConfig<FBusyItemDescription>(TEXT("ItemDesc"), RowName, RowData);
|
||||
}
|
||||
|
||||
bool UBusyGamePlayLibrary::GetCookMaterialStateConfig(const FName& RowName, FBusyCookMaterialStateConfig& RowData){
|
||||
bool UBusyGameplayLibrary::GetCookMaterialStateConfig(const FName& RowName, FBusyCookMaterialStateConfig& RowData){
|
||||
return GetTableConfig<FBusyCookMaterialStateConfig>(TEXT("CookMaterialStateConfig"), RowName, RowData);
|
||||
}
|
||||
|
||||
FLuaBPVar UBusyGamePlayLibrary::CreateTextureBuffer(UObject* WorldContextObject){
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
auto DataTexture = UTexture2D::CreateTransient(512, 1, PF_R32_FLOAT);
|
||||
DataTexture->Filter = TF_Trilinear;
|
||||
DataTexture->AddressX = TA_Clamp;
|
||||
DataTexture->AddressY = TA_Clamp;
|
||||
DataTexture->UpdateResource();
|
||||
return ULuaBlueprintLibrary::CreateVarFromObject(WorldContextObject, DataTexture);
|
||||
}
|
||||
|
||||
void UBusyGamePlayLibrary::UpdateTextureBuffer(UTexture2D *DataTexture, TArray<float> FloatData){
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD>д<EFBFBD><D0B4>
|
||||
DataTexture->GetPlatformData()->Mips[0];
|
||||
FTexture2DMipMap& Mip = DataTexture->GetPlatformData()->Mips[0];
|
||||
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݸ<EFBFBD><DDB8>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD>
|
||||
FMemory::Memcpy(Data, FloatData.GetData(), FloatData.Num() * sizeof(float));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Mip.BulkData.Unlock();
|
||||
DataTexture->UpdateResource();
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Gas/BusyAbilitySystemComponent.h"
|
||||
|
||||
@ -7,11 +7,13 @@
|
||||
|
||||
ABusyPawnBase::ABusyPawnBase()
|
||||
{
|
||||
|
||||
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
|
||||
SpineRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SpineRoot"));
|
||||
SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
|
||||
SpineRenderComponent = CreateDefaultSubobject<USpineSkeletonRendererComponent>(TEXT("SpineRenderComponent"));
|
||||
SpineAnimationComponent = CreateDefaultSubobject<USpineSkeletonAnimationComponent>(TEXT("SpineAnimationComponent"));
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UBusyAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
MovementComponent = CreateDefaultSubobject<UBusyPawnMovement>(TEXT("MovementComponent"));
|
||||
|
||||
RootComponent = RootScene;
|
||||
@ -19,16 +21,14 @@ ABusyPawnBase::ABusyPawnBase()
|
||||
SphereComponent->SetupAttachment(SpineRoot);
|
||||
SpineRenderComponent->SetupAttachment(SpineRoot);
|
||||
|
||||
|
||||
SpineRoot->SetRelativeRotation(FRotator(0, 0, -90));
|
||||
|
||||
}
|
||||
|
||||
void ABusyPawnBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
SpineAnimationComponent->SetSkin(DefaultSkinName);
|
||||
SpineAnimationComponent->SetAnimation(0, DefaultAnimationName, true);
|
||||
// SpineAnimationComponent->SetSkin(DefaultSkinName);
|
||||
// SpineAnimationComponent->SetAnimation(0, DefaultAnimationName, true);
|
||||
}
|
||||
|
||||
void ABusyPawnBase::UpdateMoveDirection_Implementation(const FVector2D& InDirection)
|
||||
@ -40,3 +40,4 @@ float ABusyPawnBase::GetSpeed_Implementation()const
|
||||
{
|
||||
return 280;
|
||||
}
|
||||
|
||||
|
||||
24
Source/BusyRabbit/Private/Level/Actor/BusyStaticResource.cpp
Normal file
24
Source/BusyRabbit/Private/Level/Actor/BusyStaticResource.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "Level/Actor/BusyStaticResource.h"
|
||||
#include "SpineSkeletonRendererComponent.h"
|
||||
#include "SpineSkeletonAnimationComponent.h"
|
||||
|
||||
ABusyStaticResource::ABusyStaticResource()
|
||||
{
|
||||
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
|
||||
SpineRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SpineRoot"));
|
||||
|
||||
SpineRenderComponent = CreateDefaultSubobject<USpineSkeletonRendererComponent>("SpineRenderComponent");
|
||||
SpineAnimationComponent = CreateDefaultSubobject<USpineSkeletonAnimationComponent>("SpineAnimationComponent");
|
||||
|
||||
SetRootComponent(RootScene);
|
||||
SpineRoot->SetupAttachment(RootScene);
|
||||
SpineRenderComponent->SetupAttachment(SpineRoot);
|
||||
SpineRoot->SetRelativeRotation(FRotator(0, 0, -90));
|
||||
}
|
||||
|
||||
void ABusyStaticResource::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
SpineAnimationComponent->SetSkin("default");
|
||||
SpineAnimationComponent->SetAnimation(0, "idle", true);
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
#include "Level/Actor/Components/BusyAbilitySystemComponent.h"
|
||||
|
||||
FString UBusyAbilitySystemComponent::GetLuaFilePath_Implementation() const
|
||||
{
|
||||
return LuaFilePath;
|
||||
}
|
||||
@ -34,6 +34,7 @@ void UBusyPawnMovement::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
AActor* Owner = GetOwner();
|
||||
const IBusyMovable* Movable = Cast<IBusyMovable>(Owner);
|
||||
if (!Owner || !Movable) return;
|
||||
@ -61,7 +62,7 @@ void UBusyPawnMovement::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
}
|
||||
if (!NewLocation.Equals(CurrentLocation))
|
||||
{
|
||||
Owner->SetActorLocation(NewLocation, true);
|
||||
Owner->SetActorLocation(NewLocation, true);
|
||||
}
|
||||
Movable->Execute_UpdateMoveDirection(Owner, GetMoveDirection());
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "Level/BusyLevelItem.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "Gas/BusyAbilitySystemComponent.h"
|
||||
#include "BusyGamePlayLibrary.h"
|
||||
|
||||
ABusyLevelItem::ABusyLevelItem(): CurrentItemID("100001") {
|
||||
@ -16,7 +15,6 @@ ABusyLevelItem::ABusyLevelItem(): CurrentItemID("100001") {
|
||||
|
||||
PickBar = CreateDefaultSubobject<UWidgetComponent>(TEXT("PickBar"));
|
||||
LevelItemAttribute = CreateDefaultSubobject<UBusyLevelItemAttributeSet>("LevelItemAttribute");
|
||||
AbilityComponent = CreateDefaultSubobject<UBusyAbilitySystemComponent>("RoleAbility");
|
||||
|
||||
InitSprite();
|
||||
InitCapsule();
|
||||
@ -34,7 +32,7 @@ void ABusyLevelItem::OnConstruction(const FTransform& Transform){
|
||||
}
|
||||
|
||||
void ABusyLevelItem::BeginPlay(){
|
||||
UClass* cls = UBusyGamePlayLibrary::GetGameUIClass("PickBar");
|
||||
UClass* cls = UBusyGameplayLibrary::GetGameUIClass("PickBar");
|
||||
if (cls != nullptr) {
|
||||
PickBar->SetWidgetClass(cls);
|
||||
}
|
||||
@ -83,7 +81,7 @@ void ABusyLevelItem::InitPickBar(){
|
||||
}
|
||||
|
||||
void ABusyLevelItem::SetItemDisplay(const FName& ItemID){
|
||||
UDataTable* Resource = UBusyGamePlayLibrary::GetGameDataTable(TEXT("LevelItemResource"));
|
||||
UDataTable* Resource = UBusyGameplayLibrary::GetGameDataTable(TEXT("LevelItemResource"));
|
||||
|
||||
// 查物品信息
|
||||
FBusyLevelItemResourceConfig* ResourceConfig = Resource->FindRow<FBusyLevelItemResourceConfig>(
|
||||
@ -99,7 +97,7 @@ void ABusyLevelItem::SetItemDisplay(const FName& ItemID){
|
||||
|
||||
void ABusyLevelItem::SetLevelItemID(const FName& ItemID){
|
||||
// 取物品资源表
|
||||
UDataTable* ConfigTable = UBusyGamePlayLibrary::GetGameDataTable(TEXT("LevelItems"));
|
||||
UDataTable* ConfigTable = UBusyGameplayLibrary::GetGameDataTable(TEXT("LevelItems"));
|
||||
// 查物品信息
|
||||
FBusyLevelItemConfig* ItemConfig = ConfigTable->FindRow<FBusyLevelItemConfig>(
|
||||
ItemID, TEXT("ABusyLevelItem::SetLevelItemID"), true
|
||||
|
||||
@ -26,7 +26,7 @@ void ALevelItemReward::SetRewardID(const FName& CurrentRewardID){
|
||||
bool bIsFind = false;
|
||||
FBusyLevelItemDescription Desc;
|
||||
if (CurrentRewardID.IsNone()) return;
|
||||
bIsFind = UBusyGamePlayLibrary::GetLevelItemDescription(CurrentRewardID, Desc);
|
||||
bIsFind = UBusyGameplayLibrary::GetLevelItemDescription(CurrentRewardID, Desc);
|
||||
if (!bIsFind) return;
|
||||
this->RewardID = CurrentRewardID;
|
||||
this->Sprite->SetFlipbook(Desc.LevelResource.LoadSynchronous());
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
#include "Level/LevelPlayerController.h"
|
||||
#include "Level/Actor/BusyPlayerRole.h"
|
||||
#include "EnhancedInput/Public/EnhancedInputSubsystems.h"
|
||||
#include "Level/LevelPlayerState.h"
|
||||
#include "EnhancedInput/Public/EnhancedInputComponent.h"
|
||||
|
||||
|
||||
inline static void BindEnhancedInputAction(UEnhancedInputComponent* EnhancedInput, const UInputAction* Action, UObject* Target, const FName& Callback)
|
||||
{
|
||||
if (Action)
|
||||
{
|
||||
EnhancedInput->BindAction(Action, ETriggerEvent::Triggered, Target, Callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALevelPlayerController::ALevelPlayerController()
|
||||
{
|
||||
@ -15,18 +27,74 @@ void ALevelPlayerController::BeginPlay()
|
||||
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
|
||||
SetInputMode(InputMode);
|
||||
|
||||
// 注册输入
|
||||
const auto Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
|
||||
if (InputMapping && Subsystem)
|
||||
if (RoamingCameraClass)
|
||||
{
|
||||
Subsystem->AddMappingContext(InputMapping, 0);
|
||||
RoamingCameraActor = GetWorld()->SpawnActor<AActor>(RoamingCameraClass);
|
||||
}
|
||||
}
|
||||
|
||||
void ALevelPlayerController::SetupInputComponent()
|
||||
{
|
||||
Super::SetupInputComponent();
|
||||
// 注册输入
|
||||
if (!InputMapping || !InputComponent) return;
|
||||
|
||||
if (const auto Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
|
||||
{
|
||||
Subsystem->AddMappingContext(InputMapping, 0);
|
||||
}
|
||||
|
||||
UEnhancedInputComponent* EnhancedInput = CastChecked<UEnhancedInputComponent>(InputComponent);
|
||||
if (!EnhancedInput) return;
|
||||
|
||||
BindEnhancedInputAction(EnhancedInput, MoveAction, this, "OnMove");
|
||||
BindEnhancedInputAction(EnhancedInput, CameraDetachAction, this, "OnCameraDetach");
|
||||
BindEnhancedInputAction(EnhancedInput, PrimarySkillAction, this, "OnPrimarySkill");
|
||||
BindEnhancedInputAction(EnhancedInput, UltimateSkillAction, this, "OnUltimateSkill");
|
||||
}
|
||||
|
||||
void ALevelPlayerController::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (!bCameraDetached || !RoamingCameraActor) return;
|
||||
|
||||
AActor *ControlledRole = GetControlledRole();
|
||||
const IBusyMovable *Movable = Cast<IBusyMovable>(GetControlledRole());
|
||||
if (!Movable) return;
|
||||
|
||||
if (float CursorX, CursorY; GetMousePosition(CursorX, CursorY))
|
||||
{
|
||||
|
||||
// 获取视口大小
|
||||
int32 ViewportSizeX, ViewportSizeY;
|
||||
GetViewportSize(ViewportSizeX, ViewportSizeY);
|
||||
|
||||
// 计算屏幕中心
|
||||
const FVector2D ScreenCenter(ViewportSizeX / 2.0f, ViewportSizeY / 2.0f);
|
||||
|
||||
// 获取漫游相机位置
|
||||
const FVector CurrentLocation = RoamingCameraActor->GetActorLocation();
|
||||
|
||||
// 计算方向向量
|
||||
FVector Direction(CursorX - ScreenCenter.X, CursorY - ScreenCenter.Y, 0);
|
||||
|
||||
// 归一化
|
||||
Direction.Normalize();
|
||||
|
||||
const float MoveSpeed = Movable->Execute_GetSpeed(ControlledRole);
|
||||
const FVector MoveDistance = Direction * DeltaTime * MoveSpeed * RoamingSpeedFactor;
|
||||
|
||||
// 更新漫游相机位置
|
||||
RoamingCameraActor->SetActorLocation(CurrentLocation + MoveDistance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool ALevelPlayerController::GetCursorPosition(FVector2D& Position) const
|
||||
{
|
||||
float CursorX = 0.f, CursorY = 0.f;
|
||||
if (GetMousePosition(CursorX, CursorY))
|
||||
if (float CursorX, CursorY; GetMousePosition(CursorX, CursorY))
|
||||
{
|
||||
FVector WorldLocation, WorldDirection;
|
||||
if (DeprojectMousePositionToWorld(WorldLocation, WorldDirection))
|
||||
@ -42,11 +110,61 @@ void ALevelPlayerController::GetCursorHitResult(TArray<AActor*>& Results) const
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::GetControlledRole() const
|
||||
ABusyPlayerRole* ALevelPlayerController::GetControlledRole() const
|
||||
{
|
||||
if (const ALevelPlayerState* PS = GetPlayerState<ALevelPlayerState>())
|
||||
{
|
||||
return PS->GetControlledRole();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ALevelPlayerController::SwitchControlledRole(ABusyPlayerRole* Target)
|
||||
{
|
||||
this->SetViewTarget(Target);
|
||||
this->Possess(Target);
|
||||
}
|
||||
|
||||
void ALevelPlayerController::OnMove(const FInputActionValue& Value) const
|
||||
{
|
||||
AActor* ControlledRole = GetControlledRole();
|
||||
if (!ControlledRole) return;
|
||||
|
||||
IBusyControllable *Controllable = Cast<IBusyControllable>(ControlledRole);
|
||||
if (!Controllable) return;
|
||||
|
||||
if (FVector2D Position; GetCursorPosition(Position))
|
||||
{
|
||||
Controllable->Execute_OnMove(ControlledRole, Position);
|
||||
}
|
||||
}
|
||||
|
||||
void ALevelPlayerController::OnPrimarySkill(const FInputActionValue& Value) const
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::OnUltimateSkill(const FInputActionValue& Value) const
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::OnCameraDetach(const FInputActionValue& Value)
|
||||
{
|
||||
if (!RoamingCameraActor) return;
|
||||
|
||||
AActor* ControlledRole = GetControlledRole();
|
||||
if (!ControlledRole) return;
|
||||
|
||||
const FVector RoleLocation = ControlledRole->GetActorLocation();
|
||||
RoamingCameraActor->SetActorLocation(RoleLocation);
|
||||
|
||||
|
||||
if (Value.GetMagnitude() > 0)
|
||||
{
|
||||
bCameraDetached = true;
|
||||
SetViewTarget(RoamingCameraActor);
|
||||
}
|
||||
else
|
||||
{
|
||||
bCameraDetached = false;
|
||||
SetViewTarget(ControlledRole);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#include "Level/LevelPlayerState.h"
|
||||
|
||||
#include "Level/LevelPlayerController.h"
|
||||
// #include "Net/UnrealNetwork.h"
|
||||
#include "Level/Actor/BusyPlayerRole.h"
|
||||
|
||||
|
||||
@ -10,38 +9,57 @@ DEFINE_LOG_CATEGORY(LogLevelPlayerState);
|
||||
void ALevelPlayerState::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
UWorld* World = GetWorld();
|
||||
const auto PC = Cast<ALevelPlayerController>(GetPlayerController());
|
||||
|
||||
if (!World || !PC)
|
||||
{
|
||||
UE_LOG(LogLevelPlayerState, Error, TEXT("ALevelPlayerController::BeginPlay() failed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
FVector2D SpawnLocation = GetSpawnLocation();
|
||||
for (auto PawnClass : RoleClasses)
|
||||
{
|
||||
FTransform SpawnTransform;
|
||||
FActorSpawnParameters Params;
|
||||
SpawnTransform.SetLocation(FVector(SpawnLocation, 50));
|
||||
if (auto *NewRole = Cast<ABusyPlayerRole>(World->SpawnActor(PawnClass, nullptr, Params)))
|
||||
{
|
||||
Roles.Add(NewRole);
|
||||
}
|
||||
}
|
||||
|
||||
if (Roles.IsValidIndex(0))
|
||||
{
|
||||
PC->SwitchControlledRole(Roles[0]);
|
||||
}
|
||||
}
|
||||
//
|
||||
// void ALevelPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
// {
|
||||
// Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
//
|
||||
// DOREPLIFETIME(ALevelPlayerState, RoleRoster);
|
||||
// DOREPLIFETIME(ALevelPlayerState, ControlledRoleIndex);
|
||||
// }
|
||||
|
||||
AActor* ALevelPlayerState::CreateRoleRoster(class APlayerController* PlayerController)
|
||||
{
|
||||
UWorld* World = PlayerController->GetWorld();
|
||||
if (!World || !PlayerController)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FVector Location = FVector(GetSpawnLocation(), 10);
|
||||
FActorSpawnParameters SpawnParameters;
|
||||
|
||||
for (const auto RoleClass : RoleClasses)
|
||||
{
|
||||
SpawnParameters.Owner = PlayerController;
|
||||
ABusyPlayerRole* NewRole = World->SpawnActor<ABusyPlayerRole>(
|
||||
RoleClass, Location,
|
||||
FRotator(), SpawnParameters
|
||||
);
|
||||
RoleRoster.Add(NewRole);
|
||||
}
|
||||
|
||||
if (RoleRoster.IsValidIndex(0))
|
||||
{
|
||||
ControlledRoleIndex = 0;
|
||||
return RoleRoster[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlledRoleIndex = -1;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ABusyPlayerRole* ALevelPlayerState::GetControlledRole() const
|
||||
{
|
||||
if (Roles.IsValidIndex(ControlledRoleIndex))
|
||||
if (RoleRoster.IsValidIndex(ControlledRoleIndex))
|
||||
{
|
||||
return Roles[ControlledRoleIndex];
|
||||
return RoleRoster[ControlledRoleIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -1,275 +0,0 @@
|
||||
#include "Level/PaperTerrainMapActor.h"
|
||||
|
||||
#include "Level/TerrainGenerator.h"
|
||||
#include "Level/TerrainGeneratorBlueprintLibrary.h"
|
||||
#include "Paper2D/Classes/PaperTileMapComponent.h"
|
||||
#include "Paper2D/Classes/PaperTileMap.h"
|
||||
#include "Paper2D/Classes/PaperTileLayer.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
/* 相邻四格地形信息映射到TileSet的索引
|
||||
* 以左上、右上、左下、右下地形块存在为1,不存在为0, 从左到右构建一个4位二进制整数
|
||||
* 假设原始TileSet中,0号索引左上、右上、右下为空, 对应二进制整数为0x0010
|
||||
* 那么TileSet的这个索引与相邻数据对应关系就是[0] -> 2
|
||||
* 也就是DefaultNeighborDataToIdxMappings[2] = 0
|
||||
*/
|
||||
const static int DefaultNeighborDataToIdxMappings[16] = {
|
||||
12, 13, 0, 3, 8, 1, 14, 5, 15, 4, 11, 2, 9, 10, 7, 6
|
||||
};
|
||||
|
||||
APaperTerrainMapActor::APaperTerrainMapActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
// 创建根组件
|
||||
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
|
||||
|
||||
// 创建TileMap组件
|
||||
TileMapComponent = CreateDefaultSubobject<UPaperTileMapComponent>(TEXT("TileMapComponent"));
|
||||
TileMapComponent->SetupAttachment(RootComponent);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (bAutoGenerateOnBeginPlay)
|
||||
{
|
||||
GenerateTerrainMap();
|
||||
}
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::OnConstruction(const FTransform& Transform)
|
||||
{
|
||||
Super::OnConstruction(Transform);
|
||||
// 初始化地形生成器
|
||||
InitializeGenerator();
|
||||
// 初始化TileMap
|
||||
InitializeTileMap();
|
||||
|
||||
if (bAutoGenerateOnConstruction)
|
||||
{
|
||||
GenerateTerrainMap();
|
||||
}
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::GenerateTerrainMap()
|
||||
{
|
||||
if (!TerrainGenerator)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("TerrainGenerator is null!"));
|
||||
return;
|
||||
}
|
||||
if (!TerrainTileSetConfigs)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("TerrainTileSetConfigs is null!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成地形数据
|
||||
GeneratedTerrainData = TerrainGenerator->GenerateMap(MapWidth, MapHeight);
|
||||
|
||||
// 应用到TileMap
|
||||
ApplyTerrainToTileMap(GeneratedTerrainData);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Terrain map generated successfully! Size: %dx%d"), MapWidth, MapHeight);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::ClearMap()
|
||||
{
|
||||
if (!TileMapComponent || !TileMapComponent->TileMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除所有Tile - 通过重新创建空的TileMap来实??
|
||||
if (TileMapComponent->TileMap)
|
||||
{
|
||||
UPaperTileMap* NewTileMap = NewObject<UPaperTileMap>(this);
|
||||
NewTileMap->MapWidth = MapWidth;
|
||||
NewTileMap->MapHeight = MapHeight;
|
||||
NewTileMap->TileWidth = TileSize;
|
||||
NewTileMap->TileHeight = TileSize;
|
||||
|
||||
// 添加一个空图层
|
||||
UPaperTileLayer* NewLayer = NewObject<UPaperTileLayer>(NewTileMap);
|
||||
NewLayer->LayerName = FText::FromString("TerrainLayer");
|
||||
NewTileMap->TileLayers.Add(NewLayer);
|
||||
|
||||
TileMapComponent->SetTileMap(NewTileMap);
|
||||
}
|
||||
|
||||
GeneratedTerrainData.Empty();
|
||||
UE_LOG(LogTemp, Log, TEXT("Map cleared!"));
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void APaperTerrainMapActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
FName PropertyName = (PropertyChangedEvent.Property != nullptr)
|
||||
? PropertyChangedEvent.Property->GetFName()
|
||||
: NAME_None;
|
||||
|
||||
// 当地图尺寸或Tile大小改变时,重新初始化TileMap
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, MapWidth) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, MapHeight) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, TileSize) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, TileSetDataAsset))
|
||||
|
||||
{
|
||||
InitializeTileMap();
|
||||
GenerateTerrainMap();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void APaperTerrainMapActor::InitializeTileMap()
|
||||
{
|
||||
if (!TileMapComponent) return;
|
||||
|
||||
if (TileSetDataAsset)
|
||||
{
|
||||
TerrainTileSetConfigs = &TileSetDataAsset.Get()->TerrainTileSetConfigs;
|
||||
}
|
||||
else
|
||||
{
|
||||
TerrainTileSetConfigs = nullptr;
|
||||
}
|
||||
|
||||
// 创建TileMap
|
||||
UPaperTileMap *TileMap = NewObject<UPaperTileMap>(this);
|
||||
|
||||
// 设置TileMap参数
|
||||
TileMap->MapWidth = MapWidth;
|
||||
TileMap->MapHeight = MapHeight;
|
||||
TileMap->TileWidth = TileSize;
|
||||
TileMap->TileHeight = TileSize;
|
||||
|
||||
// 创建四层layer
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
UPaperTileLayer* NewLayer = NewObject<UPaperTileLayer>(TileMap);
|
||||
NewLayer->LayerName = FText::FromString(
|
||||
FString::Format(TEXT("TerrainLayer{0}"), {FString::FromInt(i)})
|
||||
);
|
||||
NewLayer->ResizeMap(MapWidth, MapHeight);
|
||||
TileMap->TileLayers.Add(NewLayer);
|
||||
}
|
||||
|
||||
TileMapComponent->SetTileMap(TileMap);
|
||||
UE_LOG(LogTemp, Log, TEXT("TileMap initialized: %dx%d, TileSize: %d"), MapWidth, MapHeight, TileSize);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::InitializeGenerator()
|
||||
{
|
||||
// 创建地形生成器
|
||||
if (GeneratorClass.Get())
|
||||
{
|
||||
TerrainGenerator = NewObject<UTerrainGeneratorBase>(this, GeneratorClass.Get());
|
||||
UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(TerrainGenerator);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::APaperTerrainMapActor, GeneratorClass is nullptr"));
|
||||
}
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData) const
|
||||
{
|
||||
if (!TileMapComponent || !TileMapComponent->TileMap || TerrainData.Num() != MapWidth * MapHeight)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Cannot apply terrain data to TileMap!"));
|
||||
return;
|
||||
}
|
||||
if (TileMapComponent->TileMap->TileLayers.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("No tile layers in TileMap!"));
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayTag TerrainTags[4];
|
||||
const int32 LimitedWidth = MapWidth - 1;
|
||||
const int32 LimitedHeight = MapHeight - 1;
|
||||
|
||||
for (int32 Y = 0; Y < LimitedHeight; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < LimitedWidth; X++)
|
||||
{
|
||||
TerrainTags[0] = TerrainData[Y * MapWidth + X];
|
||||
TerrainTags[1] = TerrainData[Y * MapWidth + X + 1];
|
||||
TerrainTags[2] = TerrainData[(Y + 1) * MapWidth + X];
|
||||
TerrainTags[3] = TerrainData[(Y + 1) * MapWidth + X + 1];
|
||||
DrawTile(X, Y, TerrainTags);
|
||||
}
|
||||
}
|
||||
UE_LOG(LogTemp, Log, TEXT("Terrain data applied to TileMap!"));
|
||||
}
|
||||
|
||||
static inline int32 GetNeighborInfo(const FGameplayTag& TargetTag, FGameplayTag TerrainTags[4])
|
||||
{
|
||||
int32 NeighborInfo = 0;
|
||||
NeighborInfo += (TargetTag == TerrainTags[0] ? 1 : 0) << 3;
|
||||
NeighborInfo += (TargetTag == TerrainTags[1] ? 1 : 0) << 2;
|
||||
NeighborInfo += (TargetTag == TerrainTags[2] ? 1 : 0) << 1;
|
||||
NeighborInfo += (TargetTag == TerrainTags[3] ? 1 : 0);
|
||||
return NeighborInfo;
|
||||
}
|
||||
|
||||
static inline void GetSortedLayerDrawingInfo(FGameplayTag **LayerTerrainTags, int32 *LayerNeighborInfo, FGameplayTag TerrainTags[4])
|
||||
{
|
||||
// 返回0-3层每层应该画哪种地形格子,以及这个格子的哪种形状,可能需要排序
|
||||
LayerTerrainTags[0] = &TerrainTags[0];
|
||||
LayerTerrainTags[1] = &TerrainTags[1];
|
||||
LayerTerrainTags[2] = &TerrainTags[2];
|
||||
LayerTerrainTags[3] = &TerrainTags[3];
|
||||
LayerNeighborInfo[0] = GetNeighborInfo(TerrainTags[0], TerrainTags);
|
||||
LayerNeighborInfo[1] = GetNeighborInfo(TerrainTags[1], TerrainTags);
|
||||
LayerNeighborInfo[2] = GetNeighborInfo(TerrainTags[2], TerrainTags);
|
||||
LayerNeighborInfo[3] = GetNeighborInfo(TerrainTags[3], TerrainTags);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::DrawTile(const int32 X, const int32 Y, FGameplayTag TerrainTags[4])const
|
||||
{
|
||||
int32 LayerNeighborInfo[4];
|
||||
FGameplayTag *LayerTerrainTags[4];
|
||||
UPaperTileMap* TileMap = TileMapComponent->TileMap;
|
||||
GetSortedLayerDrawingInfo(LayerTerrainTags, LayerNeighborInfo, TerrainTags);
|
||||
|
||||
for (int32 Index = 0; Index < 4; ++Index)
|
||||
{
|
||||
int32 TileIndex = 0;
|
||||
FPaperTileInfo TileInfo;
|
||||
|
||||
const FTerrainTileSetConfig *TileSetConfig = TerrainTileSetConfigs->Find(*LayerTerrainTags[Index]);
|
||||
if (!TileSetConfig)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::DrawTile TileSetConfig not found: %s"), *LayerTerrainTags[Index]->GetTagName().ToString());
|
||||
continue;
|
||||
}
|
||||
const int32 NeighborInfo = LayerNeighborInfo[Index];
|
||||
if (TileSetConfig->bNeedOverrideMappings)
|
||||
{
|
||||
if (NeighborInfo < TileSetConfig->NeighborDataToIdxMappings.Num())
|
||||
{
|
||||
TileIndex = TileSetConfig->NeighborDataToIdxMappings[NeighborInfo];
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::DrawTile Neighbor Data not find: %s, %d"),
|
||||
*LayerTerrainTags[Index]->GetTagName().ToString(), NeighborInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TileIndex = DefaultNeighborDataToIdxMappings[NeighborInfo];
|
||||
}
|
||||
UPaperTileLayer* TerrainLayer = TileMap->TileLayers[Index];
|
||||
TileInfo.TileSet = TileSetConfig->TileSet;
|
||||
TileInfo.PackedTileIndex = TileIndex;
|
||||
TerrainLayer->SetCell(X, Y, TileInfo);
|
||||
}
|
||||
}
|
||||
@ -1,220 +0,0 @@
|
||||
#include "Level/TerrainGenerator.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "Math/RandomStream.h"
|
||||
|
||||
UTerrainGenerator::UTerrainGenerator()
|
||||
{
|
||||
NextHandle = 0;
|
||||
}
|
||||
|
||||
|
||||
bool UTerrainGenerator::GenerateWithNodes(TArray<FGameplayTag>& Map, int32 Width, int32 Height, const TArray<int32>& ValidLeafNodes)
|
||||
{
|
||||
// 使用区域生长算法生成基础地图
|
||||
RegionGrowing(Map, Width, Height, ValidLeafNodes);
|
||||
|
||||
// 应用柏林噪声进行平滑处理
|
||||
ApplyPerlinNoiseSmoothing(Map, Width, Height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UTerrainGenerator::RegionGrowing(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const
|
||||
{
|
||||
if (ValidLeafNodes.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FRandomStream RandomStream(FMath::Rand());
|
||||
|
||||
// 计算总权重
|
||||
float TotalWeight = 0.0f;
|
||||
for (int32 Handle : ValidLeafNodes)
|
||||
{
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
TotalWeight += Node->Weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (TotalWeight <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建权重分布
|
||||
TArray<float> WeightDistribution;
|
||||
TArray<int32> HandleDistribution;
|
||||
float CurrentWeight = 0.0f;
|
||||
|
||||
for (int32 Handle : ValidLeafNodes)
|
||||
{
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
CurrentWeight += Node->Weight / TotalWeight;
|
||||
WeightDistribution.Add(CurrentWeight);
|
||||
HandleDistribution.Add(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
// 区域生长算法
|
||||
TArray<int32> RegionSizes;
|
||||
RegionSizes.SetNum(ValidLeafNodes.Num());
|
||||
|
||||
int32 TotalCells = Width * Height;
|
||||
for (int32 i = 0; i < ValidLeafNodes.Num(); i++)
|
||||
{
|
||||
float Ratio = 0.0f;
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(ValidLeafNodes[i]))
|
||||
{
|
||||
Ratio = Node->Weight / TotalWeight;
|
||||
}
|
||||
RegionSizes[i] = FMath::CeilToInt(TotalCells * Ratio);
|
||||
}
|
||||
|
||||
// 初始化地图为无效标签
|
||||
FGameplayTag InvalidTag;
|
||||
for (int32 i = 0; i < Map.Num(); i++)
|
||||
{
|
||||
Map[i] = InvalidTag;
|
||||
}
|
||||
|
||||
// 从随机点开始生长
|
||||
for (int32 RegionIndex = 0; RegionIndex < ValidLeafNodes.Num(); RegionIndex++)
|
||||
{
|
||||
int32 Handle = ValidLeafNodes[RegionIndex];
|
||||
FGameplayTag TerrainTag = GetFinalTerrainTag(Handle);
|
||||
int32 TargetSize = RegionSizes[RegionIndex];
|
||||
int32 CurrentSize = 0;
|
||||
|
||||
// 寻找起始点
|
||||
int32 StartX, StartY;
|
||||
do
|
||||
{
|
||||
StartX = RandomStream.RandRange(0, Width - 1);
|
||||
StartY = RandomStream.RandRange(0, Height - 1);
|
||||
} while (Map[StartY * Width + StartX].IsValid());
|
||||
|
||||
// 使用队列进行区域生长
|
||||
TQueue<FIntPoint> Queue;
|
||||
Queue.Enqueue(FIntPoint(StartX, StartY));
|
||||
Map[StartY * Width + StartX] = TerrainTag;
|
||||
CurrentSize++;
|
||||
|
||||
while (!Queue.IsEmpty() && CurrentSize < TargetSize)
|
||||
{
|
||||
FIntPoint CurrentPoint;
|
||||
Queue.Dequeue(CurrentPoint);
|
||||
|
||||
// 检查四个方向
|
||||
TArray<FIntPoint> Directions = {
|
||||
FIntPoint(1, 0), FIntPoint(-1, 0),
|
||||
FIntPoint(0, 1), FIntPoint(0, -1)
|
||||
};
|
||||
|
||||
for (const FIntPoint& Dir : Directions)
|
||||
{
|
||||
FIntPoint Neighbor = CurrentPoint + Dir;
|
||||
|
||||
if (Neighbor.X >= 0 && Neighbor.X < Width &&
|
||||
Neighbor.Y >= 0 && Neighbor.Y < Height)
|
||||
{
|
||||
int32 Index = Neighbor.Y * Width + Neighbor.X;
|
||||
|
||||
if (!Map[Index].IsValid())
|
||||
{
|
||||
Map[Index] = TerrainTag;
|
||||
Queue.Enqueue(Neighbor);
|
||||
CurrentSize++;
|
||||
|
||||
if (CurrentSize >= TargetSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 填充剩余的空格
|
||||
for (int32 i = 0; i < Map.Num(); i++)
|
||||
{
|
||||
if (!Map[i].IsValid())
|
||||
{
|
||||
float RandValue = RandomStream.FRand();
|
||||
for (int32 j = 0; j < WeightDistribution.Num(); j++)
|
||||
{
|
||||
if (RandValue <= WeightDistribution[j])
|
||||
{
|
||||
Map[i] = GetFinalTerrainTag(HandleDistribution[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainGenerator::ApplyPerlinNoiseSmoothing(TArray<FGameplayTag>& Map, int32 Width, int32 Height) const
|
||||
{
|
||||
FRandomStream RandomStream(FMath::Rand());
|
||||
float NoiseScale = 0.1f;
|
||||
|
||||
for (int32 Y = 0; Y < Height; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < Width; X++)
|
||||
{
|
||||
int32 Index = Y * Width + X;
|
||||
|
||||
// 计算柏林噪声值
|
||||
float NoiseValue = FMath::PerlinNoise2D(FVector2D(X * NoiseScale, Y * NoiseScale));
|
||||
NoiseValue = (NoiseValue + 1.0f) / 2.0f; // 映射到 0-1
|
||||
|
||||
// 50%概率根据噪声调整地形
|
||||
if (RandomStream.FRand() < 0.5f && NoiseValue > 0.7f)
|
||||
{
|
||||
// 查找周围最多的地形
|
||||
TMap<FGameplayTag, int32> NeighborCount;
|
||||
|
||||
for (int32 dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
for (int32 dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
|
||||
int32 NX = X + dx;
|
||||
int32 NY = Y + dy;
|
||||
|
||||
if (NX >= 0 && NX < Width && NY >= 0 && NY < Height)
|
||||
{
|
||||
FGameplayTag NeighborTag = Map[NY * Width + NX];
|
||||
NeighborCount.FindOrAdd(NeighborTag)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 找到最多的邻居地形
|
||||
FGameplayTag MostCommonTag;
|
||||
int32 MaxCount = 0;
|
||||
|
||||
for (const auto& Pair : NeighborCount)
|
||||
{
|
||||
if (Pair.Value > MaxCount)
|
||||
{
|
||||
MaxCount = Pair.Value;
|
||||
MostCommonTag = Pair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxCount > 0)
|
||||
{
|
||||
Map[Index] = MostCommonTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
#include "Level/TerrainGeneratorBlueprintLibrary.h"
|
||||
#include "Level/TerrainGenerator.h"
|
||||
#include "GameplayTagsManager.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
UTerrainGenerator* UTerrainGeneratorBlueprintLibrary::CreateTerrainGenerator()
|
||||
{
|
||||
return NewObject<UTerrainGenerator>();
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(UTerrainGeneratorBase* Generator)
|
||||
{
|
||||
if (!Generator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 定义地形标签
|
||||
FGameplayTag ForestTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Forest"));
|
||||
FGameplayTag GrasslandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Grassland"));
|
||||
FGameplayTag WaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water"));
|
||||
FGameplayTag SwampTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp"));
|
||||
FGameplayTag LandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Land"));
|
||||
|
||||
FGameplayTag SwampLandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp.Land"));
|
||||
FGameplayTag SwampWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp.Water"));
|
||||
FGameplayTag ShallowWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water.Shallow"));
|
||||
FGameplayTag DeepWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water.Deep"));
|
||||
|
||||
// 添加父地形
|
||||
int32 ForestHandle = Generator->AddTerrain(ForestTag);
|
||||
int32 GrasslandHandle = Generator->AddTerrain(GrasslandTag);
|
||||
int32 WaterHandle = Generator->AddTerrain(WaterTag);
|
||||
int32 SwampHandle = Generator->AddTerrain(SwampTag);
|
||||
int32 LandHandle = Generator->AddTerrain(LandTag);
|
||||
|
||||
// 添加子地形
|
||||
int32 SwampLandHandle = Generator->AddTerrain(SwampLandTag);
|
||||
int32 SwampWaterHandle = Generator->AddTerrain(SwampWaterTag);
|
||||
int32 ShallowWaterHandle = Generator->AddTerrain(ShallowWaterTag);
|
||||
int32 DeepWaterHandle = Generator->AddTerrain(DeepWaterTag);
|
||||
|
||||
// 绑定父子关系
|
||||
Generator->BindChildTerrain(SwampHandle, {SwampLandHandle, SwampWaterHandle});
|
||||
Generator->BindChildTerrain(WaterHandle, {ShallowWaterHandle, DeepWaterHandle});
|
||||
|
||||
// 设置概率
|
||||
Generator->SetProbability(ForestHandle, 0.8f); // 森林出现概率80%
|
||||
Generator->SetProbability(GrasslandHandle, 0.9f); // 草地出现概率90%
|
||||
Generator->SetProbability(WaterHandle, 0.7f); // 水体出现概率70%
|
||||
Generator->SetProbability(SwampHandle, 0.6f); // 沼泽出现概率60%
|
||||
Generator->SetProbability(LandHandle, 1.0f); // 土地总是出现
|
||||
|
||||
// 设置权重
|
||||
Generator->SetWeight(ForestHandle, 9.0f); // 森林权重9
|
||||
Generator->SetWeight(GrasslandHandle, 5.0f); // 草地权重5
|
||||
Generator->SetWeight(LandHandle, 3.0f); // 土地权重3
|
||||
|
||||
Generator->SetWeight(SwampLandHandle, 1.0f); // 沼泽陆地权重1
|
||||
Generator->SetWeight(SwampWaterHandle, 2.0f); // 沼泽水域权重2
|
||||
|
||||
Generator->SetWeight(ShallowWaterHandle, 3.0f); // 浅水权重3
|
||||
Generator->SetWeight(DeepWaterHandle, 1.0f); // 深水权重1
|
||||
|
||||
// 设置互斥关系
|
||||
Generator->SetExclusive({ForestHandle, SwampWaterHandle}); // 森林和沼泽水域互斥
|
||||
}
|
||||
|
||||
TArray<FGameplayTag> UTerrainGeneratorBlueprintLibrary::GenerateMap(
|
||||
UTerrainGenerator* Generator,
|
||||
int32 Width,
|
||||
int32 Height)
|
||||
{
|
||||
TArray<FGameplayTag> Result;
|
||||
|
||||
if (!Generator)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
// 直接返回一维地图数据
|
||||
return Generator->GenerateMap(Width, Height);
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBlueprintLibrary::GetMapDimensions(
|
||||
UTerrainGenerator* Generator,
|
||||
int32& Width,
|
||||
int32& Height)
|
||||
{
|
||||
Width = 256;
|
||||
Height = 256;
|
||||
|
||||
if (Generator)
|
||||
{
|
||||
// 这里可以根据需要从生成器获取实际的尺寸信息
|
||||
// 目前返回默认值
|
||||
}
|
||||
}
|
||||
|
||||
FString UTerrainGeneratorBlueprintLibrary::GetTerrainDisplayName(const FGameplayTag& TerrainTag)
|
||||
{
|
||||
if (!TerrainTag.IsValid())
|
||||
{
|
||||
return TEXT("无效地形");
|
||||
}
|
||||
|
||||
FString TagName = TerrainTag.ToString();
|
||||
|
||||
// 从标签中提取显示名称
|
||||
if (TagName.StartsWith(TEXT("Terrain.")))
|
||||
{
|
||||
TagName.RemoveFromStart(TEXT("Terrain."));
|
||||
}
|
||||
|
||||
// 替换下划线为空格
|
||||
TagName.ReplaceCharInline(TEXT('.'), TEXT(' '));
|
||||
TagName.ReplaceCharInline(TEXT('_'), TEXT(' '));
|
||||
|
||||
// 首字母大写
|
||||
if (TagName.Len() > 0)
|
||||
{
|
||||
TagName[0] = FChar::ToUpper(TagName[0]);
|
||||
}
|
||||
|
||||
return TagName;
|
||||
}
|
||||
|
||||
bool UTerrainGeneratorBlueprintLibrary::IsValidTerrainTag(const FGameplayTag& TerrainTag)
|
||||
{
|
||||
return TerrainTag.IsValid();
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
#include "Level/TerrainGenerator.h"
|
||||
#include "GameplayTagsManager.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
// 测试地形生成器功能
|
||||
void TestTerrainGenerator()
|
||||
{
|
||||
// 创建地形生成器实例
|
||||
UTerrainGenerator* Generator = NewObject<UTerrainGenerator>();
|
||||
|
||||
// 定义地形标签
|
||||
FGameplayTag ForestTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Forest"));
|
||||
FGameplayTag GrasslandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Grassland"));
|
||||
FGameplayTag WaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water"));
|
||||
FGameplayTag SwampTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp"));
|
||||
FGameplayTag LandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Land"));
|
||||
|
||||
FGameplayTag SwampLandTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp.Land"));
|
||||
FGameplayTag SwampWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp.Water"));
|
||||
FGameplayTag ShallowWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water.Shallow"));
|
||||
FGameplayTag DeepWaterTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water.Deep"));
|
||||
|
||||
// 添加父地形
|
||||
int32 ForestHandle = Generator->AddTerrain(ForestTag);
|
||||
int32 GrasslandHandle = Generator->AddTerrain(GrasslandTag);
|
||||
int32 WaterHandle = Generator->AddTerrain(WaterTag);
|
||||
int32 SwampHandle = Generator->AddTerrain(SwampTag);
|
||||
int32 LandHandle = Generator->AddTerrain(LandTag);
|
||||
|
||||
// 添加子地形
|
||||
int32 SwampLandHandle = Generator->AddTerrain(SwampLandTag);
|
||||
int32 SwampWaterHandle = Generator->AddTerrain(SwampWaterTag);
|
||||
int32 ShallowWaterHandle = Generator->AddTerrain(ShallowWaterTag);
|
||||
int32 DeepWaterHandle = Generator->AddTerrain(DeepWaterTag);
|
||||
|
||||
// 绑定父子关系
|
||||
Generator->BindChildTerrain(SwampHandle, {SwampLandHandle, SwampWaterHandle});
|
||||
Generator->BindChildTerrain(WaterHandle, {ShallowWaterHandle, DeepWaterHandle});
|
||||
|
||||
// 设置概率
|
||||
Generator->SetProbability(ForestHandle, 1.0f);
|
||||
Generator->SetProbability(GrasslandHandle, 0.9f);
|
||||
Generator->SetProbability(WaterHandle, 0.7f);
|
||||
Generator->SetProbability(SwampHandle, 1.0f); // 设置为1.0确保沼泽总是出现
|
||||
Generator->SetProbability(LandHandle, 1.0f);
|
||||
|
||||
// 设置权重
|
||||
Generator->SetWeight(ForestHandle, 9.0f);
|
||||
Generator->SetWeight(GrasslandHandle, 5.0f);
|
||||
Generator->SetWeight(LandHandle, 3.0f);
|
||||
Generator->SetWeight(SwampHandle, 2.0f); // 为沼泽父节点设置权重
|
||||
|
||||
Generator->SetWeight(SwampLandHandle, 1.0f);
|
||||
Generator->SetWeight(SwampWaterHandle, 2.0f);
|
||||
|
||||
Generator->SetWeight(ShallowWaterHandle, 3.0f);
|
||||
Generator->SetWeight(DeepWaterHandle, 1.0f);
|
||||
|
||||
// 设置互斥关系
|
||||
Generator->SetExclusive({ForestHandle, SwampWaterHandle});
|
||||
|
||||
// 生成地图
|
||||
TArray<FGameplayTag> Map = Generator->GenerateMap(64, 64);
|
||||
|
||||
// 统计地形分布
|
||||
TMap<FGameplayTag, int32> TerrainCount;
|
||||
for (const FGameplayTag& Tag : Map)
|
||||
{
|
||||
TerrainCount.FindOrAdd(Tag)++;
|
||||
}
|
||||
|
||||
// 输出统计结果
|
||||
UE_LOG(LogTemp, Log, TEXT("=== 地形生成统计 ==="));
|
||||
UE_LOG(LogTemp, Log, TEXT("地图大小: %dx%d"), 64, 64);
|
||||
UE_LOG(LogTemp, Log, TEXT("总格子数: %d"), Map.Num());
|
||||
|
||||
for (const auto& Pair : TerrainCount)
|
||||
{
|
||||
float Percentage = (float)Pair.Value / Map.Num() * 100.0f;
|
||||
UE_LOG(LogTemp, Log, TEXT("%s: %d (%.1f%%)"),
|
||||
*Pair.Key.ToString(), Pair.Value, Percentage);
|
||||
}
|
||||
|
||||
// 验证互斥关系
|
||||
bool HasForest = TerrainCount.Contains(ForestTag);
|
||||
bool HasSwampWater = TerrainCount.Contains(SwampWaterTag);
|
||||
|
||||
if (HasForest && HasSwampWater)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("警告: 森林和沼泽水域同时出现,互斥关系可能未正确工作"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("互斥关系验证: 通过"));
|
||||
}
|
||||
|
||||
// 验证父子关系
|
||||
bool HasSwamp = TerrainCount.Contains(SwampTag);
|
||||
bool HasSwampChildren = TerrainCount.Contains(SwampLandTag) || TerrainCount.Contains(SwampWaterTag);
|
||||
|
||||
if (HasSwamp && !HasSwampChildren)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("警告: 沼泽出现但没有子地形"));
|
||||
}
|
||||
else if (!HasSwamp && HasSwampChildren)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("警告: 沼泽子地形出现但沼泽未出现"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("父子关系验证: 通过"));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("=== 测试完成 ==="));
|
||||
}
|
||||
|
||||
// 控制台命令用于测试
|
||||
static FAutoConsoleCommand TestTerrainGeneratorCommand(
|
||||
TEXT("TestTerrainGenerator"),
|
||||
TEXT("测试地形生成器功能"),
|
||||
FConsoleCommandDelegate::CreateStatic(TestTerrainGenerator)
|
||||
);
|
||||
@ -10,7 +10,6 @@
|
||||
#include "Role/BusyRoleMovement.h"
|
||||
#include "Role/RoleAnimation.h"
|
||||
#include "BusyGamePlayLibrary.h"
|
||||
#include "Gas/BusyAbilitySystemComponent.h"
|
||||
#include "Components/InventoryComponent.h"
|
||||
#include "Core/PW_AbilitySystemComponent.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
@ -60,7 +59,7 @@ void ABusyRole::Tick(float InDeltaSeconds){
|
||||
}
|
||||
|
||||
void ABusyRole::SetRole(const FName& Name){
|
||||
UDataTable* Table = UBusyGamePlayLibrary::GetGameDataTable("RoleConfig");
|
||||
UDataTable* Table = UBusyGameplayLibrary::GetGameDataTable("RoleConfig");
|
||||
if (Table == nullptr) {
|
||||
return;
|
||||
}
|
||||
@ -71,7 +70,7 @@ void ABusyRole::SetRole(const FName& Name){
|
||||
RoleName = Name;
|
||||
RoleConfig = *Row;
|
||||
|
||||
// <20><><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20><><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (RoleAttribute) {
|
||||
RoleAttribute->InitHealth(Row->Health);
|
||||
RoleAttribute->InitHunger(Row->Hunger);
|
||||
@ -79,7 +78,7 @@ void ABusyRole::SetRole(const FName& Name){
|
||||
RoleAttribute->RegisterCustomAttribute();
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>ü<EFBFBD><C3BC><EFBFBD>
|
||||
// <20><><EFBFBD>ü<EFBFBD><C3BC><EFBFBD>
|
||||
for (UClass* AbilityClass : Row->DefaultAbilities) {
|
||||
if (AbilityClass) {
|
||||
RoleAbility->GiveAbility(FGameplayAbilitySpec(AbilityClass));
|
||||
|
||||
@ -62,15 +62,13 @@ void URoleAnimation::PlayPickAnimation(const FName& ItemName, ERoleMoveDirection
|
||||
|
||||
|
||||
bool URoleAnimation::GetOwnerRoleInfo(UPaperFlipbookComponent*& Sprite, FBusyRoleAnimationData*& AnimationData){
|
||||
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>owner
|
||||
ABusyRole* Role = Cast<ABusyRole>(this->GetOwner());
|
||||
if (Role == nullptr) {
|
||||
UE_LOG(LogRoleAnimation, Error, TEXT("URoleAnimation::GetPaperFilpBookComponent, Can't get Role"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>owner<65><72><EFBFBD><EFBFBD>
|
||||
UDataTable* DataTable = UBusyGamePlayLibrary::GetGameDataTable("RoleAnimation");
|
||||
UDataTable* DataTable = UBusyGameplayLibrary::GetGameDataTable("RoleAnimation");
|
||||
if (DataTable == nullptr) {
|
||||
UE_LOG(LogRoleAnimation, Error, TEXT("URoleAnimation::GetOwnerRoleInfo, Can't get Animation DataTable"))
|
||||
return false;
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UBusyGamePlayLibrary : public UBlueprintFunctionLibrary
|
||||
class BUSYRABBIT_API UBusyGameplayLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
@ -33,7 +33,7 @@ public:
|
||||
static UClass* GetGameUIClass(const FString& ClassName);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static UWorld* K2_GetWorld(UObject* obj);
|
||||
static UWorld* K2_GetWorld(const UObject* UObj);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static bool GetLevelBaseConfig(const FName& RowName, FBusyLevelBaseConfig& RowData);
|
||||
@ -58,12 +58,5 @@ public:
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static bool GetCookMaterialStateConfig(const FName& RowName, FBusyCookMaterialStateConfig& RowData);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static FLuaBPVar CreateTextureBuffer(UObject* WorldContextObject);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static void UpdateTextureBuffer(UTexture2D* DataTexture, TArray<float> FloatData);
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "BusyAbilitySystemComponent.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UBusyAbilitySystemComponent : public UAbilitySystemComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "LuaPawn.h"
|
||||
#include "Level/Actor/Components/BusyPawnMovement.h"
|
||||
#include "Level/Actor/Components/BusyAbilitySystemComponent.h"
|
||||
#include "BusyPawnBase.generated.h"
|
||||
|
||||
|
||||
@ -8,6 +9,7 @@ class USphereComponent;
|
||||
class USpineBoneFollowerComponent;
|
||||
class USpineSkeletonRendererComponent;
|
||||
class USpineSkeletonAnimationComponent;
|
||||
class UBusyPawnMovementComponent;
|
||||
|
||||
|
||||
UCLASS()
|
||||
@ -22,7 +24,7 @@ public:
|
||||
virtual void UpdateMoveDirection_Implementation(const FVector2D& InDirection) override;
|
||||
|
||||
virtual float GetSpeed_Implementation()const override;
|
||||
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USceneComponent> RootScene; //场景根组件
|
||||
@ -44,7 +46,13 @@ protected:
|
||||
TObjectPtr<USpineSkeletonAnimationComponent> SpineAnimationComponent;
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*-----------------------------GAS相关--------------------------------*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<UBusyAbilitySystemComponent> AbilitySystemComponent;
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
public:
|
||||
/*-------------------------------移动组件------------------------------*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<UBusyPawnMovement> MovementComponent;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#include "BusyPawnBase.h"
|
||||
#include "Level/LevelPlayerController.h"
|
||||
#include "BusyPlayerRole.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ABusyPlayerRole : public ABusyPawnBase
|
||||
class ABusyPlayerRole : public ABusyPawnBase, public IBusyControllable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
@ -12,21 +13,9 @@ public:
|
||||
|
||||
protected:
|
||||
/*--------------------相机相关--------------------------*/
|
||||
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class USpringArmComponent> SpringArmComponent;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UCameraComponent> CameraComponent;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UBusyRoleMovement> Movement; // 移动组件
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UPW_AbilitySystemComponent> RoleAbility; // 技能组件
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TObjectPtr<class URoleAnimation> RoleAnimation;
|
||||
|
||||
};
|
||||
|
||||
33
Source/BusyRabbit/Public/Level/Actor/BusyStaticResource.h
Normal file
33
Source/BusyRabbit/Public/Level/Actor/BusyStaticResource.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "LuaActor.h"
|
||||
#include "BusyStaticResource.generated.h"
|
||||
|
||||
class USpineSkeletonRendererComponent;
|
||||
class USpineSkeletonAnimationComponent;
|
||||
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class ABusyStaticResource:public ALuaActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ABusyStaticResource();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere)
|
||||
TObjectPtr<USceneComponent> RootScene; //场景根组件
|
||||
|
||||
/*----------------------------spine相关组件----------------------------*/
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USceneComponent> SpineRoot;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USpineSkeletonRendererComponent> SpineRenderComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<USpineSkeletonAnimationComponent> SpineAnimationComponent;
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "slua.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "BusyAbilitySystemComponent.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UBusyAbilitySystemComponent : public UAbilitySystemComponent, public ILuaOverriderInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual FString GetLuaFilePath_Implementation() const override;
|
||||
|
||||
|
||||
protected:
|
||||
FString LuaFilePath;
|
||||
};
|
||||
@ -37,9 +37,6 @@ public:
|
||||
FVector2D GetMoveDirection()const;
|
||||
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)override;
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
float MoveSpeed = 400;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
|
||||
@ -3,6 +3,33 @@
|
||||
#include "LuaPlayerController.h"
|
||||
#include "LevelPlayerController.generated.h"
|
||||
|
||||
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UBusyControllable: public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
|
||||
class IBusyControllable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
// 角色移动
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void OnMove(const FVector2D& Location);
|
||||
|
||||
// 角色普通技能
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void OnPrimarySkill();
|
||||
|
||||
// 角色大招
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void OnUltimateSkill();
|
||||
};
|
||||
|
||||
|
||||
struct FInputActionValue;
|
||||
class ABusyPlayerRole;
|
||||
|
||||
UCLASS()
|
||||
@ -14,28 +41,69 @@ public:
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void SetupInputComponent() override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
bool GetCursorPosition(FVector2D& Position) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void GetCursorHitResult(TArray<AActor*>& Results) const;
|
||||
ABusyPlayerRole* GetControlledRole() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void GetControlledRole() const;
|
||||
void GetCursorHitResult(TArray<AActor*>& Results) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void SwitchControlledRole(ABusyPlayerRole* Target);
|
||||
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Roaming Camera")
|
||||
TSubclassOf<AActor> RoamingCameraClass;
|
||||
|
||||
// 漫游相机移动速度为角色移动速度 * 漫游速度系数
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DisplayName="漫游速度系数", Category = "Roaming Camera")
|
||||
float RoamingSpeedFactor = 3.0;
|
||||
|
||||
public: // 输入相关
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputMappingContext> InputMapping;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> TouchAction;
|
||||
TObjectPtr<class UInputAction> MoveAction; // 鼠标右键移动
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> SelectAction; // 鼠标左键选择
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> PrimarySkillAction; // 基础技能
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> UltimateSkillAction; // 大招
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> CameraDetachAction; // 相机脱离
|
||||
|
||||
|
||||
public:
|
||||
UFUNCTION()
|
||||
void OnMove(const FInputActionValue& Value)const;
|
||||
|
||||
UFUNCTION()
|
||||
void OnPrimarySkill(const FInputActionValue& Value)const;
|
||||
|
||||
UFUNCTION()
|
||||
void OnUltimateSkill(const FInputActionValue& Value)const;
|
||||
|
||||
UFUNCTION()
|
||||
void OnCameraDetach(const FInputActionValue& Value);
|
||||
|
||||
|
||||
protected:
|
||||
UPROPERTY()
|
||||
AActor* RoamingCameraActor = nullptr;
|
||||
|
||||
private:
|
||||
bool bCameraDetached = false;
|
||||
};
|
||||
|
||||
@ -14,27 +14,33 @@ class ALevelPlayerState : public ALuaPlayerState
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
// virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
public: // 给蓝图初始化的接口
|
||||
UFUNCTION(BlueprintCallable)
|
||||
AActor* CreateRoleRoster(class APlayerController* PlayerController);
|
||||
|
||||
|
||||
|
||||
public:
|
||||
public: // 给蓝图的Get接口
|
||||
UFUNCTION(BlueprintCallable)
|
||||
ABusyPlayerRole* GetControlledRole() const;
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
virtual FVector2D GetSpawnLocation()const;
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TArray<TSubclassOf<ABusyPlayerRole>> RoleClasses;
|
||||
|
||||
protected:
|
||||
UPROPERTY()
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
int ControlledRoleIndex = -1;
|
||||
|
||||
|
||||
UPROPERTY()
|
||||
TArray<ABusyPlayerRole*> Roles;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
TArray<ABusyPlayerRole*> RoleRoster;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Generator/TerrainGeneratorBase.h"
|
||||
#include "Map/TerrainLayerComponent.h"
|
||||
#include "PaperTerrainMapActor.generated.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class BUSYRABBIT_API APaperTerrainMapActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
APaperTerrainMapActor();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
public:
|
||||
// 生成地图(可在蓝图中调用)
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
void GenerateTerrainMap();
|
||||
|
||||
// 清除当前地图
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
void ClearMap();
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
|
||||
protected: // 生成器相关
|
||||
// 地形生成器类
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Terrain Map")
|
||||
TSubclassOf<class UTerrainGeneratorBase> GeneratorClass;
|
||||
|
||||
// 地形生成器实例
|
||||
UPROPERTY()
|
||||
TObjectPtr<class UTerrainGeneratorBase> TerrainGenerator;
|
||||
|
||||
// 生成的地形数据
|
||||
TArray<FGameplayTag> GeneratedTerrainData;
|
||||
|
||||
|
||||
protected: // 地图配置相关
|
||||
// 地图宽度
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1", ClampMax = "1024"))
|
||||
int32 MapWidth = 16;
|
||||
|
||||
// 地图高度
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1", ClampMax = "1024"))
|
||||
int32 MapHeight = 16;
|
||||
|
||||
// 瓦片大小(像素)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1"))
|
||||
int32 TileSize = 16;
|
||||
|
||||
protected: // 瓦片相关
|
||||
TMap<FGameplayTag, FTerrainTileSetConfig> *TerrainTileSetConfigs;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", DisplayName="瓦片设置")
|
||||
TObjectPtr<UTerrainDoubleGridDataAsset> TileSetDataAsset;
|
||||
|
||||
|
||||
protected:
|
||||
// Paper2D瓦片地图组件
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TObjectPtr<class UPaperTileMapComponent> TileMapComponent;
|
||||
|
||||
// 是否在构造时自动生成地图
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings")
|
||||
bool bAutoGenerateOnConstruction = true;
|
||||
|
||||
// 是否在开始时自动生成地图
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings")
|
||||
bool bAutoGenerateOnBeginPlay = true;
|
||||
private:
|
||||
// 初始化TileMap组件
|
||||
void InitializeTileMap();
|
||||
|
||||
void InitializeGenerator();
|
||||
|
||||
// 应用地形数据到TileMap
|
||||
void ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData)const;
|
||||
|
||||
void DrawTile(const int32 X, const int32 Y, FGameplayTag TerrainTags[4])const;
|
||||
|
||||
};
|
||||
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Generator/TerrainGeneratorBase.h"
|
||||
#include "TerrainGenerator.generated.h"
|
||||
|
||||
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UTerrainGenerator : public UTerrainGeneratorBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UTerrainGenerator();
|
||||
|
||||
virtual bool GenerateWithNodes(
|
||||
TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes
|
||||
)override;
|
||||
|
||||
private:
|
||||
void RegionGrowing(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const;
|
||||
|
||||
void ApplyPerlinNoiseSmoothing(TArray<FGameplayTag>& Map, int32 Width, int32 Height) const;
|
||||
|
||||
};
|
||||
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "TerrainGeneratorBlueprintLibrary.generated.h"
|
||||
|
||||
// 蓝图函数库,方便在蓝图中使用地形生成<E7949F>?
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UTerrainGeneratorBlueprintLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// 创建地形生成器实<E599A8>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static class UTerrainGenerator* CreateTerrainGenerator();
|
||||
|
||||
// 快速设置示例地形配<E5BDA2>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static void SetupExampleTerrainConfig(class UTerrainGeneratorBase* UTerrainGenerator);
|
||||
|
||||
// 生成地图并返回一维数组(蓝图不支持嵌套TArray<61>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static TArray<FGameplayTag> GenerateMap(
|
||||
class UTerrainGenerator* Generator,
|
||||
int32 Width = 256,
|
||||
int32 Height = 256
|
||||
);
|
||||
|
||||
// 获取地图尺寸信息(用于在蓝图中解析一维数组)
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static void GetMapDimensions(
|
||||
class UTerrainGenerator* Generator,
|
||||
int32& Width,
|
||||
int32& Height
|
||||
);
|
||||
|
||||
// 获取地形标签的显示名<E7A4BA>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static FString GetTerrainDisplayName(const FGameplayTag& TerrainTag);
|
||||
|
||||
// 检查地形标签是否有<E590A6>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static bool IsValidTerrainTag(const FGameplayTag& TerrainTag);
|
||||
};
|
||||
125
Tools/README.md
Normal file
125
Tools/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
# UE头文件解析工具
|
||||
|
||||
这是一个用于扫描UE头文件并生成slua插件的emmy-lua注解的Python工具。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 自动扫描指定目录下的所有UE头文件(.h)
|
||||
- 解析UCLASS、USTRUCT、UENUM、UFUNCTION、UPROPERTY等宏
|
||||
- 生成符合emmy-lua标准的类型注解文件(.d.lua)
|
||||
- 支持C++类型到Lua类型的自动转换
|
||||
- 支持递归扫描子目录
|
||||
|
||||
## 支持的UE宏
|
||||
|
||||
- **UCLASS**: 解析类定义,包括继承关系
|
||||
- **USTRUCT**: 解析结构体定义和属性
|
||||
- **UENUM**: 解析枚举定义和值
|
||||
- **UFUNCTION**: 解析函数声明,包括参数和返回值
|
||||
- **UPROPERTY**: 解析属性声明
|
||||
- **DECLARE_DYNAMIC_DELEGATE**: 解析委托声明
|
||||
|
||||
## 安装和使用
|
||||
|
||||
### 前置要求
|
||||
|
||||
- Python 3.6+
|
||||
- 无需额外依赖
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
# 扫描Source目录并生成注解文件到Content/Lua/@types目录
|
||||
python ue_header_parser.py Source/BusyRabbit -o Content/Lua/@types
|
||||
|
||||
# 扫描当前目录并生成注解文件到同目录
|
||||
python ue_header_parser.py .
|
||||
|
||||
# 递归扫描整个项目
|
||||
python ue_header_parser.py Source --recursive -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
- `directory`: 要扫描的目录路径(必需)
|
||||
- `-o, --output`: 输出目录路径(可选,默认为源文件同目录)
|
||||
- `--recursive`: 递归扫描子目录(可选)
|
||||
|
||||
## 生成的注解格式
|
||||
|
||||
工具会根据UE头文件生成对应的emmy-lua注解:
|
||||
|
||||
### 类注解示例
|
||||
```lua
|
||||
---@class UInventoryComponent : ULuaActorComponent
|
||||
---@field Capacity integer
|
||||
---@field InventoryList table<FInventoryGrid>
|
||||
|
||||
---@param ItemID integer
|
||||
---@param Count integer
|
||||
---@return boolean
|
||||
function UInventoryComponent:IsCanContain(ItemID, Count) end
|
||||
```
|
||||
|
||||
### 结构体注解示例
|
||||
```lua
|
||||
---@class FInventoryGrid
|
||||
---@field ItemID integer
|
||||
---@field CurrentCount integer
|
||||
---@field MaxCount integer
|
||||
---@field Priority integer
|
||||
local FInventoryGrid = {}
|
||||
```
|
||||
|
||||
### 枚举注解示例
|
||||
```lua
|
||||
---@enum EBusyRoleState
|
||||
local EBusyRoleState = {
|
||||
BonfireIdle = 0,
|
||||
Searching = 1,
|
||||
Picking = 2,
|
||||
PickFinished = 3,
|
||||
BackBonfire = 4
|
||||
}
|
||||
```
|
||||
|
||||
### 委托注解示例
|
||||
```lua
|
||||
---@class FOnInventoryChanged
|
||||
---@field Call fun(ItemID: integer)
|
||||
local FOnInventoryChanged = {}
|
||||
```
|
||||
|
||||
## 类型映射
|
||||
|
||||
工具会自动将C++类型映射到Lua类型:
|
||||
|
||||
| C++类型 | Lua类型 |
|
||||
|---------|---------|
|
||||
| int32, int64 | integer |
|
||||
| float, double | number |
|
||||
| bool | boolean |
|
||||
| FString, FText, FName | string |
|
||||
| void | nil |
|
||||
| TArray, TMap, TSet | table |
|
||||
| 其他类型 | any |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 工具会跳过没有UE宏的普通头文件
|
||||
2. 生成的注解文件会保存在`.d.lua`文件中
|
||||
3. 如果输出目录不存在,工具会自动创建
|
||||
4. 工具会处理编码问题,但建议确保头文件使用UTF-8编码
|
||||
5. 对于复杂的模板类型,工具会尝试解析内部类型
|
||||
|
||||
## 故障排除
|
||||
|
||||
如果遇到解析错误,请检查:
|
||||
- 头文件语法是否正确
|
||||
- UE宏格式是否符合标准
|
||||
- 文件编码是否为UTF-8
|
||||
- 是否有语法错误或缺失的分号
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎提交问题和改进建议!
|
||||
402
Tools/ue_header_parser.py
Normal file
402
Tools/ue_header_parser.py
Normal file
@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
UE头文件解析工具
|
||||
用于扫描UE头文件并生成slua插件的emmy-lua注解
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Set, Optional
|
||||
|
||||
class UEHeaderParser:
|
||||
def __init__(self):
|
||||
self.classes = []
|
||||
self.structs = []
|
||||
self.enums = []
|
||||
self.delegates = []
|
||||
|
||||
def parse_header_file(self, file_path: str) -> Dict:
|
||||
"""解析单个头文件"""
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
result = {
|
||||
'classes': [],
|
||||
'structs': [],
|
||||
'enums': [],
|
||||
'delegates': [],
|
||||
'file_path': file_path
|
||||
}
|
||||
|
||||
# 解析UCLASS
|
||||
class_pattern = r'UCLASS\s*\([^)]*\)\s*\n\s*class\s+[A-Z_]+\s+(\w+)\s*:\s*public\s+(\w+)'
|
||||
classes = re.findall(class_pattern, content)
|
||||
for class_name, parent_class in classes:
|
||||
result['classes'].append({
|
||||
'name': class_name,
|
||||
'parent': parent_class,
|
||||
'functions': self._parse_functions(content, class_name),
|
||||
'properties': self._parse_properties(content, class_name)
|
||||
})
|
||||
|
||||
# 解析USTRUCT
|
||||
struct_pattern = r'USTRUCT\s*\([^)]*\)\s*\n\s*struct\s+[A-Z_]+\s+(\w+)'
|
||||
structs = re.findall(struct_pattern, content)
|
||||
for struct_name in structs:
|
||||
result['structs'].append({
|
||||
'name': struct_name,
|
||||
'properties': self._parse_struct_properties(content, struct_name)
|
||||
})
|
||||
|
||||
# 解析USTRUCT (BlueprintType变体)
|
||||
struct_pattern2 = r'USTRUCT\s*\([^)]*\)\s*\n\s*struct\s+F(\w+)'
|
||||
structs2 = re.findall(struct_pattern2, content)
|
||||
for struct_name in structs2:
|
||||
result['structs'].append({
|
||||
'name': f'F{struct_name}',
|
||||
'properties': self._parse_struct_properties(content, f'F{struct_name}')
|
||||
})
|
||||
|
||||
# 解析UENUM
|
||||
enum_pattern = r'UENUM\s*\([^)]*\)\s*\n\s*enum\s+class\s+(\w+)'
|
||||
enums = re.findall(enum_pattern, content)
|
||||
for enum_name in enums:
|
||||
result['enums'].append({
|
||||
'name': enum_name,
|
||||
'values': self._parse_enum_values(content, enum_name)
|
||||
})
|
||||
|
||||
# 解析委托
|
||||
delegate_pattern = r'DECLARE_DYNAMIC_(MULTICAST_)?DELEGATE(?:_\w+)?\s*\(\s*(\w+)\s*'
|
||||
delegates = re.findall(delegate_pattern, content)
|
||||
for is_multicast, delegate_name in delegates:
|
||||
result['delegates'].append({
|
||||
'name': delegate_name,
|
||||
'is_multicast': bool(is_multicast),
|
||||
'params': self._parse_delegate_params(content, delegate_name)
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
def _parse_functions(self, content: str, class_name: str) -> List[Dict]:
|
||||
"""解析UFUNCTION"""
|
||||
functions = []
|
||||
|
||||
# 匹配UFUNCTION声明
|
||||
func_pattern = r'UFUNCTION\s*\([^)]*\)\s*\n\s*(\w+)\s+(\w+)\s*\(([^)]*)\)'
|
||||
matches = re.findall(func_pattern, content)
|
||||
|
||||
for return_type, func_name, params in matches:
|
||||
# 解析参数
|
||||
param_list = []
|
||||
if params.strip():
|
||||
for param in params.split(','):
|
||||
param = param.strip()
|
||||
if param:
|
||||
# 简单的参数解析
|
||||
parts = param.split()
|
||||
if len(parts) >= 2:
|
||||
param_type = parts[-2] if len(parts) > 2 else parts[0]
|
||||
param_name = parts[-1]
|
||||
param_list.append({
|
||||
'type': param_type,
|
||||
'name': param_name
|
||||
})
|
||||
|
||||
functions.append({
|
||||
'name': func_name,
|
||||
'return_type': return_type,
|
||||
'params': param_list
|
||||
})
|
||||
|
||||
return functions
|
||||
|
||||
def _parse_properties(self, content: str, class_name: str) -> List[Dict]:
|
||||
"""解析UPROPERTY"""
|
||||
properties = []
|
||||
|
||||
# 匹配UPROPERTY声明
|
||||
prop_pattern = r'UPROPERTY\s*\([^)]*\)\s*\n\s*(\w+(?:<[^>]*>)?)\s+(\w+);'
|
||||
matches = re.findall(prop_pattern, content)
|
||||
|
||||
for prop_type, prop_name in matches:
|
||||
properties.append({
|
||||
'name': prop_name,
|
||||
'type': prop_type
|
||||
})
|
||||
|
||||
return properties
|
||||
|
||||
def _parse_struct_properties(self, content: str, struct_name: str) -> List[Dict]:
|
||||
"""解析结构体属性"""
|
||||
properties = []
|
||||
|
||||
# 在结构体定义范围内查找属性
|
||||
struct_start = content.find(f'struct {struct_name}')
|
||||
if struct_start == -1:
|
||||
return properties
|
||||
|
||||
# 找到结构体结束位置
|
||||
brace_count = 0
|
||||
struct_content = ""
|
||||
for i in range(struct_start, len(content)):
|
||||
char = content[i]
|
||||
if char == '{':
|
||||
brace_count += 1
|
||||
elif char == '}':
|
||||
brace_count -= 1
|
||||
if brace_count == 0:
|
||||
struct_content = content[struct_start:i+1]
|
||||
break
|
||||
|
||||
if not struct_content:
|
||||
return properties
|
||||
|
||||
# 在结构体内容中查找UPROPERTY
|
||||
prop_pattern = r'UPROPERTY\s*\([^)]*\)\s*\n\s*(\w+(?:<[^>]*>)?)\s+(\w+);'
|
||||
matches = re.findall(prop_pattern, struct_content)
|
||||
|
||||
for prop_type, prop_name in matches:
|
||||
properties.append({
|
||||
'name': prop_name,
|
||||
'type': prop_type
|
||||
})
|
||||
|
||||
return properties
|
||||
|
||||
def _parse_enum_values(self, content: str, enum_name: str) -> List[Dict]:
|
||||
"""解析枚举值"""
|
||||
values = []
|
||||
|
||||
# 找到枚举定义
|
||||
enum_start = content.find(f'enum class {enum_name}')
|
||||
if enum_start == -1:
|
||||
return values
|
||||
|
||||
# 找到枚举内容
|
||||
brace_start = content.find('{', enum_start)
|
||||
if brace_start == -1:
|
||||
return values
|
||||
|
||||
brace_end = content.find('}', brace_start)
|
||||
if brace_end == -1:
|
||||
return values
|
||||
|
||||
enum_content = content[brace_start+1:brace_end]
|
||||
|
||||
# 解析枚举值
|
||||
value_pattern = r'(\w+)\s*(?:=\s*(\d+))?'
|
||||
matches = re.findall(value_pattern, enum_content)
|
||||
|
||||
current_value = 0
|
||||
for name, explicit_value in matches:
|
||||
if explicit_value:
|
||||
current_value = int(explicit_value)
|
||||
|
||||
values.append({
|
||||
'name': name,
|
||||
'value': current_value
|
||||
})
|
||||
|
||||
current_value += 1
|
||||
|
||||
return values
|
||||
|
||||
def _parse_delegate_params(self, content: str, delegate_name: str) -> List[Dict]:
|
||||
"""解析委托参数"""
|
||||
params = []
|
||||
|
||||
# 找到委托声明
|
||||
delegate_pattern = f'DECLARE_DYNAMIC_(MULTICAST_)?DELEGATE(?:_\\w+)?\\s*\\(\\s*{delegate_name}'
|
||||
match = re.search(delegate_pattern, content)
|
||||
if not match:
|
||||
return params
|
||||
|
||||
# 查找参数列表
|
||||
param_start = content.find('(', match.end())
|
||||
if param_start == -1:
|
||||
return params
|
||||
|
||||
param_end = content.find(')', param_start)
|
||||
if param_end == -1:
|
||||
return params
|
||||
|
||||
param_content = content[param_start+1:param_end]
|
||||
|
||||
# 解析参数
|
||||
param_parts = [p.strip() for p in param_content.split(',') if p.strip()]
|
||||
for i, param in enumerate(param_parts):
|
||||
parts = param.split()
|
||||
if len(parts) >= 2:
|
||||
param_type = ' '.join(parts[:-1])
|
||||
param_name = parts[-1]
|
||||
params.append({
|
||||
'type': param_type,
|
||||
'name': param_name
|
||||
})
|
||||
|
||||
return params
|
||||
|
||||
def generate_emmy_lua_annotations(self, parsed_data: Dict) -> str:
|
||||
"""生成emmy-lua注解"""
|
||||
output = []
|
||||
|
||||
# 文件头注释
|
||||
output.append(f'-- 自动生成的emmy-lua注解文件')
|
||||
output.append(f'-- 源文件: {parsed_data["file_path"]}')
|
||||
output.append('')
|
||||
|
||||
# 生成枚举注解
|
||||
for enum in parsed_data['enums']:
|
||||
output.append(f'---@enum {enum["name"]}')
|
||||
output.append(f'local {enum["name"]} = {{')
|
||||
for value in enum['values']:
|
||||
output.append(f' {value["name"]} = {value["value"]},')
|
||||
output.append('}')
|
||||
output.append('')
|
||||
|
||||
# 生成结构体注解
|
||||
for struct in parsed_data['structs']:
|
||||
output.append(f'---@class {struct["name"]}')
|
||||
for prop in struct['properties']:
|
||||
lua_type = self._cpp_to_lua_type(prop['type'])
|
||||
output.append(f'---@field {prop["name"]} {lua_type}')
|
||||
output.append(f'local {struct["name"]} = {{}}')
|
||||
output.append('')
|
||||
|
||||
# 生成类注解
|
||||
for cls in parsed_data['classes']:
|
||||
output.append(f'---@class {cls["name"]} : {cls["parent"]}')
|
||||
|
||||
# 添加属性
|
||||
for prop in cls['properties']:
|
||||
lua_type = self._cpp_to_lua_type(prop['type'])
|
||||
output.append(f'---@field {prop["name"]} {lua_type}')
|
||||
|
||||
# 添加方法
|
||||
for func in cls['functions']:
|
||||
lua_return_type = self._cpp_to_lua_type(func['return_type'])
|
||||
param_annotations = []
|
||||
for param in func['params']:
|
||||
lua_param_type = self._cpp_to_lua_type(param['type'])
|
||||
param_annotations.append(f'---@param {param["name"]} {lua_param_type}')
|
||||
|
||||
if param_annotations:
|
||||
output.extend(param_annotations)
|
||||
output.append(f'---@return {lua_return_type}')
|
||||
output.append(f'function {cls["name"]}:{func["name"]}({", ".join(p["name"] for p in func["params"])}) end')
|
||||
output.append('')
|
||||
|
||||
output.append('')
|
||||
|
||||
# 生成委托注解
|
||||
for delegate in parsed_data['delegates']:
|
||||
output.append(f'---@class {delegate["name"]}')
|
||||
param_types = []
|
||||
for param in delegate['params']:
|
||||
lua_type = self._cpp_to_lua_type(param['type'])
|
||||
param_types.append(f'{param["name"]}: {lua_type}')
|
||||
|
||||
if param_types:
|
||||
output.append(f'---@field Call fun({", ".join(param_types)})')
|
||||
else:
|
||||
output.append('---@field Call fun()')
|
||||
output.append(f'local {delegate["name"]} = {{}}')
|
||||
output.append('')
|
||||
|
||||
return '\n'.join(output)
|
||||
|
||||
def _cpp_to_lua_type(self, cpp_type: str) -> str:
|
||||
"""将C++类型转换为Lua类型"""
|
||||
type_mapping = {
|
||||
'int32': 'integer',
|
||||
'int64': 'integer',
|
||||
'float': 'number',
|
||||
'double': 'number',
|
||||
'bool': 'boolean',
|
||||
'FString': 'string',
|
||||
'FText': 'string',
|
||||
'FName': 'string',
|
||||
'void': 'nil',
|
||||
'TArray': 'table',
|
||||
'TMap': 'table',
|
||||
'TSet': 'table'
|
||||
}
|
||||
|
||||
# 处理模板类型
|
||||
if '<' in cpp_type and '>' in cpp_type:
|
||||
base_type = cpp_type.split('<')[0]
|
||||
inner_type = cpp_type.split('<')[1].split('>')[0]
|
||||
lua_inner_type = self._cpp_to_lua_type(inner_type)
|
||||
return f'{type_mapping.get(base_type, "any")}<{lua_inner_type}>'
|
||||
|
||||
return type_mapping.get(cpp_type, 'any')
|
||||
|
||||
def scan_directory(self, directory: str, output_dir: str = None):
|
||||
"""扫描目录或单个文件并生成注解文件"""
|
||||
header_files = []
|
||||
|
||||
if os.path.isfile(directory) and directory.endswith('.h'):
|
||||
# 单个文件
|
||||
header_files = [directory]
|
||||
elif os.path.isdir(directory):
|
||||
# 目录
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
if file.endswith('.h'):
|
||||
header_files.append(os.path.join(root, file))
|
||||
else:
|
||||
print(f'错误: {directory} 不是有效的文件或目录')
|
||||
return
|
||||
|
||||
print(f'找到 {len(header_files)} 个头文件')
|
||||
|
||||
for header_file in header_files:
|
||||
try:
|
||||
print(f'正在解析: {header_file}')
|
||||
parsed_data = self.parse_header_file(header_file)
|
||||
|
||||
if any([parsed_data['classes'], parsed_data['structs'], parsed_data['enums'], parsed_data['delegates']]):
|
||||
annotations = self.generate_emmy_lua_annotations(parsed_data)
|
||||
|
||||
# 确定输出文件路径
|
||||
if output_dir:
|
||||
if os.path.isfile(directory):
|
||||
# 单个文件的情况
|
||||
output_file = os.path.join(output_dir, os.path.basename(header_file).replace('.h', '.d.lua'))
|
||||
else:
|
||||
# 目录的情况
|
||||
relative_path = os.path.relpath(header_file, directory)
|
||||
output_file = os.path.join(output_dir, relative_path.replace('.h', '.d.lua'))
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
else:
|
||||
output_file = header_file.replace('.h', '.d.lua')
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(annotations)
|
||||
|
||||
print(f'已生成: {output_file}')
|
||||
|
||||
except Exception as e:
|
||||
print(f'解析文件 {header_file} 时出错: {e}')
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='UE头文件解析工具 - 生成slua插件的emmy-lua注解')
|
||||
parser.add_argument('directory', help='要扫描的目录路径')
|
||||
parser.add_argument('-o', '--output', help='输出目录路径(默认为源文件同目录)')
|
||||
parser.add_argument('--recursive', action='store_true', help='递归扫描子目录')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.directory):
|
||||
print(f'错误: 目录 {args.directory} 不存在')
|
||||
return
|
||||
|
||||
parser = UEHeaderParser()
|
||||
parser.scan_directory(args.directory, args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
104
Tools/使用示例.md
Normal file
104
Tools/使用示例.md
Normal file
@ -0,0 +1,104 @@
|
||||
# UE头文件解析工具使用示例
|
||||
|
||||
## 基本用法
|
||||
|
||||
### 扫描整个项目
|
||||
```bash
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 扫描特定目录
|
||||
```bash
|
||||
# 扫描Components目录
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Components -o Content/Lua/@types
|
||||
|
||||
# 扫描Level目录
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Level -o Content/Lua/@types
|
||||
```
|
||||
|
||||
### 扫描单个文件
|
||||
```bash
|
||||
# 直接指定文件路径(需要先确保输出目录存在)
|
||||
python Tools/ue_header_parser.py Source/BusyRabbit/Public/Components/InventoryComponent.h -o Content/Lua/@types
|
||||
```
|
||||
|
||||
## 生成结果示例
|
||||
|
||||
### 输入头文件 (InventoryComponent.h)
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct FInventoryGrid {
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, DisplayName = "物品ID")
|
||||
int32 ItemID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "当前的数量")
|
||||
int32 CurrentCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "最大堆叠限制")
|
||||
int32 MaxCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, DisplayName = "优先级")
|
||||
int32 Priority;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UInventoryComponent : public ULuaActorComponent {
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool IsCanContain(int32 ItemID, int32 Count);
|
||||
|
||||
// ... 其他函数和属性
|
||||
};
|
||||
```
|
||||
|
||||
### 输出注解文件 (InventoryComponent.d.lua)
|
||||
```lua
|
||||
-- 自动生成的emmy-lua注解文件
|
||||
-- 源文件: Source/BusyRabbit/Public/Components\InventoryComponent.h
|
||||
|
||||
---@class FInventoryGrid
|
||||
---@field ItemID integer
|
||||
---@field CurrentCount integer
|
||||
---@field MaxCount integer
|
||||
---@field Priority integer
|
||||
local FInventoryGrid = {}
|
||||
|
||||
---@class UInventoryComponent : ULuaActorComponent
|
||||
---@field Capacity integer
|
||||
---@field InventoryList table<any>
|
||||
---@param ItemID integer
|
||||
---@param Count integer
|
||||
---@return boolean
|
||||
function UInventoryComponent:IsCanContain(ItemID, Count) end
|
||||
|
||||
-- ... 其他函数注解
|
||||
```
|
||||
|
||||
## 集成到开发流程
|
||||
|
||||
### 1. 定期生成注解
|
||||
建议在每次UE头文件更新后运行工具重新生成注解。
|
||||
|
||||
### 2. 版本控制
|
||||
将生成的`.d.lua`文件添加到版本控制中,方便团队共享。
|
||||
|
||||
### 3. IDE配置
|
||||
确保IDE(如VSCode)能够识别`Content/Lua/@types`目录中的注解文件。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **类型映射**: 工具会自动将C++类型映射到Lua类型
|
||||
2. **模板类型**: 支持`TArray<FInventoryGrid>`等模板类型的解析
|
||||
3. **委托支持**: 自动生成委托类型的Call函数注解
|
||||
4. **错误处理**: 工具会跳过无法解析的文件并继续处理其他文件
|
||||
|
||||
## 故障排除
|
||||
|
||||
如果遇到问题,请检查:
|
||||
- 头文件语法是否正确
|
||||
- UE宏格式是否符合标准
|
||||
- 输出目录权限是否足够
|
||||
- Python版本是否为3.6+
|
||||
Reference in New Issue
Block a user