面向对象程序设计实训(吃金豆游戏设计与实现)
更新时间:2024-01-04 02:43:01 阅读量: 教育文库 文档下载
- 面向对象程序设计实训总结推荐度:
- 相关推荐
面向对象程序设计实训(吃金豆游戏设计与实现)
常熟理工学院计算机学院
在Visual Studio 2005环境下用C#编写GUI代码达到如下功能: (1) 游戏基本功能:
游戏屏幕是一个 NxN 的网格,其中某些格子是固定的墙壁,其他网格是可以通行的,开始的时候放满了金豆。游戏开始后,玩家通过键盘的方向键控制游戏主角PACMAN移动,经过的地方,金豆被吃掉,同时加分。游戏屏幕上还有一个怪物,它会随机地移动。PACMAN如果碰到怪物,游戏失败。如果吃掉了全部金豆,则游戏成功。 (2) 游戏场景(即墙壁和金豆的位置,PACMAN与怪物的初始位置等信息)是从文件 scene.txt 中读到的。格式如下:
pr,pc,mr,mc //(pr,pc)为Pacman初始行列,(mr,mc)为Monster初始行列 11111111 //第一行,N列,其中1表示墙;0表示金豆 10010.. //第2-N行 ....
(3) 附加功能,完成以上内容的同学选做: ① 游戏主菜单
开始游戏、查看成绩排行榜、退出游戏等项目。
② 每次游戏成功结束,要求输入用户名,并记录成绩到文件score.txt。如果已有同名用户,且本次得分更高,则替代之。在显示排行榜时,按得分高低排序。
③ 使用不同的关卡,即使用多个场景文件 scene(0-n).txt,在开始游戏时选择。 ④ 加入声音效果,吃到金豆时,Win/Lost时显示不同的声音。
3、基础部分训练内容
(1) C#面向对象程序设计,Visual Stuio 2005开发环境; (2) C# GUI编程; (3)游戏及动画实现;
(4)游戏相关功能(键盘处理,图形移动,随机数,数学函数等)。
4、指导教师:宋东兴、殷旭东
1.代理和事件
一、代理(delegate) 1、 代理概念
是一种将方法作为对象封装起来的引用数据类型,一个代理变量可以指向一个方法。 (1)定义一个名为OpHandler的代理类型。
delegate double OpHandler(double x,double y); (2)创建一个代理变量h1; class Calc{
public static double Add(double x,double y){return x+y;} public double Sub(double x,double y){return x-y;} }
OpHandler h1 = Calc.Add; //h1指向Add方法 (3)通过代理变量h1调用它指向的方法 double result;
result = h1(3,4); //调用Add方法 h1 = new Calc().Sub;
result = h1(3,4); //调用Sub方法 2、 代理作为方法的参数 class MathOp{
private double left; private double right;
public MathOp(double left,double right){ this.left = left; this.right = right; 代理作为参数 }
public double GetResult(OpHandler op){ return op(left,right); }
}
class App{
public static void Main(){
MathOp m1 = new MathOp(3,4);
double result = m1.GetResult(Calc.Add); Console.WtriteLine(“result = {0}”,result); } }
3、 代理变量作为类的一个数据成员
class MathOp{
public OpHandler op; //op是代理变量 private double first; private double second;
public MathOp(double first,double second){
this.first = first; this.second = second; op = null; }
public double Invoke(){
if(op==null) //如果有方法注册委托变量op throw new Exception();
return op(first,second); //通过委托来调用方法 } }
class App{
public static void Main(){
MathOp m1 = new MathOp(3,4);
m1.op = Calc.Add;
double result = m1.Invoke();
Console.WtriteLine(“result = {0}”,result); } }
4、 多播代理
在 C# 中,代理是“多播”的,这表示它可同时指向一个以上的方法。多播代理将维护一个方法列表。当调用该代理时,将会按FIFO顺序调用列表中的所有方法。
public delegate void GreetingDelegate(string name);
class GreetingManager{
public GreetingDelegate say;
public void GreetPeople(string name){ if(say!=null) say(name); } }
class App{
public static void EnglishGreeting(string name) {
Console.WriteLine(\英文问候 }
public static void ChineseGreeting(string name) {
Console.WriteLine(\早上好, \中文问候 }
public static void Main(){
GreetingManager gm = new GreetingManager ();
gm.say = EnglishGreeting;
gm.say += ChineseGreeting; //用+=合并两个代理
gm.GreetPeople(“Marry”); //也可以直接gm.say(“Marry”)调用委托
} }
注:同样用-=可以删除一个代理。 二、事件(event)
将代理封装起来就成了事件,事件对外是公开的,而它所对应的代理是私有的。事件可以看作是受限的代理。因此事件也可以绑定(注册)一个方法,但不能通过=,只能通过+=进行。事件最常见的用途是用于窗体编程,当发生像点击按钮、移动鼠标等事件时,对应的方法执行,以响应该事件。 例1:
将上例的GreetingDelegate代理封装成MakeGreet的事件,代码如下: public delegate void GreetingDelegate(string name);
class GreetingManager{
/* 将代理GreetingDelegate封装成事件MakeGreet*/
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name){ if(MakeGreet!=null)
MakeGreet(name); //调用事件 }
}
class App{
public static void EnglishGreeting(string name) {
Console.WriteLine(\英文问候 }
public static void ChineseGreeting(string name) {
Console.WriteLine(\早上好, \中文问候 }
public static void Main(){
GreetingManager gm = new GreetingManager ();
gm.MakeGreet = EnglishGreeting; //编译错误,不允许用=注册方法 gm.MakeGreet += ChineseGreeting; gm.GreetPeople(“Marry”); /*
不可以直接gm. MakeGreet (“Marry”)调用事件,只能在定义 MakeGreet的类中调用事件。
*/
} } 例2:
类Button在GUI中代表按钮,在Button类中定义了一个Click事件,它是对代理EventHandler的封装。
(1)EventHandler代理的定义如下:
delegate void EventHandler(object sender,EventArgs e);
(2)Click事件定义如下:
event EventHandler Click;
Button btnOK = new Button(); //实例化一个名btnOk的按钮 btnOK.Click += ClickProcess; //为它的Click事件注册ClickProcess方法。
则当单击btnOK按钮时,ClickProcess方法被调用以响应该单击事件。
2.窗体和按钮
一、窗体(Form类)
1、Form类是所有高级窗口的基类。 2、设置Form属性
如: (Text, Location, Size, Name)
(FormBorderStyle, BackColor, StartPosition)
(MaximunBox, MinimunBox, ControlBox)等。 二、按钮控件(Button类) 1、属性:(Text, Name, Enabled, ?)
2、方法: Show(),Hide(),Focus(),Invalidate()? 3、事件: Click
GUI应用程序的例子:
1)生成并显示一个标题为Hello的空白窗体: class MyForm:Form{ public MyForm(){
this.Text = “Hello”;
} }
class WinApp{
public static void Main(){
Application.Run(new MyForm());
} }
2)在窗体中增加一个按钮,按钮标题为OK,修改MyForm类: class MyForm:Form{
private Button btnOK; public MyForm(){
btnOK = new Button(); btnOK.Text = “OK”;
this.Text = “Hello”;
this.Controls.Add(btnOK); }
}
运行程序,单击OK按钮,看看有没有什么发生?
3)为OK按钮注册Click事件,当单击OK时,按钮的背景色变红。 修改MyForm类: class MyForm:Form{
private Button btnOK; public MyForm(){
btnOK = new Button(); btnOK.Text = “OK”;
btnOK.Click+= ClickProcess; //注册click事件 this.Text = “Hello”; this.Controls.Add(btnOK); }
public void ClickProcess(object sender,EventArgs e){ btnOK.BackColor = Color.Red;
}
}
运行程序,单击OK按钮,看看发生了什么?
3.GDI编程
GDI(Graphics Device Interface)为开发者提供了一组实现与各种设备进行交互的类库,它在开发人员与设备之间起到了一个重要的中介作用。
GDI 显示器(设备) 应用程序
GDI包括三个部分: ? 二维矢量图形绘制 ? 图像处理 ? 文字显示 一、Graphics类
该类封装了GDI的绘图表面,Windows窗体中所有的绘图操作都必须通过Graphics类进行。 1、 创建或获取Graphics对象
法一:Graphics g = 控件.CreateGraphics(); 法二:通过窗体或控件的Paint事件来获取。
以窗体为例:paint事件在窗体的任何部分需要重绘时发生。 引发paint事件的情况有:
(1) 窗体首次运行时
(2) 覆盖窗体的其他窗体移开时 (3) 窗体本身移动,或大小改变时
Private void Form1_Paint(object sender,PaintEventArgs e){ Graphics g = e.Graphics;
g.DrawLine(?);
? }
2、 用Graphics类提供的各种方法:
? 绘图 ? 显示文本
? 操作和显示图像 DrawLine(?) DrawEllipse(?) DrawRectangle(?) DrawImage(?) DrawString(?) FillEllipse(?) ?
二、创建画笔和画刷
1、 Pen类创建指定颜色和线宽画笔对象
Pen p1 = new Pen(Color.Red); //设置颜色
Pen p2 = new Pen(Color.Black,10); //设置颜色和线宽
Pen p3 = Pens.Blue; //Pens类中预定义的画笔 2、 画刷Brush抽象类
SolidBrush、HatchBrush等是Brush类的子类
前景色 背景色 Brush b1 = new SolidBrush(Color.Red);
Brush b2 = new HachBrush(HatchStyle.Cross,Color.Blue,Color.Yellow); Brush b3 = Brushes.Red; ////Brushes类中预定义的画刷
三、坐标系统 x>0 (0,0)
width (x,y) height y>0
(1)画椭圆
g.DrawEllipse(Pen pen,int x,int y,int width,int height); (2)画扇形
g.DrawArc(Pen pen,int x,int y,int width,int height,int startAngle,int degree); (3)显示文字
g.DrawString(“Hello”,this.Font,Brushes.Red,20,20);
g.DrawString(“Hello”,new Font(“宋体”,30,FontStyle.Bold),Brushes.Red?);
例一:在窗体中绘制如下的图形
private void Form1_Paint(object sender, PaintEventArgs e){ Graphics g = e.Graphics; float diameter=50;
g.DrawLine(Pens.Red, 0, 0, this.ClientSize.Width, this.ClientSize.Height); g.DrawLine(Pens.Blue), 0, this.ClientSize.Height, this.ClientSize.Width, 0); g.DrawEllipse( Pens.Black,
(this.ClientSize.Width - diameter)/2, (this.ClientSize.Height - diameter)/2, diameter, diameter ); }
//窗体大小改变时触发Resize事件
private void Form1_Resize(object sender, EventArgs e){
this.Invalidate(); //使窗体或控件无效,并人为引发其Paint事件,进行重绘 this.Update(); }
例二:在面板中绘制如下的图形:
要求:在文本框中输入行和列,单击clear按钮,对应位置的小金豆被清除。
法一:直接在面板中绘图
private void panel1_Paint(object sender, PaintEventArgs e){ Graphics g = e.Graphics; int r, c;
r = c = 8; //r行,c列
int h, w; //每个方块的高度和宽度 w = panel1.Width / c; h = panel1.Height / r;
for (int i = 0; i < r; i++){ for (int j = 0; j < c; j++){
g.DrawRectangle(Pens.Blue, j * w, i * h, w, h); g.FillEllipse( Brushes.Yellow,
j * w + (w - 20) / 2, i * h + (h - 20) / 2, 20, 20);
} } }
private void btnClear_Click(object sender, EventArgs e){ int r = int.Parse(txtRow.Text); int c = int.Parse(txtCol.Text); int w = panel1.Width / 8; int h = panel1.Height / 8;
Graphics g = panel1.CreateGraphics();
Brush b1 = new SolidBrush(panel1.BackColor);
g.FillEllipse(b1, c * w + (w - 20) / 2, r *h +(h - 20) / 2, 20, 20); }
法二:将一个方块封装成一个Cell对象。
Cell -r:int //行 -c:int //列 -w:int //宽度 -h:int //高度 +Cell(int r,int c,int w,int h); +Draw(Graphics g):void +Clear(Graphics g,Color foreColor):void class Cell{
private int r,c,w,h;
public Cell(int r, int c, int w, int h){ this.r = r; this.c = c; this.w = w; this.h = h; }
public void Draw(Graphics g){ int x, y; x = w * c; y = h * r;
g.DrawRectangle(Pens.Blue, x, y, w, h);
g.FillEllipse(Brushes.Yellow, x + (w - 20) / 2, y + (h - 20) / 2, 20, 20); }
public void Clear(Graphics g, Color foreColor){ int x, y; x = w * c; y = h * r;
g.FillEllipse(new SolidBrush(foreColor), x, y, w, h); } }
在Form1类中如下定义:
private Cell[,] cellArr; public Form1(){
InitializeComponent();
cellArr = new Cell[8, 8]; for (int i = 0; i < 8; i++){ for (int j = 0; j < 8; j++)
cellArr[i, j] = new Cell(i, j, panel1.Width / 8, panel1.Height / 8); } }
private void panel1_Paint(object sender, PaintEventArgs e){ for (int i = 0; i < cellArr.GetLength(0); i++){ for (int j = 0; j < cellArr.GetLength(1); j++) cellArr[i, j].Draw(e.Graphics); } }
private void btnClear_Click(object sender, EventArgs e){ int r, c;
r = int.Parse(txtRow.Text); c = int.Parse(txtCol.Text);
cellArr[r, c].Clear(panel1.CreateGraphics(), panel1.BackColor); }
例三:设计一个pacman,要求:
(1) 可以通过按钮控制它的上下左右运动。 (2)
Cell -r:int //行 -c:int //列 -w:int //宽度 -h:int //高度 +Cell(int r,int c,int w,int h); +Draw(Graphics g):void +Clear(Graphics g,Color foreColor):void +abstract DrawImage(Graphics g,int x,int y):void Pacman -dir:Directoin //Direction枚举四种方向 +Pacman(int r,int c,int w,int h,Direction dir); +Draw(Graphics g):void +Move():void +override DrawImage(Graphics g,int x,int y):void
代码参考:
enum Direction{Up,Down,Left,Right}; class Pacman:Cell{
protected Direction dir;
public Pacman(int r,int c,int w,int h,Direction dir):base(r,c,w,h){ this.dir = dir; }
public override void DrawImage(Graphics g, int x, int y){
g.FillEllipse(Brushes.Black, x + (w - 20) / 2, y + (h - 20) / 2, 20, 20); }
public void Move(){ switch (dir){
case Direction.Up: r--; break; case Direction.Down: r++; break; case Direction.Left: c--; break; case Direction.Right: c++; break; } }
public Direction Dir{ get {
return dir; } set {
dir = value; } } }
public partial class Form1 : Form{ private Pacman myPacman; public Form1(){
InitializeComponent();
myPacman = new Pacman( 0, 0,
panel1.Width / 16, panel1.Height / 8, Direction.Right ); }
private void panel1_Paint(object sender, PaintEventArgs e){ myPacman.Draw(e.Graphics); }
private void btnUp_Click(object sender, EventArgs e){ myPacman.Dir = Direction.Up; myPacman.Move(); panel1.Invalidate(); panel1.Update(); }
private void btnDown_Click(object sender, EventArgs e){ myPacman.Dir = Direction.Down; myPacman.Move(); panel1.Invalidate(); panel1.Update(); }
private void btnLeft_Click(object sender, EventArgs e){ myPacman.Dir = Direction.Left; myPacman.Move(); panel1.Invalidate(); panel1.Update(); }
private void btnRight_Click(object sender, EventArgs e){ myPacman.Dir = Direction.Right; myPacman.Move(); panel1.Invalidate(); panel1.Update(); } }
(2)向上、下、左、右运动时,Pacman的嘴形各不相同。
提示:用画扇形,或画饼图的方法实现 Graphics g = e.Graphic;
g.DrawArc(?); //画扇形 g.FillPie(?); //画饼图 (3)用键盘捕获其上下左右键控制它的运动。
在Form1类中重写父类的抽象方法bool ProcessDialogKey(Keys keyDate); protected override bool ProcessDialogKey(Keys keyDate){ switch (keydate){ case Keys.Up:
myPcman.dir = Direction.Up; break; case Keys.Down:
myPcman.dir = Direction.Down; break; case Keys.Left:
myPcman.dir = Direction.Left; break; case Keys.Right:
myPcman.dir = Direction.Right; break; }
return base.ProcessDialogKey(keydate); }
4.定时器控件(Timer)
Timer可以按照指定时间间隔来触发事件,可利用它来执行周期性的操作或指定时间长度操作(比如动画制作、延时等)。 ? 常用属性:
(1) Enabled:指定定时器是否运行(即是否可触发事件) (2) Interval:指定时间间隔(以毫秒为单位) ? 常用方法: (1) Start(); (2) Stop(); ? 事件
Tick //时间间隔到达指定时间且时钟处于运行状态时触发。 对应代理类型为EventHandler。
程序例:编程设计一个在窗体内随机滚动的小球。 1、 设计一个球类:
class Ball{
const int HEIGHT = 20,WIDTH=20; private int xPos; private int yPos;
private int dx = 3; private int dy = 3;
public Ball(){xPos = yPos = 0;}
public Ball(int x,int y){xPos = x,yPos = y;} public void Draw(Graphics g){?} public void Move(Size room){?} }
//小球的Draw方法
public void Draw(Graphics g){ Brush b = Brushes.Red;
g.FillEllipse(b,xPos,yPos,WIDTH,HEIGHT); }
//小球的Move方法
public void Move(Size room){
Int maxX = room.Width-WIDTH; Int maxY = room.Height-HEIGHT; xPos+=dx; yPos+=dy;
if(xPos<0 || xPos>maxX) dx = -dx;
if(yPos<0 || yPos>maxY) dy = -dy; }
2、 窗体内中增加球和定时器对象
Class Form1:Form{
Private Ball myBall; Private Timer timer; Public Form1(){
myBall = new Ball(); timer = new Timer(); timer.Enabled = true; timer.Interval = 100; timer.Tick+=TimerTick;
}
//Form1的Paintg事件处理方法
Private void Form1_Paint(object sender,PaintEventArgs e){
myBall.Draw(e.Graphics); }
//定时器的Tick事件处理方法
Private void TimerTick(object sender,EventArgs e){ myBall.Move(this.ClientSize); this.Invalidate(); this.Update(); }
}
5、吃金豆游戏的设计 一、游戏的场景:
Pacman Wall Bean
Monster
分析:
一个场景分成M 行N列的小Cell,每个Cell有行,列,宽度,高度(r,c,w,h)等属性,且有Draw(),Clear()等方法。
Pacman,Monster,Bean,Wall又可看作是一个特殊的Cell。Pacman和Monster是可运动的,而Bean和Wall是静止的,它们都能用Draw()方法绘制自己,但绘出来的图像也各不相同。
二、游戏角色 1、类Pacman ? 属性:
(1)r,c,w,h:int //(行,列,宽度,高度,继承自Cell) (2)dir:Direction //运动方向 (3)running:bool //是否运动状态 ? 方法:
(1)Draw(Graphics g):void; //继承自Cell
(2)DrawImage(Graphics g,int x,int y):void //自绘方法,重写Cell的对应方法。 (3)Move():void //根据方向尝试向下一位置运动 (4)ChangeDirection(Direction newDir):void //改变方向 2、类Monster ? 属性:
r,c,w,h:int //(行,列,宽度,高度,继承自Cell) ? 方法:
(1)Draw(Graphics g):void; //继承自Cell
(2)DrawImage(Graphics g,int x,int y):void //自绘方法,重写Cell的对应方法。 (3)Move():void //尝试向下一位置运动
(4)Eat(Pacman myPacman):bool; //是否吃到了Pacman
3、 抽象类Block
一个Block是一个Cell,同是它定义自身是否可达IsReachable(),是否可吃IsEatable()的抽象方法。
4、类Bean,Wall,White 分别代表一个小金豆;代表一块墙;代表一个空白块(豆已吃),它们都是一个特殊的Block,Block定义的IsReachable()和IsEatable()两个抽象方法由它的子类们去实现。
四、角色类关系图
Cell -r,c,w,h:int +draw(Graphics g):void +abstract drawImage(Graphics g,int x,int y):void Pacman Monster Block +abstract IsReachable():bool +abstract IsEatable():bool
Bean Wall
说明:Pacman的Move()方法的算法思路 If(isRunning){
取下一个Block ->nextBlock;
If(nextBlock.IsReachable()&&nextBlock.IsEatable()){ nextBlock的r,c ->当前r,c; nextBlock置为White; 加分;
金豆数减一
}else if(nextBlock.IsReaable()){ nextBlock的r,c ->当前r,c;
}else{
White isRunning = false; }
}
五、系统总类图: GameForm -myScene:Scene -gameOver:bool -score:int +GameForm() +Init():void +Play():void 1 Scene -row,col:int -map:Block[,] -myPacman:Pacman * -myMonster:Monster -beanNum:int -score:int +Scene() +Load(StreamReader sr):void +Draw(Graphics g):void
正在阅读:
第二章 第一节 无脊椎动物的主要类群 第1课时 - 图文03-25
实验一至三 - - 芦丁提取纯化及分析2014-修正终版09-14
2016年 春季麦田管理情况汇报10-08
欧美港口集装箱海铁联运发展概况04-04
建筑史重点、10-30
推箱子游戏-C语言12-21
第五章 化学热力学基础 习题11-18
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 金豆
- 程序设计
- 面向
- 对象
- 实现
- 设计
- 游戏
- 服务器安装基本操作步骤
- 统计学课程设计报告 - 图文
- 2018-2019年最新长沙市南雅中学初升高自主招生考试数学模拟精品试卷
- 20xx年普外科护理年度工作计划
- 大学班级出游运动策划书
- 2018-2019-2018-2019学年小学美术教学计划-优秀word范文(2页)
- 调研报告:依法治国背景下检察机关化解涉法涉诉信访案件调查
- 四校联考试卷及参考答案
- 中国调频耳机市场发展研究及投资前景报告(目录) - 图文
- 部编版《道德与法治》七年级下册期末考试试题(含答案)
- 湖北省浠水实验高中2014-2015学年高二上学期期中考试政治试题
- 数字系统课程设计教学大纲
- 职业生涯规划说明书
- 高三历史人教版一轮复习知识归纳:世纪之交的世界格局
- 如何布局产业链金融
- 浣东小学庆六一暨新队员入队活动方案
- 2018年教科版小学《品德与生活》一年级上册全册教案(3)
- 变频调速技术在电梯门机的应用
- 2017-2023年中国混凝土泵车市场运行态势报告(目录) - 图文
- 小学数学六年级培优竞赛教程 2 - 图文