Inventory System Plugin
新輸入增強系統
在cs文件中添加Module:

添加IMX和Action以及對應的回調函數:

先在BeginPlay中將輸入添加到子系統中:

創建輸入:



將Action添加到IMC中并選擇按鍵:

在PlayerController中進行賦值:

LineTrace for Pickups
創建自定義Trace Channel,并初始化為Ignore All:

創建自定義Collision Preset給PickupItem,并將ItemTrace設置為Block:

添加Trace函數:

根據屏幕中心點獲取世界位置,然后進行Line Trace,并判斷上一個物品是否和當前物品相同:


將Item的Collision Presets設置為Item:

將Item Trace Channel設置為ItemTrace:

添加PickUpMessage
在HUDWidget中創建Show和Hide Message的方法,并在藍圖中實現:


在Trace中調用對應函數:

創建ActorComponent類作為Item的Component,并初始化Item的屬性:

創建一個以UserWidget為父類的PickupMessageWidget:

將PickupMessage拖到HUD中:


在ItemComponent中添加Blueprintable,以便創建藍圖類:


在Item中添加剛剛創建的BP類ItemComponent:

效果:

添加高光效果
創建高光材質:

配置材質:

創建StaticMeshComponent用于設置高光:


創建Interface用于在其他地方也可以使用高光效果:


在Interface中創建 使用高光 和 關閉高光 的函數:

在MeshComponent中實現該函數:


在Trace中獲取 實現了指定接口的組件,即MeshComponent,并調用接口中的Highlight方法:

創建MeshComponent的藍圖類


設置MeshComponent:

在Item中添加MeshComponent:

添加庫存系統
添加InventoryComponent類:

添加一個InventoryWidget的基類:

創建子類SpatialInventoryWidget:


在Component中添加 初始化庫存 和 打開關閉庫存 函數:


在PlayerController中添加InputAction:

從藍圖中查找InventoryComponent并初始化,綁定Input Action:

創建藍圖類的SpatialInventoryWidget:



在藍圖中創建InventoryComponent:


將MenuClass設置為WBP_SpatialInventoryWidget:

在PlayerController中添加InventoryComponent:

綁定Input Action:


在PlayerController中配置對應Action:

添加不同類型的Grid以及Switcher Button
添加GridType.h:

添加Grid類:

初始化對應Gird的Category:

創建3個不同類型的Grid、Button以及Switcher,點擊對應的Button時,禁用該Button并切換Grid:

創建Grid藍圖類:

暫時添加一個Text用于測試:

不同的Category對應不同的數字:

在InventoryWidget中創建3個Button,1個Switcher包含3個Grid,并設置3個Grid的Item Category:

效果:



添加Grid Slot
添加GridSlot Widget類:

添加Slot的Index以及 設置 和 獲取Index函數:

添加Utils工具函數類,專門存放工具函數:

存放計算當前格子的索引值的工具函數:

在Grid中添加Grid Slot并添加Slot的行數、列數、大小,綁定CanvasPanel用于在上面添加Slot:

修改:
上面的行數和列數寫反了:

上面的i和j寫反了:

創建Slot藍圖類:


刪除Inventory中的SwicherBackGround:


在Grid中添加BackGround并添加Canvas用于存儲Slot:

在Grid中配置Slot對應的參數:

配置Slot:

還需要在Inventory中分別對3個Grid配置Grid Class:

效果:

Add Fast Array
創建FastArray文件:


創建InventoryItem:

添加NetCore Model:

設置FastArray:



添加委托:


Add No Room in Inventory
聲明一個No Room的委托以及一個TryAddItem的函數,并在里面進行NoRoom Broadcast用于測試::

添加消息Widget類:

添加Text,并在藍圖中添加Show和Hide Message函數定義,通過SetMessage函數讓Text在LifeTime后消失:

在HUD中創建并綁定InfoMessage,創建回調函數OnNoRoom并綁定委托:

在PlayerController中F鍵按下后,TryAddItem:

