クリア条件を満たした後は、ムービーを流してそのままエンド。これを実現するのがレベルシーケンスというムービー機能です。シーケンサーに関しても公式ドキュメントが充実していました。ありがたやー。
Add Level Sequenceで開始。マチネというのは以前までに使われていた同様の機能みたいです。
シーケンサーエディターは動画編集ソフトのような感じで直感的に操作できるので、カメラを操作したりしてキーフレームを打ち込み、ムービー部分を作って行きます。
イベントのトリガーに関しては、追加→イベントトラックでイベントを追加した後、キーフレームを打ち、Event Nameを設定することで、Custom Eventとして呼ばれます。
イベントトラックのEvent Nameとブループリント内のCustomEvent名が合っていれば、イベントを受け取ることができます。
出来たシーケンスの再生はCreate Level Sequence Playerで取得してPlay。簡単。
そんなこんなでエンディング画面が完成して、「さプライズ」の完成です。
1つの作品作りにむけて楽しめました。
Unreal Engine4のデモサンプルは好きに使っていいという太っ腹っぷりがEpic GAMESの凄いところ。各種アセットは高品質なものがそろっており、もっとクオリティアップできたかなという思いはあります。とはいえ、こういうのは自分でも作ってみたくなるし、サクッと作ってサクッと応募なら自分で作るのもまた一興。そんなわけで慣れないBlenderを使って3Dモデルも自作しました。
Unreal Engine4は初めてで、いきなり開発環境としては全く小さくないものなので、最初はどこを触っていいか戸惑いましたが、ブループリントを見た時のスッと入ってくる感じは良かったです。何かこう…私はあらためてデザイナー方面ではなく、プログラマ方面なんだなと思いました。
実は最初に学ぶ際に選んだビデオがC++でTPSゲームを作るというもの。
なんか結構ややこしい上にC++ガッツリ使うんだな…
なんて思っていたのですが、他のドキュメントを見たら全然使わなくてビックリ。結局今回のクレーンゲームも自前でC++に触ることはありませんでした。このあたりはぷちコン主催者様であるヒストリアさんの記事「C++ or Blueprints?」にもブループリントで実現可能なものはブループリントで実装する、とあり、その通りだと非常に思いました。
次はC++も使って何か作品を作れたらいいなぁと思った次第です。第8回も参加するつもりです。Tシャツ欲しいし。
ではでは。
]]>
ゲームセンターを作っていきます。
作ったクレーンゲームを複数設置。何かこれだけでもゲームセンターっぽくなりました。ゲームセンターを構成する要素にクレーンゲームって凄いある気がする。人のイメージとして。
ついでにゲームセンターといえばやっぱり夜なので、SkyLightは削除して、DirectionalLightを上向きにします。
真っ暗になるので、光源を大量に設置します。ライトは全てスタティックライトです。ここまで追加するとビルド時間がかなり長くなります。
光源を設置してビルドしたらゲームセンターの雰囲気が出てきたように思えます。
夜のゲームセンターの演出のために、太陽光を無くして店内の明かりをいっぱい設置したために、店内から店の外に視線を移動させると、自動露光の効果によって真っ暗になります。
店の外に視線を変えたところ。真っ暗です。
この自動露光の効果自体は人間の目の視覚効果と同じで、ゲームの雰囲気作りに一役買っている。なので実行時は自動露光をオンで、エディタ上でだけ自動露光を切りたい。調べてみるとビューポートのライティングのところにありました。
露出がAutomaticになっているところを、固定にすれば解決です。
自分しかいないゲームセンターは寂しいので、モブとしてデフォルトのマネキン(ブルーマン)を配置します。
このままではさすがに違和感バリバリなので、推理モノに出てくる黒い人のようにしてみたいと思います。
マテリアルのシェーディングをUnlitにして単色で塗りつぶします。
ついでに肩書きを表示するために、ビルボードを作ります。
CustomDepth Passを有効にして、常に前面に表示できるようにします。
ゲームセンター内にいる客…のような正体不明の人ができました。
吹き出しも「客」肩書きテキストと同じように、ビルボード+CustomDepth Passによる常に前面にレンダリングしています。
モブができたらゲームセンター内を徘徊するようにします。AIについては公式ドキュメントに詳しく書かれています。ドキュメントにあるようなBehaviorツリーを使わなくても、ランダムで徘徊させるぐらいなら簡単にできます。
NavMeshBoundsVolumeを追加して、動ける範囲を設定
後はGetRandomReachablePointInRadiusでランダムに移動できる点を取得、SimpleMovetoLocationでその地点まで動かすことができます。
これを定期的に行えばモブのランダム徘徊は可能です。全く賢くはないですが。
説明不要ですが、小物を配置していきます。
両替機、ゲームセンターに必須ですね。動かないけど。
ポスター。ゲームセンターに貼ってあるポスターは欲しくなります。椅子はスターターコンテンツのものです。でもサンプルのメッシュを使っていいというのも中々オツですね。
こっちはエレベーター。ドアが開閉するようになってます。
デモサンプルにも自動ドアがありましたが、ドアの開閉はタイムラインを使うといい感じにできます。開ける時はPlayに接続してタイムラインから出てくる数値に従ってドアのlocationを操作する。閉める時はReverseに接続だけでOK。
できたゲームセンターがこちら。
入り口はガラス製のドアで外が見えるので、店の外も見える範囲で作ります。
画像はスカイライトで明るくした状態でのスクリーンショットです。
こちらも明るくした状態での動画です。店の前を車と通行人が通るようにして喧騒な感じを出します。
やり方はブルーマンをスポーンさせた後、移動させると同時に、
SetLifeSpanで寿命を設定します。これで自動でDestroyされるので後は適当な間隔でスポーンさせればランダムな通行人が完成です。
車の方も同じようにしていますが、こちらはランダムで色を変える処理とリボントレイルによるテールランプの尾を引くようなエフェクトを入れています。AnsqweHubのココが参考になりました。
ポーズはSetGamePausedだけで動作します。超楽。ポーズ画面を表示して、解除やゲームのやりなおしを選択するので、ShowMouseCursorでカーソルを表示します。
ポーズ画面
ゲームをやりなおす際はRestartGameを呼び出すだけで各種設定がリセットされてリスタートできます。ポーズとリスタート共々非常に楽です。
これクレーンゲームが大体完成しました。後はエンディング画面です。
ではでは。
]]>
ぷちコン応募作品を作るにあたって思いついたクレーンゲームシミュレーター。ただ固定のカメラでクレーンゲームを作って遊ぶよりかは、やはりUnreal Engine4のパワーを使って、クレーンゲームがいっぱい置いてあるゲームセンターみたいなの作って歩き回りたいと思いました。そういうことが簡単に実現できるのがUnreal Engine4というゲームエンジンだとも思ったので。
そんなわけでゲームセンターを歩き回って、クレーンゲームを操作するFPSタイプのゲームに決定しました。
後はゲーム性です。現実のクレーンゲームを考えた時、いかにお金を使わずに景品を取れるかということが目標だと思います。そしてクレーンゲームで景品が取れる時というのは、アームが強い時です。全然取れない時もあれば、簡単に取れる時もあるアームの強度を演出できれば、目標というのが生きてきます。ついでに、昨今の全然掴んで取れないクレーンゲーム事情(ずらして取る…みたいなの)を実現できればいいとも思いました。
FPSというゲームは操作が複雑です。WASDに加えてshiftとctrlとspaceとマウスの操作。
私はSteamのゲームをよくプレイしますが、FPSのゲームにはアクションキーもしくはインタラクトキーと呼ばれるものが多くあり、キャラクターのアクションを1つのキーに集約してしまうといったのがよく見られます。
物を取るボタンはR、奥ボタンはFといった具合に分けるのではなく、HUDで状況を表示することによって、1つのボタンで色々できるインテリジェンスなキーとして集約させることによって、ただでさえややこしいFPSの操作キーの煩雑さを回避するのに一役買っています。
Payday 2のインタラクトキーの例。鍵を開ける、カードキーを通す、人質をタイで縛る、現金バックを拾う、現金バックを投げるといったアクションのほとんどを担当するのがFキー。HUDに表示されるために今インタラクトキーで何が行われるかということも理解できる。便利かつ煩わしさが無いのが好きです。
というわけで「さプライズ」にもこのインタラクトキーを導入します。クレーンゲーム機に近づいたら状況によって
を1つのキーで全て行えるようにします。
これは特に難しいことはなく、インタラクトキーに割り当てたキーが押されたら、状況によって操作を変えるだけです。
このゲームの肝はなんといってもクレーン部分の物理です。そのためクレーンの動作に関してはある程度妥協せずに物理を実現させたい。そのためには物理コンストレイントというのを使います。
物理コンストレイントは物理物体を接続するジョイントで、角度制限や移動制限を付けたり、移動エネルギーを加えることができます。
Unreal Engine4の公式ドキュメントに物理コンストレイントについて記載されていますが、
ウィジェットの時と違い、物理コンストレイントの説明はサラッとしていて、自分でトライアンドエラーを繰り返してアームの開閉ができました。なので忘れないように開発記を残しておきます
まず、物理コンストレイントには2つのメッシュが必要です。
アームの接続元となるUFOのパーツ。
アーム本体。
この2つを物理コンストレイントで接続していきます。
コンポーネントの追加ボタンを押してPhysicsConstraintを選びます。
PhysicsConstraintをクリックして詳細を開き、アーム接続元となるufoをComponent Name 1に、アームをComponent Name 2に設定します。選択タブとかは無く、キーボードでコンポーネント名を打ち込むだけです。
これでビューポートにてufoとアームが青と赤の立方体で囲われていたら物理コンストレイントは動作しています。Disable Collisionにチェックを入れると、物理コンストレイントで接続した物体同士の衝突が無視されます。今回はチェックを入れています。
続いてはアームを動作させる角度の設定です。アームの開閉角度は45度で、1方向のみという事を考えつつ数値を設定していきます。
平行移動に関しては移動させないので全てLockedで固定。
Angular Limitsがアームの開閉に適した部分です。Twistのみ有効にして、中心から両側22.5度動作するように設定します。
設定した後がこちら。物理コンストレイントがアームの回転軸になるため、物理コンストレイントの位置もキッチリ合わせておく必要があります。
物理コンストレイントの子パーツであるアームメッシュのSimulate Physicsを有効にすると、物理コンストレイントをシミュレーションで確認することができます。なお、物理コンストレイントを使用して動作させるメッシュは、プリミティブなコリジョンでないと警告が出ます。
アームのコリジョンはカプセルなどを使用して設定しています。
当然ながらアームに対してエネルギーを加えていないため、実行しても何も起きません。ただし、外から力を加えてもアームの開閉以上の動作はしないようになっています。
物理コンストレイントの詳細、Angular Motorのところでエネルギーを加えることができます。大きく2つの方法があり、Target Orientationはその角度へ持っていくためのエネルギーが発生します。Target Velocityはその角度方向のエネルギーが発生します。
アーム開閉方向へのエネルギーを加えてみます。Target VelocityのXを22.5、Twistにチェックを入れて、シミュレーションをスタートさせます。
アームが開きました。このままでは遅いので、Strengthを10倍の10.0などにすると早くアームを開くことができます。
アームとUFO部分のコンストレイントができたら、他の物理コンストレイントも加えていきます。
最終的にはアームの左右開閉、UFO部分の上下移動、横移動、奥移動と5つの物理コンストレイントを使いました。
ここから先は微調整に微調整を重ねることになりますが、微調整のポイントを整理します。
物理コンストレイントのMotor部分はエネルギーを生み出す設定そのもの。LinearもAngularもあまり変わらず、その場所に持っていく設定がPosition Target、Target Orientationです。エネルギーのベクトルを指定するのがVelocity TargetとTarget Velocityです。
例えばPosition TargetをX 100.0に設定すれば、X 100.0方向に向かうエネルギーが常に発生します。X 0の位置ある場合は+方向のエネルギー、X 200にいたら-方向のエネルギーといった具合です。
Strengthはエネルギーの強度です。
物理コンストレイントに違反(制限値に触れた場合)の動作設定。例えばSoft Constraintにチェックを入れると、スプリングのように反発する動作が得られます。アームを開いた時のように、バシッと止めたい場合はSoft Constraintのチェックを外し、Restitutionを0.0にして反発しないようにすると、アームっぽくなりました。
物理コンストレイントで接続したメッシュの物理設定も微調整のポイントとしては重要です。
Linear DampingやAngular Dampingはデフォルトでは低すぎるので、少し上げた方が良い事が多いでしょう。
例えば前述のPosition Targetを使ってエネルギーを発生させる場合、X 100を指定して、X 0にある物理メッシュを動作させるとします。実行するとX 100まで物理メッシュが動作した後、制限が無い場合はX 125などといった数値まで滑ります。そうなるとまたX 100に向かって動作するのでX 80、X 110といった具合に設定した値を行ったり来たりします。Linear Dampingの数値をすこし上げることでこの行ったり来たりを抑える摩擦のようなものが発生します。
また、Angular Motorで回転させたりする場合はCenter Of Mass Offset(重心)も重要です。クレーンゲームのアームも、重心が中心にありすぎると簡単に掴んで運べてしまうため、アームの弱さを演出するために重心をアームを開く側に設定しています。
各種物理の設定が固まったら、エネルギーの方はブループリントで制御できるようにします。
Angular Motorは、SetAngularVelocityTarget、SetAngularDriveMode、SetAngularDriveParamsで設定できます。
アームが制御できたら、クレーンゲームを肉付けしていきます。
メッシュを配置しているぐらいで特にテクニカルな事は無し。Arrowは景品の配置用に追加しています。
GetWorldTransformでTransformが取得できるので、景品の配置位置の調整に便利です。
クレーンゲームの動作を状態を分けて考えて
ステートマシンとしてブループリントを記述していきます。
スケルタルメッシュのアニメーションの方はステートマシンを使えたのですが、Actorのブループリントで使えないので、昔ながらのSwitchによる処理分けです。ブループリントをたくさん使うだけで、特に難しいことはしていません。
最終的なアーム動作はサイズが大きくてgifでは無理そうなので応募作品から確認して見て下さい。
長くなってので一旦ここまでにします。次はゲームセンターの店内についてです。
ではでは
]]>
ただ、せっかく1つの作品を作るので、ゲームとして必要な部分を揃えたいと思うのもまたクリエイター。タイトル画面~ゲーム中~エンディング画面といった遷移は考えてみます。
ゲームの遷移ってこんな感じですよね。そんなわけで今回はタイトル画面についてです。
タイトル画面を構成するゲームタイトルやスタートボタンはUMGで実現可能。UMGに関しては公式ドキュメントが充実していました。
ウィジェットを作成してパレットからテキストやイメージ、ボタンといったパーツを配置していくことで作成。
大体こんな感じになりました。
レベルブループリントにてウィジェットを追加。
このままではFirst Personキャラクターが持つキーの移動が効いてしまうので、入力をUIのみにすることと、ついでにカーソルを表示する処理も加えると完成です。
これで起動するとタイトル画面のウィジェットの表示されます。
後はタイトル画面の背景をどうするかということですが、「さプライズ」ではゲーム中の画面を背景として使い、シームレスにゲーム状態に移行させることを考えました。
色々配置したゲームセンターの建物。これを背景として使います。
カメラ(TitleCamera)を設置。カメラを右クリックしてパイロットモードにするといい感じのところに持っていきやすい。
カメラを設定してもまだ有効にはなりません。この「さプライズ」では、First Personプロジェクトをテンプレートとして使っているので、デフォルトでカメラの設定はキャラクターの持っているカメラになります(FirstPersonCharacterのFirstPersonCamera)。
カメラの制御を移すやり方は色々ありますが、今回は起動時にタイトル用のカメラにターゲットを変更する方法を使います。
レベルブループリントのBeginPlayにて、SetViewTargetwithBlendを使ってカメラを切り替えます。
これで起動時にカメラがTitleCameraになるので、ウィジェットのボタンを押したときにはFirstPersonCameraに戻るように処理を追加します。BlendTimeに少し値を設定することで、カメラの切り替わり中を補間してくれます。
ちなみにFirstPersonCharacterの初期位置はココ。扉入ってすぐのところです。
実行するとこんな感じにシュッとカメラが移動します。後はウィジェットを消せばゲーム中への遷移が完成です。
ウィジェットのボタンを押した時の処理に、ウィジェットの削除、カーソルの表示処理、入力処理の制限を解除を追加します。
これでタイトル画面からプレイ中へシームレスな移動ができました。
実際の完成版では扉を開けて閉める処理やサウンド、カメラの調整を行ったため、Level Sequenceも使っていますがが、遷移部分の処理としては同じです。
Unreal Engine4を触るのは初めてだと製作記の冒頭で書きました。
前はCocos2d-xを使っていましたが、そちらはC++を使うプログラムばりばりのテキストベースのゲームエンジンで、
さぁ作るぞ!
って思ってから、とりあえずゲームにはタイトル画面があってから、ゲーム画面。よし、タイトル画面を一旦先に作っておこうか…いやその前に抽象クラスを…みたいなイメージなわけです(私だけだったらスミマセン)。
Unreal Engine4ではプロジェクトを新規作成したら謎の青い男が動いていて、タイトル画面を作ることをスッカリ忘れて遊んでいました。いくぶんテキストベースよりもゲームを作りやすい気がします。とはいえ、Unreal Engine4でもゲームとして完成させることを考えた場合にはタイトル画面や独自のデータを取り扱う必要が出て来きます。データの取り扱いに関してはブループリント上では構造体とか列挙型の扱いがちょっと面倒くさいと感じてたりします。
次はゲーム本編、クレーンゲームの製作記です。
ではでは。
]]>
ざっくり最初から書いてたら結構な量になってしまい、今回は応募作品の紹介とさわりのさわりの話だけです。
クレーンゲームシミュレーション。
昨今のゲームセンターのアーム事情を再現したかったので、わりとアームが弱くて掴めません。ずらして落とすゲームになっています。もうそれはクレーンゲームじゃない気もするんですけどね(笑)。
ゲーム性を出したかったので、筐体によってアームの強弱を設定し、それを推測できるように周りの客がつぶやくという形になっています。そして最終的にはどれだけお金を使わずに景品を取れるかというリアルな目標になっています。
今回のぷちコンのテーマは「サプライズ」
いくつか思い立ったのですが、その中には軍艦の「サプライズ」というものがありました。結果的にはそっち側で応募したら被ることになる上に、3Dモデルの作り方がいまいちよくわかっていない私としては恥をかくところでした。
しかも私自信、ミリタリーにあまり詳しくないですしね。
そんな私が採用したのは「さ」プライズ。
プライズ=景品
よって「さ」を景品としたクレーンゲームという駄洒落。
Cocos2d-xを使ってた私としては、Unreal Engine4の物理についてはちょっと気になっていたのでこの際物理シミュレーションも試してみようとも思ったわけです。
実は3Dのゲームエンジンを初めて触ることになります。といってもぷちコンが始まってからではなく、その前から興味があってちょこちょこ触っていました。
Unreal Engineといえばゲーム好きはまず知っているゲームエンジン。Unreal Tournamentも遊んだ経験あります。採用ゲームで遊んだことのあるゲームを列挙すると大変なので、中でも好きなものといえばGears of WarとかBorderlandsでしょうか。最高ですね。
Unreal Engine4の準備は簡単、インストールするだけです。説明不要。
インストールできたらUnreal Engine4の世界に飛び込めるようになります。
新規プロジェクト作成で、名前をHelloWorldと打ち込んだ後の凄い大きな世界へ放り出された感は半端ない。どこから何を知ったらいいかもわからない。
何をしたらいいのか……開発環境というのはどうしてこうややこしいのでしょう(笑)。
ただ、そんなに悲観することはありません。Unreal Engineは日本語のドキュメントが充実している上、プレイを押すだけで綺麗な3D世界に突入可能です。ここまでのを作るのにOpenGLやDirectXだけだったら大変ですよねぇ。いい時代になったものです。
そんなこんなでビュー操作やオブジェクトの配置など、基本的な操作については操作しながら何となく理解できました。
ゲームに欠かせないスクリプト・プログラムを担うのがこのブループリントと呼ばれるフロー図のようなもの。ブループリントというのは日本語で設計図という意味です。
プログラムをちょっとでも触ったことのある方は、次の画像だけでどういうものか理解できると思います。
何となくBeginPlayからスタートして、条件分岐やループを使いつつPrint。理解可能ですね。
実行結果はもちろんテキストがprintされます。画面上ですがログ上にも出ます(出せます)。
このブループリントはテキストベースのプログラムだと下記のような感じですね。
void BeginPlay() { PrintString("Hello"); if( Now.GetHour() > 12 ) { PrintString("午後"); } else { PrintString("午前"); } for(int i=0;i<10;i++) { PrintString(toString(i)); } PrintString("GoodBye"); }
いかかでしょう、ブループリント。
私はどちらかというとプログラム畑の人間なので、ブループリントはプログラムと親和性が高いところが好みです。ついでにUnreal Engineではその気になればC++で打てるところもいい所です。基本はブループリントで記述して、できないことがあればC++を使っていく感じだと思います。でも大体の事はブループリントで事足りるでしょう。
Unreal Engine4を触った感想しか書いていませんが、長くなってきたのでとりあえず今回の製作記はここまでにします。
ではでは
]]>Cocos2d-x 10円ゲーム講座 第13回、デモ画面を実装します。
これまででゲーム部分は完成しています。デモ画面は蛇足といっても過言ではありません(笑)。
タイトルを作ってシーン遷移、というのがチュートリアル的にはシーン遷移を学べて良いかもしれません。しかし、10円ゲームにタイトルからのシーンチェンジは不要でしょう。コインを入れてサクッとスタート。現実世界でもサクサク感は10円ゲームの醍醐味の1つだと思います。
私の思い出ですが、駄菓子屋に置いてあるゲーム機のデモ画面が好きでした。今でもそうですが、筐体機のデモ画面って非常に楽しそうな感じがしますよね。お菓子にするか、ゲームをするか……どれにお金を使おうか迷ってた子供時代、デモ画面は効果的な宣伝だったように思います。
そんなわけで、10円ゲームにコインを入れるまではデモ画面にしてみます。現実世界の10円ゲーム機のデモプレイはありえませんけどね。
前置きが長くなりましたが、10円ゲームのデモ画面というのを考えてみます。
こんな感じでしょうか。
デモプレイ画面であることは、このDEMO PLAY文字を明滅させることにします。
説明する部分は少ないのでコードを載せていきます。
class OneCoinGame : public cocos2d::Layer { public: // DEMO画面用update void demoUpdate(float delta); private: // デモモード bool _isDemo; cocos2d::Sprite *_demoSprite; };
デモ画面用のスケジュール関数と変数の定義です。
// デモモードかどうか _isDemo = false; // カスタム定期処理の開始(5.0秒毎の呼び出し) this->schedule(schedule_selector(OneCoinGame::demoUpdate), 5.0f);
コインの監視の際に説明したscheduleUpdate()メソッドはフレーム毎にupdate(float delta)を繰り返す呼び出す処理と決まっていましたが、デモ画面用のスケジュール処理は毎フレーム呼び出す必要はありません。そういった場合にschedule()を使用することで任意のメソッドを任意の間隔で呼び出す処理が可能です。今回は5.0秒毎に1回demoUpdate()を呼び出します。
// デモモード時の更新処理 void OneCoinGame::demoUpdate(float delta) { // デモ状態かどうか if (!_isDemo) { return; } // coinの状態を確認 // Enableがfalseなら、コイン投入処理 if (!_coin->getPhysicsBody()->isEnabled()) { this->coinEntry(); } // コインがレバー位置にあるなら、レバー処理 for (int i = 0; i < 4; i++) { // 範囲内ならコインにランダムな力を加える if (leverArea[i].containsPoint(_coin->getPosition())) { auto power = CCRANDOM_0_1(); if (i == 0 || i == 2) { this->applyForceCoin(1.0f, power); _lever[i]->setRotation(power * -90); _lever[i]->runAction(RotateTo::create(0.2, 0.0f)); } else { this->applyForceCoin(-1.0f, power); _lever[i]->setRotation(power * 90); _lever[i]->runAction(RotateTo::create(0.2, 0.0f)); } // 効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/lever.wav"); } } }
CCRANDOM_0_1()はCocos2d-xでランダムを扱う場合に便利なマクロです。0~1までのfloat値を返してくれます。その他の説明は特に新しいことをしていないので省きます。
DEMO PLAYの明滅は、フェードイン・アウトの繰り返し処理で実現しています。Blinkを使わない理由としては、Blinkの点滅処理はbVisibleを操作するため、ボタンを押した際のsetVisibile処理と不整合が出るためです。ちなみにFadeIn, FadeOutはopacityを操作します。
デモプレイ。どうでしょうか。ソースコードはこちら
私はこういう勝手に操作しているのを眺めるのは大好きです。ランダムに力を加えているだけですが、見ていると結構楽しいですよね。
]]>Cocos2d-x 10円ゲーム講座 第12回、パーティクルを使ってエフェクトを発生させます。
10円ゲームにパーティクルを使ってエフェクト処理を施してみます。
パーティクルとは粒子のことで、小さな画像を多量に使い、ひとつひとつに動きをつけることでエフェクトを表現するものです。これはもう実際にパーティクルを作成するエディタを触ってもらった方が早いと思います。
有名なパーティクルエディタ。パーティクルエディタはいくつかありますが、こちらはフリーで使えるのでオススメです。
ササッとあれこれ弄ってパーティクルが出来たら、
出力はExport → COCOS2DXをクリックすればparticle_texture.plist(デフォルトファイル名)がダウンロードできます。
今回作成したパーティクルはこちら。コイン投入時に火花を散らすようなシンプルなエフェクトです。
Cocos2d-xではパーティクルのplistをそのまま読み込んで使用することができます。
// パーティクル処理 auto particle = ParticleSystemQuad::create("particle/particle_texture.plist"); particle->setPosition(Vec2(visibleSize.width*0.7, visibleSize.height*0.9)); particle->setAutoRemoveOnFinish(true); // パーティクルが終了したらNodeを削除する this->addChild(particle, 3);
パーティクルを使うにはParticleSystemQuadクラスを利用します。setAutoRemoveOnFinish(true)をしておけば、パーティクルが終了した際に自動で削除してくれるので便利です。
無限ループを設定しているパーティクルは当たり前ですが自動で削除されないので注意しましょう。
ソースコードはこちら
エフェクトの回でも書きましたが、10円ゲームはレトロさがウリでもあるので過度なエフェクトは不要な気もしますね。
次回はデモ画面の実装です。
]]>
Cocos2d-x 10円ゲーム講座 第11回、スプライトアクションを使ってユーザビリティを向上させる方法についです。
ゲームの骨格となる部分は完成しました。1つ問題点を上げるならボタンがボタンと解りづらいことです。つまりユーザビリティが悪いということです。
レバーは見た感じでわかるかもしれませんが、お金の投入口がボタンになっていることは、パッと見ただけではよくわからない可能性もあります。
スマートフォンの台頭で、マウスではなくタッチ操作が主流になってきました。マウスの場合は、マウスオーバーという手法によりボタンがボタンであることを明示できました。
しかし、タッチ操作ではマウスオーバーに相当する操作がありません。
ボタンを明示するよくある方法としてはグロウ処理を行うことです。ボタンを淡く光らせることで、ユーザーに装置であることを明示することができます。今回はボタン投入口とレバーにグロウ処理を施してみます。
用意したグロウ画像です。
グロウ画像をこれまで同様、Spriteクラスを使って表示します。
auto sprite = Sprite::create("coin_entry_glow.png"); sprite->setPosition(Vec2(visibleSize.width*0.7, visibleSize.height*0.9)); this->addChild(sprite, 2);
グロウ処理のためにSpriteにアクションを設定します。
// アクション auto blink = Blink::create(1.5f, 1); // 点滅 1.5秒で1回点滅 auto action = RepeatForever::create(blink); // blinkを永久に繰り返す sprite->runAction(action);
点滅を永久に繰り返すというアクションを設定します。
レバーも同様に設定します。
// レバーのglow処理 auto leverGlow = Sprite::create("lever_glow.png"); leverGlow->setAnchorPoint(Vec2(0.0f, 0.0f)); _lever[i]->addChild(leverGlow, 2); // 1.5秒に1回の点滅を永久に繰り返す leverGlow->runAction(RepeatForever::create(Blink::create(1.5f, 1)));
Cocos2d-xが使えるアクションの詳細についてはReferenceを見ると良いでしょう。
ソースコードはこちら
10円ゲームはレトロさがウリでもあるので、ボタンやレバーのグロウ処理はレトロさを損なう結果になりますが、ボタンとしての理解度は向上したと思います。
リアリティかユーザビリティか。どちらも良いに越したことはありませんが、ゲームとしてユーザビリティを重視する場面は多いと思います。
次回はパーティクルを使用します。
]]>
Cocos2d-x 10円ゲーム講座 第10回、効果音を鳴らします。
前回までの実装により、コインは転がり穴には落ちる。ゴールすれば景品も出る。しかし何か物足りませんね。何かというか、音です。
コインがぶつかる音があるだけで、リアリティがまるで変わります。そんなわけで、ゲームの大切な要素、効果音を鳴らしてみましょう。
今回は効果音を作成することにしました。ネット上にフリー素材が山ほどありますが、それは使用に関してフリーなだけで大体は再配布禁止です。githubでの公開には問題があるため、自分で作ることにしました。
コインの音は簡単に録音できます。一人ではっちゃけて歌いたくなったのでこんなマイクを持っていますが、別にヘッドセットのマイクや、スマートフォンなどでも十分録音できます。
コイン音は3つ録音しました。
ゴールした時の電子音などはBeam2002を使って作りました。Beam2002はアナログシンセ風の効果音作成ソフトウェアです。ADSRを操作するいわゆるエンベロープ・ジェネレーターのようなことができるソフトです。
レバー操作時の音と、ゴール時の音を作成しました。
次はこれらを10円ゲームに組み込み、再生します。
効果音が用意できたので鳴らしてみることにします。Cocos2d-xで効果音の再生はCocosDenshionクラスを使用します。Denshion、でんしおん、つまりは電子音です。
init()で使用する効果音をプリロードしておきます。
#include <SimpleAudioEngine.h> bool OneCoinGame::init() { /*--- 略 ---*/ // サウンドのプリロード CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_entry.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_contact.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_hole.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/goal.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/lever.wav"); }
CocosDenshionは拡張クラスなので、SimpleAudioEngine.hをインポートします。
後は効果音を鳴らしたい箇所でplayEffect()を実行するだけで、効果音を鳴らすことが出来ます。
// コイン投入ボタン void OneCoinGame::insertCoinCallback(cocos2d::Ref* pSender) { /*--- 略 ---*/ // 効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/coin_entry.wav"); }
これでコイン投入口をタッチした時に、お金を投入する効果音が鳴ります。
同じように、コインが穴に落ちた時の音、ゴール時、レバー操作時も該当箇所にコードを実装するだけです。
「コインがレールなどに接触した音」を作成しましたが、この音を鳴らすには、コインの接触を検知する必要があります。
コインの接触を検知するには、剛体が接触した時のイベントを受け取るイベントリスナーを使います。
接触イベントを受け取るメソッドを定義
class OneCoinGame : public cocos2d::Layer { public: // 接触イベントの検知 bool onContactBegin(cocos2d::PhysicsContact& contact); };
タッチイベントと同様に、接触イベント用のリスナーを生成して定義したonContactBeginメソッドを設定、イベントリスナーへ登録することで準備完了です。
// 衝突イベントの検知 auto contactListener = EventListenerPhysicsContact::create(); contactListener->onContactBegin = CC_CALLBACK_1(OneCoinGame::onContactBegin, this); this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
これで剛体同士が接触する時のイベントを受け取ることができるようになりますが、接触イベントはまだ発生する設定になっていません。
接触イベントを発生させるには、各剛体に対してsetContactTestBitmask(int bitmask)でマスクの設定を行う必要があります。この値はデフォルトで0のため、0以外に設定しないと接触イベントが発生せず、イベントを受け取ることができません。
デフォルトが0なのは剛体の接触イベントは大量に検知してしまうため、必要なものだけ検知してパフォーマンスを維持するためです。
筐体内部
wall->getPhysicsBody()->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク
レール
rail->getPhysicsBody()->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク
コイン
coinPhysics->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク coinPhysics->setName("coin");
接触イベントのビットマスク設定をそれぞれ設定しました。コインは接触時に識別したいのでsetName()で名前を付けて置きます。
ビットマスクの設定ができたら、onContactBegin(PhysicsContact& contact)で衝突を検知できるので、効果音を鳴らすコードを実装します。
// 衝突が発生した際に呼ばれるイベント bool OneCoinGame::onContactBegin(PhysicsContact& contact) { // 衝突した2つの物体について取得 auto bodyA = contact.getShapeA()->getBody(); auto bodyB = contact.getShapeB()->getBody(); // 衝突した物体がコインかどうかを調べる if ("coin" == bodyA->getName() || "coin" == bodyB->getName()) { // コインなら効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/coin_contact.wav"); } return true; }
onContactBegin(PhysicsContact& contact)は、2つの物体が衝突した際に呼ばれます。
引数contactにより、衝突した2つの物体を取得することができますが、getShapeA()かgetShapeB()がコインかどうかであることと、コインが接触した場合どちらがコインかがわかりません。コインに名前を付けたのはこの判断をするためです。
名前を取得してコインなら効果音を鳴らすことで、コイン接触音を適切なタイミングで鳴らすことができるようになります。
※音が小さいかもしれません。
ソースコードはこちら
次回はユーザビリティを向上のためにスプライトアクションを実装します。
]]>
Cocos2d-x 10円ゲーム講座 第9回、はずれ穴とゴールの処理を追加します。
今回ははずれ穴とゴールの処理です。
現実の10円ゲームのはずれ穴は奥行きを持っていて、奥に10円玉が落ちることで10円ゲームの失敗となります。Cocos2d-xの物理エンジンで奥行きをもたせることは出来ないので、穴にコインが入ったかどうかをコインの位置で判定することにします。ゴールも同様です。
コインの位置を監視するには、Cocos2d-xのスケジュール機能を使うとフレーム毎にコインの位置を監視することができます。コインの位置の監視によってはずれ穴とゴール位置に来た事を検知します。
class OneCoinGame : public cocos2d::Layer { public: // スケジュール処理 void update(float delta); };
init()内でthis->scheduleUpdate();を実行します。
// スケジュール処理の開始 this->scheduleUpdate();
これでフレーム毎にupdate(float delta)が呼ばれるようになります。
続いてはupdate(float delta)の中身を実装していきます。
// 穴の矩形領域 Rect coinHole[] = { Rect(245, 430, 30, 30), Rect(67, 335, 30, 30), Rect(213, 335, 30, 30), Rect(260, 258, 30, 30), Rect(182, 136, 30, 30), Rect(61, 67, 30, 30), }; // ゴールの矩形領域 Rect goalHole = { Rect(117, 67, 30, 30), }; // フレーム毎の処理 void OneCoinGame::update(float delta) { ///////////////////////////// // コインがはずれ穴とゴールに入っている場合の処理 // コインの速度の取得 auto velocity = _coin->getPhysicsBody()->getVelocity(); float speed = velocity.getDistance(Vec2::ZERO); // コインの速度が一定速度以下かつコインの物理が有効でかどうか if (speed < 3.0f && _coin->getPhysicsBody()->isEnabled()) { // はずれ穴に入っているかどうかの確認 for (int i = 0; i < 6; i++) { if (coinHole[i].containsPoint(_coin->getPosition())) { // コインがはずれ穴に落ちた時の処理 // コインの物理動作を停止し、非表示にする _coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false); } } // ゴールに入っているかどうかの確認 if (goalHole.containsPoint(_coin->getPosition())) { // コインがゴールに到達した時の処理 // コインの物理動作を停止し、非表示にする _coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false); } } }
穴に触れた瞬間にコインが消えると、少し理不尽な感じが出るので、コインの速度を取得しコインの速度が遅い時だけ穴に落ちたことにします。これは実際の10円ゲームでは奥行きがあることを、2Dゲームで擬似的に表現するための処理です。コインの速度が速い時は穴を通り抜ける方が理不尽に感じないためです。
はずれ穴、ゴールどちらに到達してもコインは無くなることになるため、コインの物理動作を停止し非表示にします。
_coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false);
ゴールの場合はクリア報酬として景品を出します。ついでなので、この景品も剛体にして物理を適用してみます。
gift.png
景品。おもちゃ箱っぽいイメージです。
景品口の物理領域を実装。
// 景品口の物理領域 Vec2 vec[4] = { Vec2(202, 65), // 左上 Vec2(202, 23), // 左下 Vec2(301, 23), // 右下 Vec2(301, 65), // 右上 }; auto wall = Node::create(); wall->setPhysicsBody(PhysicsBody::createEdgeChain(vec, 4, PhysicsMaterial(1.0f, 0.5f, 0.2f))); wall->getPhysicsBody()->setDynamic(false); this->addChild(wall);
筐体と同じように実装します。
続いて景品を出す処理をゴールに到達した際に追加します。
// 景品を出す処理 auto gift = Sprite::create("gift.png"); gift->setPosition(Vec2(250.0f, 74.0f)); this->addChild(gift, 1); // 景品の物理設定 auto giftPhysics = PhysicsBody::createBox(Size(26.0f, 14.0f), PhysicsMaterial(0.6f, 0.3f, 6.0f)); giftPhysics->setMass(1.0f); // 重さ giftPhysics->setMoment(10.0f); // モーメント(大きいほど回転しにくい) gift->setPhysicsBody(giftPhysics);
景品口の物理設定を作り、少し上から景品を出現させて落とします。
これを実行してみると問題があることがわかります。
景品が筐体の衝突領域に接触し、景品口に出てくれません。
ゲームで物理物体を扱っていると、ある物体とは衝突して欲しいけど、こっちの物体とは衝突してほしくない、といったことが出てきます。その解決方法として、Cocos2d-xでは物体にカテゴリーやマスクが設定できます。
今回は景品をコインや筐体と接触しないようにカテゴリ設定を行います。
剛体をカテゴライズするにはsetCategoryBitmask()を使用します。また、setContactTestBitmask()で剛体の衝突するカテゴリを指定できます。
コインとレールはそれぞれ衝突するようにカテゴリを指定します。
coinPhysics->setCategoryBitmask(0x00000001); // 物体のカテゴリ coinPhysics->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
wall->getPhysicsBody()->setCategoryBitmask(0x00000001); // 物体のカテゴリ wall->getPhysicsBody()->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
rail->getPhysicsBody()->setCategoryBitmask(0x00000001); // 物体のカテゴリ rail->getPhysicsBody()->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
コイン、レール、筐体のカテゴリを1(0x00000001)に設定し、接触する物体のカテゴリを同じく1(0x00000001)に設定します。これでコインとレール、筐体はそれぞれ接触することになります。動作としては今まで通りです。
次に景品の設定です。景品と景品口は接触して欲しいが、コインやレールとは接触して欲しくないので、コインやレールとは別のカテゴリである2(0x00000010)を設定します。
wall->getPhysicsBody()->setCategoryBitmask(0x00000010); // 物体のカテゴリ wall->getPhysicsBody()->setCollisionBitmask(0x00000010); // 接触する物体のカテゴリを指定
giftPhysics->setCategoryBitmask(0x00000010); // 物体のカテゴリ giftPhysics->setCollisionBitmask(0x00000010); // 接触する物体のカテゴリを指定
これで物体の衝突を制御できました。
ことが確認できます。
ゴールに導くのが難しくて動画撮れませんでした(笑)。内部ではテスト用のコインを作成して確認しています。
ソースコードはこちら
次回は効果音を鳴らすです。
]]>