
手札からカードを選んで敵と戦う、「Slay the Spire」のようなデッキ構築カードゲーム。ルールを考えるのも楽しいですが、作り始めるとまず悩むのが「画面をどう組むか」だったりします。カードゲームは画面のほとんどがUIなので、最初に場所の割り当てを決めておくと、あとの回がぐっと楽になります。今回はデッキ構築カードゲームのシリーズの1回目として、プロジェクトを作って、スライムちゃんが待つバトル画面の枠組みを作ってみたいと思います!
- シリーズの目次:デッキ構築カードゲームの作り方
今回作るもの
- Universal 2Dでプロジェクトを作り、フォルダとシーンを用意する
- スライムちゃんの絵と、日本語のフォントを取り込む
- Canvasを「カメラに映す」設定にして、画面を3つの場所に分ける
- Stage:スライムちゃんと敵が並ぶ場所
- HandArea:手札が並ぶ場所
- Hud:エネルギーやボタンなどの表示
- スライムちゃんを置いて、その場でぷるぷる弾ませる
作るスクリプトは1つです。
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| IdleBounce | 待機のコマを切り替えて、その場で弾ませる | Slime |
今回はまだカードは出てきません。カードの形は#2と#3で作ります。
プロジェクトを作ろう
- Unity Hubで新しいプロジェクトを作る
- エディターバージョン:Unity 6.3 LTS(記事作成時点では6000.3.22f1で確認しています)
- テンプレート:Universal 2D
- プロジェクト名:DeckBuilding(好きな名前でOK)
テンプレートの違いは1-2 プロジェクトを作る時のテンプレート選択で紹介しています。
プロジェクトが開いたら、先にフォルダとシーンを整えておきます。
- ProjectウインドのAssetsで右クリック>Create>Folderで「DeckBuilding」を作る
- 自分で作ったものは全部この中に入れるので、テンプレートに最初から入っているものと混ざりません
- その中に、Scripts・Sprites・Prefabs・Data・Effects・Audio・Fonts・Scenes の8つのフォルダを作る
- Assets>Scenes>SampleSceneをBattleにリネームして、DeckBuilding>Scenesに移動する

最後に、Gameビューの画面サイズを決めます。このシリーズはPCで遊ぶ横画面のゲームとして作ります。
- Gameビューの左上の解像度のドロップダウンを開き、一番下の+を押す
- Label:Full HD、Type:Fixed Resolution、Width & Height:1920・1080
- 作ったFull HD (1920×1080)を選ぶ
スライムGameビューの見た目が小さくなりますけど、大丈夫ですか・・・?



実際の大きさで配置を決めておくと、あとで数字をそのまま使えるんだ。見づらいときはScaleのスライダーで拡大してね!
素材を取り込もう
シリーズで使う素材は、下のリンクからダウンロードしてください。回が進んだら、敵やカードの絵も同じzipに追加していきます(リンクは変わりません)。
- DeckBuildingAssets.zip(約16MB)
取り込み方は2つあります。
- かんたん:zipの中の
DeckBuildingAssets.unitypackageをダブルクリック(またはAssets>Import Package>Custom Package…)- 取り込みの設定が入った状態で入るので、次の「スライムちゃんの絵」の設定は読むだけでOKです
- 自分で入れる:zipの中の
DeckBuildingフォルダを、UnityのAssetsにドラッグ&ドロップ- この場合は、次の設定を自分で合わせてください
素材は自由に使ってOKです(クレジット不要。作ったゲームの公開・販売にも使えます)。素材そのものの再配布はご遠慮ください。
スライムちゃんの絵
今回使うのはDeckBuilding/Sprites/Slimeの中の3枚です。右を向いたスライムちゃんのwalk_61.png〜walk_63.pngを、その場で弾む動きに使います。
- ProjectウインドでSlimeフォルダを開き、walk_61をクリック → walk_63をShift+クリック(3枚すべて選択)
- Inspectorで次のように設定して、Apply
- Texture Type:Sprite (2D and UI)
- Sprite Mode:Single


Universal 2Dのプロジェクトでは、取り込んだ画像のSprite Modeが最初からMultipleになっていることがあります。Multipleのままだと「walk_61_0」のように名前が変わり、UIのImageに入れるときに分かりにくくなるので、Singleにしておきましょう。
日本語のフォント
「ターン終了」のような日本語を出したいのですが、TextMeshProに最初から入っているフォントには日本語がありません。zipに入っているNoto Sans JPからフォントアセットを作ります。
DeckBuilding/FontsのNotoSansJP.ttfを右クリック>Create>TextMeshPro>Font Asset>SDF- 「NotoSansJP SDF」が出来る(Atlas Population ModeはDynamicのまま。使った文字だけをその場で用意してくれます)
- 出来たNotoSansJP SDFを選び、InspectorのGeneration SettingsにあるMulti Atlas Textures にチェックを入れる
- 文字を書きこむ画像(アトラス)が1枚でいっぱいになったときに、2枚目を作ってくれるようになります


