Alien Game逻辑
在这最有一个部分你将创建game-specific logic和 helper方法和类。胜利就在眼前,你的第一个winphone7程序就要出现了,加油加油!!(感谢http://winphone.us/)。另外最近imagecup要开始了,帮小美女推一下,学生们有时间都去参加。为了帮大家了解imagecup和winphone7的开发,我可能会针对学生讲一些入门级webcast,有详细计划再通知大家。刚刚翻译了imagecup的winphone7的评判标准,觉得有一条很好玩,就是要让使用了你的程序的人想去买一部winphone,呵呵这个占20%,大家加油了。另外有人要源代码,我把最后成品的源代码放到下一篇“番外篇”了,大家感兴趣可以去下载。
1. 在 GameplayScreen.cs 文件里,创建一个新的helper 类(在GameplayScreen类之外)根据下面的代码:
(Code Snippet – Game Development with XNA – Gameplay Screen – Bullet class)
C#
/// <summary>
/// Represents either an alien or player bullet
/// </summary>
public class Bullet
{
public Vector2 Position;
public Vector2 Velocity;
public bool IsAlive;
}
2. 在Bullet 类后面添加两个helper类:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player and Alien classes)
C#
/// <summary>
/// The player's state
/// </summary>
public class Player
{
public Vector2 Position;
public Vector2 Velocity;
public float Width;
public float Height;
public bool IsAlive;
public float FireTimer;
public float RespawnTimer;
public string Name;
public Texture2D Picture;
public int Score;
public int Lives;
}
/// <summary>
/// Data for an alien. The only difference between the ships
/// and the badguys are the texture used.
/// </summary>
public class Alien
{
public Vector2 Position;
public Texture2D Texture;
public Vector2 Velocity;
public float Width;
public float Height;
public int Score;
public bool IsAlive;
public float FireTimer;
public float Accuracy;
public int FireCount;
}
3. 添加如下GameplayScreen类的变量:
(Code Snippet – Game Development with XNA – Gameplay Screen – even more variables)
C#
Player player;
List<Alien> aliens;
List<Bullet> alienBullets;
List<Bullet> playerBullets;
4. 在构造函数中初始化这些变量,如下所示:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player and Alien Initialization)
C#
public GameplayScreen()
{
...
player = new Player();
playerBullets = new List<Bullet>();
aliens = new List<Alien>();
alienBullets = new List<Bullet>();
Accelerometer = new Accelerometer();
if (AccelerometerSensor.Default.State == SensorState.Ready)
{
...
}
...
}
5. 初始化player变量Width 和 Height在LoadContent 方法中 (初始化ParticleSystem之后):
(Code Snippet – Game Development with XNA – Gameplay Screen – Player Initialization in LoadContent method)
C#
public override void LoadContent()
{
...
particles = new ParticleSystem(ScreenManager.Game.Content, ScreenManager.SpriteBatch);
player.Width = tankTexture.Width;
player.Height = tankTexture.Height;
base.LoadContent();
}
6. 下面的一小段代码将添加游戏的逻辑。他们将根据用户的输入来改变“player1”的移动。导航到 HandleInput 的方法,并找到以下行:
C#
//TODO: Update player Velocity over X axis #1
在它的后面添加如下代码段:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player Movements 1 in HandleInput method)
C#
player.Velocity.X = movement;
7. 找到以下行 (在HandleInput 方法中):
C#
//TODO: Update player velocity over X axis #2
在其后添加如下代码:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player Movements 2 in HandleInput method)
C#
player.Velocity.X = -1.0f;
8. 找到以下行 (在HandleInput 方法中):
C#
//TODO: Update player velocity over X axis #3
在其后添加如下代码:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player Movements 3 in HandleInput method)
C#
player.Velocity.X = 1.0f;
9. 找到以下行 (在HandleInput 方法中):
C#
//TODO: Update player velocity over X axis #4
在其后添加如下代码:
(Code Snippet – Game Development with XNA – Gameplay Screen – Player Movements 4 in HandleInput method)
C#
player.Velocity.X = MathHelper.Min(input.CurrentGamePadStates[0].ThumbSticks.Left.X * 2.0f, 1.0f);
10. 找到以下行:
C#
//TODO: Fire the bullet
更改"if"语句根据下面的代码片断::
(Code Snippet – Game Development with XNA – Gameplay Screen – HandleInput firing the bullet code)
C#
if (player.FireTimer <= 0.0f && player.IsAlive && !gameOver)
{
Bullet bullet = CreatePlayerBullet();
bullet.Position = new Vector2((int)(player.Position.X + player.Width / 2) - bulletTexture.Width / 2, player.Position.Y - 4);
bullet.Velocity = new Vector2(0, -256.0f);
player.FireTimer = 1.0f;
particles.CreatePlayerFireSmoke(player);
playerFired.Play();
}
else if (gameOver)
finishCurrentGame();
11. 创建 GameplayScreen 类中的以下方法:
此方法将创建Bullet类的一个实例。 此实例使用在前面的代码段。
(Code Snippet – Game Development with XNA – Gameplay Screen – CreatePlayerBullet method)
C#
/// <summary>
/// Returns an instance of a usable player bullet. Prefers reusing an /// existing (dead)
/// bullet over creating a new instance.
/// </summary>
/// <returns>A bullet ready to place into the world.</returns>
Bullet CreatePlayerBullet()
{
Bullet b = null;
for (int i = 0; i < playerBullets.Count; ++i)
{
if (playerBullets[i].IsAlive == false)
{
b = playerBullets[i];
break;
}
}
if (b == null)
{
b = new Bullet();
playerBullets.Add(b);
}
b.IsAlive = true;
return b;
}
12. 更改Update方法。在"base.Update(…)"方法调用之前,添加下面的蓝色突出显示的代码段:
此块的代码实际上提供了游戏的逻辑 – 它移动玩家和调用该方法来更新异型和重新计算子弹的位置。
(Code Snippet – Game Development with XNA – Gameplay Screen – Update method)
C#
public override void Update(GameTime gameTime,
bool otherScreenHasFocus, bool coveredByOtherScreen)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
if (IsActive)
{
// Move the player
if (player.IsAlive == true)
{
player.Position += player.Velocity * 128.0f * elapsed;
player.FireTimer -= elapsed;
if (player.Position.X <= 0.0f)
player.Position = new Vector2(0.0f, player.Position.Y);
if (player.Position.X + player.Width >= worldBounds.Right)
player.Position = new Vector2(worldBounds.Right - player.Width, player.Position.Y);
}
Respawn(elapsed);
UpdateAliens(elapsed);
UpdateBullets(elapsed);
CheckHits();
if (player.IsAlive && player.Velocity.LengthSquared() > 0.0f)
particles.CreatePlayerDust(player);
particles.Update(elapsed);
}
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
}
13. 向GameplayScreen 类中添加helper方法:
Note: 下面的代码段中添加大量的帮助器方法。 方法用途如下:
重生: 检查是否,玩家已经"死亡"但这场游戏还没有结束, 如果这,它完成等待 respawnTimer结束后创建一个新的玩家实例屏幕的中间。
UpdateBullets: 检查和更新屏幕上的玩家和异形子弹的位置。
UpdateAliens: 移动异形,并计算是否发射子弹和朝哪个方向。
CheckHits: 检查所有的子弹和玩家/异形的碰撞。它还将处理游戏逻辑,当命中发生时,进行让死亡,加分或者结束游戏等。
(Code Snippet – Game Development with XNA – Gameplay Screen – Helper update methods)
C#
/// <summary>
/// Handles respawning the player if we are playing a game and the player is dead.
/// </summary>
/// <param name="elapsed">Time elapsed since Respawn was called last.</param>
void Respawn(float elapsed)
{
if (gameOver)
return;
if (!player.IsAlive)
{
player.RespawnTimer -= elapsed;
if (player.RespawnTimer <= 0.0f)
{
// See if there are any bullets close...
int left = worldBounds.Width / 2 - tankTexture.Width / 2 - 8;
int right = worldBounds.Width / 2 + tankTexture.Width / 2 + 8;
for (int i = 0; i < alienBullets.Count; ++i)
{
if (alienBullets[i].IsAlive == false)
continue;
if (alienBullets[i].Position.X >= left || alienBullets[i].Position.X <= right)
return;
}
player.IsAlive = true;
player.Position = new Vector2(worldBounds.Width / 2 - player.Width / 2, worldBounds.Bottom - groundTexture.Height + 2 - player.Height);
player.Velocity = Vector2.Zero;
player.Lives--;
}
}
}
/// <summary>
/// Moves all of the bullets (player and alien) and prunes "dead" bullets.
/// </summary>
/// <param name="elapsed"></param>
void UpdateBullets(float elapsed)
{
for (int i = 0; i < playerBullets.Count; ++i)
{
if (playerBullets[i].IsAlive == false)
continue;
playerBullets[i].Position += playerBullets[i].Velocity * elapsed;
if (playerBullets[i].Position.Y < -32)
{
playerBullets[i].IsAlive = false;
hitStreak = 0;
}
}
for (int i = 0; i < alienBullets.Count; ++i)
{
if (alienBullets[i].IsAlive == false)
continue;
alienBullets[i].Position += alienBullets[i].Velocity * elapsed;
if (alienBullets[i].Position.Y > worldBounds.Height - groundTexture.Height - laserTexture.Height)
alienBullets[i].IsAlive = false;
}
}
/// <summary>
/// Moves the aliens and performs their "thinking" by determining if they
/// should shoot and where.
/// </summary>
/// <param name="elapsed">The elapsed time since UpdateAliens was called last.</param>
private void UpdateAliens(float elapsed)
{
// See if it's time to spawn an alien;
alienSpawnTimer -= elapsed;
if (alienSpawnTimer <= 0.0f)
{
SpawnAlien();
alienSpawnTimer += alienSpawnRate;
}
for (int i = 0; i < aliens.Count; ++i)
{
if (aliens[i].IsAlive == false)
continue;
aliens[i].Position += aliens[i].Velocity * elapsed;
if ((aliens[i].Position.X < -aliens[i].Width - 64 && aliens[i].Velocity.X < 0.0f) ||
(aliens[i].Position.X > worldBounds.Width + 64 && aliens[i].Velocity.X > 0.0f))
{
aliens[i].IsAlive = false;
continue;
}
aliens[i].FireTimer -= elapsed;
if (aliens[i].FireTimer <= 0.0f && aliens[i].FireCount > 0)
{
if (player.IsAlive)
{
Bullet bullet = CreateAlienBullet();
bullet.Position.X = aliens[i].Position.X + aliens[i].Width / 2 - laserTexture.Width / 2;
bullet.Position.Y = aliens[i].Position.Y + aliens[i].Height;
if ((float)random.NextDouble() <= aliens[i].Accuracy)
{
bullet.Velocity = Vector2.Normalize(player.Position - aliens[i].Position) * 64.0f;
}
else
{
bullet.Velocity = new Vector2(-8.0f + 16.0f * (float)random.NextDouble(), 64.0f);
}
alienFired.Play();
}
aliens[i].FireCount--;
}
}
}
/// <summary>
/// Performs all bullet and player/alien collision detection. Also handles game logic
/// when a hit occurs, such as killing something, adding score, ending the game, etc.
/// </summary>
void CheckHits()
{
if (gameOver)
return;
for (int i = 0; i < playerBullets.Count; ++i)
{
if (playerBullets[i].IsAlive == false)
continue;
for (int a = 0; a < aliens.Count; ++a)
{
if (aliens[a].IsAlive == false)
continue;
if ((playerBullets[i].Position.X >= aliens[a].Position.X && playerBullets[i].Position.X <= aliens[a].Position.X + aliens[a].Width) && (playerBullets[i].Position.Y >= aliens[a].Position.Y && playerBullets[i].Position.Y <= aliens[a].Position.Y + aliens[a].Height))
{
playerBullets[i].IsAlive = false;
aliens[a].IsAlive = false;
hitStreak++;
player.Score += aliens[a].Score * (hitStreak / 5 + 1);
if (player.Score > highScore)
highScore = player.Score;
if (player.Score > nextLife)
{
player.Lives++;
nextLife += nextLife;
}
levelKillCount--;
if (levelKillCount <= 0)
AdvanceLevel();
particles.CreateAlienExplosion(new Vector2(aliens[a].Position.X + aliens[a].Width / 2, aliens[a].Position.Y + aliens[a].Height / 2));
alienDied.Play();
}
}
}
if (player.IsAlive == false)
return;
for (int i = 0; i < alienBullets.Count; ++i)
{
if (alienBullets[i].IsAlive == false)
continue;
if ((alienBullets[i].Position.X >= player.Position.X + 2 && alienBullets[i].Position.X <= player.Position.X + player.Width - 2) && (alienBullets[i].Position.Y >= player.Position.Y + 2 && alienBullets[i].Position.Y <= player.Position.Y + player.Height))
{
alienBullets[i].IsAlive = false;
player.IsAlive = false;
hitStreak = 0;
player.RespawnTimer = 3.0f;
particles.CreatePlayerExplosion(new Vector2(player.Position.X + player.Width / 2, player.Position.Y + player.Height / 2));
playerDied.Play();
if (player.Lives <= 0)
{
gameOver = true;
}
}
}
}
/// <summary>
/// Advances the difficulty of the game one level.
/// </summary>
void AdvanceLevel()
{
baseLevelKillCount += 5;
levelKillCount = baseLevelKillCount;
alienScore += 25;
alienSpawnRate -= 0.3f;
alienMaxAccuracy += 0.1f;
if (alienMaxAccuracy > 0.75f)
alienMaxAccuracy = 0.75f;
alienSpeedMin *= 1.35f;
alienSpeedMax *= 1.35f;
if (alienSpawnRate < 0.33f)
alienSpawnRate = 0.33f;
if (transitionFactor == 1.0f)
{
transitionRate = -0.5f;
}
else
{
transitionRate = 0.5f;
}
}
14. 在GameplayScreen 类中添加如下代码:
Note: 下面的代码片断还增加了大量的帮助器方法。 他们的目的是,如下所示: CreateAlienBullet: 创建的异形的子弹,将用于异形向玩家开火。
SpawnAlien: 初始化一个新的异形实例,设置了的初始位置、速度,选择颜色纹理等。
CreateAlien: 创建新异形的实例,并将其添加到异形集合
(Code Snippet – Game Development with XNA – Gameplay Screen – Helper aliens methods)
C#
/// <summary>
/// Returns an instance of a usable alien bullet. Prefers reusing an existing (dead)
/// bullet over creating a new instance.
/// </summary>
/// <returns>A bullet ready to place into the world.</returns>
Bullet CreateAlienBullet()
{
Bullet b = null;
for (int i = 0; i < alienBullets.Count; ++i)
{
if (alienBullets[i].IsAlive == false)
{
b = alienBullets[i];
break;
}
}
if (b == null)
{
b = new Bullet();
alienBullets.Add(b);
}
b.IsAlive = true;
return b;
}
/// <summary>
/// Creates an instance of an alien, sets the initial state, and places it into the world.
/// </summary>
private void SpawnAlien()
{
Alien newAlien = CreateAlien();
if (random.Next(2) == 1)
{
newAlien.Position.X = -64.0f;
newAlien.Velocity.X = random.Next((int)alienSpeedMin, (int)alienSpeedMax);
}
else
{
newAlien.Position.X = worldBounds.Width + 32;
newAlien.Velocity.X = -random.Next((int)alienSpeedMin, (int)alienSpeedMax);
}
newAlien.Position.Y = 24.0f + 80.0f * (float)random.NextDouble();
// Aliens
if (transitionFactor > 0.0f)
{
switch (random.Next(4))
{
case 0:
newAlien.Texture = badguy_blue;
break;
case 1:
newAlien.Texture = badguy_red;
break;
case 2:
newAlien.Texture = badguy_green;
break;
case 3:
newAlien.Texture = badguy_orange;
break;
}
}
else
{
newAlien.Texture = alienTexture;
}
newAlien.Width = newAlien.Texture.Width;
newAlien.Height = newAlien.Texture.Height;
newAlien.IsAlive = true;
newAlien.Score = alienScore;
float duration = screenHeight / newAlien.Velocity.Length();
newAlien.FireTimer = duration * (float)random.NextDouble();
newAlien.FireCount = 1;
newAlien.Accuracy = alienMaxAccuracy;
}
/// <summary>
/// Returns an instance of a usable alien instance. Prefers reusing an existing (dead)
/// alien over creating a new instance.
/// </summary>
/// <returns>An alien ready to place into the world.</returns>
Alien CreateAlien()
{
Alien b = null;
for (int i = 0; i < aliens.Count; ++i)
{
if (aliens[i].IsAlive == false)
{
b = aliens[i];
break;
}
}
if (b == null)
{
b = new Alien();
aliens.Add(b);
}
b.IsAlive = true;
return b;
}
15. .导航到Draw方法,并添加下面的蓝色突出显示的代码段,在Screen.SpriteBatch.Begin() 和 Screen.SpriteBatch.End() 之间:
这样更改方法方法将呼调用helper方法以在屏幕上绘制变化后的内容。
(Code Snippet – Game Development with XNA – Gameplay Screen – Draw method update)
C#
public override void Draw(GameTime gameTime)
{
float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
ScreenManager.SpriteBatch.Begin();
DrawBackground(elapsedTime);
DrawAliens();
DrawPlayer();
DrawBullets();
particles.Draw();
DrawForeground(elapsedTime);
DrawHud();
ScreenManager.SpriteBatch.End();
}
16. 在Draw 方法后面添加如下方法:
Note: 下面的代码段中添加大量的绘图相关的Helper方法。 方法用途如下: DrawPlayer: 绘制玩家的坦克。
DrawAliens: 绘制所有异形。
DrawBullets: 绘制所有的子弹 (玩家的和异形的)。
DrawForeground: 作为前台绘制云,并将其移动。
DrawBackground: 绘制草、 丘陵、 山地和太阳/月亮。 此外处理白天和黑夜之间转换。
DrawHud: 绘制分数、剩余的生命值和" GAME OVER "需要的时候。
drawString: 绘制隐藏的文本的通用方法。
(Code Snippet – Game Development with XNA – Gameplay Screen – Draw methods)
C#
/// <summary>
/// Draws the player's tank
/// </summary>
void DrawPlayer()
{
if (!gameOver && player.IsAlive)
{
ScreenManager.SpriteBatch.Draw(tankTexture, player.Position, Color.White);
}
}
/// <summary>
/// Draws all of the aliens.
/// </summary>
void DrawAliens()
{
for (int i = 0; i < aliens.Count; ++i)
{
if (aliens[i].IsAlive) ScreenManager.SpriteBatch.Draw(aliens[i].Texture, new Rectangle((int)aliens[i].Position.X, (int)aliens[i].Position.Y, (int)aliens[i].Width, (int)aliens[i].Height), Color.White);
}
}
/// <summary>
/// Draw both the player and alien bullets.
/// </summary>
private void DrawBullets()
{
for (int i = 0; i < playerBullets.Count; ++i)
{
if (playerBullets[i].IsAlive)
ScreenManager.SpriteBatch.Draw(bulletTexture, playerBullets[i].Position, Color.White);
}
for (int i = 0; i < alienBullets.Count; ++i)
{
if (alienBullets[i].IsAlive)
ScreenManager.SpriteBatch.Draw(laserTexture, alienBullets[i].Position, Color.White);
}
}
/// <summary>
/// Draw the foreground, which is basically the clouds. I think I had planned on one point
/// having foreground grass that was drawn in front of the tank.
/// </summary>
/// <param name="elapsedTime">The elapsed time since last Draw</param>
private void DrawForeground(float elapsedTime)
{
// Move the clouds. Movement seems like an Update thing to do, but this animations
// have no impact over gameplay.
cloud1Position += new Vector2(24.0f, 0.0f) * elapsedTime;
if (cloud1Position.X > screenWidth)
cloud1Position.X = -cloud1Texture.Width * 2.0f;
cloud2Position += new Vector2(16.0f, 0.0f) * elapsedTime;
if (cloud2Position.X > screenWidth)
cloud2Position.X = -cloud1Texture.Width * 2.0f;
ScreenManager.SpriteBatch.Draw(cloud1Texture, cloud1Position, Color.White);
ScreenManager.SpriteBatch.Draw(cloud2Texture, cloud2Position, Color.White);
}
/// <summary>
/// Draw the grass, hills, mountains, and sun/moon. Handle transitioning
/// between day and night as well.
/// </summary>
/// <param name="elapsedTime">The elapsed time since last Draw</param>
private void DrawBackground(float elapsedTime)
{
transitionFactor += transitionRate * elapsedTime;
if (transitionFactor < 0.0f)
{
transitionFactor = 0.0f;
transitionRate = 0.0f;
}
if (transitionFactor > 1.0f)
{
transitionFactor = 1.0f;
transitionRate = 0.0f;
}
Vector3 day = Color.White.ToVector3();
Vector3 night = new Color(80, 80, 180).ToVector3();
Vector3 dayClear = Color.CornflowerBlue.ToVector3();
Vector3 nightClear = night;
Color clear = new Color(Vector3.Lerp(dayClear, nightClear, transitionFactor));
Color tint = new Color(Vector3.Lerp(day, night, transitionFactor));
// Clear the background, using the day/night color
ScreenManager.Game.GraphicsDevice.Clear(clear);
// Draw the mountains
ScreenManager.SpriteBatch.Draw(mountainsTexture, new Vector2(0, screenHeight - mountainsTexture.Height), tint);
// Draw the hills
ScreenManager.SpriteBatch.Draw(hillsTexture, new Vector2(0, screenHeight - hillsTexture.Height), tint);
// Draw the ground
ScreenManager.SpriteBatch.Draw(groundTexture, new Vector2(0, screenHeight - groundTexture.Height), tint);
// Draw the sun or moon (based on time)
ScreenManager.SpriteBatch.Draw(sunTexture, sunPosition, new Color(255, 255, 255, (byte)(255.0f * (1.0f - transitionFactor))));
ScreenManager.SpriteBatch.Draw(moonTexture, sunPosition, new Color(255, 255, 255, (byte)(255.0f * transitionFactor)));
}
/// <summary>
/// Draw the hud, which consists of the score elements and the GAME OVER tag.
/// </summary>
void DrawHud()
{
float scale = 2.0f;
if (gameOver)
{
Vector2 size = menuFont.MeasureString("GAME OVER");
DrawString(menuFont, "GAME OVER", new Vector2(ScreenManager.Game.GraphicsDevice.Viewport.Width / 2 - size.X, ScreenManager.Game.GraphicsDevice.Viewport.Height / 2 - size.Y / 2), new Color(255, 64, 64), scale);
}
else
{
int bonus = 100 * (hitStreak / 5);
string bonusString = (bonus > 0 ? " (" + bonus.ToString(System.Globalization.CultureInfo.CurrentCulture) + "%)" : "");
// Score
DrawString(scoreFont, "SCORE: " + player.Score.ToString(System.Globalization.CultureInfo .CurrentCulture) + bonusString, new Vector2(leftOffset, topOffset), Color.Yellow, scale);
string text = "LIVES: " + player.Lives.ToString(System.Globalization.CultureInfo .CurrentCulture);
Vector2 size = scoreFont.MeasureString(text);
size *= scale;
// Lives
DrawString(scoreFont, text, new Vector2(screenWidth - leftOffset - (int)size.X, topOffset), Color.Yellow, scale);
DrawString(scoreFont, "LEVEL: " + (((baseLevelKillCount - 5) / 5) + 1).ToString(System.Globalization.CultureInfo.CurrentCulture), new Vector2(leftOffset, screenHeight - bottomOffset), Color.Yellow, scale);
text = "HIGH SCORE: " + highScore.ToString(System.Globalization.CultureInfo
.CurrentCulture);
size = scoreFont.MeasureString(text);
DrawString(scoreFont, text, new Vector2(screenWidth - leftOffset - (int)size.X * 2, screenHeight - bottomOffset), Color.Yellow, scale);
}
}
/// <summary>
/// A simple helper to draw shadowed text.
/// </summary>
void DrawString(SpriteFont font, string text,
Vector2 position, Color color)
{
ScreenManager.SpriteBatch.DrawString(font, text, new Vector2(position.X + 1, position.Y + 1), Color.Black);
ScreenManager.SpriteBatch.DrawString(font, text, position, color);
}
/// <summary>
/// A simple helper to draw shadowed text.
/// </summary>
void DrawString(SpriteFont font, string text,
Vector2 position, Color color, float fontScale)
{
ScreenManager.SpriteBatch.DrawString(font, text, new Vector2(position.X + 1, position.Y + 1), Color.Black, 0, new Vector2(0, font.LineSpacing / 2), fontScale, SpriteEffects.None, 0);
ScreenManager.SpriteBatch.DrawString(font, text, position, color, 0, new Vector2(0, font.LineSpacing / 2), fontScale, SpriteEffects.None, 0);
}
17. 找到 LoadContent 方法,在base.LoadContent() 调用后面体检如下代码:
C#
public override void LoadContent()
{
...
player.Width = tankTexture.Width;
player.Height = tankTexture.Height;
base.LoadContent();
LoadHighscore();
Start();
}
18. 找到 UnloadContent 方法并在“particles = null;”之前的位置添加下面的代码段:
C#
public override void UnloadContent()
{
SaveHighscore();
particles = null;
base.UnloadContent();
}
19. 创建一个区域loading/unloading high scores logic。我们需要通过Isolated Storage来读取Windows Phone 文件系统中的数据。用如下代码在GameplayScreen类中创建logic:
(Code Snippet – Game Development with XNA – Gameplay Screen – Highscore storage methods)
C#
#region Highscore loading/saving logic
/// <summary>
/// Saves the current highscore to a text file. The StorageDevice was selected during screen loading.
/// </summary>
private void SaveHighscore()
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("highscores.txt", FileMode.Create, isf))
{
using (StreamWriter writer = new StreamWriter(isfs))
{
writer.Write(highScore.ToString(System.Globalization.CultureInfo.InvariantCulture));
writer.Flush();
writer.Close();
}
}
}
}
/// <summary>
/// Loads the high score from a text file. The StorageDevice was selected during the loading screen.
/// </summary>
private void LoadHighscore()
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isf.FileExists("highscores.txt"))
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("highscores.txt", FileMode.Open, isf))
{
using (StreamReader reader = new StreamReader(isfs))
{
try
{
highScore = Int32.Parse(reader.ReadToEnd(), System.Globalization.CultureInfo.InvariantCulture);
}
catch (FormatException)
{
highScore = 10000;
}
finally
{
if (reader != null)
reader.Close();
}
}
}
}
}
}
#endregion
20. 在GameplayScreen 类中创建Start方法来开始一个新游戏:
(Code Snippet – Game Development with XNA – Gameplay Screen – Start method)
C#
/// <summary>
/// Starts a new game session, setting all game states to initial values.
/// </summary>
void Start()
{
if (gameOver)
{
player.Score = 0;
player.Lives = 3;
player.RespawnTimer = 0.0f;
gameOver = false;
aliens.Clear();
alienBullets.Clear();
playerBullets.Clear();
Respawn(0.0f);
}
transitionRate = 0.0f;
transitionFactor = 0.0f;
levelKillCount = 5;
baseLevelKillCount = 5;
alienScore = 25;
alienSpawnRate = 1.0f;
alienMaxAccuracy = 0.25f;
alienSpeedMin = 24.0f;
alienSpeedMax = 32.0f;
alienSpawnRate = 2.0f;
alienSpawnTimer = alienSpawnRate;
nextLife = 5000;
}
21. 打开 ParticleSystem.cs文件
22. 添加如下命名申明:
(Code Snippet – Game Development with XNA – ParticleSystem – using statement)
C#
using AlienGame;
23. 在这个步骤,你将添加两个方法。一个用来创建玩家控制坦克时泥土/灰尘的效果。一个用来创建玩家发射子弹时,火焰的效果。添加如下方法到ParticleSystem 类中:
(Code Snippet – Game Development with XNA – ParticleSystem – Helper player effects methods)
C#
/// <summary>
/// Creates the mud/dust effect when the player moves.
/// </summary>
/// <param name="position">Where on the screen to create the effect.</param>
public void CreatePlayerDust(Player player)
{
for (int i = 0; i < 2; ++i)
{
Particle p = CreateParticle();
p.Texture = smoke;
p.Color = new Color(125, 108, 43);
p.Position.X = player.Position.X + player.Width * (float)random.NextDouble();
p.Position.Y = player.Position.Y + player.Height - 3.0f * (float)random.NextDouble();
p.Alpha = 1.0f;
p.AlphaRate = -2.0f;
p.Life = 0.5f;
p.Rotation = 0.0f;
p.RotationRate = -2.0f + 4.0f * (float)random.NextDouble();
p.Scale = 0.25f;
p.ScaleRate = 0.5f;
p.Velocity.X = -4 + 8.0f * (float)random.NextDouble();
p.Velocity.Y = -8 + 4.0f * (float)random.NextDouble();
}
}
/// <summary>
/// Creates the effect for when the player fires a bullet.
/// </summary>
/// <param name="position">Where on the screen to create the effect.</param>
public void CreatePlayerFireSmoke(Player player)
{
for (int i = 0; i < 8; ++i)
{
Particle p = CreateParticle();
p.Texture = smoke;
p.Color = Color.White;
p.Position.X = player.Position.X + player.Width / 2;
p.Position.Y = player.Position.Y;
p.Alpha = 1.0f;
p.AlphaRate = -1.0f;
p.Life = 1.0f;
p.Rotation = 0.0f;
p.RotationRate = -2.0f + 4.0f * (float)random.NextDouble();
p.Scale = 0.25f;
p.ScaleRate = 0.25f;
p.Velocity.X = -4 + 8.0f * (float)random.NextDouble();
p.Velocity.Y = -16.0f + -32.0f * (float)random.NextDouble();
}
}
24. 编译并运行该应用程序。 选择"开始游戏"菜单项,并享受你自己编写的游戏吧。
Note: 在模拟器中我们使用桌面的键盘移动车辆,您需要首先按PAUSE /BREAK 键。 这将开启或者关闭仿真程序软件输入面板 (SIP) 或者桌面的键盘。因为键盘不能同时作为仿真程序软件输入面板 (SIP)和桌面的键盘,这个问题将在后面的版本里解决。
图1
完工的游戏
恭喜你,你终于完成了一个属于你自己的winphone7手机程序。
在今天的部分的过程中,您创建包括玩家和异形的移动计算、 命中的检测、 屏幕绘制,和其他一些的 AlienGame 逻辑。
虽然这个游戏完成了,但是我想大家可能有更多的疑问和想改进的地方,那让我们进入
手把手教用XNA开发winphone7游戏中级教程