在藍圖中創建WBP_InfoMessage Widget:

為Text創建Fade Animation:

設置Show和Hide Message函數:

在HUD中添加WBP_InfoMessage:

效果:

Check Room for Item:
創建兩個結構體用于存儲數據:

在父類BaseInventory中聲明是否有Room的函數,并返回空的SlotAvailabilityResult:

在子類SpatialIventory中定義該函數:

在TryAddItem中使用該函數判斷是否有空間:

Item Manifest
創建ItemManifest類,用于存儲Item的所有信息:


添加StructUtils Model:






在ItemComponent中設置Manifest中的Category:

Item Type Tags
添加Tag類:

添加GamePlayTags Model:

在.h中聲明,在.cpp中定義Tag:

在Manifest中添加ItemType:

在ItemComponent中設置具體的Item Type:

On Item Added
添加獲取ItemManifest的函數:

添加AddItem回調函數,若MatchesCategory則AddItem:

如果是服務器,則直接Broadcast:

設置FastArray的Component:

Item Fragment
創建ItemFragment類:


創建FragmentTags:


創建父類結構體Fragment,子類結構體GridFragment繼承父類結構體:

在Manifest中創建Fragments變量:

定義Fragment Tags:

在藍圖中設置Fragments:

添加子結構體ImageFragment:



Has Room For Item
在Grid中添加HasRoomForItem函數并添加重載函數:

在SpatialInventory的HasRoomForItem函數中使用switch分別調用對應Grid的HasRoomForItem函數:

Add Item to Indices



Create Widget to add to the grid
創建Widget類:

綁定Image Icon:

添加Widget到AddItem中:

Add Slotted Items to Canvas
創建工具函數,通過Index獲取Position:

將Slotted Items添加到Canvas中并設置大小和位置:

創建SlottedItem Widget藍圖類:


添加Image_Icon:

在Grid中設置Slotted Item Class:

在測試之前,需要先刪除原先的Item,因為細節面板不會刷新,修改細節面板需要在對應物品的Instance中修改
效果:

Grid Slot Texture
在Slot中添加GridSlotState,并設置不同的Brush對應不同的State:

創建遍歷二維網格的模板函數:

為每個Slot設置Texture:

在Slot中配置FSlateBrush:

效果:

Add Stack Count
創建一個新的子結構Stackable Fragment用于設置 物品的最大存儲容量 和 物品的數量:

添加Tag:

在SlottedItem中添加Text用于顯示StackCount:

用于測試的數據:

在創建SlottedItem時UpdateStackCount:

在藍圖中設置:

添加Text:

效果:

Update Grid Slot


Add Item in the inventory
獲取FragmentOfType:

判斷是否可以堆疊:





查找第一個與指定類型匹配的物品:


添加當前物品的堆疊總數


Pick up 后 Destroy
添加可以Mutable函數,即非const函數:

添加設置StackCount函數:

PickedUp函數:


在藍圖中將Item的Replicates打開,確保在客戶端可以使用:

Add Stacks Count
添加委托,并在添加可堆疊物品時調用:


更新物品和槽位的堆疊數量:

效果:

創建Hover Item
創建Widget類:


創建Widget藍圖類:


配置Hover Item:

創建InputCore Model:

添加點擊事件委托:


綁定委托:




效果:

添加Tile Parameters
添加枚舉類型,用于判斷鼠標處于哪個象限:

獲取Widget的位置:

在Tick中獲取畫布位置和鼠標位置,并傳遞給函數用來更新Tile parameters:

Highlight and UnHighlight Hover Item
創建工具函數,用于獲取Widget Size和判斷鼠標是否處于Canvas內:

創建結構體,用于判斷Hover時處于的格子是否有物品:









將GridSlot中bAvailable默認設置為true:

將CanvasPanel用Overlay包裹,不然無法計算:

效果:


Swap Item
添加StackCount = Count用于記錄StackCount:

添加委托事件,用于處理鼠標進入,離開,點擊GridSlot事件:












