Unreal Engine C++ The Ultimate Shooter Course (4)

Date:     Updated:

카테고리:

Chapter 10 Outline and Glow Effects

10-148 Outline Effect Theory

  • When Unreal Engine renders the scene, it uses several buffers
  • A buffer is just information pertaining to each pixel

buffer

  • Now, the reason we’re talking about these buffers is because in order to create an outline effect

depth

  • Now, Unreal Engine has an additional buffer called custom depth, and you’re allowed to select specific objects to participate in the custom depth buffer
  • Let’s take all the pixels that have no white on them and let’s call these pixels object pixels
  • No white neighbors, call these interior pixels
  • And here’s the result of subtracting away the interior pixels for each object(inverse object pixels) This is the basis for creating an outline effect

10-149 Post Process Materials

mat

pp

volume

level

adapt

color

post

blue

10-150 Custom Depth

check

buffer

10-151 Texel Position and Size

node

  • Texels are containers for pixels
  • Screen position is giving us the position on the screen and U, V coordinates for a given Texel

10-152 Show Interior Pixels

nodenext

now

sum

error

conclude

  • If it’s negative after ceil and clamp, it’s going to be zero And if it’s positive after ceil and clamp, it’s going to be one
  • Ceil에 대해 설명하면? 소수점을 무조건 올려 더 큰 정수로 만든 결과를 출력합니다 (참고)
  • Clamp에 대해 설명하면? 값을 받아 최소치와 최대치로 정의된 특정 범위로 제한시킵니다 (참고)

10-153 Getting The Border

edge

  • At Multiply, the only thing that these two have in common that are both one is the border

10-154 Adding the Border to the Scene Color

end

  • Lerp 노드에 대해 설명하면? If Alpha is 0.0, the first input is used If Alpha is 1.0, the second input is used (참고)

second

10-155 Hide Occluded Pixels

ppre

pre

conclude

10-156 Change Outline Thickness

texel

uv

10-157 Color Tinting Effect

conclude

10-158 Multiple Colors with Custom Depth Stencil

stencil

value

blue

create

dif

line

soft

occpro

10-159 Blend Materials with Material Functions

mf

mat

drag

cube

ori

next

guns

half

10-160 Fresnel Effect on Glow Material

par

fres

change

ref

more

effect

convert

last

10-161 Material Instances

ins

import

10-162 Scrolling Lines Effect

texture

problem

size

10-163 Enable Custom Depth in C++

void AShooterCharacter::TraceForItems()
{
	if (bShouldTraceForItems)
	{
		FHitResult ItemTraceResult;
		FVector HitLocation;
		TraceUnderCrosshairs(ItemTraceResult, HitLocation);
		if (ItemTraceResult.bBlockingHit)
		{
			TraceHitItem = Cast<AItem>(ItemTraceResult.Actor);
			if (TraceHitItem && TraceHitItem->GetPickupWidget())
			{
				TraceHitItem->GetPickupWidget()->SetVisibility(true);

				// Enable Custom Depth
				TraceHitItem->EnableCustomDepth();
			}

			if (TraceHitItemLastFrame)
			{
				if (TraceHitItem != TraceHitItemLastFrame)
				{
					TraceHitItemLastFrame->GetPickupWidget()->SetVisibility(false);

					// Disable Custom Depth
					TraceHitItemLastFrame->DisableCustomDepth();
				}
			}
			TraceHitItemLastFrame = TraceHitItem;
		}
	}
	else if (TraceHitItemLastFrame)
	{
		TraceHitItemLastFrame->GetPickupWidget()->SetVisibility(false);

		// Disable Custom Depth
		TraceHitItemLastFrame->DisableCustomDepth();
	}
}

10-164 Dynamic Material Instances

  • So we’re going to add this material index as a variable on the item class
  • Now, the next thing we’re going to do if we want to be able to change materials properties at runtime is we’re going to use something called a dynamic material A dynamic material instance allows us to use a material instance and set properties on that material instance at runtime
  • So why two different variables? The material instance is what we will select in our blueprint That way we’ll know which material instance to use And once we create a dynamic material instance, we’re going to use this material instance in that dynamic material instance
  • The construction script is a script that runs under certain conditions It runs if we move our actor in the world or if we change a property on the actor This is run before the game starts Nothing in the construction script will be run during the game
  • OnConstruction() is the C++ version of the construction script

