主なコンテンツ

〜主なコンテンツ〜

1. Unityで製作したゲームと製作Tips
  1. 三月精チャレンジ(東方Project二次創作)
    1. 作り方
  2. 英語学習2D(オリジナルスマホアプリ)
2. UE4
3. ゲームアプリ見学
4. Bitbucket & SourceTreeでの一連の流れ
  1. 前半
  2. 後半
5. Tips
  1. UnityのTips
  5. SQL文のTips
  6. Final IK
  7. GearVR+Unity

2023年3月19日日曜日

45. Weapon Class

 おかえり。


さて、キャラクターは別として、ゲームで最も重要なクラスの 1 つは武器クラスです。 それでは、武器クラスを作成します。 この講義では、すべての武器に必要ないくつかの基本的なコンポーネントと、武器の状態の列挙型を追加します。 だから私たちは武器を扱う方法を知っています。 その状態次第です。


それでは始めましょう。 そのため、武器クラスが必要になります。 右クリックして [新しい C++ クラス] を追加します。 そして、私たちの武器は単に ACTOR にすることができます。 だから私はACTORを選ぶつもりです。 次へをクリックします。


そして、武器クラスと [Weapon] という独自のフォルダーを保持したいと思います。 後で、Weapon からさらに多くのクラスを派生させるときに、そのようにします。 これらすべての武器クラスを武器フォルダに貼り付けることができます。 さて、このクラスは「武器」と呼ばれます。 それでは[クラスを作成]をクリックしましょう。 したがって、このポップアップ メッセージで [いいえ] を押すことができます。


ここ Visual Studio には、武器クラスがあります。 今のところ、残りのタブを開く必要はありません。


[ Weapon.h] を右クリックして [これ以外をすべて閉じる] を選択します。 [Ctrl + K または O] を使用して、武器のヘッダー ファイルから [weapon.cpp] を開くことができます。


武器は武器フォルダにあるので、これまで見てきたように、ここではこの武器パーツは必要ありません。 そのため、[weapon.h] だけを含めるようにインクルードを変更すると、すべてのコンパイラ エラーが修正されます。

それでは、[weapon.h] ファイルに向かいましょう。ここには、コンストラクター beginPlay と tick 関数があります。 CHARACTER クラスで行ったのと同じように、これらのコメントは今は必要ないので削除します。


そして、すべての武器に必要な基本コンポーネントを追加したいと思います。 そのため、これらのコンポーネント変数用に [プライベート セクション] をここに作成できます。


先に進み、TICK をここの [public section] の Weapon コンストラクターのすぐ下に移動します。


[プライベート セクション] では、武器とすべてのアセットのメッシュが必要です。 ASSETS フォルダに移動して、[militaryWeaponsSilver] と [weapons] を確認します。 これらはすべて SKELETAL メッシュであることがわかります。 したがって、私の武器クラスには確実に SKELETAL メッシュ コンポーネントが必要です。


それでは、そのうちの 1 つを追加しましょう。 これは USkeletalMeshComponent になり、これを WeaponMesh と呼ぶことができます。


さて、weaponMesh はもちろん UProperty を取得しており、[visibleAnywhere] で作成します。 そして、ブループリントからコンポーネントにスケルタル メッシュを設定できるようになります。 では、これにカテゴリを付けて、そのカテゴリを [武器のプロパティ] と呼びます。


ここで、SKELETAL メッシュに加えて、キャラクターが近くにいるときにこの武器を装備できるように、ある種のオーバーラップ ボリュームが必要になります。


そこで、USphereComponent を前方宣言し、areaSphere という sphereComponent を使用します。 また、エリアスフィアは武器メッシュと同じプロパティを取得できます。


この武器を開発するにつれて、より多くのコンポーネントができあがります。 でも今のところ、これでよかったと思っています。 これらと [weapon.cpp] を作成してみましょう。


さて、これらのコンポーネントを作成する前に最初に行うことは、bCanEverTick を false に設定することです。 後で武器を作動させる必要があると判断した場合は、いつでもこれを変更できます。 しかし、今のところ false に設定します。


また、これらのコメントは実際には何の役にも立たないため、これらのコメントも削除します。