為3類Grid創建不同的鼠標指針:


為3類Grid分別配置Visible和Hidden Cursor Widget Class:

ItemPopUpMenu
創建ItemPopUp類:

創建3種委托:




判斷物品是否為Consumable:

創建設置CanvasPanel的函數:

委托的回調函數:

綁定ItemPopUp:

點擊時隱藏ItemPopUpMenu:


根據不同Item類型創建不同的ItemPopUpMenu:

設置CanvasPanel:

隱藏PopUpMenu:


創建ItemPopUp藍圖類:


添加SizeBox,Button,Split:

配置Item Pop Up Class:

Split,Drop,Consume Item
創建丟棄區域類:

創建委托,當在丟棄區域鼠標按下時觸發:

創建丟棄函數:

設置ItemPopUp的GridIndex:


設置ItemManifest:

在ItemManifest中創建當前物品的Class,并添加生成Actor的函數:

在Spatial中創建丟棄區域類,并綁定委托,執行丟棄物品函數:

在Fragment中創建Consumable Fragment,并創建2個Consumable Fragment的子類:

創建Consumable的Tag:

將Component Replicates 設置為True:

添加Health Fragment 和 Pickup Actor Class:

Item Description
創建ItemDescription Widget類:

設置SizeBox:

在BaseWidget中創建父函數:

判斷是否存在HoverItem:

從InventoryComponent中獲取InventoryBaseWidget:

創建工具函數,調用OnItemHovered和OnItemUnHovered函數 并 創建獲取控件位置的函數:

在SlottedItem中創建鼠標進入和離開函數,分別調用對應的工具函數:

在BaseWidget的子類SpatialWidget中添加繼承父類的函數 并 添加描述界面的Class:

延遲一段時間后顯示描述界面:


當物品被點擊并處于Hover狀態時,隱藏描述界面:

創建Item Description藍圖類:



設置Item Description Class:

Composite Pattern
創建UInv_CompositeBase基類,定義了組合模式中的通用接口和方法:

創建UInv_Composite組合類,繼承自 UInv_CompositeBase:

創建UInv_Leaf葉子節點類,繼承自 UInv_CompositeBase:

獲取和設置標簽,Collapse和Expand功能,應用函數(ApplyFunction)用于遞歸操作子節點

初始化時收集子節點,遞歸調用子節點的 ApplyFunction 和 Collapse

直接執行傳入的函數(ApplyFunction)

將ItemDescription的父類改為Composite:

Assimilate Inventory Fragments
玩家懸停一個物品時,系統獲取該物品的 Manifest(包含所有片段),通過 AssimilateInventoryFragments將片段同化到描述控件(DescriptionWidget)




Image Fragment
創建Image的葉子節點并繼承自葉子類:

創建Image Icon并配置大小:

將ImageFragment的父類改為InventoryItemFragment并重寫父類的同化函數:

創建Leaf_Image藍圖類:


在Description Widget中添加Leaf Icon Widget 并 配置Fragment Tag:

效果:

Text Fragment
創建Leaf_Text類:

創建Text:

創建Text Fragment,用于在Manifest中修改對應物品的屬性:

創建FragmentTag,用于確保LeafText和Item中的TextFragment屬性一致:

創建藍圖類:



設置Leaf_ItemName的FragmentTag:

設置ItemManifest的Text和FragmentTag:

效果:

Labeled Number Fragment
創建LabeledValue類:

設置Text和Value:

在父類中添加Manifest繼承函數:

添加LabeledNumberFragment并重寫父類的Manifest函數,用于在開始時隨機初始化Value:

在Manifest中調用每個Fragment的Manifest函數:

創建PrimaryStatFragment:

創建藍圖類:


設置Tag:

添加到Description Widget中:

添加Item的Fragment并設置Tag:

效果:

添加Consume Modifiers并確保隨機值Value與使用時相同
更改Consumable的父類為InventoryItemFragment,添加Consumable的子類ConsumeModifiers但子類繼承自LabelNumberFragment,修改Health和Eat的父類為ConsumeModifiers:

