C#坦克大战流程设计与源代码(1):基本对象类规划

小游戏坦克大战,看似是一个很小的程序。

但实际使用做起来,发现需要用到多种功能:

多线程并发与同步,事件触发,GDI绘图。

可以采用事件订阅 与事件触发的观察者模式。

从网络上下载声音文件 以及 各种坦克图片等素材。

实体分类如下:

1.有静止的地图元素【MapElement】:如墙体、水域、草丛、钢铁墙、老鹰等,为了区分不同的元素,同时加入地图元素类型枚举【MapElementCategory】。

2.有坦克【Tank】,又细分为玩家坦克【PlayerTank】和敌方坦克【EnemyTank】两种。玩家坦克可以升星,增加坦克等级枚举【PlayerTankLevel】。敌方坦克也有不同的种类【EnemyTankCategory】。

3.坦克可以攻击,产生子弹【Bullet】,子弹分玩家子弹和敌方子弹。这里用子弹类型枚举【BulletCategory】表示。

4.玩家子弹击中发光的敌方坦克【会掉落道具的】会产生道具【Prop】,每种道具都有不同的效果,因此增加道具枚举【PropCategory】。

5.子弹和坦克的移动需要有方向,增加方向枚举Direction.

6.以上的地图元素、坦克、子弹、道具都可以认为是一个矩形,需要考虑两个矩形的碰撞。因此建立抽象类矩形对象【RectangleObject】,所有对象都继承该抽象类。

整体类的属性源程序如下:

一、RectangleObject.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 【抽象类】矩形物体:所有的坦克,地图元素【墙、钢铁、草丛】,子弹,道具都是由一个填充图片的矩形构成
    /// 该类是所有地图资源的基类,主要用于判断两个地图资源是否碰撞(相交)
    /// </summary>
    public abstract class RectangleObject
    {
        public RectangleObject(Image image, int x, int y, int width, int height)
        {
            this.Image = image;
            this.X = x;
            this.Y = y;
            this.Width = width == 0 ? image.Width : width;
            this.Height = height == 0 ? image.Height : height;
        }

        /// <summary>
        /// 图片资源,一般是位图资源Bitmap
        /// </summary>
        public Image Image { get; set; }

        /// <summary>
        /// 起始X坐标
        /// </summary>
        public int X { get; set; }

        /// <summary>
        /// 起始Y坐标
        /// </summary>
        public int Y { get; set; }

        /// <summary>
        /// 宽度
        /// </summary>
        public int Width { get; set; }

        /// <summary>
        /// 高度
        /// </summary>
        public int Height { get; set; }

        /// <summary>
        /// 判断两个物体是否是相交的,
        /// 比如 子弹与敌方坦克是否碰撞,玩家坦克与道具是否碰撞,子弹与墙体进行碰撞等
        /// </summary>
        /// <param name="rectangleObject"></param>
        /// <returns></returns>
        public virtual bool IsIntersected(RectangleObject rectangleObject)
        {
            Rectangle rect = new Rectangle(X, Y, Width, Height);
            return rect.IntersectsWith(new Rectangle(rectangleObject.X, rectangleObject.Y, rectangleObject.Width, rectangleObject.Height));
        }

        /// <summary>
        /// 判断当前物体与一个矩形区域是否相交
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public virtual bool IsIntersected(Rectangle rectangle)
        {
            Rectangle rect = new Rectangle(X, Y, Width, Height);
            return rect.IntersectsWith(rectangle);
        }
    }
}
 

二、MapElement.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 地图元素
    /// </summary>
    public class MapElement : RectangleObject
    {
        /// <summary>
        /// 实例化地图元素
        /// </summary>
        /// <param name="image">位图资源</param>
        /// <param name="x">起始X坐标</param>
        /// <param name="y">起始Y坐标</param>
        /// <param name="elementCategory">元素类型</param>
        public MapElement(Image image, int x, int y, MapElementCategory elementCategory, int width = 0, int height = 0)
            : base(image, x, y, width, height)
        {
            this.ElementCategory = elementCategory;
        }

        /// <summary>
        /// 地图元素类型
        /// </summary>
        public MapElementCategory ElementCategory { get; set; }
    }
}
 

