【Unity講座】STG 制作講座#2 ~弾幕にする~
初めに
今回の記事で、以下のような弾幕が作れるようになりますので、是非とも最後まで記事を読んでみてください。
本記事の内容は、動画でも解説しているので参考にしてみてください。
関連資料
本記事は第二回目の記事です。
一回目の記事は以下になりますので、先にそちらを読んでみてください。
プロジェクトオープン
第1回で作成したプロジェクトを改造するので、そのプロジェクトを以下からダウンロードして下さい。https://drive.google.com/file/d/1Pv0Puk3cu6Qq4rAB8BPmRrnTdxqwm5IU/view
解凍後にSTG_Game/Assets/Scenes/SampleScene.unityをダブルクリックしてSTG_Gameプロジェクトを開きます。
前回の確認
前回は弾が1つ動くところまで作成しました。
今回はこれを改造して、弾幕のように沢山発射します。
プレハブ化
前回と同じように画像を配置、スクリプトを設定するのは手間なので、
プレハブというUnityの機能を使って弾を複製出来るようにします。
プロジェクト直下にPrefabフォルダを作成します。
そして、Prafabフォルダの中に、前回作成したbullet_image_01をドラッグ&ドロップします。
シーンの配置していたbullet_image_01の文字が青くなったと思います。
これでprefab化に成功です!
次にprefabの中にあるbullet_image_01をシーンにドラッグ&ドロップします。
bullet_image_01が作成されたと思います。
それの中身を見てみると、既にbulletsucriptが設定されています。
先ほどprefabに入れたbullet_image_01の複製を作成することが出来ました。
Prefab化することで、効率よく開発が出来るようになります。
5way作成
試しに5way弾を作ってみましょう。
同様にbullet_image_01を複製して、合計5つにします。
弾の座標を全部(0,0,0)にして、
angle-30、-15、0、15、30にして実行してみましょう。
無事、5way弾が出来ました!
ですが、このような手順で弾幕を作るのは大変ですので、次はスクリプトから弾を発射できるようにしましょう。
スクリプトから弾を発射
Scriptフォルダに、Launcherスクリプトを新規作成します。
Launcherスクリプトをダブルクリックして、VisualStudioを開きます。
このスクリプトは1秒間隔で弾を生成し発射する内容になっています。
以下のコードをコピペして下さい。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Launcher : MonoBehaviour
{
float timeCount = 0; // 経過時間
[SerializeField] GameObject shotBullet; // 発射する弾
void Start()
{
// 何も書かない
}
void Update()
{
// 前フレームからの時間の差を加算
timeCount += Time.deltaTime;
// 1秒を超えているか
if (timeCount > 1f)
{
timeCount = 0; // 再発射のために時間をリセット
// GameObjectを新たに生成する
// 第一引数:生成するGameObject
// 第二引数:生成する座標
// 第三引数:生成する角度
Instantiate(shotBullet, transform.position, Quaternion.identity);
}
}
}
float timeCount = 0; // 経過時間
[SerializeField] GameObject shotBullet; // 発射する弾
timeCountで1秒毎の間隔を管理します。
[SerializeField] GameObject shotBullet;で発射する弾をインスペクターから設定できるようにします。
今回だと、先ほどprefab化したbullet_image_01をここに設定します。
void Start()
{
// 何も書かない
}
Startは今回何も書きません。
void Update()
{
// 前フレームからの時間の差を加算
timeCount += Time.deltaTime;
// 1秒を超えているか
if (timeCount > 1f)
{
timeCount = 0; // 再発射のために時間をリセット
Updateで毎フレーム毎にTime.deltaTimeをtimeCountに加算します。
Time.deltaTimeは直前のUpdateと今回のUpdateが呼ばれたタイミングの時間の差の値になります。
具体的には直前のUpdateから今回のUpdateが0.2秒後に呼ばれた場合、Time.deltaTimeは0.2になります。
1秒経過したことを知りたいなら、Update毎にTime.deltaTimeをtimeCountに加算していき、それが1.0を超えたら1秒経過していることになります。
if文でtimeCountが1.0以上なら1秒超えたと判定して、その中で弾の発射処理を書いています。
またtimeCountを0にして、再発射まで1秒掛かるようにしています。
// GameObjectを新たに生成する
// 第一引数:生成するGameObject
// 第二引数:生成する座標
// 第三引数:生成する角度
Instantiate(shotBullet, transform.position, Quaternion.identity);
新たにGameObjectを生成するInstantiateで弾を生成します。
第一引数は生成するGameObjectです。今回だとSerializeFieldが設定されたshotBulletなので、後ほど設定するbullet_image_01になります。
第二引数は生成する座標です。今回はオブジェクトの座標になります。
第三引数は生成するオブジェクトの角度になります。今回はQuaternion.identityと指定して回転しないようにします。
これで弾が1秒間隔で発射するスクリプトが書けました。
弾を発射する方法はいろいろあり、FixedUpdateでやる方法、コルーチンを使う方法、UniRXを使う方法 等々ありますが、今回はシンプルにするためにUpdateで弾を発射しています。
詳しく知りたい人は調べてみてください。
保存して動かしてみましょう。
Launcherスクリプト実行
作成した5way弾は邪魔なので削除します。
空のGameObjectを作成します。
Hierarchyで右クリックしてCreateEmptyで空のGameObjectが作成できます。
名前はEnemyにして、座標は(0,0,0)にします。
EnemyにLauncherスクリプトをドラッグします。
これでLauncherスクリプトの設定が出来ました。
発射する弾を設定するためにEnemyのInspectorを開いて、ShotBulletにPrefabのbullet_image_01をドラッグ&ドロップします。
実行します。
弾が1秒毎に発射されているのがわかります。
Hierarchyにもbullet_image_01(Clone)というGameObjectがどんどん生成されています。
発射座標の確認
発射する座標をtransform.positionにしたのを覚えていますか?
これはスクリプトが設定されたGameObjectの座標になります。
試しに、Enemyの座標を(5,0,0)にしてみましょう。
そして実行します。
すると、弾の発射位置が右側になったと思います。
スクリプトから弾の角度と速度の変更
BulletスクリプトとLauncherスクリプトの2つに手を加えます。
Bulletスクリプト
全体のプラグラムは以下の通りです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
[SerializeField] float angle; // 角度
[SerializeField] float speed; // 速度
Vector3 velocity; // 移動量
void Start()
{
// X方向の移動量を設定する
velocity.x = speed * Mathf.Cos(angle * Mathf.Deg2Rad);
// Y方向の移動量を設定する
velocity.y = speed * Mathf.Sin(angle * Mathf.Deg2Rad);
// 弾の向きを設定する
float zAngle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg - 90.0f;
transform.rotation = Quaternion.Euler(0, 0, zAngle);
// 5秒後に削除
Destroy(gameObject, 5.0f);
}
void Update()
{
// 毎フレーム、弾を移動させる
transform.position += velocity * Time.deltaTime;
}
// !!追加!!
// 角度と速度を設定する関数
public void Init(float input_angle, float input_speed)
{
angle = input_angle;
speed = input_speed;
}
}
// !!追加!!
// 角度と速度を設定する関数
public void Init(float input_angle, float input_speed)
{
angle = input_angle;
speed = input_speed;
}
前回から弾の角度と速度を設定するInit関数が増えています。
今までInspectorで角度と速度を設定していましたが、この関数を呼ぶことで設定できるようになります。
Init関数の宣言にpublicを付けるのがポイントで、これは他のスクリプトから呼び出し可能にする宣言です。
Launcherスクリプト
全体のプラグラムは以下の通りです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Launcher : MonoBehaviour
{
float timeCount = 0; // 経過時間
[SerializeField] GameObject shotBullet; // 発射する弾
void Start()
{
// 何も書かない
}
void Update()
{
// 前フレームからの時間の差を加算
timeCount += Time.deltaTime;
// 1秒を超えているか
if (timeCount > 1f)
{
timeCount = 0; // 再発射のために時間をリセット
// GameObjectを新たに生成する
// 第一引数:生成するGameObject
// 第二引数:生成する座標
// 第三引数:生成する角度
// 戻り値:生成したGameObject
GameObject createObject = Instantiate(shotBullet, transform.position, Quaternion.identity);
// 生成したGameObjectに設定されている、Bulletスクリプトを取得する
Bullet bulletScript = createObject.GetComponent<Bullet>();
// BulletスクリプトのInitを呼び出す
bulletScript.Init(180, 3);
}
}
}
// GameObjectを新たに生成する
// 第一引数:生成するGameObject
// 第二引数:生成する座標
// 第三引数:生成する角度
// 戻り値:生成したGameObject
GameObject createObject = Instantiate(shotBullet, transform.position, Quaternion.identity);
// 生成したGameObjectに設定されている、Bulletスクリプトを取得する
Bullet bulletScript = createObject.GetComponent<Bullet>();
// BulletスクリプトのInitを呼び出す
bulletScript.Init(180, 3);
Instantiate以降を改造します。
Instantiateは生成したGameObjectを戻り値として返してくれます。
そのためcreateObjectが今回発射した弾になります。
弾に設定されているBulletスクリプトをGetComponentで取得して、
先ほど書いたBulletスクリプトのInitを呼び出します。
Initでは角度と速度が設定できるようにしたので、ここで発射した弾の角度と速度を設定します。
今回は角度は270度、速度は3にしてみました。
実行
実行してみると、弾が定期的に真下(270度)に発射されていると思います。
これで弾をスクリプトから発射するのが完成しました。
弾幕にする
Launcherスクリプトを改造して弾の発射方向を回転するようにして弾幕っぽく改造してみましょう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Launcher : MonoBehaviour
{
float timeCount = 0; // 経過時間
float shotAngle = 0; // 発射角度
[SerializeField] GameObject shotBullet; // 発射する弾
void Start()
{
// 何も書かない
}
void Update()
{
// 前フレームからの時間の差を加算
timeCount += Time.deltaTime;
// 0.1秒を超えているか
if (timeCount > 0.1f)
{
timeCount = 0; // 再発射のために時間をリセット
shotAngle += 10;
// GameObjectを新たに生成する
// 第一引数:生成するGameObject
// 第二引数:生成する座標
// 第三引数:生成する角度
// 戻り値:生成したGameObject
GameObject createObject = Instantiate(shotBullet, transform.position, Quaternion.identity);
// 生成したGameObjectに設定されている、Bulletスクリプトを取得する
Bullet bulletScript = createObject.GetComponent<Bullet>();
// BulletスクリプトのInitを呼び出す
bulletScript.Init(shotAngle, 3);
}
}
}
float shotAngle = 0; // 発射角度
まず弾を回転させるために、角度を保持するための変数shotAngleを用意します。
shotAngle += 10;
そして、発射毎に発射角度を10度加算します。
// BulletスクリプトのInitを呼び出す
bulletScript.Init(shotAngle, 3);
Init関数の中を270からshotAngleに変更しました。
最後にInitにshotAngleを渡すようにすれば、発射する毎に弾の角度が加算されて弾が回転するようになります。
// 0.1秒を超えているか
if (timeCount > 0.1f)
{
弾の回転を見やすくするために弾の発射間隔を早くしてみましょう
if文の1秒の判定を0.1秒に変更します。
これで1秒間に10発も発射されるようになりました。
実行②
弾の発射方向が回転していると思います。
これにて今回は終了です。
Launcherを改造して弾幕の形を変えてみたり、
Enemyを増やしてみると面白いかもしれませんね。
プロジェクト配布
以下のGoogleドライブから本プロジェクトをダウンロードできます。https://drive.google.com/file/d/16WqYS8HkwpQH65yRWuv8G2Olv1DvjVWD/view?usp=sharing
STG_Game\Assets\Scenes\SampleScene.unityをダブルクリックすればプロジェクトを開くことが出来ると思います。
※Libraryフォルダを削除しているので、起動に時間が掛かります。
●免責 このファイルを使用することにより発生したいかなる損害についても、責任を負いません。