WeaponMesh を作成できます。 WeaponMesh = createDefaultSubObject として、USkeletalMeshComponent を使用します。 そして、これをweaponMeshと呼ぶことにします。


[weaponMesh->setupAttachment(rootComponent)] とします。

後で、この武器をドロップできるようにする予定です。ドロップするたびに、武器が壁の地面で跳ね返るようにしたいと思います。 そこで、ブロックするすべてのチャネルに衝突応答を設定します。 そのためには、weaponMesh->SetCollisionResponseToAllChannels と言うことができます。 衝突応答は ECollisionResponse::BLOCK になります。 したがって、完全にブロックするように設定されています。


ここで、ドロップしたら、ポーンを無視して、ポーンがそれをまたいで、衝突せずに通り抜けることができるようにします。 だから、weaponMesh->SetCollisionResponseToChannel と言って、その応答を ECollisionChannel に設定し、ポーンを選択します。 そして、その RESPONDS を ECollisionResponse::ignore に設定します。


もちろん、武器を落とすときはこれですべてですが、武器はその状態では開始されません。 そのまま歩いて持ち上げられる状態からスタートです。 そのため、最初から、weaponMesh の衝突を無効にしたいと考えています。 そのため、weaponMesh を取得し、setCollisionEnabled を ECollisionEnabled:: noCollision に呼び出すことでそれを行うことができます。


したがって、これらの衝突設定をそのまま維持できます。 そして、武器を実際に物と衝突させたい場合は、武器を落とすか投げるとします。次に、衝突を有効にして、物理のみを言うことができます。 またはAquarian Physicsまたはそのようなもの。

これで、areaSphere もできました。


エンジンのバージョンごとに変更される可能性のあるスケルタル メッシュ コンポーネント タイプを使用しても、エラーは発生しません。


そのため、インクルード エラーが発生した場合は、必ずそのヘッダーをインクルードしてください。


アクター クラスでこれを使用しなくても問題ありませんが、使用する sphereComponents を使用するには、sphereComponent ヘッダー ファイルをインクルードする必要があります。 それが [components/sphereComponent.h] にあることがわかっているので、それをインクルードして、areaSphere を作成します。 [areaSphere = createDefaultSubObject(USphere コンポーネント)] とします。 そして、これは単純にareaSphereという名前になります。


また、ルートにもareaSphereをアタッチしたいと思います。 [areaSphere->SetupAttachment(RootComponent)] ということになります。


さて、このエリアスフィアについて話しましょう。 この SPHERE は、CHARACTERS との重複を検出するために使用するものになります。 そしてそれらが重なり合ったら、武器を拾えるようにしたいと考えています。 ピックアップ ウィジェットのようなものを表示して、アイテムの状態などを設定できます。


しかし、[マルチプレイヤー ゲーム] と [マルチプレイヤー ゲーム内のシングル プレイヤー ゲーム] では、このタイプの違いがあります。 このような重要なことは、サーバー上でのみ実行することをお勧めします。


したがって、サーバー上にいる場合にのみ、areaSphere にオーバーラップ イベントを検出させたいと考えています。 したがって、areaSphere を取得して setCollisionResponseToAllChannels を呼び出したいと思います。 そして、ECollisionResponse::ignore を使用します。 そして、それはareaSphereが存在すらしないようなものです。


ここで、areaSphere とコンストラクターの衝突を無効にしてから、beginPlaying を実行したいと思います。 有効にしますが、サーバー上でのみ有効にします。


ここで、areaSphere->SetCollisionEnabled と言って ECollisionEnabled::NoCollision を渡します。 したがって、すべてのマシンで、これは衝突なしに設定されます。 しかし、サーバー マシンでは実際にコリジョンを有効にするように設定します。


先に進む前に、実際にはルートがないことに気付きました。 ルートをweaponMeshに設定します。 それでは、setRouteComponent を呼び出しましょう。 そしてweaponMeshを渡します。


サーバー上にいる場合は、衝突を有効にしたいと思います。 しかし、ここでコンストラクターでそれを行うのではなく、それを実行して beginPlay を開始します。 したがって、ここで権限があるかどうかを確認し、衝突を有効にすることができます。 後でオーバーラップ関数を追加するときに、それらをここにバインドして、サーバー上にある場合は beginPlay を実行できます。

