Змейка на c windows form

  • Subject:
    C# Tutorials
  • Learning Time: 2 hours

Hi Welcome to this tutorial. This tutorial is an updated version of the C# Snakes game tutorial we have done before on the website.  This video tutorial will explain all of the core components needed to make this classic game in windows form in visual studio with C# programming language. We wont be using any oher game frameworks to make this work however we will be using some OOP programming practices in this tutorial. Everything you need to know and how to use them inside of the game effectively is explained in the tutorial.

Lesson objectives –

  1. Make a full snakes game in visual studio
  2. Using custom classes to load and reload default settings
  3. Using custom classes to draw the snake and food to the game
  4. Working with Keyboard controls
  5. Keeping score and high score in the current game session
  6. Able to take snap of the game when it ends
  7. Starting and Restarting the game to replay

Full Video Tutorial on How to make the classic snake game in Visual Studio with C#

 
Download Snake Game Project Tutorial on MOOICT GitHub
 

Source Code –

Circle Class –

This class will help us draw circles to the form, we will use this one to determine the X and Y position of the circles in the screen. This will be used for both the SNAKE and the Food objects inside of the game.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Classic_Snakes_Game_Tutorial___MOO_ICT
{
    class Circle
    {
        public int X { get; set; }
        public int Y { get; set; }

        public Circle()
        {
            X = 0;
            Y = 0;
        }
    }
}

Settings Class

This is the settings class for the game. This class will be used to load up the default settings for the game objects only. It will load the size in height and width of the circles that will be drawn to the project also the direction the snake should be moving when the game initially loads.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Classic_Snakes_Game_Tutorial___MOO_ICT
{
    class Settings
    {

        public static int Width { get; set; }
        public static int Height { get; set; }

        public static string directions;

        public Settings()
        {
            Width = 16;
            Height = 16;
            directions = "left";
        }
    }
}

Main Form C# Scripts

This is the main form1.cs code. In this script we have given all of the instructions necessary for the game and how it will behave when the game loads up. We have several events in the code below for example the Key Down, Key Up, Timer,  2 Click Events and a Paint Event. Also we have 3 custom functions such as Restart Game, Game Over and Eat Food function for the snake.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Drawing.Imaging; // add this for the JPG compressor

namespace Classic_Snakes_Game_Tutorial___MOO_ICT
{
    public partial class Form1 : Form
    {

        private List<Circle> Snake = new List<Circle>();
        private Circle food = new Circle();

        int maxWidth;
        int maxHeight;

        int score;
        int highScore;

        Random rand = new Random();

        bool goLeft, goRight, goDown, goUp;


        public Form1()
        {
            InitializeComponent();

            new Settings();
        }

        private void KeyIsDown(object sender, KeyEventArgs e)
        {

            if (e.KeyCode == Keys.Left && Settings.directions != "right")
            {
                goLeft = true;
            }
            if (e.KeyCode == Keys.Right && Settings.directions != "left")
            {
                goRight = true;
            }
            if (e.KeyCode == Keys.Up && Settings.directions != "down")
            {
                goUp = true;
            }
            if (e.KeyCode == Keys.Down && Settings.directions != "up")
            {
                goDown = true;
            }



        }

