iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
自我挑戰組

遊戲中的冰山一角-網路連線系列 第 20

Day20-Unreal連線初學(5)

  • 分享至 

  • xImage
  •  

今天將完子彈的完整功能!

首先是要完成子彈,這是今天要用在標投檔裡的程式碼:

// ProjectileBase.h

// 在protected裡面添加
protected:

	virtual void Destroyed() override;

	UFUNCTION(Category = "Projectile")
	void OnProjectileHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
  • Destroyed是UE本身就有寫的函式,他會在物件確定要被刪除的時候執行,這邊將它重新改寫成我們要的內容
  • OnProjectileHit式用來註冊在當SphereCollision碰撞到東西時要執行的函式,前面有提過UE的程式風格會將事件的函式前面加上On,而它裡面要用到的參數只要比照要註冊的原函式使用的參數即可。

接著是在子彈的.cpp裡要用的程式碼:

// ProjectileBase.cpp

// 在Beginplay中添加
BeginPlay()
{
	if(GetLocalRole() == ROLE_Authority)
		{
			SphereCollision->OnComponentHit.AddDynamic(this, &AProjectileBase::OnProjectileHit);
		}
}

// 實作Destroyed
Destroyed()
{
	FVector spawn_location = this->GetActorLocation();
	UGameplayStatics::SpawnEmitterAtLocation(this, ExplosionEffect, spawn_location, FRotator(0), true, EPSCPoolMethod::AutoRelease);
}

// 實作OnProjectileHit
OnProjectileHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector Impulse, const FHitResult& Hit)
{
	if(OtherActor)
	{
		UGameplayStatics::ApplyPointDamage(OtherActor, Damage, Impulse, Hit, GetInstigator()->Controller, this, DamageType);
	}

	Destroy();
}
  • 在BeginPlay裡面將我們自己寫的OnProjectileHit註冊在SphereCollision的OnComponentHit的事件上,並且先判斷Role讓註冊在伺服端執行。
  • 在Destroyed裡面生成爆炸的特效。
  • OnProjectileHit負責去實現當SphereCollision碰撞後要執行的內容,這邊先檢查是否有沒有碰到其他的Actor物件,這樣才不會讓ApplyPointDamage發生錯誤,而ApplyPointDamage就是之前提到過的CauseDamage,他會去觸發腳色身上的TakeDamage函式,最後再呼叫Destroy,這與Destroyed不同的是Destroy是執行刪除物件的動作,Destroyed是確認要被刪除時執行的動作。

在這邊就將子彈的基本都完成了,接著要回到腳色裡面實現發射子彈。


首先回到專案裡面設定操作按鈕,點選Edit然後進到Project Setting裡面,在左側列表找到Input然後在Action Mappings下面添加Shoot並設定為左滑鼠鍵。
https://ithelp.ithome.com.tw/upload/images/20220920/20151289yEiMJcCaoG.png
https://ithelp.ithome.com.tw/upload/images/20220920/201512890EOVNUQujo.png

接著開啟腳色的程式碼,這是要在標投檔裡新增的程式碼:

// NetworkDemoCharacter.h

// 在protected裡面添加
protected:

	UPROPERTY(EditDefaultsOnly, Category = "Projectile")
	TSubclassOf<AActor> Projectile;

	UPROPERTY(EditDefaultsOnly, Category = "Shoot")
	float ShootingRate;

	UFUNCTION(BlueprintCallable, Category = "Shoot")
	void Shooting();

	UFUNCTION(BlueprintCallable, Category = "Shoot")
	void StopShooting();

	UFUNCTION(Server, Reliable)
	void ControlShooting();

	bool bIsShooting;

	FTimerHandle ShootingTimer;
  • Projectile是用來設定在發射的時候要生成的物件類別
  • ShootingRate決定發射的頻率
  • Shooting與StopShooting負責射擊開始與射擊結束的時候要執行的邏輯,並且會將Shooting註冊在左滑鼠鍵上面。
  • ControlShooting是用來統整實際發射時要執行的內容,也是先前提到的RPCs的實際範例,指令的部分將它設定為Server與Reliable
  • bIsShooting是射擊的開關
  • ShootingTimer是一個計時器,會在一定的時間去執行我們想要的函式,因此是用來實現發射頻率要用到的函式