三、MapElementCategory.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 地图元素类型【枚举】
    /// 主要有:墙体、水域、草丛、钢铁墙等
    /// </summary>
    public enum MapElementCategory
    {
        /// <summary>
        /// 墙体:无法移动通过。被子弹攻击到将消失
        /// </summary>
        Wall = 0,
        /// <summary>
        /// 水域:无法移动通过,但子弹可以穿过去
        /// </summary>
        Water = 1,
        /// <summary>
        /// 草丛:可以移动通过,子弹可以穿过去
        /// </summary>
        Grass = 2,
        /// <summary>
        /// 钢铁墙:无法移动通过。可以抵挡所有敌方坦克的子弹。我方坦克达到3级【吃三个星星】后,击中钢铁将消失,我方坦克低于三级将抵挡子弹
        /// </summary>
        Steel = 3,
        /// <summary>
        /// 象征,符号。鹰,Boss。玩家需要守护鹰不被击中
        /// 子弹与鹰碰撞后,将游戏结束
        /// </summary>
        Symbol = 4
    }
}
 

四、Tank.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 分为玩家坦克 和 敌方坦克两种
    /// 是玩家坦克、敌方坦克的基类
    /// </summary>
    public class Tank : RectangleObject
    {
        public Tank(Image image, int tankIndex, int x, int y, int width = 60, int height = 60, int health = 1, int speed = 15, Direction direction = Direction.Up)
            : base(image, x, y, width, height)
        {
            this.TankIndex = tankIndex;
            this.Health = health;
            this.Speed = speed;
            this.Direction = direction;
        }

        /// <summary>
        /// 坦克唯一编号:主要用于区分是否是当前坦克
        /// </summary>
        public int TankIndex { get; set; }

        /// <summary>
        /// 生命值
        /// </summary>
        public int Health { get; set; }

        /// <summary>
        /// 移动速度:速度越快,同一时间内 坦克移动的位置就越远。也就是说:X坐标或者Y坐标偏移较大
        /// </summary>
        public int Speed { get; set; }

        /// <summary>
        /// 方向:上、下、左、右
        /// </summary>
        public Direction Direction { get; set; }

        /// <summary>
        /// 玩家坦克 和 敌方坦克 是否可以向前移动
        /// 玩家坦克 是手动控制,敌方坦克是自动
        /// </summary>
        /// <param name="MapWidth"></param>
        /// <param name="MapHeight"></param>
        /// <param name="mapElements"></param>
        /// <param name="tanks"></param>
        /// <returns></returns>
        public bool IsAllowMove(int MapWidth, int MapHeight, List<MapElement> mapElements, List<Tank> tanks)
        {
            bool allowMove = true;
            Rectangle rect = new Rectangle(0, 0, 0, 0);
            switch (this.Direction)
            {
                //如果到达边界,直接返回false
                case Direction.Up:
                    if (this.Y - this.Speed < 0)
                    {
                        return false;
                    }
                    rect = new Rectangle(this.X, this.Y - this.Speed, this.Width, this.Height);
                    break;
                case Direction.Down:
                    if (this.Y + this.Speed > MapHeight - this.Height)
                    {
                        return false;
                    }
                    rect = new Rectangle(this.X, this.Y + this.Speed, this.Width, this.Height);
                    break;
                case Direction.Left:
                    if (this.X - this.Speed < 0)
                    {
                        return false;
                    }
                    rect = new Rectangle(this.X - this.Speed, this.Y, this.Width, this.Height);
                    break;
                case Direction.Right:
                    if (this.X + this.Speed > MapWidth - this.Width)
                    {
                        return false;
                    }
                    rect = new Rectangle(this.X + this.Speed, this.Y, this.Width, this.Height);
                    break;
            }
            //如果与墙体、钢铁、老鹰、水域碰撞【可以穿过草丛】
            for (int i = 0; i < mapElements.Count; i++)
            {
                Rectangle rectElement = new Rectangle(mapElements[i].X, mapElements[i].Y, mapElements[i].Width, mapElements[i].Height);
                if (mapElements[i].ElementCategory == MapElementCategory.Steel || mapElements[i].ElementCategory == MapElementCategory.Symbol
                    || mapElements[i].ElementCategory == MapElementCategory.Wall || mapElements[i].ElementCategory == MapElementCategory.Water)
                {
                    if (rect.IntersectsWith(rectElement))
                    {
                        return false;
                    }
                }
            }
            //如果与坦克碰撞
            for (int i = 0; i < tanks.Count; i++)
            {
                Rectangle rectElement = new Rectangle(tanks[i].X, tanks[i].Y, tanks[i].Width, tanks[i].Height);
                if (this != tanks[i] && rect.IntersectsWith(rectElement))
                {
                    return false;
                }
            }
            return allowMove;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Tank))
                return false;
            return this.TankIndex == ((Tank)obj).TankIndex;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// 操作符重载,比较两个坦克是否是同一个,即TankIndex相同
        /// </summary>
        /// <param name="tank1"></param>
        /// <param name="tank2"></param>
        /// <returns></returns>
        public static bool operator ==(Tank tank1, Tank tank2)
        {
            if ((tank1 as object) == null)
                return (tank2 as object) == null;//引用基类object的比较操作符
            return tank1.TankIndex == tank2.TankIndex;
            //return tank1.Equals(tank2);
        }

        /// <summary>
        /// 操作符重载,比较两个坦克是否不是同一个,即TankIndex不同
        /// </summary>
        /// <param name="tank1"></param>
        /// <param name="tank2"></param>
        /// <returns></returns>
        public static bool operator !=(Tank tank1, Tank tank2)
        {
            return !(tank1 == tank2);
        }
    }
}
 