        private void KeyIsUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Left)
            {
                goLeft = false;
            }
            if (e.KeyCode == Keys.Right)
            {
                goRight = false;
            }
            if (e.KeyCode == Keys.Up)
            {
                goUp = false;
            }
            if (e.KeyCode == Keys.Down)
            {
                goDown = false;
            }
        }

        private void StartGame(object sender, EventArgs e)
        {
            RestartGame();
        }

        private void TakeSnapShot(object sender, EventArgs e)
        {
            Label caption = new Label();
            caption.Text = "I scored: " + score + " and my Highscore is " + highScore + " on the Snake Game from MOO ICT";
            caption.Font = new Font("Ariel", 12, FontStyle.Bold);
            caption.ForeColor = Color.Purple;
            caption.AutoSize = false;
            caption.Width = picCanvas.Width;
            caption.Height = 30;
            caption.TextAlign = ContentAlignment.MiddleCenter;
            picCanvas.Controls.Add(caption);

            SaveFileDialog dialog = new SaveFileDialog();
            dialog.FileName = "Snake Game SnapShot MOO ICT";
            dialog.DefaultExt = "jpg";
            dialog.Filter = "JPG Image File | *.jpg";
            dialog.ValidateNames = true;

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                int width = Convert.ToInt32(picCanvas.Width);
                int height = Convert.ToInt32(picCanvas.Height);
                Bitmap bmp = new Bitmap(width, height);
                picCanvas.DrawToBitmap(bmp, new Rectangle(0,0, width, height));
                bmp.Save(dialog.FileName, ImageFormat.Jpeg);
                picCanvas.Controls.Remove(caption);
            }





        }

        private void GameTimerEvent(object sender, EventArgs e)
        {
            // setting the directions

            if (goLeft)
            {
                Settings.directions = "left";
            }
            if (goRight)
            {
                Settings.directions = "right";
            }
            if (goDown)
            {
                Settings.directions = "down";
            }
            if (goUp)
            {
                Settings.directions = "up";
            }
            // end of directions

            for (int i = Snake.Count - 1; i >= 0; i--)
            {
                if (i == 0)
                {

                    switch (Settings.directions)
                    {
                        case "left":
                            Snake[i].X--;
                            break;
                        case "right":
                            Snake[i].X++;
                            break;
                        case "down":
                            Snake[i].Y++;
                            break;
                        case "up":
                            Snake[i].Y--;
                            break;
                    }

                    if (Snake[i].X < 0)
                    {
                        Snake[i].X = maxWidth;
                    }
                    if (Snake[i].X > maxWidth)
                    {
                        Snake[i].X = 0;
                    }
                    if (Snake[i].Y < 0)
                    {
                        Snake[i].Y = maxHeight;
                    }
                    if (Snake[i].Y > maxHeight)
                    {
                        Snake[i].Y = 0;
                    }


                    if (Snake[i].X == food.X && Snake[i].Y == food.Y)
                    {
                        EatFood();
                    }

                    for (int j = 1; j < Snake.Count; j++)
                    {

                        if (Snake[i].X == Snake[j].X && Snake[i].Y == Snake[j].Y)
                        {
                            GameOver();
                        }

                    }


                }
                else
                {
                    Snake[i].X = Snake[i - 1].X;
                    Snake[i].Y = Snake[i - 1].Y;
                }
            }
            picCanvas.Invalidate();
        }

        private void UpdatePictureBoxGraphics(object sender, PaintEventArgs e)
        {
            Graphics canvas = e.Graphics;

            Brush snakeColour;

            for (int i = 0; i < Snake.Count; i++)
            {
                if (i == 0)
                {
                    snakeColour = Brushes.Black;
                }
                else
                {
                    snakeColour = Brushes.DarkGreen;
                }

                canvas.FillEllipse(snakeColour, new Rectangle
                    (
                    Snake[i].X * Settings.Width,
                    Snake[i].Y * Settings.Height,
                    Settings.Width, Settings.Height
                    ));
            }


            canvas.FillEllipse(Brushes.DarkRed, new Rectangle
            (
            food.X * Settings.Width,
            food.Y * Settings.Height,
            Settings.Width, Settings.Height
            ));
        }

        private void RestartGame()
        {
            maxWidth = picCanvas.Width / Settings.Width - 1;
            maxHeight = picCanvas.Height / Settings.Height - 1;

            Snake.Clear();

            startButton.Enabled = false;
            snapButton.Enabled = false;
            score = 0;
            txtScore.Text = "Score: " + score;

            Circle head = new Circle { X = 10, Y = 5 };
            Snake.Add(head); // adding the head part of the snake to the list

            for (int i = 0; i < 100; i++)
            {
                Circle body = new Circle();
                Snake.Add(body);
            }

            food = new Circle { X = rand.Next(2, maxWidth), Y = rand.Next(2, maxHeight)};

            gameTimer.Start();

        }

        private void EatFood()
        {
            score += 1;

            txtScore.Text = "Score: " + score;

            Circle body = new Circle
            {
                X = Snake[Snake.Count - 1].X,
                Y = Snake[Snake.Count - 1].Y
            };

            Snake.Add(body);

            food = new Circle { X = rand.Next(2, maxWidth), Y = rand.Next(2, maxHeight) };
        }

        private void GameOver()
        {
            gameTimer.Stop();
            startButton.Enabled = true;
            snapButton.Enabled = true;

            if (score > highScore)
            {
                highScore = score;

                txtHighScore.Text = "High Score: " + Environment.NewLine + highScore;
                txtHighScore.ForeColor = Color.Maroon;
                txtHighScore.TextAlign = ContentAlignment.MiddleCenter;
            }
        }
    }
}

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

1
branch

0
tags


Code

  • Use Git or checkout with SVN using the web URL.

  • Open with GitHub Desktop

  • Download ZIP

Latest commit

Files

Permalink

Failed to load latest commit information.

Type

Name

Latest commit message

Commit time

C# Tutorial — Create a Classic Snakes Game in Visual Studio UPDATED