Dynamicのフォントアセットは、使った文字をその場で1枚の画像(1024×1024)に書き足していきます。日本語は文字の種類が多いので、このシリーズを進めていくと1枚に入りきらなくなり、「The character … was not found」という警告が出て、文字が空白になります(自分の場合は#12で起きました)。最初にMulti Atlas Texturesにチェックを入れておけば安心です。
- Edit>Project Settings>TextMesh Pro>Settings のDefault Font Assetに、NotoSansJP SDFを入れる
- 新しく作るテキストが、最初から日本語で表示されるようになる


- 初めてTextMeshProを使うと「TMP Importer」のウインドが出るので、Import TMP Essentialsを押す
フォントアセットの作り方は、9-3 TextMeshProで美しいテキストを表示しようで詳しく紹介しています。
Canvasを用意しよう
画面の部品は、ほとんどUIで作ります。まずは土台になるCanvasです(Canvasの基本は9-1 UnityのUI(Canvas)システムを知ろう)。
- Hierarchyで右クリック>UI (Canvas)>Canvas(EventSystemも一緒に出来ます)
- Canvasを選んで、Inspectorで次のように設定
- Render Mode:Screen Space – Camera
- Render Camera:Main Cameraをドラッグ
- Canvas Scalerを次のように設定
- UI Scale Mode:Scale With Screen Size
- Reference Resolution:X 1920、Y 1080
- Match:0.5


- Screen Space – Camera
- UIを、カメラの前に置いた板のように映す設定
- 最初のScreen Space – Overlayは「カメラと関係なく画面の一番手前に描く」ので、パーティクル(火花やキラキラ)をUIの手前に出せない
- このシリーズでは#10・#11で攻撃や回復のエフェクトを出すので、最初からカメラに映す設定にしておきます
- Match:0.5
- 画面の縦横比が1920×1080と違うとき、幅と高さのどちらに合わせるか。0.5は「両方の間」
- PCのゲームは、ウインドの大きさや画面の比率が人によって違うので、どちらにも偏らない0.5にしておくと、はみ出しにくくなります
画面を3つの場所に分けよう
いよいよレイアウトです。最終的なHierarchyは次の形になります。
- Canvas
- Background(背景)
- Stage(スライムちゃんと敵が並ぶ場所)
- PlayerArea
- Slime
- EnemyArea
- PlayerArea
- HandArea(手札が並ぶ場所)
- Hud(エネルギーとボタン)
- Energy
- Text (TMP)
- EndTurnButton
- Energy


Hierarchyで下にあるものほど、画面では手前に描かれます。背景を一番上、ボタンなどの表示(Hud)を一番下にしておくと、カードが重なっても表示が隠れません。
背景と、3つの場所を作る
Canvasの子に、次のものを作ります。アンカーは、Rect Transformの左上の四角いボタンで選び、そのあと位置と大きさの数字を入れます。
- Background(UI (Canvas)>Image)
- アンカー:stretch-stretch、Left・Top・Right・Bottom:0(画面いっぱいに広がる)
- Color:#F6E7CC(子ども部屋の壁のような、うすいクリーム色)
- Stage(Create Empty)
- アンカー:stretch-stretch
- Left:0、Top:0、Right:0、Bottom:320(下の手札の分をあける)
- HandArea(UI (Canvas)>Image)
- アンカー:bottom-stretch
- Left・Right:0、Pos Y:160、Height:320
- Color:黒、Alpha:40ぐらい(場所が分かるように、うすく色を付けておく)
- Hud(Create Empty)
- アンカー:stretch-stretch、Left・Top・Right・Bottom:0
- Pos Y:160
- Pos Yは、アンカー(画面の下の端)から自分の中心(Pivot)までの距離
- 高さ320の真ん中を下の端から160の位置に置くと、ちょうど下の端にくっつく
アンカーのボタンをAlt(Macは Option)を押しながら選ぶと、位置も一緒にそろえてくれます。ただ、そのあとHeightを変えると、中心を基準に上下へ伸びるので、下の端から離れたりはみ出したりします。数字を入れるときは、Pos Yも一緒に確認しましょう。


Stageの中を分ける
Stageの子に、スライムちゃんの場所と敵の場所を作ります。
- PlayerArea(Create Empty)
- アンカー:middle-left
- Pos X:420、Pos Y:0、Width:420、Height:480
- EnemyArea(UI (Canvas)>Image)
- アンカー:middle-right
- Pos X:-560、Pos Y:0、Width:900、Height:480
- Color:白、Alpha:90ぐらい(敵が並ぶ場所の目印。#8で敵を並べます)
アンカーを「左」「右」にしておくと、画面の横幅が変わっても、スライムちゃんは左、敵は右にくっついたままになります。
エネルギーとターン終了ボタンを仮に置く
エネルギーとターン終了ボタンは、中身を#7と#9で作ります。今回は場所だけ決めておきましょう。
- Hudの子にEnergy(UI (Canvas)>Image)
- アンカー:bottom-left、Pos X:150、Pos Y:400、Width・Height:150
- Source Image:Knob(丸い画像)、Color:#FF9A3C(オレンジ)
- Energyの子にText – TextMeshPro
- アンカー:stretch-stretch、Left・Top・Right・Bottom:0
- テキスト:「3/3」、Font Size:56、Alignment:上下左右とも中央、Color:白
- Hudの子にEndTurnButton(UI (Canvas)>Button – TextMeshPro)
- アンカー:bottom-right、Pos X:-190、Pos Y:400、Width:280、Height:100
- 子のテキスト:「ターン終了」、Font Size:44


スライムちゃんを置こう
最後に、PlayerAreaにスライムちゃんを置きます。
- PlayerAreaの子にSlime(UI (Canvas)>Image)
- アンカー:middle-center、Pos X・Pos Y:0、Width・Height:360
- Source Image:walk_61
- Preserve Aspectにチェック(絵の縦横比を保つ)
このままだと止まった絵のままなので、待機のコマを切り替えて、その場で弾むようにします。
- DeckBuilding/Scriptsに「IdleBounce」スクリプトを作る
using UnityEngine;
using UnityEngine.UI;
// 待機のコマを 1→2→3→2 の順に切り替えて、その場で弾ませる
public class IdleBounce : MonoBehaviour
{
[SerializeField] private Image image;
[SerializeField] private Sprite[] frames; // walk_61・walk_62・walk_63
[SerializeField] private float frameSeconds = 0.15f; // 1コマを出しておく時間
// 3枚を 1→2→3→2 の順に使う(2枚目を折り返しに使うと、なめらかにつながる)
private static readonly int[] frameOrder = { 0, 1, 2, 1 };
private float timer;
private int orderIndex;
private void Update()
{
timer += Time.deltaTime;
if (timer < frameSeconds)
{
return;
}
timer -= frameSeconds;
orderIndex = (orderIndex + 1) % frameOrder.Length;
image.sprite = frames[frameOrder[orderIndex]];
}
}
- frameOrder
- 3枚の絵を「何番目の順に使うか」の表。1→2→3→2→1→2→3・・・とくり返す
- 1→2→3→1だと、3から1にいきなり戻るので動きがカクッとなる。2枚目を折り返しに使うとなめらかになります
- timer
- 経過時間をためて、frameSeconds(0.15秒)をこえたら次のコマにする
timer = 0ではなくtimer -= frameSecondsにしているのは、こえた分を次に持ち越して、コマの長さをそろえるため
- %(あまり)
(orderIndex + 1) % 4は、0→1→2→3→0とくり返す。表の最後まで行ったら最初に戻る
スクリプトを付けて、Inspectorで設定します。
- IdleBounceを、HierarchyのSlimeにドラッグ&ドロップ
- Image:Slime(自分自身)をドラッグ
- Frames:ProjectウインドでSlimeフォルダのwalk_61〜walk_63を選んで、InspectorのFramesの文字の上にドラッグ&ドロップ
- walk_61・walk_62・walk_63の順に並んでいるかを確認


動かして確認!
再生して、スライムちゃんが弾めば成功です!


続けて、画面の比率を変えてもくずれないか確かめておきましょう。
- Gameビューの解像度のドロップダウンで、16:10 Aspectに切り替えてみる
- もっと極端な比率でも試したいときは、ドロップダウンの+で4:3を足す
- Label:4:3、Type:Aspect Ratio、Width & Height:4・3


#1の動作確認
- スライムちゃんが、その場でなめらかに弾んでいる
- 手札の場所が画面の下、敵の場所が右、スライムちゃんが左にある
- 16:10や4:3にしても、部品同士が重ならない
- 「3/3」「ターン終了」の日本語が表示されている



色の付いた四角が並んでいるだけで、まだ地味ですね・・・



今は場所の目印だからね。カードが並んで、敵が出てきて、エフェクトが飛ぶようになると一気に変わるよ!
あとがき
今回はプロジェクトを作って、バトル画面の場所の割り当てを決めました。まだ何も戦えませんが、スライムちゃんが画面の中で動いているだけでも、ちょっとゲームらしくなりますね。次回は、カードの名前・コスト・効果などを入れておく「カードのデータ」をScriptableObjectで作りたいと思います!
コメント
コメント一覧 (1件)
[…] 前回:#1 プロジェクトの準備とバトル画面の枠組みを作ろう […]