dynamic

void AItem::OnConstruction(const FTransform& Transform)
{
	if (MaterialInstance)
	{
		DynamicMaterialInstance = UMaterialInstanceDynamic::Create(MaterialInstance, this);

		// It's going to assign this instance for the material with the given material index
		ItemMesh->SetMaterial(MaterialIndex, DynamicMaterialInstance);
	}
}

10-165 Enable Glow Material in C++

code

10-166 Show Outline While Interping

type

in

void AItem::StartItemCurve(AShooterCharacter* Char)
{
	//..

	// 이코드가 필요한 이유는?
	// ItemCurve가 진행되는 동안에는 CustomDepth가 살아있어야 하기 때문이다
	bCanChangeCustomDepth = false;
}

10-167 Curve Vector for Material Parameters

curve

addkey

palse

void AItem::UpdatePulse()
{
	if (ItemState != EItemState::EIS_Pickup) return;

	const float ElapsedTime{ GetWorldTimerManager().GetTimerElapsed(PulseTimer) };
	if (PulseCurve)
	{
		FVector CurveValue{ PulseCurve->GetVectorValue(ElapsedTime) };

		DynamicMaterialInstance->SetScalarParameterValue(TEXT("GlowAmount"), CurveValue.X * GlowAmount);
		DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelExponent"), CurveValue.Y * FresnelExponent);
		DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelReflectFraction"), CurveValue.Z * FresnelReflectFraction);
	}
}

10-168 Material Pulse When Interping

again

sector

conclude

void AItem::UpdatePulse()
{
	float ElapsedTime{};
	FVector CurveValue{};
	switch (ItemState)
	{
	case EItemState::EIS_Pickup:
		if (PulseCurve)
		{
			ElapsedTime = GetWorldTimerManager().GetTimerElapsed(PulseTimer);
			CurveValue = PulseCurve->GetVectorValue(ElapsedTime);
		}
		break;
	case EItemState::EIS_EquipInterping:
		if (InterpPulseCurve)
		{
			// ItemInterpTimer를 사용해도 되는 이유는?
			// itemZCurve의 특정 구간과 MaterialPulseCurve의 특정 구간을 동기화 시켜
			// 해당 구간을 반짝이게 하고 싶기 때문이다
			ElapsedTime = GetWorldTimerManager().GetTimerElapsed(ItemInterpTimer);
			CurveValue = InterpPulseCurve->GetVectorValue(ElapsedTime);
		}
		break;
	}
	if (DynamicMaterialInstance)
	{
		DynamicMaterialInstance->SetScalarParameterValue(TEXT("GlowAmount"), CurveValue.X * GlowAmount);
		DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelExponent"), CurveValue.Y * FresnelExponent);
		DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelReflectFraction"), CurveValue.Z * FresnelReflectFraction);
	}
}
void AItem::StartItemCurve(AShooterCharacter* Char)
{
	//..

	// 이 코드의 목적은?
	// 타이머 초기화
	GetWorldTimerManager().ClearTimer(PulseTimer);

	//..
}

10-169 Inventory Slot Widget

2d

bp

ammo

10-170 Inventory Bar Widget

create

ammo

end

10-171 Add Button Icons to the Inventory Bar

conclude

10-172 Add Inventory Bar to the ShooterHUDOverlay

insert

10-173 Inventory Array in C++

class SHOOTER_API AShooterCharacter : public ACharacter
{
	//..

	// 헤더파일에서 유니폼 초기화가 가능하다
	const int32 INVENTORY_CAPACITY{ 6 };

	//..
}

inven

slot

last

10-174 Binding the Background Icon

bind

button

good

wis

node

10-175 Binding the Item Icon

newbind

end

possible

10-176 Binding the Ammo Icon

re

ammo

10-177 Binding Weapon Ammo Text

createb

nice

value

10-178 Set Item State for Picked Up

void AShooterCharacter::GetPickupItem(AItem* Item)
{
	//..

	if (Weapon)
	{
		// 이 코드의 결과는?
		// 슬롯 인덱스가 하나씩 밀려나간다
		Weapon->SetSlotIndex(Inventory.Num());
		if (Inventory.Num() < INVENTORY_CAPACITY)
		{
			Inventory.Add(Weapon);
			Weapon->SetItemState(EItemState::EIS_PickedUp);
		}
		else // Inventory is full! Swap with EquippedWeapon
		{
			SwapWeapon(Weapon);
		}
	}

	//..
}