The classic snake game project is now updated and added with some extra features to boost your game play and power up your C# programming skills using the Windows Form Framework in Visual Studio.

From the last project the following were added to the new and updated one

  1. You can go through the walls from up/down/left/right and appear on the other side with reversed speed
  2. You can Take keep the score and save a highscore from any game plays session
  3. You can take snap shot of the game and save it as JPG file with the Score and Highscore as a caption in the image
  4. Start and Restart game with a button on run time

Link to the Website —

C# Tutorial – How to make a Classic Snakes Game with Windows form and Visual Studio [Updated]

Full Video Tutorial on the official Channel —

YouTube Video for Classic Snake Game Tutorial in C#

About

Create the fun classic snakes game using C# in Windows Form. We will be using little elements of OOP programming to make this project, the code, project files and video tutorial is all presented with this project here

Topics

Resources

Readme

License

Apache-2.0 license

Activity

Stars

4
stars

Watchers

1
watching

Forks

7
forks

_Eldar_

45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

1

Змейка

30.07.2010, 01:31. Показов 63215. Ответов 18

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Привет всем. Написал змейку, принимаю критику по коду и оптимизации:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
 
 
namespace Snake
{
    public partial class Form1 : Form
    {
        
        Graphics g; // графическая поверхность        
        List<Rectangle> snake = new List<Rectangle>(); // змейка        
        public enum Course {UP, DOWN, LEFT, RIGHT };    
        public int lengthSnake; // длина змейки
        public bool existsFood; // существование еды на поле
        Rectangle food = new Rectangle(); // поле с едой
 
        List<Rectangle> empty_filed = new List<Rectangle>(); // список незанятых ячеек
        public Random rnd = new Random();
 
        public Course course; // направление движения     
 
        public int addX, addY; // смещение координат
 
        public Form1()
        {
            InitializeComponent();
            course = Course.LEFT; // начальное направление
            // добавляем в змейку три начальных сегмента
            snake.Add(new Rectangle(200, 0, 20, 20));
            snake.Add(new Rectangle(220, 0, 20, 20));
            snake.Add(new Rectangle(240, 0, 20, 20));
            
            timer1.Interval = 200; // устанавливаем таймер            
            lengthSnake = 3; // начальная длина змейки            
            existsFood = false; // еды нет
 
            g = this.CreateGraphics();// создаем графичскую поверхность
 
            DrawSnake();  // рисуем змейку             
 
            DrawFood(); // рисуем еду
            
            timer1.Enabled = true;  // запускаем таймер         
 
        } // Form1        
 
        private void Form1_TickTimer(object sender, EventArgs e)
        {
            Refresh(); // очищаем экран
 
            timer1.Enabled = false; // останавливаем таймер
            
            // в зависимости от выбранного направления
            // наращиваем координаты головы (snake[0])
            if (course == Course.UP) 
            {
                addX = 0;
                addY = -20;
            }
 
            if (course == Course.DOWN)
            {
                addX = 0;
                addY = 20;
            }
 
            if (course == Course.LEFT)
            {
                addX = -20;
                addY = 0;
            }
 
            if (course == Course.RIGHT)
            {
                addX = 20;
                addY = 0;
            }
 
            Rectangle prev_segment;
            Rectangle next_segment;
 
            prev_segment = snake[0]; // запоминаем значение старой головы
                                     // чтобы присвоить его след. сегменту
            // по циклу присваиваем значение предыдущего сегмента следующему
            for (int i = 0; i < snake.Count - 1; i++)
            {
                
                if (i == 0)
                {
                    
                    snake[i] = new Rectangle(
                        snake[i].X + addX,
                        snake[i].Y + addY,
                        20, 20);
                }                
                if (!(snake[i + 1].IsEmpty))
                {
                    next_segment = snake[i + 1];
                    snake[i + 1] = prev_segment;
                    prev_segment = next_segment;
                }
            }
            // если голова "съела" еду
            if (snake[0] == food)
            {
                snake.Add(food); // добавляем сегмент с коор-ами еды
                                 // этот сегмент пройдет через всю змейку
                                 // и "прицепиться" в конце
                lengthSnake++; // увеличиваем длину змейки
                existsFood = false; // еды нет
                // увеличиваем скорость через каждые 7 сегментов змейки
                if ((lengthSnake % 7 == 0) &&
                    (timer1.Interval > 50) )
                {
                    timer1.Interval -= 30;                    
                }
            }
            // Проверка на проигрыш
            if (// выход за границу игрового поля
                (snake[0].X < 0 || snake[0].X > 240 ||
                snake[0].Y < 0 || snake[0].Y > 240)
                ||
                // проверка на самосъедение
                EatMySelf())
            {
                MessageBox.Show("Игра закончена\n Длина змейки равна: " + lengthSnake.ToString());
                this.Close();
                return;
            }
 
            DrawSnake();  // рисуем змейку 
            DrawFood(); // рисуем еду             
 
            timer1.Enabled = true; // запускаем таймер
 
        } // Form1_TickTimer
 
