目录
摘 要
Microsoft给开发人员一个选择-一专门用于. NET、具有新起点的语言,即Visual C# .NET。Microsoft 在正式场合把C#描述成一种简单、现代、面向对象、类型非常安全、派生于C和C++的编程语言。大多独立的评论员对其说法是“继承于C、C++和Java”。这种描述在技术上是非常精准的,但没有涉及到该种语言的真正优点。从语法上看,C#非常类同于C++和Java, 许多关键字都是相同的,C#也使用类似于C++和Java的块状结构,并用括号( {} )来标识代码块,用分号分隔各行语句。对C#代码的第一个印象是它非常类似于C++或Java代码。但在这些表面级的类似性后面,C#学习起来要比C++轻松得多,但比Java难一些。 其设计与现代开发工具的可适应性要比其他语言更高,它同时具有Visual Basic 的易用性、高性能以及C++的低级内存访问性。
在游戏开发领域,C#已经逐渐变成了使用人数最多的语言。不仅是Unity,还有一些新兴的游戏引擎也采用了C#语言(比如godot引擎)。
在游戏行业从业多年的开发者,大都掌握多门开发语言,可以对比评测。总体来看,C#在游戏开发领域口碑非常不错,具体在学习上手、招聘人才、编程规范、运行性能方面都做的十分平衡,没有明显的缺陷。
除游戏前端,C#和.net技术在游戏后端(游戏服务器)也获得了越来越广泛的应用,以前C#在linux上运行有一些问题,现在有了.net core,问题基本解决。
现在市面上大部分游戏都能盈利,相对比影视来讲,影视开发周期长,上线后能不能盈利还不好说。 现在是市场经济,都追求能快熟盈利的产品无论你在哪里,只要有手机,电脑,iPad,估计您都能看到玩游戏的人,VR虚拟游戏体验也非常受大众喜爱,特别是90后,00后,对游戏的接受喜爱程度都很高
这已经足以支撑游戏产业的蓬勃发展,所以,游戏在未来将会越来越火,这一行的薪资待遇自然是非常不错的。
关键词:c#;游戏;.net技术;
第1章 任务描述
在Visual Studio平台下基于C# 利用Form.NET编程独立游戏,塔防卡牌游戏《混战》,并完成以下的总体目标:
- 体现出面向对象思想的三大特性:封装,继承,多态。
- 自主设计,体现出卡牌,对战,角色扮演的游戏要素。
- 在游戏中加载基本的图片和音效资源,实现基础打击反馈。
- 至少有一个开始界面和游戏界面。
- 利用拖动和手动编程俩种方法来生成和显示控件。
- 利用属性事件填写和自定义委托俩种方法来完成事件绑定。
- 游戏完成后,录制视频,进行简单的游戏介绍和编程思想解释。
第二章 总体设计
2.1 框架结构
表2.1 框架结构
平台 | Window10 |
软件 | Visual Studio 2019 |
编程语言 | C# |
编程框架 | WinForm.NET窗体应用程序 |
2.2 目录结构
图2.1目录结构图
2.3 下载资源
2.3.1 图片资源
图2.2 图片资源
2.3.2 音效资源
图2.3 音效资源
第三章 详细设计
列出了主要类和主要方法名,以及类和方法的用途,方法体内容省略,进行大概理解整体框架结构。主要有Manager类,People类,role类组,Player类和俩个辅助类MCI类和PanelEnhanced类。
3.1 Manager类
用于统筹管理,进行显示和数据管理,对整个系统进行整合,关联所有类
class Manager
{
Form cform = new Form();
public int mouse_int = 0;//鼠标编号
public static List<People> roles = new List<People>();//角色图签,只是可读
public Player I_Player = new Player(1);//自己玩家
public Player E_Player = new Player(2);//敌人玩家
public List<People> road1 = new List<People>();//1路自己
public List<People> eroad1 = new List<People>();//1路敌人
public List<People> road2 = new List<People>();//2路自己
public List<People> eroad2 = new List<People>();//2路敌人
public List<People> road3 = new List<People>();//3路自己
public List<People> eroad3 = new List<People>();//3路敌人
int E_MaxValue = 0;
Button PB = new Button();//头像框
Button I_HPB = new Button();//血条框
Button E_HPB = new Button();//敌人血条框
public Manager(){...}//初始化-无参构造函数
public void Ini_Eintlist(){...}//敌人卡牌集初始化
public void AddRoles(){...}//图签初始化
public void GenerateRole(int sum,int team,int road){...}//生成角色,sun编号,team队伍,road生成道路
public void addform(Form _cform){...}//绑定当前窗口 初始化窗口控件位置 卡牌框组样式既位置
public void Move_or_Atk(){...}//检测移动攻击
//以下私有成员函数,用于简化前面公有函数所提取
private void random(int[] a,int n){...}//不重复随机整数数组生成,用于初始化敌人卡牌图签
private void change_mouse(object sender,EventArgs e){...}//点击角色卡牌,鼠标数值变化
private void Place_people(Object sender,EventArgs e){...}//放置角色卡牌
private void cards_down(){...} //点击其他地方 卡牌下移
private void Addroad(People p, int road){...}//把某人加到某路
private void Fram_rate(){...}//帧率:敌我血条变化 金币显示 敌人AI出兵 敌我增长钱
private void one_MA(List<People> road1, List<People> eroad1){...}//一路检测移动攻击
private void ATK_one(List<People> road1, List<People> eroad1){...}
private void check_state(List<People> road1){...}//状态检测
private void check_die(){...} //检测死亡和兵走到路尽头扣血
}
3.3 Player类
玩家类,继承People,用于判断游戏输赢,拥有自己卡牌组和金币
class Player:People
{
public int[] intList= new int[6];//自己的卡牌号码组
public int gold=13;//玩家金币
public int gold_getspeed=10;//玩家得到金币速度
public int gold_time = 0;//满百得1
public int grade=1;//玩家等级
public Player(){...}//无参构造,初始化玩家血量
public Player(int _team) : this(){...}//生成有位置,有队伍的图片,且调用无参构造
public void addgold()//自动增长金币
//以下为无用的空函数,为了满足抽象类People重写
public override void atk_sound(){}
public override void PassiveSkill(List<People> road1, List<People> eroad1, People ep){}
public override void Skill(List<People> road1, List<People> eroad1, People ep)
public override void skill_sound(){}
public override void Skill_time_change(){}
}
3.4 role类组举例
卡牌角色类,继承People,游戏中基本单位,拥有各自的特征(生命,攻击,移速,攻速,图片,技能等等....)可实现扩展,用其中之一类SwordMan剑士类作为举例:
图3.1 role类组包含的角色类
【举例】SwordMan类:
class SwordMan : People
{ public SwordMan()//角色初始化
{
NAME = "剑士";//名称
num = 1;//编号
VALUE = 14;//购买价格
MHP = 100;//最大血量
HP = 100;//血量
ATK = 10;//攻击力
DEF = 10;//防御力 减伤百分比=防御力/(防御力+X)
ATK_mothod = 1;//攻击方法
ATK_range = 10;//攻击范围
ATK_speed = 10;//攻击速度
SPEED = 10;//移动速度
PBox.Image = System.Drawing.Image.FromFile(@"..//..//Resources//剑.png");
//队伍team
//显示框PBox
//当前位置_P
}
public SwordMan(int _team):this(){...}//生成有位置,有队伍的图片,调用无参构造
public override void Skill_time_change(){...}//损失血量增加技能释放概率
public override void Skill(List<People> road1,List<People> eroad1,People ep){...}//主动技能 激励:攻速加快
public override void PassiveSkill(List<People> road1, List<People> eroad1, People ep){}//被动技能:无
public override void atk_sound(){...}//攻击音效
public override void skill_sound(){...}//技能音效
}
3.5 MCI类
自带的音乐播放函数System.Meida.SoundPlayer(),会造成播放阻塞,不能同时播放俩首音乐。为了使得游戏音效重叠播放需要自定义音乐播放类.用线程来解决在播放的时候指定wait时产生线程阻塞,从而导致界面假死的现象。
public class MCI
{
[DllImport("winmm.dll")]
private static extern long mciSendString(
string command, //MCI命令字符串
string returnString, //存放反馈信息的缓冲区
int returnSize, //缓冲区的长度
IntPtr hwndCallback //回调窗口的句柄,一般为NULL
);
private Thread thread;
private void PlayWait(string file){...}// 按指定次数播放
private void PlayRepeat(string file){...} // 循环播放
public void Play(string file, int times){...}//播放音频文件
public void Exit(){...}// 结束播放的线程
}
3.6自定义控件PanelEnhanced类
由于背景图的存在,当角色移动时,由于窗口频繁刷新会产生角色白光闪烁,为解决这一问题,需要自定义Panel控件重写里面的函数,来放置背景,利用双缓冲区解决问题
class PanelEnhanced : Panel
{
protected override void OnPaintBackground(PaintEventArgs e){...}// 重载基类的背景擦除函数, 解决窗口刷新,放大,图像闪烁
protected override void OnPaint(PaintEventArgs e){...}// 使用双缓冲 背景重绘移动到此
}
第四章 介面设计
为适应游戏的可扩展性,只有少部分不动的控件利用拖动和属性修改器进行生成控件,但为了随着游戏的随机性和角色图签的扩展性,是通过代码来根据数据自动生成界面。
4.1 开始界面
图4.1 开始界面
其中只有“请选择六名角色卡牌”是拖动Label控件完成,其他都是代码书写样式并显示,下面展示样式及其按钮事件代码
public partial class Start : Form
{
int selectCount = 0;
public static int[] intlist=new int[6];
public Start()
{
InitializeComponent();
}
private void Start_Load(object sender, EventArgs e)
{
int x = this.Width;//获得屏幕宽度
// 卡牌样式
for (int i = 0; i < Manager.roles.Count; i++)
{
Button B_I_role = new Button();
B_I_role.Size = new Size(150,150);
this.Controls.Add(B_I_role);
B_I_role.Location = new Point(i*160%(Width-150), (i*160/(Width-150))*160+100);
B_I_role.Name = "button" + Convert.ToString(i);
B_I_role.Text = Convert.ToString(i + 1)+Manager.roles[i].NAME;
B_I_role.TextAlign = ContentAlignment.TopCenter;
B_I_role.Image = Manager.roles[i].PBox.Image;
B_I_role.Click += new EventHandler(select);//绑定按钮事件
B_I_role.Tag ="0";
}
//开始按钮及其样式
Button Run = new Button();
Run.Size = new Size(300, 50);
this.Controls.Add(Run);
Run.Location = new Point(600,700);
Run.Text = "开始";
Run.TextAlign = ContentAlignment.MiddleCenter;
Run.Click += new EventHandler(run);//绑定按钮事件
Run.Tag = "0";
}
//开始按钮点击事件
private void select(object sender, EventArgs e)
{
Button button = (Button)sender;//获取调用事件的主体
for (int i = 0; i < Manager.roles.Count; i++)
{
if (button.Name == "button" + Convert.ToString(i))//找到所点击的按钮
{
if ((string)button.Tag == "0"&&selectCount<6)
{ button.Tag ="1";
selectCount++;
button.BackColor = Color.Red;
for (int j = 0; j < 6; j++)
{if (intlist[j] == 0)
{ intlist[j]= i+1;
break;
}
}
break;
};
if ((string)button.Tag == "1")
{ button.Tag ="0";
selectCount--;
button.BackColor = Color.White;
for (int j = 0; j < 6; j++)
{
if (intlist[j] == i+1)
{
intlist[j] =0;
break;
}
}
break; };
}
}
}
private void run(object sender,EventArgs e)
{ //检测选满后创建新窗口
Button b = (Button)sender;
for(int i=0;i<6; i++)
{
if (intlist[i] != 0)
{ }
else
{ b.Text = "未满6名角色无法开始游戏"; return; }
}
Form1 form1 = new Form1(intlist);
form1.ShowDialog();
this.Close();
}
}
4.2 游戏界面
图4.2 游戏界面
本页面全通过手动代码完成,通过Manager类根据数据进行自动显示,然后在窗体的Load函数中调用该函数完成页面初始化,只挑选出有关此游戏页面样式的代码进行展示:
class Manager
{
......
//绑定当前窗口 初始化窗口控件 卡牌框组样式既位置
public void addform(Form _cform)
{
cform = _cform;
cform.Text = "《混战》";
cform.MouseDown += new MouseEventHandler(Place_people);//给窗体添加点击事件
/* AI_first();//AI初始化*/
//初始化窗口控件
//敌人金币初始化(难度)
E_Player.gold += 5;
E_Player.gold_getspeed +=1;
//头像界面
PB.Size = new Size(130, 130);
I_HPB.Size = new Size(970, 20);
E_HPB.Size = new Size(970, 20);
cform.Controls.Add(PB);
cform.Controls.Add(I_HPB);
cform.Controls.Add(E_HPB);
PB.Location = new Point(260, 515);
I_HPB.Location = new Point(260, 480);
E_HPB.Location = new Point(260, 460);
PB.Name = "头像";
PB.BackColor =Color.White;
I_HPB.BackColor = Color.Green;
E_HPB.BackColor = Color.Red;
PB.Enabled = false;
I_HPB.Enabled = false;
E_HPB.Enabled = false;
PB.TextAlign =ContentAlignment.BottomCenter;
for (int i = 0; i < 6; i++)
{//卡牌组样式既位置
Button B_I_role = new Button();
B_I_role.Size = new Size(130, 130);
cform.Controls.Add(B_I_role);
B_I_role.Location = new Point(400+i*140,515);
B_I_role.Name = "button"+Convert.ToString(i + 1);
B_I_role.Text =Convert.ToString(i+1);
for (int j=0; j < roles.Count; j++)
{ if (I_Player.intList[i] == roles[j].num)
{
B_I_role.Text +=roles[j].NAME;
B_I_role.Text += "\n";
B_I_role.Text +="价格:"+ roles[j].VALUE+"金币";
break;
}
}
B_I_role.TextAlign = ContentAlignment.TopLeft;
B_I_role.Image = roles[I_Player.intList[i]-1].PBox.Image;
B_I_role.Click += new EventHandler(change_mouse);//绑定自定义按钮事件
}
}
.....
}
第5章 代码设计
此部分主要用来介绍一些主要功能代码,详细到方法体内容。
5.1 焦点点击屏幕放置功能
public void addform(Form _cform)
{
......
cform.MouseDown += new MouseEventHandler(Place_people);//给窗体添加点击事件
......
}
private void Place_people(Object sender,EventArgs e)//放置角色卡牌
{
if (mouse_int > 0&&I_Player.gold>= roles[mouse_int - 1].VALUE)
{
I_Player.gold -= roles[mouse_int-1].VALUE;
Point MouP = cform.PointToClient(Control.MousePosition);//焦点点击
if (MouP.Y > 85 && MouP.Y <= 115 + People.SIDE)//焦点点击在第一路
{
GenerateRole(mouse_int,1,1);
}
else if (MouP.Y > 115 + People.SIDE && MouP.Y <= 145 + 2 * People.SIDE)//第二路
{
GenerateRole(mouse_int, 1, 2);
}
else if (MouP.Y > 145 + 2 * People.SIDE && MouP.Y <= 175 + 3 * People.SIDE)//第三路
{
GenerateRole(mouse_int, 1, 3);
}
else
{
//点击其他地方 卡牌下移
cards_down();
I_Player.gold += roles[mouse_int - 1].VALUE;
mouse_int = 0;
}
}
}
public void GenerateRole(int sum,int team,int road)//生成角色,sun编号,team队伍,road生成道路
{
People p;
switch (sum)//编号
{
case 0: return;
case 1: p = new SwordMan(team); p.addform(cform); Addroad(p, road); break;
case 2: p = new StoneMan(team); p.addform(cform); Addroad(p, road); break;
case 3: p = new vampire(team); p.addform(cform); Addroad(p, road); break;
case 4: p = new FireMage(team); p.addform(cform); Addroad(p, road); break;
case 5: p = new Archer(team); p.addform(cform); Addroad(p, road); break;
case 6: p = new Dog(team); p.addform(cform); Addroad(p, road); break;
case 7: p = new Minister(team); p.addform(cform); Addroad(p, road); break;
case 8: p = new ThornBeast(team); p.addform(cform); Addroad(p, road); break;
case 9: p = new Orcs(team); p.addform(cform); Addroad(p, road); break;
case 10: p = new SKeleton(team); p.addform(cform); Addroad(p, road); break;
case 11: p = new SKeletonMage(team); p.addform(cform); Addroad(p, road); break;
case 12: p = new Boom(team); p.addform(cform); Addroad(p, road); break;
case 13: p = new Shrem(team); p.addform(cform); Addroad(p, road); break;
}
}
private void Addroad(People p, int road)//把某人加到某路
{
switch (road)
{
case 1:
if (p.team == 1)
{ road1.Add(p);
p.road1 = road1;
p.eroad1 = eroad1;
p._P = new Point(People.SIDE, 100);
}
else
{ eroad1.Add(p);
p.road1 = eroad1;
p.eroad1 = road1;
p._P = new Point(1552-People.SIDE*2, 100);
} break;
case 2:
if (p.team == 1)
{ road2.Add(p);
p.road1 = road2;
p.eroad1 = eroad2;
p._P = new Point(People.SIDE, 100 + (People.SIDE + 30));
}
else
{ eroad2.Add(p);
p.road1 = eroad2;
p.eroad1 = road2;
p._P = new Point(1552 - People.SIDE * 2, 100 + (People.SIDE + 30));
} break;
case 3:
if (p.team == 1)
{ road3.Add(p);
p.road1 = road3;
p.eroad1 = eroad3;
p._P = new Point(People.SIDE, 100 + (People.SIDE + 30) * 2);
}
else
{ eroad3.Add(p);
p.road1 = eroad3;
p.eroad1 = road3;
p._P = new Point(1552 - People.SIDE * 2, 100 + (People.SIDE + 30) * 2);
} break;
}
//显示
p.PBox.Location = p._P;
p.HpBox.Location = p._P;
cform.Controls.Add(p.HpBox);
cform.Controls.Add(p.PBox);
}
5.2 随机化敌人卡牌功能
public void Ini_Eintlist()//敌人卡牌集初始化
{
//敌人初始化
Array.Clear(E_Player.intList, 0, E_Player.intList.Length);
random(E_Player.intList, 6);
E_MaxValue = roles[E_Player.intList[0] - 1].VALUE; //敌人最大价格
foreach (int sum in E_Player.intList)
{
if (roles[sum - 1].VALUE > E_MaxValue)
{
E_MaxValue = roles[sum - 1].VALUE;
}
}
}
//不重复随机整数数组生成,用于初始化敌人卡牌图签
private void random(int[] a,int n)
{
long tick = DateTime.Now.Ticks;
Random ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
for (int i = 0; i < n; i++)
{
a[i] = ran.Next(1, roles.Count+1);
}
Boolean bol = true;
while (bol)
{
Array.Sort(a);
int num = 0;
for (int i = 0; i < 5; i++)
{
if (a[i] != a[i + 1])
{
num++;
}
else
{
a[i + 1] = ran.Next(1, roles.Count+1);
}
if (num == 5)
{
bol = false;
}
}
}
}
5.3 卡牌移动与攻击功能
class Manager
{
.....
public void Move_or_Atk()//检测移动攻击
{ // 帧率变化
Fram_rate();
//检测移动攻击
one_MA(road1, eroad1);
one_MA(road2, eroad2);
one_MA(road3, eroad3);
check_die();//检测死亡
}
private void one_MA(List<People> road1, List<People> eroad1)//一路检测移动攻击
{
//状态检测
check_state(road1);
check_state(eroad1);
//如果两边都有人
if (road1.Count != 0 && eroad1.Count != 0)
{ //攻击或移动
ATK_one(road1, eroad1);
ATK_one(eroad1, road1); //交换检测
}
else
{//如果只有一边有人
foreach (People p1 in road1)
{ p1.Move(); };
foreach (People p2 in eroad1)
{ p2.Move(); };
}
}
//攻击或移动
private void ATK_one(List<People> road1, List<People> eroad1)
{
try//避免空集暂停程序
{
foreach (People p1 in road1)
{
bool IsMove = true;
bool IsAtk = true;
foreach (People p2 in eroad1)
{
int range = Math.Abs(p2._P.X - p1._P.X);
IsAtk = ((p1.ATK_range + People.SIDE) >= range);
if (IsAtk)
{
p1.Atk(p2);
if (p1.ATK_mothod == 1) break;//单体攻击找到后退出循环
IsMove = false; //(解决后方问题?会移动后方,由于判断移动了)
}
}
if (IsAtk == false && IsMove == true)
{
p1.Move();
}
}
}
catch { };
}
.....
}
abstract class People
{
......
public void Move()//公共方法-移动
{
//移动上下跳动
_P.Y = (int)(_P.Y - SPEED * Math.Cos(time++)+0.45);
//移动
switch (team)
{
case 1: _P.X += (int)(SPEED / 1.2 + 1); break;
case 2: _P.X -= (int)(SPEED / 1.2 + 1); break;
}
PBox.Location = _P;
HpBox.Location = _P;
HpBox.BringToFront();
}
public virtual void Atk(People p)//虚方法-攻击/特殊的重写
{
Skill_time_change();//技能概率变化
//技能随机释放
int a = 1+Convert.ToInt32(Guid.NewGuid().GetHashCode() % 100);
int random=0;
if (a < 0) random = a + 100;
else { random = 100; }
this.ATK_time += this.ATK_speed;//攻速满一百一次攻击
if (this.ATK_time >= 100)
{
this.ATK_time -= 100;
if (random >= Skill_time)
{
p.PassiveSkill(eroad1, road1, this);//被动技能
atk_sound();//攻击音效
p.HP -= (int)(this.ATK * (1 - (p.DEF / (p.DEF + 30))));
}
else { Skill(road1, eroad1, p); }
}
}
public abstract void Skill_time_change();//多态方法-技能概率变换
public abstract void Skill(List<People> road1,List<People> eroad1,People ep);//多态方法-技能
public abstract void PassiveSkill(List<People> road1, List<People> eroad1, People ep);//多态方法-被动技能
public abstract void atk_sound();//攻击音效
public abstract void skill_sound();//技能音效
}
5.4动态化频率显示功能
private void Fram_rate()
{ //敌我血条变化
I_HPB.Size =new Size(970*I_Player.HP/2000,20);
E_HPB.Size = new Size(970 * E_Player.HP / 2000, 20);
//金币显示
PB.Text ="生命:"+I_Player.HP+"\n"+ "金币:" + I_Player.gold;
//敌人AI出兵
if (E_MaxValue <= E_Player.gold)
{
int MaxX = 0;
int road = 2;
foreach (People p1 in road1)
{
if (p1._P.X > MaxX)
{
MaxX = p1._P.X; road = 1;
}
}
foreach (People p1 in road2)
{
if (p1._P.X > MaxX)
{
MaxX = p1._P.X; road = 2;
}
}
foreach (People p1 in road3)
{
if (p1._P.X > MaxX)
{
MaxX = p1._P.X; road = 3;
}
}
int r2 =100* (MaxX-People.SIDE) /( 1552 - People.SIDE);
int r3 = (UInt16)DateTime.Now.Ticks % 101;
int r = (UInt16)DateTime.Now.Ticks % 6;
if (roles[E_Player.intList[r]-1].VALUE <= E_Player.gold)
{
if (r3 <= r2 + 30)
{ GenerateRole(E_Player.intList[r], 2, road); }
else
{
int road2 = 1 + r3 % 3;
GenerateRole(E_Player.intList[r], 2, road2);
}
E_Player.gold -= roles[E_Player.intList[r]-1].VALUE;
}
}
//敌我增长钱
I_Player.addgold();
E_Player.addgold();
for (int i = 0; i < 6; i++)
{
Control[] c = cform.Controls.Find("button" + Convert.ToString(i + 1), false);
if (roles[I_Player.intList[i]-1].VALUE > I_Player.gold)
c[0].BackColor = Color.Red;
else { c[0].BackColor = Color.White; }
}
}
第六章 总结
刚开始作为游戏编程小白的我,听别人建议,这一学期专门学习了c#来进行编程,为了使得游戏好玩,我零零散散学了许多东西,例如像素画,音效合成,打击反馈等等。刚开始是这个c#WinForm窗口提起了我的兴趣(作为一个被控制台文字毒害的我,终于发现了一个可视化程序编程宝藏),于是我马不停蹄的俩周学完了c#基础,然后因为拥有c++编程的原因,以及做过几个小系统,立马完成了我的第一款自己做的《飞机大战》小游戏,但是这个游戏的编程思想纯粹是顺序编程,没有体现面向对象,于是在接下来的一周时间,我好好计划了一下项目,既不能太大让我束手无策,也不能太小没有可玩性。于是《混战》便诞生了,刚开始的版本非常简陋,只有几个角色,而且也有许多BUG,可能一天查BUG到最后才发现不小心写错了单词。但我还是坚持下来了,总的来说,编程是最重要的,但是查找各种图片音效资源,以及手动画了一张像素背景画却也花费了我的大量时间,所以说游戏编程并不容易。
虽然说这个版本还有许多遗憾没有完成,像开始界面的设置按钮(调音量,调背景,调上传头像),关卡界面(增强可玩性),存档功能(IO流记录的比较多,不想搞了),以及比较难搞的攻击和移动动画等都是一些遗憾,但就此结束吧!
最主要原因,还是这个WinForm的框架编游戏太困难了,不仅出现背景闪烁(加入新控件,已解决),音频互相阻塞(加入新类,已解决),,帧移动不流畅(未解决),用过的垃圾不能手动回收(未解决,死亡的人物占内存,没有垃圾池机制)等等许多毛病,而且需要手动编写碰撞箱,基础表格,以及运行起来缓慢,让我放弃了WinForm窗体应用程序框架。
幸运的是,在我学习过程中,我了解了Unity软件,它对3D游戏编程最为方便也可进行2D游戏编程,且内置了许多功能,方便一众独立游戏编程,我已经大概了解了软件使用基本过程,准备在假期期间继续对C#的深入学习和Unity的熟练掌握。
第七章 参考文献
- 知乎,皮皮关,《c#目前在游戏领域应用》
- CSDN,kucoffee12,《解决C#Winform应用程序中窗体背景闪烁问题》
- CSDN,Zhy,《调用API函数mciSendString播放音频文件》