スマホアプリで一人回しがしたい(ついでにSwiftUIお勉強)その1
スマホで一人回しがしたい
どうも、湊です。
私はTCGでよく一人回しというのをします。*1
ただこの一人回し、自宅などカードを広げることが許されるスペースでしかできません(当然)。
どこでもやりたくない...?
スマホでやりたくない...?
と思ったのでアプリを作ることにしました。
趣味のツール作成と仕事の勉強(SwiftUI)ができて一石二鳥です。
題材
今一番練習したいのはLyceeなのでLyceeで作ってみることにします。
(盤面として表現する必要のあるものが少ないので作るのが楽そうという事情もあります。)
要件(ざっくり)
- 盤面の情報表示
- AF, DFに配置しているカード情報
- 山札、ゴミ箱、○○置き場などの情報
- 手札
- 盤面の操作
- ドロー, シャッフル, デッキを見る, デッキからカードを抜き出し手札に加える etc...
- カードを盤面に配置する, 盤面のカードを移動する, キャラクターをタップする(攻撃など) etc...
まずは情報表示の部分をやっていこうと思います。
表示内容洗い出し
プログラムを作るにはゲームの要素をコードで表現する必要があります。
対象を洗い出していきましょう。
カード
リセのカードはこんな感じ。
今回の要件ですと、とりあえず盤面にカードを表示できればよさそうなので、一旦テキストは無視して画像だけ呼び出せるようにしてみます。
オブジェクトとしてはIDを持っていてそれに対応した画像を呼び出せれば十分そうなので以下のようにしました。
struct Card { let id: CardID private let imageName: String init(id: CardID) { self.id = id self.imageName = id.prefix + String(id.number) + (id.pallarelSuffix ?? "") } } struct CardID { let prefix = "LO-" let number: Int let pallarelSuffix: String? init?(number: Int, pallarel: String? = nil) { guard number > 0 else { return nil } self.number = number if let pallarel = pallarel { self.pallarelSuffix = "-\(pallarel)" } else { self.pallarelSuffix = nil } } var string: String { return prefix + String(format: "%04d", number) + (pallarelSuffix ?? "") } }
盤面
Lyceeの盤面はこんな感じ
キャラクターなどのカードを置く枠が6つ、デッキやゴミ箱を置く枠があります。
この画像には現れていませんが、その他「横」と呼ばれる領域や「〇〇置き場」という領域が利用できます。
(実際の対戦では左の部分に置いている方が多いと思います)
キャラクターなどのカードを配置する枠 (6枠)
紹介していませんが、リセのカード種類には4種類(キャラクター、イベント、アイテム、エリア)があります。
そしてこの6枠にはキャラクター、アイテム、エリアがそれぞれ一枚ずつしか配置できません。*2
というわけでこのように表現してみました。
struct Field { var character: Card? var characterState: CharacterState = .stand var item: Card? var area: Card? enum CharacterState { case stand, rest } }
紹介していませんでしたが、キャラクターは縦向き、横向きの状態で配置されることがあり、CharacterStateはそれを表現しています。
盤面全体
上記キャラクターなどを配置する6枠に加え、デッキ、ゴミ箱、「横」、「〇〇置き場」などをプレイヤーの管理領域として表現するならこのような形でしょうか
struct PlayerDomain { var fields = Fields() var deck = Deck() var trashBox = TrashBox() var somePlaces = [Place]() var hand = Hand() } struct Fields { var leftAF = Field() var centerAF = Field() var rightAF = Field() var leftDF = Field() var centerDF = Field() var rightDF = Field() } struct Deck { var cards = [Card]() } struct TrashBox { var cards = [Card]() } struct Hand { var cards = [Card]() } struct Place { let name: String var cards = [Card]() }
粗はありそうですが、これでカードや盤面の情報を表現できました。
これをSwiftUIを用いて画面に表示していきます。
表示の実装
キャラクターを配置する枠
まずはキャラクターを枠に配置できるようにしましょう。
また、1枠にはキャラクターやアイテム、エリアが配置されうるため、複数のものを配置している場合はすべてが視認できるようにずらして配置してあげるのが良さそうですね。
こんな感じにしてみました。
struct FieldView: View { var field: Field var body: some View { GeometryReader { geometry in let fieldWidth = min(geometry.size.width, geometry.size.height) let areaOffset = CGSize(width: fieldWidth / 16, height: fieldWidth / 10) let itemOffset = CGSize(width: fieldWidth / 8, height: fieldWidth / 6) ZStack { if let area = field.area { Image(area.imageName) .resizable() .scaledToFit() .offset(field.character != nil ? areaOffset : .zero) } if let character = field.character { Image(character.imageName) .resizable() .rotationEffect(.init(degrees: field.characterState == .rest ? 270 : 0)) .scaledToFit() if let item = field.item { Image(item.imageName) .resizable() .offset(itemOffset) .scaledToFit() } } } .frame(width: fieldWidth, height: fieldWidth) } } }
プレビューするとこんな感じです。
あとはこれを複数個並べたらキャラクターなどを配置する枠は出来上がりそうです。
次回
長くなってきたので今回はここまでにします。
②以降で他のViewの構築や操作周りの実装をしていきます。
SwiftUIは不慣れでまだ探り探りですので、ご指摘・ご質問等歓迎です。
各種画像はLycee公式サイトから拝借しました。
LYCEE OVERTURE TRADING CARD GAME || リセ オーバーチュア トレーディングカードゲーム