        // метод меняет направление, в зависимости от нажатой стрелки
        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Up && course != Course.DOWN)
            {
                course = Course.UP;
            }
            if (e.KeyCode == Keys.Down && course != Course.UP)
            {
                course = Course.DOWN;
            }
            if (e.KeyCode == Keys.Left && course != Course.RIGHT)
            {
                course = Course.LEFT;
            }
            if (e.KeyCode == Keys.Right && course != Course.LEFT)
            {
                course = Course.RIGHT;
            }
 
        } // Form1_KeyDown
 
 
        private void DrawSnake()
        {
            for (int i = 0; i < snake.Count; i++)
            {
                if (i == 0) // рисуем голову 
                    g.FillRectangle(Brushes.Green, snake[i]);
                else // рисуем оставшиеся сегменты
                    g.FillRectangle(Brushes.Black, snake[i]);
                g.DrawRectangle(Pens.Green, snake[i]);
            }
        } //DrawSnake
 
        private void DrawFood()
        {   
            // если еда съедена - русуем новую
            if (!existsFood)
            {
                for (int i = 0; i < 13; i++)
                {
                    for (int j = 0; j < 13; j++)
                    {
                        Rectangle temp = new Rectangle(i * 20, j * 20, 20, 20);
                        // заполняем список свободных сегментов игрового поля
                        if (snake.IndexOf(temp) == -1)
                        {
                            empty_filed.Add(temp);
                        }
                    }
                }
                // находим случайным образом свободную ячейку
                food = empty_filed[rnd.Next(0, empty_filed.Count - 1)];
                empty_filed.Clear(); // очищаем список свободных сегментов игрового поля
                g.FillRectangle(Brushes.Red, food); // рисуем еду
                existsFood = true; //еда есть 
            }
            else
            {   // иначе рисуем старую еду
                g.FillRectangle(Brushes.Red, food); 
            }
        }
        // проверка на самосъедение
        private bool EatMySelf()
        {
            int count = 0; // количество сегментов равных голове
            foreach (Rectangle t in snake)
            {
                if (t == snake[0]) count++;
            }
            // еда совпадает с координатами головы (это не считаем за самосъедение)
            if (count > 1 && food != snake[0])
                return true;
            else
                return false;
        } // EatMySelf            
    }
}



3



Programming

Эксперт

94731 / 64177 / 26122

Регистрация: 12.04.2006

Сообщений: 116,782

30.07.2010, 01:31

Ответы с готовыми решениями:

Змейка
Помогите пожалуйста, как сделать так чтобы змейка съедала фрукт, какбы написанно, но что-то идет не…

Консольная змейка
Я уже устал и отчаялся. Никак не могу понять как нарисовать горизонтальную линию.
У меня есть…

Змейка в консоли
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using…

Консольная змейка
Сижу дома на летних каникулах, развлекаюсь..
З,Ы, особо забавный 3-й режим игры с клоном)) он…

18

637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 01:59

2

В общем неплохо, но мерцание изображения выглядит не очень. Советую делать отрисовку на PictureBox’е и использовать двойную буферизацию. А так, то играбельно.



0



45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 02:00

 [ТС]

3

Alex Sabaka, что за двойная буфферизация?



0



Alex_Sabaka

637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 02:09

4

Из вики:

Двойная буферизация — в информатике метод подготовки данных, обеспечивающий возможность отдачи готового результата, без прерывания процесса подготовки следующего результата.

А если своими словами, то это когда ты рисуешь всю графику на одном изображении(второй буфер), а когда отрисовка окончена, то копируешь содержимое первого изображения во второе(основной буфер). На практике это можно сделать так:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        Bitmap view;
        Graphics graph;
 
        protected override void OnShown(EventArgs e)
        {
            view = new Bitmap(1024, 768);
            graph = Graphics.FromImage((Image)view);
            viewBox.Image = (Image)view;
            base.OnShown(e);
        }
 
        private void timer1_Tick(object sender, EventArgs e)
        {
            graph.Clear(Color.White);
            graph.FillRectangle(Brushes.Aqua, new Rectangle(10, 10, 500, 500));
            viewBox.Image = (Image)view;
        }