添加3種屬性標簽:

添加一個新的LabeledValue Widget:



在Description中添加3個StaticModifier并配置對應的Tag:



在Item中刪除原先的Health Fragment和Labeled Number Fragment 并 添加Consumable Fragment,添加兩個Consume Modifiers:

效果:

Add more widget
添加更多的Fragment Tag:

添加更多的Widget:

添加到Description Widget中:

在Item中配置對應Fragment:

Add Equipped Grid Slots
創建以GridSlot為父類的EquippedGridSlot類:

在Grid中添加獲取HoverItem的函數:

在BaseInventory中添加獲取HoverItem的虛函數:

在Spatial中實現父類添加獲取HoverItem的虛函數 并 創建數組以及點擊的回調函數:

遍歷所有Widget來獲取EquippedGridSlot并綁定委托:

創建工具函數獲取HoverItem:

在EquippedGridSlot中通過HoverItem是否與EquipmentTypeTag一致來判斷進入或離開:

創建多個藍圖類:


設置每個的Equipment Type Tag:

添加到Spatial Widget中:

效果:

Equip the Cloak
創建EquippedSlottedItem Widget類并繼承自SlottedItem類:

在GridSlot中創建函數用于Create EquippedSlottedItem Widget:

創建Equipped Fragment,與Consume Fragment類似:

設置EquippedSlottedItem并添加點擊事件:

創建工具函數用于獲取InventoryWidget:

在InventoryComponent中創建委托并創建EquipSlotClicked的多播函數:

在Spatial Widget中設置EquippedGridSlotClicked的綁定函數,并調用InventoryComponent的多播函數:

創建EquippedSlottedItem Widget的藍圖類:


為3個EquippedGridSlot都設置Equipped Slotted Item Class:

效果:

Equip and UnEquip Cloak
處理EquippedSlot的點擊事件:



裝備/卸下事件:

Bug:切換Gird或關閉庫存會導致HoverItem丟失
在Component中創建委托,用于判斷Inventory的開關:

創建將HoverItem放回去的函數,并為HasRoomForItem添加強制堆疊數量,從而確保HoverItem放回時的數量正確:

綁定函數,若庫存關閉,則調用PutHoverItemBack,若沒有Room則DropItem:

配置強制指定的數量:

若切換Grid,則調用PutHoverItemBack:

Bug:格子有物品卻可以放下并重疊
添加判斷,格子是否為空:

Bug:交換物品時,物品位置不對 并且 添加若被點擊物品數量為最大值,則交換物品而不是只交換StackCount:

Bug:拾取物品時,若還有剩余,物品剩余數量卻沒有減少 并且 撿起新物品時,也應該判斷是否會有剩余


Equip Component
創建Equip Component類:

通過InventoryComponent綁定委托,并在回調函數中調用EquipmentFragment的Equip函數:

創建EquipmentFragment Tag:

在EquipmentFragment中創建Manifest函數:

將清除HoverItem的函數移動到最后使用:

創建EquipComponent藍圖類:


添加到PlayerController中:

在Cloak中添加Equipment Fragment:

將Cloak改為Replicates:

效果:

Spawn Equip Actor
創建EquipActor類用于Spawn:

在EquipActor中添加EquipmentType:

在EquipmentFragment中添加要Spawn的EquipActorClass和EquipActor和SocketAttachPoint和EquipmentTag,以及Spawn和Destroy的函數:

在EquipComponent中添加Spawn和Remove的函數:

初始化Inventory Component:



添加EquipActor藍圖類:

cloak的Spawn Item類:

添加SkeletalMesh:

設置Equipment Tag為Red Cloak:

在Equipment Fragment中添加Equip Actor Class:

Proxy Mesh
創建ProxyMeshActor類:

在EquipComponent中為ProxyMesh設置函數:




創建ProxyMeshActor藍圖類:


效果:

Character Display
創建CharacterDisplay Widget類:

獲取ProxyMeshActor的Mesh:

通過獲取的Mesh創建新的Mesh并判斷鼠標拖拽時,角色旋轉:

在SpatialWidget中創建CharacterDisplay并在鼠標松開或關閉庫存時設置停止拖拽:

取消ProxyMeshActor的復制:

創建藍圖類:


創建Render Target:


配置大?。?/strong>

創建Material并將Texture改為Render Target:

為Material設置背景透明:

將CharacterDisplay的Image設置為剛剛的材質:

在Spatial Widget中添加CharacterDisplay Widget:

在ProxyMeshActor中添加SpringArm和SceneCaptureComponent2D,并將Texture設置為Render Target,并隱藏除了Skeletal Mesh以外的:

將ProxyMeshActor拖到世界中即可:
只有在打包后才會在多人游戲中正確運行 或者 取消Run Under One Process進行測試:

效果:

Bug:在設置另一個ItemDescription時,沒有提前清除上一個的ItemDescription
獲取所有的子類:

設置Visibility時,將所有子類先銷毀:

調換兩個函數位置,先銷毀,后同化:

將PlayerController的內容移動到新的Actor Component:Inv_InteractComponent,并將其添加到PlayerController中:
All, with Sensei's permission, I am posting my changes to the plugin. I have moved all the functionality from the PlayerController into a separate component called Inv_InteractComponent. By separating this functionality from the player controller, this makes the plugin attachable to any existing PC with no changes to the PC, unless you have a functional conflict, like the LineTracing, etc. being done previously.
Instructions:
Important: If you have been modifying your own PC for your own project, you will need to remove all the course code from it for this to work! If you are worried about bricking your project because it is currently working for you, then don't do this on that project. Do it on a blank project like what we created for the course at the start. I am not liable for you screwing up your existing project - this is intended to augment Sensei Stephen's course as taught.
- Add the Inv_InteractComponent files to the InventoryManagement/Components public and private folders
- Replace your Inv_PlayerController files from the course with the empty ones I have attached if you want a quick replacement. Otherwise create your own empty Inv_PlayerController class, or remove all of the existing interaction code that we put into it.
- Compile and launch editor
- Make a blueprint of the Inv_InteractComponent class and fill in its fields in the Inventory category
- Attach the component to the player controller blueprint alongside the Inv_InventoryComponent
Important change:
6) Modify the close button blueprint to call Get Component By Class from the PlayerController, and select Inv_InteractComponent. Then call ToggleInventory from that. Pic is attached.

