jpnEnemy Sort - B477042/GraduationProject GitHub Wiki
Index
- Enemy Character系列クラスの親クラスです。
- Enemy Actorたちの共通の特性を宣言しました。
UCLASS()
class ESCAPEGAME_API AEnemyCharacter : public AGameCharacter
{
public:
virtual void Attack() PURE_VIRTUAL(AEnemyCharacter::Attack,);
}
Attack??をPureVirtualと宣言し、Abstract classに作りました。
- Enemy Characterの共通的な特性は何なのか考えました。
- 「Paragon」のキャラクタ?「Howitzer」を利用して 作ったキャラクタ?です。
- 近接攻?のみ可能でしたが、改善作業によって遠距離攻?も可能になりました。
- CSVファイルにて、5タイプを作成しています。 タイプごとにステップが異なって表現されております。
- AI Perceptionとして??と視?を追加しました。
- 遠距離攻?アルゴリズムを??しています。
後でシュ?ティングゲ?ムを作る時に使えるアルゴリズムを作りたかったのです。
シュ?ティングゲ?ムについて勉?している途中、GruntにRayを利用した射?を適用させることにしました。
シュ?ティングゲ?ムをプレイした時の感?と'バトルフィ?ルド4'に?するDICEの?表で聞いた言葉を組み合わせました。
照準した地点に銃?が飛ぶ方式ではなく、その地点を中心に任意の範?だけ?が飛ぶという意味でした。
Gruntは?射する前にTargetに向かって?を回します。
銃口をスタ?ト点にGruntの前方DistOffset地点に球を生成します。
球?の任意の点と銃口をRangeの値を直線の長さにして直線を描きます。
そのラインの最初に衝突した地点にエフェクトを表示し、Playerならダメ?ジを?えます。
void AGruntCharacter::FireAttack()
{
//?射パ?ティクルの位置です。
FVector PosPSPlay = GetMesh()->GetSocketLocation(SockFirePointR);
//?射パ?ティクルの回?値です。
FRotator RotPSPlay = GetActorRotation();
// ?射地点から球までの距離です。
float DistOffset = 30.0f;
//有?射程距離です。
float Range = 3000.0f;
//球の半?
float Radius = 200.0f;
//球の中心位置
FVector Center = PosPSPlay + (GetActorForwardVector() * DistOffset);
//照準地点。球の位置でランダムにする。
FVector AimPoint;
AimPoint.X = FMath::RandRange(Center.X - Radius, Center.X + Radius);
AimPoint.Y = FMath::RandRange(Center.Y - Radius, Center.Y + Radius);
AimPoint.Z = FMath::RandRange(Center.Z - Radius, Center.Z + Radius);
//Rayの最後の地点
FVector EndPoint= AimPoint + (GetActorForwardVector() * Range);
FHitResult HitResult;
}
- 作業効率を改善するために、武器をC++基盤を作り、BPでMeshだけを設定する方法を試みました。
- 非同期ロードを通じてGunnerのマテリアルを呼び込み、Weaponもスポンサーにしました。
- 非同期ロードのためのアセットのアドレスをiniファイルで管理しました。
- Projectileを利用したシューティングを実現しました。
UE4のMovementの代わりに直接Movementを作りました。 - AWeaopnのUCompoennt_MagでObject poolingを通じてProjectileを管理しました。
OwnerがDestroyされるとAWeaponとUComponent_MagのProjectileが一緒にDestroyされるように処理されました。 - Gruntとは異なる方法で打ち上げを実現しました。 標的を中心に銃弾が飛ぶように飛ぶ姿を作りたかったのです。
銃口と標的の間の距離ベクトルを求め、その値にFireControl_DistanceOffsetを掛けました。
掛けて出てくる結果、値に球を作り、球の中の任意の地点を照準点にしました。
こうすると、標的に向かって高い命中率を見せながら細かく別の方向に飛んでいきます。
//Projectileを管理するためのコンポーネントです。
//MaxCapacityだけTArrayのサイズを割り当てた後、AGunnerBulletを指す方式です。
//BPで作られたWeaponで、Bulletのクラスを割り当てられるようにしました。
class ESCAPEGAME_API UComponent_Mag : public UActorComponent
{
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TArray<TSoftObjectPtr<AGunnerBullet>> Mag;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TSoftObjectPtr<AGunnerBullet> TopBullet;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (AllowPrivateAccess = true))
TSubclassOf<AGunnerBullet>SpawnBulletClass;
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
int idxBullet;
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
int MaxCapacity;
}
// Onwerが消えるとき呼び出すようにしました。
// BulletをDerefernceした後、Destoryを呼び出しました。
void UComponent_Mag::ClearBullet()
{
EGLOG(Error, TEXT("Clear Bullet"));
if (Mag.Num() == 0)
{
return;
}
for (auto it : Mag)
{
if (it.IsValid())
{
it.Get()->Destroy();
EGLOG(Log, TEXT("Destory"));
}
}
Mag.Empty();
TopBullet.Get();
}
// OwnerであるGunnerのBlackBoardのTargetのLocationを受けてきます。
bool AWeapon::Attack(const FVector& TargetLocation)
{
if (bIsEjcting)
{
return false;
}
FVector FireLocation = MainBody->GetSocketLocation(Name_Muzzle);
FVector FireDirection = CalcFireDirection(TargetLocation);
FRotator FireRotation = CalcRotationForBullet(FireDirection);
//Magに発射情報を送ってくれます。
Mag->FireBullet(FireLocation, FireRotation, FireDirection);
bIsEjcting = true;
Anim->SetIsFired(true);
return true;
}
FVector AWeapon::CalcFireDirection(const FVector& TargetLocation)
{
FVector Retval = TargetLocation;
FVector MuzzleLocation = MainBody->GetSocketLocation(Name_Muzzle);
//Muzzle to TargetLocation
FVector DistanceVector = TargetLocation - MuzzleLocation;
//Spread Sphere Center Location
FVector SphereCenter = DistanceVector* FireControl_DistanceOffset;
//照準地点。球の位置でランダムにする。
FVector AimPoint;
AimPoint.X = FMath::RandRange(SphereCenter.X - FireControl_Radius, SphereCenter.X + FireControl_Radius);
AimPoint.Y = FMath::RandRange(SphereCenter.Y - FireControl_Radius, SphereCenter.Y + FireControl_Radius);
AimPoint.Z = FMath::RandRange(SphereCenter.Z - FireControl_Radius, SphereCenter.Z + FireControl_Radius);
AimPoint.Normalize();
Retval = AimPoint;
return Retval;
}
//Reference https://amored8701.tistory.com/132
//外敵を利用する部分を参照
FRotator AWeapon::CalcRotationForBullet(const FVector& FireDirection)
{
FRotator Retval = GetActorRotation();
FVector FW = GetActorForwardVector();
float Dot = FVector::DotProduct(FW, FireDirection);
float Angle = FMath::Acos(Dot / (FW.Size() * FireDirection.Size()))*100;
EGLOG(Warning, TEXT("Angle : %f"), Angle);
FVector Cross = FVector::CrossProduct(FW,FireDirection);
if(Cross.Z>0)
{
Retval.Yaw += Angle;
return Retval;
}
Retval.Yaw += -Angle;
return Retval;
}
銃弾を進行方向に向けて回転させる計算をする過程が難しかったのです。
手で計算した結果と違う結果値が出て弾丸が異様に回転しました。
ゲームを一時停止してPlayerとWeponの座標を計算しましたが、値が異なりました。
コードをもう一度確認した結果、WeaponのRotation値を加えませんでした。
こんな小さなミスが時間を奪うことになると感じて反省するようになりました。
そして角度を計算するとき、外敵を使うことができるということを知りました。
- 「Paragon」のGideonアセットを使用しました。
- テレポートパターンを中心にアクションを作ることに集中しました。
どうすれば難しすぎたり、とても簡単ではないパターンを構成することができるのかに対する部分を重点としました。