Где view — второй буыер, а viewBox — компонент типа PictureBox(основной буфер).



0



45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 02:14

 [ТС]

5

Alex Sabaka, пока не сильно понятно( , разберусь со временем.



0



637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 02:18

6

Скажите что именно не понятно, может помогу.

ЗЫ. Про двойную буферизацию хорошо написано в книге «Занимательное программирование» Мозговой М.



0



45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 02:35

 [ТС]

7

Alex Sabaka, зачем копировать старое изображение в буфер, оно ведь уже не нужно



0



Alex_Sabaka

637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 02:49

8

Возьмём тот пример который я привёл.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        Bitmap view; // второй буфер, который НЕ отображается на экране
        Graphics graph;
 
        protected override void OnShown(EventArgs e)
        {
            view = new Bitmap(1024, 768); // инициализируем второй буфер
            graph = Graphics.FromImage((Image)view); // и графику для него
            viewBox.Image = (Image)view; // это можно и не писать
            base.OnShown(e);
        }
 
        private void timer1_Tick(object sender, EventArgs e)
        {
            graph.Clear(Color.White); // очищаем второй буфер от старой графики
            graph.FillRectangle(Brushes.Aqua, new Rectangle(10, 10, 500, 500)); // рисуем новую
            viewBox.Image = (Image)view; // и выводим содержимое второго буфера на экран
        }

А если всю графику рисовать непосредственно на viewBox.Image, или(что еще хуже) на форме, то при отрисовке будут появляться артефакты в виде мерцания изображения. Теперь ясно для чего копировать старое изображение? Иначе же оно никак не выводиться на экране, и мы будем смотреть на белый фон.



0



_Eldar_

45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 03:14

 [ТС]

9

Alex Sabaka,

этот метод тоже делегируется какому то событию? если да то какому?:

C#
1
2
3
4
5
6
7
protected override void OnShown(EventArgs e)
        {
            view = new Bitmap(1024, 768); 
            graph = Graphics.FromImage((Image)view); 
            viewBox.Image = (Image)view; // что это делает?
            base.OnShown(e); // это можно пояснить?
        }

Добавлено через 1 минуту
viewBox — это компонент PictureBox?

Добавлено через 1 минуту
и за чем ты перегружаешь его (метод)?



0



В астрале

Эксперт С++

8049 / 4806 / 655

Регистрация: 24.06.2010

Сообщений: 10,562

30.07.2010, 03:21

10

Шикарно. Понравилось)



0



637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 03:24

11

1. Этот метод вызывается при первом показе формы, это все можно было сделать и в конструкторы формы, но у меня уже было так, а менять лень.
2. Да, viewBox — это PictureBox
3. Перезагружаю в коде потому что лень брать мышку и лезть в свойства формы.



0



_Eldar_

45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 03:38

 [ТС]

12

Alex Sabaka,
так. Разбираем метод:

C#
1
2
3
4
5
6
7
8
9
10
private void timer1_Tick(object sender, EventArgs e)
        {
            graph.Clear(Color.White); // очищаем второй буфер от старой графики
            // Graphics graph -это второй буфер?
            // выше ты обявил Bitmap view; // второй буфер, который НЕ отображается на экране
            // не понятно какой их них первый а какой второй
            graph.FillRectangle(Brushes.Aqua, new Rectangle(10, 10, 500, 500)); 
            // Здесь вывод буфера, которой  "НЕ отображается на экране"
            viewBox.Image = (Image)view; // и выводим содержимое второго буфера на экран
        }

чето я запутался



0



Alex_Sabaka

637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 03:45

13

C#
1
2
3
4
            graph.Clear(Color.White); // очищаем второй буфер от старой графики
            // Graphics graph -это второй буфер?
            // выше ты обявил Bitmap view; // второй буфер, который НЕ отображается на экране
            // не понятно какой их них первый а какой второй

Второй буфер это «Bitmap view;», а первый — «viewBox.Image». «Graphics graph» — это для работы с графикой, то есть для отображения примитивов на view.

C#
1
2
            // Здесь вывод буфера, которой  "НЕ отображается на экране"
            viewBox.Image = (Image)view; // и выводим содержимое второго буфера на экран

Не вывод, а копирование содержимого второго буфера в основной(первый), и отображается на экране первый буфер, а не второй.



1



_Eldar_

45 / 30 / 11

Регистрация: 31.10.2009

Сообщений: 200

30.07.2010, 04:16

 [ТС]

14

Лучший ответ Сообщение было отмечено как решение

Решение