ここで if チェックを入れます。 ここでできることは、getLocalRole を呼び出して、それが ENetRole::authority と等しいかどうかを確認することです。


その場合は、areaSphere で有効になっている衝突を呼び出すことができ、NoCollision の代わりに queryAndPhysics のようなものを使用できます。 また、オーバーラップさせたいチャネルに衝突応答を設定することもできます。これは、カプセルの衝突オブジェクト タイプであるため、ポーン衝突タイプになります。


そこで、areaSphere->setCollisionResponseToChannel と言います。 チャネルは ECollisionChannel になり、カプセルのコリジョン タイプであるポーン コリジョン タイプを選択します。 衝突応答は ECollisionResponse::Overlap になります。


だから私たちはこれを行うことができました。 しかし、私たちの役割が権威であるかどうかを確認する簡単な方法があります。 そして、それは hasAuthority 関数です。 これは同じことです。 ローカルの役割をチェックします。 そして、それが role [authority] と等しい場合、hasAuthority は True を返します。 これは、武器がレプリケート アクターである場合に当てはまります。 これは、サーバーがすべての武器オブジェクトを担当することを意味します。 したがって、私たちの武器はサーバー上でのみ権限を持ちます。 レプリケートするように設定する必要があり、コンストラクターでそれを行うことができます。 アクターには bReplicates ブール値があり、それを true に設定できます。

さて、このビデオを締めくくる前に最後に追加したいのは、武器の状態の列挙型です。 これを [weapon.h] のクラス宣言の直前に追加できます。


ここで、この列挙型の武器の状態を呼び出したいと思います。これはスコープ付きの列挙型になります。 つまり、EWeaponState という enum クラスを作成し、ここで uint8 を使用します。


したがって、enum 定数は符号なし 8 ビット整数であり、これもブループリント タイプになります。 そこで、blueprintType で UENUM を指定します。 そうすれば、この列挙型をブループリントの型として使用できます。


EWeaponsStates では、定数を列挙します。 EWeaponsState の前に EWS を付けます。


そして、初期状態が欲しいです。 これは、武器がワールドに置かれているときの状態です。 まだ拾ったことがなく、私たちのキャラクターは歩いて拾い上げて装備することができます.


EWS_initial 状態があり、これに UMETA マクロを指定して表示名を設定できます。これは表示名とブループリント用になります。 そして、これを【初期状態】と呼ぶことにします。


さて、イニシャルに加えて、実際に武器を装備したときの状態が欲しいです。 そこで、[装備] という別の列挙型定数を追加します。 その表示名は単に[装備]することができます。


そして、武器がドロップされたときの別の列挙型定数が必要です。 だから私はこれを[落とされた]と呼ぼう。 ここで、物理演算をオンにして、衝突をオンにして、武器を地面で跳ね返らせます。 だから私はこれに表示名を付けるつもりです[ドロップ]。


もちろん、ほとんどの enum 定数にはデフォルトの最大定数があり、これは [EWS_MAX] と呼ばれます。 そして、表示名 [default Max] を付けます。 そうすれば、この列挙型に実際に含まれる定数の数を知る必要がある場合は、EWS_Max の数値を確認できます。


よし、武器はこの武器状態列挙型を定義する。 このタイプの変数を武器クラスに追加してみましょう。 そして、これはプライベートでも構いません。 ここに貼り付けます。


EWeaponState、これをweaponStateと呼びます。 これを UProperty にします。 そして、BLUEPRINTS から見えるように、これを visibleAnywhere にします。


しかし、私はこの変数をゲーム ロジックから設定したいと考えており、それはほとんど C++ で行われます。 後でそれをブループリントに公開することに決めた場合は、公開できます。 しかし、実際には、そうする必要はありません。


これで、コードをコンパイルできます。


