打砖块
此游戏代码的核心部分来自于§3.7Breakout游戏,我只是根据引擎适当地修改了代码。比起上一个乒乓游戏(用于Sliverlight的StunEngine引擎),场景只有一个,其实更简单,新多出的东西就是使用包围盒BoundingBox进行碰撞检测,XNA框架中已经内置了BoundingBox.Intersects方法进行相交性检测,所以并没有特别难的东西,细节部分可参考代码的注释。
游戏主类GameplayScreen的代码如下:
namespace Silverlight3dApp
{
/// <summary>
/// 实际进行游戏的屏幕
/// </summary>
class GameplayScreen:Scene2D
{
#region 常量
static readonly Rectangle
GamePaddleRect = new Rectangle(39, 1, 93, 23),
GameBallRect = new Rectangle(1, 1, 36, 36),
GameBlockRect = new Rectangle(136, 1, 62, 27),
GameYouWonRect = new Rectangle(2, 39, 92, 21),
GameYouLostRect = new Rectangle(105, 39, 94, 21);
// 球的速度的乘积因数,0.85表示球的运动速度是挡板速度的0.85倍
const float BallSpeedMultiplicator = 0.85f;
// 球拍的Y坐标
const float paddlePositionY = 570;
/// <summary>
/// 砖块的行列数,设置为11行11列
/// </summary>
const int NumOfColumns = 11,NumOfRows = 11;
#endregion
#region 变量
// 球拍、小球、背景等Renderable2DSceneNode对象。
Renderable2DSceneNode paddle, ball, youWon, youLost, background;
// 挡板的位置,初始设在中间
float paddlePositionX = 353.5f;
// 挡板大小
Vector2 paddleSize = new Vector2(GamePaddleRect.Width, GamePaddleRect.Height);
// 小球的位置
Vector2 ballPosition;
// 小球的大小
Vector2 ballSize = new Vector2(36f, 36f);
// 球的速度
Vector2 ballSpeedVector = new Vector2(0, 0);
// 砖块的数组,如果全部被消,则进入下一个关卡。
bool[,] blocksVisible = new bool[NumOfColumns, NumOfRows];
Renderable2DSceneNode[,] blocks=new Renderable2DSceneNode[NumOfColumns, NumOfRows];
// 砖块的位置数组。
Vector2[,] blockPositions = new Vector2[NumOfColumns, NumOfRows];
// 砖块的包围盒数组。
BoundingBox[,] blockBoxes = new BoundingBox[NumOfColumns, NumOfRows];
// 现在的关卡和得分
int level = 1, score = 0;
// 玩家按space键后开始关卡
bool pressSpaceToStart = true, lostGame = false;
// 显示当前关卡和分数的文字标签
UILabel lblLevel, lblScore;
// 音效
readonly SoundEffectInstance BlockKillInstance;
readonly SoundEffectInstance VictoryInstance;
readonly SoundEffectInstance BallHitInstance;
readonly SoundEffectInstance BallLostInstance;
#endregion
public GameplayScreen(Engine engine):base(engine,"Gameplay")
{
// 加载素材
Texture2D backgroundTexture = engine.Content.Load<Texture2D>("SpaceBackground");
Texture2D gameTexture = engine.Content.Load<Texture2D>("BreakoutGame");
SpriteFont font = engine.Content.Load<SpriteFont>("Font");
SoundEffect BlockKill = engine.Content.Load<SoundEffect>("BreakoutBlockKill");
BlockKillInstance = BlockKill.CreateInstance();
SoundEffect Victory = engine.Content.Load<SoundEffect>("BreakoutVictory");
VictoryInstance = Victory.CreateInstance();
SoundEffect BallHit = engine.Content.Load<SoundEffect>("PongBallHit");
BallHitInstance = BallHit.CreateInstance();
SoundEffect BallLost = engine.Content.Load<SoundEffect>("PongBallLost");
BallLostInstance = BallLost.CreateInstance();
// 背景
background = new Renderable2DSceneNode(backgroundTexture)
{
Size = new Vector2(800, 600)
};
AddNode(background);
// 球拍
paddle = new Renderable2DSceneNode(gameTexture)
{
Size = paddleSize,RectSourceOrigin = GamePaddleRect,
Position = new Vector2(paddlePositionX, paddlePositionY)
};
AddNode(paddle);
// 小球
ball = new Renderable2DSceneNode(gameTexture)
{
Size = ballSize,
RectSourceOrigin = GameBallRect
};
AddNode(ball);
// 初始化所有砖块,设置位置和包围盒
for (int y = 0; y < NumOfRows; y++)
for (int x = 0; x < NumOfColumns; x++)
{
blockPositions[x, y] = new Vector2(
15f + 715f * x / (float)(NumOfColumns - 1),
15f + 330f * y / (float)(NumOfRows - 1));
Vector3 pos = new Vector3(blockPositions[x, y], 0);
Vector3 blockSize = new Vector3(GameBlockRect.Width, GameBlockRect.Height, 0);
blockBoxes[x, y] = new BoundingBox(pos, pos + blockSize);
blocks[x, y] = new Renderable2DSceneNode(gameTexture)
{
Size = new Vector2(GameBlockRect.Width,GameBlockRect.Height),
RectSourceOrigin = GameBlockRect,
Position = blockPositions[x, y]
};
AddNode(blocks[x, y]);
}
// 显示的youWon信息
youWon = new Renderable2DSceneNode(gameTexture)
{
Size = new Vector2(GameYouWonRect.Width, GameYouWonRect.Height),
RectSourceOrigin = GameYouWonRect,
Position = new Vector2(400 - GameYouWonRect.X / 2, 300 - GameYouWonRect.Y / 2),
Visible = false
};
AddNode(youWon);
// 显示的youLost信息
youLost = new Renderable2DSceneNode(gameTexture)
{
Size = new Vector2(GameYouLostRect.Width, GameYouLostRect.Height),
RectSourceOrigin = GameYouLostRect,
Position = new Vector2(400 - GameYouLostRect.Width / 2, 300 - GameYouLostRect.Height / 2),
Visible = false
};
AddNode(youLost);
// 显示当前关卡
lblLevel = new UILabel(this.UiManager, font)
{
Position = new Vector2(10, 10)
};
AddNode(lblLevel);
// 显示当前得分
lblScore = new UILabel(this.UiManager, font);
lblScore.Position = new Vector2(680, 10);
AddNode(lblScore);
// 开始关卡
StartLevel();
}
void StartLevel()
{
// 随机产生关卡,但一关比一关难
for (int y = 0; y < NumOfRows; y++)
for (int x = 0; x < NumOfColumns; x++)
{
blocksVisible[x, y] = RandomHelper.GetRandomInt(10) < level;
blocks[x, y].Visible = blocksVisible[x, y];
}
// 后面的关卡会使用更下面行的砖块增加难度
if (level < 6)
for (int x = 0; x < NumOfColumns; x++)
{
blocksVisible[x, NumOfRows - 1] = false;
blocks[x, NumOfRows - 1].Visible = false;
}
if (level < 4)
for (int x = 0; x < NumOfColumns; x++)
{
blocksVisible[x, NumOfRows - 2] = false;
blocks[x, NumOfRows - 2].Visible = false;
}
if (level < 2)
for (int x = 0; x < NumOfColumns; x++)
{
blocksVisible[x, NumOfRows - 3] = false;
blocks[x, NumOfRows - 3].Visible = false;
}
// 暂停小球
ballSpeedVector = Vector2.Zero;
// 显示文字
lblLevel.Text = "Level:" + level;
lblScore.Text = "Score:" + score;
//youLost.Visible = false;
// 等待玩家按空格键开始游戏
pressSpaceToStart = true;
}
/// <summary>
/// 新建一个小球
/// </summary>
public void StartNewBall()
{
if (lostGame)
{
level = 1;
score = 0;
lostGame = false;
StartLevel();
}
// 随机产生小球方向,但总是向上方发射
ballSpeedVector =
new Vector2(RandomHelper.GetRandomFloat(-1, +1), -1);
ballSpeedVector.Normalize();
pressSpaceToStart = false;
}
public override void Update(GameTime gameTime)
{
float elapsed =
(float)gameTime.ElapsedGameTime.TotalSeconds;
// 每秒移动400像素的距离
float moveFactorPerSecond = 400f *
(float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
// 按键盘左右键移动挡板
if (Input.Keyboard.IsKeyDown(Key.Right))
{
paddlePositionX += moveFactorPerSecond;
paddle.Position = new Vector2(paddlePositionX, paddlePositionY);
}
if (Input.Keyboard .IsKeyDown(Key.Left))
{
paddlePositionX -= moveFactorPerSecond;
paddle.Position = new Vector2(paddlePositionX, paddlePositionY);
}
// 将球板的运动限制在屏幕中
if (paddlePositionX < 4f)
paddlePositionX = 4f;
if (paddlePositionX > 705f)
paddlePositionX = 705f;
// 如果游戏还未开始,则将球放置在球板上。
if (pressSpaceToStart)
{
ballPosition = new Vector2(paddlePositionX - 20 + paddleSize .X /2, 537f);
ball.Position = ballPosition;
// 按下键盘空格键开始游戏发射小球。
if (Input.KeyboardKeyJustPressed (Key.Space))
{
StartNewBall();
youWon.Visible = false;
youLost.Visible = false;
}
}
// 如果游戏开始则进行碰撞检测,更新小球位置
else
{
// 碰撞检测
CheckBallCollisions(moveFactorPerSecond);
// 更新小球位置
ballPosition += ballSpeedVector *
moveFactorPerSecond * BallSpeedMultiplicator;
ball.Position = ballPosition;
// 检测是否所有砖块都被消除,如果是则完成了此关卡。
bool allBlocksKilled = true;
for (int y = 0; y < NumOfRows; y++)
{
for (int x = 0; x < NumOfColumns; x++)
{
if (blocksVisible[x, y])
{
allBlocksKilled = false;
break;
}
}
}
// 如果游戏胜利则进入下一关卡
if (allBlocksKilled)
{
// 播放声音
VictoryInstance.Play();
youWon.Visible = true;
level++;
StartLevel();
}
// 小球出下边界
if (ballPosition.Y > 564f)
{
// 播放声音
BallLostInstance.Play();
// 显示失败信息
youLost.Visible = true;
lostGame = true;
pressSpaceToStart = true;
}
}
base.Update(gameTime);
}
/// <summary>
/// 碰撞检测
/// </summary>
void CheckBallCollisions(float moveFactorPerSecond)
{
// 如果小球碰到顶部,则向下弹回
if ((ballPosition.Y < 0)&&(ballSpeedVector .Y <0))
{
ballSpeedVector.Y = -ballSpeedVector.Y;
if (ballPosition.X < 0)
ballPosition.X = 0;
// 播放声音
BallHitInstance.Play();
}
// 如果小球与左右边界碰撞,则反弹小球
if (ballPosition.X < 0 || ballPosition.X > 764)
{
ballSpeedVector.X = -ballSpeedVector.X;
if (ballPosition.X < 0)
ballPosition.X = 0;
if (ballPosition.X > 764)
ballPosition.X = 764;
// 播放声音
BallHitInstance.Play();
}
// -------------------------
// 检查小球与挡板的碰撞
// -------------------------
// 小球的包围盒
BoundingBox ballBox = new BoundingBox(
new Vector3(ballPosition.X, ballPosition.Y, 0),
new Vector3(ballPosition.X + ballSize.X, ballPosition.Y + ballSize.Y, 0));
// 挡板的包围盒
BoundingBox paddleBox = new BoundingBox(
new Vector3(paddlePositionX, paddlePositionY, 0),
new Vector3(paddlePositionX + paddleSize.X, paddlePositionY + paddleSize.Y, 0));
if (ballBox.Intersects(paddleBox))
{
// 若小球中心正好撞于挡板中心,则X方向速度保持不变;
// 若碰撞位置位于挡板右侧,则越靠右X方向的速度增加(减少)得越多;碰撞在挡板左侧处理方法相同。
ballSpeedVector.X += (18f + ballPosition.X - paddlePositionX-47) / (50f);
// 将X方向的反弹限制在-1到+1之间
if (ballSpeedVector.X < -1)
ballSpeedVector.X = -1;
if (ballSpeedVector.X > 1)
ballSpeedVector.X = 1;
// 反弹小球
if(ballSpeedVector.Y>0)
ballSpeedVector.Y = -ballSpeedVector.Y;
// 归一化球的速度矢量
ballSpeedVector.Normalize();
// 播放声音
BallHitInstance.Play();
}
// -------------------------
// 检查小球与砖块的碰撞
// -------------------------
for (int y = 0; y < NumOfRows; y++)
{
for (int x = 0; x < NumOfColumns; x++)
{
if (blocksVisible[x, y])
{
if (ballBox.Intersects(blockBoxes[x, y]))
{
// 消去砖块
blocksVisible[x, y] = false;
blocks[x, y].Visible = false;
// 加分
score++;
lblScore.Text = "Score:" + score;
// 播放声音
BlockKillInstance.Play();
// 反弹小球,但首先需要判断小球撞击了砖块的哪一面。
// 小球是否与砖块的右表面相撞
if (Math.Abs(blockBoxes[x, y].Max.X - ballBox.Min.X) <
moveFactorPerSecond)
{
ballSpeedVector.X = Math.Abs(ballSpeedVector.X);
// 将小球回退到一帧前的位置
ballPosition.X += moveFactorPerSecond;
}
// 小球是否与砖块的左表面相撞
else if (Math.Abs(blockBoxes[x, y].Min.X - ballBox.Max.X) <
moveFactorPerSecond)
{
ballSpeedVector.X = -Math.Abs(ballSpeedVector.X);
// 将小球回退到一帧前的位置
ballPosition.X -= moveFactorPerSecond;
}
// 小球是否与砖块的下表面相撞
else if (Math.Abs(blockBoxes[x, y].Max.Y - ballBox.Min.Y) <
moveFactorPerSecond)
{
ballSpeedVector.Y = Math.Abs(ballSpeedVector.Y);
// 将小球回退到一帧前的位置
ballPosition.Y += moveFactorPerSecond;
}
// 小球是否与砖块的上表面相撞
else if (Math.Abs(blockBoxes[x, y].Min.Y - ballBox.Max.Y) <
moveFactorPerSecond)
{
ballSpeedVector.Y = -Math.Abs(ballSpeedVector.Y);
// 将小球回退到一帧前的位置
ballPosition.Y -= moveFactorPerSecond;
}
else
ballSpeedVector *= -1;
// 跳出循环,这样一次只处理一个砖块的碰撞
break;
}
}
}
}
}
}
}
源代码下载:BreakOut.zip。
文件下载(已下载 2171 次)发布时间:2012/10/11 下午10:25:37 阅读次数:7314
