[翻译]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类如下:

 1 public class Award
 2 {
 3     public string Name { getset; }
 4     public string TextureAssetName { getset; }
 5     public Texture2D Texture { getprivate set; }
 6     public void LoadTexture(ContentManager content)
 7     {
 8         Texture = content.Load<Texture2D>(TextureAssetName);
 9     }
10 }
                                   我们包含一个TextureAssetName属性和一个LoadTexture方法去使它很容易去创建一个对象在开始和后来加载需要的纹理。
                                   我们准备使用一个事件去通报给奖励的监听者,因为他们没有被锁定。为了这个我们将使用一个自定义的事件参数类:
1 public class AwardUnlockedEventArgs : EventArgs
2 {
3     public Award Award { getprivate set; }
4     public AwardUnlockedEventArgs(Award award) 
5     {
6         Award = award; 
7     }
8 }
                                  
Award Componet(奖励部分)
                                   下一步,我们将开始我们的AwardsComponent,它将会是主要的工作work-horse(驮马)为这个系统。这个组件将会管理奖励的表单,以及采取任何消息的显示提示。让我们开始通过创建我们准备使用的成分和某些基础的文件,属性,和事件。这里内在解释的行,所以我不想再多说每条数据的代表什么。
 1 public class AwardsComponent : DrawableGameComponent
 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 { getprivate 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
alu
e;
47     }
48 }
 
Managing The Awards(管理奖励)                                    
                                 希望你已经熟悉你自己的这个类,现在我们将继续添加功能性,它将允许我们去管理这个奖励。首先我们需要一些帮助方法:
 1 public Award GetAwardByName(string name)
 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(thisnew AwardUnlockedEventArgs(award));
17     }
18 }

                                  这些方法相当的简单,但是我们要回顾下我们所使用的。GetAwardBy名字简单,使用Find的方法以一个lamda表达式,为了去找到一个基于它自己的一个奖励。IsAwardUnlocked只检查去看看,这个奖励是否在unlockedAwards表单里面。UnlockAward首先确保这个奖励是在这个系统中并且一直没有被锁,然后移动它到解锁的表单中,声明排列它,并且引发该事件。

Updateing(更新)
                                 在这点上我们准备有一个功能性的奖励管理系统,不幸的是,它仍然缺少介绍。让我们从我们的Update方法开始添加,视觉和声音flair(译者:能力)到奖励中。同样,我将留下注释行,因为它很容易去解释每一行:

 1 public override void Update(GameTime gameTime)
 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方法中。这个方法十分的简单明了,依赖少数的帮助方法去使事情变的很整洁。也同样有注释。

 1 public override void Draw(GameTime gameTime)
 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(帮助方法)
                              现在让我们看下帮助方法。这里同样十分简单明了,同样也有注释行:

 1 private Vector2 GetNamePosition(Rectangle announcementBounds)
 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, 6464);
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(00, 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(译者:成就),这样用户不用每一次他们玩游戏时,必须解锁他们所有。为此,我们实行一个非常基本的保存和加载系统:

 1 public void SaveUnlockedAwards(string file)
 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
(完)

posted on 2009-09-10 16:32  一盘散沙  阅读(513)  评论(0编辑  收藏  举报

导航