2023年12月31日日曜日

【Unity】アスペクト比を維持して固定値を使わずに整列させる

アスペクト比を維持して固定値を使わずに整列させる方法をなんと2つも説明します。

問題設定

まずは目指す状態について説明します。

上の画像では半透明の範囲に本の画像とその白背景のセットが2つ描かれています。また白背景は正方形で、本の画像はその中に収まります。つまり、本+白背景の縦横比は1:1になります。

整列の設定を上手く施すことで、中身を増減させても自動的に間隔を調整してくれます。実際私は、以下の画像を作る時に位置や大きさを入力しませんでした。


以降の章でこの自動整列の設定方法について解説します。

なお、この記事では横に整列させる方法を解説しますが、Horizontal←→Vertical、Width←→Heigh(t)と適宜変換すれば縦にも同様に整列させられます。

共通部分

自動整列を実装する方法は複数ありますので、まずはそれらの共通部分について説明します。

上の画像は本の画像と白背景のセットが2つある場合のヒエラルキーです。

上のヒエラルキーに登場するCanvas内のGameObjectが有するComponentを以下に示します。

  • Area(親オブジェクト)
    • Horizontal Layout Group
      • 横に整列させるやつ。今回の主役
      • Control Child SizeのHeighをTrueにして、子の縦の長さを親に合わせるようにする
      • Child Force ExpandはいずれもTrueに
    • Image
      • 半透明なやつ
  • GameObject 1(子オブジェクト)
    • 今は何もつけない
  • Background(孫オブジェクト)
    • Image
      • ただの白背景なので、Spriteは何も入れない
  • Book(孫オブジェクト)
    • Image
      • 本の画像を挿す
      • 画像の比を維持するため、Preserve Aspectをtrueに

これらに追加でComponentを加えるケースもあります。

Aspect Ratio Filterを子オブジェクトにつける方法

Areaに紐づける"Horizontal Layout Group"の"Control Child Size"のHeighがTrueになっている場合(上の画像)、子のオブジェクト(GameObject1)のHeightには何も入力できない状態になります。"Control Child Size"はその名の通り、子のWidthやHeightを親が決定するかどうかのフラグであり、Trueになると子オブジェクトに対するWidth/Heightの入力を受け付けなくなります。

子オブジェクトにてHeightが入力できない状態

本と白画像のセットの縦横比は1:1にするためには、Widthの値をHeightからコピーしてくる必要があります。そこで、子オブジェクトに"Aspect Ratio Filter"を適用します。

Aspect Ratio Filterはそのオブジェクトのアスペクト比を自動で決定するためのComponentです。Aspect Modeで"Height Controls Width"を選択すると、WidthがHeightの値から自動入力されます。このように設定することで、本+白背景を複製しても同じ親にある限り上手に間隔を作ってくれます。

しかし残念なことに、子オブジェクト以下をPrefab化してスクリプト上から複製すると自動整列が機能しません。Aspect Ratio FilterとHorizontal Layout Groupが共存すると発生する問題のようで、入力をロックする箇所が異なっていても動作しないようです(実際警告が出てる)。Aspect Ratio Filterが先にWidthやHeightをロックすることで後のLayoutGroupが動作できなくなり、機能しないのではないかと睨んでいます。Prefabから生成する場合は次の方法を試してみてください。

Aspect Ratio Filterを孫オブジェクトにつける

代替案として、子オブジェクトの代わりに孫オブジェクト(Background)にAspect Ratio Filterをつけることでも自動整列が可能です。その際、Aspect Modeを"Envelope Parent"に設定しましょう。Envelope Parentモード下では、元々設定していた孫オブジェクトの大きさを子の大きさによって拡大することが可能になります。

孫オブジェクト(Background)のComponent

上の画像ではW Deltaに82.79999と入力されています。これは親オブジェクトのHeightが182.79999で、元々設定されていた子オブジェクトのWidthが100であり、Envelope Parentによって引き伸ばされた分がW Deltaに入力されます。

