VOICEROID実況をはじめてから。投稿者側になって考えたこと①
◆動画投稿はじめました
Unreal Engine練習中①
いろいろあって現在Unityを使わなくなってUnreal Engineの習得を始めているのですが、以前に買ったのにぜんぜん進めていなかった定番の書籍
これにまた最初から手を出すことになったんですが、せっかくの休暇ということで手を出したのはいいものの途中で詰まる……_(:3」∠)_
18.3章部分ですね。
解説の図なんですけどこれができないんですよ…。
デリゲートとデリゲート(参照渡し)と互換性がない、といわれて繋がらないまま。
( ゚Д゚)ウァアアアアッ!!
どうなってるんだろうとネットを検索していると解決策がありました。
こ!れ!!
そしてこの手段の通りにやると解決しました。
いままでUnreal Engineに魅力を感じたことがあまりなくて、Unityばかりだったんですがそうもいってられず。早く慣れないとなぁ。
そんなわけでして、UnityとCorgi Engineを使った記事はちょっと続くかが微妙です。
作ってみたいことは終えてしまったので……ちゃんと一本のゲームを作る、となると最初から企画しないとモチベーションが続かないみたいです。
このへん、機能を作ってみたい、と思えるプログラマーと決定的に違いますねぇ。
なにかまた、やりたいことが見つかったりしたらこのブログは活用したいと思います。
Corgi EngineVer5.0のアップデートのメモ
私情でバタバタしており更新が滞ってました……。
そんななかCorgi Engineが5.0にバージョンアップしてました。
-
エネミーのAIのアクションやシステムの追加
-
ボスの行動決定
-
新しい武器システムの追加
-
『非常に簡単にコンボベースの武器を作成する可能性を追加』(!!??
-
キャラクターの能力を追加(泳ぐ、飛ぶ、グライド?)
-
武器の所有者がヒットした時に攻撃中断オプション
-
武器を称している間、水平動作(反転)を防ぐオプション追加
-
ほか、バグや調整、画面フラッシュイベントの追加など
コンボベースの武器!?
(注:このブログはコンボアクションゲームを作りたいという動機のもとに書かれています)
■エネミーのAI
- 独自の使いやすいステートマシンを備えた新しい高度なAIシステムを追加しました。複雑な行動、パターンを作成し、意思決定と行動を組み合わせて独自の敵を創造する。
- 健康(注 Health:体力設定などのスクリプトかな?)、
ヒット、レイキャストターゲット検出、ワイドキャストターゲット検出、
エリアターゲット検出、距離ベースの決定、時間、接地の新しいAI決定を追加
- パトロール、エリア内のパトロール、ターゲットへの移動、
ジャンプ、撃墜、待機、飛行、武器の変更
■ボスのAI
- Corgiエンジンで複雑なボスの動作の最初の例であるRetroRabbitのボスを追加します。複数の破壊的な武器、フェイズ、ムーブメントを完成させる。
■コンボ武器
- ComboWeapon
- MeleeWeapon
- MeleeWeapon
- MeleeWeapon
Unity&Corgi Engineを使用したアクションゲームづくり ~近接武器で殴りたい①~
少しバタバタとしてました更新ができてませんでした。
ゲームづくり自体は続けてますよ(`・ω・´)
Anima2Dを使ってキャラクターのスケルタルアニメーション関連とかを勉強中です。
前回はアニメーション関連をいじってました。当たり判定の追従やらを行いましたが、いかんせん近接武器で殴りたいのに銃撃用の無駄なパラメーターが多いので見通しが悪いです。
これから改造することも考えて、また内部の構造を知るためにスクリプトを組み替えましょう。
(本来はパラメーターの調整などを行う前にやるべきですが、先に仕組みを知りたいのでこうなりました)
前回はこちらです。
blue-mist.hatenablog.jp
■武器の構成
Corgi Engineで使われている武器の構成は以下のような構造になっています。
- CharacterHandleWeapon
- 所持する武器
- 入力受付
- 状態管理
- Weapon
- 攻撃時間
- アニメーション
- 攻撃範囲、威力クラス
- 攻撃状態管理
- 弾の管理など
- MeleeWeapon
- 攻撃範囲の生成/消滅
CharacterHandleWeaponが入力を受け付け、所持している武器に対して攻撃開始の指示を出します。
対応する武器(MeleeWeapon)が攻撃範囲を生成します。
武器の状態は
攻撃開始⇒攻撃前⇒攻撃中⇒攻撃終了待機⇒攻撃終了
という状態を遷移していきますが、こちらはMeleeWeaponのもとのクラス、Weaponクラスが握っています。
終了後、MeleeWeaponは攻撃範囲をオフにして、CharacterHandleWeaponに管理を戻します。
ということで修正するべきスクリプトは先に挙げた三点ですね。
先に改造後のスクリプト名称を決めておきます(何のひねりもないですが)。
ちなみに少し手間がかかってももとのスクリプトを残したまま、継承などを使い組み直したほうがいいと思います。Corgi Engineでは関数をoverrideするだけで使えるように構成されていることも多いです。
- CharacterHandleWeapon
- ->CharacterHandleWeaponController
- Weapon
- ->SwordWeapon
- MeleeWeapon
- ->MeleeAttackWeapon
では元のCharacterHandleWeaponのスクリプトを改造、もしくはコピーして新たにスクリプトを作ります。
HandleInput()も銃関連が混じっているのでごくシンプルになります。
protected override void HandleInput () { if ((_inputManager.ShootButton.State.CurrentState == MMInput.ButtonStates.ButtonDown) || (ContinuousPress && (CurrentWeapon.TriggerMode == Weapon.TriggerModes.Auto) && (_inputManager.ShootButton.State.CurrentState == MMInput.ButtonStates.ButtonPressed))) { ShootStart(); } }
Animator関連のパラメータ項目の修正は、
/// <summary> /// 必要なアニメータパラメータが存在する場合、アニメータパラメータリストに追加します。 /// </summary> protected override void InitializeAnimatorParameters() { if (CurrentWeapon == null) { return; } RegisterAnimatorParameter(CurrentWeapon.IdleAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.StartAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.DelayBeforeUseAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.DelayBetweenUsesAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.StopAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.SingleUseAnimationParameter, AnimatorControllerParameterType.Bool); RegisterAnimatorParameter(CurrentWeapon.UseAnimationParameter, AnimatorControllerParameterType.Bool); } /// <summary> ///これをオーバーライドして、キャラクターのアニメーターにパラメーターを送信します。 ///これは、キャラクターによってサイクルごとに1回呼び出されます /// class、Early、normal、Late processの後)。 /// </summary> public override void UpdateAnimator() { if (CurrentWeapon == null) { return; } MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.IdleAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponIdle), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.StartAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponStart), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.DelayBeforeUseAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponDelayBeforeUse), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.UseAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponDelayBeforeUse || CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponUse || CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponDelayBetweenUses), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.SingleUseAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponUse), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.DelayBetweenUsesAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponDelayBetweenUses), _character._animatorParameters); MMAnimator.UpdateAnimatorBool(_animator, CurrentWeapon.StopAnimationParameter, (CurrentWeapon.WeaponStates.CurrentState == SwordWeapon.SwordWeaponStates.WeaponStop), _character._animatorParameters); }
ほとんどが消しただけです。
ChangeWeapon関数部分がWeaponをキャストしている部分があるので、これを近接武器専用クラス、仮にSwordWeaponという基底クラスを作って組み替えます。
元のWeaponクラスはReload、Ammo、Magazineなどの銃に関連した用語部分を消していく形です。
Animatorのパラメータ部分はCharacterHandleWeaponの項目と連動していますのでそちらに合わせるようにします。
また、Weapon States項目を近接武器用に、そちらも余計な項目を消して、enumとして新しく定義します。
// Weapon State public enum SwordWeaponStates {WeaponIdle, WeaponStart, WeaponDelayBeforeUse, WeaponUse, WeaponDelayBetweenUses, WeaponStop } [Header("Use")] // the delay before use, that will be applied for every shot public float DelayBeforeUse = 0f; // the time (in seconds) between two shots public float TimeBetweenUses = 0f; [Header("Position")] /// an offset that will be applied to the weapon once attached to /// the center of the WeaponAttachment transform. public Vector3 WeaponAttachmentOffset = Vector3.zero; /// キャラクターが反転した時に同じく反転する必要があるか? public bool FlipWeaponOnCharacterFlip = true; /// FlipValue は、フリップ上でモデルの transform's localscale を乗算するために使用されます。 /// 通常は-1, 1, 1, しかし、あなたのモデルの仕様に合わせてそれを変更することをお気軽に public Vector3 FlipValue = new Vector3(-1, 1, 1); [Header("Hands Position")] /// キャラクターの左手につけるべき変換 public Transform LeftHandHandle; /// キャラクターの右手につけるべき変換 public Transform RightHandHandle; [Header("Effects")] /// 武器を使用した際に発生させるエフェクト public List<ParticleSystem> ParticleEffects; [Header("Movement")] /// これがtrueなら、武器がactiveな間、乗数は動きに適用されるでしょう public bool ModifyMovementWhileAttacking = false; /// 攻撃中の動きに対する乗数 public float MovementMultiplier = 0f; [Header("Animation Parameter Names")] /// Idle:これは、武器が使用されている場合を除き、すべての時間を true になります public string IdleAnimationParameter; /// Start:武器が使用され始めているフレームにTrue public string StartAnimationParameter; /// DelayBeforeUse:武器がアクティブになっているが、まだ使用されていない場合は true public string DelayBeforeUseAnimationParameter; /// SingleUse:Shotがtrueの後、次のいずれかまたは武器の停止の前に public string SingleUseAnimationParameter; /// Use:武器が発射を開始したが、まだ停止していない各フレームで true public string UseAnimationParameter; /// DelayBetweenUses:武器が使用中の場合はTrue public string DelayBetweenUsesAnimationParameter; /// DelayBetweenUses:武器が使用中の場合はTrue public string DelayBetweenUsesAnimationIntegerParameter; /// Stop:武器が使用された後の次のいずれかの武器の使用前にTrue public string StopAnimationParameter; [Header("Sounds")] /// the sound to play when the weapon starts being used public AudioClip WeaponStartSfx; /// the sound to play while the weapon is in use public AudioClip WeaponUsedSfx; /// the sound to play when the weapon stops being used public AudioClip WeaponStopSfx; /// the sound to play when the weapon gets reloaded public AudioClip WeaponReloadSfx; /// the sound to play when the weapon gets reloaded public AudioClip WeaponReloadNeededSfx; /// the name of the inventory item corresponding to this weapon. /// Automatically set (if needed) by InventoryEngineWeapon public string WeaponID { get; set; } /// the weapon's owner public Character Owner { get; protected set; } /// the weapon's owner's CharacterHandleWeapon component public CharacterHandleWeapon CharacterHandleWeapon { get; set; } /// if true, the weapon is flipped public bool Flipped { get; protected set; } /// the WeaponAmmo component optionnally associated to this weapon //public WeaponAmmo WeaponAmmo { get; protected set; } /// 武器のステートマシン public MMStateMachine<SwordWeaponStates> WeaponStates; public HandleWeaponCategory WeaponCategory; protected SpriteRenderer _spriteRenderer; protected CharacterGravity _characterGravity; protected CharacterHorizontalMovement _characterHorizontalMovement; protected float _movementMultiplierStorage = 1f; protected bool _triggerReleased = false; protected Vector3 _weaponOffset; protected Vector3 _weaponAttachmentOffset; /// 武器の使用前などを測るためのカウンタ protected float _delayBeforeUseCounter = 0f; protected float _delayBetweenUsesCounter = 0f;
武器はProcessWeaponState()で状態管理がなされています。ここから少し組みなおさないといけなくなります。
銃の場合はShootRequest関数で
- 次の弾が入っているか?
- 弾が装填されて発射可能か?
といった状態を見て武器を使用状態に変更し攻撃を開始させます。
この辺りは近接武器なので必要ない項目になっていきますので削除していきます。
/// <summary> /// Called every lastUpdate, processes the weapon's state machine /// </summary> protected virtual void ProcessWeaponState() { ~~~~ case SwordWeaponStates.WeaponUse: WeaponUse(); _delayBetweenUsesCounter = TimeBetweenUses; WeaponStates.ChangeState(SwordWeaponStates.WeaponDelayBetweenUses); break; case SwordWeaponStates.WeaponDelayBetweenUses: _delayBetweenUsesCounter -= Time.deltaTime; if (_delayBetweenUsesCounter <= 0) { TurnWeaponOff(); } break; case SwordWeaponStates.WeaponStop: WeaponStates.ChangeState(SwordWeaponStates.WeaponIdle); break; } } /// <summary> /// 武器が発射できるかどうかを判断する /// </summary> protected virtual void ShootRequest() { // そのままWeponUseに切り替え WeaponStates.ChangeState(SwordWeaponStates.WeaponUse); } /// <summary> /// 武器を使用しているときの状態 /// </summary> protected virtual void WeaponUse() { SetParticleEffects(true); SfxPlayWeaponUsedSound(); } /// <summary> /// 武器を使用不可に /// </summary> public virtual void TurnWeaponOff() { _triggerReleased = true; SfxPlayWeaponStopSound(); WeaponStates.ChangeState(SwordWeaponStates.WeaponStop); ResetMovementMultiplier(); }
微調整はいりますがおおむねこんな感じになります。
作成したスクリプトを継承して、SwordWeaponクラスを継承した近接武器クラスMeleeAttackWeaponを新規作成します。
こちらはほぼもとのMeleeWeaponと変更は大差ないので割愛します。
このスクリプトを作成した後、キャラクターにCharacterHandleWeaponControllerを直接アタッチして使用できるようにします。
このとき、前回までのMeleeWeaponやCorgi SwordなどのWeaponを継承して作成したプレハブは使用できなくなりますので、MeleeWeaponも新しく作成し、CharacterHandleWeaponControllerのInitialWeaponに付け直すことを忘れずに行います。
動きが特に変わらず、攻撃判定が行われていれば成功です。
◆今後のカスタム
上述の通り、これで最低構成の形態を引き継ぐことができるようになります。
ですが、最初に考案した通り、今後コマンドやコンボ攻撃などを実装する際にはいろいろと不都合が生じます。
Corgi Engineでは一つの武器に対して
- 攻撃の威力
- 攻撃のノックバック値
- 攻撃時間、待機時間、攻撃硬直時間
- 攻撃範囲
などが紐づいて管理されています。ですから現時点では
- 攻撃の威力が固定のため、コンボ攻撃のメリットが少ない、攻撃の種類を増やすメリットがない
- ノックバック値が固定のため、攻撃①(ノックバック弱)⇒攻撃②(ノックバック強)という使い分け方ができない
- 敵を打ち上げたりといった処理(ノックバック値をY方向に)が行えない
といった不都合が生じます。
これを改善するには一つの攻撃に対して一つの武器として管理し、攻撃ごとに武器を変更する、という処理が一番構成を崩さず管理ができそうに思えます。
つまり、4種類のパラメータを武器ごとに調整して、攻撃を管理する方式ですね。
とりあえず思いつく限りはこんな感じになります。
攻撃は5種類ですが、コンボ攻撃の1~2段目といった共通化できそうな部分は共通武器を使います。
最後の攻撃だけノックバックと攻撃の威力を強めたり、といった微調整ができるようにします。
こうすることで攻撃アニメーションを追加しても後に武器パラメータを変更した違う武器を用意して対応することができます。
理想としては今回追加したMeleeAttackWeaponに図にあるような攻撃カテゴリを用意し、複数の武器を用意します。
そして現在では一種類しか所持することができないCharacterHandleWeaponControllerに武器種類を最初に定義し、アニメーションごとに武器を交換します。
といった流れになりますね。
次回はこれらの実装を行うことを目標にします。
今回の記事はおもにブログでソースコードを書くには、って感じの自分用の記事になりました('ω') ソースコードの埋め込み大きさでブログ全体のサイズ変わっちゃうのなんとかならないかなぁ……。
Unity&Corgi Engineを使ったアクションゲーム作り ~アニメーションと当たり判定②~
-
Use
-
Delay Before Use…使用前の遅延
-
Time Between Use…使用時間
-
Position
-
Weapon Attachment Offset…実際の装着している部分のオフセット
-
Damage Area
-
Damage Area Shape…当たり判定の形状
-
Area Size…当たり判定の大きさ
-
Area Offset…当たり判定のオフセット値
-
Damage Area Timing
-
Initial Delay…判定が出るまでの遅延時間
-
Active Duration…攻撃判定の時間
-
Damage Caused
-
Target Layer Mask…当たり判定を持つレイヤー属性の設定
-
Damage Caused…ダメージ量
-
Knockback…ノックバックのセットの仕方
-
Knockback Force…ノックバックの力
-
Invincibility Duration…無敵時間
Flip Weapon On Character Flipのチェックを外さないと逆側の判定がおかしくなります。
-
MeleeIdle…武器の入力待機状態
-
MeleeStart…武器の入力開始
-
MeleeUse…武器の攻撃アニメーション中
-
MeleeStop…武器の攻撃終了
季節感を出そうとするとワンテンポ遅れる ~小説投稿~
今日は小説の宣伝です。実は書いたりもしてます。
梅雨が明け始めて、この夏に踏み込みそうな時期に雨をモチーフにした小説を上げました!
……いつも季節感を出そうとするとワンテンポ遅くなるんです( 一一)
これを書いたのはスマートフォンが普及しはじめた頃になります。ずいぶんと前ですが、今やネットがあればだれとでも繋がれる世の中になりました。
今の世のひととの繋がりって、不特定多数に向けた発言が誰かに聞こえるような環境だと思うんですね。
私たちはいったい誰に向けて声を発しているのだろう?
そんな疑問を抱くことがあります。
私は雨の日に傘を差すのが好きなんですよね。路面に当たる雨音を聞きながら、切り離されたような感覚を覚えて落ち着くんですよね。
密に張り巡らされたコミュニケーションからすこし離れられて、一息つけるような。
今やみんな、まるで宝石箱を眺めるように、誰もが目の前の端末の中の世界に夢中です。それが身の回りで起こっていることに無関心になりはじめてしまっているような気がして。
現代は、誰かがすぐそばにいるような感覚の中で私たちは過ごしているんじゃないだろうか。
それは、幽霊に取り憑かれたような状態なのではないだろうか。
そう思ったことが書き始めた発端ですね。
久しぶりな投稿になりますが、最後までお読みくださると幸いです。
カクヨム 近況ノートより。
Unity&Corgi Engineを使ったアクションゲーム作り ~プレイヤーキャラクターのセットアップ~
-
Scale Xを2⇒-2にする
-
Animatorの
-
Apply Root Motionを切る(Animator準拠で座標移動などをさせる設定?だったような)
-
Culling Mode ⇒ Always Animate
-
Character Crouch しゃがみ
-
Character Dangling ぶら下がり
-
Character Jetpack ジェットパック
-
Character Wall Clinging 壁につかまる
-
Character Walljump 壁ジャンプ
こういう遷移図を書くのにGoogleドライブのプラグイン、draw.ioが便利です。