[翻译]XNA外文博客文章精选之fourteen
PS:自己翻译的,转载请著明出处
在XNA中创建一个In-Game的奖励
Nick Gravelyn
导言
一个共同的点在Xbox LIVE Arcade和零售Xbox360游戏是熟悉的声音和sight of unlocking achievenments(译者:看到解锁的成就).这个成就是一个很大的方式来增加你游戏的游戏时间,通过给玩家额外的挑战设法去完成它。这个成就可以得到任何东西:完成的水平,在Xbox LIVE打败一个对手,或者刚刚找到一个愚蠢的复活节彩蛋。
不幸的是,Xbox LIVE社区游戏不能得到官方的成就系统。但是,这也不是说你的游戏不能有这些额外的挑战而去努力。在这篇文章中,我将显示给你如何去创建你自己的完成奖励系统,以一条消息显示和声音效果来表示。
Creating an Award System(创建一个奖励系统)
为了开始我们需要去定义什么是真实的奖励。对于我们的目的来说,它是一个有名字和纹理的一个对象。所以我们将创建我们的Award类如下:
2 {
3 public string Name { get; set; }
4 public string TextureAssetName { get; set; }
5 public Texture2D Texture { get; private set; }
6 public void LoadTexture(ContentManager content)
7 {
8 Texture = content.Load<Texture2D>(TextureAssetName);
9 }
10 }
我们准备使用一个事件去通报给奖励的监听者,因为他们没有被锁定。为了这个我们将使用一个自定义的事件参数类:
2 {
3 public Award Award { get; private set; }
4 public AwardUnlockedEventArgs(Award award)
5 {
6 Award = award;
7 }
8 }
Award Componet(奖励部分)
下一步,我们将开始我们的AwardsComponent,它将会是主要的工作work-horse(驮马)为这个系统。这个组件将会管理奖励的表单,以及采取任何消息的显示提示。让我们开始通过创建我们准备使用的成分和某些基础的文件,属性,和事件。这里内在解释的行,所以我不想再多说每条数据的代表什么。
2 {
3 // 我门保留一个没有锁的奖励列表
4 private readonly List<Award> unlockedAwards = new List<Award>();
5 // 我们需要声明一个近期没有锁的奖励队列
6 private readonly Queue<Award> justUnlocked = new Queue<Award>();
7 // the length of time spent fading in and out announcements
8 private const float AnnouncementFadeLength = .25f;
9 // the length of time the announcement
10 // is at full opacity on the screen
11 private const float AnnouncementLength = 2f;
12 // sets the offset (in pixels from the top-left corner)
13 // of where to place the award icon relative
14 // to the background texture
15 private const int IconOffsetX = 11;
16 private const int IconOffsetY = 8;
17 // sets the location of the center of the
18 // award name in the announcement
19 private const int TextCenterX = 161;
20 private const int TextCenterY = 25;
21 // a timer for announcements
22 private float announcementTimer;
23 // the background to use for the announcement
24 private Texture2D announcementBackground;
25 // a sound effect to play when a new announcement is going up
26 private SoundEffect announcementSound;
27 // a SpriteBatch used to draw everything
28 private SpriteBatch spriteBatch;
29 // the font used for rendering award names over the background
30 private SpriteFont announcementFont;
31 // whether or not to draw the announcement
32 private bool showAnnouncement;
33 /// Gets the list of all awards in the game.
34 public List<Award> Awards { get; private set; }
35 /// Raised when an award is unlocked.
36 /// The event is useful for games that unlock special content when awards
37 /// are unlocked. This allows games to unlock awards from anywhere and have
38 /// a single location listen for the events to respond accordingly.
39 public event EventHandler<AwardUnlockedEventArgs> AwardUnlocked;
40 /// Creates a new AwardsComponent for the given game.;
41 public AwardsComponent(Game game): base(game)
42 {
43 Awards = new List<Award>();
44 // we want awards to draw absolutely last
45 // so we set the draw order to max
46 DrawOrder = int.MaxV
47 }
48 }
Managing The Awards(管理奖励)
希望你已经熟悉你自己的这个类,现在我们将继续添加功能性,它将允许我们去管理这个奖励。首先我们需要一些帮助方法:
2 {
3 return Awards.Find(a => a.Name == name);
4 }
5 public bool IsAwardUnlocked(Award award)
6 {
7 return unlockedAwards.Contains(award);
8 }
9 public void UnlockAward(Award award)
10 {
11 if (Awards.Contains(award) && !IsAwardUnlocked(award))
12 {
13 unlockedAwards.Add(award);
14 justUnlocked.Enqueue(award);
15 if (AwardUnlocked != null)
16 AwardUnlocked(this, new AwardUnlockedEventArgs(award));
17 }
18 }
这些方法相当的简单,但是我们要回顾下我们所使用的。GetAwardBy名字简单,使用Find的方法以一个lamda表达式,为了去找到一个基于它自己的一个奖励。IsAwardUnlocked只检查去看看,这个奖励是否在unlockedAwards表单里面。UnlockAward首先确保这个奖励是在这个系统中并且一直没有被锁,然后移动它到解锁的表单中,声明排列它,并且引发该事件。
Updateing(更新)
在这点上我们准备有一个功能性的奖励管理系统,不幸的是,它仍然缺少介绍。让我们从我们的Update方法开始添加,视觉和声音flair(译者:能力)到奖励中。同样,我将留下注释行,因为它很容易去解释每一行:
2 {
3 // if there are unlocked awards left to announce
4 if (justUnlocked.Count > 0)
5 {
6 // if we're not currently showing an announcement
7 if (!showAnnouncement)
8 {
9 // reset the timer and show the announcement
10 announcementTimer = 0f;
11 showAnnouncement = true;
12 // play our sound effect
13 announcementSound.Play();
14 }
15 // otherwise if we are showing an announcement
16 else
17 {
18 // add to the timer
19 announcementTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
20 // if the timer is past the announcement period
21 //(fade in, on screen, fade out)
22 if (announcementTimer >= (AnnouncementFadeLength * 2) + AnnouncementLength)
23 {
24 // hide the announcement
25 showAnnouncement = false;
26 // dequeue the award since it has been announced
27 justUnlocked.Dequeue();
28 }
29 }
30 }
31 }
正如你看的Update十分的简单。它只处理计时器以及播放声音,当一个新的announcement(译者:通知或者宣告)被显示出来时。
Drawing the Award(绘制奖励)
现在,我们将移动到Draw方法中。这个方法十分的简单明了,依赖少数的帮助方法去使事情变的很整洁。也同样有注释。
2 {
3 // if an announcement is being shown
4 if (showAnnouncement)
5 {
6 // default to full opacity
7 float opacity = 1f;
8 // figure out if we're fading in or out
9 // and set the opacity accordingly
10 if (announcementTimer < AnnouncementFadeLength)
11 {
12 opacity = announcementTimer / AnnouncementFadeLength;
13 }
14 else if (announcementTimer >
15 AnnouncementFadeLength + AnnouncementLength)
16 {
17 opacity = 1.0f - ((announcementTimer - AnnouncementLength - AnnouncementFadeLength) / AnnouncementFadeLength);
18 }
19 // figure out the bounds based on the
20 // texture size and the Guide.NotificationPosition
21 Rectangle announcementBounds = CalculateAnnouncementBounds();
22 // make the colors
23 Color color = new Color(Color.White, opacity);
24 Color fontColor = new Color(Color.Black, opacity);
25 // start drawing. we use SaveState in case you have
26 // 3D rendering so it won't go bad
27 spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
28 // draw the background
29 spriteBatch.Draw(announcementBackground, announcementBounds, color);
30 // draw the icon
31 spriteBatch.Draw(justUnlocked.Peek().Texture, GetIconBounds(announcementBounds), color);
32 // draw the award name
33 spriteBatch.DrawString(announcementFont, justUnlocked.Peek().Name, GetNamePosition(announcementBounds), fontColor);
34 // done drawing
35 spriteBatch.End();
36 }
37 base.Draw(gameTime);
38 }
绘制是相当的简单。我们声明我们的不透明性,然后绘制我们的背景,图标,和使用一些帮助方法去放置这些项目。
Helper Methods(帮助方法)
现在让我们看下帮助方法。这里同样十分简单明了,同样也有注释行:
2 {
3 // get the center position of the text using our
4 // TextCenterX and TextCenterY offsets
5 Vector2 position = new Vector2(announcementBounds.X + TextCenterX, announcementBounds.Y + TextCenterY);
6 // measure the text we'll be drawing
7 Vector2 stringSize = announcementFont.MeasureString(justUnlocked.Peek().Name);
8 // subtract half of the size from the position to center it
9 position -= stringSize / 2f;
10 // return the position,
11 // but cast the X and Y to integers to avoid filtering
12 return new Vector2((int)position.X, (int)position.Y);
13 }
14 private Rectangle GetIconBounds(Rectangle announcementBounds)
15 {
16 // simply offset the announcement bounds position
17 // by the icon offsets and set the width and height to 64
18 return new Rectangle(announcementBounds.X + IconOffsetX, announcementBounds.Y + IconOffsetY, 64, 64);
19 }
20 private Rectangle CalculateAnnouncementBounds()
21 {
22 // the amount of the screen to buffer
23 // on the sides for the safe zone
24 const float safeZoneBuffer = .1f;
25 PresentationParameters pp = GraphicsDevice.PresentationParameters;
26 // the actual buffer sizes for the width and height
27 int widthBuffer = (int)(pp.BackBufferWidth * safeZoneBuffer);
28 int heightBuffer =(int)(pp.BackBufferHeight * safeZoneBuffer);
29 // start with a basic rectangle the size of our texture
30 Rectangle announcementBounds = new Rectangle(0, 0, announcementBackground.Width, announcementBackground.Height);
31 // figure out all six of our values now to
32 // make our switch statement cleaner
33 int topY = heightBuffer;
34 int centerY = pp.BackBufferHeight / 2 - announcementBounds.Height / 2;
35 int bottomY = pp.BackBufferHeight - heightBuffer - announcementBounds.Height;
36 int leftX = widthBuffer;
37 int centerX = pp.BackBufferWidth / 2 - announcementBounds.Width / 2;
38 int rightX =pp.BackBufferWidth - widthBuffer - announcementBounds.Width;
39 // set the bounds X and Y based on the notification position
40 switch (Guide.NotificationPosition)
41 {
42 case NotificationPosition.BottomCenter:
43 announcementBounds.X = centerX;
44 announcementBounds.Y = bottomY;
45 break;
46 case NotificationPosition.BottomLeft:
47 announcementBounds.X = leftX;
48 announcementBounds.Y = bottomY;
49 break;
50 case NotificationPosition.BottomRight:
51 announcementBounds.X = rightX;
52 announcementBounds.Y = bottomY;
53 break;
54 case NotificationPosition.Center:
55 announcementBounds.X = centerX;
56 announcementBounds.Y = centerY;
57 break;
58 case NotificationPosition.CenterLeft:
59 announcementBounds.X = leftX;
60 announcementBounds.Y = centerY;
61 break;
62 case NotificationPosition.CenterRight:
63 announcementBounds.X = rightX;
64 announcementBounds.Y = centerY;
65 break;
66 case NotificationPosition.TopCenter:
67 announcementBounds.X = centerX;
68 announcementBounds.Y = topY;
69 break;
70 case NotificationPosition.TopLeft:
71 announcementBounds.X = leftX;
72 announcementBounds.Y = topY;
73 break;
74 case NotificationPosition.TopRight:
75 announcementBounds.X = rightX;
76 announcementBounds.Y = topY;
77 break;
78 }
79 return announcementBounds;
80 }
Saving and Loading(保存和加载)
这就是它的介绍。现在我们解锁成就可以突然出现,让游戏知道它们做的很好。最后一件事,我们的系统需要一个方法去保存被解锁的achievementts(译者:成就),这样用户不用每一次他们玩游戏时,必须解锁他们所有。为此,我们实行一个非常基本的保存和加载系统:
2 {
3 // delete the file if it exists
4 if (File.Exists(file))File.Delete(file);
5 // use a StreamWriter to write the name of
6 // each unlocked award to the file
7 using (StreamWriter writer = new StreamWriter(file))
8 {
9 unlockedAwards.ForEach(a => writer.WriteLine(a.Name));
10 }
11 }
12 public void LoadUnlockedAwards(string file)
13 {
14 // clear the list of unlocked awards
15 unlockedAwards.Clear();
16 // use a StreamReader to read in the data
17 using (StreamReader reader = new StreamReader(file))
18 {
19 // loop while we're not at the end of the stream
20 while (!reader.EndOfStream)
21 {
22 // get an award by name where each line is an award
23 Award award = GetAwardByName(reader.ReadLine());
24 // if the award is not null, add it to our unlocked awards list
25 if (award != null)
26 unlockedAwards.Add(award);
27 }
28 }
29 }
结论
现在我们真的可以完成了。你的游戏现在有一个有能力给为你想要给你的玩家的任何的数目的奖励。现在你必须想出所有这些奖励,并且提出好听的名字和漂亮的图标。
下载例子包含所有的资源代码(通过行注释和智能感知XML注释)以及样例游戏,它会
向你显示你的游戏可以使用这些奖励。
源代码:http://www.ziggyware.com/readarticle.php?article_id=217
(完)