エディタに戻って、新しい武器クラスに基づいてブループリントを作成できます。 そこで、私の blueprints フォルダーに移動して新しいフォルダーを作成し、これを [weapon] と呼びます。 そして、weapon フォルダで右クリックし、[Blueprint クラス] を作成します。 ここですべてのクラスが展開されているので、weapon を検索して [Weapon C++ クラス] を選択し、それを選択します。 ここで、これを BP_weapon と呼び、これをダブルクリックします。 そして、これが私たちの武器クラスです。


これで、武器メッシュと areaSphere が表示されました。 これで、SKELETAL メッシュの横にあるドロップダウンをクリックして、すぐに武器メッシュを設定できるようになりました。 そして、「アサルトライフル」を選択するだけです。 今のところそれを使用します。


私のアサルトライフルがありますが、少しずれていることがわかります。 そこで、areaSphere を武器の中心まで少し移動します。 そして、球の半径を少しだけ大きくしてみようと思います。 そうすれば、かなり近くにいて BP_weapons self を選択しているときに、このことを拾うことができます。

[武器の状態] があり、[初期状態] である最初の列挙型定数に自動的に設定されます。


これで武器ができました。 それをコンパイルして保存します。 そして、ここで 1 つをレベルにドラッグしてみませんか。 そして今、私たちは武器を持っています。 現在、これに対する機能をプログラムしていないため、これと重複しても実際には何もしません。 しかし、それがゲームのクライアント インスタンスに存在し、サーバー上に武器が存在することはまだわかります。


この講義では、武器クラスを作成し、それにいくつかのコンポーネントを追加しました。また、現在の状態に応じて、コードの観点から武器をどのように扱うかを決定するために使用できる [weapons state] 列挙型も追加しました。


ここで、ポップアップするピックアップ ウィジェットを表示して、武器と重なるとすぐに武器を拾うことができることを知らせたいと思います。 これについては、今後の講義で扱います。


素晴らしい仕事。


また後で。


Welcome back.


Now, aside from our character, one of the more important classes in our game is going to be the weapon class. So we're going to create our weapon class. and this lecture, and we'll add some basic components that all weapons should have, as well as an enum for the weapon state. So we know how to treat the weapon. Depending on the state that it's in.


So let's get started. So we're going to need a weapon class. So I'm going to right click and add a [new C++ class]. And our weapon can simply be an ACTOR. So I'm going to choose ACTOR. Click next.


And I'd like to keep our weapon class and its own folder called [Weapon]. That Way later, when we derive more classes from Weapon. We can stick all those weapon classes in the weapon folder. Now, this class is going to be called [weapon]. So let's click [create class]. So we can hit no on this pop up message.


And here in Visual Studio, I have my weapon class. Now, I don't need the rest of these tabs open for now.


So I'm going to right click on[ Weapon.h] and select [close all but this]. and I can use [Ctrl + K or O], to open [weapon.cpp] from the weapon header file.


Now because weapon is in the weapon folder, we don't need this weapon part here as we've seen. So changing the include to just include [weapon.h] will fix all compiler errors.

So let's head over to our [weapon.h] file, where we have a constructor beginPlay and tick function. Now, just like I did with the CHARACTER class, I'm going to remove these comments as we don't really need them now.


And I'd like to add the basic components that all weapons should have. So I can make a [private section] here, for those component variables.


I'm going to go ahead and move TICK up here to the [public section] just under our Weapon constructor.


Now, in the [private section], I'd like a mesh for the weapon and all of the assets we have. If we go into our ASSETS folder and check out [militaryWeaponsSilver] and [weapons]. I see that these are all SKELETAL meshes. So my weapon class definitely needs a SKELETAL mesh component. 


So let's add one of those. It's going to be a USkeletalMeshComponent, And I can call this weaponMesh. 


Now, weaponMesh is getting a UProperty, of course, and I'm going to make in [visibleAnywhere]. And we'll be able to set the skeletal mesh on our component from blueprints. Now, I'll go ahead and give this a category, and call the category [weapon properties].


Now, in addition to a SKELETAL mesh, we're going to need some sort of overlap volume so that we can enable equipping this weapon when the character is close.


So I'm going to forward declare a USphereComponent and use a sphereComponent called areaSphere. And area sphere can get the same you property as weapon mesh.