この方法ではPrefabから生成しても自動整列が機能します。Aspect Ratio FilterとHorizontal Layout Groupが同じ箇所をロックすることもないので、警告も発生しません

余談

自動整列問題がややこしいのは、WidthやHeightを画面の大きさに応じて変化させようとするからです。WidthやHeightに固定値を入力させられれば、Layout Elementなどを利用してもっとわかりやすく自動整列を実装できます。まずは画面のサイズを最初に決めて、WidthやHeightに固定値を入力させるのが良いと思います(一敗)。

まとめ

  • XXX Layout Groupを親に設定して整列させる
  • Aspect Ratio Filterを子か孫に設定してアスペクトを維持させる
  • 画面のサイズは最初に決めよう


以上です。

【ゲーム製作】日◯簿記の問題集で家を確保するゲーム

日◯簿記の問題集で家を確保するゲームを公開しました!

URL: https://unityroom.com/games/boki-warashibe

よかったら遊んでください。(偉い人に怒られたら消します)

2023年7月2日日曜日

【プログラミング】ヘクス座標系でのマスの管理方法

 
こんにちは、あすらと(@__asurato__)です。

モダンなゲームではあまり見かけなくなりましたが、大戦略やCivilizationのような戦略シミュレーションではヘクス座標系が採用されています。ヘクス座標系では1つのマスが正六角形になっており、正六角形の各マスは隙間なくマップに敷き詰められます。本日はヘクス座標系のマスの管理方法について考察しましたのでご覧ください。



ヘクスマップの例

前提


本記事では以下を前提としています。これらの前提が容易・高速に達成できるアルゴリズムが良い手法であるとここでは定義します。
  • Tileクラスが各マスの情報を表す
  • Tile型のインスタンスは配列や二次元配列に格納される
  • マスが指定されたらそのマスに関するTile型のインスタンスを取得したい
  • あるマスに隣接しているマスも取得したい

例えば tiles[id] 等でそのidに関するTile型のインスタンスを取得することができれば前の3つについて達成されます。言い方を変えると、関数f: id→Tile となる関数が作れるならOKです。

またマスXに隣接したマスYを取得できれば最後の項目は達成されます。前3つの項目によりidからTileへの変換は前提とするため、XのidからYのidが取得できれば達成されます。


以下からマスの管理方法について章立てで説明します。


並んでる方向にIDを振る手法(一次元)


すごく自然なIDの振り方で、右上のマスは+3、右下は+4と隣接マスも計算しやすいです。ただし以下のような上下で対称でないマップの場合、右上のマスはIDが+4 or +5され固定値になりません。

上下が非対称なヘクスマップ

このような場合はIDを振る方向を横に変えるとなんと解決します。


すごくない?
  • 特徴
    • 隣接マスのIDの取得が簡単
    • 自然な発想
    • メモリの無駄がない
    • マップが長方形の場合のみ使用可能

二次元直交座標に変換する手法


一次元的にIDを与えるのではなく、二次元座標を用いて各マスを二次元配列に格納することもできます。座標[a, b]のマスはtiles[a][b]として取得します。

問題点として、tiles[0][0]とは異なりtiles[0][1]には値が存在しません。tiles[a][b]が存在するかを常にチェックする必要があります。

  • 特徴
    • 隣接マスのIDの取得が簡単
      • 例)a→a+1、b→b+1で右下のマスを取得できる
    • 全てのマップ系で使用可能
    • 座標にマイナスの値が含まれるときは別途対処の必要あり
    • 配列の戻り値がTileとNullの2種類があり、Nullチェックが必要
    • 二次元配列のうち半分が死にマスとなり、メモリの無駄遣い♠️


二次元直交座標+Map法


上の手法の派生系で、二次元座標を文字列に変換してMapに格納する手法です。ちなみにPythonのようにタプルをキーにできる言語や、座標を示す構造体をMapのキーに自前で設定する場合は文字列への変換は不要です。メリットはメモリの無駄がないこと、デメリットは座標とMapのキーとの相互変換が面倒な点でしょう。

  • 特徴
    • 隣接マスのIDの取得には座標とMapのキーとの相互変換が必要
    • メモリの無駄がない
    • 座標がマイナスを含む場合も対応できる
    • 全てのマップ系で使用可能


