Форум программистов, компьютерный форум, киберфорум
Наши страницы
Storm23
Войти
Регистрация
Восстановить пароль
Рейтинг: 5.00. Голосов: 2.

Простейшая 2D игра на GDI+

Запись от Storm23 размещена 10.10.2015 в 13:34
Обновил(-а) Storm23 13.04.2016 в 10:00

Реализованы основные компоненты 2D игры: графика, обработка столкновений, управление с клавиатуры, анимированные спрайты, простейший AI, игровая механика.

Основные классы:
Коллайдер:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// коллайдер
    /// </summary>
    class Collider
    {
        public void Update()
        {
            var items = Game.Instance.Items.ToList();
 
            //проверка столкновения объектов
            for (int i = 0; i < items.Count; i++)
                for (int j = i + 1; j < items.Count; j++)
                {
                    var p1 = items[i].Location;
                    var p2 = items[j].Location;
                    var d = p1.Distance(p2);
                    var overlap = items[i].Radius + items[j].Radius - d;
                    if (overlap > 0)
                    {
                        //столкновение
                        var dir = new PointF(p1.X - p2.X, p1.Y - p2.Y);
                        var l = dir.Distance(PointF.Empty);
                        if (items[i].Mass > items[j].Mass)
                            items[j].Location = new PointF(p2.X - dir.X * overlap / l, p2.Y - dir.Y * overlap / l);//отскок
                        else
                            items[i].Location = new PointF(p1.X + dir.X * overlap / l, p1.Y + dir.Y * overlap / l);//отскок
                        //
                        items[i].OnCoollide(items[j]);
                        items[j].OnCoollide(items[i]);
                    }
                }
 
            //проверка столкновения со стеной
            for (int i = 0; i < items.Count; i++)
            {
                var p = items[i].Location;
                if (p.X < 0) p.X = 0;
                if (p.Y < 0) p.Y = 0;
                if (p.X > Game.Instance.FieldSize.Width) p.X = Game.Instance.FieldSize.Width;
                if (p.Y > Game.Instance.FieldSize.Height) p.Y = Game.Instance.FieldSize.Height;
 
                if(p != items[i].Location)
                {
                    //столкновение со стеной
                    items[i].Location = p;
                    items[i].OnCoollide(null);
                }
            }
        }
    }


Базовый класс для игровых объектов:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// базовый класс для игровых объектов
    /// </summary>
    [Serializable]
    abstract class GameItem
    {
        public PointF Location { get; set;}
        public int Radius { get; protected set; }
        public virtual void Update(float dt){;}
        public float Mass { get; set; }
        public virtual void OnCoollide(GameItem otherItem){;}
 
        public GameItem()
        {
            Game.Instance.Items.Add(this);
        }
 
        public void Remove()
        {
            Game.Instance.Items.Remove(this);
        }
 
        public virtual void Draw(Graphics gr)
        {
            if (Game.DebugMode)
                gr.DrawEllipse(Pens.Red, -Radius, -Radius, 2 * Radius, 2 * Radius);
        }
    }


Танк:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// танк
    /// </summary>
    [Serializable]
    class Tank : GameItem
    {
        public PointF Velocity { get; set; }
        public PointF TowerDirection { get; set; }
        public virtual float MaxSpeed { get; protected set; }
        public bool IsDead { get; set; }
 
        public Tank()
        {
            Location = Game.Instance.GetRndPoint();
            Radius = 25;
            MaxSpeed = 5f;
            Mass = 50;
        }
        
        public override void Update(float dt)
        {
            if(!IsDead)
                Location = new PointF(Location.X + Velocity.X * dt, Location.Y + Velocity.Y * dt);
        }
 
        public override void Draw(Graphics gr)
        {
            //корпус
            var img = Properties.Resources.TankBase;
            gr.ResetTransform();
            gr.TranslateTransform(Location.X, Location.Y);
            gr.RotateTransform(180 + 180 * (float)(Math.Atan2(Velocity.Y, Velocity.X) / Math.PI));
            gr.DrawImage(img, -50, -40);
 
            //башня
            img = Properties.Resources.TankTower;
            gr.ResetTransform();
            gr.TranslateTransform(Location.X, Location.Y);
            gr.RotateTransform(180 + 180 * (float)(Math.Atan2(TowerDirection.Y, TowerDirection.X) / Math.PI));
            gr.DrawImage(img, -50, -40);
 
            //если горим
            if(IsDead)
            {
                gr.TranslateTransform(-28, -28);
                Game.FireSprite.Draw(gr);
            }
 
            base.Draw(gr);
        }
 
        [NonSerialized]
        private DateTime lastFireTime;
 
        //сделать выстрел
        public void Fire()
        {
            if ((DateTime.Now - lastFireTime).TotalMilliseconds < 2500)
                return;
 
            if (IsDead)
                return;
 
            var speed = 13;
            var l = TowerDirection.Distance(PointF.Empty);
            var dir = new PointF(TowerDirection.X / l, TowerDirection.Y / l);
            var v = new PointF(speed * dir.X, speed * dir.Y);
            var bullet = new Bullet(){Velocity = v};
            var pad = (Radius + bullet.Radius + 20);
            bullet.Location = new PointF(Location.X + dir.X * pad, Location.Y + dir.Y * pad);
            lastFireTime = DateTime.Now;
        }
    }


