using Microsoft.Xna.Framework; using Microsoft.Xna...
Criado em: 10 de junho de 2026
Criado em: 10 de junho de 2026
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Framework.WpfInterop;
using MonoGame.Framework.WpfInterop.Input;
namespace ShooterGame.Backend.Models.Game
{
public class Game : WpfGame
{
private IGraphicsDeviceService _graphicsDeviceManager;
private SpriteBatch _spriteBatch;
private WpfKeyboard _keyboard;
private WpfMouse _mouse;
textpublic Game() { } protected override void Initialize() { _graphicsDeviceManager = new WpfGraphicsDeviceService(this); _keyboard = new WpfKeyboard(this); _mouse = new WpfMouse(this); base.Initialize(); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); } protected override void Update(GameTime gameTime) { base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); _spriteBatch.Begin(); _spriteBatch.End(); base.Draw(gameTime); } }
}<Window x:Class="ShooterGame.Backend.Models.Game.GameWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ShooterGame.Backend.Models.Game"
mc:Ignorable="d"
Title="GameWindow" Height="450" Width="800">
<Grid x:Name="GameHost">
</Grid>
</Window>
using System.Windows;
using Game = ShooterGame.Backend.Models.Game;
namespace ShooterGame.Backend.Models.Game
{
public partial class GameWindow : Window
{
public GameWindow()
{
InitializeComponent();
Loaded += GameWindow_Loaded;
Closed += GameWindow_Closed;
}
textprivate void GameWindow_Loaded(object sender, RoutedEventArgs e) { var game = new Game(); GameHost.Children.Add(game); } private void GameWindow_Closed(object sender, EventArgs e) { GameHost.Children.Clear(); } }
}<Window x:Class="ShooterGame.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ShooterGame"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button Content="StartGame
" HorizontalAlignment="Left" Margin="652,26,0,0" VerticalAlignment="Top" Height="38" Width="102" Click="Button_Click"/>
</Grid>
</Window>
using Microsoft.Xna.Framework;
using System.Windows;
using ShooterGame.Backend.Models.Game;
namespace ShooterGame
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
textprivate void Button_Click(object sender, RoutedEventArgs e) { Backend.Models.Game.GameWindow gameWindow = new(); gameWindow.ShowDialog(); } }
}почему у меня зависает окно стартовон, я вот открою игру, зщаровю игровое окно и стартовое виснет и вечно грузит, не отвечает
Проблема почти точно из-за ShowDialog().
ShowDialog() блокирует стартовое окно, пока игровое окно открыто. А WpfGame/MonoGame запускает свой игровой цикл внутри WPF, и после закрытия может не освобождаться нормально, особенно если сам game не остановлен/не disposed.
Замени:
csharpgameWindow.ShowDialog();
на:
csharpgameWindow.Show();
И в GameWindow храни игру в поле и корректно освобождай:
csharppublic partial class GameWindow : Window { private Backend.Models.Game.Game? _game; public GameWindow() { InitializeComponent(); Loaded += GameWindow_Loaded; Closed += GameWindow_Closed; } private void GameWindow_Loaded(object sender, RoutedEventArgs e) { _game = new Backend.Models.Game.Game(); GameHost.Children.Add(_game); } private void GameWindow_Closed(object sender, EventArgs e) { if (_game != null) { GameHost.Children.Remove(_game); _game.Dispose(); _game = null; } } }
Ещё у тебя конфликт имён:
csharpusing Game = ShooterGame.Backend.Models.Game;
Это неправильно, потому что Game указывает на namespace, а не на класс. Лучше убери этот alias вообще.
В MainWindow оставь так:
csharpprivate void Button_Click(object sender, RoutedEventArgs e) { var gameWindow = new ShooterGame.Backend.Models.Game.GameWindow(); gameWindow.Show(); }
Главная причина: ShowDialog() делает стартовое окно модальным и блокирует его поток. Для игрового окна лучше использовать обычный Show() + корректный Dispose() игры при закрытии.
вот такую ошиюку я отследил
Да, это похоже на проблему с WPF Interop: WpfGame не должен добавляться в Grid через Children.Add(game) как обычный контрол.
Для MonoGame.Framework.WpfInterop обычно надо использовать MonoGameContentControl.
В XAML игрового окна:
xml<Window x:Class="ShooterGame.Backend.Models.Game.GameWindow" ... xmlns:mono="clr-namespace:MonoGame.Framework.WpfInterop;assembly=MonoGame.Framework.WpfInterop"> <Grid> <mono:MonoGameContentControl x:Name="GameHost" /> </Grid> </Window>
В GameWindow.xaml.cs:
csharppublic partial class GameWindow : Window { private Game? _game; public GameWindow() { InitializeComponent(); Loaded += GameWindow_Loaded; Closed += GameWindow_Closed; } private void GameWindow_Loaded(object sender, RoutedEventArgs e) { _game = new Game(); GameHost.Content = _game; } private void GameWindow_Closed(object sender, EventArgs e) { _game?.Dispose(); _game = null; } }
И открывай окно так:
csharpprivate void Button_Click(object sender, RoutedEventArgs e) { var gameWindow = new GameWindow(); gameWindow.Show(); }
Ошибка 0x80070005 E_ACCESSDENIED часто появляется, когда DirectX/graphics device пытается работать с уже закрытым/некорректно размещённым WPF host-контролом. Главные правки: не ShowDialog(), не Grid.Children.Add(game), а MonoGameContentControl + Content = _game + Dispose() при закрытии.
<Window x:Class="FifaFootballGame.GameWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Игра" Height="600" Width="900" WindowStyle="ToolWindow" WindowStartupLocation="CenterScreen">
<Grid x:Name="GameHost" />
</Window> это мой прошлый проект гдк все отлично, ту т нету никакой хуйни, не надор мне content, без него можно
Согласен, если в прошлом проекте работало через обычный Grid, то проблема скорее всего не в ContentControl.
Ошибка, которую ты поймал:
textHRESULT: [0x80070005] E_ACCESSDENIED Отказано в доступе
для MonoGame/DXGI чаще означает не "нет прав пользователя", а что DirectX пытается обратиться к ресурсу, который уже уничтожен или принадлежит другому потоку.
Я бы посмотрел на следующие вещи.
У тебя сейчас:
csharpprivate void Button_Click(object sender, RoutedEventArgs e) { Backend.Models.Game.GameWindow gameWindow = new(); gameWindow.ShowDialog(); }
Если несколько раз открыть/закрыть окно, могут оставаться старые GraphicsDevice.
Добавь лог в конструктор Game:
csharppublic Game() { Debug.WriteLine("Game created"); }
и в Dispose.
Поставь breakpoint на:
csharp_graphicsDeviceManager = new WpfGraphicsDeviceService(this);
и на
csharp_spriteBatch = new SpriteBatch(GraphicsDevice);
Если исключение вылетает на одной из этих строк — это уже сильно сужает круг.
Сейчас:
csharpprivate void GameWindow_Closed(object sender, EventArgs e) { GameHost.Children.Clear(); }
Но сам объект игры живёт дальше.
Сделай так:
csharpprivate Game? _game; private void GameWindow_Loaded(object sender, RoutedEventArgs e) { _game = new Game(); GameHost.Children.Add(_game); } private void GameWindow_Closed(object sender, EventArgs e) { _game?.Dispose(); _game = null; GameHost.Children.Clear(); }
У тебя класс называется:
csharppublic class Game : WpfGame
а в проекте ещё подключён
csharpusing Microsoft.Xna.Framework;
где уже есть:
csharpMicrosoft.Xna.Framework.Game
Иногда из-за такого имени возникают странные конфликты типов в WPF Interop.
Я бы переименовал:
csharppublic class ShooterGameCore : WpfGame
и проверил.
Скрин показывает только HRESULT.
Нужен полный exception:
csharptry { _graphicsDeviceManager = new WpfGraphicsDeviceService(this); } catch (Exception ex) { MessageBox.Show(ex.ToString()); }
или покажи вывод из Visual Studio (Output).
Тогда будет видно, кто именно кидает 0x80070005:
D3D11DXGIWpfGraphicsDeviceServiceSpriteBatchGraphicsDeviceБез стека сейчас можно только гадать. HRESULT говорит лишь, что DirectX потерял доступ к ресурсу.
using FifaFootballGame.Data;
using FifaFootballGame.Service;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Framework.WpfInterop;
using MonoGame.Framework.WpfInterop.Input;
using System.Linq;
using System.IO;
namespace FifaFootballGame.Models.GamePresentWindowFolder.GameFolder
{
public class Game : WpfGame
{
//переменные используемые во время матча, для отслеживания начала и конца матча
private const float MATCH_DURATION = 180f; // 3 минуты
private float _matchTimeLeft = MATCH_DURATION;
private bool _matchEnded = false;
textprivate Vector2 _lastOutPosition; //переменные для использования, как константы private const int SPEED_REFERI = 1; private IGraphicsDeviceService _graphicsDeviceManager; private SpriteBatch _spriteBatch; private Ball _gameBall; private WpfKeyboard _keyboard; private WpfMouse _mouse; private FootballPlayer _footballPlayer; private Dictionary<(FootballPlayer, FootballPlayer), float> _dictionaryDistance; // Текстуры для рефери (судей) private Texture2D _textureReferi; private Vector2 _referiPosition; private Texture2D _textureReferiSecond; private Vector2 _referiSecondPosition; // Текстуры для судей (если будут отдельные картинки) private Texture2D _mainRefereeTexture; private Texture2D _secondRefereeTexture; // Текстуры поля, мяча и игроков private Texture2D _fieldTexture; private Texture2D _ballTexture; // Текстуры для своей команды private Texture2D _forwardTexture; private Texture2D _midfielderTexture; private Texture2D _defenderTexture; private Texture2D _goalkeeperTexture; // Текстуры для команды противника private Texture2D _enemyForwardTexture; private Texture2D _enemyMidfielderTexture; private Texture2D _enemyDefenderTexture; private Texture2D _enemyGoalkeeperTexture; //футболисты Forward _forward; Midfielder _leftMidfielder; Midfielder _rightMidfielder; Defender _defender; Goalkeeper _goalkeeper; //противники EnemyFootball _aiForward; EnemyFootball _aiLMidfielder; EnemyFootball _aiRMidfielder; EnemyFootball _aiDefender; EnemyFootball _aiGoalkeeper; //комагда противников в качестве отдельной структуры private EnemyTeamAI _enemyTeamAI; //для коианды пользователя private PlayerTeamAI _playerTeamAI; //статистика игры private MatchState _matchState; private SpriteFont _font; //переменные для рефери private bool _directionReferi; //переменная, чтобы отдать пас private bool _wasPassPressed = false; private bool _wasShootPressed = false; private List<FootballPlayer> _footballPlayers; private List<EnemyFootball> _aiFootballAgents; private int _windowWidth = 900; private int _radiusCentreCircle = 128; private int _windowHeight = 600; private bool _wasMousePressed = false; private GameLogic _gameLogic; //серверная логика, основная для курсача private GameServer _gameServer; private GameClient _gameClient; private bool _isServerGame; private bool _isClientGame; public Game() { _dictionaryDistance = new Dictionary<(FootballPlayer, FootballPlayer), float>(); } public Game(GameServer server) { _dictionaryDistance = new Dictionary<(FootballPlayer, FootballPlayer), float>(); _gameServer = server; _isServerGame = true; } public Game(GameClient client) { _dictionaryDistance = new Dictionary<(FootballPlayer, FootballPlayer), float>(); _gameClient = client; _isClientGame = true; } //метод нажатия на игрока private void MouseClick(WpfMouse mouse) { var mouseState = mouse.GetState(); bool isPressed = (int)mouseState.LeftButton == 1; if (isPressed && !_wasMousePressed) { foreach (var player in _footballPlayers) { Vector2 playerPos = player.GetPosition(); if (mouseState.X >= playerPos.X && mouseState.X <= playerPos.X + 32 && mouseState.Y >= playerPos.Y && mouseState.Y <= playerPos.Y + 32) { foreach (var p in _footballPlayers) p.isActive = false; player.isActive = true; break; } } } _wasMousePressed = isPressed; } protected override void Initialize() { _graphicsDeviceManager = new WpfGraphicsDeviceService(this); _keyboard = new WpfKeyboard(this); _mouse = new WpfMouse(this); base.Initialize(); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); // Загрузка всех текстур LoadAllTextures(); //загрузка шрифта //try //{ // _font = Content.Load<SpriteFont>("GameFont"); //} //catch //{ // _font = null; //} LoadBall(); LoadFootballPlayers(); LoadTextureAI(); LoadReferi(); } // МЕТОД ЗАГРУЗКИ ВСЕХ ТЕКСТУР private void LoadAllTextures() { string exePath = AppDomain.CurrentDomain.BaseDirectory; string contentPath = Path.Combine(exePath, "Content"); if (!Directory.Exists(contentPath)) contentPath = exePath; // ===== ПОЛЕ ===== try { string fieldFile = Path.Combine(contentPath, "field.png"); if (File.Exists(fieldFile)) { using (var stream = new FileStream(fieldFile, FileMode.Open)) _fieldTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _fieldTexture = CreateFieldTexture(); } catch { _fieldTexture = CreateFieldTexture(); } // ===== МЯЧ ===== try { string ballFile = Path.Combine(contentPath, "ball.png"); if (File.Exists(ballFile)) { using (var stream = new FileStream(ballFile, FileMode.Open)) _ballTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _ballTexture = GenerateTextureEntity(Color.Red); } catch { _ballTexture = GenerateTextureEntity(Color.Red); } // ===== СВОЯ КОМАНДА ===== try { string file = Path.Combine(contentPath, "forward.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _forwardTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _forwardTexture = GenerateTextureEntity(Color.BlueViolet); } catch { _forwardTexture = GenerateTextureEntity(Color.BlueViolet); } try { string file = Path.Combine(contentPath, "midfielder.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _midfielderTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _midfielderTexture = GenerateTextureEntity(Color.Blue); } catch { _midfielderTexture = GenerateTextureEntity(Color.Blue); } try { string file = Path.Combine(contentPath, "defender.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _defenderTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _defenderTexture = GenerateTextureEntity(Color.DarkBlue); } catch { _defenderTexture = GenerateTextureEntity(Color.DarkBlue); } try { string file = Path.Combine(contentPath, "goalkeeper.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _goalkeeperTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _goalkeeperTexture = GenerateTextureEntity(Color.Red); } catch { _goalkeeperTexture = GenerateTextureEntity(Color.Red); } // ===== КОМАНДА ПРОТИВНИКА ===== try { string file = Path.Combine(contentPath, "enemy_forward.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _enemyForwardTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _enemyForwardTexture = GenerateTextureEntity(Color.Green); } catch { _enemyForwardTexture = GenerateTextureEntity(Color.Green); } try { string file = Path.Combine(contentPath, "enemy_midfielder.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _enemyMidfielderTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _enemyMidfielderTexture = GenerateTextureEntity(Color.LightGreen); } catch { _enemyMidfielderTexture = GenerateTextureEntity(Color.LightGreen); } try { string file = Path.Combine(contentPath, "enemy_defender.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _enemyDefenderTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _enemyDefenderTexture = GenerateTextureEntity(Color.DarkGreen); } catch { _enemyDefenderTexture = GenerateTextureEntity(Color.DarkGreen); } try { string file = Path.Combine(contentPath, "enemy_goalkeeper.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _enemyGoalkeeperTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _enemyGoalkeeperTexture = GenerateTextureEntity(Color.YellowGreen); } catch { _enemyGoalkeeperTexture = GenerateTextureEntity(Color.YellowGreen); } // ===== СУДЬИ ===== try { string file = Path.Combine(contentPath, "main_referee.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _mainRefereeTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _mainRefereeTexture = GenerateTextureEntity(Color.Yellow); } catch { _mainRefereeTexture = GenerateTextureEntity(Color.Yellow); } try { string file = Path.Combine(contentPath, "second_referee.png"); if (File.Exists(file)) { using (var stream = new FileStream(file, FileMode.Open)) _secondRefereeTexture = Texture2D.FromStream(GraphicsDevice, stream); } else _secondRefereeTexture = GenerateTextureEntity(Color.Orange); } catch { _secondRefereeTexture = GenerateTextureEntity(Color.Orange); } } // СОЗДАНИЕ ТЕКСТУРЫ ПОЛЯ С РАЗМЕТКОЙ private Texture2D CreateFieldTexture() { Texture2D texture = new Texture2D(GraphicsDevice, _windowWidth, _windowHeight); Color[] colors = new Color[_windowWidth * _windowHeight]; for (int y = 0; y < _windowHeight; y++) { for (int x = 0; x < _windowWidth; x++) { colors[y * _windowWidth + x] = Color.ForestGreen; int centerX = _windowWidth / 2; int centerY = _windowHeight / 2; // Центральный круг int dx = x - centerX; int dy = y - centerY; float distance = (float)Math.Sqrt(dx * dx + dy * dy); if (distance < _radiusCentreCircle && distance > _radiusCentreCircle - 3) colors[y * _windowWidth + x] = Color.White; // Центральная линия if (Math.Abs(x - centerX) < 2) colors[y * _windowWidth + x] = Color.White; // Штрафные площади if ((x >= 20 && x <= 120 && y >= centerY - 100 && y <= centerY + 100) || (x >= _windowWidth - 120 && x <= _windowWidth - 20 && y >= centerY - 100 && y <= centerY + 100)) colors[y * _windowWidth + x] = Color.LightGreen; // Линии штрафных if (x == 20 || x == 120 || x == _windowWidth - 120 || x == _windowWidth - 20) { if (y >= centerY - 100 && y <= centerY + 100) colors[y * _windowWidth + x] = Color.White; } } } texture.SetData(colors); return texture; } // СОЗДАНИЕ ЦВЕТНОГО КВАДРАТА 32x32 private Texture2D GenerateTextureEntity(Color colors) { Texture2D textureEntity = new Texture2D(GraphicsDevice, 32, 32); Color[] color = new Color[32 * 32]; for (int i = 0; i < color.Length; i++) color[i] = colors; textureEntity.SetData(color); return textureEntity; } // ЗАГРУЗКА МЯЧА private void LoadBall() { Vector2 startPosition = new Vector2(_windowWidth / 2 - 16, _windowHeight / 2 - 16); _gameBall = new Ball(_ballTexture, startPosition, 16f); _gameBall.OwnerChanged += SetActivePlayer; _matchState = new MatchState(); _gameLogic = new GameLogic(_gameBall, _matchState); } // ЗАГРУЗКА СВОИХ ИГРОКОВ private void LoadFootballPlayers() { Vector2 startPositionForward = new Vector2((int)(_windowWidth / 2 - _radiusCentreCircle), (int)(_windowHeight / 2 - 16)); int positionXMidfielder = (_windowWidth / 2 - _radiusCentreCircle) - (_windowWidth / 2 * 10 / 42); Vector2 startPositionLeftMidfielder = new Vector2(positionXMidfielder, _windowHeight / 2 - 144); Vector2 startPositionRightMidfielder = new Vector2(positionXMidfielder, _windowHeight / 2 + 144 - 64); Vector2 startPositionDefender = new Vector2((int)(_windowWidth / 2 * 10 / 42), (int)(_windowHeight / 2 - 16)); Vector2 startPositionGoalkeeper = new Vector2(10, (int)(_windowHeight / 2 - 16)); _forward = new Forward(_forwardTexture, startPositionForward, _gameBall); _leftMidfielder = new Midfielder(_midfielderTexture, startPositionLeftMidfielder, _gameBall, -1); _rightMidfielder = new Midfielder(_midfielderTexture, startPositionRightMidfielder, _gameBall, 1); _defender = new Defender(_defenderTexture, startPositionDefender, _gameBall); _goalkeeper = new Goalkeeper(_goalkeeperTexture, startPositionGoalkeeper, _gameBall); _footballPlayers = new List<FootballPlayer>() { _forward, _leftMidfielder, _rightMidfielder, _defender, _goalkeeper }; _forward.isActive = true; _playerTeamAI = new PlayerTeamAI(_footballPlayers, _aiFootballAgents, _gameBall); } // ЗАГРУЗКА ИГРОКОВ ПРОТИВНИКА private void LoadTextureAI() { int positionXMidfielder = ((_windowWidth / 2 - _radiusCentreCircle) + (_windowWidth / 2 * 10 / 42) + 2 * _radiusCentreCircle); Vector2 startPositionForwardAI = new Vector2((int)(_windowWidth / 2 + _radiusCentreCircle), (int)(_windowHeight / 2 - 16)); Vector2 startPositionLeftMidfielderAI = new Vector2(positionXMidfielder, _windowHeight / 2 + 144); Vector2 startPositionRightMidfielderAI = new Vector2(positionXMidfielder, _windowHeight / 2 - 144 - 64); Vector2 startPositionDefenderAI = new Vector2((int)(((_windowWidth / 10) - 10) * 10), (int)((_windowHeight / 2) - 16)); Vector2 startPositionGoalkeeperAI = new Vector2(_windowWidth - 42, _windowHeight / 2 - 16); if (_isServerGame) _aiForward = new SecondFootballPlayer(_enemyForwardTexture, startPositionForwardAI, _gameBall, "forward"); else _aiForward = new AIFootballAgent(_enemyForwardTexture, startPositionForwardAI, _gameBall, "forward"); _aiLMidfielder = new AIFootballAgent(_enemyMidfielderTexture, startPositionLeftMidfielderAI, _gameBall, "leftMidfielder"); _aiRMidfielder = new AIFootballAgent(_enemyMidfielderTexture, startPositionRightMidfielderAI, _gameBall, "rightMidfielder"); _aiDefender = new AIFootballAgent(_enemyDefenderTexture, startPositionDefenderAI, _gameBall, "defender"); _aiGoalkeeper = new AIFootballAgent(_enemyGoalkeeperTexture, startPositionGoalkeeperAI, _gameBall, "goalkeeper"); _aiFootballAgents = new List<EnemyFootball>() { _aiForward, _aiLMidfielder, _aiRMidfielder, _aiDefender, _aiGoalkeeper }; _enemyTeamAI = new EnemyTeamAI(_aiFootballAgents, _footballPlayers, _gameBall); } // ЗАГРУЗКА СУДЕЙ (ОДИНАКОВОГО РАЗМЕРА С ИГРОКАМИ) private void LoadReferi() { // Позиции судей Vector2 startPositionFirstReferi = new Vector2(0, 0); Vector2 startPositionSecondReferi = new Vector2(0, (int)(_windowHeight) - 70); // Создаем текстуры 32x32 для судей (такого же размера как у игроков) _textureReferi = _mainRefereeTexture ?? GenerateTextureEntity(Color.Yellow); _referiPosition = startPositionFirstReferi; _textureReferiSecond = _secondRefereeTexture ?? GenerateTextureEntity(Color.Orange); _referiSecondPosition = startPositionSecondReferi; } // ОТРИСОВКА - ВСЕ ОБЪЕКТЫ РАЗМЕРОМ 32x32 protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); if (_spriteBatch != null && _gameBall != null) { _spriteBatch.Begin(); // Поле if (_fieldTexture != null) _spriteBatch.Draw(_fieldTexture, new Rectangle(0, 0, _windowWidth, _windowHeight), Color.White); // Мяч (32x32) _gameBall.Draw(_spriteBatch); // Свои игроки (32x32) _forward.DrawFootballPlayer(_spriteBatch); _leftMidfielder.DrawFootballPlayer(_spriteBatch); _rightMidfielder.DrawFootballPlayer(_spriteBatch); _defender.DrawFootballPlayer(_spriteBatch); _goalkeeper.DrawFootballPlayer(_spriteBatch); // Игроки противника (32x32) _aiForward.DrawAIPlayers(_spriteBatch); _aiLMidfielder.DrawAIPlayers(_spriteBatch); _aiRMidfielder.DrawAIPlayers(_spriteBatch); _aiDefender.DrawAIPlayers(_spriteBatch); _aiGoalkeeper.DrawAIPlayers(_spriteBatch); // СУДЬИ - ТОЖЕ РАЗМЕР 32x32 (КАК У ИГРОКОВ) _spriteBatch.Draw(_textureReferi, new Rectangle((int)_referiPosition.X, (int)_referiPosition.Y, 32, 32), Color.White); _spriteBatch.Draw(_textureReferiSecond, new Rectangle((int)_referiSecondPosition.X, (int)_referiSecondPosition.Y, 32, 32), Color.White); ///участок кода, нужно добавить файл со шрифтом //if (_font != null) //{ // int minutes = (int)(_matchTimeLeft / 60); // int seconds = (int)(_matchTimeLeft % 60); // string scoreText = $"{_matchState.PlayerScore} : {_matchState.EnemyScore}"; // string timeText = $"{minutes:00}:{seconds:00}"; // _spriteBatch.DrawString(_font, scoreText, new Vector2(390, 15), Color.White); // _spriteBatch.DrawString(_font, timeText, new Vector2(410, 45), Color.White); // if (_matchEnded) // _spriteBatch.DrawString(_font, "МАТЧ ОКОНЧЕН", new Vector2(330, 260), Color.Yellow); //} _spriteBatch.End(); } base.Draw(gameTime); } // ============= ДВИЖЕНИЕ СУДЕЙ ============= private void MoveReferi(WpfKeyboard keyboard) { if (_directionReferi) { _referiPosition.X += SPEED_REFERI; if (_referiPosition.X >= _windowWidth - 32) _directionReferi = false; } else { _referiPosition.X -= SPEED_REFERI; if (_referiPosition.X <= 0) _directionReferi = true; } if (_directionReferi) { _referiSecondPosition.X += SPEED_REFERI; if (_referiSecondPosition.X >= _windowWidth - 32) _directionReferi = false; } else { _referiSecondPosition.X -= SPEED_REFERI; if (_referiSecondPosition.X <= 0) _directionReferi = true; } } // ============= UPDATE ============= protected override void Update(GameTime gameTime) { if (_keyboard == null || _gameBall == null) { base.Update(gameTime); return; } float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; if (_matchEnded) { SendServerStateIfNeeded(); base.Update(gameTime); return; } if (_isClientGame && _gameClient != null) { SendClientInput(); if (_gameClient.LastState != null) ApplyGameState(_gameClient.LastState); base.Update(gameTime); return; } if (_isServerGame && _gameServer != null) { if (_gameServer.IsClientConnected && !_gameServer.ConnectionMessageShown) { _gameServer.ConnectionMessageShown = true; System.Windows.MessageBox.Show("Второй игрок подключился!"); } } if (_matchState != null && _matchState.IsPausedAfterEvent) { _gameLogic.Update(deltaTime); if (_gameLogic.NeedResetPositions) ResetAllPlayersToStart(); SendServerStateIfNeeded(); base.Update(gameTime); return; } if (_matchState != null && _matchState.IsKickOff) { HandleKickOff(); SendServerStateIfNeeded(); base.Update(gameTime); return; } bool playerTeamHasBall = _footballPlayers.Any(p => p.IsControllBall); foreach (var player in _footballPlayers) player.IsControllBallTeam = playerTeamHasBall; CalculateDistanceAllPlayers(); MouseClick(_mouse); var keyboardState = _keyboard.GetState(); bool isPassPressed = keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P); if (isPassPressed && !_wasPassPressed) PassToNearestPlayer(); _wasPassPressed = isPassPressed; bool isShootPressed = keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space); if (isShootPressed && !_wasShootPressed) ShootToGoal(); _wasShootPressed = isShootPressed; TeamTacticContext playerContext = _playerTeamAI.BuildContext(_windowWidth, _windowHeight); foreach (var player in _footballPlayers) { if (!player.isActive) player.SmartMove(_windowWidth, _windowHeight, _footballPlayers, _aiFootballAgents, playerContext); player.MoveCurrentPlayer(_keyboard); } if (_isServerGame && _gameServer != null) { PlayerInputPacket input = _gameServer.LastInput; if (_aiForward is SecondFootballPlayer secondPlayer) { secondPlayer.isActive = true; secondPlayer.MoveFromNetwork(input, _windowWidth, _windowHeight); } _enemyTeamAI.Update(_windowWidth, _windowHeight); foreach (var ai in _aiFootballAgents) { if (ai == _aiForward) continue; ai.MoveAI(_windowWidth, _windowHeight, _footballPlayers, _aiFootballAgents); } } else { _enemyTeamAI.Update(_windowWidth, _windowHeight); foreach (var ai in _aiFootballAgents) ai.MoveAI(_windowWidth, _windowHeight, _footballPlayers, _aiFootballAgents); } ClampAllPlayersToField();//метод,отслеживающий выход игрока за границы окна PickBall(); ResolveBallPlayerCollisions(); TryPlayerTackle(); _gameBall.Update(); MoveReferi(_keyboard); _gameLogic.Update(deltaTime); if (_gameLogic.NeedResetPositions) ResetAllPlayersToStart(); SendServerStateIfNeeded(); base.Update(gameTime); } // ============= ОСТАЛЬНЫЕ МЕТОДЫ ============= private void SendServerStateIfNeeded() { if (_isServerGame && _gameServer != null && _gameServer.IsClientConnected) _ = _gameServer.SendStateAsync(BuildGameState()); } private async void SendClientInput() { var state = _keyboard.GetState(); var input = new PlayerInputPacket { W = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.W), A = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.A), S = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.S), D = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.D), Space = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space), P = state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P) }; await _gameClient.SendInputAsync(input); } private GameStatePacket BuildGameState() { return new GameStatePacket { BallX = _gameBall.GetPositionBall().X, BallY = _gameBall.GetPositionBall().Y, ForwardX = _forward.Position().X, ForwardY = _forward.Position().Y, LeftMidX = _leftMidfielder.GetPosition().X, LeftMidY = _leftMidfielder.GetPosition().Y, RightMidX = _rightMidfielder.GetPosition().X, RightMidY = _rightMidfielder.GetPosition().Y, DefenderX = _defender.GetPosition().X, DefenderY = _defender.GetPosition().Y, AiForwardX = _aiForward.Position().X, AiForwardY = _aiForward.Position().Y, AiLeftMidX = _aiLMidfielder.Position().X, AiLeftMidY = _aiLMidfielder.Position().Y, AiRightMidX = _aiRMidfielder.Position().X, AiRightMidY = _aiRMidfielder.Position().Y, AiDefenderX = _aiDefender.Position().X, AiDefenderY = _aiDefender.Position().Y, AiGoalkeeperX = _aiGoalkeeper.Position().X, AiGoalkeeperY = _aiGoalkeeper.Position().Y }; } private void ApplyGameState(GameStatePacket state) { if (state == null) return; _gameBall.SetPosition(new Vector2(state.BallX, state.BallY)); _forward.SetNetworkPosition(new Vector2(state.ForwardX, state.ForwardY)); _leftMidfielder.SetNetworkPosition(new Vector2(state.LeftMidX, state.LeftMidY)); _rightMidfielder.SetNetworkPosition(new Vector2(state.RightMidX, state.RightMidY)); _defender.SetNetworkPosition(new Vector2(state.DefenderX, state.DefenderY)); _aiForward.SetNetworkPosition(new Vector2(state.AiForwardX, state.AiForwardY)); _aiLMidfielder.SetNetworkPosition(new Vector2(state.AiLeftMidX, state.AiLeftMidY)); _aiRMidfielder.SetNetworkPosition(new Vector2(state.AiRightMidX, state.AiRightMidY)); _aiDefender.SetNetworkPosition(new Vector2(state.AiDefenderX, state.AiDefenderY)); _aiGoalkeeper.SetNetworkPosition(new Vector2(state.AiGoalkeeperX, state.AiGoalkeeperY)); } private void HandleKickOff() { var keyboardState = _keyboard.GetState(); _gameBall.StartPositionBall(); if (_matchState.PlayerKickOff) { _gameBall.SetOwner(_forward); SetActivePlayer(_forward); bool startPressed = keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.W) || keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.A) || keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.S) || keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.D) || keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P) || keyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space); if (startPressed) _matchState.EndKickOff(); } else { _gameBall.SetEnemyOwner(_aiForward); if (_aiForward is AIFootballAgent) _matchState.EndKickOff(); } } private void ResolveBallPlayerCollisions() { if (_gameBall.IsControlled() || _gameBall.IsPassing() || _gameBall.IsShooting()) return; Vector2 ballPos = _gameBall.GetPositionBall(); foreach (var player in _footballPlayers) { Vector2 playerCenter = player.GetPosition() + new Vector2(16, 16); float distance = Vector2.Distance(ballPos, playerCenter); if (distance < 30) { SetActivePlayer(player); _gameBall.SetOwner(player); return; } } foreach (var enemy in _aiFootballAgents) { Vector2 enemyCenter = enemy.Position() + new Vector2(16, 16); float distance = Vector2.Distance(ballPos, enemyCenter); if (distance < 30) { _gameBall.SetEnemyOwner(enemy); return; } } } private void ResetAllPlayersToStart() { foreach (var player in _footballPlayers) player.ResetToHome(); foreach (var enemy in _aiFootballAgents) enemy.ResetToHome(); _forward.isActive = true; _gameBall.DropOwner(); if (_matchState != null && _matchState.IsKickOff) _gameBall.StartPositionBall(); } private void SetActivePlayer(FootballPlayer player) { foreach (var p in _footballPlayers) p.isActive = false; player.isActive = true; } private void CalculateDistanceAllPlayers() { _dictionaryDistance.Clear(); var players = _footballPlayers; for (int i = 0; i < players.Count; i++) { for (int j = i + 1; j < players.Count; j++) { float distance = Vector2.Distance(players[i].GetPosition(), players[j].GetPosition()); _dictionaryDistance[(players[i], players[j])] = distance; _dictionaryDistance[(players[j], players[i])] = distance; } } } private FootballPlayer FindNearestPlayer(FootballPlayer owner) { FootballPlayer nearestPlayer = null; float minDistance = float.MaxValue; foreach (var player in _footballPlayers) { if (player == owner) continue; float distance = Vector2.Distance(owner.GetPosition(), player.GetPosition()); if (distance < minDistance) { minDistance = distance; nearestPlayer = player; } } return nearestPlayer; } private void PassToNearestPlayer() { if (!_gameBall.IsControlled()) return; FootballPlayer owner = _gameBall.GetOwner(); if (owner == null) return; FootballPlayer target = FindNearestPlayer(owner); if (target == null) return; _gameBall.StartPass(target); } //передача мача private void PickBall() { if (_gameBall.IsControlled() || _gameBall.IsPassing() || _gameBall.IsShooting()) return; foreach (var player in _footballPlayers) { float distance = Vector2.Distance(player.GetPosition() + new Vector2(16, 16), _gameBall.GetPositionBall()); if (distance < 35) { SetActivePlayer(player); _gameBall.SetOwner(player); return; } } foreach (var ai in _aiFootballAgents) { float distance = Vector2.Distance(ai.Position() + new Vector2(16, 16), _gameBall.GetPositionBall()); if (distance < 30) { _gameBall.SetEnemyOwner(ai); return; } } } //отсчет гола private void ShootToGoal() { if (!_gameBall.IsControlled()) return; FootballPlayer owner = _gameBall.GetOwner(); if (owner == null) return; Vector2 enemyGoal = new Vector2(_windowWidth, _windowHeight / 2); Vector2 direction = enemyGoal - owner.GetPosition(); _gameBall.Shoot(direction, 10f); } private void TryPlayerTackle() { if (!_gameBall.IsEnemyOwner()) return; EnemyFootball enemy = _gameBall.GetEnemyOwner(); foreach (var player in _footballPlayers) { float distance = Vector2.Distance(player.GetPosition() + new Vector2(16, 16), enemy.Position() + new Vector2(16, 16)); if (distance < 34) { int playerPower = player.GetValueDribling() + player.GetValueSpeed() + player.GetValueHeadingGame(); int enemyPower = enemy.GetValueDribling() + enemy.GetValueSpeed() + enemy.GetValueHeadingGame(); int chance = 35 + (playerPower - enemyPower) / 4; chance = MathHelper.Clamp(chance, 15, 70); if (new Random().Next(0, 100) < chance) { SetActivePlayer(player); _gameBall.SetOwner(player); return; } } } } //метод, ограничивающий движение игроков за границы поля private void ClampAllPlayersToField() { foreach (var player in _footballPlayers) { Vector2 pos = player.GetPosition(); pos.X = MathHelper.Clamp(pos.X, 0, _windowWidth - 32); pos.Y = MathHelper.Clamp(pos.Y, 0, _windowHeight - 32); player.SetNetworkPosition(pos); } foreach (var enemy in _aiFootballAgents) { Vector2 pos = enemy.Position(); pos.X = MathHelper.Clamp(pos.X, 0, _windowWidth - 32); pos.Y = MathHelper.Clamp(pos.Y, 0, _windowHeight - 32); enemy.SetNetworkPosition(pos); } } }
} вот проект который раьотает, тут много кода, но возьми самый старт и в новый проект пришли мне
Вот минимальный старт, как в рабочем проекте: обычный Grid, без ContentControl.
Game.cs
csharpusing Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Framework.WpfInterop; using MonoGame.Framework.WpfInterop.Input; namespace ShooterGame.Backend.Models.Game { public class ShooterGameCore : WpfGame { private IGraphicsDeviceService _graphicsDeviceManager; private SpriteBatch _spriteBatch; private WpfKeyboard _keyboard; private WpfMouse _mouse; protected override void Initialize() { _graphicsDeviceManager = new WpfGraphicsDeviceService(this); _keyboard = new WpfKeyboard(this); _mouse = new WpfMouse(this); base.Initialize(); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); } protected override void Update(GameTime gameTime) { base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); _spriteBatch.Begin(); // здесь потом рисуешь объекты _spriteBatch.End(); base.Draw(gameTime); } } }
GameWindow.xaml
xml<Window x:Class="ShooterGame.Backend.Models.Game.GameWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Игра" Height="600" Width="900" WindowStyle="ToolWindow" WindowStartupLocation="CenterScreen"> <Grid x:Name="GameHost" /> </Window>
GameWindow.xaml.cs
csharpusing System; using System.Windows; namespace ShooterGame.Backend.Models.Game { public partial class GameWindow : Window { private ShooterGameCore? _game; public GameWindow() { InitializeComponent(); Loaded += GameWindow_Loaded; Closed += GameWindow_Closed; } private void GameWindow_Loaded(object sender, RoutedEventArgs e) { _game = new ShooterGameCore(); GameHost.Children.Add(_game); } private void GameWindow_Closed(object sender, EventArgs e) { if (_game != null) { GameHost.Children.Remove(_game); _game.Dispose(); _game = null; } } } }
MainWindow.xaml.cs
csharpusing System.Windows; using ShooterGame.Backend.Models.Game; namespace ShooterGame { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { GameWindow gameWindow = new GameWindow(); gameWindow.Show(); } } }
Главное: не называй класс просто Game. Назови ShooterGameCore, GameCore, ShooterMonoGame — как угодно, но не Game, чтобы не конфликтовать с Microsoft.Xna.Framework.Game.