Alex Sabaka, Я понял так(конкретно твой пример), при запуске формы создается второй буфер, при срабатывании таймера второй буфер очищается, затем на нем же рисуется прямоугольник, затем буфер копируется в певый и выволится на экран. Так я понял? В чем выигрыш, то? замена картинки viewBox.Image будет происходить без мерцания?

Добавлено через 15 минут
Alex Sabaka, Ну действительно не мерцает))), спасибо, вот обновленный код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
 
 
namespace Snake
{
    public partial class Form1 : Form
    {
        Bitmap view;
        Graphics g; // графическая поверхность        
        List<Rectangle> snake = new List<Rectangle>(); // змейка        
        public enum Course {UP, DOWN, LEFT, RIGHT };    
        public int lengthSnake; // длина змейки
        public bool existsFood; // существование еды на поле
        Rectangle food = new Rectangle(); // поле с едой
 
        List<Rectangle> empty_filed = new List<Rectangle>(); // список незанятых ячеек
        public Random rnd = new Random();
 
        public Course course; // направление движения     
 
        public int addX, addY; // смещение координат
 
        public Form1()
        {
            InitializeComponent();
            course = Course.LEFT; // начальное направление
            // добавляем в змейку три начальных сегмента
            snake.Add(new Rectangle(200, 0, 20, 20));
            snake.Add(new Rectangle(220, 0, 20, 20));
            snake.Add(new Rectangle(240, 0, 20, 20));
            
            timer1.Interval = 200; // устанавливаем таймер            
            lengthSnake = 3; // начальная длина змейки            
            existsFood = false; // еды нет
 
            g = this.CreateGraphics();// создаем графичскую поверхность
 
            DrawSnake();  // рисуем змейку             
 
            DrawFood(); // рисуем еду
            
            timer1.Enabled = true;  // запускаем таймер         
 
        } // Form1        
 
        private void Form1_TickTimer(object sender, EventArgs e)
        {
            //Refresh(); // очищаем экран
 
            g.Clear(this.BackColor);
 
            timer1.Enabled = false; // останавливаем таймер
            
            // в зависимости от выбранного направления
            // наращиваем координаты головы (snake[0])
            if (course == Course.UP) 
            {
                addX = 0;
                addY = -20;
            }
 
            if (course == Course.DOWN)
            {
                addX = 0;
                addY = 20;
            }
 
            if (course == Course.LEFT)
            {
                addX = -20;
                addY = 0;
            }
 
            if (course == Course.RIGHT)
            {
                addX = 20;
                addY = 0;
            }
 
            Rectangle prev_segment;
            Rectangle next_segment;
 
            prev_segment = snake[0]; // запоминаем значение старой головы
                                     // чтобы присвоить его след. сегменту
            // по циклу присваиваем значение предыдущего сегмента следующему
            for (int i = 0; i < snake.Count - 1; i++)
            {
                
                if (i == 0)
                {
                    
                    snake[i] = new Rectangle(
                        snake[i].X + addX,
                        snake[i].Y + addY,
                        20, 20);
                }                
                if (!(snake[i + 1].IsEmpty))
                {
                    next_segment = snake[i + 1];
                    snake[i + 1] = prev_segment;
                    prev_segment = next_segment;
                }
            }
            // если голова "съела" еду
            if (snake[0] == food)
            {
                snake.Add(food); // добавляем сегмент с коор-ами еды
                                 // этот сегмент пройдет через всю змейку
                                 // и "прицепиться" в конце
                lengthSnake++; // увеличиваем длину змейки
                existsFood = false; // еды нет
                // увеличиваем скорость через каждые 7 сегментов змейки
                if ((lengthSnake % 7 == 0) &&
                    (timer1.Interval > 50) )
                {
                    timer1.Interval -= 30;                    
                }
            }
            // Проверка на проигрыш
            if (// выход за границу игрового поля
                (snake[0].X < 0 || snake[0].X > 240 ||
                snake[0].Y < 0 || snake[0].Y > 240)
                ||
                // проверка на самосъедение
                EatMySelf())
            {
                MessageBox.Show("Игра закончена\n Длина змейки равна: " + lengthSnake.ToString());
                this.Close();
                return;
            }
 
            DrawSnake();  // рисуем змейку 
            DrawFood(); // рисуем еду   
 
            pictureBox1.Image = (Image)view;
 
            timer1.Enabled = true; // запускаем таймер
 
        } // Form1_TickTimer
 
