リソース整理
@@ -1,164 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | -using System.Collections.Generic; | |
5 | -using System.Diagnostics; | |
6 | -using System.Text; | |
7 | - | |
8 | -using Microsoft.Xna.Framework; | |
9 | -using Microsoft.Xna.Framework.Graphics; | |
10 | - | |
11 | -#endregion | |
12 | - | |
13 | -namespace DebugSample | |
14 | -{ | |
15 | - /// <summary> | |
16 | - /// FPSの測定と表示用コンポーネント | |
17 | - /// </summary> | |
18 | - public class FpsCounter : DrawableGameComponent | |
19 | - { | |
20 | - #region プロパティ | |
21 | - | |
22 | - /// <summary> | |
23 | - /// 現在のFPS値 | |
24 | - /// </summary> | |
25 | - public float Fps { get; private set; } | |
26 | - | |
27 | - /// <summary> | |
28 | - /// FPS測定間隔の所得と設定 | |
29 | - /// </summary> | |
30 | - public TimeSpan SampleSpan { get; set; } | |
31 | - | |
32 | - #endregion | |
33 | - | |
34 | - #region フィールド | |
35 | - | |
36 | - // デバッグマネージャー | |
37 | - private DebugManager debugManager; | |
38 | - | |
39 | - // 測定用のストップウォッチ | |
40 | - private Stopwatch stopwatch; | |
41 | - | |
42 | - // 測定中のフレーム数 | |
43 | - private int sampleFrames; | |
44 | - | |
45 | - // FPS表示用の文字バッファ | |
46 | - private StringBuilder stringBuilder = new StringBuilder(16); | |
47 | - | |
48 | - #endregion | |
49 | - | |
50 | - #region 初期化 | |
51 | - | |
52 | - public FpsCounter(Game game) | |
53 | - : base(game) | |
54 | - { | |
55 | - SampleSpan = TimeSpan.FromSeconds(1); | |
56 | - } | |
57 | - | |
58 | - public override void Initialize() | |
59 | - { | |
60 | - // デバッグマネージャーをゲームのサービスから取得 | |
61 | - debugManager = | |
62 | - Game.Services.GetService(typeof(DebugManager)) as DebugManager; | |
63 | - | |
64 | - if (debugManager == null) | |
65 | - throw new InvalidOperationException("DebugManaerが登録されていません"); | |
66 | - | |
67 | - // デバッグコマンドがサービスに登録されているなら、FPSコマンドを登録する | |
68 | - IDebugCommandHost host = | |
69 | - Game.Services.GetService(typeof(IDebugCommandHost)) | |
70 | - as IDebugCommandHost; | |
71 | - | |
72 | - if (host != null) | |
73 | - { | |
74 | - host.RegisterCommand("fps", "FPS Counter", this.CommandExecute); | |
75 | - Visible = false; | |
76 | - } | |
77 | - | |
78 | - // パラメーターの初期化 | |
79 | - Fps = 0; | |
80 | - sampleFrames = 0; | |
81 | - stopwatch = Stopwatch.StartNew(); | |
82 | - stringBuilder.Length = 0; | |
83 | - | |
84 | - base.Initialize(); | |
85 | - } | |
86 | - | |
87 | - #endregion | |
88 | - | |
89 | - /// <summary> | |
90 | - /// FPSコマンド処理 | |
91 | - /// </summary> | |
92 | - private void CommandExecute(IDebugCommandHost host, | |
93 | - string command, IList<string> arguments) | |
94 | - { | |
95 | - if (arguments.Count == 0) | |
96 | - Visible = !Visible; | |
97 | - | |
98 | - foreach (string arg in arguments) | |
99 | - { | |
100 | - switch (arg.ToLower()) | |
101 | - { | |
102 | - case "on": | |
103 | - Visible = true; | |
104 | - break; | |
105 | - case "off": | |
106 | - Visible = false; | |
107 | - break; | |
108 | - } | |
109 | - } | |
110 | - } | |
111 | - | |
112 | - #region 更新と描画 | |
113 | - | |
114 | - public override void Update(GameTime gameTime) | |
115 | - { | |
116 | - if (stopwatch.Elapsed > SampleSpan) | |
117 | - { | |
118 | - // FPSの更新と次の測定期間の開始 | |
119 | - Fps = (float)sampleFrames / (float)stopwatch.Elapsed.TotalSeconds; | |
120 | - | |
121 | - stopwatch.Reset(); | |
122 | - stopwatch.Start(); | |
123 | - sampleFrames = 0; | |
124 | - | |
125 | - // 表示文字列の更新 | |
126 | - stringBuilder.Length = 0; | |
127 | - stringBuilder.Append("FPS: "); | |
128 | - stringBuilder.AppendNumber(Fps); | |
129 | - } | |
130 | - } | |
131 | - | |
132 | - public override void Draw(GameTime gameTime) | |
133 | - { | |
134 | - sampleFrames++; | |
135 | - | |
136 | - SpriteBatch spriteBatch = debugManager.SpriteBatch; | |
137 | - SpriteFont font = debugManager.DebugFont; | |
138 | - | |
139 | - // FPS表示の周りに半透明の黒い矩形のサイズ計算と配置 | |
140 | - Vector2 size = font.MeasureString("X"); | |
141 | - Rectangle rc = | |
142 | - new Rectangle(0, 0, (int)(size.X * 14f), (int)(size.Y * 1.3f)); | |
143 | - | |
144 | - Layout layout = new Layout(spriteBatch.GraphicsDevice.Viewport); | |
145 | - rc = layout.Place(rc, 0.01f, 0.01f, Alignment.TopLeft); | |
146 | - | |
147 | - // FPS表示を矩形の中で配置 | |
148 | - size = font.MeasureString(stringBuilder); | |
149 | - layout.ClientArea = rc; | |
150 | - Vector2 pos = layout.Place(size, 0, 0.1f, Alignment.Center); | |
151 | - | |
152 | - // 描画 | |
153 | - spriteBatch.Begin(); | |
154 | - spriteBatch.Draw(debugManager.WhiteTexture, rc, new Color(0, 0, 0, 128)); | |
155 | - spriteBatch.DrawString(font, stringBuilder, pos, Color.White); | |
156 | - spriteBatch.End(); | |
157 | - | |
158 | - base.Draw(gameTime); | |
159 | - } | |
160 | - | |
161 | - #endregion | |
162 | - | |
163 | - } | |
164 | -} |
@@ -1,834 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | -using System.Collections.Generic; | |
5 | -using System.Diagnostics; | |
6 | -using System.Text; | |
7 | -using System.Threading; | |
8 | -using Microsoft.Xna.Framework; | |
9 | -using Microsoft.Xna.Framework.Graphics; | |
10 | - | |
11 | -#endregion | |
12 | - | |
13 | -namespace DebugSample | |
14 | -{ | |
15 | - /// <summary> | |
16 | - /// CPU処理速度のリアルタイム測定用ツール | |
17 | - /// </summary> | |
18 | - /// <remarks> | |
19 | - /// このツールを使うことでボトルネックの発見と、あとどれだけの処理ができるのかが | |
20 | - /// 視覚的に判る。 | |
21 | - /// また、リアルタイムプロファイラーなのでゲーム中に瞬間的に大量の処理をする場合の | |
22 | - /// 様子も把握することができる。 | |
23 | - /// | |
24 | - /// TimeRulerがサポートしている機能は以下の通り: | |
25 | - /// * 最大8個(変更可)のバー表示 | |
26 | - /// * 各マーカーに任意の色をつけることができる | |
27 | - /// * マーカーログ表示機能 | |
28 | - /// * TRACE設定を外すとBeginMark/EndMark等のメソッドの呼び出し自体をしない | |
29 | - /// * 最大32個(変更可)のBeginMarkのネスト呼び出しに対応 | |
30 | - /// * マルチスレッド対応 | |
31 | - /// * 負荷状態による表示フレーム数の自動変化機能 | |
32 | - /// | |
33 | - /// 基本的な使用方法は、Game.ComponentsにTimeRulerのインスタンスを追加し、 | |
34 | - /// Game.Updateメソッドの先頭でtimerRuler.StartFrame()メソッドを呼ぶようにする。 | |
35 | - /// | |
36 | - /// 後は測定したい部分の前後でBeginMark,EndMarkを呼び出す。 | |
37 | - /// | |
38 | - /// timeRuler.BeginMark( "Update", Color.Blue ); | |
39 | - /// // 測定したい処理 | |
40 | - /// timerRuler.EndMark( "Update" ); | |
41 | - /// | |
42 | - /// また、BeginMarkには測定結果を表示するバーのインデックスを指定できる(規定値は0) | |
43 | - /// | |
44 | - /// timeRuler.BeginMark( 1, "Update", Color.Blue ); | |
45 | - /// | |
46 | - /// プロファイルに使用するメソッド自体にはConditionalAttributeを指定しているので、 | |
47 | - /// BeginMark/EndMark等のメソッドはTRACEが設定されていないとメソッド呼び出しのコード | |
48 | - /// を生成しないようになっている。実際のリリース時にはビルド設定の | |
49 | - /// TRACE定数の定義のチェックボックスをクリアするのを忘れないようにすること。 | |
50 | - /// | |
51 | - /// </remarks> | |
52 | - public class TimeRuler : DrawableGameComponent | |
53 | - { | |
54 | - #region 定数宣言 | |
55 | - | |
56 | - /// <summary> | |
57 | - /// 最大バー表示数 | |
58 | - /// </summary> | |
59 | - const int MaxBars = 8; | |
60 | - | |
61 | - /// <summary> | |
62 | - /// バーひとつあたりの最大サンプル数 | |
63 | - /// </summary> | |
64 | - const int MaxSamples = 256; | |
65 | - | |
66 | - /// <summary> | |
67 | - /// バーひとつあたりの最大ネスト数 | |
68 | - /// </summary> | |
69 | - const int MaxNestCall = 32; | |
70 | - | |
71 | - /// <summary> | |
72 | - /// 最多表示フレーム数 | |
73 | - /// </summary> | |
74 | - const int MaxSampleFrames = 4; | |
75 | - | |
76 | - /// <summary> | |
77 | - /// ログのスナップを取る間隔(フレーム数) | |
78 | - /// </summary> | |
79 | - const int LogSnapDuration = 120; | |
80 | - | |
81 | - /// <summary> | |
82 | - /// バーの高さ(ピクセル) | |
83 | - /// </summary> | |
84 | - const int BarHeight = 8; | |
85 | - | |
86 | - /// <summary> | |
87 | - /// バーのパディング(ピクセル) | |
88 | - /// </summary> | |
89 | - const int BarPadding = 2; | |
90 | - | |
91 | - /// <summary> | |
92 | - /// 自動表示フレーム調整に掛かるフレーム数 | |
93 | - /// </summary> | |
94 | - const int AutoAdjustDelay = 30; | |
95 | - | |
96 | - #endregion | |
97 | - | |
98 | - #region プロパティ | |
99 | - | |
100 | - /// <summary> | |
101 | - /// ログの表示設定の設定と取得 | |
102 | - /// </summary> | |
103 | - public bool ShowLog { get; set; } | |
104 | - | |
105 | - /// <summary> | |
106 | - /// 目標表示フレーム数の取得と設定 | |
107 | - /// </summary> | |
108 | - public int TargetSampleFrames { get; set; } | |
109 | - | |
110 | - /// <summary> | |
111 | - /// TimeRuler描画位置の取得と設定 | |
112 | - /// </summary> | |
113 | - public Vector2 Position { get { return position; } set { position = value; } } | |
114 | - | |
115 | - /// <summary> | |
116 | - /// TimeRuler描画幅の取得と設定 | |
117 | - /// </summary> | |
118 | - public int Width { get; set; } | |
119 | - | |
120 | - #endregion | |
121 | - | |
122 | - #region フィールド | |
123 | - | |
124 | -#if TRACE | |
125 | - | |
126 | - /// <summary> | |
127 | - /// マーカー構造体 | |
128 | - /// </summary> | |
129 | - private struct Marker | |
130 | - { | |
131 | - public int MarkerId; | |
132 | - public float BeginTime; | |
133 | - public float EndTime; | |
134 | - public Color Color; | |
135 | - } | |
136 | - | |
137 | - /// <summary> | |
138 | - /// マーカーコレクション | |
139 | - /// </summary> | |
140 | - private class MarkerCollection | |
141 | - { | |
142 | - // マーカーコレクション | |
143 | - public Marker[] Markers = new Marker[MaxSamples]; | |
144 | - public int MarkCount; | |
145 | - | |
146 | - // マーカーネスト情報 | |
147 | - public int[] MarkerNests = new int[MaxNestCall]; | |
148 | - public int NestCount; | |
149 | - } | |
150 | - | |
151 | - /// <summary> | |
152 | - /// フレームのログ | |
153 | - /// </summary> | |
154 | - private class FrameLog | |
155 | - { | |
156 | - // バー情報 | |
157 | - public MarkerCollection[] Bars; | |
158 | - | |
159 | - public FrameLog() | |
160 | - { | |
161 | - // マーカーコレクション配列の初期化 | |
162 | - Bars = new MarkerCollection[MaxBars]; | |
163 | - for (int i = 0; i < MaxBars; ++i) | |
164 | - Bars[i] = new MarkerCollection(); | |
165 | - } | |
166 | - } | |
167 | - | |
168 | - /// <summary> | |
169 | - /// マーカー情報 | |
170 | - /// </summary> | |
171 | - private class MarkerInfo | |
172 | - { | |
173 | - // マーカー名 | |
174 | - public string Name; | |
175 | - | |
176 | - // マーカーログ | |
177 | - public MarkerLog[] Logs = new MarkerLog[MaxBars]; | |
178 | - | |
179 | - public MarkerInfo(string name) | |
180 | - { | |
181 | - Name = name; | |
182 | - } | |
183 | - } | |
184 | - | |
185 | - /// <summary> | |
186 | - /// マーカーログ情報 | |
187 | - /// </summary> | |
188 | - private struct MarkerLog | |
189 | - { | |
190 | - public float SnapMin; | |
191 | - public float SnapMax; | |
192 | - public float SnapAvg; | |
193 | - | |
194 | - public float Min; | |
195 | - public float Max; | |
196 | - public float Avg; | |
197 | - | |
198 | - public int Samples; | |
199 | - | |
200 | - public Color Color; | |
201 | - | |
202 | - public bool Initialized; | |
203 | - } | |
204 | - | |
205 | - // デバッグマネージャー | |
206 | - DebugManager debugManager; | |
207 | - | |
208 | - // フレーム毎のログ | |
209 | - FrameLog[] logs; | |
210 | - | |
211 | - // 前フレームのログ | |
212 | - FrameLog prevLog; | |
213 | - | |
214 | - // 測定中のフレームログ | |
215 | - FrameLog curLog; | |
216 | - | |
217 | - // 現在のフレーム数 | |
218 | - int frameCount; | |
219 | - | |
220 | - // 計測に使用するストップウォッチ | |
221 | - Stopwatch stopwatch = new Stopwatch(); | |
222 | - | |
223 | - // マーカー情報配列 | |
224 | - List<MarkerInfo> markers = new List<MarkerInfo>(); | |
225 | - | |
226 | - // マーカー名からマーカーIDへの変換マップ | |
227 | - Dictionary<string, int> markerNameToIdMap = new Dictionary<string, int>(); | |
228 | - | |
229 | - // サンプルフレーム自動調整用のカウンタ | |
230 | - int frameAdjust; | |
231 | - | |
232 | - // 現在の表示フレーム数 | |
233 | - int sampleFrames; | |
234 | - | |
235 | - // マーカーログ表示文字列 | |
236 | - StringBuilder logString = new StringBuilder(512); | |
237 | - | |
238 | - // You want to call StartFrame at beginning of Game.Update method. | |
239 | - // But Game.Update gets calls multiple time when game runs slow in fixed time step mode. | |
240 | - // In this case, we should ignore StartFrame call. | |
241 | - // To do this, we just keep tracking of number of StartFrame calls untile Draw gets called. | |
242 | - int updateCount; | |
243 | - | |
244 | -#endif | |
245 | - // TimerRulerの表示位置 | |
246 | - Vector2 position; | |
247 | - | |
248 | - #endregion | |
249 | - | |
250 | - #region 初期化 | |
251 | - | |
252 | - public TimeRuler(Game game) | |
253 | - : base(game) | |
254 | - { | |
255 | - // サービスとして登録する | |
256 | - // timeLine.BeginMarkを使っているラインはTRACE設定なしの場合でも残ってる | |
257 | - // 場合があるので、サービス登録は必要になる | |
258 | - Game.Services.AddService(typeof(TimeRuler), this); | |
259 | - } | |
260 | - | |
261 | - public override void Initialize() | |
262 | - { | |
263 | -#if TRACE | |
264 | - debugManager = | |
265 | - Game.Services.GetService(typeof(DebugManager)) as DebugManager; | |
266 | - | |
267 | - if (debugManager == null) | |
268 | - throw new InvalidOperationException("DebugManagerが登録されていません"); | |
269 | - | |
270 | - // DebugCommandHostが登録されているのなら、コマンドを登録 | |
271 | - IDebugCommandHost host = | |
272 | - Game.Services.GetService(typeof(IDebugCommandHost)) | |
273 | - as IDebugCommandHost; | |
274 | - | |
275 | - if (host != null) | |
276 | - { | |
277 | - host.RegisterCommand("tr", "TimeRuler", this.CommandExecute); | |
278 | - this.Visible = false; | |
279 | - this.Enabled = false; | |
280 | - } | |
281 | - | |
282 | - // パラメーターの初期化 | |
283 | - logs = new FrameLog[2]; | |
284 | - for (int i = 0; i < logs.Length; ++i) | |
285 | - logs[i] = new FrameLog(); | |
286 | - | |
287 | - sampleFrames = TargetSampleFrames = 1; | |
288 | - | |
289 | - // Time-Ruler's update method doesn't need to get called. | |
290 | - this.Enabled = false; | |
291 | -#endif | |
292 | - base.Initialize(); | |
293 | - } | |
294 | - | |
295 | - protected override void LoadContent() | |
296 | - { | |
297 | - Width = (int)(GraphicsDevice.Viewport.Width * 0.8f); | |
298 | - | |
299 | - Layout layout = new Layout(GraphicsDevice.Viewport); | |
300 | - position = layout.Place(new Vector2(Width, BarHeight), | |
301 | - 0, 0.01f, Alignment.BottomCenter); | |
302 | - | |
303 | - base.LoadContent(); | |
304 | - } | |
305 | - | |
306 | -#if TRACE | |
307 | - /// <summary> | |
308 | - /// TimeRulerコマンド処理 | |
309 | - /// </summary> | |
310 | - void CommandExecute(IDebugCommandHost host, string command, | |
311 | - IList<string> arguments) | |
312 | - { | |
313 | - bool previousVisible = Visible; | |
314 | - | |
315 | - if (arguments.Count == 0) | |
316 | - Visible = !Visible; | |
317 | - | |
318 | - char[] subArgSeparator = new[] { ':' }; | |
319 | - foreach (string orgArg in arguments) | |
320 | - { | |
321 | - string arg = orgArg.ToLower(); | |
322 | - string[] subargs = arg.Split(subArgSeparator); | |
323 | - switch (subargs[0]) | |
324 | - { | |
325 | - case "on": | |
326 | - Visible = true; | |
327 | - break; | |
328 | - case "off": | |
329 | - Visible = false; | |
330 | - break; | |
331 | - case "reset": | |
332 | - ResetLog(); | |
333 | - break; | |
334 | - case "log": | |
335 | - if (subargs.Length > 1) | |
336 | - { | |
337 | - if (String.Compare(subargs[1], "on") == 0) | |
338 | - ShowLog = true; | |
339 | - if (String.Compare(subargs[1], "off") == 0) | |
340 | - ShowLog = false; | |
341 | - } | |
342 | - else | |
343 | - { | |
344 | - ShowLog = !ShowLog; | |
345 | - } | |
346 | - break; | |
347 | - case "frame": | |
348 | - int a = Int32.Parse(subargs[1]); | |
349 | - a = Math.Max(a, 1); | |
350 | - a = Math.Min(a, MaxSampleFrames); | |
351 | - TargetSampleFrames = a; | |
352 | - break; | |
353 | - case "/?": | |
354 | - case "--help": | |
355 | - host.Echo("tr [log|on|off|reset|frame]"); | |
356 | - host.Echo("Options:"); | |
357 | - host.Echo(" on Display TimeRuler."); | |
358 | - host.Echo(" off Hide TimeRuler."); | |
359 | - host.Echo(" log Show/Hide marker log."); | |
360 | - host.Echo(" reset Reset marker log."); | |
361 | - host.Echo(" frame:sampleFrames"); | |
362 | - host.Echo(" Change target sample frame count"); | |
363 | - break; | |
364 | - default: | |
365 | - break; | |
366 | - } | |
367 | - } | |
368 | - | |
369 | - // Reset update count when Visible state changed. | |
370 | - if (Visible != previousVisible) | |
371 | - { | |
372 | - Interlocked.Exchange(ref updateCount, 0); | |
373 | - } | |
374 | - } | |
375 | -#endif | |
376 | - | |
377 | - #endregion | |
378 | - | |
379 | - #region 測定用メソッド | |
380 | - | |
381 | - /// <summary> | |
382 | - /// 新しいフレームの開始 | |
383 | - /// </summary> | |
384 | - [Conditional("TRACE")] | |
385 | - public void StartFrame() | |
386 | - { | |
387 | -#if TRACE | |
388 | - lock (this) | |
389 | - { | |
390 | - // Game.IsFixedTimeStepがtrueの場合、Game.Updateが複数回呼ばれることがある。 | |
391 | - // http://blogs.msdn.com/b/ito/archive/2007/03/08/2-update.aspx | |
392 | - // このケースに対処する為に、タイムルーラーの描画が呼び出されずにStartFrameが複数回呼ばれた場合は | |
393 | - // フレームリセットするのではなく、測定を継続するようになっている。 | |
394 | - int count = Interlocked.Increment(ref updateCount); | |
395 | - if (Visible && (1 < count && count < MaxSampleFrames)) | |
396 | - return; | |
397 | - | |
398 | - // 現フレームログの更新 | |
399 | - prevLog = logs[frameCount++ & 0x1]; | |
400 | - curLog = logs[frameCount & 0x1]; | |
401 | - | |
402 | - float endFrameTime = (float)stopwatch.Elapsed.TotalMilliseconds; | |
403 | - | |
404 | - // マーカーの更新とログ生成 | |
405 | - for (int barIdx = 0; barIdx < prevLog.Bars.Length; ++barIdx) | |
406 | - { | |
407 | - MarkerCollection prevBar = prevLog.Bars[barIdx]; | |
408 | - MarkerCollection nextBar = curLog.Bars[barIdx]; | |
409 | - | |
410 | - // 前フレームでEndMarkを呼んでいないマーカーを閉じ、現フレームで | |
411 | - // 再度開く。 | |
412 | - for (int nest = 0; nest < prevBar.NestCount; ++nest) | |
413 | - { | |
414 | - int markerIdx = prevBar.MarkerNests[nest]; | |
415 | - | |
416 | - prevBar.Markers[markerIdx].EndTime = endFrameTime; | |
417 | - | |
418 | - nextBar.MarkerNests[nest] = nest; | |
419 | - nextBar.Markers[nest].MarkerId = | |
420 | - prevBar.Markers[markerIdx].MarkerId; | |
421 | - nextBar.Markers[nest].BeginTime = 0; | |
422 | - nextBar.Markers[nest].EndTime = -1; | |
423 | - nextBar.Markers[nest].Color = prevBar.Markers[markerIdx].Color; | |
424 | - } | |
425 | - | |
426 | - // マーカーログの更新 | |
427 | - for (int markerIdx = 0; markerIdx < prevBar.MarkCount; ++markerIdx) | |
428 | - { | |
429 | - float duration = prevBar.Markers[markerIdx].EndTime - | |
430 | - prevBar.Markers[markerIdx].BeginTime; | |
431 | - | |
432 | - int markerId = prevBar.Markers[markerIdx].MarkerId; | |
433 | - MarkerInfo m = markers[markerId]; | |
434 | - | |
435 | - m.Logs[barIdx].Color = prevBar.Markers[markerIdx].Color; | |
436 | - | |
437 | - if (!m.Logs[barIdx].Initialized) | |
438 | - { | |
439 | - // 最初のフレームの処理 | |
440 | - m.Logs[barIdx].Min = duration; | |
441 | - m.Logs[barIdx].Max = duration; | |
442 | - m.Logs[barIdx].Avg = duration; | |
443 | - | |
444 | - m.Logs[barIdx].Initialized = true; | |
445 | - } | |
446 | - else | |
447 | - { | |
448 | - // 2フレーム目以降の処理 | |
449 | - m.Logs[barIdx].Min = Math.Min(m.Logs[barIdx].Min, duration); | |
450 | - m.Logs[barIdx].Max = Math.Min(m.Logs[barIdx].Max, duration); | |
451 | - m.Logs[barIdx].Avg += duration; | |
452 | - m.Logs[barIdx].Avg *= 0.5f; | |
453 | - | |
454 | - if (m.Logs[barIdx].Samples++ >= LogSnapDuration) | |
455 | - { | |
456 | - m.Logs[barIdx].SnapMin = m.Logs[barIdx].Min; | |
457 | - m.Logs[barIdx].SnapMax = m.Logs[barIdx].Max; | |
458 | - m.Logs[barIdx].SnapAvg = m.Logs[barIdx].Avg; | |
459 | - m.Logs[barIdx].Samples = 0; | |
460 | - } | |
461 | - } | |
462 | - } | |
463 | - | |
464 | - nextBar.MarkCount = prevBar.NestCount; | |
465 | - nextBar.NestCount = prevBar.NestCount; | |
466 | - } | |
467 | - | |
468 | - // このフレームの測定開始 | |
469 | - stopwatch.Reset(); | |
470 | - stopwatch.Start(); | |
471 | - } | |
472 | -#endif | |
473 | - } | |
474 | - | |
475 | - /// <summary> | |
476 | - /// マーカーの開始 | |
477 | - /// </summary> | |
478 | - /// <param name="markerName">マーカー名</param> | |
479 | - /// <param name="color">カラー</param> | |
480 | - [Conditional("TRACE")] | |
481 | - public void BeginMark(string markerName, Color color) | |
482 | - { | |
483 | -#if TRACE | |
484 | - BeginMark(0, markerName, color); | |
485 | -#endif | |
486 | - } | |
487 | - | |
488 | - /// <summary> | |
489 | - /// マーカーの開始 | |
490 | - /// </summary> | |
491 | - /// <param name="barIndex">バーのインデックス値</param> | |
492 | - /// <param name="markerName">マーカー名</param> | |
493 | - /// <param name="color">カラー</param> | |
494 | - [Conditional("TRACE")] | |
495 | - public void BeginMark(int barIndex, string markerName, Color color) | |
496 | - { | |
497 | -#if TRACE | |
498 | - lock (this) | |
499 | - { | |
500 | - if (barIndex < 0 || barIndex >= MaxBars) | |
501 | - throw new ArgumentOutOfRangeException("barIndex"); | |
502 | - | |
503 | - MarkerCollection bar = curLog.Bars[barIndex]; | |
504 | - | |
505 | - if (bar.MarkCount >= MaxSamples) | |
506 | - { | |
507 | - throw new OverflowException( | |
508 | - "サンプル数がMaxSampleを超えました。\n" + | |
509 | - "TimeRuler.MaxSmpaleの値を大きくするか、" + | |
510 | - "サンプル数を少なくしてください。"); | |
511 | - } | |
512 | - | |
513 | - if (bar.NestCount >= MaxNestCall) | |
514 | - { | |
515 | - throw new OverflowException( | |
516 | - "ネスト数がMaxNestCallを超えました。\n" + | |
517 | - "TimeRuler.MaxNestCallの値を大きくするか、" + | |
518 | - "ネスト呼び出し数を減らしてください。"); | |
519 | - } | |
520 | - | |
521 | - // 登録されているマーカーを取得 | |
522 | - int markerId; | |
523 | - if (!markerNameToIdMap.TryGetValue(markerName, out markerId)) | |
524 | - { | |
525 | - // 登録されていなければ新たに登録する | |
526 | - markerId = markers.Count; | |
527 | - markerNameToIdMap.Add(markerName, markerId); | |
528 | - markers.Add(new MarkerInfo(markerName)); | |
529 | - } | |
530 | - | |
531 | - // 測定開始 | |
532 | - bar.MarkerNests[bar.NestCount++] = bar.MarkCount; | |
533 | - | |
534 | - // マーカーのパラメーターを設定 | |
535 | - bar.Markers[bar.MarkCount].MarkerId = markerId; | |
536 | - bar.Markers[bar.MarkCount].Color = color; | |
537 | - bar.Markers[bar.MarkCount].BeginTime = | |
538 | - (float)stopwatch.Elapsed.TotalMilliseconds; | |
539 | - | |
540 | - bar.Markers[bar.MarkCount].EndTime = -1; | |
541 | - | |
542 | - bar.MarkCount++; | |
543 | - } | |
544 | -#endif | |
545 | - } | |
546 | - | |
547 | - /// <summary> | |
548 | - /// マーカーの終了 | |
549 | - /// </summary> | |
550 | - /// <param name="markerName">マーカー名</param> | |
551 | - [Conditional("TRACE")] | |
552 | - public void EndMark(string markerName) | |
553 | - { | |
554 | -#if TRACE | |
555 | - EndMark(0, markerName); | |
556 | -#endif | |
557 | - } | |
558 | - | |
559 | - /// <summary> | |
560 | - /// マーカーの終了 | |
561 | - /// </summary> | |
562 | - /// <param name="barIndex">バーのインデックス値</param> | |
563 | - /// <param name="markerName">マーカー名</param> | |
564 | - [Conditional("TRACE")] | |
565 | - public void EndMark(int barIndex, string markerName) | |
566 | - { | |
567 | -#if TRACE | |
568 | - lock (this) | |
569 | - { | |
570 | - if (barIndex < 0 || barIndex >= MaxBars) | |
571 | - throw new ArgumentOutOfRangeException("barIndex"); | |
572 | - | |
573 | - MarkerCollection bar = curLog.Bars[barIndex]; | |
574 | - | |
575 | - if (bar.NestCount <= 0) | |
576 | - { | |
577 | - throw new InvalidOperationException( | |
578 | - "EndMarkを呼び出す前に、BeginMarkメソッドを呼んでください。"); | |
579 | - } | |
580 | - | |
581 | - int markerId; | |
582 | - if (!markerNameToIdMap.TryGetValue(markerName, out markerId)) | |
583 | - { | |
584 | - throw new InvalidOperationException( | |
585 | - String.Format("マーカー名「{0}」は登録されていません。" + | |
586 | - "BeginMarkで使った名前と同じ名前か確認してください。", | |
587 | - markerName)); | |
588 | - } | |
589 | - | |
590 | - int markerIdx = bar.MarkerNests[--bar.NestCount]; | |
591 | - if (bar.Markers[markerIdx].MarkerId != markerId) | |
592 | - { | |
593 | - throw new InvalidOperationException( | |
594 | - "BeginMark/EndMarkの呼び出し順序が不正です。" + | |
595 | - "BeginMark(A), BeginMark(B), EndMark(B), EndMark(A)の" + | |
596 | - "のようには呼べますが、" + | |
597 | - "BeginMark(A), BeginMark(B), EndMark(A), EndMark(B)のようには" + | |
598 | - "呼べません。"); | |
599 | - } | |
600 | - | |
601 | - bar.Markers[markerIdx].EndTime = | |
602 | - (float)stopwatch.Elapsed.TotalMilliseconds; | |
603 | - } | |
604 | -#endif | |
605 | - } | |
606 | - | |
607 | - /// <summary> | |
608 | - /// 指定された測定バーインデックスとマーカー名の平均処理時間を返す。 | |
609 | - /// </summary> | |
610 | - /// <param name="barIndex">測定バーのインデックス</param> | |
611 | - /// <param name="markerName">マーカー名</param> | |
612 | - /// <returns>平均処理時間(ミリ秒)</returns> | |
613 | - public float GetAverageTime(int barIndex, string markerName) | |
614 | - { | |
615 | -#if TRACE | |
616 | - if (barIndex < 0 || barIndex >= MaxBars) | |
617 | - throw new ArgumentOutOfRangeException("barIndex"); | |
618 | - | |
619 | - float result = 0; | |
620 | - int markerId; | |
621 | - if (markerNameToIdMap.TryGetValue(markerName, out markerId)) | |
622 | - result = markers[markerId].Logs[barIndex].Avg; | |
623 | - | |
624 | - return result; | |
625 | -#endif | |
626 | - } | |
627 | - | |
628 | - /// <summary> | |
629 | - /// マーカーログのリセット | |
630 | - /// </summary> | |
631 | - [Conditional("TRACE")] | |
632 | - public void ResetLog() | |
633 | - { | |
634 | -#if TRACE | |
635 | - lock (this) | |
636 | - { | |
637 | - foreach (MarkerInfo markerInfo in markers) | |
638 | - { | |
639 | - for (int i = 0; i < markerInfo.Logs.Length; ++i) | |
640 | - { | |
641 | - markerInfo.Logs[i].Initialized = false; | |
642 | - markerInfo.Logs[i].SnapMin = 0; | |
643 | - markerInfo.Logs[i].SnapMax = 0; | |
644 | - markerInfo.Logs[i].SnapAvg = 0; | |
645 | - | |
646 | - markerInfo.Logs[i].Min = 0; | |
647 | - markerInfo.Logs[i].Max = 0; | |
648 | - markerInfo.Logs[i].Avg = 0; | |
649 | - | |
650 | - markerInfo.Logs[i].Samples = 0; | |
651 | - } | |
652 | - } | |
653 | - } | |
654 | -#endif | |
655 | - } | |
656 | - | |
657 | - #endregion | |
658 | - | |
659 | - #region 描画 | |
660 | - | |
661 | - public override void Draw(GameTime gameTime) | |
662 | - { | |
663 | - Draw(position, Width); | |
664 | - base.Draw(gameTime); | |
665 | - } | |
666 | - | |
667 | - [Conditional("TRACE")] | |
668 | - public void Draw(Vector2 position, int width) | |
669 | - { | |
670 | -#if TRACE | |
671 | - // 更新カウントをリセットする。 | |
672 | - Interlocked.Exchange(ref updateCount, 0); | |
673 | - | |
674 | - // SpriteBatch, SpriteFont, WhiteTextureをDebugManagerから取得する。 | |
675 | - SpriteBatch spriteBatch = debugManager.SpriteBatch; | |
676 | - SpriteFont font = debugManager.DebugFont; | |
677 | - Texture2D texture = debugManager.WhiteTexture; | |
678 | - | |
679 | - // 表示するべきバーの数によって表示サイズと位置を変更する | |
680 | - int height = 0; | |
681 | - float maxTime = 0; | |
682 | - foreach (MarkerCollection bar in prevLog.Bars) | |
683 | - { | |
684 | - if (bar.MarkCount > 0) | |
685 | - { | |
686 | - height += BarHeight + BarPadding * 2; | |
687 | - maxTime = Math.Max(maxTime, | |
688 | - bar.Markers[bar.MarkCount - 1].EndTime); | |
689 | - } | |
690 | - } | |
691 | - | |
692 | - // 表示フレーム数の自動調整 | |
693 | - // 例えば16.6msで処理が間に合わなかった状態が一定時間以上続くと | |
694 | - // 自動的に表示する時間間隔を33.3msに調整する | |
695 | - const float frameSpan = 1.0f / 60.0f * 1000f; | |
696 | - float sampleSpan = (float)sampleFrames * frameSpan; | |
697 | - | |
698 | - if (maxTime > sampleSpan) | |
699 | - frameAdjust = Math.Max(0, frameAdjust) + 1; | |
700 | - else | |
701 | - frameAdjust = Math.Min(0, frameAdjust) - 1; | |
702 | - | |
703 | - if (Math.Abs(frameAdjust) > AutoAdjustDelay) | |
704 | - { | |
705 | - sampleFrames = Math.Min(MaxSampleFrames, sampleFrames); | |
706 | - sampleFrames = | |
707 | - Math.Max(TargetSampleFrames, (int)(maxTime / frameSpan) + 1); | |
708 | - | |
709 | - frameAdjust = 0; | |
710 | - } | |
711 | - | |
712 | - // ミリ秒からピクセルに変換する係数を計算 | |
713 | - float msToPs = (float)width / sampleSpan; | |
714 | - | |
715 | - // 描画開始位置 | |
716 | - int startY = (int)position.Y - (height - BarHeight); | |
717 | - | |
718 | - // 現在のy座標 | |
719 | - int y = startY; | |
720 | - | |
721 | - spriteBatch.Begin(); | |
722 | - | |
723 | - // 背景の半透明の矩形を描画 | |
724 | - Rectangle rc = new Rectangle((int)position.X, y, width, height); | |
725 | - spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128)); | |
726 | - | |
727 | - // 各バーのマーカーを描画 | |
728 | - rc.Height = BarHeight; | |
729 | - foreach (MarkerCollection bar in prevLog.Bars) | |
730 | - { | |
731 | - rc.Y = y + BarPadding; | |
732 | - if (bar.MarkCount > 0) | |
733 | - { | |
734 | - for (int j = 0; j < bar.MarkCount; ++j) | |
735 | - { | |
736 | - float bt = bar.Markers[j].BeginTime; | |
737 | - float et = bar.Markers[j].EndTime; | |
738 | - int sx = (int)(position.X + bt * msToPs); | |
739 | - int ex = (int)(position.X + et * msToPs); | |
740 | - rc.X = sx; | |
741 | - rc.Width = Math.Max(ex - sx, 1); | |
742 | - | |
743 | - spriteBatch.Draw(texture, rc, bar.Markers[j].Color); | |
744 | - } | |
745 | - } | |
746 | - | |
747 | - y += BarHeight + BarPadding; | |
748 | - } | |
749 | - | |
750 | - // グリッドを描画する | |
751 | - // ミリ秒単位のグリッド描画 | |
752 | - rc = new Rectangle((int)position.X, (int)startY, 1, height); | |
753 | - for (float t = 1.0f; t < sampleSpan; t += 1.0f) | |
754 | - { | |
755 | - rc.X = (int)(position.X + t * msToPs); | |
756 | - spriteBatch.Draw(texture, rc, Color.Gray); | |
757 | - } | |
758 | - | |
759 | - // フレーム単位のグリッド描画 | |
760 | - for (int i = 0; i <= sampleFrames; ++i) | |
761 | - { | |
762 | - rc.X = (int)(position.X + frameSpan * (float)i * msToPs); | |
763 | - spriteBatch.Draw(texture, rc, Color.White); | |
764 | - } | |
765 | - | |
766 | - // ログの表示 | |
767 | - if (ShowLog) | |
768 | - { | |
769 | - // 表示する文字列をStringBuilderで生成する | |
770 | - y = startY - font.LineSpacing; | |
771 | - logString.Length = 0; | |
772 | - foreach (MarkerInfo markerInfo in markers) | |
773 | - { | |
774 | - for (int i = 0; i < MaxBars; ++i) | |
775 | - { | |
776 | - if (markerInfo.Logs[i].Initialized) | |
777 | - { | |
778 | - if (logString.Length > 0) | |
779 | - logString.Append("\n"); | |
780 | - | |
781 | - logString.Append(" Bar "); | |
782 | - logString.AppendNumber(i); | |
783 | - logString.Append(" "); | |
784 | - logString.Append(markerInfo.Name); | |
785 | - | |
786 | - logString.Append(" Avg.:"); | |
787 | - logString.AppendNumber(markerInfo.Logs[i].SnapAvg); | |
788 | - logString.Append("ms "); | |
789 | - | |
790 | - y -= font.LineSpacing; | |
791 | - } | |
792 | - } | |
793 | - } | |
794 | - | |
795 | - // 表示する文字列の背景の矩形サイズの計算と描画 | |
796 | - Vector2 size = font.MeasureString(logString); | |
797 | - rc = new Rectangle((int)position.X, (int)y, (int)size.X + 12, (int)size.Y); | |
798 | - spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128)); | |
799 | - | |
800 | - // ログ文字列の描画 | |
801 | - spriteBatch.DrawString(font, logString, | |
802 | - new Vector2(position.X + 12, y), Color.White); | |
803 | - | |
804 | - // ログカラーボックスの描画 | |
805 | - y += (int)((float)font.LineSpacing * 0.3f); | |
806 | - rc = new Rectangle((int)position.X + 4, y, 10, 10); | |
807 | - Rectangle rc2 = new Rectangle((int)position.X + 5, y + 1, 8, 8); | |
808 | - foreach (MarkerInfo markerInfo in markers) | |
809 | - { | |
810 | - for (int i = 0; i < MaxBars; ++i) | |
811 | - { | |
812 | - if (markerInfo.Logs[i].Initialized) | |
813 | - { | |
814 | - rc.Y = y; | |
815 | - rc2.Y = y + 1; | |
816 | - spriteBatch.Draw(texture, rc, Color.White); | |
817 | - spriteBatch.Draw(texture, rc2, markerInfo.Logs[i].Color); | |
818 | - | |
819 | - y += font.LineSpacing; | |
820 | - } | |
821 | - } | |
822 | - } | |
823 | - | |
824 | - | |
825 | - } | |
826 | - | |
827 | - spriteBatch.End(); | |
828 | -#endif | |
829 | - } | |
830 | - | |
831 | - #endregion | |
832 | - | |
833 | - } | |
834 | -} |
@@ -1,120 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System.Collections.Generic; | |
4 | - | |
5 | -#endregion | |
6 | - | |
7 | -namespace DebugSample | |
8 | -{ | |
9 | - /// <summary> | |
10 | - /// デバッグコマンドのメッセージタイプ | |
11 | - /// </summary> | |
12 | - public enum DebugCommandMessage | |
13 | - { | |
14 | - // 標準出力 | |
15 | - Standard = 1, | |
16 | - | |
17 | - // エラー出力 | |
18 | - Error = 2, | |
19 | - | |
20 | - // 警告出力 | |
21 | - Warning = 3 | |
22 | - } | |
23 | - | |
24 | - /// <summary> | |
25 | - /// デバッグコマンド実行用のデリゲーション | |
26 | - /// </summary> | |
27 | - /// <param name="host">実行を発行したホスト</param> | |
28 | - /// <param name="command">コマンド</param> | |
29 | - /// <param name="arguments">コマンドの引数</param> | |
30 | - public delegate void DebugCommandExecute(IDebugCommandHost host, string command, | |
31 | - IList<string> arguments); | |
32 | - | |
33 | - /// <summary> | |
34 | - /// デバッグコマンド実行者インターフェース | |
35 | - /// </summary> | |
36 | - public interface IDebugCommandExecutioner | |
37 | - { | |
38 | - /// <summary> | |
39 | - /// コマンドの実行 | |
40 | - /// </summary> | |
41 | - /// <param name="command">実行コマンド</param> | |
42 | - void ExecuteCommand(string command); | |
43 | - } | |
44 | - | |
45 | - /// <summary> | |
46 | - /// デバッグコマンドメッセージのリスナー用インターフェース | |
47 | - /// </summary> | |
48 | - public interface IDebugEchoListner | |
49 | - { | |
50 | - /// <summary> | |
51 | - /// メッセージの出力 | |
52 | - /// </summary> | |
53 | - /// <param name="messageType">メッセージの種類</param> | |
54 | - /// <param name="text">メッセージ</param> | |
55 | - void Echo(DebugCommandMessage messageType, string text); | |
56 | - } | |
57 | - | |
58 | - /// <summary> | |
59 | - /// デバッグコマンドホスト用のインターフェース | |
60 | - /// </summary> | |
61 | - public interface IDebugCommandHost : IDebugEchoListner, IDebugCommandExecutioner | |
62 | - { | |
63 | - /// <summary> | |
64 | - /// コマンドの登録 | |
65 | - /// </summary> | |
66 | - /// <param name="command">コマンド</param> | |
67 | - /// <param name="description">コマンドの説明</param> | |
68 | - /// <param name="callback">実行時のデリゲーション</param> | |
69 | - void RegisterCommand(string command, string description, | |
70 | - DebugCommandExecute callback); | |
71 | - | |
72 | - /// <summary> | |
73 | - /// コマンドの登録解除 | |
74 | - /// </summary> | |
75 | - /// <param name="command">コマンド</param> | |
76 | - void UnregisterCommand(string command); | |
77 | - | |
78 | - | |
79 | - /// <summary> | |
80 | - /// 標準メッセージ出力 | |
81 | - /// </summary> | |
82 | - /// <param name="text"></param> | |
83 | - void Echo(string text); | |
84 | - | |
85 | - /// <summary> | |
86 | - /// 警告メッセージ出力 | |
87 | - /// </summary> | |
88 | - /// <param name="text"></param> | |
89 | - void EchoWarning(string text); | |
90 | - | |
91 | - /// <summary> | |
92 | - /// エラーメッセージ出力 | |
93 | - /// </summary> | |
94 | - /// <param name="text"></param> | |
95 | - void EchoError(string text); | |
96 | - | |
97 | - /// <summary> | |
98 | - /// メッセージ出力リスナーの登録 | |
99 | - /// </summary> | |
100 | - /// <param name="listner"></param> | |
101 | - void RegisterEchoListner(IDebugEchoListner listner); | |
102 | - | |
103 | - /// <summary> | |
104 | - /// メッセージ出力リスナーの登録解除 | |
105 | - /// </summary> | |
106 | - /// <param name="listner"></param> | |
107 | - void UnregisterEchoListner(IDebugEchoListner listner); | |
108 | - | |
109 | - /// <summary> | |
110 | - /// コマンド実行者の追加 | |
111 | - /// </summary> | |
112 | - void PushExecutioner(IDebugCommandExecutioner executioner); | |
113 | - | |
114 | - /// <summary> | |
115 | - /// コマンド実行者の削除 | |
116 | - /// </summary> | |
117 | - void PopExecutioner(); | |
118 | - } | |
119 | - | |
120 | -} |
@@ -1,403 +0,0 @@ | ||
1 | -#if XBOX360 || WINDOWS | |
2 | - | |
3 | -#region Using ステートメント | |
4 | - | |
5 | -using System; | |
6 | -using System.Collections.Generic; | |
7 | -using System.Text.RegularExpressions; | |
8 | -using Microsoft.Xna.Framework; | |
9 | -using Microsoft.Xna.Framework.GamerServices; | |
10 | -using Microsoft.Xna.Framework.Net; | |
11 | - | |
12 | -#endregion | |
13 | - | |
14 | -namespace DebugSample | |
15 | -{ | |
16 | - /// <summary> | |
17 | - /// リモートデバッグコマンドコンポーネント | |
18 | - /// </summary> | |
19 | - /// <remarks> | |
20 | - /// オリジナルのデバッグコマンドはXbox 360にキーボードを接続することで使えたが、 | |
21 | - /// キーボードが二つない場合や、Xbox 360本体が開発しているPCよりも遠くにある場合に | |
22 | - /// 不便なので、NetworkSessionを使ってPCサイドからリモート接続するのが、 | |
23 | - /// このコンポーネントである。 | |
24 | - /// | |
25 | - /// Xbox 360、Windowsで同じゲームを走らせておいた状態で、"remote"コマンドを | |
26 | - /// 使うと、WindowsからXbox 360へと接続する | |
27 | - /// | |
28 | - /// 接続が完了するとWindows側でタイプされたコマンドはXbox 360側に送られ実行される | |
29 | - /// 実行結果はXbox 360、Windowsの両方で表示される | |
30 | - /// | |
31 | - /// "quit"コマンドでリモート接続を終了する | |
32 | - /// </remarks> | |
33 | - public class RemoteDebugCommand : GameComponent, | |
34 | - IDebugCommandExecutioner, IDebugEchoListner | |
35 | - { | |
36 | - #region プロパティ | |
37 | - | |
38 | - /// <summary> | |
39 | - /// 接続に使うNetworkSessionの取得と設定 | |
40 | - /// </summary> | |
41 | - public NetworkSession NetworkSession { get; set; } | |
42 | - | |
43 | - /// <summary> | |
44 | - /// このコンポーネントがNetworkSessionのオーナーか? | |
45 | - /// </summary> | |
46 | - public bool OwnsNetworkSession { get; private set; } | |
47 | - | |
48 | - #endregion | |
49 | - | |
50 | - #region 定数宣言 | |
51 | - | |
52 | - const string StartPacketHeader = "RmtStart"; | |
53 | - const string ExecutePacketHeader = "RmtCmd"; | |
54 | - const string EchoPacketHeader = "RmtEcho"; | |
55 | - const string ErrorPacketHeader = "RmtErr"; | |
56 | - const string WarningPacketHeader = "RmtWrn"; | |
57 | - const string QuitPacketHeader = "RmtQuit"; | |
58 | - | |
59 | - #endregion | |
60 | - | |
61 | - #region フィールド | |
62 | - | |
63 | - IDebugCommandHost commandHost; | |
64 | - | |
65 | -#if WINDOWS | |
66 | - bool IsHost = false; | |
67 | -#else | |
68 | - bool IsHost = true; | |
69 | -#endif | |
70 | - | |
71 | - Regex packetRe = new Regex(@"\$(?<header>[^$]+)\$:(?<text>.+)"); | |
72 | - | |
73 | - PacketReader packetReader = new PacketReader(); | |
74 | - PacketWriter packetWriter = new PacketWriter(); | |
75 | - | |
76 | - IAsyncResult asyncResult; | |
77 | - | |
78 | - enum ConnectionPahse | |
79 | - { | |
80 | - None, | |
81 | - EnsureSignedIn, | |
82 | - FindSessions, | |
83 | - Joining, | |
84 | - } | |
85 | - | |
86 | - ConnectionPahse phase = ConnectionPahse.None; | |
87 | - | |
88 | - #endregion | |
89 | - | |
90 | - #region 初期化 | |
91 | - | |
92 | - public RemoteDebugCommand(Game game) | |
93 | - : base(game) | |
94 | - { | |
95 | - commandHost = | |
96 | - game.Services.GetService(typeof(IDebugCommandHost)) as IDebugCommandHost; | |
97 | - | |
98 | - if (!IsHost) | |
99 | - { | |
100 | - commandHost.RegisterCommand("remote", "Start remote command", | |
101 | - ExecuteRemoteCommand); | |
102 | - } | |
103 | - } | |
104 | - | |
105 | - public override void Initialize() | |
106 | - { | |
107 | - if (IsHost) | |
108 | - { | |
109 | - commandHost.RegisterEchoListner(this); | |
110 | - | |
111 | - // Create network session if NetworkSession is not setted. | |
112 | - if (NetworkSession == null) | |
113 | - { | |
114 | - GamerServicesDispatcher.WindowHandle = Game.Window.Handle; | |
115 | - GamerServicesDispatcher.Initialize(Game.Services); | |
116 | - NetworkSession = | |
117 | - NetworkSession.Create(NetworkSessionType.SystemLink, 1, 2); | |
118 | - | |
119 | - OwnsNetworkSession = true; | |
120 | - } | |
121 | - } | |
122 | - | |
123 | - base.Initialize(); | |
124 | - } | |
125 | - | |
126 | - #endregion | |
127 | - | |
128 | - /// <summary> | |
129 | - /// パケット文字の処理 | |
130 | - /// </summary> | |
131 | - /// <remarks>ゲーム側でNetworkSessionを保持している場合に受け取ったリモート | |
132 | - /// デバッグコマンド用のパケットはこのメソッドで処理する | |
133 | - /// </remarks> | |
134 | - /// <param name="packetString"></param> | |
135 | - /// <returns>指定されたパケット文字が処理されたか?</returns> | |
136 | - public bool ProcessRecievedPacket(string packetString) | |
137 | - { | |
138 | - bool processed = false; | |
139 | - | |
140 | - Match mc = packetRe.Match(packetString); | |
141 | - if (mc.Success) | |
142 | - { | |
143 | - string packetHeader = mc.Groups["header"].Value; | |
144 | - string text = mc.Groups["text"].Value; | |
145 | - switch (packetHeader) | |
146 | - { | |
147 | - case ExecutePacketHeader: | |
148 | - commandHost.ExecuteCommand(text); | |
149 | - processed = true; | |
150 | - break; | |
151 | - case EchoPacketHeader: | |
152 | - commandHost.Echo(text); | |
153 | - processed = true; | |
154 | - break; | |
155 | - case ErrorPacketHeader: | |
156 | - commandHost.EchoError(text); | |
157 | - processed = true; | |
158 | - break; | |
159 | - case WarningPacketHeader: | |
160 | - commandHost.EchoWarning(text); | |
161 | - processed = true; | |
162 | - break; | |
163 | - case StartPacketHeader: | |
164 | - ConnectedToRemote(); | |
165 | - commandHost.Echo(text); | |
166 | - processed = true; | |
167 | - break; | |
168 | - case QuitPacketHeader: | |
169 | - commandHost.Echo(text); | |
170 | - DisconnectedFromRemote(); | |
171 | - processed = true; | |
172 | - break; | |
173 | - } | |
174 | - } | |
175 | - | |
176 | - return processed; | |
177 | - } | |
178 | - | |
179 | - #region 実装 | |
180 | - | |
181 | - /// <summary> | |
182 | - /// 更新 | |
183 | - /// </summary> | |
184 | - public override void Update(GameTime gameTime) | |
185 | - { | |
186 | - // 複数のフェーズ処理 | |
187 | - switch (phase) | |
188 | - { | |
189 | - case ConnectionPahse.EnsureSignedIn: | |
190 | - GamerServicesDispatcher.Update(); | |
191 | - break; | |
192 | - | |
193 | - case ConnectionPahse.FindSessions: | |
194 | - GamerServicesDispatcher.Update(); | |
195 | - if (asyncResult.IsCompleted) | |
196 | - { | |
197 | - AvailableNetworkSessionCollection sessions = | |
198 | - NetworkSession.EndFind(asyncResult); | |
199 | - | |
200 | - if (sessions.Count > 0) | |
201 | - { | |
202 | - asyncResult = NetworkSession.BeginJoin(sessions[0], | |
203 | - null, null); | |
204 | - commandHost.EchoError("Connecting to the host..."); | |
205 | - phase = ConnectionPahse.Joining; | |
206 | - } | |
207 | - else | |
208 | - { | |
209 | - commandHost.EchoError("Couldn't find a session."); | |
210 | - phase = ConnectionPahse.None; | |
211 | - } | |
212 | - } | |
213 | - break; | |
214 | - case ConnectionPahse.Joining: | |
215 | - GamerServicesDispatcher.Update(); | |
216 | - if (asyncResult.IsCompleted) | |
217 | - { | |
218 | - NetworkSession = NetworkSession.EndJoin(asyncResult); | |
219 | - NetworkSession.SessionEnded += | |
220 | - new EventHandler<NetworkSessionEndedEventArgs>( | |
221 | - NetworkSession_SessionEnded); | |
222 | - | |
223 | - OwnsNetworkSession = true; | |
224 | - commandHost.EchoError("Connected to the host."); | |
225 | - phase = ConnectionPahse.None; | |
226 | - asyncResult = null; | |
227 | - | |
228 | - ConnectedToRemote(); | |
229 | - } | |
230 | - break; | |
231 | - } | |
232 | - | |
233 | - // NetworkSessionの更新 | |
234 | - if (OwnsNetworkSession) | |
235 | - { | |
236 | - GamerServicesDispatcher.Update(); | |
237 | - NetworkSession.Update(); | |
238 | - | |
239 | - if (NetworkSession != null) | |
240 | - { | |
241 | - // 受け取ったパケットの処理 | |
242 | - foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers) | |
243 | - { | |
244 | - while (gamer.IsDataAvailable) | |
245 | - { | |
246 | - NetworkGamer sender; | |
247 | - gamer.ReceiveData(packetReader, out sender); | |
248 | - if (!sender.IsLocal) | |
249 | - ProcessRecievedPacket(packetReader.ReadString()); | |
250 | - } | |
251 | - } | |
252 | - } | |
253 | - } | |
254 | - | |
255 | - base.Update(gameTime); | |
256 | - } | |
257 | - | |
258 | - /// <summary> | |
259 | - /// デバッグコマンドパケットを送信する | |
260 | - /// </summary> | |
261 | - void SendPacket(string header, string text) | |
262 | - { | |
263 | - if (NetworkSession != null) | |
264 | - { | |
265 | - packetWriter.Write("$" + header + "$:" + text); | |
266 | - NetworkSession.LocalGamers[0].SendData(packetWriter, | |
267 | - SendDataOptions.ReliableInOrder); | |
268 | - } | |
269 | - } | |
270 | - | |
271 | - /// <summary> | |
272 | - /// リモートデバッグコマンドの開始 | |
273 | - /// </summary> | |
274 | - void ConnectedToRemote() | |
275 | - { | |
276 | - DebugCommandUI commandUI = commandHost as DebugCommandUI; | |
277 | - | |
278 | - if (IsHost) | |
279 | - { | |
280 | - if (commandUI != null) | |
281 | - commandUI.Prompt = "[Host]>"; | |
282 | - } | |
283 | - else | |
284 | - { | |
285 | - if (commandUI != null) | |
286 | - commandUI.Prompt = "[Client]>"; | |
287 | - | |
288 | - commandHost.PushExecutioner(this); | |
289 | - | |
290 | - SendPacket(StartPacketHeader, "Remote Debug Command Started!!"); | |
291 | - } | |
292 | - | |
293 | - commandHost.RegisterCommand("quit", "Quit from remote command", | |
294 | - ExecuteQuitCommand); | |
295 | - } | |
296 | - | |
297 | - /// <summary> | |
298 | - /// リモートデバッグコマンドの終了 | |
299 | - /// </summary> | |
300 | - void DisconnectedFromRemote() | |
301 | - { | |
302 | - DebugCommandUI commandUI = commandHost as DebugCommandUI; | |
303 | - if (commandUI != null) | |
304 | - commandUI.Prompt = DebugCommandUI.DefaultPrompt; | |
305 | - | |
306 | - commandHost.UnregisterCommand("quit"); | |
307 | - | |
308 | - if (!IsHost) | |
309 | - { | |
310 | - commandHost.PopExecutioner(); | |
311 | - | |
312 | - if (OwnsNetworkSession) | |
313 | - { | |
314 | - NetworkSession.Dispose(); | |
315 | - NetworkSession = null; | |
316 | - OwnsNetworkSession = false; | |
317 | - } | |
318 | - } | |
319 | - } | |
320 | - | |
321 | - #region デバッグコマンドの実装 | |
322 | - | |
323 | - private void ExecuteRemoteCommand(IDebugCommandHost host, string command, | |
324 | - IList<string> arguments) | |
325 | - { | |
326 | - if (NetworkSession == null) | |
327 | - { | |
328 | - try | |
329 | - { | |
330 | - GamerServicesDispatcher.WindowHandle = Game.Window.Handle; | |
331 | - GamerServicesDispatcher.Initialize(Game.Services); | |
332 | - } | |
333 | - catch { } | |
334 | - | |
335 | - if (SignedInGamer.SignedInGamers.Count > 0) | |
336 | - { | |
337 | - commandHost.Echo("Finding available sessions..."); | |
338 | - | |
339 | - asyncResult = NetworkSession.BeginFind( | |
340 | - NetworkSessionType.SystemLink, 1, null, null, null); | |
341 | - | |
342 | - phase = ConnectionPahse.FindSessions; | |
343 | - } | |
344 | - else | |
345 | - { | |
346 | - host.Echo("Please signed in."); | |
347 | - phase = ConnectionPahse.EnsureSignedIn; | |
348 | - } | |
349 | - } | |
350 | - else | |
351 | - { | |
352 | - ConnectedToRemote(); | |
353 | - } | |
354 | - } | |
355 | - | |
356 | - private void ExecuteQuitCommand(IDebugCommandHost host, string command, | |
357 | - IList<string> arguments) | |
358 | - { | |
359 | - SendPacket(QuitPacketHeader, "End Remote Debug Command."); | |
360 | - DisconnectedFromRemote(); | |
361 | - } | |
362 | - | |
363 | - #endregion | |
364 | - | |
365 | - #region IDebugCommandExecutionerとIDebugEchoListnerの実装 | |
366 | - | |
367 | - public void ExecuteCommand(string command) | |
368 | - { | |
369 | - SendPacket(ExecutePacketHeader, command); | |
370 | - } | |
371 | - | |
372 | - public void Echo(DebugCommandMessage messageType, string text) | |
373 | - { | |
374 | - switch (messageType) | |
375 | - { | |
376 | - case DebugCommandMessage.Standard: | |
377 | - SendPacket(EchoPacketHeader, text); | |
378 | - break; | |
379 | - case DebugCommandMessage.Warning: | |
380 | - SendPacket(WarningPacketHeader, text); | |
381 | - break; | |
382 | - case DebugCommandMessage.Error: | |
383 | - SendPacket(ErrorPacketHeader, text); | |
384 | - break; | |
385 | - } | |
386 | - } | |
387 | - | |
388 | - #endregion | |
389 | - | |
390 | - /// <summary> | |
391 | - /// ホストが消失したときの処理 | |
392 | - /// </summary> | |
393 | - void NetworkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e) | |
394 | - { | |
395 | - DisconnectedFromRemote(); | |
396 | - commandHost.EchoWarning("Disconnected from the Host."); | |
397 | - } | |
398 | - | |
399 | - #endregion | |
400 | - } | |
401 | -} | |
402 | - | |
403 | -#endif | |
\ No newline at end of file |
@@ -1,73 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using Microsoft.Xna.Framework; | |
4 | -using Microsoft.Xna.Framework.Content; | |
5 | -using Microsoft.Xna.Framework.Graphics; | |
6 | - | |
7 | -#endregion | |
8 | - | |
9 | -namespace DebugSample | |
10 | -{ | |
11 | - /// <summary> | |
12 | - /// デバッグ用のグラフィクスコンテントを格納する為のデバッグ用マネージャー | |
13 | - /// </summary> | |
14 | - public class DebugManager : DrawableGameComponent | |
15 | - { | |
16 | - #region プロパティ | |
17 | - | |
18 | - /// <summary> | |
19 | - /// デバッグ用コンテントマネージャーの取得 | |
20 | - /// </summary> | |
21 | - public ContentManager Content { get; private set; } | |
22 | - | |
23 | - /// <summary> | |
24 | - /// デバッグ用SpriteBatchの取得 | |
25 | - /// </summary> | |
26 | - public SpriteBatch SpriteBatch { get; private set; } | |
27 | - | |
28 | - /// <summary> | |
29 | - /// 白テクスチャの取得 | |
30 | - /// </summary> | |
31 | - public Texture2D WhiteTexture { get; private set; } | |
32 | - | |
33 | - /// <summary> | |
34 | - /// デバッグ用フォント | |
35 | - /// </summary> | |
36 | - public SpriteFont DebugFont { get; private set; } | |
37 | - | |
38 | - #endregion | |
39 | - | |
40 | - #region 初期化 | |
41 | - | |
42 | - public DebugManager(Game game) | |
43 | - : base(game) | |
44 | - { | |
45 | - // サービスとして登録する | |
46 | - Game.Services.AddService(typeof(DebugManager), this); | |
47 | - | |
48 | - Content = new ContentManager(game.Services); | |
49 | - Content.RootDirectory = "Content/Debug"; | |
50 | - | |
51 | - // このコンポーネント自体はUpdate、Drawが呼ばれる必要はない | |
52 | - this.Enabled = false; | |
53 | - this.Visible = false; | |
54 | - } | |
55 | - | |
56 | - protected override void LoadContent() | |
57 | - { | |
58 | - // デバッグ用コンテントの読み込み | |
59 | - SpriteBatch = new SpriteBatch(GraphicsDevice); | |
60 | - | |
61 | - DebugFont = Content.Load<SpriteFont>("DebugFont"); | |
62 | - | |
63 | - // 白テクスチャの生成 | |
64 | - WhiteTexture = new Texture2D(GraphicsDevice, 1, 1); | |
65 | - Color[] whitePixels = new Color[] { Color.White }; | |
66 | - WhiteTexture.SetData<Color>(whitePixels); | |
67 | - | |
68 | - base.LoadContent(); | |
69 | - } | |
70 | - | |
71 | - #endregion | |
72 | - } | |
73 | -} | |
\ No newline at end of file |
@@ -1,156 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | -using System.Collections.Generic; | |
5 | -using Microsoft.Xna.Framework.Input; | |
6 | - | |
7 | -#endregion | |
8 | - | |
9 | -namespace DebugSample | |
10 | -{ | |
11 | - /// <summary> | |
12 | - /// キーボード入力関連のユーティリティクラス | |
13 | - /// </summary> | |
14 | - public static class KeyboardUtils | |
15 | - { | |
16 | - #region フィールド | |
17 | - | |
18 | - /// <summary> | |
19 | - /// 通常の文字とシフトキーが押下された時の文字を保持する文字ペアクラス | |
20 | - /// </summary> | |
21 | - class CharPair | |
22 | - { | |
23 | - public CharPair(char normalChar, Nullable<char> shiftChar) | |
24 | - { | |
25 | - this.NormalChar = normalChar; | |
26 | - this.ShiftChar = shiftChar; | |
27 | - } | |
28 | - | |
29 | - public char NormalChar; | |
30 | - public Nullable<char> ShiftChar; | |
31 | - } | |
32 | - | |
33 | - // キー:Keys, 値:CharPair | |
34 | - static private Dictionary<Keys, CharPair> keyMap = | |
35 | - new Dictionary<Keys, CharPair>(); | |
36 | - | |
37 | - #endregion | |
38 | - | |
39 | - /// <summary> | |
40 | - /// キー情報から文字を取得する | |
41 | - /// </summary> | |
42 | - /// <param name="key">押下されたキー</param> | |
43 | - /// <param name="shitKeyPressed">シフトキーが押されていたか?</param> | |
44 | - /// <param name="character">キー入力から変換された文字</param> | |
45 | - /// <returns>文字取得が成功した場合trueを返す</returns> | |
46 | - public static bool KeyToString(Keys key, bool shitKeyPressed, | |
47 | - out char character) | |
48 | - { | |
49 | - bool result = false; | |
50 | - character = ' '; | |
51 | - CharPair charPair; | |
52 | - | |
53 | - if ((Keys.A <= key && key <= Keys.Z) || key == Keys.Space) | |
54 | - { | |
55 | - // A~Z、スペースキーはそのまま文字コードとして使用する | |
56 | - character = (shitKeyPressed) ? (char)key : Char.ToLower((char)key); | |
57 | - result = true; | |
58 | - } | |
59 | - else if (keyMap.TryGetValue(key, out charPair)) | |
60 | - { | |
61 | - // それ以外の場合はKeyMapの情報を元に変換する | |
62 | - if (!shitKeyPressed) | |
63 | - { | |
64 | - character = charPair.NormalChar; | |
65 | - result = true; | |
66 | - } | |
67 | - else if (charPair.ShiftChar.HasValue) | |
68 | - { | |
69 | - character = charPair.ShiftChar.Value; | |
70 | - result = true; | |
71 | - } | |
72 | - } | |
73 | - | |
74 | - return result; | |
75 | - } | |
76 | - | |
77 | - #region 初期化 | |
78 | - | |
79 | - static KeyboardUtils() | |
80 | - { | |
81 | - InitializeKeyMap(); | |
82 | - } | |
83 | - | |
84 | - /// <summary> | |
85 | - /// 英字以外のキーの文字マップの初期化 | |
86 | - /// </summary> | |
87 | - /// <remarks>ここではUSキーボード用のものを宣言しているので、 | |
88 | - /// 日本語キーボードでは変更する必要があるかも</remarks> | |
89 | - static void InitializeKeyMap() | |
90 | - { | |
91 | - // 英語キーボードの上から1列目 | |
92 | - AddKeyMap(Keys.OemTilde, "`~"); | |
93 | - AddKeyMap(Keys.D1, "1!"); | |
94 | - AddKeyMap(Keys.D2, "2@"); | |
95 | - AddKeyMap(Keys.D3, "3#"); | |
96 | - AddKeyMap(Keys.D4, "4$"); | |
97 | - AddKeyMap(Keys.D5, "5%"); | |
98 | - AddKeyMap(Keys.D6, "6^"); | |
99 | - AddKeyMap(Keys.D7, "7&"); | |
100 | - AddKeyMap(Keys.D8, "8*"); | |
101 | - AddKeyMap(Keys.D9, "9("); | |
102 | - AddKeyMap(Keys.D0, "0)"); | |
103 | - AddKeyMap(Keys.OemMinus, "-_"); | |
104 | - AddKeyMap(Keys.OemPlus, "=+"); | |
105 | - | |
106 | - // 英語キーボードの上から2列目 | |
107 | - AddKeyMap(Keys.OemOpenBrackets, "[{"); | |
108 | - AddKeyMap(Keys.OemCloseBrackets, "]}"); | |
109 | - AddKeyMap(Keys.OemPipe, "\\|"); | |
110 | - | |
111 | - // 英語キーボードの上から3列目 | |
112 | - AddKeyMap(Keys.OemSemicolon, ";:"); | |
113 | - AddKeyMap(Keys.OemQuotes, "'\""); | |
114 | - AddKeyMap(Keys.OemComma, ",<"); | |
115 | - AddKeyMap(Keys.OemPeriod, ".>"); | |
116 | - AddKeyMap(Keys.OemQuestion, "/?"); | |
117 | - | |
118 | - // 英語キーボードのキーパッドのキー | |
119 | - AddKeyMap(Keys.NumPad1, "1"); | |
120 | - AddKeyMap(Keys.NumPad2, "2"); | |
121 | - AddKeyMap(Keys.NumPad3, "3"); | |
122 | - AddKeyMap(Keys.NumPad4, "4"); | |
123 | - AddKeyMap(Keys.NumPad5, "5"); | |
124 | - AddKeyMap(Keys.NumPad6, "6"); | |
125 | - AddKeyMap(Keys.NumPad7, "7"); | |
126 | - AddKeyMap(Keys.NumPad8, "8"); | |
127 | - AddKeyMap(Keys.NumPad9, "9"); | |
128 | - AddKeyMap(Keys.NumPad0, "0"); | |
129 | - AddKeyMap(Keys.Add, "+"); | |
130 | - AddKeyMap(Keys.Divide, "/"); | |
131 | - AddKeyMap(Keys.Multiply, "*"); | |
132 | - AddKeyMap(Keys.Subtract, "-"); | |
133 | - AddKeyMap(Keys.Decimal, "."); | |
134 | - } | |
135 | - | |
136 | - /// <summary> | |
137 | - /// キーボードのキーと文字マップの追加 | |
138 | - /// </summary> | |
139 | - /// <param name="key">キーボードのキー</param> | |
140 | - /// <param name="charPair"> | |
141 | - /// 文字、2文字の場合はシフトキー無し、有り順番に記述する</param> | |
142 | - static void AddKeyMap(Keys key, string charPair) | |
143 | - { | |
144 | - char char1 = charPair[0]; | |
145 | - Nullable<char> char2 = null; | |
146 | - if (charPair.Length > 1) | |
147 | - char2 = charPair[1]; | |
148 | - | |
149 | - keyMap.Add(key, new CharPair(char1, char2)); | |
150 | - } | |
151 | - | |
152 | - #endregion | |
153 | - | |
154 | - } | |
155 | - | |
156 | -} |
@@ -1,572 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | -using System.Collections.Generic; | |
5 | -using Microsoft.Xna.Framework; | |
6 | -using Microsoft.Xna.Framework.Graphics; | |
7 | -using Microsoft.Xna.Framework.Input; | |
8 | - | |
9 | -#endregion | |
10 | - | |
11 | -namespace DebugSample | |
12 | -{ | |
13 | - /// <summary> | |
14 | - /// デバッグ用コマンドウィンドウクラス | |
15 | - /// </summary> | |
16 | - /// <remarks> | |
17 | - /// ゲーム内で動作するデバックコマンドウィンドウUI部分 | |
18 | - /// キーボード入力によってコマンドを入力、実行することができる。 | |
19 | - /// Xbox 360でもUSBキーボードを接続することで動作可能。 | |
20 | - /// | |
21 | - /// 使用方法: | |
22 | - /// 1)このコンポーネントをゲームに追加。 | |
23 | - /// 2)RegisterCommandメソッドを使ってコマンドを登録する | |
24 | - /// 3)Tabキーでデバッグウィンドウの開閉しコマンド入力 | |
25 | - /// </remarks> | |
26 | - public class DebugCommandUI : DrawableGameComponent, IDebugCommandHost | |
27 | - { | |
28 | - #region 定数宣言 | |
29 | - | |
30 | - /// <summary> | |
31 | - /// 最大行数 | |
32 | - /// </summary> | |
33 | - const int MaxLineCount = 20; | |
34 | - | |
35 | - /// <summary> | |
36 | - /// コマンドヒストリ数 | |
37 | - /// </summary> | |
38 | - const int MaxCommandHistory = 32; | |
39 | - | |
40 | - /// <summary> | |
41 | - /// カーソル文字。ここではUnicodeのBlock Eleemntsからカーソルっぽいものを使用 | |
42 | - /// http://www.unicode.org/charts/PDF/U2580.pdf | |
43 | - /// </summary> | |
44 | - const string Cursor = "\u2582"; | |
45 | - | |
46 | - /// <summary> | |
47 | - /// デフォルトのコマンドプロンプト文字列 | |
48 | - /// </summary> | |
49 | - public const string DefaultPrompt = "CMD>"; | |
50 | - | |
51 | - #endregion | |
52 | - | |
53 | - #region プロパティ | |
54 | - | |
55 | - /// <summary> | |
56 | - /// コマンドプロンプト文字列 | |
57 | - /// </summary> | |
58 | - public string Prompt { get; set; } | |
59 | - | |
60 | - /// <summary> | |
61 | - /// キー入力待機状態か | |
62 | - /// </summary> | |
63 | - public bool Focused { get { return state != State.Closed; } } | |
64 | - | |
65 | - #endregion | |
66 | - | |
67 | - #region フィールド | |
68 | - | |
69 | - // コマンドウィンドウのステート | |
70 | - enum State | |
71 | - { | |
72 | - Closed, // 閉じている | |
73 | - Opening, // 開いている途中 | |
74 | - Opened, // 開いている(コマンド入力待機中) | |
75 | - Closing // 閉じている途中 | |
76 | - } | |
77 | - | |
78 | - /// <summary> | |
79 | - /// コマンド実行用情報格納用のクラス | |
80 | - /// </summary> | |
81 | - class CommandInfo | |
82 | - { | |
83 | - public CommandInfo( | |
84 | - string command, string description, DebugCommandExecute callback) | |
85 | - { | |
86 | - this.command = command; | |
87 | - this.description = description; | |
88 | - this.callback = callback; | |
89 | - } | |
90 | - | |
91 | - // コマンド名 | |
92 | - public string command; | |
93 | - | |
94 | - // コマンド詳細 | |
95 | - public string description; | |
96 | - | |
97 | - // コマンド実行用のデリゲート | |
98 | - public DebugCommandExecute callback; | |
99 | - } | |
100 | - | |
101 | - // デバッグマネージャーへの参照 | |
102 | - private DebugManager debugManager; | |
103 | - | |
104 | - // 現在のステート | |
105 | - private State state = State.Closed; | |
106 | - | |
107 | - // ステート移行用のタイマー | |
108 | - private float stateTransition; | |
109 | - | |
110 | - // 登録されているEchoリスナー | |
111 | - List<IDebugEchoListner> listenrs = new List<IDebugEchoListner>(); | |
112 | - | |
113 | - // 登録されているコマンド実行者 | |
114 | - Stack<IDebugCommandExecutioner> executioners = new Stack<IDebugCommandExecutioner>(); | |
115 | - | |
116 | - // 登録されているコマンド | |
117 | - private Dictionary<string, CommandInfo> commandTable = | |
118 | - new Dictionary<string, CommandInfo>(); | |
119 | - | |
120 | - // 現在入力中のコマンドライン文字列と、カーソル位置 | |
121 | - private string commandLine = String.Empty; | |
122 | - private int cursorIndex = 0; | |
123 | - | |
124 | - // コマンドライン表示文字列 | |
125 | - private Queue<string> lines = new Queue<string>(); | |
126 | - | |
127 | - // コマンド履歴用バッファ | |
128 | - private List<string> commandHistory = new List<string>(); | |
129 | - | |
130 | - // 現在選択されている履歴インデックス | |
131 | - private int commandHistoryIndex; | |
132 | - | |
133 | - #region キーボード入力処理用の変数群 | |
134 | - | |
135 | - // 前フレームのキーボードステート | |
136 | - private KeyboardState prevKeyState; | |
137 | - | |
138 | - // 最後に押されたキー | |
139 | - private Keys pressedKey; | |
140 | - | |
141 | - // キーリピートタイマー | |
142 | - private float keyRepeatTimer; | |
143 | - | |
144 | - // 最初のキー押下時のリピート時間(秒) | |
145 | - private float keyRepeatStartDuration = 0.3f; | |
146 | - | |
147 | - // 2回目以降のキーリピート時間(秒) | |
148 | - private float keyRepeatDuration = 0.03f; | |
149 | - | |
150 | - #endregion | |
151 | - | |
152 | - #endregion | |
153 | - | |
154 | - #region 初期化 | |
155 | - | |
156 | - /// <summary> | |
157 | - /// コンストラクタ | |
158 | - /// </summary> | |
159 | - public DebugCommandUI(Game game) | |
160 | - : base(game) | |
161 | - { | |
162 | - Prompt = DefaultPrompt; | |
163 | - | |
164 | - // サービスとして追加する | |
165 | - Game.Services.AddService(typeof(IDebugCommandHost), this); | |
166 | - | |
167 | - // 基本コマンドの追加 | |
168 | - | |
169 | - // ヘルプコマンド | |
170 | - // 登録されているコマンド情報の表示 | |
171 | - RegisterCommand("help", "Show Command helps", | |
172 | - delegate(IDebugCommandHost host, string command, IList<string> args) | |
173 | - { | |
174 | - int maxLen = 0; | |
175 | - foreach (CommandInfo cmd in commandTable.Values) | |
176 | - maxLen = Math.Max(maxLen, cmd.command.Length); | |
177 | - | |
178 | - string fmt = String.Format("{{0,-{0}}} {{1}}", maxLen); | |
179 | - | |
180 | - foreach (CommandInfo cmd in commandTable.Values) | |
181 | - { | |
182 | - Echo(String.Format(fmt, cmd.command, cmd.description)); | |
183 | - } | |
184 | - }); | |
185 | - | |
186 | - // クリアスクリーン | |
187 | - // コマンド画面クリア | |
188 | - RegisterCommand("cls", "Clear Screen", | |
189 | - delegate(IDebugCommandHost host, string command, IList<string> args) | |
190 | - { | |
191 | - lines.Clear(); | |
192 | - }); | |
193 | - | |
194 | - // Echoコマンド | |
195 | - RegisterCommand("echo", "Display Messages", | |
196 | - delegate(IDebugCommandHost host, string command, IList<string> args) | |
197 | - { | |
198 | - Echo(command.Substring(5)); | |
199 | - }); | |
200 | - } | |
201 | - | |
202 | - /// <summary> | |
203 | - /// コンポーネントの初期化 | |
204 | - /// </summary> | |
205 | - public override void Initialize() | |
206 | - { | |
207 | - debugManager = | |
208 | - Game.Services.GetService(typeof(DebugManager)) as DebugManager; | |
209 | - | |
210 | - if (debugManager == null) | |
211 | - throw new InvalidOperationException("DebugManagerが見つかりません。"); | |
212 | - | |
213 | - base.Initialize(); | |
214 | - } | |
215 | - | |
216 | - #endregion | |
217 | - | |
218 | - #region IDebugCommandHostインターフェースの実装 | |
219 | - | |
220 | - public void RegisterCommand( | |
221 | - string command, string description, DebugCommandExecute callback) | |
222 | - { | |
223 | - string lowerCommand = command.ToLower(); | |
224 | - if (commandTable.ContainsKey(lowerCommand)) | |
225 | - { | |
226 | - throw new InvalidOperationException( | |
227 | - String.Format("{0}は既に登録されています", command)); | |
228 | - } | |
229 | - | |
230 | - commandTable.Add( | |
231 | - lowerCommand, new CommandInfo(command, description, callback)); | |
232 | - } | |
233 | - | |
234 | - public void UnregisterCommand(string command) | |
235 | - { | |
236 | - string lowerCommand = command.ToLower(); | |
237 | - if (!commandTable.ContainsKey(lowerCommand)) | |
238 | - { | |
239 | - throw new InvalidOperationException( | |
240 | - String.Format("{0}は登録されていません", command)); | |
241 | - } | |
242 | - | |
243 | - commandTable.Remove(command); | |
244 | - } | |
245 | - | |
246 | - public void ExecuteCommand(string command) | |
247 | - { | |
248 | - // 他のコマンド実行者が登録されている場合は、最新の登録者にコマンドを | |
249 | - // 実行させる。 | |
250 | - if (executioners.Count != 0) | |
251 | - { | |
252 | - executioners.Peek().ExecuteCommand(command); | |
253 | - return; | |
254 | - } | |
255 | - | |
256 | - // コマンドの実行 | |
257 | - char[] spaceChars = new char[] { ' ' }; | |
258 | - | |
259 | - Echo(Prompt + command); | |
260 | - | |
261 | - command = command.TrimStart(spaceChars); | |
262 | - | |
263 | - List<string> args = new List<string>(command.Split(spaceChars)); | |
264 | - string cmdText = args[0]; | |
265 | - args.RemoveAt(0); | |
266 | - | |
267 | - CommandInfo cmd; | |
268 | - if (commandTable.TryGetValue(cmdText.ToLower(), out cmd)) | |
269 | - { | |
270 | - try | |
271 | - { | |
272 | - // 登録されているコマンドのデリゲートを呼び出す | |
273 | - cmd.callback(this, command, args); | |
274 | - } | |
275 | - catch (Exception e) | |
276 | - { | |
277 | - // 例外がコマンド実行中に発生 | |
278 | - EchoError("Unhandled Exception occured"); | |
279 | - | |
280 | - string[] lines = e.Message.Split(new char[] { '\n' }); | |
281 | - foreach (string line in lines) | |
282 | - EchoError(line); | |
283 | - } | |
284 | - } | |
285 | - else | |
286 | - { | |
287 | - Echo("Unknown Command"); | |
288 | - } | |
289 | - | |
290 | - // コマンドヒストリに追加する | |
291 | - commandHistory.Add(command); | |
292 | - while (commandHistory.Count > MaxCommandHistory) | |
293 | - commandHistory.RemoveAt(0); | |
294 | - | |
295 | - commandHistoryIndex = commandHistory.Count; | |
296 | - } | |
297 | - | |
298 | - public void RegisterEchoListner(IDebugEchoListner listner) | |
299 | - { | |
300 | - listenrs.Add(listner); | |
301 | - } | |
302 | - | |
303 | - public void UnregisterEchoListner(IDebugEchoListner listner) | |
304 | - { | |
305 | - listenrs.Remove(listner); | |
306 | - } | |
307 | - | |
308 | - public void Echo(DebugCommandMessage messageType, string text) | |
309 | - { | |
310 | - lines.Enqueue(text); | |
311 | - while (lines.Count >= MaxLineCount) | |
312 | - lines.Dequeue(); | |
313 | - | |
314 | - // 登録されているリスナーを呼び出す | |
315 | - foreach (IDebugEchoListner listner in listenrs) | |
316 | - listner.Echo(messageType, text); | |
317 | - } | |
318 | - | |
319 | - public void Echo(string text) | |
320 | - { | |
321 | - Echo(DebugCommandMessage.Standard, text); | |
322 | - } | |
323 | - | |
324 | - public void EchoWarning(string text) | |
325 | - { | |
326 | - Echo(DebugCommandMessage.Warning, text); | |
327 | - } | |
328 | - | |
329 | - public void EchoError(string text) | |
330 | - { | |
331 | - Echo(DebugCommandMessage.Error, text); | |
332 | - } | |
333 | - | |
334 | - public void PushExecutioner(IDebugCommandExecutioner executioner) | |
335 | - { | |
336 | - executioners.Push(executioner); | |
337 | - } | |
338 | - | |
339 | - public void PopExecutioner() | |
340 | - { | |
341 | - executioners.Pop(); | |
342 | - } | |
343 | - | |
344 | - #endregion | |
345 | - | |
346 | - #region 更新と描画 | |
347 | - | |
348 | - /// <summary> | |
349 | - /// デバッグコマンドウィンドウを表示する。 | |
350 | - /// </summary> | |
351 | - public void Show() | |
352 | - { | |
353 | - if (state == State.Closed) | |
354 | - { | |
355 | - stateTransition = 0.0f; | |
356 | - state = State.Opening; | |
357 | - } | |
358 | - } | |
359 | - | |
360 | - /// <summary> | |
361 | - /// デバッグコマンドウィンドウを非表示にする。 | |
362 | - /// </summary> | |
363 | - public void Hide() | |
364 | - { | |
365 | - if (state == State.Opened) | |
366 | - { | |
367 | - stateTransition = 1.0f; | |
368 | - state = State.Closing; | |
369 | - } | |
370 | - } | |
371 | - | |
372 | - public override void Update(GameTime gameTime) | |
373 | - { | |
374 | - KeyboardState keyState = Keyboard.GetState(); | |
375 | - | |
376 | - float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; | |
377 | - const float OpenSpeed = 8.0f; | |
378 | - const float CloseSpeed = 8.0f; | |
379 | - | |
380 | - switch (state) | |
381 | - { | |
382 | - case State.Closed: | |
383 | - if (keyState.IsKeyDown(Keys.Tab)) | |
384 | - Show(); | |
385 | - break; | |
386 | - case State.Opening: | |
387 | - stateTransition += dt * OpenSpeed; | |
388 | - if (stateTransition > 1.0f) | |
389 | - { | |
390 | - stateTransition = 1.0f; | |
391 | - state = State.Opened; | |
392 | - } | |
393 | - break; | |
394 | - case State.Opened: | |
395 | - ProcessKeyInputs(dt); | |
396 | - break; | |
397 | - case State.Closing: | |
398 | - stateTransition -= dt * CloseSpeed; | |
399 | - if (stateTransition < 0.0f) | |
400 | - { | |
401 | - stateTransition = 0.0f; | |
402 | - state = State.Closed; | |
403 | - } | |
404 | - break; | |
405 | - } | |
406 | - | |
407 | - prevKeyState = keyState; | |
408 | - | |
409 | - base.Update(gameTime); | |
410 | - } | |
411 | - | |
412 | - /// <summary> | |
413 | - /// キー入力処理 | |
414 | - /// </summary> | |
415 | - /// <param name="dt"></param> | |
416 | - public void ProcessKeyInputs(float dt) | |
417 | - { | |
418 | - KeyboardState keyState = Keyboard.GetState(); | |
419 | - Keys[] keys = keyState.GetPressedKeys(); | |
420 | - | |
421 | - bool shift = keyState.IsKeyDown(Keys.LeftShift) || | |
422 | - keyState.IsKeyDown(Keys.RightShift); | |
423 | - | |
424 | - foreach (Keys key in keys) | |
425 | - { | |
426 | - if (!IsKeyPressed(key, dt)) continue; | |
427 | - | |
428 | - char ch; | |
429 | - if (KeyboardUtils.KeyToString(key, shift, out ch)) | |
430 | - { | |
431 | - // 通常文字の入力 | |
432 | - commandLine = commandLine.Insert(cursorIndex, new string(ch, 1)); | |
433 | - cursorIndex++; | |
434 | - } | |
435 | - else | |
436 | - { | |
437 | - switch (key) | |
438 | - { | |
439 | - case Keys.Back: | |
440 | - if (cursorIndex > 0) | |
441 | - commandLine = commandLine.Remove(--cursorIndex, 1); | |
442 | - break; | |
443 | - case Keys.Delete: | |
444 | - if (cursorIndex < commandLine.Length) | |
445 | - commandLine = commandLine.Remove(cursorIndex, 1); | |
446 | - break; | |
447 | - case Keys.Left: | |
448 | - if (cursorIndex > 0) | |
449 | - cursorIndex--; | |
450 | - break; | |
451 | - case Keys.Right: | |
452 | - if (cursorIndex < commandLine.Length) | |
453 | - cursorIndex++; | |
454 | - break; | |
455 | - case Keys.Enter: | |
456 | - // コマンドの実行 | |
457 | - ExecuteCommand(commandLine); | |
458 | - commandLine = string.Empty; | |
459 | - cursorIndex = 0; | |
460 | - break; | |
461 | - case Keys.Up: | |
462 | - // ヒストリ表示 | |
463 | - if (commandHistory.Count > 0) | |
464 | - { | |
465 | - commandHistoryIndex = | |
466 | - Math.Max(0, commandHistoryIndex - 1); | |
467 | - | |
468 | - commandLine = commandHistory[commandHistoryIndex]; | |
469 | - cursorIndex = commandLine.Length; | |
470 | - } | |
471 | - break; | |
472 | - case Keys.Down: | |
473 | - // ヒストリ表示 | |
474 | - if (commandHistory.Count > 0) | |
475 | - { | |
476 | - commandHistoryIndex = Math.Min(commandHistory.Count - 1, | |
477 | - commandHistoryIndex + 1); | |
478 | - commandLine = commandHistory[commandHistoryIndex]; | |
479 | - cursorIndex = commandLine.Length; | |
480 | - } | |
481 | - break; | |
482 | - case Keys.Tab: | |
483 | - Hide(); | |
484 | - break; | |
485 | - } | |
486 | - } | |
487 | - } | |
488 | - | |
489 | - } | |
490 | - | |
491 | - /// <summary> | |
492 | - /// キーリピート付きキー押下チェック | |
493 | - /// </summary> | |
494 | - /// <param name="key"></param> | |
495 | - /// <returns></returns> | |
496 | - bool IsKeyPressed(Keys key, float dt) | |
497 | - { | |
498 | - // 前フレームでキーが押されていなければ、キーが押されていると判定 | |
499 | - if (prevKeyState.IsKeyUp(key)) | |
500 | - { | |
501 | - keyRepeatTimer = keyRepeatStartDuration; | |
502 | - pressedKey = key; | |
503 | - return true; | |
504 | - } | |
505 | - | |
506 | - // 前フレームでキーが押されていた場合はリピート処理 | |
507 | - if (key == pressedKey) | |
508 | - { | |
509 | - keyRepeatTimer -= dt; | |
510 | - if (keyRepeatTimer <= 0.0f) | |
511 | - { | |
512 | - keyRepeatTimer += keyRepeatDuration; | |
513 | - return true; | |
514 | - } | |
515 | - } | |
516 | - | |
517 | - return false; | |
518 | - } | |
519 | - | |
520 | - public override void Draw(GameTime gameTime) | |
521 | - { | |
522 | - // コマンドウィンドウが完全に閉じている場合は描画処理をしない | |
523 | - if (state == State.Closed) | |
524 | - return; | |
525 | - | |
526 | - SpriteFont font = debugManager.DebugFont; | |
527 | - SpriteBatch spriteBatch = debugManager.SpriteBatch; | |
528 | - Texture2D whiteTexture = debugManager.WhiteTexture; | |
529 | - | |
530 | - // コマンドウィンドウのサイズ計算と描画 | |
531 | - float w = GraphicsDevice.Viewport.Width; | |
532 | - float h = GraphicsDevice.Viewport.Height; | |
533 | - float topMargin = h * 0.1f; | |
534 | - float leftMargin = w * 0.1f; | |
535 | - | |
536 | - Rectangle rect = new Rectangle(); | |
537 | - rect.X = (int)leftMargin; | |
538 | - rect.Y = (int)topMargin; | |
539 | - rect.Width = (int)(w * 0.8f); | |
540 | - rect.Height = (int)(MaxLineCount * font.LineSpacing); | |
541 | - | |
542 | - Matrix mtx = Matrix.CreateTranslation( | |
543 | - new Vector3(0, -rect.Height * (1.0f - stateTransition), 0)); | |
544 | - | |
545 | - spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, mtx); | |
546 | - | |
547 | - spriteBatch.Draw(whiteTexture, rect, new Color(0, 0, 0, 200)); | |
548 | - | |
549 | - // 文字列の描画 | |
550 | - Vector2 pos = new Vector2(leftMargin, topMargin); | |
551 | - foreach (string line in lines) | |
552 | - { | |
553 | - spriteBatch.DrawString(font, line, pos, Color.White); | |
554 | - pos.Y += font.LineSpacing; | |
555 | - } | |
556 | - | |
557 | - // プロンプト文字列の描画 | |
558 | - string leftPart = Prompt + commandLine.Substring(0, cursorIndex); | |
559 | - Vector2 cursorPos = pos + font.MeasureString(leftPart); | |
560 | - cursorPos.Y = pos.Y; | |
561 | - | |
562 | - spriteBatch.DrawString(font, | |
563 | - String.Format("{0}{1}", Prompt, commandLine), pos, Color.White); | |
564 | - spriteBatch.DrawString(font, Cursor, cursorPos, Color.White); | |
565 | - | |
566 | - spriteBatch.End(); | |
567 | - } | |
568 | - | |
569 | - #endregion | |
570 | - | |
571 | - } | |
572 | -} |
@@ -1,201 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | -using System.Globalization; | |
5 | -using System.Text; | |
6 | - | |
7 | -#endregion | |
8 | - | |
9 | -namespace DebugSample | |
10 | -{ | |
11 | - /// <summary> | |
12 | - /// StringBuilder拡張メソッド用オプション | |
13 | - /// </summary> | |
14 | - [Flags] | |
15 | - public enum AppendNumberOptions | |
16 | - { | |
17 | - // 通常フォーマット | |
18 | - None = 0, | |
19 | - | |
20 | - // 正の値の時にも"+"をつける | |
21 | - PositiveSign = 1, | |
22 | - | |
23 | - // 3桁毎に","を表示する | |
24 | - NumberGroup = 2, | |
25 | - } | |
26 | - | |
27 | - /// <summary> | |
28 | - /// StringBuilder用の拡張メソッド宣言用静的クラス | |
29 | - /// </summary> | |
30 | - /// <remarks> | |
31 | - /// XNA GS 3.0からSpriteFont.DrawStringにはStringBuilderを | |
32 | - /// 指定できるようになり、不必要なメモリ確保が少なくなった。 | |
33 | - /// | |
34 | - /// しかし、数字を表示するには2つの問題がある。 | |
35 | - /// ひとつはStringBuilder.AppendFormatメソッドを使うと | |
36 | - /// ボクシングが発生してしまうこと。 | |
37 | - /// もうひとつは、StringBuilder.Appendで整数値やfloat値を指定すると | |
38 | - /// 内部でメモリ確保が発生してしまうこと。 | |
39 | - /// | |
40 | - /// メモリ確保やボクシングが発生させずに数字表示に最低限必要なフォーマット機能を | |
41 | - /// このクラスは提供する。 | |
42 | - /// | |
43 | - /// StringBuilderの拡張メソッドとして宣言しているので以下のようにして使用する | |
44 | - /// | |
45 | - /// stringBuilder.AppendNumber(12345); | |
46 | - /// | |
47 | - /// </remarks> | |
48 | - public static class StringBuilderExtensions | |
49 | - { | |
50 | - #region フィールド | |
51 | - | |
52 | - /// <summary> | |
53 | - /// NumberFormatクラスのNumberGroupSizesのキッシュ | |
54 | - /// </summary> | |
55 | - static int[] numberGroupSizes = | |
56 | - CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes; | |
57 | - | |
58 | - /// <summary> | |
59 | - /// 文字列変換用のバッファ | |
60 | - /// </summary> | |
61 | - static char[] numberString = new char[32]; | |
62 | - | |
63 | - #endregion | |
64 | - | |
65 | - /// <summary> | |
66 | - /// 整数を文字列に変換してStringBuilderに追加する | |
67 | - /// </summary> | |
68 | - public static void AppendNumber(this StringBuilder builder, int number) | |
69 | - { | |
70 | - AppendNumbernternal(builder, number, 0, AppendNumberOptions.None); | |
71 | - } | |
72 | - | |
73 | - /// <summary> | |
74 | - /// 整数を文字列に変換してStringBuilderに追加する | |
75 | - /// </summary> | |
76 | - /// <param name="number"></param> | |
77 | - /// <param name="options">フォーマット指定オプション</param> | |
78 | - public static void AppendNumber(this StringBuilder builder, int number, | |
79 | - AppendNumberOptions options) | |
80 | - { | |
81 | - AppendNumbernternal(builder, number, 0, options); | |
82 | - } | |
83 | - | |
84 | - /// <summary> | |
85 | - /// float値を文字列に変換してStringBuilderに追加する | |
86 | - /// </summary> | |
87 | - /// <param name="number">変換する数字</param> | |
88 | - /// <remarks>小数点以下二桁まで表示する</remarks> | |
89 | - public static void AppendNumber(this StringBuilder builder, float number) | |
90 | - { | |
91 | - AppendNumber(builder, number, 2, AppendNumberOptions.None); | |
92 | - } | |
93 | - | |
94 | - /// <summary> | |
95 | - /// float値を文字列に変換してStringBuilderに追加する | |
96 | - /// </summary> | |
97 | - /// <param name="number">変換する数字</param> | |
98 | - /// <param name="options">フォーマット指定オプション</param> | |
99 | - /// <remarks>小数点以下二桁まで表示する</remarks> | |
100 | - public static void AppendNumber(this StringBuilder builder, float number, | |
101 | - AppendNumberOptions options) | |
102 | - { | |
103 | - AppendNumber(builder, number, 2, options); | |
104 | - } | |
105 | - | |
106 | - /// <summary> | |
107 | - /// float値を文字列に変換してStringBuilderに追加する | |
108 | - /// </summary> | |
109 | - /// <param name="number">変換する数字</param> | |
110 | - /// <param name="decimalCount">表示する小数点以下の桁数</param> | |
111 | - /// <param name="options">フォーマット指定オプション</param> | |
112 | - public static void AppendNumber(this StringBuilder builder, float number, | |
113 | - int decimalCount, AppendNumberOptions options) | |
114 | - { | |
115 | - // NaN, Infinity等の数値の特殊ケース判定 | |
116 | - if (float.IsNaN(number)) | |
117 | - { | |
118 | - builder.Append("NaN"); | |
119 | - } | |
120 | - else if (float.IsNegativeInfinity(number)) | |
121 | - { | |
122 | - builder.Append("-Infinity"); | |
123 | - } | |
124 | - else if (float.IsPositiveInfinity(number)) | |
125 | - { | |
126 | - builder.Append("+Infinity"); | |
127 | - } | |
128 | - else | |
129 | - { | |
130 | - int intNumber = | |
131 | - (int)(number * (float)Math.Pow(10, decimalCount) + 0.5f); | |
132 | - | |
133 | - AppendNumbernternal(builder, intNumber, decimalCount, options); | |
134 | - } | |
135 | - } | |
136 | - | |
137 | - | |
138 | - static void AppendNumbernternal(StringBuilder builder, int number, | |
139 | - int decimalCount, AppendNumberOptions options) | |
140 | - { | |
141 | - // 変換に必要な変数の初期化 | |
142 | - NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat; | |
143 | - | |
144 | - int idx = numberString.Length; | |
145 | - int decimalPos = idx - decimalCount; | |
146 | - | |
147 | - if (decimalPos == idx) | |
148 | - decimalPos = idx + 1; | |
149 | - | |
150 | - int numberGroupIdx = 0; | |
151 | - int numberGroupCount = numberGroupSizes[numberGroupIdx] + decimalCount; | |
152 | - | |
153 | - bool showNumberGroup = (options & AppendNumberOptions.NumberGroup) != 0; | |
154 | - bool showPositiveSign = (options & AppendNumberOptions.PositiveSign) != 0; | |
155 | - | |
156 | - bool isNegative = number < 0; | |
157 | - number = Math.Abs(number); | |
158 | - | |
159 | - // 最小桁から各桁を文字に変換する | |
160 | - do | |
161 | - { | |
162 | - // 小数点の区切り文字(日本では".")の追加 | |
163 | - if (idx == decimalPos) | |
164 | - { | |
165 | - numberString[--idx] = nfi.NumberDecimalSeparator[0]; | |
166 | - } | |
167 | - | |
168 | - // 桁グループ区切り文字(日本では",")の追加 | |
169 | - if (--numberGroupCount < 0 && showNumberGroup) | |
170 | - { | |
171 | - numberString[--idx] = nfi.NumberGroupSeparator[0]; | |
172 | - | |
173 | - if (numberGroupIdx < numberGroupSizes.Length - 1) | |
174 | - numberGroupIdx++; | |
175 | - | |
176 | - numberGroupCount = numberGroupSizes[numberGroupIdx] - 1; | |
177 | - } | |
178 | - | |
179 | - // 現在の桁を文字に変換してバッファに追加 | |
180 | - numberString[--idx] = (char)('0' + (number % 10)); | |
181 | - number /= 10; | |
182 | - | |
183 | - } while (number > 0 || decimalPos <= idx); | |
184 | - | |
185 | - | |
186 | - // 符号文字を必要なら追加する | |
187 | - if (isNegative) | |
188 | - { | |
189 | - numberString[--idx] = nfi.NegativeSign[0]; | |
190 | - } | |
191 | - else if (showPositiveSign) | |
192 | - { | |
193 | - numberString[--idx] = nfi.PositiveSign[0]; | |
194 | - } | |
195 | - | |
196 | - // 変換結果をStringBuilderに追加する | |
197 | - builder.Append(numberString, idx, numberString.Length - idx); | |
198 | - } | |
199 | - | |
200 | - } | |
201 | -} | |
\ No newline at end of file |
@@ -1,235 +0,0 @@ | ||
1 | -#region Using ステートメント | |
2 | - | |
3 | -using System; | |
4 | - | |
5 | -using Microsoft.Xna.Framework; | |
6 | -using Microsoft.Xna.Framework.Graphics; | |
7 | - | |
8 | -#endregion | |
9 | - | |
10 | -namespace DebugSample | |
11 | -{ | |
12 | - /// <summary> | |
13 | - /// レイアウトのアライメント位置 | |
14 | - /// </summary> | |
15 | - [Flags] | |
16 | - public enum Alignment | |
17 | - { | |
18 | - // レイアウトなし | |
19 | - None = 0, | |
20 | - | |
21 | - // 水平方向のレイアウト群 | |
22 | - // 左側 | |
23 | - Left = 1, | |
24 | - // 右側 | |
25 | - Right = 2, | |
26 | - // 水平方向中央 | |
27 | - HorizontalCenter = 4, | |
28 | - | |
29 | - // 垂直方向のレイアウト群 | |
30 | - // 上側 | |
31 | - Top = 8, | |
32 | - // 下側 | |
33 | - Bottom = 16, | |
34 | - // 垂直方向中央 | |
35 | - VerticalCenter = 32, | |
36 | - | |
37 | - // 左上 | |
38 | - TopLeft = Top | Left, | |
39 | - // 右上 | |
40 | - TopRight = Top | Right, | |
41 | - // 上側、中央 | |
42 | - TopCenter = Top | HorizontalCenter, | |
43 | - | |
44 | - // 左下 | |
45 | - BottomLeft = Bottom | Left, | |
46 | - // 右下 | |
47 | - BottomRight = Bottom | Right, | |
48 | - // 下側、中央 | |
49 | - BottomCenter = Bottom | HorizontalCenter, | |
50 | - | |
51 | - // 中央、左側 | |
52 | - CenterLeft = VerticalCenter | Left, | |
53 | - // 中央、右側 | |
54 | - CenterRight = VerticalCenter | Right, | |
55 | - // 中央 | |
56 | - Center = VerticalCenter | HorizontalCenter | |
57 | - } | |
58 | - | |
59 | - /// <summary> | |
60 | - /// セーフエリアに対応したレイアウト構造体 | |
61 | - /// </summary> | |
62 | - /// <remarks> | |
63 | - /// Windows、Xbox 360の両プラットフォームで動作するゲームは様々な解像度、 | |
64 | - /// アスペクト比に対応する必要がある。加えてXbox 360で動作するゲームは | |
65 | - /// タイトルセーフエリアにも対応する必要がある。 | |
66 | - /// | |
67 | - /// この構造体では、レイアウト対象領域(クライアントエリア)と、 | |
68 | - /// セーフエリア領域を保持しAligmentと水平垂直のマージン値から | |
69 | - /// 指定した矩形を配置する。 | |
70 | - /// 配置後の矩形がセーフエリア外にある場合はセーフエリア内に再配置される。 | |
71 | - /// | |
72 | - /// マージンはクライアントエリアの割合で示す。 | |
73 | - /// | |
74 | - /// 使用例: | |
75 | - /// | |
76 | - /// Place( region, 0.1f, 0.2f, Aligment.TopLeft ); | |
77 | - /// | |
78 | - /// クライアントエリアの左端から10%、上端から20%の部分にregionを配置する | |
79 | - /// | |
80 | - /// | |
81 | - /// Place( region, 0.3f, 0.4f, Aligment.BottomRight ); | |
82 | - /// | |
83 | - /// クライアントエリアの右端から30%、下端から40%の部分にregionを配置する | |
84 | - /// | |
85 | - /// | |
86 | - /// クライアントエリアとセーフエリアを別々に指定できるので、 | |
87 | - /// 画面分割時にも、セーフエリアにタイトルセーフエリアを指定し、 | |
88 | - /// クライアントエリアに分割した画面の領域を指定することで、レイアウトは | |
89 | - /// 画面分割領域ベースで行いつつも、タイトルセーフエリア内に正しく配置することが | |
90 | - /// できる。 | |
91 | - /// | |
92 | - /// | |
93 | - /// セーフエリアについては以下のURLを参照 | |
94 | - /// http://blogs.msdn.com/ito/archive/2008/11/21/safearea-sample.aspx | |
95 | - /// /// </remarks> | |
96 | - public struct Layout | |
97 | - { | |
98 | - #region フィールド | |
99 | - | |
100 | - /// <summary> | |
101 | - /// クライアントエリアの取得と設定 | |
102 | - /// </summary> | |
103 | - public Rectangle ClientArea; | |
104 | - | |
105 | - /// <summary> | |
106 | - /// セーフエリアの取得と設定 | |
107 | - /// </summary> | |
108 | - public Rectangle SafeArea; | |
109 | - | |
110 | - #endregion | |
111 | - | |
112 | - #region 初期化 | |
113 | - | |
114 | - /// <summary> | |
115 | - /// クライアントエリアとセーフエリアを別々に指定して初期化する | |
116 | - /// </summary> | |
117 | - /// <param name="client">クライアントエリア</param> | |
118 | - /// <param name="safeArea">セーフエリア</param> | |
119 | - public Layout(Rectangle clientArea, Rectangle safeArea) | |
120 | - { | |
121 | - ClientArea = clientArea; | |
122 | - SafeArea = safeArea; | |
123 | - } | |
124 | - | |
125 | - /// <summary> | |
126 | - /// クライアントエリアのみを指定して初期化する | |
127 | - /// セーフエリアはクライアントエリアと同じサイズになる | |
128 | - /// </summary> | |
129 | - /// <param name="client">クライアントエリア</param> | |
130 | - public Layout(Rectangle clientArea) | |
131 | - : this(clientArea, clientArea) | |
132 | - { | |
133 | - } | |
134 | - | |
135 | - /// <summary> | |
136 | - /// Viewportを指定して初期化する | |
137 | - /// セーフエリアはViewport.TitleSafeAreaになる | |
138 | - /// </summary> | |
139 | - /// <param name="viewport">ビューポート</param> | |
140 | - public Layout(Viewport viewport) | |
141 | - { | |
142 | - ClientArea = new Rectangle((int)viewport.X, (int)viewport.Y, | |
143 | - (int)viewport.Width, (int)viewport.Height); | |
144 | - SafeArea = viewport.TitleSafeArea; | |
145 | - } | |
146 | - | |
147 | - #endregion | |
148 | - | |
149 | - /// <summary> | |
150 | - /// 指定したサイズ矩形のレイアウト | |
151 | - /// </summary> | |
152 | - /// <param name="region">配置する矩形</param> | |
153 | - /// <param name="horizontalMargin">垂直方向のマージン</param> | |
154 | - /// <param name="verticalMargine">水平方向のマージン</param> | |
155 | - /// <param name="alignment">アライメント</param> | |
156 | - /// <returns>配置された矩形</returns> | |
157 | - public Vector2 Place(Vector2 size, float horizontalMargin, | |
158 | - float verticalMargine, Alignment alignment) | |
159 | - { | |
160 | - Rectangle rc = new Rectangle(0, 0, (int)size.X, (int)size.Y); | |
161 | - rc = Place(rc, horizontalMargin, verticalMargine, alignment); | |
162 | - return new Vector2(rc.X, rc.Y); | |
163 | - } | |
164 | - | |
165 | - /// <summary> | |
166 | - /// 指定した矩形のレイアウト | |
167 | - /// </summary> | |
168 | - /// <param name="region">配置する矩形</param> | |
169 | - /// <param name="horizontalMargin">垂直方向のマージン</param> | |
170 | - /// <param name="verticalMargine">水平方向のマージン</param> | |
171 | - /// <param name="alignment">アライメント</param> | |
172 | - /// <returns>配置された矩形</returns> | |
173 | - public Rectangle Place(Rectangle region, float horizontalMargin, | |
174 | - float verticalMargine, Alignment alignment) | |
175 | - { | |
176 | - // 水平方向のレイアウト | |
177 | - if ((alignment & Alignment.Left) != 0) | |
178 | - { | |
179 | - region.X = ClientArea.X + (int)(ClientArea.Width * horizontalMargin); | |
180 | - } | |
181 | - else if ((alignment & Alignment.Right) != 0) | |
182 | - { | |
183 | - region.X = ClientArea.X + | |
184 | - (int)(ClientArea.Width * (1.0f - horizontalMargin)) - | |
185 | - region.Width; | |
186 | - } | |
187 | - else if ((alignment & Alignment.HorizontalCenter) != 0) | |
188 | - { | |
189 | - region.X = ClientArea.X + (ClientArea.Width - region.Width) / 2 + | |
190 | - (int)(horizontalMargin * ClientArea.Width); | |
191 | - } | |
192 | - else | |
193 | - { | |
194 | - // レイアウトなし | |
195 | - } | |
196 | - | |
197 | - // 垂直方向のレイアウト | |
198 | - if ((alignment & Alignment.Top) != 0) | |
199 | - { | |
200 | - region.Y = ClientArea.Y + (int)(ClientArea.Height * verticalMargine); | |
201 | - } | |
202 | - else if ((alignment & Alignment.Bottom) != 0) | |
203 | - { | |
204 | - region.Y = ClientArea.Y + | |
205 | - (int)(ClientArea.Height * (1.0f - verticalMargine)) - | |
206 | - region.Height; | |
207 | - } | |
208 | - else if ((alignment & Alignment.VerticalCenter) != 0) | |
209 | - { | |
210 | - region.Y = ClientArea.Y + (ClientArea.Height - region.Height) / 2 + | |
211 | - (int)(verticalMargine * ClientArea.Height); | |
212 | - } | |
213 | - else | |
214 | - { | |
215 | - // レイアウトなし | |
216 | - } | |
217 | - | |
218 | - // レイアウトした領域をセーフエリア内にあるか確かめる | |
219 | - if (region.Left < SafeArea.Left) | |
220 | - region.X = SafeArea.Left; | |
221 | - | |
222 | - if (region.Right > SafeArea.Right) | |
223 | - region.X = SafeArea.Right - region.Width; | |
224 | - | |
225 | - if (region.Top < SafeArea.Top) | |
226 | - region.Y = SafeArea.Top; | |
227 | - | |
228 | - if (region.Bottom > SafeArea.Bottom) | |
229 | - region.Y = SafeArea.Bottom - region.Height; | |
230 | - | |
231 | - return region; | |
232 | - } | |
233 | - | |
234 | - } | |
235 | -} | |
\ No newline at end of file |