地上を歩けて、空も飛べるキャラクター( ACharacter )の C++ な作り方
C++/TPSのテンプレプロジェクトを作ってソースを眺めると「地上を歩けるキャラクター」の作り方がわかります。そこで、追加要素として「この ACharacter
を空も飛べるようにしたい」と思っても、エディターで “Can Fly” をチェックONするだけではまったく飛べないので、どうしたらよいか整理します。
歩く、飛ぶの切り替え方法
ACharacter::GetCharacterMovement
で UCharacterMovementComponent
を取得して、 SetMovementMode
に移動モードの定数を放り込む。
// --> 飛行移動モード GetCharacterMovement()->SetMovementMode( MOVE_Flying ); // --> 歩行移動モード GetCharacterMovement()->SetMovementMode( MOVE_Walking );
歩く、飛ぶの他に今回の記事でも使う落下なんかもある。他にも泳ぐとかあるけど今回は使わないので説明略。
// --> 落下移動モード
GetCharacterMovement()->SetMovementMode( MOVE_Falling );
ジャンプをトリガーにして飛ばす
- ジャンプ中にもう一度ジャンプ入力が来たら飛ばす。
- 飛行中にジャンプ入力が来たら落下させる。
// AtpsCharacter::Jump をオーバーライド virtual void Jump() override;
void AtpsCharacter::Jump() { // 1. 落下移動モードでジャンプ入力 --> 飛行移動モードへ移行 if ( GetCharacterMovement()->IsFalling() ) GetCharacterMovement()->SetMovementMode( MOVE_Flying ); // 2. 飛行移動モードでジャンプ入力 --> 落下移動モードへ移行 else if ( GetCharacterMovement()->IsFlying() ) GetCharacterMovement()->SetMovementMode( MOVE_Falling ); // その他の移動モード(歩行など)でジャンプ入力 --> ジャンプ else Super::Jump(); }
これで AtpsCharacter
は最低限、歩いて飛べるキャラクターになる。
上昇/降下の入力を追加して飛ばす
- 飛行モードに対応したキャラクターは上昇/下降も入力で制御したい
- 歩行移動モードからジャンプしなくても、上昇の入力ですぐに飛行移動モードにしたい
- 落下中に上昇の入力でも飛行移動モードにしたい
(1)の実装には先だってエディターでプロジェクトの設定の入力から適当なAxis(例えば "MoveTop"
)とキーなどを割り当てておく。その上で、 void AtpsCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
内で、
PlayerInputComponent->BindAxis( "MoveTop", this, &AtpsCharacter::MoveTop);
としつつ、 AtpsCharacter::MoveTop
も実装する:
// for tpsCharacter.h void MoveTop( float Value );
// for tpsCharacter.cpp // TPSテンプレプロジェクトで始めれば既に定義済みのはずの MoveRight, MoveForward を参考にほぼそのまま軸の向きだけ変えればよい void AtpsCharacter::MoveTop( float Value ) { if ( ( Controller != NULL ) && ( Value != 0.0f ) ) { // TODO: このあとここに (2), (3) 用のコードを追加 // find out which way is right const FRotator Rotation = Controller->GetControlRotation(); const FRotator YawRotation( 0, Rotation.Yaw, 0 ); // get right vector const FVector Direction = FRotationMatrix( YawRotation ).GetUnitAxis( EAxis::Z ); // add movement in that direction AddMovementInput( Direction, Value ); } }
ここまでで飛行中に上昇/下降は可能になる。続けて、↑の MoveTop
の実装を少し工夫して (2), (3) も実現できる。
// TODO: このあとここに (2), (3) 用のコードを追加
この部分を次のように実装する。
// 2. 歩行移動モードからジャンプしなくても、上昇の入力ですぐに飛行移動モードにしたい // 3. 落下中に上昇の入力でも飛行移動モードにしたい if ( Value > 0 && !GetCharacterMovement()->IsFlying() ) GetCharacterMovement()->SetMovementMode( MOVE_Flying );
飛行中に足場に接触したら歩行移動モードにする
この機能を実装しようとすると嵌りそうなちょっとした罠がある。
Tick
でGetCharacterMovement()->IsFlying()
とGetCharacterMovement()->IsMovingOnGround()
をチェックして・・・IsMovingOnGround
はMODE_Walking
では意図通りに機能するけどMODE_Flying
では常にfalse
になるので使えません。
ACharacter::Landed( const FHitResult& )
をoverride
してGetCharacterMovement()->IsFlying()
なら・・・MODE_Flying
ではLanded
(ブループリント的にはOnLanded
) がそもそも発生しないので使えません。
と、言うわけで、この機能の実装は素直な当たり判定で処理する事になる。
// ACharacter::NotifyHit を override virtual void NotifyHit ( class UPrimitiveComponent* MyComp , AActor* Other , class UPrimitiveComponent* OtherComp , bool bSelfMoved , FVector HitLocation , FVector HitNormal , FVector NormalImpulse , const FHitResult& Hit ) override;
// 実装 void AtpsCharacter::NotifyHit ( class UPrimitiveComponent* MyComp , AActor* Other , class UPrimitiveComponent* OtherComp , bool bSelfMoved , FVector HitLocation , FVector HitNormal , FVector NormalImpulse , const FHitResult& Hit ) { Super::NotifyHit( MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse, Hit ); // 飛行移動モードかつ接触した物体から受ける法線が上向きから45[deg]未満ならば歩行移動モードへ移行する if ( GetCharacterMovement()->IsFlying() && acosf( FVector::DotProduct( HitNormal, FVector::UpVector ) ) < PI / 4 ) GetCharacterMovement()->SetMovementMode( MOVE_Walking ); }
ちなみに、この機能を実装しないと、地上に接しても空中飛行中と同じように移動しても構わないのでは?と思うかもしれない。それはそれで摩擦などを与えれば問題無いのかもしれないけれど、素直に地表では歩行モードに切り替えた方が自然な挙動となる場合が多いと思います。床がツルッツルの濡れた氷上という設定ならいいかもしれないけどね。
また、もし、固定角度ではなく、歩行移動モードで歩行可能な角度の場合にしたければ、 PI / 4
の部分を FMath::DegreesToRadians( GetCharacterMovement()->GetWalkableFloorAngle() )
にします。
こうして作った AtpsCharacter のプレイ動画
あとは飛行中にそれっぽいメッシュやスケルタルアニメーションに切り替えたり、用途に併せて細かな挙動付けや調整を施せば実用できると思います。
参考
UCharacterMovementComponent::SetMovementMode | Unreal Engine API Reference UCharacterMovementComponent | Unreal Engine API Reference
- ACharacter::Landed | Unreal Engine API Reference
- ACharacter::OnLanded | Unreal Engine API Reference
- AActor::NotifyHit | Unreal Engine API Reference
- GetWalkableFloorAngle | Unreal Engine Blueprint API Reference
- Making character class fly? - UE4 AnswerHub