        // метод меняет направление, в зависимости от нажатой стрелки
        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Up && course != Course.DOWN)
            {
                course = Course.UP;
            }
            if (e.KeyCode == Keys.Down && course != Course.UP)
            {
                course = Course.DOWN;
            }
            if (e.KeyCode == Keys.Left && course != Course.RIGHT)
            {
                course = Course.LEFT;
            }
            if (e.KeyCode == Keys.Right && course != Course.LEFT)
            {
                course = Course.RIGHT;
            }
 
        } // Form1_KeyDown
 
 
        private void DrawSnake()
        {
            for (int i = 0; i < snake.Count; i++)
            {
                if (i == 0) // рисуем голову 
                    g.FillRectangle(Brushes.Green, snake[i]);
                else // рисуем оставшиеся сегменты
                    g.FillRectangle(Brushes.Black, snake[i]);
                g.DrawRectangle(Pens.Green, snake[i]);
            }
        } //DrawSnake
 
        private void DrawFood()
        {   
            // если еда съедена - русуем новую
            if (!existsFood)
            {
                for (int i = 0; i < 13; i++)
                {
                    for (int j = 0; j < 13; j++)
                    {
                        Rectangle temp = new Rectangle(i * 20, j * 20, 20, 20);
                        // заполняем список свободных сегментов игрового поля
                        if (snake.IndexOf(temp) == -1)
                        {
                            empty_filed.Add(temp);
                        }
                    }
                }
                // находим случайным образом свободную ячейку
                food = empty_filed[rnd.Next(0, empty_filed.Count - 1)];
                empty_filed.Clear(); // очищаем список свободных сегментов игрового поля
                g.FillRectangle(Brushes.Red, food); // рисуем еду
                existsFood = true; //еда есть 
            }
            else
            {   // иначе рисуем старую еду
                g.FillRectangle(Brushes.Red, food); 
            }
        }
        // проверка на самосъедение
        private bool EatMySelf()
        {
            int count = 0; // количество сегментов равных голове
            foreach (Rectangle t in snake)
            {
                if (t == snake[0]) count++;
            }
            // еда совпадает с координатами головы (это не считаем за самосъедение)
            if (count > 1 && food != snake[0])
                return true;
            else
                return false;
        } // EatMySelf     
 
        private void Form1_Shown(object sender, EventArgs e)
        {
            view = new Bitmap(270, 270); // инициализируем второй буфер
            g = Graphics.FromImage((Image)view); // и графику для него
        }       
    }
}

Добавлено через 4 минуты
Получается, вся фишка в том что на экран выводтся уже готовые картинки, а у меня было так, что какждый сегмент змейки выводился поочердно и из — за этого были мерцания



4



1319 / 992 / 127

Регистрация: 08.12.2009

Сообщений: 1,299

30.07.2010, 12:09

15

1) в свойствах формы надо поставить DoubleBuffered = true;
2) (snake[0].X < 0 || snake[0].X > 240 || snake[0].Y < 0 || snake[0].Y > 240) у меня работает неправильно, хоть и форсированно задал размер клиентской области. в общем надо параметризовать ширину и высоту кратными ширине и высоте «сегментов» змеи и при загрузке уже выставлять размер формы, или же, как это сделано в других змейках — предложить выбрать размер (мал, сред, бол) и отрисовать границы поля на форме отдельно
3) поля классов предпочтительнее (обязательно в 99% случаев) делать закрытыми (private)
4) ну и самое главное — один раз создавать Graphics — противозаконно. лучше все делать через метод OnPaint, а по таймеру делать Invalidate

А вообще говоря, мне очень даже понравилось.



0



Alex_Sabaka

637 / 498 / 77

Регистрация: 28.07.2010

Сообщений: 895

30.07.2010, 14:37

16

C#
1
2
3
4
5
6
7
8
9
10
11
        private void Form1_TickTimer(object sender, EventArgs e)
        {
            //...
 
            timer1.Enabled = false; // останавливаем таймер
            
            //...
 
            timer1.Enabled = true; // запускаем таймер
 
        } // Form1_TickTimer

Зачем это?

И еще:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        // проверка на самосъедение
        private bool EatMySelf()
        {
            int count = 0; // количество сегментов равных голове
            foreach (Rectangle t in snake)
            {
                if (t == snake[0]) count++;
            }
            // еда совпадает с координатами головы (это не считаем за самосъедение)
            if (count > 1 && food != snake[0])
                return true;
            else
                return false;
        } // EatMySelf

Эффективнее сделать так:

C#
1
2
3
4
5
6
7
        // проверка на самосъедение
        private bool EatMySelf()
        {
               for (int i=1; i < snake.Count; i++)
                       if (snake[0] == snake[i]) return true;
               return false;
        } // EatMySelf



1



1319 / 992 / 127

Регистрация: 08.12.2009