10-179 Send Slot Index with a Delegate

new

del

nodegood

10-180 Play Widget Animation

ani

move

box

reverse

col

invenbar

10-181 Exchange Inventory Items

void AShooterCharacter::SwapWeapon(AWeapon* WeaponToSwap)
{
	//..

	// 아래 코드에 대해 설명하면?
	// 인벤토리의 아이템이 교체된다
	if (Inventory.Num() - 1 >= EquippedWeapon->GetSlotIndex())
	{
		Inventory[EquippedWeapon->GetSlotIndex()] = WeaponToSwap;
		WeaponToSwap->SetSlotIndex(EquippedWeapon->GetSlotIndex());
	}

	//..
}
void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex)
{
	if ((CurrentItemIndex == NewItemIndex) || (NewItemIndex >= Inventory.Num()) || (CombatState != ECombatState::ECS_Unoccupied)) return;
	auto OldEquippedWeapon = EquippedWeapon;
	auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]);
	EquipWeapon(NewWeapon);

	// 아래 상태는 무슨 상태로 전환 되는 것인지 설명하면?
	// 장착하고 있지는 않으나 인벤토리에 있는 상태
	OldEquippedWeapon->SetItemState(EItemState::EIS_PickedUp);
	NewWeapon->SetItemState(EItemState::EIS_Equipped);
}

10-182 Disable Trace While Interping

void AShooterCharacter::SelectButtonPressed()
{
	if (CombatState != ECombatState::ECS_Unoccupied) return;
	if (TraceHitItem)
	{
		TraceHitItem->StartItemCurve(this);

		// 아래 코드가 필요한 이유는?
		// It's not going to allow us to hit the select button again and
		// Start the item curve again when we just started the item curve
		TraceHitItem = nullptr;
	}
}

10-183 Prevent Swapping while Reloading

void AShooterCharacter::SelectButtonPressed()
{
	// 아래 코드가 필요한 이유는?
	// ECS_Unoccupied 상태가 아니면 아이템을 얻지 못하게 하기 위함
	if (CombatState != ECombatState::ECS_Unoccupied) return;
	if (TraceHitItem)
	{
		TraceHitItem->StartItemCurve(this);
		TraceHitItem = nullptr;
	}
}
void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex)
{
	// 아래 코드에서 가장 중요한 것을 설명하면?
	// ECombatState::ECS_Unoccupied 상태가 아니면 아이템 교환을 못하게 한다
	if ((CurrentItemIndex == NewItemIndex) || (NewItemIndex >= Inventory.Num()) || (CombatState != ECombatState::ECS_Unoccupied)) return;
	auto OldEquippedWeapon = EquippedWeapon;
	auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]);
	EquipWeapon(NewWeapon);

	OldEquippedWeapon->SetItemState(EItemState::EIS_PickedUp);
	NewWeapon->SetItemState(EItemState::EIS_Equipped);
}

10-184 Equip Montage

dup

remove

mont

info

finish

each

zero

10-185 Play Equip Sound while Swapping

void AItem::PlayEquipSound(bool bForcePlaySound)
{
	if (Character)
	{
		// bForcePlaySound 매개변수가 필요한 이유는?
		// 사운드를 강제적으로 내야할 상황에 사용하기 위함이다
		if (bForcePlaySound)
		{
			if (EquipSound)
			{
				UGameplayStatics::PlaySound2D(this, EquipSound);
			}
		}
		else if (Character->ShouldPlayEquipSound())
		{
			Character->StartEquipSoundTimer();
			if (EquipSound)
			{
				UGameplayStatics::PlaySound2D(this, EquipSound);
			}
		}
	}
}

10-186 Swap Pickup Text

bind

node

10-187 Swap Animation Limitations

