commit e4a0f60f3c028df930b02120344213215c6787fb Author: Gennaro-Gattuso <22e1372@andrew.ac.jp> Date: Tue Dec 9 12:52:41 2025 +0900 "/" にファイルをアップロード diff --git a/formation_positions.dart b/formation_positions.dart new file mode 100644 index 0000000..e640712 --- /dev/null +++ b/formation_positions.dart @@ -0,0 +1,101 @@ +// フォーメーションごとの座標配置と役割キー(posは最終役割を示唆するが、実際の役割は生成ロジックで上書きされます) +final Map>>> formationLayouts = { + // 4-1-2-3 (例): 上からFW, 攻撃MF行, 守備MF行, DF行, GK行 + '4-1-2-3': [ + // FW (3) + [ + {'x': 0.25, 'y': 0.08}, // LW + {'x': 0.5, 'y': 0.06}, // CF + {'x': 0.75, 'y': 0.08}, // RW + ], + // 攻撃的MF(2) + [ + {'x': 0.4, 'y': 0.25}, + {'x': 0.6, 'y': 0.25}, + ], + // 守備的MF(1) + [ + {'x': 0.5, 'y': 0.4}, + ], + // DF (4) + [ + {'x': 0.15, 'y': 0.58}, + {'x': 0.35, 'y': 0.55}, + {'x': 0.65, 'y': 0.55}, + {'x': 0.85, 'y': 0.58}, + ], + // GK + [ + {'x': 0.5, 'y': 0.86}, + ], + ], + + // 4-3-3(例) + '4-3-3': [ + [ + {'x': 0.2, 'y': 0.08}, + {'x': 0.5, 'y': 0.06}, + {'x': 0.8, 'y': 0.08}, + ], + [ + {'x': 0.3, 'y': 0.26}, + {'x': 0.5, 'y': 0.24}, + {'x': 0.7, 'y': 0.26}, + ], + [ + {'x': 0.2, 'y': 0.52}, + {'x': 0.4, 'y': 0.5}, + {'x': 0.6, 'y': 0.5}, + {'x': 0.8, 'y': 0.52}, + ], + [ + {'x': 0.5, 'y': 0.86}, + ], + ], + + // 4-4-2 + '4-4-2': [ + [ + {'x': 0.35, 'y': 0.08}, + {'x': 0.65, 'y': 0.08}, + ], + [ + {'x': 0.2, 'y': 0.26}, + {'x': 0.4, 'y': 0.26}, + {'x': 0.6, 'y': 0.26}, + {'x': 0.8, 'y': 0.26}, + ], + [ + {'x': 0.15, 'y': 0.52}, + {'x': 0.35, 'y': 0.5}, + {'x': 0.65, 'y': 0.5}, + {'x': 0.85, 'y': 0.52}, + ], + [ + {'x': 0.5, 'y': 0.86}, + ], + ], + + // 5-3-2 + '5-3-2': [ + [ + {'x': 0.4, 'y': 0.08}, + {'x': 0.6, 'y': 0.08}, + ], + [ + {'x': 0.3, 'y': 0.26}, + {'x': 0.5, 'y': 0.24}, + {'x': 0.7, 'y': 0.26}, + ], + [ + {'x': 0.12, 'y': 0.52}, + {'x': 0.32, 'y': 0.5}, + {'x': 0.5, 'y': 0.48}, + {'x': 0.68, 'y': 0.5}, + {'x': 0.88, 'y': 0.52}, + ], + [ + {'x': 0.5, 'y': 0.86}, + ], + ], +}; \ No newline at end of file diff --git a/home_screen.dart b/home_screen.dart new file mode 100644 index 0000000..ceae2cb --- /dev/null +++ b/home_screen.dart @@ -0,0 +1,232 @@ +import 'package:flutter/material.dart'; +import '../services/formation_generator.dart'; +import '../data/formation_positions.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + Map? _result; + List> _visiblePlayers = []; + bool _isAnimating = false; + + // 低確率スタイルモード(デコイラン / ナンバー10 などの確率を下げる) + bool _lowProbabilityMode = false; + + Future _generateFormation() async { + setState(() { + _result = null; + _visiblePlayers = []; + _isAnimating = true; + }); + + // ★ 低確率モードを渡してフォメ+スカッド生成 + final newFormation = FormationGenerator.generate( + lowProbMode: _lowProbabilityMode, + ); + + setState(() { + _result = newFormation; + }); + + final players = newFormation['positions'] as List; + + // 1枚ずつアニメーション的に追加 + for (int i = 0; i < players.length; i++) { + await Future.delayed(const Duration(milliseconds: 150)); + setState(() { + _visiblePlayers.add(Map.from(players[i])); + }); + } + + setState(() { + _isAnimating = false; + }); + } + + // ポジションで色分け(表示はしないが色には使う) + Color _getColor(String pos) { + if (pos == 'GK') return Colors.orange; + if (['CB', 'LSB', 'RSB'].contains(pos)) return Colors.blue; + if (['DMF', 'CMF', 'OMF', 'LMF', 'RMF'].contains(pos)) return Colors.green; + if (['CF', 'ST', 'RWG', 'LWG'].contains(pos)) return Colors.red; + return Colors.grey; + } + + // カード:表示はプレースタイルのみ + Widget _buildPlayerCard(String pos, String style) { + final color = _getColor(pos); + + return Container( + width: 80, + height: 60, + decoration: BoxDecoration( + color: color.withOpacity(0.9), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white, width: 1), + ), + padding: const EdgeInsets.all(4), + child: Center( + child: Text( + style, + textAlign: TextAlign.center, + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + } + + // ピッチにカードを並べる + Widget _buildPitch(String formation, List> roles) { + final layout = formationLayouts[formation]; + if (layout == null) { + return Center( + child: Text( + 'レイアウトが定義されていません: $formation', + style: const TextStyle(color: Colors.white), + ), + ); + } + + return LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth; + final height = constraints.maxHeight; + const cardW = 80.0; + const cardH = 60.0; + + final List children = []; + + // 背景(ピッチ) + children.add(Container( + width: width, + height: height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.green.shade800, Colors.green.shade700], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + )); + + int index = 0; + + for (int row = 0; row < layout.length; row++) { + for (int col = 0; col < layout[row].length; col++) { + if (index >= roles.length) break; + + final posData = roles[index]; + index++; + + final xNorm = layout[row][col]['x'] ?? 0.5; + final yNorm = layout[row][col]['y'] ?? 0.5; + + final left = width * xNorm - cardW / 2; + final top = height * yNorm - cardH / 2; + + children.add( + Positioned( + left: left, + top: top, + child: _buildPlayerCard( + posData['pos'] ?? '', + posData['style'] ?? '', + ), + ), + ); + } + } + + return Stack(children: children); + }, + ); + } + + @override + Widget build(BuildContext context) { + final formation = _result?['formation'] as String?; + final positions = _visiblePlayers; + + return Scaffold( + backgroundColor: Colors.green[900], + appBar: AppBar( + title: const Text('eFootball スカッドシミュレーター'), + centerTitle: true, + backgroundColor: Colors.green[900], + ), + body: Column( + children: [ + const SizedBox(height: 8), + + // 低確率モードスイッチ + SwitchListTile( + title: const Text( + '低確率スタイルモード(デコイラン等の出現率を下げる)', + style: TextStyle(color: Colors.white), + ), + value: _lowProbabilityMode, + onChanged: (v) { + setState(() { + _lowProbabilityMode = v; + }); + }, + ), + + const SizedBox(height: 4), + + // 抽選ボタン + ElevatedButton.icon( + onPressed: _isAnimating ? null : _generateFormation, + icon: const Icon(Icons.shuffle), + label: const Text('フォーメーション抽選'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: Colors.green[900], + ), + ), + + const SizedBox(height: 8), + + // フォーメーション表示 + if (formation != null) + Text( + 'フォーメーション: $formation', + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + + const SizedBox(height: 8), + + // ピッチ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: formation == null + ? const Center( + child: Text( + '抽選ボタンを押してスカッドを生成', + style: TextStyle(color: Colors.white70, fontSize: 16), + ), + ) + : _buildPitch(formation, positions), + ), + ), + ], + ), + ); + } +} diff --git a/main.dart b/main.dart new file mode 100644 index 0000000..c3008c4 --- /dev/null +++ b/main.dart @@ -0,0 +1,25 @@ + +import 'package:flutter/material.dart'; +import 'screens/home_screen.dart'; + +void main() { + runApp(const SquadSimulatorApp()); +} + +class SquadSimulatorApp extends StatelessWidget { + const SquadSimulatorApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'eFootball Squad Simulator', + theme: ThemeData.dark(useMaterial3: true), + home: const HomeScreen(), + debugShowCheckedModeBanner: false, + ); + } +} + + + + \ No newline at end of file diff --git a/player_attributes.dart b/player_attributes.dart new file mode 100644 index 0000000..387e8fc --- /dev/null +++ b/player_attributes.dart @@ -0,0 +1,158 @@ +//各ポジションごとのプレースタイルを定義 = { + final Map> playerAttributes ={ + 'GK':[ + '守備的GK', + '攻撃的GK', + ], + + 'CB':[ + 'ハードプレス', + 'ビルドアップ', + 'オーバーラップ', + '無印', + ], + + 'LSB':[ + '守備的サイドバック', + '攻撃的サイドバック', + 'インナーラップサイドバック', + '無印', + ], + + 'RSB':[ + '守備的サイドバック', + '攻撃的サイドバック', + 'インナーラップサイドバック', + '無印', + ], + + 'DMF':[ + 'ハードプレス', + 'アンカー', + 'ボックストゥボックス', + 'プレーメイカー', + '無印', + ], + + 'CMF':[ + 'ハードプレス', + 'プレーメイカー', + 'ボックストゥボックス', + '2列目からの飛び出し' + '無印', + ], + + 'OMF':[ + '2列目からの飛び出し', + 'チャンスメイカー', + 'ナンバー10', + 'デコイラン', + '無印', + ], + + 'LMF':[ + '2列目からの飛び出し', + 'ボックストゥボックス', + 'クロサー', + 'インサイドレシーバー', + 'チャンスメイカー', + '無印', + ], + + 'RMF':[ + '2列目からの飛び出し', + 'ボックストゥボックス', + 'クロサー', + 'インサイドレシーバー', + 'チャンスメイカー', + '無印', + ], + + 'ST':[ + '2列目からの飛び出し', + 'ナンバー10', + 'チャンスメイカー', + 'リンクフォワード', + 'デコイラン', + '無印', + ], + + 'LWG':[ + 'チャンスメイカー', + 'クロサー', + 'インサイドレシーバー', + 'ウイングストライカー', + '無印' + ], + + 'RWG':[ + 'チャンスメイカー', + 'クロサー', + 'インサイドレシーバー', + 'ウイングストライカー', + '無印' + ], + + 'CF':[ + 'ラインブレイカー', + 'ボックスストライカー', + 'ターゲットマン', + 'リンクフォワード', + 'デコイラン', + '無印', + ], +}; + +//全てのプレースタイル(完全ランダム用) +const allPlayStyle = [ + '守備的GK', + '攻撃的GK', + 'インナーラップサイドバック', + '守備的サイドバック', + '攻撃的サイドバック', + 'オーバーラップ', + 'ビルドアップ', + 'プレーメイカー', + 'ハードプレス', + 'アンカー', + 'ボックストゥボックス', + '2列目からの飛び出し', + 'ナンバー10', + 'クロサー', + 'インサイドレシーバー', + 'ウイングストライカー', + 'チャンスメイカー', + 'リンクフォワード', + 'ターゲットマン', + 'ボックスストライカー', + 'デコイラン', + 'ラインブレイカー', +]; + +const lowProbabilityStyles = [ + 'デコイラン', + 'ナンバー10', + 'ターゲットマン', + 'インナーラップサイドバック', + 'オーバーラップ', +]; + + +//抽選用フォーメーション候補 +const formations = [ + '4-4-2', + '4-3-3', + '4-3-2-1', + '4-3-1-2', + '4-2-3-1', + '4-2-1-3', + '4-2-2-2', + '3-4-3', + '3-2-4-1', + '3-2-3-2', + '3-1-4-2', + '5-3-2', + '5-2-2-1', + '5-2-1-2', + '5-2-3', +]; \ No newline at end of file diff --git a/player_positions.dart b/player_positions.dart new file mode 100644 index 0000000..5d24e15 --- /dev/null +++ b/player_positions.dart @@ -0,0 +1,6 @@ +final Map > positionsGroups = { + 'GK': ['GK'], + 'DF': ['CB','LSB','RSB'], + 'MF': ['DMF','CMF','OMF','LMF','RMF'], + 'FW': ['CF','ST','LWG','RWG'], +}; \ No newline at end of file