接著是.cpp檔中要添加的程式碼:

// NetworkDemoCharacter.cpp

// 在Constructor裡面添加
Constructor()
{
	ShootingRate = .2f;
	bIsShooting = false;
}

// 在SetupPlayerInputComponent裡面添加
SetupPlayerInputComponent()
{
	PlayerInputComponent->BindAction("Shoot", IE_Pressed, this, &ANetworkDemoCharacter::Shooting);
}

void ANetworkDemoCharacter::Shooting()
{
	if(!bIsShooting)
	{
		bIsShooting = true;
		UWorld* world = GetWorld();
		world->GetTimerManager().SetTimer(ShootingTimer, this, &ANetworkDemoCharacter::StopShooting, ShootingRate, false);
		ControlShooting();
	}
}

void ANetworkDemoCharacter::ControlShooting_Implementation()
{
		FVector current_location = this->GetActorLocation();
		FVector current_forward = this->GetActorForwardVector();

		FRotator current_rotation = this->GetControlRotation();
		FVector current_rotation_vector = this->GetControlRotation().Vector();

		FVector current_upvector = this->GetActorUpVector();
		FVector spawn_location = current_location + (current_rotation_vector * 100.f) + (current_upvector * 50.f);

		FActorSpawnParameters spawn_parameters;
		spawn_parameters.Instigator = this->GetInstigator();
		spawn_parameters.Owner = this;

		AActor* spawn_projectile = this->GetWorld()->SpawnActor<AActor>(Projectile, spawn_location, current_rotation, spawn_parameters);
}

void ANetworkDemoCharacter::StopShooting()
{
	bIsShooting = false;
}
  • 在SetupPlayerInputComponent裡面是整合所有按鍵綁定的的內容,在這邊將Shooting註冊在剛剛設定好的Shoot上面。
  • 實作Shooting裡面用bIsShooting確保不會連續發射,然後設定計時器將ShootingRate設定進去,只要時間一到就會自動去執行我們設定好的函式,也就是StopShooting。在這裡也要呼叫ControlShooting去執行真正在射擊時要做的事。
  • ControlShooting實作要加上_Implementation這在之前介紹RPCs中有提到,這個函式主要是負責生成子彈的流程
  • StopShooting會在計時器時間到的時候觸發,將bIsShooting設定為false讓玩家可以再次去射擊

上述都完成後就能回到專案中重新編譯,編譯成功後做一些基本的設定就可以來測試網路的功能。

首先打開Character在右邊可以找到Projectile的欄位,設定成我們自己創造的ProjectileBase_BP
https://ithelp.ithome.com.tw/upload/images/20220920/20151289WMhwYUWTTu.png
也可以看到下面有Shooting Rate讀者們可以自行調整,設定好之後點選左上角的編譯儲存。

打開開始模擬的設定選項將設定改成如下圖
https://ithelp.ithome.com.tw/upload/images/20220920/20151289UxKER10UoO.png

  • 打開新的編輯器視窗
  • 玩家數量設定成2
  • 網路模式設定成Listen Server

都設定完就可以開始執行遊戲,讀者會發現打開了兩個視窗並且腳色分別生成在一開始百在場景中的Player Start物件上,按下左鍵可以發射,當子彈碰撞到東西就會刪除並產生爆炸特效。

在OutputLog裡面可以看到我們設定的UE_LOG輸出的資訊
https://ithelp.ithome.com.tw/upload/images/20220920/20151289HJrLjTcPiS.png
按下shift + F1就可選擇另一個視窗進行遊玩。


上一篇
Day19-Unreal連線初學(4)
下一篇
Day21-Unreal連線初學(6)
系列文
遊戲中的冰山一角-網路連線30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言