void AShooterCharacter::EquipWeapon(AWeapon* WeaponToEquip, bool bSwapping)
{
	if (WeaponToEquip)
	{
		const USkeletalMeshSocket* HandSocket = GetMesh()->GetSocketByName(
			FName("RightHandSocket"));
		if (HandSocket)
		{
			// Attach the Weapon to the hand socket RightHandSocket
			HandSocket->AttachActor(WeaponToEquip, GetMesh());
		}

		if (EquippedWeapon == nullptr)
		{
			// -1 == no EquippedWeapon yet. No need to reverse the icon animation
			EquipItemDelegate.Broadcast(-1, WeaponToEquip->GetSlotIndex());
		}
		// bSwapping 변수가 필요한 이유를 설명하면?
		// 무기를 교체하는 상황이 아니라 무기를 얻고있는 상황에서는 UI 애니메이션이 동작하지 않게 하기위해
		else if(!bSwapping)
		{
			EquipItemDelegate.Broadcast(EquippedWeapon->GetSlotIndex(), WeaponToEquip->GetSlotIndex());
		}

		// Set EquippedWeapon to the newly spawned Weapon
		EquippedWeapon = WeaponToEquip;
		EquippedWeapon->SetItemState(EItemState::EIS_Equipped);
	}
}
void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex)
{
	// ECombatState::ECS_Equipping 상태를 추가한 이유를 설명하면?
	// 무기를 교체중인 상황에서 다시 교체를 시도했을때 기다리지 않고 교체되게 하기 위함이다
	const bool bCanExchangeItems =
		(CurrentItemIndex != NewItemIndex) &&
		(NewItemIndex < Inventory.Num()) &&
		(CombatState == ECombatState::ECS_Unoccupied || CombatState == ECombatState::ECS_Equipping);

	if (bCanExchangeItems)
	{
		auto OldEquippedWeapon = EquippedWeapon;
		auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]);
		EquipWeapon(NewWeapon);

		OldEquippedWeapon->SetItemState(EItemState::EIS_PickedUp);
		NewWeapon->SetItemState(EItemState::EIS_Equipped);

		CombatState = ECombatState::ECS_Equipping;
		UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
		if (AnimInstance && EquipMontage)
		{
			AnimInstance->Montage_Play(EquipMontage, 1.0f);
			AnimInstance->Montage_JumpToSection(FName("Equip"));
		}
		NewWeapon->PlayEquipSound(true);
	}
}

10-188 Create Icon Animation

margin

trackone

render

small

arrow

op

scale

10-189 Create Icon Highlight Delegate

// 아이콘 애니메이션 델리게이트 선언
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FHighlightIconDelegate, int32, SlotIndex, bool, bStartAnimation);

10-190 Functions to Broadcast Icon Highlight Delegate

void AShooterCharacter::HighlightInventorySlot()
{
	// 아래 코드에 대해 설명하면?
	// 델리게이트를 호출하는 코드이다
	const int32 EmptySlot{ GetEmptyInventorySlot() };
	HighlightIconDelegate.Broadcast(EmptySlot, true);
	HighlightedSlot = EmptySlot;
}

10-191 Calling Highlight and UnHighlight Inventory Slot

void AItem::FinishInterping()
{
	bInterping = false;
	if (Character)
	{
		Character->IncrementInterpLocItemCount(InterpLocIndex, -1);
		Character->GetPickupItem(this);

		// Important
		Character->UnHighlightInventorySlot();
	}

	//..
}
void AItem::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor)
	{
		AShooterCharacter* ShooterCharacter = Cast<AShooterCharacter>(OtherActor);
		if (ShooterCharacter)
		{
			ShooterCharacter->IncrementOverlappedItemCount(1);

			// Important
			ShooterCharacter->UnHighlightInventorySlot();
		}
	}
}

10-192 Assigning the Icon Delegate

assign

next

ani

hidden

high

vari

copy

nice

conre

10-193 Data Tables FTableRowBase

USTRUCT(BlueprintType)
struct FItemRarityTable : public FTableRowBase
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		FLinearColor GlowColor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		FLinearColor LightColor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		FLinearColor DarkColor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		int32 NumberOfStars;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		UTexture2D* IconBackground;
};
class SHOOTER_API AItem : public AActor
{
	GENERATED_BODY()

	//..

	/** Item Rarity data table */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true"))
		class UDataTable* ItemRarityDataTable;

	//..
}
  • Now, UDataTable is not the same thing as FTableRowBase
  • OnConstruction() 함수에 대해 설명하면? It gets called When the item changes or when we move it in the world

table

data

ref

10-194 Accessing Data Table Rows in C++