Now we'll have more components as we develop this weapon. But for now, I think this is good. Let's go ahead and construct these and [weapon.cpp].


Now, the first thing I'm going to do before we construct these components is set bCanEverTick to false. If we decide later we need our weapon to tick, we can always change this. But I'm going to set it to false for now.


And I'm also going to remove these comments as they're not really helping us any.


we can construct our weaponMesh. I'm going to say weaponMesh = createDefaultSubObject and use USkeletalMeshComponent. And I'm going to call this weaponMesh.


So I'm going to say [weaponMesh->setupAttachment(rootComponent)].

Now later on, I plan on being able to drop this weapon, and I'd like the weapon to bounce off of the ground in the walls whenever we drop it. So I'm going to set its collision response to all channels to block. So to do that, I can say weaponMesh->SetCollisionResponseToAllChannels. And the collision response will be ECollisionResponse::BLOCK. So it's set to block at all.


Now, once we drop it, I would like to ignore the pawn so the pawn can step over it and run through it without colliding with it. So I'm going to say weaponMesh->SetCollisionResponseToChannel and I'm going to set its response to ECollisionChannel and I'm going to choose pawn. And set that RESPONDS to ECollisionResponse::ignore.


Now this is all, of course when we drop the weapon, but the weapon doesn't start off in that state. It's going to start off in a state where we can just walk up and pick it up. And so from the start, I'd like my weaponMesh to have its collision disabled. so I can do that by taking a weaponMesh and calling setCollisionEnabled to ECollisionEnabled:: noCollision.


So we can keep these collision settings as is. And once we want the weapon to actually start colliding with things, say we drop it or throw it. Then we can set its collision enabled to say physics only. or queryAndPhysics or something like that.

Now we also have our areaSphere.

Now I'm not getting any included errors for using the you skeletal mesh component type that may change from engine version to version.

So if you do get include errors, make sure you include the header for that.

I can get away with not using it here in the actor class, but to use use sphereComponents, I am going to need to include the sphereComponent header file. and I know that that is in [components/sphereComponent.h] so I have that include and I'm going to construct my areaSphere. So I'm going to say [areaSphere = createDefaultSubObject(USphere component)]. And this is going to have the name simply areaSphere.

And I'd like to attach areaSphere to the root as well. So I'm going to say [areaSphere->SetupAttachment(RootComponent)].

Now, let's talk about this areaSphere. This SPHERE is going to be what we use to detect overlaps with CHARACTERS. And once they overlap, we'd like to be able to pick up the weapon. We can display some sort of pickup widget and set the item state and all that good stuff.

But here's where this type of thing differs in a [multiplayer game] versus a [single player game in a multiplayer game]. It's a good idea for important things like this to be done on the server only.

So I'd like to only have our areaSphere detect overlap events if we're on the server. So therefore I'd like to take areaSphere and call setCollisionResponseToAllChannels. And I'm going to use ECollisionResponse::ignore. And that way it's like the areaSphere doesn't even exist.

Now what I'd like to do is disable collision for the areaSphere and the constructor and then and beginPlaying. I'll enable it, but only on the server. 

So right here I'm going to say areaSphere->SetCollisionEnabled and pass ECollisionEnabled::NoCollision. So on all machines this will be set to no collision. But on the server machine we're going to set it to actually enable collision. 

So before we move on to that, I notice that we don't actually have a route. So I'm going to set the route to be the weaponMesh. So let's call setRouteComponent. And pass in weaponMesh.

Now if we are on the server then I would like to enable collision. But rather than doing it here in the constructor, I'm going to do it and beginPlay. So we can check to see if we have authority here and enable collision. And then later when we add overlap functions, we can bind those here and beginPlay If we're on the server.

So I'm going to place an if check here. Now what we could do is call getLocalRole and check to see if that's equal to ENetRole::authority.

And if that's the case we can call said collision enabled on the areaSphere and instead of NoCollision we can use something like queryAndPhysics. and we can also set the collision response to the channel that we would like to overlap with, which is going to be the pawn collision type as that's the collision object type of our capsule.

So I'm going to say areaSphere->setCollisionResponseToChannel. Now the channel is going to be ECollisionChannel and I'm going to select the pawn collision type since that's the collision type for the capsule. And the collision response is going to be ECollisionResponse::Overlap.

