С# Windows Forms: игра-ходилка с фишками и кубиками
По просьбам трудящихся... а, достало. Dice & Chips Desktop Game, в сети не увидел образцов.
Игроки по очереди бросают шестигранный кубик и ходят на выпавшее количество пунктов. Переходы вперёд и назад, бонусные ходы и пропуски хода в комплекте, в общем, традиционно. Блокировка кнопок на время хода, псевдоанимация двумя способами, старым и новым.
Так как данные хранятся в массивах, нетрудно сделать свой аналог или чтение данных из файла.
Проект C# Windows Forms проверялся в актуальных сборках Visual Studio 2019/2022.
Более-менее проблемой оказалось "один вход и один выход" в основной функции MakeStep
, без этого способ 2 работал порой странно.
Код выглядит хуже, чем мог бы, потому что я писал это второпях, одновременно рассуждая на полуотвлечённые темы.
Файл Form1.cs
using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace DesktopGame { public partial class Form1 : Form { private PictureBox pictureBox1, pictureBox2, pictureBoxDice; private List<Point> pointList = new List<Point> (); private const int dx = 52; //смещение для второй фишки Point [] pointArray = new Point [] { //координаты полей new Point(10, 503), new Point(112, 550), new Point(213, 459), new Point(225, 393), new Point(152, 300), new Point(130, 196), new Point(243, 123), new Point(374, 167), new Point(505, 120), new Point(637, 187), new Point(642, 281), new Point(568, 369), new Point(472, 322), new Point(376, 346), new Point(357, 450), new Point(422, 523), new Point(536, 526), new Point(645, 501), new Point(739, 412), new Point(785, 310), new Point(796, 192), new Point(800, 69) }; Point [] goArray = new Point [] { //поля перехода X->Y new Point(5, 9), new Point(12, 8) }; Point [] bonusArray = new Point [] { //пропустить ход X->-1, бонус-ход X->1 new Point(6, -1), new Point(16, -1), new Point(11, 1) }; private int finishPosition; private int player1Position = 0, player2Position = 0, playerNumber = -1, cubeState = 0; private int [] skipped = new int [2] { 0, 0 }; private DiceAnimator dice = new DiceAnimator (); private Bitmap diceImage = new Bitmap (Properties.Resources.dice); private int mainDelay = 100; //основная задержка времени public Form1 () { InitializeComponent (); Image backgroundImage = Properties.Resources.field; this.BackgroundImage = backgroundImage; this.BackgroundImageLayout = ImageLayout.Stretch; this.ClientSize = new Size (backgroundImage.Width, backgroundImage.Height + menuStrip1.Height + statusStrip1.Height); pictureBoxDice = new PictureBox (); pictureBoxDice.Image = Properties.Resources.dice; pictureBoxDice.BackColor = Color.Transparent; pictureBoxDice.Location = new Point (815, 550); pictureBoxDice.Size = new Size (52, 60); this.Controls.Add (pictureBoxDice); pictureBoxDice.Hide (); foreach (Point point in pointArray) pointList.Add (point); finishPosition = pointArray.Length - 1; pictureBox1 = new PictureBox (); pictureBox1.Image = Properties.Resources.p1; pictureBox1.BackColor = Color.Transparent; pictureBox1.Location = pointArray [0]; pictureBox1.Size = new Size (52, 60); this.Controls.Add (pictureBox1); pictureBox2 = new PictureBox (); pictureBox2.Image = Properties.Resources.p2; pictureBox2.BackColor = Color.Transparent; pictureBox2.Location = new Point (pointArray [0].X + dx, pointArray [0].Y); pictureBox2.Size = new Size (52, 60); this.Controls.Add (pictureBox2); dice.DiceRollResult += diceAnimator; } private void новаяToolStripMenuItem_Click (object sender, EventArgs e) { player1Position = 0; player2Position = 0; playerNumber = 1; cubeState = 0; skipped [0] = skipped [1] = 0; pictureBox1.Location = pointArray [player1Position]; pictureBox2.Location = new Point (pointArray[player2Position].X + dx, pointArray[player2Position].Y); pictureBoxDice.Show (); toolStripStatusLabel1.Text = "Ходит игрок 1, бросаем кубик"; MakeStep (); } private void броситьКубикToolStripMenuItem_Click (object sender, EventArgs e) { if (playerNumber == -1) { menuStrip1.Items [0].Select (); //новаяToolStripMenuItem_Click (this, e); //или так вместо пред. строки return; } MakeStep (); } private async void MakeStep () { await Task.Delay (mainDelay); новаяToolStripMenuItem.Enabled = false; броситьКубикToolStripMenuItem.Enabled = false; dice.RollDice (); await Task.Delay (mainDelay); if (playerNumber == 1) { if (skipped [0] == 1) { skipped [0] = 0; playerNumber = 2; toolStripStatusLabel1.Text = "Игрок 1 пропускает ход, ходит игрок 2"; } else { player1Position += cubeState; if (player1Position >= finishPosition) { cubeState -= ( player1Position - finishPosition ); player1Position = finishPosition; } toolStripStatusLabel1.Text = String.Format ("Игрок 1 сходил на поле {0}", player1Position); for (int i = player1Position - cubeState; i <= player1Position; i++) { //Основной ход pictureBox1.Location = pointArray [i]; this.Refresh (); await Task.Delay (mainDelay); } foreach (Point point in goArray) { //Переходы по стрелкам if (point.X == player1Position) { int d = Math.Sign (point.Y - point.X); for (int i = point.X; ; i += d) { pictureBox1.Location = pointArray [i]; this.Refresh (); await Task.Delay (mainDelay); if (i == point.Y) break; } player1Position = point.Y; break; } } bool bonus = false; foreach (Point point in bonusArray) { //Пропуск хода и лишний ход if (point.X == player1Position) { if (point.Y == 1) { toolStripStatusLabel1.Text = "Игрок 1 получает бонусный ход!"; bonus = true; break; } else if (point.Y == -1) { skipped [0] = 1; break; } } } if (bonus) playerNumber = 1; else { if (player1Position == finishPosition && skipped [1] == 1) { playerNumber = -1; toolStripStatusLabel1.Text = "Второй игрок пропускает ход, выиграл игрок 1!"; pictureBoxDice.Hide (); } else { playerNumber = 2; toolStripStatusLabel1.Text = "Ходит игрок 2"; } } } } else if (playerNumber == 2) { bool bonus = false; if (skipped [1] == 1) { skipped [1] = 0; playerNumber = 1; toolStripStatusLabel1.Text = "Игрок 2 пропускает ход, ходит игрок 1"; } else { player2Position += cubeState; if (player2Position >= finishPosition) { cubeState -= ( player2Position - finishPosition ); player2Position = finishPosition; playerNumber = -1; } toolStripStatusLabel1.Text = String.Format ("Игрок 2 сходил на поле {0}", player2Position); for (int i = player2Position - cubeState; i <= player2Position; i++) { //Основной ход pictureBox2.Location = new Point (pointArray [i].X + dx, pointArray [i].Y); this.Refresh (); await Task.Delay (mainDelay); } foreach (Point point in goArray) { //Переходы по стрелкам if (point.X == player2Position) { int d = Math.Sign (point.Y - point.X); for (int i = point.X; ; i += d) { pictureBox2.Location = new Point (pointArray [i].X + dx, pointArray [i].Y); if (i == point.Y) break; } player2Position = point.Y; break; } } foreach (Point point in bonusArray) { //Пропуск хода и лишний ход if (point.X == player2Position) { if (point.Y == 1) { toolStripStatusLabel1.Text = "Игрок 2 получает бонусный ход!"; bonus = true; break; } else if (point.Y == -1) { skipped [1] = 1; break; } } } if (bonus) playerNumber = 2; else { if (player1Position >= finishPosition) playerNumber = -1; //Первый уже финишировал - игра закончена if (playerNumber == -1) { if (player1Position > player2Position) toolStripStatusLabel1.Text = "Ура, игрок 1 выиграл!"; else if (player1Position == player2Position) toolStripStatusLabel1.Text = "Ничья!"; else toolStripStatusLabel1.Text = "Ура, игрок 2 выиграл!"; pictureBoxDice.Hide (); } else { playerNumber = 1; toolStripStatusLabel1.Text = "Ходит игрок 1"; } } } } await Task.Delay (mainDelay); новаяToolStripMenuItem.Enabled = true; броситьКубикToolStripMenuItem.Enabled = true; this.Refresh (); } private void diceAnimator (int result) { cubeState = result; this.Refresh (); } private void Form1_Paint (object sender, PaintEventArgs e) { if (cubeState < 1 || cubeState > 6) return; Rectangle cropRect = new Rectangle ((cubeState - 1) * 52, 0, 52, 60); Graphics g = Graphics.FromImage (pictureBoxDice.Image); g.DrawImage (diceImage, new Rectangle (0, 0, cropRect.Width, cropRect.Height), cropRect, GraphicsUnit.Pixel); } /// } }
Файл DiceAnimator.cs
using System; using System.Threading; namespace DesktopGame { public class DiceAnimator { private Random random = new Random (); public delegate void DiceHandler (int result); public event DiceHandler DiceRollResult; public void RollDice () { for (int i = 0; i < 20; i++) { // Количество кадров анимации OnDiceRollResult (random.Next (1, 7)); // Получаем случайное число от 1 до 6 и генерируем событие для передачи номера грани Thread.Sleep (100); // Задержка между кадрами } } protected virtual void OnDiceRollResult (int result) { DiceRollResult.Invoke (result); // Передаём событие подписчику } } }
Скачать этот проект Visual Studio 2019 в архиве .zip, развернуть в новую папку (961 Кб)
Программа в работе (скриншот, качество ухудшено движком блога)
Хозяйке на заметку: Как вывести в PictureBox картинку с прозрачным фоном из ресурсов приложения?
Файл PNG сохраните как 32-битный (например, в XnView меню Изображение - 32-битное).
Укажите прозрачный фоновый цвет при добавлении изображения из ресурсов в
PictureBox
, кодом вродеpictureBox1 = new PictureBox (); pictureBox1.Image = Properties.Resources.myFile; pictureBox1.BackColor = Color.Transparent;Вместо
myFile
впишите имя, которые Вы назначили ресурсу (в Visual Studio - Обозреватель решений, Properties, двойной клик наResources.resx
, список "Добавить ресурс", "Добавить существующий ресурс").
15.05.2024, 18:36 [286 просмотров]