前回でクリッカーゲームの基本的な動作ができ上がったので、ビジュアルを派手にして行きたいと思います。アニメーションと効果音をつけていきます。
寿司の画像を本番用に
ここまで、ゲーム内で使う画像は私が描いた仮の画像を使っていましたが、ちゃんと動くゲームを作れる目処が立ったので本番用の画像に差し替えたいと思います。
画像はいらすとやさんからお借りしました。こんな感じになりました。
画像の透過部分をクリックできないようにする
この巨大な寿司をクリックするとお金が増えていくというのがこのゲームなのですが、気になる点がありまして。四角いImageオブジェクトに透過PNG画像を貼り付けているのですが、透過部分(寿司じゃないところ)もクリックできてしまうわけです。
透過部分のクリック判定を無効化します。
- 寿司画像のインスペクターで「Advanced > Read/Write」にチェックを入れる
- スクリプトを修正する
public Image imageSushi; // 巨大寿司画像
void Start()
{
...省略...
imageSushi.alphaHitTestMinimumThreshold = 1; // 透過部分をクリックできないようにする
}
imageSushi
変数にはUnity側でSushiオブジェクトを紐付けます。
alphaHitTestMinimumThreshold
はクリック判定が有効になる不透明度のしきい値です。0
ならば不透明度が0%以上でクリック判定が有効(初期値)、0.5f
ならば50%以上で有効、1
ならば100%以上で有効です。
これで、寿司部分だけをクリックできるようになりました。
寿司をクリックしたときのアニメーション
寿司をクリックしたときにアニメーションさせたい。
ヒエラルキーのSushi
オブジェクトを選択した状態でメニューの「Window > Animation > Animation」を選択します。
Animationウインドウが開くので「Create」ボタンをクリック。「SushiAnimation」と名前をつけて保存します。
これでAssetsフォルダにアニメーションクリップとアニメーターが作成されます。また、SushiオブジェクトにはAnimatorコンポーネントが追加されます。
Animationウインドウの「Add Property > Rect Transform > Scale の +」をクリックして、プロパティを追加します。Scaleはオブジェクトの拡大/縮小をすることができるプロパティです。
とりあえずアニメーションの長さを0:30に、途中の0:15あたりで x, y の値を2にしてみます。で、再生ボタンをクリックすると、ちゃんと寿司が動きました。
これだと動きが大きすぎるので、x, y は 1.2 に、長さは 0:20 にしました。
アニメーションをループさせないようにします。AssetsフォルダのSushiAnimationを選択し、インスペクターで「Loop Time」のチェックを外します。
ヒエラルキーのSushiオブジェクトを選択した状態でメニューの「Window > Animation > Animator」を選択します。
Animatorタブが開きます。Animatorはアニメーションをコントロールするための仕組みです。
「Parameters」タブをクリックして、「+」から「Trigger」を追加します。「New Trigger」という名前のトリガーが追加されるので「SushiClick」に名前を変更します。
あとで、このトリガーを使って寿司がクリックされたときにアニメーションするようにします。
Animatorタブの右側の図を見ると「Entry」から「SushiAnimation」に矢印がつながっています。これだと、ゲームが開始した途端にSushiAnimationが実行されてしまうので、次のように変更します。
- 「Any State」を右クリックして「Make Transition」を選択。
- 矢印を「SushiAnimation」につなげる。
- 今つなげた矢印を選択してインスペクターの下のほう「Conditions」の「+」をクリックしてトリガー「SushiClick」を設定。
- Animatorのなにもないところで右クリックして「Create State > Empty」を選択。「New State」が作成されます。
- 「Entry」を右クリックして「Set StateMachine Default State」をクリック。矢印を「New State」につなげる。
以下のようになります。
スクリプトを修正します。
public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
...省略...
public Animator sushiAnimator; // Animatorコンポーネント
...省略...
// 巨大寿司をクリックしたとき
public void OnPointerClick(PointerEventData eventData)
{
...省略...
if (sushiAnimator != null)
sushiAnimator.SetTrigger("SushiClick"); // クリック時アニメーションを再生
}
...省略...
}
sushiAnimator
変数にはUnity上でSushi(Animator)を紐つけておきます。
以上で、寿司をクリックしたときにぴょこんと動くアニメーションができました。
寿司をクリックしたときの効果音
寿司をクリックしたときに音を鳴らしたいです。ボタンにクリック音を付けるのは以前やったんだけど、今回はImageオブジェクトなので違うやり方。
- クリック音の音声ファイルをAssetsフォルダに入れておく。効果音ラボさんからお借りしました。
- ヒエラルキータブの何もないところで右クリック「Audio > Audio Source」を選択して Audio Source を作成。
- スクリプトの修正
public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
...省略...
public AudioSource audioSource; // Audio Source
public AudioClip clickSound; // 寿司のクリック音
...省略...
// 巨大寿司をクリックしたとき
public void OnPointerClick(PointerEventData eventData)
{
...省略...
if (clickSound != null)
audioSource.PlayOneShot(clickSound); // クリック音を再生
}
...省略...
}
Unity側でaudioSource
とclickSound
を設定。
効果音を再生するメソッドはPlayOneShot
を使います。Play
メソッドはBGMを再生するとき、PlayOneShot
メソッドは効果音を再生するとき。
おしながきのレベルに合わせて寿司画像を変更したい
おしながきのレベルをアップしたら巨大寿司の画像が別の寿司ネタに変わるようにしたい。ついでに右下に「?」マークのリストを作っておいて、1つずつアンロックされていくみたいにしたい。
スクリプトをゴリゴリ書いていきます。
public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
...省略...
public Image[] sushiMenuImages; // 小さい寿司画像を表示するImageオブジェクトの配列
public TextMeshProUGUI[] sushiMenuTexts; // 寿司の名前を表示するTMPの配列
public Sprite[] sushiSprites; // 寿司画像の配列
public string[] sushiNames; // 寿司の名前の配列
...省略...
// メニューのレベルアップ
void UpgradeMenu()
{
if (menuCurrentLevel < menuUpgradeCosts.Length - 1) // メニューのレベルが最大でない場合
{
double upgradeCost = menuUpgradeCosts[menuCurrentLevel + 1]; // 次のメニューのアップグレードコストの取得
if (money >= upgradeCost) // 所持金がアップグレードコスト以上の場合
{
...省略...
UpdateSushiImage(); // 寿司画像の更新
}
}
}
// メニューの寿司画像の更新
void UpdateSushiImage()
{
int level = Mathf.Clamp(menuCurrentLevel, 0, sushiSprites.Length - 1);
GetComponent<Image>().sprite = sushiSprites[level]; // 巨大寿司の画像を変更
for (int i = 0; i < sushiMenuImages.Length; i++)
{
if (i == level)
{
sushiMenuImages[i].sprite = sushiSprites[level]; // ?マークを寿司画像に変更
sushiMenuTexts[i].text = sushiNames[level]; // 寿司の名前を表示
}
}
}
...省略...
}
これで実行してみたところ以下のエラーが発生。
新しく追加した寿司画像の「Advanced > Read/Write」にチェックが入っていないことが原因でした。てへぺろ(古い)。
画像の透過部分をクリックできないようにしたとき、まぐろ画像はチェック入れたんだけど、他の画像入れてませんでした。
うしろに寿司を流したい
巨大寿司のうしろが殺風景なので寿司を流したい。自動収益(寿司ロボ)のレベルが1になったら流れ始めて、寿司ネタがアンロックされたら流れる寿司の種類も増えていったら楽しそう。こんな感じで。
頭で想像するのは簡単だけど、実現するのは大変だったばい。
小さい寿司を流すためのレーンを「UI > Panel」で作ります。これは大きい寿司オブジェクト(Sushi
)と同じ階層に作ります。そして大きい寿司オブジェクトよりも上に配置します。
SushiLane1
の下にImageオブジェクトを作成して「RunningSushi
」と名付けます。
インスペクターでオブジェクト名の横のチェックを外します。これでオブジェクトが非アクティブになりました。
RunningSushi
のアニメーションを作成します。プロパティは「Rect Transform > Anchored Position」を使います。8秒かけて左から右へ流れていくアニメーションを作りました。
このとき、Position.xの値を数値で指定しただけではうまくいきませんでした。一定の速度で移動させたいのに、最初はゆっくりで徐々に加速し減速して止まるという動きになってしまいます。これで半日悩みました。
答えはAnimationウインドウの下にある「Curves」をクリックしたところにありました。
ベジェ曲線みたいなのが表示されました。横軸が時間で縦軸が座標です。ハンドルをいじってこの曲線が直線になるようにすると一定の速度でオブジェクトが移動するようになります。
RunningSushi
オブジェクトをプレハブ化します。Assets
フォルダの下にPrefabs
フォルダを作成し、ヒエラルキーのRunningSushi
オブジェクトをPrefabs
フォルダにドラッグ&ドロップします。
逆方向に流れる寿司を作ります。
SushiLane2
の下にImageオブジェクトを作成して「RunningSushiReverse
」と名付けます。- アニメーションを作ります。
- プレハブ化します。
スクリプトを書きます。スクリプトでは次のことをしています。
- コルーチンを使って1秒おきに1つプレハブを元に新しい寿司オブジェクトを生成する。
- 奇数の寿司レーンでは左から右へ、偶数の寿司レーンでは右から左へ流れるオブジェクトを生成する。
- おしながきレベルに応じて寿司画像を変更する。
- 流れる寿司が画面から消えたら削除する。
public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
...省略...
public GameObject[] sushiLanes; // 寿司レーンを格納する配列
public GameObject runningSushiPrefab; // 流れる寿司画像のプレハブ
public GameObject runningSushiReversePrefab; // 逆に流れる寿司画像のプレハブ
...省略...
private int runningSushiCount = 0; // 流れる寿司の生成数をカウントする変数
void Start()
{
...省略...
StartCoroutine(CreateRunningSushi()); // 流れる寿司のコルーチンを開始
}
...省略...
// 流れる寿司を作成するコルーチン
IEnumerator CreateRunningSushi()
{
while (true)
{
yield return new WaitForSeconds(1f); // 1秒待つ
if (roboCurrentUpgradeIndex >= 1) // 寿司ロボのレベルが1以上ならば
{
for (int i = 0; i < sushiLanes.Length; i++)
{
// プレハブを元に新しいオブジェクトを作成
GameObject newSushi = Instantiate(
i % 2 == 0 ? runningSushiPrefab : runningSushiReversePrefab,
Vector3.zero,
Quaternion.identity,
sushiLanes[i].transform
);
// Imageコンポーネントを取得し、Spriteを変更
Image image = newSushi.GetComponent<Image>();
if (image != null)
{
int spriteIndex = runningSushiCount % (menuCurrentLevel + 1);
image.sprite = sushiSprites[spriteIndex];
}
// 寿司のアクティブ化と削除
newSushi.SetActive(true);
Destroy(newSushi, 8f);
}
// カウントを増加
runningSushiCount++;
}
}
}
}
振り返り
- はじめてゲームらしいゲームを作ることができた。余は満足じゃ。
- もっといろんな要素を入れたかったけど、時間が足りなかった。(そもそも Game A Week といいながら2週間かかってる)
- 巨大寿司をクリックしたときのアニメーション、1回目だけ大きくて2回目以降が小さくなる現象の原因はわからないままだった。
- ゲームって思ってた以上にグラフィックやサウンドで面白く感じるようになるもんなんだなぁと思った。もっと見た目にこだわらなきゃいけないね。
このゲームはunityroomで公開しています。
でわでわ
コメント