So we could do this. But there's an easier way to check to see if our role is authority. And that's with the hasAuthority function. This is the same thing. It checks the local role. And if that is equal to role [authority] then hasAuthority will return True. Now, this will be the case if our weapon is a replicating actor. That means the server will be in charge of all weapon objects. So for our weapon to have authority only on the server. We need to set it to replicate and we can do that in the constructor. The actor has a bReplicates boolean and we can set that to true.

Now, the last thing I'd like to add before we wrap up this video is an enum for the state of the weapon. And we can add this up here in [weapon.h] just before the class declaration.

Now I'd like to call this enum weapon state and it's going to be a scoped enum. In other words, I'm going to make an enum class called EWeaponState and use uint8 here.

So our enum constants are unsigned eight bit integers and this is also going to be a blueprint type. So I'm going to give it a UENUM with blueprintType. That way we can use this enum as a type in blueprints.

Now for EWeaponsStates, enum constants. I'm going to prefix them with EWS for EWeaponsState.

and I'd like an initial state. This will be the state for when the weapon is just sitting there in the world. It has never been picked up yet and our character can walk up and pick it up and equip it.

So we have the EWS_initial state and I can give this a UMETA macro to set its display name and this will be for its display name and blueprints. And I'm just going to call this [initial state].

Now in addition to initial, I'd like a state for when we actually equip the weapon. So I'm going to add another enum constant called [equipped]. and its display name can simply be [equipped].

And then I'd like another enum constant for when the weapon is dropped. So I'm going to call this one [dropped]. And this is where we can turn on physics, turn on the collision and let the weapon bounce around on the ground. So I'm going to give this one the display name [dropped].

And of course most enum constants will have a default max constant and this one is going to be called [EWS_MAX]. And I'll give it the display name [default Max]. That way, if we ever need to know how many constants are actually in this enum, we can check the numerical value of EWS_Max.

All right, so our weapon defines this weapons state enum. Why don't we go ahead and add a variable of this type to the weapon class. And this could be private as well. I'm going to stick it right down here.

EWeaponState, I'm going to call this weaponState. and I'll make this a UProperty. And I'm going to go ahead and make this visibleAnywhere so we can see it from BLUEPRINTS.

But I'd like to set this variable from game logic, and that's mostly going to be in C++. If later you decide you'd like to expose it to blueprints, you can. But really, we have no need to do that.

So we can compile our code.

And back here in the Editor, we can create a blueprint based on our new weapon class. So I'm going to head over to my blueprints folder and make a new folder and call this [weapon]. And in the weapon folder, I'm going to right click, Create a [Blueprint class]. And with all classes expanded here, I'm going to search for weapon and choose the [Weapon C++ class] and select that. Now I'm going to call this BP_weapon for now and double click this. And here's our weapon class.

Now I see that I have my weapon mesh and my areaSphere. Now I can set my weapon mesh right off the bat by clicking the dropdown next to SKELETAL mesh. And I'm just going to select the [assault rifle]. I'll use that for now.

So there's my assault rifle and I see that it's a little offset. So I'm going to move my areaSphere over just a bit and up to the center of the weapon. And I think I'll go ahead and increase the sphere radius just a bit. So that way we can pick this thing up when we're reasonably close and selecting BP_weapons self.

There's my [weapon state] and it's set automatically to the first enum constant, which is [initial state]. 

So we now have a weapon. I'm going to compile and save that. And why not I'm going to drag one right here into the level. And now we have a weapon. Now, we haven't programmed any functionality for this, so it's not really going to do anything if I overlap with it. But I can still see that it exists here on my client instances of the game and I see the weapon here on the server.

So in this lecture we created the weapon class and we added a few components to it, as well as a [weapons state] enum that we can use to determine how to treat the weapon from a code standpoint, depending on its current state.

Now we'd like to show a pickup widget that will pop up and inform us that we can pick up the weapon as soon as we overlap with it. And we'll handle that in the lectures to come.

Awesome job.

I'll see you soon.

0 件のコメント:

コメントを投稿