二次元座標+配列法


私が考えたイケイケアルゴリズムです。

まず上図のように軸を設定し座標を計算します。B軸の位置について、Aの要素が0以上になるように設定しましょう。

次に2つの配列を用意します。1つ目の配列には全てのマスが格納され、もう1つの配列には前者の配列のキー(ID)が入ります。


まず要素Aが小さい順に、要素Aが同値の場合は要素Bが小さい順にマスを配列1へ格納します。その格納の際に、要素B=0となるマスの(配列1における)IDを配列2に格納します。2つの配列を用いて、座標(a, b)のマスは arr1[arr2[a] + b] として取得できます。


  • 特徴
    • 隣接マスのIDの取得が簡単
      • 例)a→a-1、b→b+1で右下のマスを取得できる
    • ほとんどのマップ系で使用可能
      • ただし間が空くマップ系だと「二次元直交座標に変換する手法」と同様にNullチェックが必要になる
    • メモリの無駄がない
    • 軸が斜めになっているので直感的ではない
      • でも頑張れば直交座標でも使えそう

まとめ

  • マップが長方形ならIDを縦or横方向に振る方法でよい
    • 縦か横かはその時のマップの性質によって見極める必要がある
  • 二次元直交座標+Map法は任意のマップで使えるので、最終手段として検討しよう
  • 二次元座標+配列法はコードが少し難しくなるが色々効率的

参考サイト

http://higesusk.blog.fc2.com/blog-entry-146.html
https://ninagreen.hatenablog.com/entry/2015/11/14/193417
https://sinsei.space/blog/11747
https://www.redblobgames.com/grids/hexagons/

2023年6月28日水曜日

【Blogger】ラベルを階層化する


階層構造に、改装!w こんにちは、あすらとです。

Bloggerでは記事に対してラベルを付与できますが、このラベルの機能はあまりリッチではありません。そこで、当BlogではBloggerのブログアーカイブのHTML構造を参考にし、比較的自然なラベルの階層化機能を自前実装しました。本日はこのラベル階層化の手順について解説します。

先に完成後のスクリーンショットを以下に載せます(まあPCなら横に表示されているとは思いますが念の為)。



ご自身のブログに同様の機能を実装したいと感じた方は是非最後までご覧ください。



まずBloggerはラベルの階層化機能を提供しておりませんので、HTMLを自分で編集する必要があります。「テーマ」→「カスタマイズの横の下三角」→「HTMLを編集」とクリックしHTMLの編集画面を開きます。


続いて、編集画面で「id='Label1'」で検索し、ラベルの機能を探します。(検索にない場合は元の画面に戻り、「レイアウト」からラベルガジェットを追加してください。)おそらく以下のように表示されていると思います。