五、PlayerTank.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 玩家坦克
    /// </summary>
    public class PlayerTank : Tank
    {
        public PlayerTank(Image image, int tankIndex, int x, int y, int width = 60, int height = 60, int health = 1, int speed = 15, Direction direction = Direction.Up, PlayerTankLevel tankLevel = PlayerTankLevel.Initial)
            : base(image, tankIndex, x, y, width, height, health, speed, direction)
        {
            this.TankLevel = tankLevel;
        }

        /// <summary>
        /// 玩家坦克等级
        /// </summary>
        public PlayerTankLevel TankLevel { get; set; }

        /// <summary>
        /// 当前道具:玩家坦克 有定时道具 或者 无敌状态 或者 为鹰增加钢铁墙道具等
        /// </summary>
        public Prop CurrentProp { get; set; }

        /// <summary>
        /// 玩家坦克移动
        /// </summary>
        public void Move(int MapWidth, int MapHeight, List<MapElement> mapElements, List<Tank> tanks, Prop prop = null)
        {
            //坦克是否可以向前移动,如果不能向前移动,尝试转换方向
            //我方坦克在移动中是否碰撞道具,如果碰撞道具,道具将消失,我方坦克增加相应持续状态【生命值加强、火力加强、无敌等】
            if (IsAllowMove(MapWidth, MapHeight, mapElements, tanks))
            {
                switch (this.Direction)
                {
                    case Direction.Up:
                        this.Y = this.Y - this.Speed;
                        break;
                    case Direction.Down:
                        this.Y = this.Y + this.Speed;
                        break;
                    case Direction.Left:
                        this.X = this.X - this.Speed;
                        break;
                    case Direction.Right:
                        this.X = this.X + this.Speed;
                        break;
                }
                if (prop != null && this.IsIntersected(prop))
                {
                    Prop propTemp = prop;
                    System.Windows.Forms.MessageBox.Show($"玩家坦克与道具【{prop.PropCategory}】碰撞,将产生相应的事件");
                    this.CurrentProp = propTemp;
                    prop = null;
                }
            }
            else
            {
                System.Media.SystemSounds.Asterisk.Play();
            }
        }

        

        /// <summary>
        /// 坦克攻击:向玩家子弹列表增加数据
        /// </summary>
        public void Fire()
        {

        }
    }
}
 

六、EnemyTank.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 敌方坦克
    /// </summary>
    public class EnemyTank : Tank
    {
        public EnemyTank(Image image, int tankIndex, int x, int y, int width = 60, int height = 60, int health = 1, int speed = 15, Direction direction = Direction.Down, EnemyTankCategory tankCategory = EnemyTankCategory.Normal, bool isDropProps = false)
            : base(image, tankIndex, x, y, width, height, health, speed, direction)
        {
            this.TankCategory = tankCategory;
            this.IsDropProps = isDropProps;
        }

        /// <summary>
        /// 敌方坦克类型:快、慢、装甲
        /// </summary>
        public EnemyTankCategory TankCategory { get; set; }

        /// <summary>
        /// 是否掉落物品:当为true时,敌方坦克被玩家坦克的子弹击中,将随机产生一个道具。然后将该属性置为false
        /// </summary>
        public bool IsDropProps { get; set; }
    }
}
 

