接上一篇结构不好的C#微信飞机大战,这个版本改成面向对象的了,并且加入了工厂模式(不知道算不算是工厂模式,可以看下编代码)。
700+行代码完全模拟微信飞机大战。功能效果基本全部实现。 ps:我的敌军飞机还会斜着走,微信的只会直着走,并且我的这个属于变态版本,就像Dota和Imba。 有图有真相:class Entity { public string Name { get; set; } public int X { get; set; } public int Y { get; set; } public int Width { get; set; } public int Height { get; set; } public int SpeedY { get; set; } public int SpeedX { get; set; } public System.Drawing.Bitmap Image{get;set;} public override string ToString() { return Name; } }
1.飞机类,游戏中只有一个
class Plane :Entity { public int Level { get; set; } public Plane(string name, int x, int y, int width, int height, int speedX,int speedY, Bitmap bmp) { this.Name = name; this.X = x; this.Y = y; this.Width = width; this.Height = height; this.SpeedX = speedX; this.SpeedY = speedY; this.Image = bmp; this.Level = 1; } public void LevelUp() { Level++; } }
2.子弹类,飞机发射的子弹
class Bullet:Entity { public Bullet(string name, int x, int y, int width, int height, int speedX, int speedY, Bitmap bmp) { this.Name = name; this.X = x; this.Y = y; this.Width = width; this.Height = height; this.SpeedX = speedX; this.SpeedY = speedY; this.Image = bmp; } }
3.敌军类,分不同大小的敌军飞机
class Enemy:Entity { public int HP { get; set; } public Enemy(string name, int x, int y, int width, int height, int speedX, int speedY, int hp, Bitmap bmp) { this.Name = name; this.X = x; this.Y = y; this.Width = width; this.Height = height; this.SpeedX = speedX; this.SpeedY = speedY; this.HP = hp; this.Image = bmp; } }
4.奖励类,空中会掉落奖励物品,多出了两个属性StnTimes停留时间,Counter计数器,Counter好像没用到。
class Reward:Entity { public int StnTimes { get; set; } public int Counter { get; set; } public Reward(string name, int x, int y, int width, int height, int speedX, int speedY, int stnTimes, Bitmap bmp) { this.Name = name; this.X = x; this.Y = y; this.Width = width; this.Height = height; this.SpeedX = speedX; this.SpeedY = speedY; this.StnTimes = stnTimes; this.Image = bmp; this.Counter = 0; } }
5.爆炸效果类,即在子弹击破敌军飞机位置处绘制一个爆炸效果。参数偏差较多,没继承Entity。
class Explosion { public int X { get; set; } public int Y { get; set; } public int Width { get; set; } public int Height { get; set; } public int StnTimes { get; set; } public int Counter { get; set; } public System.Drawing.Bitmap[] Images { get; set; } public Explosion(int x, int y, int stnTimes, System.Drawing.Bitmap[] bmp) { this.X = x; this.Y = y; this.StnTimes = stnTimes; this.Images = bmp; this.Counter = 0; } }
6.实体构建的工厂类,重量级类出现了:求赐教 //解释下细节:image_item是图片集合,分别是不同的物体图像,详见文件resource/plane.xml。需要一个标识ImgItem来表示不同图片的名字。工厂类在使用前需要初始化这些图片,所以写了个InitFactory(string xmlPath)方法,剩下的方法就是生产不同实体的静态方法了,因为参数实在太多,所以参数大多数在这里直接设置定值/或者随机数。
class EntityFactory { enum ImgItem { boom_add = 1, bomb_icon = 2, bullet_0 = 3, bullet_1 = 4, bullet_add = 5, enemy_b = 6, enemy_m = 7, enemy_s = 8, explosion_01 = 9, explosion_02 = 10, explosion_03 = 11, hero_1 = 12, hero_2 = 13, pause_button = 14, resume_button = 15, smoke_01 = 16, smoke_02 = 17 }; static Bitmap[] image_item = new Bitmap[18]; public static void InitFactory(string xmlPath) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(xmlPath); XmlNode parent = xmlDoc.SelectSingleNode("TextureAtlas"); Bitmap bmp = new Bitmap(Bitmap.FromFile(System.IO.Path.GetDirectoryName(xmlPath) + "/" +((XmlElement)parent).GetAttribute("imagePath"))); XmlNodeList nodes = parent.ChildNodes; int i = 1; foreach (XmlNode xn in nodes) { XmlElement xe = (XmlElement)xn; string name = xe.GetAttribute("name"); int x = int.Parse(xe.GetAttribute("x")); int y = int.Parse(xe.GetAttribute("y")); int width = int.Parse(xe.GetAttribute("width")); int height = int.Parse(xe.GetAttribute("height")); Bitmap subBmp = new Bitmap(width, height); Graphics g = Graphics.FromImage(subBmp); g.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(x, y, width, height), GraphicsUnit.Pixel); image_item[i] = subBmp; i++; } } public static Plane GenPlane(string style) { if ("normal".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.hero_1]; return new Plane("small", 250,500, tempBmp.Width, tempBmp.Height,0, 0, tempBmp); } else if ("super".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.hero_2]; return new Plane("mid", 350, 700, tempBmp.Width, tempBmp.Height, 0,0, tempBmp); } return null; } public static Enemy GenEnemy(string size,int speedBase) { if ("small".Equals(size)) { Bitmap tempBmp = image_item[(int)ImgItem.enemy_s]; return new Enemy("small", new Random().Next(450)+50, 0, tempBmp.Width, tempBmp.Height,newRandom().Next(10000)%5-2, new Random().Next(10000)%4+2+speedBase,1,tempBmp); } else if ("mid".Equals(size)) { Bitmap tempBmp = image_item[(int)ImgItem.enemy_m]; return new Enemy("mid", new Random().Next(450) + 50, 0, tempBmp.Width, tempBmp.Height, newRandom().Next(10000) % 5 - 2, new Random().Next(10000) % 4 + 1+speedBase, 5, tempBmp); } else if ("big".Equals(size)) { Bitmap tempBmp = image_item[(int)ImgItem.enemy_b]; return new Enemy("big", new Random().Next(450) + 50, 0, tempBmp.Width, tempBmp.Height, newRandom().Next(10000) % 3 - 1, new Random().Next(10000) % 3 + 1+speedBase, 20, tempBmp); } return null; } public static Bullet GenBullet(string style,int p_x,int p_y) { if ("red".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.bullet_0]; return new Bullet("small", p_x, p_y, tempBmp.Width, tempBmp.Height,0, 20, tempBmp); } else if ("blue".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.bullet_1]; return new Bullet("mid", p_x, p_y, tempBmp.Width, tempBmp.Height,0, 20, tempBmp); } return null; } public static Reward GenReward(string style, int p_x, int p_y) { if ("bullet_add".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.bullet_add]; return new Reward("bullet_add", p_x, p_y, tempBmp.Width, tempBmp.Height, new Random().Next(10000) % 5 - 2, 3,5000, tempBmp); } else if ("boom_add".Equals(style)) { Bitmap tempBmp = image_item[(int)ImgItem.boom_add]; return new Reward("boom_add", p_x, p_y, tempBmp.Width, tempBmp.Height, new Random().Next(10000) % 5 - 2, 3,5000, tempBmp); } return null; } public static Bitmap GetBoomIcon() { return image_item[(int)ImgItem.bomb_icon]; } public static Explosion GenExplosion(string style, int p_x, int p_y) { if ("small".Equals(style)) { Bitmap[] tempBmp = { image_item[(int)ImgItem.explosion_01], image_item[(int)ImgItem.explosion_02], image_item[(int)ImgItem.explosion_03] , image_item[(int)ImgItem.explosion_02],image_item[(int)ImgItem.explosion_01]}; return new Explosion(p_x, p_y, 300, tempBmp); } else if ("mid".Equals(style)) { Bitmap[] tempBmp = { image_item[(int)ImgItem.explosion_01] }; return new Explosion(p_x, p_y, 500, tempBmp); } else if ("big".Equals(style)) { Bitmap[] tempBmp = { image_item[(int)ImgItem.explosion_01] }; return new Explosion(p_x, p_y, 500, tempBmp); } return null; } }
7.音效播放类,封装在DXPlay中,用到DirectX和DirectSound,在Windows下找到引用就行了。很好地解决了异步同时播放多个音频。
class DXPlay { private string musicPath; Form1 form; public DXPlay(Form1 form,string musicPath) { this.form = form; this.musicPath = musicPath; } public void Play() { SecondaryBuffer secBuffer;//缓冲区对象 Device secDev;//设备对象 secDev = new Device(); secDev.SetCooperativeLevel(form, CooperativeLevel.Normal);//设置设备协作级别 ,当窗体失去焦点时,音频停止播放。 secBuffer = new SecondaryBuffer(musicPath, secDev);//创建辅助缓冲区 secBuffer.Play(0, BufferPlayFlags.Default);//设置缓冲区为默认播放 } delegate void DelegatePlay(); public void ThreadPlay() { Thread t = new Thread(new ThreadStart(CorssThreadPlay)); t.Start(); } public void CorssThreadPlay()//异步播放 { if (form.InvokeRequired) { DelegatePlay dp = new DelegatePlay(CorssThreadPlay); form.Invoke(dp); } else { SecondaryBuffer secBuffer;//缓冲区对象 Device secDev;//设备对象 secDev = new Device(); secDev.SetCooperativeLevel(form, CooperativeLevel.Normal);//设置设备协作级别 ,当窗体失去焦点时,音频停止播放。 secBuffer = new SecondaryBuffer(musicPath, secDev);//创建辅助缓冲区 secBuffer.Play(0, BufferPlayFlags.Default);//设置缓冲区为默认播放 } } }
8.主窗体Form,所有显示窗体效果,鼠标键盘事件监听+处理,实体物体生成,各种条件检测(碰撞检测),图形绘制(50 fps不知道为什么全屏会卡,而且光绘图就耗费了十几毫秒,网上有说用硬件加速,不解!),各种参数控制(时间控制,游戏中各种物体生成速度控制,界面大小,积分,难度,等级,等等)。参数值我就不一个一个解释了,因为都是些实际运行后反复改正的最后得到合适的值。
public partial class Form1 : Form { Plane plane; Timer t_draw; List<Enemy> enemy_lsit = new List<Enemy>(); List<Bullet> bullet_lsit = new List<Bullet>(); List<Explosion> explosion_list = new List<Explosion>(); List<Reward> reward_list = new List<Reward>(); int score = 0; int boom_count = 5; bool pause = false; Bitmap background; public Form1() { //this.SetStyle(ControlStyles.UserPaint, true);//没搞懂这个双缓冲区绘图有什么作用,加不加没效果 //this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. //this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); // 双缓冲 this.StartPosition = FormStartPosition.CenterScreen; this.Load += new System.EventHandler(this.Form1_Load); this.MouseMove += new MouseEventHandler(Form1_MouseMove); this.MouseClick += new MouseEventHandler(Form1_MouseClick); this.KeyPress += new KeyPressEventHandler(Form1_KeyPress); InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { EntityFactory.InitFactory("resource/plane.xml"); background = new Bitmap(Image.FromFile(@"resource/bg_02.jpg")); plane = EntityFactory.GenPlane("normal"); this.Cursor.Dispose(); Cursor.Position = new Point(plane.X + this.Location.X, plane.Y + this.Location.Y); t_draw = new Timer(); t_draw.Interval = 20; send_interval = 100 / t_draw.Interval; block_interval = 260 / t_draw.Interval; reward_interval = 5000 / t_draw.Interval; t_draw.Tick += new EventHandler(t_draw_Tick); t_draw.Start(); } void Form1_MouseClick(object sender, MouseEventArgs e) { if (!pause && e.Button == MouseButtons.Right) { if (boom_count > 0) { boom_count--; for (int i = 0; i < enemy_lsit.Count; i++) { //socre ++ if (enemy_lsit[i].Name == "small") score += 1000; else if (enemy_lsit[i].Name == "mid") score += 6000; else if (enemy_lsit[i].Name == "big") score += 25000; //add to explosion explosion_list.Add(EntityFactory.GenExplosion("small", enemy_lsit[i].X, enemy_lsit[i].Y)); } new DXPlay(this, @"resource/BOMB3.wav").ThreadPlay(); enemy_lsit.Clear(); } } } void Form1_MouseMove(object sender, MouseEventArgs e) { if (!pause) { plane.X = e.X; plane.Y = e.Y; } } void Form1_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == ' ') { pause = !pause; if (pause) { this.Cursor = new Cursor (Cursors.Arrow.CopyHandle()); } else { this.Cursor.Dispose(); Cursor.Position = new Point(plane.X + this.Location.X, plane.Y + this.Location.Y); } } /*else if (e.KeyChar == 27) { this.WindowState = FormWindowState.Normal; } else if (e.KeyChar == '\r') { this.WindowState = FormWindowState.Maximized; }*/ } int block_time = 1; int block_interval = 0; int send_time = 0; int send_interval = 0; int reward_time = 1; int reward_interval = 0; int rwd_bullet_stnTime = 0; int backY = 800; //DateTime dis = DateTime.Now; private void t_draw_Tick(object sender, EventArgs e) { //Console.Write("\n ALLTime--->" + (DateTime.Now - dis)); if (pause) { this.CreateGraphics().DrawString("暂 停", new Font("微软雅黑", 22), Brushes.Red, new PointF(this.Width / 2 -30, this.Height / 2 - 50)); return; } ////////////////////////////////////////////////////////////////// /////////////////// ///////////////////////////// ////////////////// Create ////////////////////////////// ///////////////// /////////////////////////////// ////////////////////////////////////////////////////////////////// /*------send bullets-----*/ if (send_time > send_interval) { if (rwd_bullet_stnTime > 0) { bullet_lsit.Add(EntityFactory.GenBullet("blue", plane.X - 6, plane.Y - 50)); bullet_lsit.Add(EntityFactory.GenBullet("blue", plane.X + 6, plane.Y - 50)); rwd_bullet_stnTime -= t_draw.Interval * send_interval; } else { bullet_lsit.Add(EntityFactory.GenBullet("red", plane.X, plane.Y - 50)); } new DXPlay(this, @"resource/shoot.wav").ThreadPlay(); send_time = 0; } /*------generate enemy-----*/ if (block_time % block_interval == 0) { int speedBase = 0; if (block_interval < 2) speedBase = 1; if (block_interval < 5) speedBase = 2; else if (block_interval < 10) speedBase = 1; if (block_time % (block_interval * 20) == 0) { enemy_lsit.Add(EntityFactory.GenEnemy("big",speedBase)); } else if (block_time % (block_interval * 10) == 0) { enemy_lsit.Add(EntityFactory.GenEnemy("mid", speedBase)); } else { enemy_lsit.Add(EntityFactory.GenEnemy("small", speedBase)); } } /*-----reward-----*/ if (reward_time == reward_interval) { if (new Random().Next(10000) % 2 == 0) { reward_list.Add(EntityFactory.GenReward("bullet_add", new Random().Next(50, this.Width - 50), 0)); } else { reward_list.Add(EntityFactory.GenReward("boom_add", new Random().Next(50, this.Width - 50), 0)); } reward_time = 0; } send_time++; block_time++; reward_time++; ////////////////////////////////////////////////////////////////// /////////////////// ///////////////////////////// ////////////////// Judge ////////////////////////////// ///////////////// /////////////////////////////// ////////////////////////////////////////////////////////////////// /*-----plane level up-----*/ if (send_interval>0&&score > plane.Level * plane.Level * 50000) { plane.LevelUp();] send_interval--; } /*-----enemy lv up-----*/ if (block_interval > 1 && block_time % 300 == 300-1) { block_interval--; } /*-----enemy crash-----*/ for (int i = 0; i < enemy_lsit.Count; i++) { for (int j = 0; j < bullet_lsit.Count; j++) { if (Math.Abs(bullet_lsit[j].X - enemy_lsit[i].X) < (bullet_lsit[j].Width + enemy_lsit[i].Width) / 2 &&Math.Abs(bullet_lsit[j].Y - enemy_lsit[i].Y) < (bullet_lsit[j].Height + enemy_lsit[i].Height) / 2) { enemy_lsit[i].HP--; if (enemy_lsit[i].HP == 0)//explose { //socre ++ if (enemy_lsit[i].Name == "small") score += 1000; else if (enemy_lsit[i].Name == "mid") score += 6000; else if (enemy_lsit[i].Name == "big") score += 25000; //add to explosion explosion_list.Add(EntityFactory.GenExplosion("small", enemy_lsit[i].X, enemy_lsit[i].Y)); new DXPlay(this, @"resource/explosion.wav").ThreadPlay(); //remove both enemy_lsit.Remove(enemy_lsit[i]); bullet_lsit.Remove(bullet_lsit[j]); } else { //g.FillRectangle(Brushes.Red,new Rectangle(bullet_lsit[j].X,bullet_lsit[j].Y-bullet_lsit[j].Width/2,30,5)); bullet_lsit.Remove(bullet_lsit[j]); } break; } } } /*-----get reward-----*/ for (int i = 0; i < reward_list.Count; i++) { if (Math.Abs(plane.X - reward_list[i].X) < (plane.Width + reward_list[i].Width) / 2 && Math.Abs(plane.Y -reward_list[i].Y) < (plane.Height + reward_list[i].Height) / 2) { if (reward_list[i].Name == "bullet_add") { rwd_bullet_stnTime += reward_list[i].StnTimes; } else if (reward_list[i].Name == "boom_add") { boom_count++; } reward_list.Remove(reward_list[i]); } } /*-----plane crash-----*/ for (int i = 0; i < enemy_lsit.Count; i++) { bool isCrashed = false; if (Math.Abs(plane.X - enemy_lsit[i].X) < (plane.Width / 4 + enemy_lsit[i].Width) / 2 && Math.Abs(plane.Y -enemy_lsit[i].Y) < (plane.Height - 30 + enemy_lsit[i].Height) / 2) { isCrashed = true; } if (isCrashed) { t_draw.Stop(); this.CreateGraphics().DrawString("Game Over", new Font("微软雅黑", 22), Brushes.Red, new PointF(this.Width/ 2 - 100, this.Height / 2 - 50)); //enemy_lsit.Remove(enemy_lsit[i]); return; } } ////////////////////////////////////////////////////////////////// /////////////////// ///////////////////////////// ////////////////// Draw ////////////////////////////// ///////////////// /////////////////////////////// ////////////////////////////////////////////////////////////////// Bitmap bmp = new Bitmap(this.Width, this.Height); Graphics g = Graphics.FromImage(bmp); /*-----clear panel-----*/ g.Clear(this.BackColor); /*-----background-----*/ int img_count = 0; if (background.Width < this.Width) { Bitmap tempBg = new Bitmap(this.Width, 1600); while (background.Width * (img_count) < this.Width) { Graphics g_tempBg = Graphics.FromImage(tempBg); g_tempBg.DrawImage(background, background.Width * img_count, 0); g_tempBg.DrawImage(background, background.Width * img_count, 800); img_count++; } background = tempBg; } g.DrawImage(background, new Rectangle(0, 0, this.Width, this.Height), new Rectangle(0, backY, this.Width,this.Height), GraphicsUnit.Pixel); backY -= 2; if (backY < 0) backY = 800; /*------plane------*/ g.DrawImage(plane.Image, new Point(plane.X - plane.Width / 2, plane.Y - plane.Height / 2)); /*-----bullets-----*/ for (int i = 0; i < bullet_lsit.Count; i++) { g.DrawImage(bullet_lsit[i].Image, new Point(bullet_lsit[i].X - bullet_lsit[i].Width / 2, bullet_lsit[i].Y -bullet_lsit[i].Height / 2)); bullet_lsit[i].Y -= bullet_lsit[i].SpeedY; if (bullet_lsit[i].Y < -40) { bullet_lsit.Remove(bullet_lsit[i]); } } /*-----draw reward-----*/ for (int i = 0; i < reward_list.Count; i++) { g.DrawImage(reward_list[i].Image, new Point(reward_list[i].X - reward_list[i].Width / 2, reward_list[i].Y -reward_list[i].Height / 2)); reward_list[i].Y += reward_list[i].SpeedY; reward_list[i].X += reward_list[i].SpeedX; if (reward_list[i].Y > this.Height + 20) { reward_list.Remove(reward_list[i]); } } /*-----draw boom icon-----*/ Bitmap boom_icon = EntityFactory.GetBoomIcon(); if (boom_count > 0) { g.DrawImage(boom_icon, new Point(10, this.Height - 40 - boom_icon.Height)); g.DrawString("×" + boom_count, new Font("微软雅黑", 18), Brushes.RosyBrown, new Point(10 + boom_icon.Width,this.Height - 40 - boom_icon.Height)); } /*-----enemy-----*/ for (int i = 0; i < enemy_lsit.Count; i++) { g.DrawImage(enemy_lsit[i].Image, new Point(enemy_lsit[i].X - enemy_lsit[i].Width / 2, enemy_lsit[i].Y -enemy_lsit[i].Height / 2)); enemy_lsit[i].Y += enemy_lsit[i].SpeedY; enemy_lsit[i].X += enemy_lsit[i].SpeedX; if (enemy_lsit[i].X > this.Width || enemy_lsit[i].X < 0) { enemy_lsit[i].SpeedX = -enemy_lsit[i].SpeedX; } if (enemy_lsit[i].Y > this.Width + 20) { enemy_lsit.Remove(enemy_lsit[i]); } } /*-----draw explose-----*/ for (int i = 0; i < explosion_list.Count; i++) { Bitmap temp_explose = explosion_list[i].Images[explosion_list[i].Counter / (explosion_list[i].StnTimes /explosion_list[i].Images.Length)]; g.DrawImage(temp_explose, new Point(explosion_list[i].X - temp_explose.Width / 2, explosion_list[i].Y -temp_explose.Height / 2)); explosion_list[i].Counter += 24; if (explosion_list[i].Counter > explosion_list[i].StnTimes) explosion_list.Remove(explosion_list[i]); } /*-----score panel-----*/ g.DrawString("分数:" + score, new Font("微软雅黑", 14), Brushes.Green, new PointF(10, 10)); /*-----level panel-----*/ g.DrawString("等级:" + (send_interval == 1 ? "满级" : plane.Level.ToString()), new Font("微软雅黑", 14), Brushes.Green, new PointF(this.Width - 120, 10)); g.Dispose(); this.CreateGraphics().DrawImage(bmp, 0, 0); bmp.Dispose(); //dis = DateTime.Now; } }
以上就是该程序的所有代码,共计700+行代码。游戏挺占CPU的,可能是帧率问题。或者说是绘图问题,并且全屏情况下会卡顿,硬件加速正在研究。有好的意见请不吝提出。 就到此结束吧!以下是 游戏+源码 下载链接。 游戏:http://download.csdn.net/detail/y85171642/6527087 源码:http://download.csdn.net/detail/y85171642/6527099