太平洋戦線で静的なマップ。近い都市から都市の移動で航空機であれば1ターンで移動、攻撃、移動。戦車、歩兵、戦艦、だとそれぞれ移動。移動中にも地形により部隊数の上下がある。
また、都市攻撃では部隊数の減少、経験値の上昇などを自動計算し勝敗をつける。都市占領は部隊の数が0になれば占領完了。資源(ボーキサイト等)は都市毎に保有しており占領により使えるようにして武器製造可能になる。1ターンは1ヶ月。
上記の条件で簡単な操作の趣味レーションゲームをrailsとjavascriptで作成してみよう。
🔹 ゲームの基本構成
1. ゲームの流れ
1. ターン開始(1ターン=1ヶ月)
• 各プレイヤー(国)がユニットを移動
• 航空機は「移動 → 攻撃 → 移動」が可能
• 地形の影響で部隊の損耗が発生
2. 戦闘処理
• 都市攻撃では「部隊の減少」「経験値上昇」「勝敗判定」
• 敵部隊が0になれば都市占領(資源獲得)
3. 資源と生産
• 都市の資源を使用して武器・ユニットを生産
4. ターン終了 → 次ターンへ
🔹 開発技術
• Rails(バックエンド)
• ユーザー認証(プレイヤー管理)
• ユニット・都市・資源のデータ管理(ActiveRecord)
• ゲームロジック処理(戦闘、占領、ターン進行)
• APIエンドポイント(JSONでフロントにデータ提供)
• JavaScript(フロントエンド)
• 地図の表示(Canvas + クリックイベント)
• ユニットの移動・攻撃処理
• 都市の占領、資源管理、UI制御
🔹 データモデル設計
# 都市情報
class City < ApplicationRecord
has_many :units
belongs_to :owner, class_name: 'Player', optional: true
has_many :resources
end
# ユニット情報(戦艦・戦車・航空機・歩兵)
class Unit < ApplicationRecord
belongs_to :player
belongs_to :city
enum unit_type: { infantry: 0, tank: 1, battleship: 2, aircraft: 3 }
attr_accessor :movement_points, :attack_power, :defense_power, :experience
end
# プレイヤー
class Player < ApplicationRecord
has_many :units
has_many :cities
has_many :resources
end
# 資源情報(ボーキサイト・鉄など)
class Resource < ApplicationRecord
belongs_to :city
belongs_to :player, optional: true
end
🔹 Rails APIエンドポイント
都市情報の取得
# app/controllers/cities_controller.rb
class CitiesController < ApplicationController
def index
cities = City.includes(:resources, :owner).all
render json: cities.as_json(include: [:resources, :owner])
end
end
エンドポイント: GET /cities
ユニットの移動
# app/controllers/units_controller.rb
class UnitsController < ApplicationController
def move
unit = Unit.find(params[:id])
destination = City.find(params[:city_id])
if unit.movement_points > 0
unit.city = destination
unit.movement_points -= 1
unit.save
render json: { status: "moved", unit: unit }
else
render json: { status: "no movement points" }, status: 400
end
end
end
エンドポイント: POST /units/:id/move
🔹 JavaScriptフロントエンド(マップ表示)
document.addEventListener("DOMContentLoaded", function () {
fetch('/cities')
.then(response => response.json())
.then(cities => {
const mapCanvas = document.getElementById('mapCanvas');
const ctx = mapCanvas.getContext('2d');
cities.forEach(city => {
ctx.fillStyle = city.owner ? "red" : "blue"; // 所有者で色分け
ctx.fillRect(city.x * 10, city.y * 10, 20, 20);
// クリックイベントで移動
mapCanvas.addEventListener('click', (event) => {
const clickX = event.offsetX;
const clickY = event.offsetY;
if (clickX >= city.x * 10 && clickX <= city.x * 10 + 20 &&
clickY >= city.y * 10 && clickY <= city.y * 10 + 20) {
moveUnitToCity(city.id);
}
});
});
});
function moveUnitToCity(cityId) {
fetch(`/units/1/move`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ city_id: cityId })
}).then(response => response.json())
.then(data => console.log("Unit moved:", data));
}
});🔹 戦闘処理
戦闘計算(Rails側)
class BattlesController < ApplicationController
def attack
attacker = Unit.find(params[:attacker_id])
defender = Unit.find(params[:defender_id])
# 攻撃力、経験値によるダメージ計算
damage = (attacker.attack_power * (1 + attacker.experience * 0.1)) - defender.defense_power
damage = 1 if damage < 1 # 最小1ダメージ
defender.health -= damage
attacker.experience += 1 if defender.health <= 0
# 戦闘結果の更新
if defender.health <= 0
defender.destroy
message = "Defender defeated!"
else
defender.save
message = "Attack successful!"
end
render json: { status: message, attacker: attacker, defender: defender }
end
end
エンドポイント: POST /battles/attack
🔹 データモデル設計(カラム構成)
1️⃣ プレイヤー(players)
プレイヤーの基本情報を管理します。
| この肉絶対美味い^_^ |
| エコフローwave2 ポータブルエアコンには必須です |
| さぁ!車中泊の世界に |
| タイトル | 更新日 |
|---|---|
| この肉絶対美味い^_^ | 2025-07-30 |
| エコフローwave2 ポータブルエアコンには必須です | 2025-06-22 |
| さぁ!車中泊の世界に | 2025-06-17 |
| 何気ない一枚 | 2025-02-20 |
| 雪の帰り道 | 2025-02-09 |
| 冬の天気 | 2025-02-06 |
| タイトル | 更新日 |
|---|---|
| HEROKUからのデータのCSVファイルダウンロード | 2025-03-25 |
| railsで戦争ゲーム作成 | 2025-03-02 |
| rails8 heroku での migrateについて | 2025-02-19 |
| タイトル | 更新日 |
|---|---|
| 最高のバックスタイル | 2025-02-20 |
| うぉぉ!きもぴー | 2025-02-17 |
| X350と | 2025-02-17 |
| 明日のツーリング日記 #X350 | 2025-02-08 |