七、PlayerTankLevel.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 我方【玩家】坦克等级:攻击速度和子弹个数
    /// </summary>
    public enum PlayerTankLevel
    {
        /// <summary>
        /// 初始:攻击速度1.5秒发射一发子弹
        /// </summary>
        Initial = 0,
        /// <summary>
        /// 一星:攻击速度0.75秒发射一发子弹
        /// </summary>
        OneStar = 1,
        /// <summary>
        /// 二星:攻击速度0.75秒发射两发子弹
        /// </summary>
        TwoStar = 2,
        /// <summary>
        /// 三星:攻击速度0.75秒发射两发子弹,击中钢铁墙,钢铁墙将消失
        /// </summary>
        ThreeStar = 3
    }
}
 

八、EnemyTankCategory.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 敌方坦克类型:普通、快速、装甲
    /// </summary>
    public enum EnemyTankCategory
    {
        /// <summary>
        /// 普通坦克
        /// </summary>
        Normal = 1,
        /// <summary>
        /// 快速坦克:移动速度较快
        /// </summary>
        Quick = 2,
        /// <summary>
        /// 装甲坦克:生命值较多
        /// </summary>
        Armor = 3
    }
}
 

九、Direction.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 方向:坦克的移动方向 以及 子弹的前进方向
    /// </summary>
    public enum Direction
    {
        /// <summary>
        /// 上
        /// </summary>
        Up,
        /// <summary>
        /// 下
        /// </summary>
        Down,
        /// <summary>
        /// 左
        /// </summary>
        Left,
        /// <summary>
        /// 右
        /// </summary>
        Right
    }
}
 

