Unity_ブロック崩し
まずこれからやればよかったかもしれない
Git
リモートでリポジトリ作成
.gitignoreはUnityで
こんなかんじ
https://scrapbox.io/files/6915a39e677c9fd554044c72.png
指定のフォルダにプロジェクトデータ全てを移す
Unity HubのAddから指定の物を入れる
https://scrapbox.io/files/6915a3e639e92c569e3ed1bc.png
Unity 文字化け対策
.editorconfigを自分で作って、配置
https://scrapbox.io/files/6915b3488bade09b1448c346.png
注意点
↑は、.editorconfig.txtで保存しているので動作しない
ちゃんと、.editorconfigという拡張子なしの形式で保存する。
ほか AI
「.txt」を付けずにファイルを保存するには、「ファイル名をダブルクォーテーション(「""」)で囲んでから入力する」方法があります。
windowsはこうしよう
そのあと、スクリプトをコンパイルしなおすと文字化けが消えている
⭐ほかにも、最終行に空行を入れたりできるので活用しよう
コンパイル高速化設定
https://scrapbox.io/files/691805b4d4434c74874e7127.png
https://scrapbox.io/files/6918058901e3fca6566eba41.png
デメリット
static の値は初期化されなくなるので注意。とりあえずやってみる
参考
覚えたことのメモ
Rigidbody
割り当てたGameObjectに物理属性を持たせる。
動きに空気抵抗を与えたり、重力を与えたりできる。
Body Type
Dynamic
物と物がぶつかると、押されたりするようになる
Kinematic
ぶつかっても押されなくなる。パドルなどはこれをしておくとよいだろう。
Angular Damping
回転に対する空気抵抗。高いほど回りづらくなる。
Collision Detection
衝突判定の設定。
Contrinuous
動きを予測して、高速で物体がぶつかった場合の衝突検知を無視する挙動を防ぐ
Discreate
設定なし。処理を軽くしたい場合はこっち。
Collider
当たり判定。物と物のぶつかりをできるようにする。
これがなければ地面と球のObjectがあっても、貫通したりする
is Trigger
物理的な衝突をなくしてコライダー同士がすり抜けるようになる代わりに、イベントが使えるようになる
OnTriggerEnter, OnTriggerExit など、触れた時のメソッドが使える。
アイテムとか、エリア侵入判定に使う
Physics Material 2D
Friction
摩擦のこと。
Bounciness
跳ね返り係数。1に近いほどより跳ね返り、0に近いほど跳ね返りづらい。
linearVelocityとAddForce
linearVelocity
線形速度
ジャンプ処理とか、ノックバックみたいな動き
いきなり前回速度になる
AddForce
物理演算に基づいて、力を加えることになる
はねたり弾ませたりするときはこっち。アイテム出現時にはねるモーションはこれを使っている
Player
transform.position
Update()で弄ることで、このスクリプトを持つ物体の位置を変えている
ただし、transformは即時移動なのであまり推奨されない。Rigidbodyを用意して、そちらで捜査すべき
FixedUpdate()
物理演算の更新タイミングで呼ばれる。必ず同じ時間間隔で呼ぶ
物理にかかわる処理(移動など)はこれを使う
code:c#
private void FixedUpdate()
{
var next = rb.position + Vector2.right * (xInput * speed * Time.fixedDeltaTime);
rb.MovePosition(next); // 物理フレンドリーな移動
}
var next
rb.position: プレイヤーの位置に、
Vector2.right: (1,0)を表すベクトル 。つまり→(xに1)
指定のスピードで
xInput (+1 か-1をかけて、右か左にする)
Time.fixedDeltaTime: 1回の物理ステップの時間(デフォルト0.02秒)
MovePosition
rbを指定の位置まで物理移動してくれる関数。
不自然ながたつきやすり抜けがなくなる
パドルの壁抜け
Kinematic設定としているため、壁に当たっても衝突検知せず、貫通するという理屈(おしかえしがない)
なので、Scriptで制御する
rb.positionは、GameObjectの中心座標を返す
code:c#
private void FixedUpdate()
{
var next = rb.position + Vector2.right * (xInput * speed * Time.fixedDeltaTime);
if (next.x > rightLimit)
next.x = rightLimit;
if (next.x < leftLimit)
next.x = leftLimit;
rb.MovePosition(next);
}
x座標が-3より減りそう(左に進みすぎ)たら、超えないように。3より増えそう(→)ならそれも超えないように。
limitを可変にする
まずは、playerの横幅を変数で調整できるようにする
code:c#
private void Awake()
{
// 現在のScale値を取得(transformはVector3なので、これで保管)
Vector3 scale = transform.localScale;
scale.x = length;
transform.localScale = scale.x // GameObjectに反映。
例えばこの場合
lengthが2fでlocalScale.x(gameObjectのscale.xの値)が0なら、xに -1, +1伸びて、合計2になる
パドル制御を改めて考えよう
パドルの右端
Playerの中心がtransform.position.x
左右には、length / 2で伸びている
つまり、x + length / 2が、実際のパドル端のx座標になる。
code:c#
// Awake()
halfWidth = width / 2;
// 画面サイズの取得
var cam = Camera.main;
// 画面の左下(0,0)と右上(1,1)をワールド座標に変換
Vector3 bottomLeft = cam.ViewportToWorldPoint(new Vector3(0f, 0f, 0f));
Vector3 topRight = cam.ViewportToWorldPoint(new Vector3(1f, 1f, 0f));
// 画面の左端のx, 右端のxを取得できた。
float worldLeft = bottomLeft.x;
float worldRight = topRight.x;
// パドルがはみ出さないように、半幅ぶんだけ内側にオフセット
leftLimit = worldLeft + halfWidth;
rightLimit = worldRight - halfWidth;
length / 2について
中心から片方の長さというのはゲームでよく使うので、Unity側が用意してくれているのでこっちを使おう
コライダーからとることができる
GetComponent<BoxCollider2D>().bounds.extents.x;
https://scrapbox.io/files/69156683abd5ca5daf116ebc.png
可変式にする
ゲーム中にパドルの長さが変わっても、都度計算されるようにしよう
Awake
それぞれの変数の初期値を格納。ゲーム開始時のパドルを決定。
code:c#
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
co = GetComponent<BoxCollider2D>();
cam = Camera.main;
// 画面の左下(0,0)と右上(1,1)をワールド座標に変換
bottomLeft = cam.ViewportToWorldPoint(new Vector3(0f, 0f, 0f));
topRight = cam.ViewportToWorldPoint(new Vector3(1f, 1f, 0f));
// 画面の左端のx, 右端のxを取得できたので、パドルがはみ出さないように半幅ぶんだけ内側にオフセット
worldLeft = bottomLeft.x;
worldRight = topRight.x;
// 開始時にInspectorで横幅を決められるようにする
// 現在のScale値を取得(transformはVector3なので、これで保管)
Vector3 scale = transform.localScale;
scale.x = width;
transform.localScale = scale; // gameObjectへ反映
}
Update
入力
code:c#
private void Update()
{
xInput = 0f;
if (Input.GetKey(KeyCode.A)) xInput = -1f;
if (Input.GetKey(KeyCode.D)) xInput = 1f;
// パドルの長さを取得し、移動上限を割り当て
Vector3 scale = transform.localScale;
scale.x = width;
transform.localScale = scale;
halfWidth = co.bounds.extents.x;
leftLimit = worldLeft + halfWidth;
rightLimit = worldRight - halfWidth;
アイテムを作ろう
Collider 2Dを持たせて、is Triggerで取得判定を作る
取得処理を考える
プレイヤー側
code:Player.cs
// どのコードからでも、Player.Instance.Xxx()と呼べるようにしておく
public static Player Instance { get; private set; }
private void Awake()
{
Instance = this;
アイテム側
code:Item.cs
private void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("アイテム取得");
Player.Instance.ChangeWidth(newWidth);
Destroy(gameObject);
}
GameManagerを使って管理する
どこからでも参照したい、ゲーム全体にかかわる状態を集約する。
Playerの参照
スコアや残りライフ
ステージクリア、ゲームオーバー判定
ポーズ中かどうか
BGMやSE等の管理
自分で作って、そこにどこからでも参照したい情報を持たせる
private Player Playerとか。
それの登録メソッドを用意し、実際の登録は別のところでしてもらう
パワーアップを10秒間にする
Coroutineを使おう。基本的に、影響対象とするObjectのクラスに配置する。
Playerの速度を上げる、パドルの長さを上げるなら、Playerにコルーチンを配置すればよい
アイテムをスポーンさせよう
まず、アイテムをPrefab化する
code:Block.cs
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ball")
{
Destroy(this.gameObject);
// 一定確率でアイテム生成
if (Random.value < dropChance)
Instantiate(itemPrefab, transform.position, Quaternion.identity);
}
}
Instantiateで増やすのが基本。
⭐itemPrefabにはコード上ではなく、Unity側でItemのPrefabを割り当てる。
ブロックを壊すとアイテムが出るようになった
はねさせる
code:Item.cs
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
private void Start()
{
// linearVelocityとは違う、力の加え方
// ぽよんと弾ませるならこっちかも?
rb.AddForce(Vector2.up * hopPower, ForceMode2D.Impulse);
}
アイテムが生まれるときに、力を加えて、その後自由落下させてやればよい。
ボールの速度が遅い
現状、こちらの手順で跳ね返らせているだけ
Ballオブジェクトに、Physics 2D Materialを紐づけ
WallオブジェクトにColliderを付与
なので、ボールが止まってしまったり、ずっと横に行ったりしてゲームが進まなくなるので解決が必要
スクリプトで以下の内容で制御する
ボールの速さが一定である
水平な角度を禁止する
まずは、以下の設定をする
Ball
RIgidBody2D
空気抵抗、重力抵抗などをなくし、Collision DetectionをContinuousとする。またz軸を固定。
Physics Material
Frictionを0にする
Script
FIxedUpdate()で(秒単位)、速度に関する内容を制御する
⭐ちなみに、Update()はフレーム単位なので60fps, 240fps環境では差が出る
まず、その状態のベクトルを保存
Vector2 v = rb.linearVelocity;
そのベクトルをifでチェックして、補正をかけていけばよい
ボールの速さ
物体の速さは、ベクトルの長さ 速さ = sqrt(x² + y²) ※sqrt: 平方根
v.sqlMagnitude
速さ(ベクトルの長さ) 二乗を返す
これが0.01とかなら、止まっている
10なら結構早いとか、それで判断ができる
ちなみに、速さを返すv.magnitudeもあるけど、平方根の計算が絡むので処理が少し重い
なので、最初から二乗を返すsqlMagnitudeが存在するわけだ
水平な角度かどうかの確認
y方向の力が少ないかどうかで分かる。
上向き(y = 1)のパターン, 下向き(y = -1)のパターンがあるので、絶対値を使う。
Mathf.Abs()
|y|を規定値とチェックして、少なければ増してやればよい
v.y = Mathf.Sign(v.y) * minY;
Mathf.Sign(): +1 or -1 or 0を返すメソッド
つまり、元々がマイナスなら、 -1 * 規定値とするのでそのまま下に移動していくことになる
調整したが、右上でスタックしてしまっている
code:ball.cs
// 最低速度チェック
// 負荷を考慮してv.magnitudeでなくsqlMagnitudeを使う
float speedSqrMagnitude = v.sqrMagnitude;
if (speedSqrMagnitude < 0.01f)
{
Debug.Log("最低速度補正");
v.x = v.x + minimumSpeed;
v.y = v.y + minimumSpeed;
}
// 例えば(5, 5)の時は、10²になるのでギリギリセーフ
else if (speedSqrMagnitude > 100f)
{
Debug.Log("最高速度補正");
v.x = maximumSpeed;
v.y = maximumSpeed;
}
速度が少ないとき、必ず右上にいくようになってしまっている
右上でスタックしてしまっている
本来やりたいことは、そのままの向きで、大きさだけ変えるということ。
方向だけを取り出す
v.normalizedを使うと、速さが1の場合のx, yが帰ってくるようになる
例えばv = (3, 4)を`normalizedする
この速さは、√3² + √4² = √25になり 、5である
normalizedを使うと、(0.6, 0.8)で返ってくる
計算するとわかるが、速さ1
code:c#
float speedSqrMagnitude = v.sqrMagnitude;
if (speedSqrMagnitude < 0.01f)
v = v.normalized * minimumSpeed;
else if (speedSqrMagnitude > 100f)
v = v.normalized * maximumSpeed;
なので、こんな感じにする
遅すぎる場合は、速度1 * 最低速度
早すぎる場合は、速度1 * 最大速度
タイトル画面を作ろう
シーン構成を決める
TitleScene
GameSceneなど。
シーン作成
まずは今のシーンを、名前を付けて保存する
新しいシーンをFile > New Scene > Basic 2Dとして作る
開いているシーンを入れておき、Build Profiles > Scene Listに追加しておく
タイトルにindexとして0を割り振っておくとよい
https://scrapbox.io/files/6916ecd88b789de87e144a9f.png
文字を書く
UI > Canvas 。EventSystemというUIを管理するオブジェクトも自動で作られる
キャンバス設定
Screen Space - Overlayで画面にUIを書くシンプルな設定
pixec perfect : 2Dなら有効にしておくとピクセルと整列するように調整される。ぼかしも減る
Canvasの子要素に、UI > textでテキストを作る
初回だけ、各要素のインポートがあるかも。
ボタンを作る
Canvasの子要素として、UI > Buttonを持たせる
色々設定
ボタンにクリック処理を作ろう
スクリプトTitleUIControllerみたいなものを作る。
中に、SceneManager.LoadScene("GameScene");みたいなものを持たせたメソッドを作ればよい
GameObject TitleUIみたいなものを作って、そこにスクリプトを割り当てる
ButtonのGameObjectに、On Click()という項目がある
ここに、スクリプトを当てたGameObjectを割り当てればよい
⭐スクリプトの割り当てではない
https://scrapbox.io/files/6916f41e920377bc4d00c6cf.png
こんな感じで、作った関数があればそれが候補に出る。
割り当てると、ボタンクリック時にシーンを変えられるようになる。
ボタンを薄暗くする
マウスホバー時に、押せる感が欲しい。
ボタンのGameObjectで、HighlighterColorを設定すればよい。
いきなりゲームが始まる
GameScene側で修正が必要
やること
ボールを止めておく
画面に3,2,1, GOみたいなのを出す
パドルはまあ動いてもいいか。
ボールを止める
発射をStart()で制御するのではなく、別メソッドで書き換える
今回はLaunch()で。
そのメソッドを、別のオブジェクトから呼ぶイメージになる。
画面に出す
同じように、UI > Textで追加。適当に場所を調整。文字は仮の文章で良い。
GameStartControllerみたいな感じのGameObjectを作る。
カウントダウン用スクリプトを作る。
これもコルーチンで制御すればいい。1秒ごとに文字を変えて、最後はGoにする
それが終わったら、Ball.Launch()を実行し、ボールを飛ばす
終わったら破棄させたっていい
問題がある
GO!の前にボールを止めていたら、最低速度補正が働いて少しずつ動いてしまう
フラグ制御にする
code:Ball.cs
public void Launch()
{
isLaunched = true;
rb.linearVelocity = new Vector3(defaultSpeed, defaultSpeed, 0);
}
private void FixedUpdate()
{
// スタート前の場合は止まっていてほしいので、補正をかけない
if (!isLaunched)
return;
// ...
こんな感じにするとよい。
あとは、カウントダウン側のコルーチンでball.launch()を実行してやるとフラグが有効となり、ボールが動き始める。
コルーチンについて
code:c#
private Coroutine countdownCo;
private void Start()
{
if (countdownCo != null)
StopCoroutine(countdownCo);
countdownCo = StartCoroutine(CountdownCo());
}
こんな感じで重複しない調整にしているが、Start()で走らせるコルーチンにはしなくてもよい
? どんなときにやるか
敵AIの巡回処理
状態異常の付与時間(重ね掛けのケースなど, 過去のコルーチンが残っているとバグになる)
フラグ管理について
Ball.csに、isLaunchedを持たせてゲーム開始を制御しているが、これは全体の状態を確認するフラグisGameStartedとしても、GameManagerに持たせたほうがよい。
それぞれ別の要素で管理するのが良い
ボールの状態は、isLaunchedで。
ゲーム全体の状態は、isGameStartedで管理する。
ゲーム関連フラグについて
code:c#
public bool isGameStarted { get; private set; }
public bool isGameOver { get; private set; }
この記述の意図
getはpublicなので、GameManager.Instance.isGameStartedという形でフラグのチェックができる
ただし、GameManager.Instance.isGameStarted = falseというsetはprivateなのでできないよということ
C#では基本的に、publicのみ書いて運用するケースは少ない。(PHPがおかしいか)
GameManagerの管理について
基本的に、一番最初のシーンで設置しておけばよい
このように書いたはず
code:GameManager.cs
private void Awake()
{
// シングルトンパターンで設計
// 他に同じInstanceが存在していたら、消すという設計(1個だけ残す)
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);// シーンをまたいで残したい場合は有効化
}
シーンをまたいだ時(タイトル→ゲーム画面など)、ここにGameManagerが保管されるようになる
https://scrapbox.io/files/6917ff289b090055817a2de8.png
なので、どこでもGameManagerオブジェクトが使えるようになるということだ
ゲームクリア画面を作ろう
設計
ブロックが全て壊れたとき
Block.csでisAllCollapseをtrueにする
isGameClearをtrueにする
SceneManager.LoadScene("ClearScene")にする
この設計でもいいが、ブロックが「全て壊れたか」を判定することになっている
なので、
GameManagerが、ブロックの数を示す変数を持つ
ブロックがStart()したとき、GameManagerに増えたよと伝える
ブロックが壊れた時、Gamemanagerに減ったよと伝える
GameManagerが、減らす処理のとき、全部壊れたかを判定するような設計にしてやるとよい。
クリア時の設計
ClearSceneではなく、カウントダウンのUIを併用すればいいだろう。フラグは立てる。
そのフラグがtrueだと、もう1回みたいなボタンを表示させる。
ボタン作成
TextMeshProで日本語が化ける
Google Fontから落とす
gistから落とす
展開
今回はUnityのAssetsに、Fontsというフォルダを作って管理することにする
Google Fontから落としたものを回答して、Midiumサイズを格納
こんな感じで設定
結構失敗したけどこれで行けた
https://scrapbox.io/files/6918234d4f0127a64c0f6084.png
Git管理するとき
まず、.gitignoreに追加してからpushすること
ファイル容量が大きすぎると怒られるし、やり直しがかなり大変
やってもうたら、リセットしてやり直すのが一番手っ取り早い
コードとかオブジェクト書き直しはがんばる...
https://scrapbox.io/files/6918396f3d98075e72ac7926.png
もう一度遊ぶボタンの実装
GameStartControllerでの、クリアフラグチェック
Update()で見ればよいのだが、処理が走り続けてしまう挙動を防ぐ
このコントローラで、クリア表示を出すかのフラグを作り、ゲームクリアフラグと一緒に見るとよい
デフォルトで見えないようにする
これは、Unity側で非アクティブとなるようなチェック操作をすればよい
クリア時に表示する
GameStartControllerの子要素に、カウントダウンとこのボタンを持たせた
このボタンの取得はGetComponentInChidren()で持たせず、Unityで直接参照させるのが基本
クリア時にボールを止める
ボールの速度補正処理に、スタート時点はしないという分岐がある
そちらにクリア時も追加
その後、GameStartManagerでball.StopBall()をすればよい
押すと、もう一度遊べるようにしよう
ボタンにもう一度遊べるようにするメソッドを割り当てればよい。
このメソッドの詳細は別で考える
デフォルトの状態に戻せるようなメソッドを用意する
要するにブロックを崩した後でも復活し、もう一度遊ぶことができる
ブロック周り
Destroyしたブロックを復活させて再配置するには、どうすればいいのだろうか
今回は、シーンの再ロードをとろう
stage2, stage3... とすると、別の処理が必要になるかもしれない。それは次のゲームで。
GameManagerの問題について
TitleSceneに配置しており、シーン遷移してもDontDestroyOnLoadで稼働する。
ただしデバッグ時などにGameSceneだけを再生したい場合、存在しないためエラーになる挙動を防ぎたい
調整する
[RuntimeInitializeOnLoadMethod()]
シーン再生の前に呼び出すようにできる
RuntimeInitializeLoadType.BeforeSceneLoad
Enum。それぞれ種類があるが、今回はシーンが読まれる前に呼ぶようにする
つまり、GameScene再生前に呼び出すようにした
他のスクリプトとの兼ね合いはどうするか
たとえばPlayer.csがAwakeしたとき、GameManager.Playerに割り当てる設定をしている
ただし、GameManagerが生まれるのが遅く、NullReferenceException: Object reference not set to an instance of an objectエラーに遭遇してしまった
これは、挙動としてはおかしい
実行順は、RuntimeInitializeOnLoadMethod -> 各Awake()となるので。
なのでコードが変で、正しくGameManagerを作れていないということになる
code:c#
private static void AutoCreate()
{
if (Instance == null)
{
var prefab = Resources.Load<GameManager>("GameManager");
if (prefab != null)
Instantiate(prefab);
}
}
var prefab = Resources.Load<GameManager>("GameManager");
これが悪い
Resources.loadは、Assets/Resources下のファイルを呼び出す処理
今ファイルは、Assets/Prefabsにあるので読めていない
ただAssets/Prefabsが悪い配置なのかといえばそうでもないっぽい。
なので、GameManagerはPrefabs化するのではなく、直接作るようにしよう
code:c#
var go = new GameObject("GameManager");
go.AddComponent<GameManager>();
こうすればよい
⭐Prefabsから、データを取ってくるようなことは基本しない。Unity側のInspectorで割り当てるために使うのがほとんど。
なので、PrefabsからGameManagerは消しちゃおう。
ゲームオーバーを作る
設計
カメラとボールの座標関係を考える
画面の左下が(0, 0)なので、x, yどちらかがマイナスになると外とわかる
そのとき、ゲームオーバーにすればよい
ボール座標と、カメラ外かどうかのチェック
FixedUpdate()でtransformをチェック仕様と思ったが、Ballのデフォルトは画面中心である
カメラ座標のベースは左下なので、かみ合わない。どうするか?
便利なメソッド、Camera.WorldToViewportPointを使えば解決する
WorldToViewportPointを扱うにあたって、以下の座標系について理解する必要がある
座標一覧
ワールド座標
transform.positionが返す座標のこと
ゲーム内の絶対的な位置を表す
スクリーン座標
画面のピクセル座標
左下が(0, 0)で、右上が画面最大の座標となる
UIの設定とかでよく使う。(スコア表示はどんな画面でも右上に設定しておくとか)
ビューポート座標
画面を0から1の範囲で正規化した座標。
要するに左下が(0, 0), 中央が(0.5, 0.5), 右上が(1, 1)である
つまり、
左に飛び出した: x < 0
右に飛び出した: x > 1
下に飛び出した: y < 0
上に飛び出した: y > 1
と、簡単に検知できるのである。これはすごい
WorldToViewportPoint
ワールド座標をビューポート座標に変換するメソッド
code:Ball.cs
private void CheckGameOver()
{
if (hasGameOvered)
return;
Vector3 v = GameManager.Instance.cam.WorldToViewportPoint(transform.position);
if (v.y < 0f)
{
Debug.Log("GameOver");
hasGameOvered = true;
GameManager.Instance.GameOver();
}
}
とすると、Ballのtransform.position(ワールド座標)を変換した値がvに入っている
なので、画面下に出てしまったらゲームオーバーなら、こうしてやれば解決する
他にも変換ができるので、機会があれば使う
ViewportToWorldPoint
WorldToScreenPoint
エラーの解決
Ball.csのUpdate()
code:cs#
Vector3 v = GameManager.Instance.cam.WorldToViewportPoint(transform.position);
MissingReferenceException: The object of type 'UnityEngine.Camera' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
カメラ情報が取れずエラーになる。これは、GameManagerにカメラをもたせている設計が悪い
カメラは、シーンごとに存在している
シーン遷移すると、前のCameraは破壊され、新しいCameraが生まれる
GameManagerがTitleSceneのCameraをとって、GameSceneに移動したときになくなってしまっている
カメラは軽めに取得できるので(内部キャッシュされるらしい)、毎回その場で撮って構わない。
GameManagerに持たせるのはだめ
おとなしく、Playerにprivate Camera vamとして持たせるのが良い
? シーン遷移したとき、Playerに持たせたCameraは大丈夫なのか?
大丈夫。GameManagerとPlayerはライフサイクルが違う。
GameManager
一度作ったら、基本的にそのまま。
Player
シーン遷移すると、流用せず再生成するコードになっている
Playerを使いまわして進むケースは少ない
⭐疑問: RPGとかそういう場合のケースはどうか
Playerが1面から2面に進むとき、レベル情報などを保持しているはずである
その場合でも、基本的にPlayerのライフサイクルは基本的にシーンごとに切り替える
キャラとキャラの状態は分離する
Playerと、PlayerStatus
シーン移動したら
Playerを再生成して
player.ApplyStatus(PlayerStatus)というように、データ注入する形が基本
例外は、オープンワールド系のゲーム。
なるほどー
EX: スコアを出す
スコアUIをつくろう
テキストやUIは、必ずCanvasの子要素としよう
ScoreControllerをつくる
子要素にCanvasをもたせる
ScoreController.csをつくる
[SerializeField]でスコア情報を割り当てられるような領域を作る
それを操作していく
できた!
ビルドを試して分かった不具合の修正をする
UIを100pxなどで書いていると、解像度を下げた時に大きくなりすぎる
画面サイズに応じてスケールさせる。
対象のCanvas > Canvas Scalerインスペクタ
UI Scale ModeをConstant から Scale With Screen SIzeに
Reference Resolutionを想定している比率に(1080 * 1920)
Matchを1に (1: 縦長基準 0: 横長基準)
https://scrapbox.io/files/691aa13458c86068ec825f7c.png
Todo
タイトル画面 ✅
ゲームクリア ✅
ゲームオーバー画面✅
アイテムが上から降ってきて、それをとるとパドルが伸びる ✅
ボールの跳ね返りが真横になりすぎないようにしたい ✅
基本的なQA
壁とボールが貫通しない
ボールにはRigidbody 2d. Collider 2Dを持たせる
壁には、Collider 2Dを持たせる。ちゃんと2Dを持たせているか確認
次は、壁のCollider2DのisTriggerが無効になっているか。
WebGLとして公開してみる
第一歩としてやってみよう
Unity 2022 まで WebGL Build Support -> Unity 6(正式には Unity 6000 系)から Web Build Support(新名称)になった
Unity Hubからこれを入れた
File > Build Profiles
Switch Platformで、Web用のプロジェクトに切り替わる
画面解像度などのチェック
Edit > Project Settings
https://scrapbox.io/files/6919922448281e8d31aa84db.png
Resolution and Presentation
Publishing Settingsなどで、色々変えられる
例:
Width: 800
Height: 600
📌 画面調整でよく変更する項目
① Default Canvas Width / Height
→ UnityRoomでの見た目を決める
② Auto Graphics API
→ 通常はそのままで OK
③ Publishing Settings
ここでは WebGL のビルド品質や圧縮を設定できます。
Compression Format: Gzip or Brotli(推奨)
Decompression Fallback: Enabled
Data Caching: オン
Memory Size: 256〜512MB(作品による)
ビルドしよう
File > Build Runで!
Development Build は、Build Setting > Platform Settingsにある
https://scrapbox.io/files/691aaa45c5dd9a2aafa06127.png
Unityroomへのアップ
https://scrapbox.io/files/691aab6ab54a114b677a9c81.png
こんな感じで上げれる
あげた
すごい