點擊查看代碼
// Fill out your copyright notice in the Description page of Project Settings.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Inv_InteractComponent.generated.h"
class UInv_InventoryComponent;
class UInv_HUDWidget;
class UInputMappingContext;
class UInputAction;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FShortItemDescription, UInv_ItemComponent*, ItemComponent);
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), Blueprintable)
class INVENTORYSYSTEM_API UInv_InteractComponent : public UActorComponent
{
GENERATED_BODY()
public:
UInv_InteractComponent();
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UFUNCTION(BlueprintCallable, Category = "Inventory")
void ToggleInventory();
FShortItemDescription CreateShortItemDescription;
FShortItemDescription DestroyShortItemDescription;
protected:
virtual void BeginPlay() override;
virtual void SetupInputComponent();
private:
void PrimaryInteract();
void CreateHUDWidget();
void TraceForItem();
TWeakObjectPtr<UInv_InventoryComponent> InventoryComponent;
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
TObjectPtr<UInputMappingContext> DefaultIMC;
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
TObjectPtr<UInputAction> PrimaryInteractAction;
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
TObjectPtr<UInputAction> ToggleInventoryAction;
//assign the class for our HUD Widget in the editor
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
TSubclassOf<UInv_HUDWidget> HUDWidgetClass;
UPROPERTY()
TObjectPtr<UInv_HUDWidget> HUDWidget;
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
double TraceLength;
// Cannot expose an ENum directly for changing in the editor with UPROPERTY. It muSt be wrapped in a
// TEnumAsByte<> for it to appear as a list.
UPROPERTY(EditDefaultsOnly, Category = "Inventory")
TEnumAsByte<ECollisionChannel> ItemTraceChannel;
TWeakObjectPtr<AActor> ThisActor; //actor hit in this frame
TWeakObjectPtr<AActor> LastActor; //actor hit last frame
};
點擊查看代碼
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventorySystem/InventoryManagement/Components/Inv_InteractComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InventorySystem/InventoryManagement/Components/Inv_InventoryComponent.h"
#include "InventorySystem/Interaction/Inv_Highlightable.h"
#include "InventorySystem/Items/Components/Inv_ItemComponent.h"
#include "Kismet/GameplayStatics.h"
#include "InventorySystem/Widgets/HUD/Inv_HUDWidget.h"
#include "InventorySystem/Widgets/Utils/Inv_WidgetUtils.h"
class UEnhancedInputLocalPlayerSubsystem;
UInv_InteractComponent::UInv_InteractComponent()
{
PrimaryComponentTick.bCanEverTick = true;
TraceLength = 500.0f;
ItemTraceChannel = ECC_GameTraceChannel1;
}
void UInv_InteractComponent::BeginPlay()
{
Super::BeginPlay();
SetupInputComponent();
CreateHUDWidget();
InventoryComponent = GetOwner()->FindComponentByClass<UInv_InventoryComponent>();
}
// *********************************************************************************************************
// Actor Components do not have a SetupInputComponent function that can be overriden.
// Therefore, this is defined manually and called from BeginPlay.
// Get the PlayerController this is attached to and then bind actions to the PC's Enhanced Input Component
// *********************************************************************************************************
void UInv_InteractComponent::SetupInputComponent()
{
if (const APlayerController* PC = Cast<APlayerController>(GetOwner()))
{
if (const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* EISubSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
EISubSystem->AddMappingContext(DefaultIMC, 0);
}
}
// Retrieve the EnhancedInputComponent from the PC
if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PC->InputComponent))
{
// now bind exactly as we did in SetupInputComponent on the PC
EIC->BindAction(PrimaryInteractAction, ETriggerEvent::Started,this, &ThisClass::PrimaryInteract);
EIC->BindAction(ToggleInventoryAction, ETriggerEvent::Started,this, &ThisClass::ToggleInventory);
}
}
}
void UInv_InteractComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
//Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
TraceForItem();
}
void UInv_InteractComponent::PrimaryInteract()
{
if (!ThisActor.IsValid()) return;
UInv_ItemComponent* ItemComp = ThisActor->FindComponentByClass<UInv_ItemComponent>();
if (!IsValid(ItemComp) || !InventoryComponent.IsValid()) return;
InventoryComponent->TryAddItem(ItemComp);
}
void UInv_InteractComponent::CreateHUDWidget()
{
APlayerController* PC = Cast<APlayerController>(GetOwner());
if (!PC->IsLocalController()) return;
HUDWidget = CreateWidget<UInv_HUDWidget>(PC, HUDWidgetClass);
if (IsValid(HUDWidget))
{
HUDWidget->AddToViewport();
}
}
void UInv_InteractComponent::ToggleInventory()
{
if (!InventoryComponent.IsValid()) return;
InventoryComponent->ToggleInventoryMenu();
if (InventoryComponent->IsMenuOpen())
{
HUDWidget->SetVisibility(ESlateVisibility::Hidden);
}
else
{
// 將鼠標指針恢復到中心點
if (APlayerController* PC = Cast<APlayerController>(GetOwner()))
{
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize); // 獲取視口大小
const FVector2D ViewportCenter = ViewportSize / 2.f; // 獲取中心點位置
PC->SetMouseLocation(ViewportCenter.X, ViewportCenter.Y);
}
HUDWidget->SetVisibility(ESlateVisibility::HitTestInvisible);
}
}
void UInv_InteractComponent::TraceForItem()
{
if (!IsValid(GEngine) || !IsValid(GEngine->GameViewport)) return;
const APlayerController* PC = Cast<APlayerController>(GetOwner());
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize); // 獲取視口大小
const FVector2D ViewportCenter = ViewportSize / 2.f; // 獲取中心點位置
FVector TraceStart;
FVector Forward;
bool bSuccessful = UGameplayStatics::DeprojectScreenToWorld(PC, ViewportCenter, TraceStart, Forward); // 轉換為世界位置
if (!bSuccessful) return;
const FVector TraceEnd = TraceStart + Forward * TraceLength;
FHitResult HitResult;
GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ItemTraceChannel); // Line Trace
LastActor = ThisActor;
ThisActor = HitResult.GetActor();
if (!ThisActor.IsValid())
{
// Hide Message
if (IsValid(HUDWidget)) HUDWidget->HidePickupMessage();
}
if (ThisActor == LastActor) return;
if (ThisActor.IsValid())
{
// High light
UActorComponent* Highlightable = ThisActor->FindComponentByInterface(UInv_Highlightable::StaticClass());
if (IsValid(Highlightable))
{
IInv_Highlightable::Execute_Highlight(Highlightable);
}
// Show Message
UInv_ItemComponent* ItemComponent = ThisActor->FindComponentByClass<UInv_ItemComponent>();
if (IsValid(ItemComponent))
{
if (IsValid(HUDWidget)) HUDWidget->ShowPickupMessage(ItemComponent->GetPickupMessage());
// Create Short ItemDescription
CreateShortItemDescription.Broadcast(ItemComponent);
}
}
if (LastActor.IsValid())
{
// UnHigh light
UActorComponent* Highlightable = LastActor->FindComponentByInterface(UInv_Highlightable::StaticClass());
if (IsValid(Highlightable))
{
IInv_Highlightable::Execute_UnHighlight(Highlightable);
}
UInv_ItemComponent* ItemComponent = LastActor->FindComponentByClass<UInv_ItemComponent>();
// Destroy Short ItemDescription
if (ItemComponent)
{
DestroyShortItemDescription.Broadcast(ItemComponent);
}
}
}
點擊查看代碼
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "Inv_PlayerController.generated.h"
/**
*
*/
UCLASS()
class INVENTORYSYSTEM_API AInv_PlayerController : public APlayerController
{
GENERATED_BODY()
public:
AInv_PlayerController();
virtual void Tick(float DeltaSeconds) override;
protected:
virtual void BeginPlay() override;
private:
};
點擊查看代碼
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventorySystem/Player/Inv_PlayerController.h"
AInv_PlayerController::AInv_PlayerController()
{
PrimaryActorTick.bCanEverTick = true;
}
void AInv_PlayerController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
}
void AInv_PlayerController::BeginPlay()
{
Super::BeginPlay();
}
Instructions
========================================
** Important: If you have been modifying your own PC for your own project, you will need to remove all the course code from it for this to work! If you are worried about bricking your project because it is currently working for you, don't do this on that project. Do it on a blank project like what we created for the course at the start. I am not liable for you screwing up your exiting project - this is intended to augment Sensei Stephen's course *as is*.
1) Add the Inv_InteractComponent files to the InventoryManagement/Components public and private folders
2) Replace your Inv_PlayerController files in the course with the empty ones I have attached if you want a quick replacement. Otherwise create your own empty PlayerController class.
3) Compile and launch editor
4) Make a blueprint of the Inv_InteractComponent class and fill in its fields in the Inventory category
5) Attach the component to the player controller blueprint alongside the Inv_InventoryComponent
****** Important change:
6) Modify the close button blueprint to call "Get Component By Class" from the PlayerController, and select Inv_InteractComponent. Then call ToggleInventory from that.
Once these changes are made, the plugin can be added to an project. Just make sure to add both components to the PC and you should be good.

浙公網安備 33010602011771號