Сообщений: 1,299

30.07.2010, 17:54

17

Лучший ответ Сообщение было отмечено как решение

Решение

мой лютый ответ — «сколопендра»=))) яблоко очень тяжело поймать, да и её потерять=)))



2



WARen1k

19.12.2010, 11:45

18

Мне очень понравилась реализация. Все просто и со вкусом, только не пойму как можно, например, увеличить размер поля?

6 / 6 / 1

Регистрация: 16.02.2011

Сообщений: 45

02.05.2011, 15:30

19

Не могу запустить змейку, помогите.
Скрин:

Змейка

timer 1 не существует в данном контексте



0



  • Remove From My Forums
  • Question

  • Hi 

    I need help the make a classic snake game in windows form application. The most guides for the snake game are in console so they don’t really help. If you could post the basic code (or all of the code) i would be very thankfull.  

    • Moved by

      Tuesday, March 17, 2015 5:03 PM
      Winforms related

Answers

  • This is a very broad question. For that reason, I will provide a broad answer. If you need code specific help, please post code specific questions. 

    This is my way of thinking and it may not be the best solution:

    1- You need to create a virtual grid on your form. 

    Let say you have a 800 x 800 form and you will divide this into 20 x 20 grids. 

    Each grid will be 40 px width and height. 

    Create a grid class to hold x, y values. 

    2- For your snake you need to create a snake class. 

    Snake class will hold the length of the snake.

    3 — Game class:

    This will be the main container class that holds snake, timer (if available), bates for the snake, game logic.

    4- Game logic Class:

    in your game logic class, define how your snake behaves for example when a user clicks up button while snakes direction is left.

    5- Form elements:

    Each grid will hold a picture box and your picture box will be filled with different elements such as:

    — plain

    — bate

    — snake

    6 — Collusion detection:

    This will be a part of game logic but it will be better if you have separate collusion detection class.

    • Proposed as answer by
      Carl Cai
      Wednesday, March 18, 2015 4:28 AM
    • Marked as answer by
      Carl Cai
      Wednesday, March 25, 2015 5:51 AM

  • Hi,

    Please try to follow
    Val10’s ideas and write it by yourself.

    You also can put some keywords like «C# snake  game » in the search engine.

    It will shows many resluts about it.  You could choose to refer someone to get some hints.

    This project seems meet your requirements. Please take a look

    #Snake Game in C#

    http://www.c-sharpcorner.com/uploadfile/prink_mob/snake-game-in-C-Sharp/

    If all of these are not what you want, please submit a sample request to
    http://code.msdn.microsoft.com/windowsapps/site/requests.

    If you encountered any issue when implementing it, you could open new thread
    for each specific issue.

    Best regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.

    Click
    HERE to participate the survey.

    • Edited by
      Kristin Xie
      Tuesday, March 17, 2015 8:17 AM
    • Proposed as answer by
      Carl Cai
      Wednesday, March 18, 2015 4:28 AM
    • Marked as answer by
      Carl Cai
      Wednesday, March 25, 2015 5:51 AM

    • Proposed as answer by
      Carl Cai
      Wednesday, March 18, 2015 4:28 AM
    • Marked as answer by
      Carl Cai
      Wednesday, March 25, 2015 5:51 AM
    • Proposed as answer by
      Carl Cai
      Wednesday, March 18, 2015 4:28 AM
    • Marked as answer by
      Carl Cai
      Wednesday, March 25, 2015 5:51 AM

stuff and things

Menu

  • Home
  • His
  • Hers
  • Contact

Browse: Home
» 2014
» February
» Classic Snake Game using C# Windows Forms

Classic Snake Game using C# Windows Forms

February 21, 2014 · by admin · in His

snake_screenshot Everybody likes the classic snake game. Here is a simple C# implementation built for funsies.

admin

Chris Grgich-Tran is a Development Manager from Brisbane, Australia. When he’s not dad-ing it up, he works with some very talented people.

More Posts

Follow Me:
TwitterFacebookLinkedInGoogle PlusFlickrYouTube

Comments

Tags: .net, c#, code, games, windows forms

Comments are closed, but trackbacks and pingbacks are open.

← Subtle waiting effect for buttons

Unlocking the BRZ Sat Nav Unit – Eclipse AVN 827GA →

Google Ads

About

A blog about stuff as most blogs generally are.

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Copyright © 2023 grgichtran

Powered by WordPress and Origin

  • Звуковой сигнал каждый час windows
  • Златогорье как запустить на windows 10
  • Звуковой плеер для windows 10
  • Звуковой микшер для windows 10
  • Зеркальное отображение камеры windows 10