void AItem::OnConstruction(const FTransform& Transform)
{
	if (MaterialInstance)
	{
		DynamicMaterialInstance = UMaterialInstanceDynamic::Create(MaterialInstance, this);
		ItemMesh->SetMaterial(MaterialIndex, DynamicMaterialInstance);
	}

	EnableGlowMaterial();

	// Load the data in the Item Rarity Data Table

	// Path to the Item Rarity Data Table
	FString RarityTablePath(TEXT("DataTable'/Game/_Game/DataTable/ItemRarityDataTable.ItemRarityDataTable'"));
	UDataTable* RarityTableObject = Cast<UDataTable>(StaticLoadObject(UDataTable::StaticClass(), nullptr, *RarityTablePath));
	if (RarityTableObject)
	{
		FItemRarityTable* RarityRow = nullptr;
		switch (ItemRarity)
		{
		case EItemRarity::EIR_Damaged:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Damaged"), TEXT(""));
			break;
		case EItemRarity::EIR_Common:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Common"), TEXT(""));
			break;
		case EItemRarity::EIR_Uncommon:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Uncommon"), TEXT(""));
			break;
		case EItemRarity::EIR_Rare:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Rare"), TEXT(""));
			break;
		case EItemRarity::EIR_Legendary:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Legendary"), TEXT(""));
			break;
		}

		if (RarityRow)
		{
			GlowColor = RarityRow->GlowColor;
			LightColor = RarityRow->LightColor;
			DarkColor = RarityRow->DarkColor;
			NumberOfStars = RarityRow->NumberOfStars;
			IconBackground = RarityRow->IconBackground;
		}
	}
}

rarity

  • OnConstruction() 함수에 대해 설명하면? It gets called When the item changes or when we move it in the world
  • StaticLoadObject()에 대해 설명하면? Find or load an object by string name with optional outer and filename specifications (참고)

10-195 Setting Widget Colors from Data Table Values

sel

seln

10-196 Setting Glow Color from Data Table Value

void AItem::OnConstruction(const FTransform& Transform)
{
	// Load the data in the Item Rarity Data Table

	// Path to the Item Rarity Data Table
	FString RarityTablePath(TEXT("DataTable'/Game/_Game/DataTable/ItemRarityDataTable.ItemRarityDataTable'"));
	UDataTable* RarityTableObject = Cast<UDataTable>(StaticLoadObject(UDataTable::StaticClass(), nullptr, *RarityTablePath));
	if (RarityTableObject)
	{
		FItemRarityTable* RarityRow = nullptr;
		switch (ItemRarity)
		{
		case EItemRarity::EIR_Damaged:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Damaged"), TEXT(""));
			break;
		case EItemRarity::EIR_Common:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Common"), TEXT(""));
			break;
		case EItemRarity::EIR_Uncommon:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Uncommon"), TEXT(""));
			break;
		case EItemRarity::EIR_Rare:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Rare"), TEXT(""));
			break;
		case EItemRarity::EIR_Legendary:
			RarityRow = RarityTableObject->FindRow<FItemRarityTable>(FName("Legendary"), TEXT(""));
			break;
		}

		if (RarityRow)
		{
			GlowColor = RarityRow->GlowColor;
			LightColor = RarityRow->LightColor;
			DarkColor = RarityRow->DarkColor;
			NumberOfStars = RarityRow->NumberOfStars;
			IconBackground = RarityRow->IconBackground;
		}
	}

	// 이코드가 여기있는 이유는?
	// 위에서 GlowColor를 불러오고 난뒤 Set을 해줘야 하기 때문이다
	if (MaterialInstance)
	{
		DynamicMaterialInstance = UMaterialInstanceDynamic::Create(MaterialInstance, this);
		DynamicMaterialInstance->SetVectorParameterValue(TEXT("FresnelColor"), GlowColor);
		ItemMesh->SetMaterial(MaterialIndex, DynamicMaterialInstance);

		EnableGlowMaterial();
	}
}

10-197 Set Post Process Highlight Color from Data Table

struct FItemRarityTable : public FTableRowBase
{
	GENERATED_BODY()

	//..

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		int32 CustomDepthStencil;
};

remind

table

void AItem::OnConstruction(const FTransform& Transform)
{
	//..

	if (GetItemMesh())
	{
		GetItemMesh()->SetCustomDepthStencilValue(RarityRow->CustomDepthStencil);
	}

	//..
}
  • What does StaticLoadObject do? Takes a UClass and a path and constructs an instance of the specified class