十、Bullet.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 子弹:有方向,分玩家子弹、敌方子弹
    /// 以及是否可以消除墙体
    /// </summary>
    public class Bullet : RectangleObject
    {
        /// <summary>
        /// 初始化子弹
        /// </summary>
        /// <param name="image"></param>
        /// <param name="direction"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="speed"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public Bullet(Image image, int bulletIndex, Direction direction, int x, int y, int speed, int width = 60, int height = 60, BulletCategory bulletCategory = BulletCategory.PlayerBullet, bool canEliminateSteel = false)
            : base(image, x, y, width, height)
        {
            this.BulletIndex = bulletIndex;
            this.Direction = direction;
            this.Speed = speed;
            this.BulletCategory = bulletCategory;
            this.CanEliminateSteel = canEliminateSteel;
        }

        /// <summary>
        /// 子弹的唯一索引
        /// </summary>
        public int BulletIndex { get; }

        /// <summary>
        /// 子弹的方向
        /// </summary>
        public Direction Direction { get; }

        /// <summary>
        /// 子弹的速度,可以认为是 坦克的攻击速度。速度越快,同一时间内 子弹移动的位置就越远。
        /// 子弹的移动速度逻辑:因速度越大,相同时间内移动的距离越远。因此我们可以使用位移(距离、坐标)的大小来模拟表示
        /// 比如每隔50ms,坐标的变化来表示速度的快慢即可。
        /// </summary>
        public int Speed { get; set; }        

        /// <summary>
        /// 子弹类型:玩家子弹还是敌方子弹
        /// </summary>
        public BulletCategory BulletCategory { get; set; }

        /// <summary>
        /// 子弹是否可以消除钢铁墙:玩家坦克等级3星时可以击穿钢铁
        /// </summary>
        public bool CanEliminateSteel { get; set; }

        /// <summary>
        /// 所有子弹【玩家子弹 和敌方子弹的合集】与其他物体的碰撞
        /// 注意,子弹与子弹的碰撞要递减处理
        /// </summary>
        /// <param name="mapElements"></param>
        /// <param name="bullets">玩家子弹 和 敌方子弹</param>
        /// <param name="enemyTanks"></param>
        public static void BulletHit(List<MapElement> mapElements, List<Bullet> bullets, List<EnemyTank> enemyTanks, int MapWidth, int MapHeight, Form form)
        {
            /*
             * 子弹在移动过程中一定会触发碰撞事件。子弹在移动过程中没有和其他物体碰撞,则最终会和地图边界碰撞
             * 玩家子弹碰撞事件:子弹与地图元素【墙体、钢铁】的碰撞,子弹与敌方子弹的碰撞,子弹与敌方坦克碰撞
             * 玩家子弹与【掉落道具的敌方坦克 碰撞时,将随机产生一个道具】
             * 
             * 敌方子弹碰撞事件:子弹与地图元素【墙体、钢铁】的碰撞,子弹与玩家子弹的碰撞,子弹与玩家坦克碰撞
            */
            for (int bulletIndex = 0; bulletIndex < bullets.Count; bulletIndex++)
            {
                Bullet bulletPlayer = bullets[bulletIndex];
                switch (bulletPlayer.Direction)
                {
                    case Direction.Up:
                        bulletPlayer.Y = bulletPlayer.Y - bulletPlayer.Speed;
                        break;
                    case Direction.Down:
                        bulletPlayer.Y = bulletPlayer.Y + bulletPlayer.Speed;
                        break;
                    case Direction.Left:
                        bulletPlayer.X = bulletPlayer.X - bulletPlayer.Speed;
                        break;
                    case Direction.Right:
                        bulletPlayer.X = bulletPlayer.X + bulletPlayer.Speed;
                        break;
                }
                if (bulletPlayer.X <= 0 || bulletPlayer.X >= MapWidth || bulletPlayer.Y <= 0 || bulletPlayer.Y >= MapHeight)
                {
                    //子弹到达边界 或者 触发碰撞事件,子弹从集合中清除。播放爆炸音效。显示爆炸图片");
                    Blast(form, bulletPlayer);
                    //赋值为null,用于清除集合中值为null的元素
                    bulletIndex--;
                    bullets.RemoveAt(bulletIndex);
                }

                List<MapElement> listRemoves = new List<MapElement>();
                //子弹与墙体、钢铁碰撞
                for (int wallIndex = 0; wallIndex < mapElements.Count; wallIndex++)
                {
                    MapElement mapElement = mapElements[wallIndex];
                    if (mapElement.ElementCategory == MapElementCategory.Wall && bulletPlayer.IsIntersected(mapElement))
                    {
                        //如果子弹与墙体的矩形 相交。则认为触发 碰撞。子弹可以消失 多个窗体
                        listRemoves.Add(mapElement);
                    }
                    else if (mapElement.ElementCategory == MapElementCategory.Steel && bulletPlayer.IsIntersected(mapElement))
                    {
                        if (bulletPlayer.CanEliminateSteel)
                        {
                            //如果当前子弹可以 清除钢铁墙
                            listRemoves.Add(mapElement);
                        }
                        Blast(form, bulletPlayer);
                    }
                    else if (mapElement.ElementCategory == MapElementCategory.Symbol && bulletPlayer.IsIntersected(mapElement))
                    {
                        //子弹与 老鹰碰撞,则游戏结束
                        MessageBox.Show("游戏结束Game Over");
                        Blast(form, bulletPlayer);
                    }
                }
                //清除相交的所有墙体
                for (int removeIndex = 0; removeIndex < listRemoves.Count; removeIndex++)
                {
                    //如果子弹与墙体的矩形 相交。则认为触发 碰撞。子弹可以消失 多个窗体
                    mapElements.Remove(listRemoves[removeIndex]);
                    if (removeIndex == 0)
                    {
                        //只清除一个 子弹
                        Blast(form, bulletPlayer);
                        //bulletPlayer = null;
                    }
                }
                //移除临时变量
                listRemoves.Clear();

                //子弹与敌方坦克的碰撞
                for (int tankIndex = 0; tankIndex < enemyTanks.Count; tankIndex++)
                {
                    if (bulletPlayer.IsIntersected(enemyTanks[tankIndex]))
                    {
                        if (enemyTanks[tankIndex].IsDropProps)
                        {
                            //如果掉落物品
                            Prop prop = new Prop(Properties.Resources.GEMSTAR, new Random().Next(0, MapWidth - 30), new Random().Next(0, MapHeight - 30), PropCategory.Star, 30, 30);
                            MessageBox.Show($"随机生成一个道具【{prop.PropCategory}】");
                        }
                        Blast(form, bulletPlayer);
                        //敌方坦克和子弹都消失:这里应该考虑敌方坦克的生命值减少1.变成0时爆炸死亡
                        //如果敌方坦克可以掉落道具
                        enemyTanks.RemoveAt(tankIndex);
                        //bulletPlayer = null;
                        break;
                    }
                }

                //子弹与敌方子弹的碰撞
                for (int i = bulletIndex + 1; i < bullets.Count; i++)
                {
                    if (((bullets[i].BulletCategory == BulletCategory.EnemyBullet && bulletPlayer.BulletCategory == BulletCategory.PlayerBullet)
                        || (bullets[i].BulletCategory == BulletCategory.PlayerBullet && bulletPlayer.BulletCategory == BulletCategory.EnemyBullet))
                       && bulletPlayer.IsIntersected(bullets[i]))
                    {
                        bullets.RemoveAt(i);
                        bulletIndex--;
                        break;
                    }
                }
                bullets.RemoveAt(bulletIndex);
                bulletIndex--;
            }
        }

        public static void EnemyBulletHit(Bullet bulletEnemy, List<MapElement> mapElements, List<Bullet> playerBullets, List<PlayerTank> playerTanks)
        {
            /*
             * 敌方子弹碰撞事件:子弹与地图元素【墙体、钢铁】的碰撞,子弹与玩家子弹的碰撞,子弹与玩家坦克碰撞
            */
        }

        /// <summary>
        /// 爆炸效果:子弹碰撞到坦克、墙体、钢铁墙、老鹰【子弹分我方子弹 和 敌方子弹】
        /// </summary>
        public static void Blast(Form form, Bullet bullet)
        {
            //爆炸音效
            System.Media.SoundPlayer sp = new System.Media.SoundPlayer(Properties.Resources.blast);
            sp.Play();
            //爆炸动画
            Task taskBlast = Task.Run(() =>
            {
                for (int index = 0; index < 25; index++)
                {
                    Image image = Properties.Resources.EXP1;
                    if (index >= 5)
                    {
                        image = Properties.Resources.EXP2;
                    }
                    else if (index >= 10)
                    {
                        image = Properties.Resources.EXP3;
                    }
                    else if (index >= 15)
                    {
                        image = Properties.Resources.EXP4;
                    }
                    else if (index >= 20)
                    {
                        image = Properties.Resources.EXP5;
                    }
                    form.CreateGraphics().DrawImage(image, bullet.X, bullet.Y);
                }
            });
            Task.WaitAll(taskBlast);
        }
    }
}
 

