《java游戏大作业设计》Word文档格式.docx
《《java游戏大作业设计》Word文档格式.docx》由会员分享,可在线阅读,更多相关《《java游戏大作业设计》Word文档格式.docx(19页珍藏版)》请在冰豆网上搜索。
三、总体设计
3.1类的设计
图3-1
在吃豆人游戏的类设计中,首先要确立的是类的关联、划分主次。
整个游戏主要由两大部分组成,在这其中PacWorld类既是其中的一大组成部分也游戏一个重要的“舞台”,承载了其他的类方法,在这其中,设计者可以根据具体的游戏需要来加入相应的游戏元素、游戏规则,从而达到丰富游戏的目的。
World类又分为PacWorld和MyWorld两个类,其中PacWorld类是游戏场景元素的一个大集合。
作为一个舞台的大容器,它包括游戏区块(左中右三部分)的布局、边界(墙壁)的绘制、背景颜色、空白填充、位置信息显示、分数、大小豆子、怪物以及的位置生成、豆子的生成位置等元素,给游戏塑造出了一个整体框架。
而另一大组成部分Actor类则作为各种对象类的集合定义游戏中的各种方法。
其中包括Animal类、Writing类以及Object类。
Animal类中包含了两个重要的元素——PacMan类和Ghost类,分别实现吃豆人和怪物的运动、动画、触发事件等算法逻辑及调用方法。
Writing类包含主游戏框架面板上计分板、位置显示、空白填充以及生命计数器的方法类。
Object类中则是定义被吃的大小豆子。
所有的类在设计的时候保证了相互之间的联系性,类与类之间存在方法上的调用,对象方法的灵活运用是吃豆人游戏的一大主体。
3.2事件的设计
作为一个游戏,事件的设计必不可少。
在设计一个游戏时总是会在特定的游戏场景中抛出一个自定义事件,由相关的界面(可能是多个)去订阅这个事件,当接收到这个事件时,这些界面刷新显示数据,从而解除游戏逻辑层和界面显示层的耦合关系。
每一个游戏都有其自定义的游戏触发事件,本次设计的吃豆人游戏也有着其独到的自定义事件。
这次的设计中,吃豆人的事件主要分为两大类,它们分别是针对吃豆子和触碰Ghost的触发事件。
在吃豆子的事件中(如图3-3所示),当玩家操控吃豆人角色吃到小豆子(coin)时,分数加1(更新计分板)并移除小豆子。
而当吃到大豆子(Power_Pellet)时,就需要在分数加5并移除大豆子的基础上更新Ghost状态(这里需要改变Ghost的图形,使玩家能在视觉上感受变化)。
图3-2
在触碰Ghost事件中(如图3-4所示),只有当Ghost处于特殊状态时,玩家角色才可以将其吞食消除,否则吃豆人将会失去一条生命并且重置回出生点,并在计分板上使其生命显示减1(同步计数器)。
图3-3
四、详细设计
4.1PacWorld类
在游戏场景中,除了中央的游戏区域外,左右各有三块区域用于显示游戏信息,而其余部分则用Blank类方法填充空白。
游戏中央的场景中绘制出地图(边界线/墙壁),留出通道,在其中的相应位置放入对应的怪物、大小豆子以及游戏的控制主体——吃豆人,左右放置对应的计分板和计数器,构成整个游戏的基本元素框架。
图4-1
先定义各元素信息:
①左右空白填充:
BlankblankR=newBlank();
BlankblankL=newBlank();
②生命显示:
(左下角三个小吃豆人)
Liveslife1=newLives
(1);
Liveslife2=newLives
(2);
Liveslife3=newLives(3)
③四种颜色的怪物:
Inkyinky=newInky();
Blinkyblinky=newBlinky();
Clydeclyde=newClyde();
Pinkypinky=newPinky();
Ghost.speed=gSpeed;
④吃豆人:
staticPacManpacman;
pacman=newPacMan(pPoints,pLives);
⑤地图信息:
首先,将整个地图存入到一个二维数组中:
for(inti=0;
i<
pacworld.length;
i++)
{
for(intj=0;
j<
pacworld[i].length;
j++)
{
pacworld[i][j]=false;
}
}
接下来根据数组绘制地图DrawPath,将绘制的边界以特殊的信息保存到数组中。
privatevoidDrawPath()(部分代码)
min=200;
max=442;
for(intx=min;
x<
=max;
x++)
{pacworld[x][341]=true;
}
min=283;
max=358;
pacworld[x][221]=true;
pacworld[x][213]=true;
pacworld[x][204]=true;
min=120;
max=522;
pacworld[x][426]=true;
pacworld[x][87]=true;
min=248;
max=394;
pacworld[x][257]=true;
pacworld[x][171]=true;
……//为简明说明此处省略90%的边界代码
图4-2
绘制完成后就向其中加入实体:
(计分板、空白填充、生命计数器、吃豆人、怪物)
Positionposition=newPosition();
ScorescorE=newScore();
addObject(position,320,460);
addObject(pacman,320,257);
addObject(scorE,46,75);
addObject(inky,283,210);
addObject(blinky,320,171);
addObject(pinky,320,217);
addObject(clyde,358,210);
addObject(blankL,75,214);
addObject(blankR,575,214);
addObject(life1,70,350);
addObject(life2,45,350);
addObject(life3,20,350);
豆子的绘制(程序中放置在转角处)需要对通路进行统计,即CoinFill函数的square的计数:
①.在指定的区域放置大豆子,当吃到大豆子时所有的怪物立刻变为可以被吃掉的状态。
注:
在吃掉大豆子后吃豆人的标记run会变为true,意味着此时可以吃掉怪物,这个状态的持续时间定义为5秒。
②.在路口处放置小豆子
③.放置完豆子使其计数器加一
publicvoidCoinFill()
coins=0;
for(inti=0;
i++){
j++){
if(pacworld[i][j]==true){
intsquare=1;
if(pacworld[i+1][j]==true)square++;
elseif(pacworld[i-1][j]==true)square++;
if(pacworld[i][j+1]==true)square++;
elseif(pacworld[i][j-1]==true)square++;
:
if(/*(i==320&
&
j==171)||*/(i==200&
j==87)||(i==442&
j==87)||(i==420&
j==341)||(i==224&
j==341))
addObject(newPower_Pellet(),i,j);
coins++;
elseif(i>
=283&
=358&
j>
=172&
=221)coins=coins;
elseif(square>
2)
addObject(newCoin(),i,j);
c=coins;
4.2Animal类
在Animal类中定义了用于子类继承的抽象方法,如:
方向的移动、方向移动的提前量判断等,在该类中还设置了角色的场景移动传送方法,用于对游戏场景中部特殊的通道移动的定义。
对于Ghost而言,他们的移动的范围要求比吃豆人更广提高难度,当他们从地图中央两侧的通道进入时,将会从另一侧出现。
这种突然出现在身边的Ghost往往能提高玩家的紧张感,提升游戏性。
protectedvoidwarp(){
positionX=this.getX();
positionY=this.getY();
if(positionX==94)setLocation(550,213);
elseif(positionX==556)setLocation(104,213);
例如:
当Ghost从右侧通道走出游戏场景时将会从左侧的通道重新出现在玩家的面前(如图4-3、图4-4所示)。
图4-3
图4-4
在游戏中,总是会在一个方向上碰到无法继续前进的情况,这个时候就需要判断此时还能往哪个方向进行运动,时刻检查能否向四周运动,并根据这个方向来和键盘输入的方向键进行比较,来执行最终的动作方向。
CheckD:
用于检查你可以去往哪个方向
privatebooleancheckD(chars)
intx=0,y=0;
switch(s)
case'
u'
:
y=-steps;
break;
d'
y=steps;
l'
x=-steps;
r'
x=steps;
returnPacWorld.pacworld[getX()+x][getY()+y];
只有当判定可以往目标方向移动时,角色才可以进行移动
protectedvoidmove(chardirection)
intx=0,y=0;
if(direction=='
&
checkD(direction)==true)y=-steps;
checkD(direction)==true)y=steps;
checkD(direction)==true)x=-steps;
checkD(direction)==true)x=steps;
this.setLocation(this.getX()+x,this.getY()+y);
4.3PacMan类
privateGreenfootImageMopen=newGreenfootImage("
open.png"
);
privateGreenfootImageMclosed=newGreenfootImage("
closed.png"
定义吃豆人运动的两种状态图:
开口和闭口,用于运动时变换显示。
privatevoidmovePic(){
if(mCounter>
=mChange){
setImage(Mopen);
if(mCounter==2*mChange)mCounter=0;
elsesetImage(Mclosed);
在类中,还需要专门设置一个用于标记吃豆人运动时图片的变化显示的量:
privatechard='
;
定义初始运动向左,吃豆人的四个运动方向分别是:
上(Up:
u),下(Down:
d),左(Left:
l),右(Right:
r)。
如:
(键盘点击方向键向上后,改变吃豆人的运动方向向上)
if(Greenfoot.isKeyDown("
up"
)){
setRotation(90);
d='
图4-5
在获得了键盘输入的方向后,改变吃豆人的运动(位置、方向),这里还会对可能移动的方向先进行判断(判定是否可以朝目标方向移动)。
(图4-5)
privatevoidcorner(charside){
for(inti=-7;
i<
=7;
i++){
switch(side){
y=-1;
x=i;
y=1;
x=-1;
y=i;
x=1;
if(PacWorld.pacworld[getX()+x][getY()+y]==true)
setLocation(getX()+x,getY()+y);
吃豆人在吃豆子时需要调用eat函数:
当吃到小豆子时,移除当前接触到的小豆子并让分数加一;
当吃到大豆子时,移除当前接触到的大豆子并让分数加五;
当分数高于200且当前游戏中仍存有豆子时,怪物的速度提升(难度提升)。
privatevoideat(){
if(isTouching(Coin.class)){
removeTouching(Coin.class);
coinsCollected++;
points++;
if(isTouching(Power_Pellet.class)){
removeTouching(Power_Pellet.class);
run=true;
points+=5;
if(coinsCollected==PacWorld.coins&
PacWorld.coins!
=0){
if(points>
200*Ghost.speed)Ghost.speed++;
Greenfoot.setWorld(newPacWorld(l,points,Ghost.speed));
在游戏中,吃豆人难免会出现触碰到怪物而死亡的情况,这个时候就需要死亡函数——在出生点重生,生命减一,并执行一次判断:
当生命所剩为零时重置游戏。
执行这个死亡函数的前提条件是吃豆人触碰到四个怪物中的任意一个(if语句)。
privatevoiddie(){
if(isTouching(Inky.class)||isTouching(Pinky.class)||isTouching(Blinky.class)||isTouching(Clyde.class)){
l--;
setLocation(320,257);
if(l==0){
points=0;
Greenfoot.setWorld(newPacWorld(3,0,1));
4.4Ghost类
在吃豆人游戏中,怪物是一个非常重要的存在,要让一个怪物实现在场景中移动就需要一个用于定义怪物随机移动的函数。
使用random函数随机生成四个方向的移动,当怪物移动到一个方向的尽头时则执行下一个方向的运动判断。
protectedvoidrandomWalk(){
if(PacWorld.pacworld[this.getX()+1][this.getY()]==true)square++;
elseif(PacWorld.pacworld[this.getX()-1][this.getY()]==true)square++;
if(PacWorld.pacworld[this.getX()][this.getY()+1]==true)square++;
elseif(PacWorld.pacworld[this.getX()][this.getY()-1]==true)square++;
if(square>
2){
do{
intr=(int)(int)(Math.random()*4);
switch(r){
case0:
o='
case1:
case2:
case3:
bX=this.getX();
bY=this.getY();
move(d);
aX=this.getX();
aY=this.getY();
move(o);
}while(bX==aX&
bY==aY);
speed;
i++)move(d);
if(this.getX()==320&
this.getY()>
171&
this.getY()<
221)this.setLocation(320,171);
protectedvoidrun(){
if(run)runCount++;
if(isTouching(PacMan.class)&
run==true){
PacMan.points+=20;
setLocation(320,213);
this.start=true;
elseif(800<
runCount&
runCount<
1000){
countR++;
if(countR==50){
countR=0;
this.setImage(uPic);
elsethis.setImage(gPic);
elseif(runCount==1000){
runCount=0;
run=false;
设置怪物由出生点出发(图4-6):
图4-6
protectedvoidleave(){
lCount++;
if(lCount==100){
this.order--;
Count=0;
if(this.position==false&
this.order!
=1){
if(this.getY()==204)this.d='
elseif(this.getY()==221)this.d='
move(this.d);
elseif(this.position==false&
this.order==1){
if(this.getY()<
213)move('
elseif(this.getY()>
elseif(this.getY()==213){
if(this.getX()<
320)move('
elseif(this.getX()>
else{
this.position=true;
elseif(this.position==true){
if(this.getY()>
171)move('
else{
this.start=false;
inta=(int)(Math.random()*2);
if(a==0)this.d='
elsethis.d='
吃豆人有着其运动的样式,而怪物也同样有着四个方向运动的图案变化(图4-7):
图4-7
protectedvoidchangePic(){
if(run==false){
switch(this.d){
this.setImage(this.uPic);
this.setImage(this.dPic);
this.setImage(this.rPic);
this.setImage(this.lPic);
4.5Blank类