Chapter 11 Multiple Weapon Types

11-198 Weapon Data Table

new

newdata

11-199 Getting Data from Weapon Data Table

void AWeapon::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);
	const FString WeaponTablePath{ TEXT("DataTable'/Game/_Game/DataTable/WeaponDataTable.WeaponDataTable'") };
	UDataTable* WeaponTableObject = Cast<UDataTable>(StaticLoadObject(UDataTable::StaticClass(), nullptr, *WeaponTablePath));

	if (WeaponTableObject)
	{
		FWeaponDataTable* WeaponDataRow = nullptr;
		switch (WeaponType)
		{
		case EWeaponType::EWT_SubmachineGun:
			WeaponDataRow = WeaponTableObject->FindRow<FWeaponDataTable>(FName("SubmachineGun"), TEXT(""));
			break;
		case EWeaponType::EWT_AssaultRifle:
			WeaponDataRow = WeaponTableObject->FindRow<FWeaponDataTable>(FName("AssaultRifle"), TEXT(""));
			break;
		}

		if (WeaponDataRow)
		{
			AmmoType = WeaponDataRow->AmmoType;
			Ammo = WeaponDataRow->WeaponAmmo;
			MagazineCapacity = WeaponDataRow->MagazingCapacity;
			SetPickupSound(WeaponDataRow->PickupSound);
			SetEquipSound(WeaponDataRow->EquipSound);
			GetItemMesh()->SetSkeletalMesh(WeaponDataRow->ItemMesh);
			SetItemName(WeaponDataRow->ItemName);
			SetIconItem(WeaponDataRow->InventoryIcon);
			SetAmmoIcon(WeaponDataRow->AmmoIcon);
		}
	}
}

cons

weapon

bp

11-200 Assault Rifle Glow Material

fun

drag

nice

next

re

copy

nmat

move

convert

must

create

color

final

11-201 Set Material Instance and Index with Data Table

struct FWeaponDataTable : public FTableRowBase
{
	//..

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		UMaterialInstance* MaterialInstance;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		int32 MaterialIndex;
}
void AWeapon::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);

	//..

	SetMaterialInstance(WeaponDataRow->MaterialInstance);
	// 아래 두 구문이 필요한 이유는?
	// 무기마다 material 정보가 다르다 때문에 이전 material 정보를 지워 줘야한다
	PreviousMaterialIndex = GetMaterialIndex();
	GetItemMesh()->SetMaterial(PreviousMaterialIndex, nullptr);
	SetMaterialIndex(WeaponDataRow->MaterialIndex);

	// Item 클래스에서 아래 구문을 이미 실행하고 있는데 여기서 또 하는 이유는?
	// Dynamic으로 Material를 교체해주기 위해
	if (GetMaterialInstance())
	{
		SetDynamicMaterialInstance(UMaterialInstanceDynamic::Create(GetMaterialInstance(), this));
		GetDynamicMaterialInstance()->SetVectorParameterValue(TEXT("FresnelColor"), GetGlowColor());
		GetItemMesh()->SetMaterial(GetMaterialIndex(), GetDynamicMaterialInstance());

		EnableGlowMaterial();
	}
}

table

tablenext

11-202 Adding Barrel Socket to AR

socket

11-203 FABRIK IK

  • What is the Fabric? Fabric is an inverse kinematics technique that allows us to move a particular bone on our skeleton

check

change

fab

maken

reconnect

remove

this

pose

11-204 Switch On Weapon Type

create

addpin

nice

11-205 Disable FABRIK when Reloading

node

11-206 AR Reload Animation Bone Name

find

dup

cur

11-207 Reload Montage Section in Data Table

pre

end

table

clear

move

11-208 AR AnimBP

refinish

cbp

vari

set

nodenext

nodeend

11-209 Set AnimBP for Weapon from Data Table

animbp

real

11-210 Different Crosshairs Per Weapon

smg

ar

11-211 Drawing Crosshairs from Weapon Variables

newcon

middle

11-212 More Weapon Properties in Data Table

createcue

sound

11-213 Use Weapon Properties in FireWeapon

