"/" にファイルをアップロード
This commit is contained in:
101
formation_positions.dart
Normal file
101
formation_positions.dart
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// フォーメーションごとの座標配置と役割キー(posは最終役割を示唆するが、実際の役割は生成ロジックで上書きされます)
|
||||||
|
final Map<String, List<List<Map<String, double>>>> 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},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
232
home_screen.dart
Normal file
232
home_screen.dart
Normal file
@@ -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<HomeScreen> createState() => _HomeScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
|
Map<String, dynamic>? _result;
|
||||||
|
List<Map<String, String>> _visiblePlayers = [];
|
||||||
|
bool _isAnimating = false;
|
||||||
|
|
||||||
|
// 低確率スタイルモード(デコイラン / ナンバー10 などの確率を下げる)
|
||||||
|
bool _lowProbabilityMode = false;
|
||||||
|
|
||||||
|
Future<void> _generateFormation() async {
|
||||||
|
setState(() {
|
||||||
|
_result = null;
|
||||||
|
_visiblePlayers = [];
|
||||||
|
_isAnimating = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ★ 低確率モードを渡してフォメ+スカッド生成
|
||||||
|
final newFormation = FormationGenerator.generate(
|
||||||
|
lowProbMode: _lowProbabilityMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_result = newFormation;
|
||||||
|
});
|
||||||
|
|
||||||
|
final players = newFormation['positions'] as List<dynamic>;
|
||||||
|
|
||||||
|
// 1枚ずつアニメーション的に追加
|
||||||
|
for (int i = 0; i < players.length; i++) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 150));
|
||||||
|
setState(() {
|
||||||
|
_visiblePlayers.add(Map<String, String>.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<Map<String, String>> 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<Widget> 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
main.dart
Normal file
25
main.dart
Normal file
@@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
158
player_attributes.dart
Normal file
158
player_attributes.dart
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
//各ポジションごとのプレースタイルを定義 = {
|
||||||
|
final Map<String, List<String>> 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',
|
||||||
|
];
|
||||||
6
player_positions.dart
Normal file
6
player_positions.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
final Map <String, List<String>> positionsGroups = {
|
||||||
|
'GK': ['GK'],
|
||||||
|
'DF': ['CB','LSB','RSB'],
|
||||||
|
'MF': ['DMF','CMF','OMF','LMF','RMF'],
|
||||||
|
'FW': ['CF','ST','LWG','RWG'],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user