十一、BulletCategory.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 子弹类型:我方【玩家】子弹、敌方子弹
    /// </summary>
    public enum BulletCategory
    {
        /// <summary>
        /// 我方【玩家】子弹
        /// </summary>
        PlayerBullet,
        /// <summary>
        /// 敌方子弹
        /// </summary>
        EnemyBullet
    }
}
 

十二、Prop.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 道具:主要有 定时、炸弹、无敌、钢铁、升级星星等。
    /// 玩家子弹 击中 可以掉落道具的敌方坦克,可以产生一个道具。
    /// 为了减少bug,同一时刻地图上最多允许只有一个道具【上次的道具消失 或者 被新的道具覆盖】。
    /// 我方坦克移动时,碰撞到道具时,道具消失,同时将触发相应的事件
    /// </summary>
    public class Prop : RectangleObject
    {
        public Prop(Image image, int x, int y, PropCategory propCategory, int width = 0, int height = 0)
            : base(image, x, y, width, height)
        {
            this.PropCategory = propCategory;
        }

        /// <summary>
        /// 道具类别
        /// </summary>
        public PropCategory PropCategory { get; }
    }
}
 

十三、PropCategory.cs 源程序如下:

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

namespace SuperTankDemo
{
    /// <summary>
    /// 道具类别,不同的道具将触发不同的事件。主要有:星星,定时器,炸弹,箭头等
    /// </summary>
    public enum PropCategory
    {
        /// <summary>
        /// 升星:一级星增加攻击速度【子弹的移动速度轨迹变快】 二级将攻击两次、三级可以击穿钢铁墙
        /// </summary>
        Star = 1,
        /// <summary>
        /// 定时器。所有敌方坦克将静止,无法移动和攻击。持续15秒
        /// </summary>
        Timer = 2,
        /// <summary>
        /// 炸弹。当前所有敌方坦克 将爆炸消失
        /// </summary>
        Bomb = 3,
        /// <summary>
        /// 箭头,我方坦克碰撞后,老鹰四方将产生钢铁墙,也就是说无敌状态。持续15秒
        /// </summary>
        Arrow = 4,
        /// <summary>
        /// 帽子:我方坦克碰撞后 将无敌,持续15秒
        /// </summary>
        Hat = 5,
        /// <summary>
        /// 坦克礼物,复活次数加1
        /// </summary>
        TankGift = 6,
        /// <summary>
        /// 我方生命值增加1,可以抵挡一次攻击伤害
        /// </summary>
        Apple = 7,
        /// <summary>
        /// 我方坦克直接到最高级3级,相当于3个星星【Super Weapon:超级武器】
        /// </summary>
        Blow = 8
    }
}
 

PS:目前只定义和相关的类与属性,下一步将完善订阅事件。比如子弹与窗体的碰撞,玩家子弹与敌方坦克的碰撞,玩家坦克与道具的碰撞事件等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值