Танк под управлением AI:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// танк под управлением ИИ
    /// </summary>
    [Serializable]
    class TankAI : Tank
    {
        private PointF targetPoint;
        private float timeToChangeTargetPoint;   
 
        public override void  Update(float dt)
        {
            
            timeToChangeTargetPoint -= dt;
 
            if (!IsDead)
                if (timeToChangeTargetPoint <= 0 || targetPoint.Distance(Location) < Radius)           
                {
                    //меняем направление движения
                    targetPoint = Game.Instance.GetRndPoint();
                    timeToChangeTargetPoint = 150;
 
                    var d = new PointF(targetPoint.X - Location.X, targetPoint.Y - Location.Y);
                    var l = d.Distance(PointF.Empty);
 
                    Velocity = new PointF(MaxSpeed*d.X/l, MaxSpeed*d.Y/l);
                }
 
            //башня - на игрока
            var p = Game.Instance.PlayerTank.Location;
            TowerDirection = new PointF(p.X - Location.X, p.Y - Location.Y);
 
            //стреляем постоянно
            if(!Game.Instance.PlayerTank.IsDead)
                if(Game.Rnd.Next(300) == 1)
                    Fire();
 
            base.Update(dt);
        }
 
        public override void OnCoollide(GameItem otherItem)
        {
            if (otherItem == null || otherItem.Mass >= Mass)
                timeToChangeTargetPoint = 0;//будем менять направление
 
            base.OnCoollide(otherItem);
        }
    }


Клавиатура (контроль состояния клавиш):
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// клавиатура
    /// </summary>
    public static class Keyboard
    {
        [Flags]
        private enum KeyStates
        {
            None = 0,
            Down = 1,
            Toggled = 2
        }
 
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern short GetKeyState(int keyCode);
 
        private static KeyStates GetKeyState(Keys key)
        {
            KeyStates state = KeyStates.None;
 
            short retVal = GetKeyState((int)key);
 
            if ((retVal & 0x8000) == 0x8000)
                state |= KeyStates.Down;
 
            if ((retVal & 1) == 1)
                state |= KeyStates.Toggled;
 
            return state;
        }
 
        public static bool IsKeyDown(Keys key)
        {
            return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
        }
 
        public static bool IsKeyToggled(Keys key)
        {
            return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
        }
    }


Главная форма:
Кликните здесь для просмотра всего текста
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
/// (C) Storm23, 2015
using System.Drawing;
using System.Windows.Forms;
 
namespace WindowsFormsApplication300
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
 
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
 
            BackgroundImage = Properties.Resources.grass;
 
            Text = "Tanks";
 
            new Game();
            MinimumSize = Game.Instance.FieldSize;
        }
 
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
 
            Game.MousePosition = PointToClient(MousePosition);
            Game.MouseButtons = MouseButtons;
 
            Game.Instance.Update();
            Game.Instance.Draw(e.Graphics);
 
            if (Game.Instance.PlayerTank.IsDead)
            {
                e.Graphics.ResetTransform();
                e.Graphics.DrawString("Press Space to new Game", Font, Brushes.White, 0, 0);
            }
 
            Invalidate();
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyData == Keys.Space)
                new Game();
        }
    }
}


Анимированный спрайт:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// Спрайт
    /// </summary>
    class Sprite
    {
        public Size FrameSize;
        public Image Image;
        public int CurrentFrame = -1;
        public int FrameCount;
 
        public Point CurrentFrameLocation
        {
            get
            {
                var framesPerRow = Image.Width / FrameSize.Width;
                var x = CurrentFrame % framesPerRow;
                var y = CurrentFrame / framesPerRow;
 
                return new Point(x * FrameSize.Width, y * FrameSize.Height);
            }
        }
 
        public void GotoNextFrame()
        {
            CurrentFrame = (CurrentFrame + 1) % FrameCount;
        }
 
        public void Draw(Graphics gr)
        {
            gr.DrawImage(Image, new Rectangle(Point.Empty, FrameSize), new Rectangle(CurrentFrameLocation, FrameSize), GraphicsUnit.Pixel);
        }
    }


Проект целиком: здесь.

Нажмите на изображение для увеличения
Название: tanks.png
Просмотров: 369
Размер:	461.9 Кб
ID:	3391
Размещено в Без категории
Просмотров 1094 Комментарии 1
Всего комментариев 1
Комментарии
  1. Старый комментарий
    можно обновить net framework проекта до 4.6.1 хотя бы?
    а то у вас там 4.0
    Запись от MuaddibFremen размещена 14.03.2019 в 13:16 MuaddibFremen вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.