struct FWeaponDataTable : public FTableRowBase
{
	//..

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float AutoFireRate;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	class UParticleSystem* MuzzleFlash;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	USoundCue* FireSound;
}
class SHOOTER_API AWeapon : public AItem
{
	//..

	/** The speed at which automatic fire happens */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true"))
	float AutoFireRate;

	/** Particle system spawned at the BarrelSocket */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true"))
	UParticleSystem* MuzzleFlash;

	/** Sound played when the weapon is fired */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true"))
	USoundCue* FireSound;

	//..

	FORCEINLINE float GetAutoFireRate() const { return AutoFireRate; }
	FORCEINLINE UParticleSystem* GetMuzzleFlash() const { return MuzzleFlash; }
	FORCEINLINE USoundCue* GetFireSound() const { return FireSound; }
}

11-214 Adding Pistol Assets

muti

11-215 Hide Bone By Name

hide

dup

bone

weapon

add

pistol

void AWeapon::BeginPlay()
{
	Super::BeginPlay();

	// 아래 코드에서 EPhysBodyOp::PBO_None를 세팅해준 이유는?
	// We don't care about physics for this
	if (BoneToHide != FName(""))
	{
		GetItemMesh()->HideBoneByName(BoneToHide, EPhysBodyOp::PBO_None);
	}
}

11-216 Pistol Icon

data

11-217 Pistol Data Table Properties

value

11-218 Barrel Socket on the Pistol

socket

11-219 Pistol Reload Animation

dup

reload

grab

sound

conclude

11-220 Pistol FABRIK IK

new

pis

add

11-221 Pistol Aiming Pose

re

idle

arm

nice

11-222 Aiming Pose for Each Weapon

ar

rot

how

ama

ready

11-223 Pistol Slide Curve

zang

curve

base

auto

11-224 Pistol Slide Timer

slide

void AShooterCharacter::FireWeapon()
{
	if (EquippedWeapon == nullptr) return;
	if (CombatState != ECombatState::ECS_Unoccupied) return;

	if (WeaponHasAmmo())
	{
		PlayFireSound();
		SendBullet();
		PlayGunfireMontage();
		EquippedWeapon->DecrementAmmo();

		StartFireTimer();

		// Important
		if (EquippedWeapon->GetWeaponType() == EWeaponType::EWT_Pistol)
		{
			// Start moving slide timer
			EquippedWeapon->StartSlideTimer();
		}
	}
}

11-225 Update Slide Displacement

void AWeapon::UpdateSlideDisplacement()
{
	if (SlideDisplacementCurve && bMovingSlide)
	{
		const float ElapsedTime{ GetWorldTimerManager().GetTimerElapsed(SlideTimer) };
		const float CurveValue{ SlideDisplacementCurve->GetFloatValue(ElapsedTime) };
		SlideDisplacement = CurveValue * MaxSlideDisplacement;
	}
}

11-226 Transform Pistol Slide Bone

bp

machine

pistol

float

graph

bone

add

complete

datapistol

sel

exist

re

right

end

11-227 Pistol Glow Material

cm

att

one

mat

inst

11-228 Semi Automatic Fire

false

rate

void AShooterCharacter::AutoFireReset()
{
	CombatState = ECombatState::ECS_Unoccupied;
	if (EquippedWeapon == nullptr) return;
	if (WeaponHasAmmo())
	{
		// Important
		if (bFireButtonPressed && EquippedWeapon->GetAutomatic())
		{
			FireWeapon();
		}
	}
	else
	{
		ReloadWeapon();
	}
}

11-229 Stop Aiming when Exchanging Weapons

void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex)
{
	const bool bCanExchangeItems =
		(CurrentItemIndex != NewItemIndex) &&
		(NewItemIndex < Inventory.Num()) &&
		(CombatState == ECombatState::ECS_Unoccupied || CombatState == ECombatState::ECS_Equipping);

	if (bCanExchangeItems)
	{
		// Important
		if (bAiming)
		{
			StopAiming();
		}

		//..
	}
}
void AShooterCharacter::AimingButtonPressed()
{
	// So now if the combat state is reloading or equipping, we will not aim
	bAimingButtonPressed = true;
	if (CombatState != ECombatState::ECS_Reloading && CombatState != ECombatState::ECS_Equipping)
	{
		Aim();
	}
}


맨 위로 이동하기

댓글남기기