最後に<b:widget...から次の</b:widget>まで以下のコードに差し替えて終了です。


  1. <b:widget id='Label1' locked='false' title='カテゴリ別' type='Label' version='1'>
  2. <b:widget-settings>
  3. <b:widget-setting name='sorting'>ALPHA</b:widget-setting>
  4. <b:widget-setting name='display'>LIST</b:widget-setting>
  5. <b:widget-setting name='selectedLabelsList'/>
  6. <b:widget-setting name='showType'>ALL</b:widget-setting>
  7. <b:widget-setting name='showFreqNumbers'>false</b:widget-setting>
  8. </b:widget-settings>
  9. <b:includable id='main'>
  10. <b:if cond='data:title != &quot;&quot;'>
  11. <h2><data:title/></h2>
  12. </b:if>
  13. <div expr:class='&quot;widget-content &quot; + data:display + &quot;-label-widget-content&quot;'>
  14. <ul class='hierarchy' id='rootNode'>
  15. <b:loop values='data:labels' var='label'>
  16. <li class='labelNode archivedate collapsed' expr:name='data:label.name'>
  17. <b:include name='myToggle'/>
  18. <a class='post-count-link' expr:href='data:label.url'>
  19. <data:label.name/>
  20. </a>
  21. <span class='post-count' dir='ltr'>(<data:label.count/>)</span>
  22. <ul class='hierarchy' style='padding: 10px 0 0 10px'/>
  23. </li>
  24. </b:loop>
  25. </ul>
  26. <ul class='hierarchy' id='templateElement' style='display:none;'>
  27. <li class='labelNode archivedate collapsed'>
  28. <b:include name='myToggle'/>
  29. <a class='post-count-link' href='javascript:void(0)'>LABEL_NAME</a>
  30. <ul class='hierarchy'/>
  31. </li>
  32. </ul>
  33. <b:include name='quickedit'/>
  34. </div>
  35. <style type='text/css'>
  36. li.collapsed &gt; ul.hierarchy {
  37. display: none;
  38. }
  39. </style>
  40. <script type='text/javascript'>
  41. // 深さ優先探索でHTMLタグを形成する関数
  42. function dfs(ul, obj) {
  43. for (const label of Object.keys(obj)) {
  44. let {node, children} = obj[label]
  45.  
  46. if (!node) {
  47. const template = document.querySelector(&#39;#templateElement&#39;).innerHTML.replace(&#39;LABEL_NAME&#39;, label)
  48. node = new DOMParser().parseFromString(template, &#39;text/xml&#39;)
  49. }
  50. node.firstElementChild.addEventListener(&#39;click&#39;, () =&gt; onToggled(node.firstElementChild))
  51. console.log(ul)
  52. console.log(node)
  53. ul.appendChild(node)
  54.  
  55. if (Object.keys(children).length &gt; 0) {
  56. dfs(node.lastElementChild, children)
  57. continue
  58. }
  59.  
  60. node.removeChild(node.lastElementChild)
  61. node.removeChild(node.firstElementChild)
  62. }
  63. }
  64. // トグルがクリックされた時の関数
  65. function onToggled(element) {
  66. element.parentElement.classList.toggle(&#39;collapsed&#39;)
  67. element.parentElement.classList.toggle(&#39;expanded&#39;)
  68. element.firstElementChild.classList.toggle(&#39;toggle-open&#39;)
  69. if (element.parentElement.classList.contains(&#39;expanded&#39;)) {
  70. element.firstElementChild.innerHTML = &#39;&#9660;&#160;&#39;
  71. } else { // 縮小する
  72. element.firstElementChild.innerHTML = &#39;&#9658;&#160;&#39;
  73. }
  74. }
  75. function main() {
  76. const treeObj = {} // 木構造を有するデータ
  77. document.querySelectorAll(&#39;li.labelNode[name]&#39;).forEach(node =&gt; {
  78. node.parentElement.removeChild(node) // 一旦DOMから消えてもらう
  79.  
  80. // ラベルの名前をツリー状になるように登録する
  81. let obj = treeObj
  82. node.getAttribute(&#39;name&#39;).split(&#39;_&#39;).forEach((label, i, a) =&gt; {
  83. if (label in obj === false) {
  84. obj[label] = {&#39;children&#39;: {}, &#39;node&#39;: null}
  85. }
  86. if (i === a.length - 1) {
  87. obj[label][&#39;node&#39;] = node
  88. } else {
  89. obj = obj[label][&#39;children&#39;]
  90. }
  91. })
  92. })
  93. dfs(document.querySelector(&#39;#rootNode&#39;), treeObj)
  94. }
  95. main()
  96. </script>
  97. </b:includable>
  98. <b:includable id='myToggle' var='interval'>
  99. <a class='myToggle' href='javascript:void(0)'>
  100. <span class='zippy'>
  101. &#9658;&#160;
  102. </span>
  103. </a>
  104. </b:includable>
  105. </b:widget>

保存は忘れずに!お疲れ様でした!

2023年5月6日土曜日

【Rust】IteratorとVecの関数が多すぎ?


RustはC言語並みの実行速度、モダンな言語特有の安全性、堅牢な型システムを有するプログラミング言語です。

Stack Overflowの調査ではRustは最も愛される言語に選ばれましたが、Rust固有の概念や型システムは難解で言語の性能に比べて愛用者は少ない印象です。「好きな言語?まぁ、Rustかな。」と言った日には圧倒的玄人感で周囲を威圧させ、その眩き威光には流石の水戸黄門も平伏すしかありません。

そんな厨二病患者へのサポートも欠かさない言語、Rust。私も最近勉強を始めました。しかしサクッと競プロも問題を数問解いてみたところ、このような感想を持ちました。



ということで検証しました。


Rust


Rustの関数の個数は以下でした。(手作業のため誤差があるかもしれません)
  • Iterator: 75個
  • Vec: 50個
  • Array: 12個

やっぱりVecとIteratorの数が多いですね。これはRustがオーバーロードを実装していない点が原因の一つです。reduce関数fold関数は顕著な例で、初期値の有無のみで2つの関数に分かれています。


続いてRust以外の言語の関数の数について紹介します。


JavaScript


  • Array: 45個

(そうだよ!これだよこれ!)

JavaScriptのArrayはVecとIteratorの役割を同時に果たすことも相俟って、関数の数は少ないです。まあブラウザで動かし続けないといけないからね。そりゃ慎重にもなる。


Java


  • Stream: 33個(オーバーロードの関数名重複ありで39個)
  • List: 22個(オーバーロードの関数名重複ありで28個)
  • Arrays: 17個(オーバーロードの関数名重複ありで155個)


155個!?

これは私の推測ですが、Arraysクラスの関数の多さはJavaが互換性を残しているためだと考えられます。binarySearch関数は全部で18個存在し、T[]のようなジェネリクス以外にint[]やdouble[]を引数として用意する徹底ぶり。

しかし、比較的新しいStreamクラスについてはRustに比して関数は少なく、Listクラスについても同様です。


C#


  • LINQ(IEnumerator): 66個(オーバーロードの関数名重複ありで213個)
  • List: 32個(オーバーロードの関数名重複ありで48個)
  • Array: 32個(オーバーロードの関数名重複ありで94個)


さて私の愛するC#ですが、意外と関数が多い。競プロで使うくらいLINQ好きなんですけどね...

なんか雲行きが怪しくなりました。


Scala


さて、Pythonは組み込み関数だから比較は難しいし、あと触ったことがある言語は......Scalaかぁ。




  • Collection: 150個




結論



疑ってすまないRustくん。君も多い方なんだろうけど......

あと今日からScala叩きに転身します。


P.S. Pythonのリストの関数は11個でした。



終わり

2023年2月11日土曜日

【Civ6GS】難易度神の倒し方、知ってますよ

このタイトルだと倒せなさそう(小並感)

久々の投稿となります、あすらと(@__asurato__)と申します。本日はCiv6での難易度神の倒し方について説明します。特にまだ難易度神で勝利したことがない方は参考になると思います。目指せ†ゴッドスレイヤー†


私の実績

私が勝利した時の指導者と勝利方法は以下になります。

  • ジョン・カーティン(オーストラリア)→外交勝利
  • トラヤヌス(ローマ)→科学勝利
  • ヴィクトリア(イギリス)→科学勝利
  • ペリクレス(ギリシャ)→文化勝利
  • トミュリス(スキタイ)→制覇勝利
  • 赤髭王フリードリヒ1世(ドイツ)→科学勝利

プレイ環境については、ゲームスピードは速い、マップは小、地形はイギリスのみ大陸で他はパンゲアを選択しました。

科学勝利が多いですが、ローマやイギリスは頑張れば別の方法でも勝利できたと思います。スキタイについてはわざと宇宙開発プロジェクトを止めてようやく制覇勝利を達成しました。そのため必然的に科学勝利に寄った解説になってしまっていると思います(各勝利条件ごとの最適行動とか分からん)。以降で神の攻略法について章ごとに説明します。


仮想敵国を設定する

Civilizationシリーズは戦略シミュレーションゲームであり、文字通り戦略が重要なゲームです。Civにおいては自分の文明を勝利に持ち込むための方針が戦略にあたります。この方針がグラグラだと、意味の無い戦争をしたり、逆に戦争をせずにジリ貧で負けたりします。戦略を明確にするために「戦争するならこいつ!」のように仮想敵国を作り、その後で「こいつとは戦争したくない!」という文明と同盟を組むことをお勧めします。

仮想敵国の設定の観点について以下にまとめました。

  • 自分と相手の都市の立地
  • 自分と相手の指導者・文明特性
  • 自分の文明の技術的優勢
  • 自分が有する都市数や科学力・文化力

敵となる文明を決める上で最も重要なのが立地です。自分の都市の近くに出せるかどうか、忠誠心を維持できるかどうか、国境線が山脈で守られているかどうかを確認しましょう。例えば山脈で自国の都市への入り口が部分的に遮られていると、防衛の際に敵のユニットを各個撃破できる可能性が上昇します。逆に攻勢には手間取ってしまうかもしれません。この場合はお互いに戦争をするのが不利なので、積極的に同盟を組みたい文明だと言えるでしょう。

指導者や文明の特性を見極めるのも重要です。自分の指導者・文明が戦争に向くかどうか、他の文明が戦争が得意かどうかを確認しましょう。ただ個人的には指導者や文明の特性よりも立地の方が戦争を決定づける要因としては強いと思います。

立地や指導者特性を考慮しても敵となる文明を絞り込めないなら、より技術力が低い文明を敵とするべきです。逆に相手の方が技術力が大幅に高いなら、戦争は諦めましょう。また相手の方がちょっと高い程度であれば小手先のテクニックでカバーして領土拡張に努めましょう。むしろ十二分に都市数を繰り出せないことを恐れるべきです。

確か偉い人もこんな言葉を残していた気がしないこともありません。 


「戦争を恐れるな、ジリ貧を恐れろ。」ーーーーーアスラト・フォン・ミネストローネ 

 

また自分が十分な都市数を持っており高い科学力や文化力を出力するなら戦争をする必要がありません。従って仮想敵国を設定する必要もありません。ただ難易度が神にもなるとこのようなケースは稀です。特に非戦で科学勝利を狙うのは難しいので積極的に戦争しましょう。


戦争を仕掛けるタイミングを考える

戦争にベストなタイミングは相手よりも自分の方が有利なユニットを使用できる時です。例えば弩兵を量産して戦争を仕掛けた時に、相手が弩兵を研究し終えていなかったら、一方的に蹂躙できます。以下のユニットは早めに研究・量産することをお勧めします。

  • 弓兵
  • 弩兵
  • 野戦砲
  • 戦車
  • 爆撃機
  • 自文明の固有ユニット(強いなら)

敵とする文明が他の文明と戦争をしている場合があります。その際はその文明に乗っかり、敵文明に二正面作戦を強いると良いです。自国が戦争を準備している最中に和平されないよう、開戦から8ターン以内に自文明も攻撃を開始するのが望ましいです。

注意点として敵の固有ユニットの時期が攻撃のタイミングと重なる場合は他の時代より苦戦する可能性があります。またアップグレードにより新しいユニットを調達する場合はアップグレードに必要な金額が半額になる政策をスロットに入れましょう。ただし投石兵→弓兵と弓兵→弩兵の場合はこの政策の取得が間に合わない可能性が高いです。

ハイキング中に出会ったCiv6プレイヤーも確かこのように言っていたように思われます。


「大量生産した弓兵は捨て駒にし新たに弩兵を生産するのもまた一考に値する。」ーーーーーアスラト・フォン・カルボナーラ


都市国家を喰らう

都市国家に囲まれた立地の場合、都市国家が領土拡張の邪魔になる場合があります。基本的には容赦無く侵略して構わないのですが、条件があります。

  • 科学重視・文化重視の都市国家ではない
    • 仮に宗主国になれなくても科学力・文化力のボーナスが美味しいから
  • その都市国家の宗主国が以下のいずれかであること
    • 宗主国がいない
    • 宗主国とは友好宣言等により戦争にならない
    • 宗主国と戦争してもよい
  • 宗主国ボーナスが強くない
    • 強いなら宗主国を目指すべき

都市国家への侵攻のタイミングは弩兵(中世)〜複葉機(近代)の間が良いと思います。この時期には相手の文明の都市に防壁が建設されており、攻囲ユニットが必要になります。しかし攻囲ユニットは防壁の長距離攻撃の的になり、侵攻に苦戦する場合があります。この問題は航空技術の解禁による攻囲ユニットの射程を伸ばす支援ユニットの導入により解決できます。でもこの時期に全く領土拡張しないのは勿体無い......ということで、他文明の都市と比べ都市の防御力が低い都市国家に侵略することで軍事力を文明の成長に繋げられます。


戦力を多めに作る

難易度神に挑戦した人なら突然宣戦布告を受けた経験があると思います。その後、弓兵の大量生産で戦争を凌ぎきってしまった人も少なくないと思います。このような経験から、過去の私のように必要最低限の軍事ユニットしか生産せず、毎回のように宣戦布告を受ける人がこの記事の読者の中にもいるかもしれません。

知っておくべきなのはCPUは真の軍事力ではなく、数値としての軍事力を参照し宣戦の布告を判断することです。中身が弓兵なのか戦士なのかは軍事力としては(おそらく)区別されません。そのため攻撃しても反撃されない長距離ユニットを保有するほど、真の軍事力と数値上の軍事力は乖離します。CPUは自他の数値上の軍事力を参照し、数値上優勢であると判断すると宣戦布告に乗り出します。

数値上の軍事力は赤枠の箇所からわかる

この問題への対策は単純で、大量のユニットをあらかじめ生産しておくことです。たくさん生産すればその分数値上の軍事力が上昇し、戦争の確率を減らせます。軍事力の目安は周辺の文明と同程度、最低でも3分の2は確保するようにしましょう。体感になりますが、相手文明の軍事力が自文明の倍以上になると宣戦布告を受ける確率が高くなる気がします。職人技(文化ツリーの最初の分岐を上に進んで得る政策)を使ってユニットを効率よく生産しましょう。また軍事力増強の際に傭兵のひらめき(陸上ユニット数が8以上が条件)を確保しておきましょう。


都市は少し疎に

「都市数を多く出せ」という主張がCiv6の攻略記事によく見られます。私はこれに賛成です。しかし「都市数を多く出すために都市を密に作れ」という立場には賛成しません。科学勝利には都市の生産力が高いほど有利になるため、生産力が強い場所を自分の都市同士が取り合う状況は好ましくありません。文化勝利の場合は劇場区域をたくさん置くために密の方がいいのかもしれませんが......

私は5マスを都市の間隔の目安にしています。都市間が5マスなら市民の配置箇所の受け渡しが簡単で、区域を建設するスペースも十分あります。また沿岸部などで区域を建設するスペースが少ない場合は6マスくらい空けることもあります。好みの問題かもしれませんが、迷っている方は参考にしてみてください。


商業ハブは弱い

私が商業ハブが弱いと考える理由は以下の3点です。

  • 自分で稼ぐよりCPUが生み出すお金から徴収する方が稼げる
  • 灯台や造船所と政策による生産力の増加が強い
  • お金を稼ぐという点で交易商が強くなるタイミングが遅い

難易度が神になるとCPU文明が出力する生産力やゴールドが倍になるため、高級資源や戦略資源を売って稼ぐ方が手っ取り早い場合があります。中世あたりからでも高級資源は1つ11ゴールド(30ターン)で取引される時もあり、ゲーム後半の交易商の半分ぐらいの出力を期待できます。また馬や鉄などの余らせがちな戦略資源は、特に同資源を相手が保有していない時に高値で買い取ってくれます。ゲーム後半であればスパイを使ってゴールドを奪取する方法も有効です。お金を自力で調達するのではなく領土を広げ新たに得た資源を輸出することで、CPUの出力ボーナスでさえも自分の文明に利用できます。

交易商のために市場(商業ハブの建造物)を建設する人も多いかと思われますが、交易商は灯台(港の建造物)を建設することでも上限を解放できます。港はお金を稼ぐ以外の能力も優秀で、灯台は交易商と住宅の増加が、造船所(港の建造物)は都市の生産力の向上が可能です。またベテランという政策では港と兵営+その建造物に対する生産力を向上、統合宇宙機構という政策では士官学校(兵営の建造物)や港湾(港の建造物)がある都市での宇宙開発競争プロジェクトへの生産力を向上でき、これらも非常に強力です。港の建設のデメリットはそもそも沿岸部への都市出し自体が弱い点ですが、湖に港を建設する場合は港の強さをフルに享受できます。ちなみに私は湖から真水を引く場合は必ず港を建設しています。商業ハブの代わりに港を建てることを検討してみてください。

高名なインフルエンサーもこう言っていたとか言わなかったとか。


「片思いとは商業ハブと交易商の関係と同じである。つまり商業ハブに交易商は必要不可欠だが、交易商は商業ハブを必要とはしないのだ。」ーーーーーアスラト・フォン・フォッカチオ


また交易商はゲームの前半と後半で使い方が異なります。ゲームの前半では開拓したばかりの都市から出発させ、食糧や生産力の都市への提供と道路の敷設が一般的な目的だと思います。ゲーム後半でこそ大量のゴールドを交易商から得られますが、ゲーム前半では微々たるもので、お金を稼ぐ目的なら序盤に交易商を作ってもあまり機能しません。一方でゲーム終盤ではスパイを利用することで交易商以上にゴールドを生産することも可能です。交易商が効果的に機能する場面は意外と少ないのではないかと考えてます。

以上までで商業ハブをディスってきましたが、全く不要なわけでもありません。都心を川と海の両方に隣接させ港と商業ハブと都心を三角形状に建設することで、港と商業ハブの隣接ボーナスは合計+8も得られます。それに加え総督レイナの能力で港と商業ハブの隣接ボーナスを2倍にさせたり、研究の自由という黄金時代の公約により港と商業ハブの隣接ボーナスを研究力に変換させたりできます。そもそも近くに海も湖もない場合は港を建設できませんから、あくまで商業ハブを建てる優先度が低くなるだけだと認識してください。意見が分かれそうですが、私は優先度について「キャンパス>>>劇場広場>商業ハブ」くらいの気持ちで区域を建設しています。


マルチプレイの戦い方とは違う

相手がCPU(難易度神)か人かでは理想とする動き方がかなり異なります。例えばマルチプレイではコロッセオという6タイル以内の都市の快適性を向上する遺産は奪い合いになるほど人気ですが、難易度神の攻略ではCPUから安価で高級資源を調達できるため重要視される遺産ではありません。他にもマルチプレイでは戦争を前提としていたり中世での黄金時代が必須とされていたりしますが、必ずしもこれらを守る必要はありません。マルチプレイの実況動画を難易度神の攻略の参考とする場合は気をつけましょう。

......と得意気に語ってきましたが、一緒にCivをやる友達なんて存在するはずがないので当然マルチプレイ未経験です。あくまで参考までに。


その他

  • 最前線都市は防壁優先
  • 真水を得られるなら高級資源の上に都市を出すことも考える
    • 高級資源の上に都市を敷いてもその高級資源は獲得される
    • 灌漑が必要な高級資源は灌漑の研究が遅い分有利
    • 特に砂糖は強い
  • 蛮族は早めに潰す
  • 文化勝利しそうな文明から傑作を買いまくる&スパイで盗む
  • ルール地方は科学勝利にすごく役に立つ
    • ルール地方+マグナスの垂直統合で生産力200とかにできる


まとめ

全体をまとめると、正しく戦争し領土を拡張することが重要で、そのために方針を定め戦力を確保しようというお話でした。人により好みが分かれる箇所もあるかと思いますが、ぜひ参考にしてみてください。最後はあの方の言葉で締めようと思います。








「すき焼き食べたい」ーーーーーアスラト・フォン・マルゲリータ


終わり