はじめに
こんばんは。第2回です。
今日やること
- プレイヤーの動きを実装する
- 壁、ボールのオブジェクトを追加する
- 物理演算を実装する
前回のおさらい
前回(https://ch-random.net/post/1942/)では、オブジェクトの作成方法・スクリプトの使い方・C#の基礎知識を扱いました。
いくつかUnityの用語が出てきたと思うので、もう一度確認しておきましょう。
用語 | 英語名 | 意味 |
---|---|---|
オブジェクト | Game Object | シーンに配置する物体すべてを指す用語 |
スクリプト | Script | C#のプログラムファイル(拡張子はcs) |
シーン | Scene | ゲームの1画面のデータ |
プレイヤー用オブジェクトの編集
まずは、前回作った正方形のオブジェクト(「Square」)を少し編集します。
ヒエラルキーからSquareオブジェクトを選択し、右クリック→「名前を変更」から名前を変更します。名前はPlayerにしておきましょう。
次に大きさを変更します。Playerオブジェクトを選択した状態で、右側のインスペクターウィンドウを見てください。
インスペクター上部に「Transform」という項目があります。ここからオブジェクトの位置・角度・大きさを変更することができます。
今回は以下のように設定しましょう。
項目 | X | Y | Z |
---|---|---|---|
位置 | 0 | -3.5 | 0 |
回転 | 0 | 0 | 0 |
スケール | 2 | 0.5 | 1 |
このようになればOKです。
プレイヤーにコンポーネントを追加
次に、Playerオブジェクトにコンポーネントと呼ばれるものを追加していきます。
コンポーネント(Component)とはオブジェクトに機能を追加するもので、様々な種類があります。
コンポーネントの管理はインスペクターから行うことができ、Playerオブジェクトには「Transform」、「Sprite Renderer」、「Playerスクリプト」の3つのコンポーネントが追加されています。(スクリプトもコンポーネントの一種です)
インスペクター下部の「コンポーネントを追加」から、「Rigidbody 2D」というコンポーネントを追加してみましょう。(「Rigidbody」は3D用のコンポーネントなので間違えないように)
Rigidbodyは剛体という意味ですが、これを追加することでオブジェクトに物理演算を適用することができます。
これを追加した状態で試しにテストプレイを行うと、Playerオブジェクトが下へ落下していきます。
これはRigidbody2Dを追加するとデフォルトで重力が適用されてしまうためです。インスペクターから「重力スケール」の値を0にすることで落ちなくなりますので設定しておきましょう。
また、Playerオブジェクトは左右のみに移動するようにしたいので、Rigidbody2Dの「Constraints」のY, Zにチェックを入れておきます。
これでPlayerオブジェクトがボールにあたったときに上下方向に位置がずれたり、回転したりしなくなります。
もう一つコンポーネントを追加します。「コンポーネントを追加」→「Box Collider 2D」を選択してください。
Box Collider 2Dはコライダーと呼ばれるものの一つで、当たり判定を追加するものです。Box Collider 2D以外にもCircle Collider 2Dなどもあります。
以下によく使われる(であろう)コンポーネントの一覧を載せておきます。
名前 | 機能 |
---|---|
Transform | オブジェクトの位置・回転・大きさを扱う。すべてのオブジェクトについている。 |
Sprite Renderer | スプライト(画像)を扱う。オブジェクトの見た目として画像を使いたい場合に使う。 |
Rigidbody2D | 物理演算を実装する。重力を実装したり、速度を加えてオブジェクトを動かしたいときに使う。 |
BoxCollider2D | 四角形の当たり判定を実装する。Rigidbody2Dと併用することが多い。 |
プレイヤーの動きの実装
前回作ったPlayerスクリプト(Player.cs)を編集して、プレイヤーの動きを実装してみましょう。
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 自身のRigidbody2Dを取得してrbに代入
Rigidbody2D rb = GetComponent<Rigidbody2D>();
// 速度を0にする
rb.velocity = new Vector2(0, 0);
if (Input.GetKey(KeyCode.LeftArrow))
{
// もし左矢印キーが押されたら速度ベクトルを(-3, 0)にする
rb.velocity = new Vector2(-3, 0);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
// もし右矢印キーが押されたら速度ベクトルを(3, 0)にする
rb.velocity = new Vector2(3, 0);
}
}
}
いきなりよくわからないコードが出てきて困惑しているかもしれませんが、とりあえず上のコードをコピーしてください。
(このコードの解説は次回以降のどこかでする予定です)
コピーできたら保存して、テストプレイしてみましょう。
左右の矢印キーを押してプレイヤーが動けば成功です!
このようなエラーが出た場合はRigidbody2Dのコンポーネントが追加できていないので確認してみましょう。
これで一旦プレイヤーのオブジェクトは完成です。
さらにオブジェクトを追加
壁の追加
次は他のオブジェクト(壁・ボール)を追加していきましょう。
まずは壁を作ります。ヒエラルキーウィンドウから、右クリック→「2D オブジェクト」→「スプライト」→「正方形」をクリックします。
これを合計4回やって、4つの正方形オブジェクトを作りましょう。
名前はWall1, 2, 3 ,4にしておきます。
Playerオブジェクトのときと同様に、インスペクターのTransformの値を変更していい感じの位置とサイズにします。
位置とサイズの例を以下に書いておきます。(この通りでなくてもOKです。自由に決めてもらって構いません)
Wall1 | X | Y | Z |
---|---|---|---|
位置 | -3.5 | 0 | 0 |
回転 | 0 | 0 | 0 |
スケール | 0.5 | 9.5 | 1 |
Wall2 | X | Y | Z |
---|---|---|---|
位置 | 3.5 | 0 | 0 |
回転 | 0 | 0 | 0 |
スケール | 0.5 | 9.5 | 1 |
Wall3 | X | Y | Z |
---|---|---|---|
位置 | 0 | 4.5 | 0 |
回転 | 0 | 0 | 0 |
スケール | 7.5 | 0.5 | 1 |
Wall4 | X | Y | Z |
---|---|---|---|
位置 | 0 | -4.5 | 0 |
回転 | 0 | 0 | 0 |
スケール | 7.5 | 0.5 | 1 |
上の例だとこんな感じになります。
これら4つのWallオブジェクトにも、BoxCollider2Dをアタッチしていきます。(Rigidbody2Dは要りません)
Unityでは複数のオブジェクトに対して同じ操作を繰り返す場合、オブジェクトを複数選択してその全てに操作を適用することができます。
ヒエラルキーから4つのWallオブジェクトを選択した状態で、インスペクターからBoxCollider2Dを追加すると4つ全てにBoxCollider2Dが追加できます。
複数のオブジェクトを選択するには、Shiftキーを使う必要があります。
今回の場合、Wall1を選択した状態でShiftキーを押しながらWall4をクリックするか、Wall1を選択したあとにShiftキーを押しながら下矢印キーを3回押すことでWall1~4を選択できます。
ボールの追加
同様にしてボールのオブジェクトも作成します。
ヒエラルキーで右クリック→「2D オブジェクト」→「スプライト」→「サークル」で円のオブジェクトを作成します。
名前は「Ball」にしておきます。
デフォルトのままだと少し大きいので、大きさを1/4(0.25倍)にします。上の画像で赤く囲ってあるアイコンをクリックすると、スケールのX, Y, Zの値を全て等しく保ったまま変更できます。
スケールのX, Y, Zがそれぞれ0.25になるようにします。
こちらにはコライダーとしてCircleCollider2Dを追加します。当たり判定が円の形になるだけで、機能はBoxCollider2Dと同じです。
あとRigidbody2Dも追加しておきます。重力スケールを0にするのを忘れないようにしましょう。
ボールのスクリプト
ボールを制御するスクリプトを作っていきます。
前回作ったScriptsフォルダに、新しくBallという名前のスクリプトを作成しましょう。
中身は以下のようにします。
public class Ball : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(5, 5);
}
// Update is called once per frame
void Update()
{
}
}
スクリプトの内容は、ゲーム開始時にボールの速度ベクトルを(5, 5)にするというものです。(つまり、ボールは右上に移動します)
スクリプトを保存したら、Ballオブジェクトにアタッチします。
テストプレイして、ボールの挙動を確認してみましょう。
ボールが右上に移動すればOKです。
物理マテリアルを適用
さっきのテストプレイでは、ボールは壁に当たったあと跳ね返りませんでした。ブロック崩しではボールは壁やプレイヤーに当たると速度を変えずに跳ね返る(反発係数e=1)ので、次はこれを実装しましょう。
反発係数を実装するために、今回は物理マテリアル(Physics Material)を使います。
まずは物理マテリアル用のフォルダを作ります。名前はMaterialsにします。
Materialsフォルダ内で右クリック→「作成」→「2D」→「Physics Material 2D」を選択します。
注意:必ず2D用の物理マテリアル(Physics Material 2D)を作るようにしてください。「マテリアル」や、「物理マテリアル」(「2D」がついていないのは3Dゲーム用です)は別物なので選択しないようにしましょう。
名前はBallMaterialとでもしておきます。
作った物理マテリアルを選択して、インスペクターから「Friction」を0に、弾性力を1にしましょう。これで摩擦係数が0に、反発係数が1となります。
物理マテリアルはRigidbody2Dで設定する形でオブジェクトに適用することができます。
Ballオブジェクトを選択して、インスペクターのRigidbody2Dの「マテリアル」の項目にBallMaterialをドラッグアンドドロップで設定しましょう。
テストプレイ
テストプレイしてみましょう。
ボールが速度を保ったまま跳ね返ればOKです。
今回の作業はここまでです。お疲れ様でした。
課題1(Player, Ballスクリプト)
今回は課題を2つ用意しました。1つ目は、今回作成したPlayerスクリプトとBallスクリプトに少し手を加える課題です。
今、どちらのスクリプトもオブジェクトの速度を変更する際に定数(数字)をそのまま書いて速度を指定しています。
ですが実際にゲームを作る際は、それぞれの速度をすぐに変更しやすいように速度の数値を変数で置くことが多いです。
前回(第1回)の課題でC#での変数の扱い方について解説しましたので、その知識を使って実際にPlayerスクリプトとBallスクリプトを修正してみましょう。
2つのスクリプトそれぞれでfloat型のspeedという変数を作り、Playerスクリプトでは3を、Ballスクリプトでは5を代入して、速度ベクトルの要素としてspeedを使うようにしてください。
答えは次回(第3回)の最初の方に載せておきます。
以下にスクリプトを載せておきます。
Player.cs:
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 自身のRigidbody2Dを取得してrbに代入
Rigidbody2D rb = GetComponent<Rigidbody2D>();
// 速度を0にする
rb.velocity = new Vector2(0, 0);
if (Input.GetKey(KeyCode.LeftArrow))
{
// もし左矢印キーが押されたら速度ベクトルを(-3, 0)にする
rb.velocity = new Vector2(-3, 0);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
// もし右矢印キーが押されたら速度ベクトルを(3, 0)にする
rb.velocity = new Vector2(3, 0);
}
}
}
Ball.cs:
public class Ball : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(5, 5);
}
// Update is called once per frame
void Update()
{
}
}
課題2(C#プログラミング入門2)
前回と同様に、C#の基礎についての解説を載せておきます。
今回も基礎的な内容が多いのでプログラミング経験者は飛ばしてもらっても構いません。
コメント
今回の作ったPlayer, Ballスクリプトにも書いていましたが、プログラム内に説明書きを書く場合はコメントと呼ばれるものを使います。
コメントの書き方は2種類あります。
// 一行のコメント
/*
複数行の
コメント
*/
/(スラッシュ)を2つつなげて行の最初に書くとその行全てがコメントとなり、/*と*/で囲むとその部分がコメントとなります。
コメントの内容はプログラムの処理内容に影響を及ぼしません。
条件分岐
プログラミングにおいて重要な構文である条件分岐(if文, else文)についてです。
if・else文は以下のように書きます。
if(条件式1){
//条件式1を満たすときの処理
}
else if(条件式2){
//条件式1は満たさないが、条件式2は満たすときの処理
}
else{
//条件式1も条件式2も満たさないときの処理
}
条件式の部分はtrueまたはfalseの値を取るものが入ります。trueまたはfalseを取るものとして、bool型の変数または以下の関係演算子・論理演算子がよく使われます。
関係演算子 | 意味 | 例 |
---|---|---|
< | より小さい | a < b |
<= | 以下 | a <= b |
> | より大きい | a > b |
>= | 以上 | a >= b |
== | 等しい | a == b |
!= | 等しくない | a != b |
論理演算子 | 意味 | 働き | 例 |
---|---|---|---|
|| | または | 左右の条件式のいずれかがtrueのときtrueを返す | a == b || c == d |
&& | かつ | 左右の条件式がどちらもtrueのときにtrueを返す | a == b && c == d |
! | 否定 | 右の条件式の逆の値を返す(trueならfalse、falseならtrueを返す) | !(a == b) |
これらの演算子を使う際は、演算子の優先順位に気をつけましょう。(今回は割愛します)
前回と同様に、サンプルコードを実行する場合はPlayerスクリプトのStartメソッドに書き加えてみましょう。
void Start(){
int num = 162;
if(num % 2 == 0){
Debug.Log("偶数です"); //これが出力されます
}
else{
Debug.Log("奇数です");
}
}
↑こちらはnumという変数に代入された数値が偶数か奇数か判定するプログラムです。もう2つほど例を載せておきます。
void Start(){
int num = 5;
if(num > 5){
Debug.Log("5より大きいです");
}
else if(num < 5){
Debug.Log("5より小さいです");
}
else{
Debug.Log("5です"); //これが出力されます
}
}
こちらはnumが5より大きいか、小さいか、または5と等しいかを判定するプログラムです。
void Start(){
bool satsuki_sho = true;
bool tokyo_yushun = true;
bool kikuka_sho = true;
if(satsuki_sho && tokyo_yushun && kikuka_sho){
Debug.Log("クラシック三冠"); //これが出力されます
}
else if((satsuki_sho && tokyo_yushun) || (tokyo_yushun && kikuka_sho) || (satsuki_sho && kikuka_sho)){
Debug.Log("クラシック二冠");
}
else if(satsuki_sho || tokyo_yushun || kikuka_sho){
Debug.Log("クラシック一冠");
}
else{
Debug.Log("クラシック零冠");
}
}
こちらは三冠競走のうち何冠を達成したかを判定するプログラムです。
繰り返し処理
繰り返し処理も条件分岐と同様に、プログラミングにおいて重要な構文の一つです。必ず使いこなせるようになりましょう。
繰り返し処理の実装方法として、主にwhile文を使う方法とfor文を使う方法があります。
while文・for文は以下のように書きます。
while(条件式){
//ここに処理を書く
}
for(初期化式;条件式;反復式){
//ここに処理を書く
}
while文でもfor文でも、条件式の中身がtrueのときにブロック(波括弧の中)内の処理を繰り返します。
for文について、
- 初期化式・・・for文が呼ばれたときに最初に一度だけ呼ばれる処理です。int型の変数を置いて0を代入することが非常に多いです
- 反復式・・・for文のブロック内の処理が全て終わり、処理がもう一度繰り返されるときに呼ばれます。
以下にサンプルコードを載せておきます。
void Start(){
// 0~9の数字を出力
for(int i = 0;i < 10;i++){
Debug.Log("i:" + i);
}
//以下は上のfor文と等価
int j = 0;
while(j < 10){
Debug.Log("i:" + j);
j++;
}
}
実行結果は以下のようになります。
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
参考文献: [1]「UnityではじめるC# 基礎編」, 大槻有一郎著, エムディエヌコーポレーション, 2016