这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十一篇。在这篇文章中,介绍 Common/Env.cs 源程序文件。这个源程序文件中包含表示“工作环境”的密封类 Env 。也就是说,主程序中重要的变量都封装在这个类中,作为整个程序的“工作环境”。她还起着桥梁作用,其中两个字段:
正是我们以前介绍过的管理数据文件的密封类 DataFile 和管理配置文件的密封类 ConfigFile 的实例。密封类 Env 中的不少属性和方法是通过这两个字段调用其各自的属性和方法。
下面对密封类 Env 中的一些方法作点说明:
GetClientSize 方法用来计算当使用标准箱子尺寸时主窗体客户区的尺寸。该方法仅当程序运行在计算机上时才会被调用,使主窗体的尺寸根据当前关的尺寸自动调整。程序运行在智能手机时是不会被调用的,因为在智能手机上本程序并不改变主窗体的尺寸。
SetBoxInfo 方法的作用是根据客户区尺寸计算箱子的尺寸,并相应设定要显示的图形单元。图形单元共有 24x24、20x20、16x16 和 12x12 四种尺寸,如下所示:



如果使用 12x12 的图形单元还不能在客户区完整显示地图的话,可能这一关的游戏就无法玩了。
Draw 方法用来更新主窗体客户区,也就是在主窗体客户区画出本关的地图,并根据玩家的动作随时更新地图。
Design 方法实现在设计模式下,当鼠标点击时要采取的动作。
StepIt 方法实现工人往指定方向前进一步(可能推着箱子)。
Back 方法实现工人后退一步(可能连带箱子一起后退)。
GetMoveInfo 方法寻找一条将工人移动到鼠标点击的位置的路线。她调用我们以前介绍过的静态类 FindPath 的 Seek 方法来寻找最短路线。
GetPushInfo 方法给出将箱子推动到鼠标点击的位置所需的信息。
到此为止,Common 目录下所有源程序文件都介绍完了,这些源程序文件中包含的类是实现整个程序功能的基础。在随后的文章中将介绍 Windows 目录下的源程序文件,她们包含的类实现整个程序的用户界面。
下面就是密封类 Env 的源程序代码,虽然稍微长了一点,但是里面的注释比较详细,应该不难理解。
上一篇:使用 C# 开发智能手机软件:推箱子(十)
下一篇:使用 C# 开发智能手机软件:推箱子(十二)
返回目录
正是我们以前介绍过的管理数据文件的密封类 DataFile 和管理配置文件的密封类 ConfigFile 的实例。密封类 Env 中的不少属性和方法是通过这两个字段调用其各自的属性和方法。
下面对密封类 Env 中的一些方法作点说明:
GetClientSize 方法用来计算当使用标准箱子尺寸时主窗体客户区的尺寸。该方法仅当程序运行在计算机上时才会被调用,使主窗体的尺寸根据当前关的尺寸自动调整。程序运行在智能手机时是不会被调用的,因为在智能手机上本程序并不改变主窗体的尺寸。
SetBoxInfo 方法的作用是根据客户区尺寸计算箱子的尺寸,并相应设定要显示的图形单元。图形单元共有 24x24、20x20、16x16 和 12x12 四种尺寸,如下所示:




如果使用 12x12 的图形单元还不能在客户区完整显示地图的话,可能这一关的游戏就无法玩了。
Draw 方法用来更新主窗体客户区,也就是在主窗体客户区画出本关的地图,并根据玩家的动作随时更新地图。
Design 方法实现在设计模式下,当鼠标点击时要采取的动作。
StepIt 方法实现工人往指定方向前进一步(可能推着箱子)。
Back 方法实现工人后退一步(可能连带箱子一起后退)。
GetMoveInfo 方法寻找一条将工人移动到鼠标点击的位置的路线。她调用我们以前介绍过的静态类 FindPath 的 Seek 方法来寻找最短路线。
GetPushInfo 方法给出将箱子推动到鼠标点击的位置所需的信息。
到此为止,Common 目录下所有源程序文件都介绍完了,这些源程序文件中包含的类是实现整个程序功能的基础。在随后的文章中将介绍 Windows 目录下的源程序文件,她们包含的类实现整个程序的用户界面。
下面就是密封类 Env 的源程序代码,虽然稍微长了一点,但是里面的注释比较详细,应该不难理解。
1
using System;
2
using System.Drawing;
3
using System.Collections.Generic;
4
5
namespace Skyiv.Ben.PushBox.Common
6
{
7
/// <summary>
8
/// 工作环境
9
/// </summary>
10
sealed class Env : IDisposable
11
{
12
DataFile db; // 数据文件
13
ConfigFile cfg; // 配置文件
14
string errorMsg; // 错误信息
15
string debugMsg; // 调试信息
16
bool isReplay; // 是否正在回放
17
Action active; // 模式: 正常 新建 编辑 删除
18
byte pen; // 设计时的笔
19
Bitmap img; // 图形单元, 横向被均匀分为八份
20
Stack<Step> stack; // 历史路线, 用于后退功能
21
Size clientSize; // 工作区域尺寸(以像素为单位)
22
Size boxSize; // 图形元素尺寸(以像素为单位)
23
Point toPixel; // 将要到达的位置(以像素为单位)
24
Point worker; // 当前工人位置(以单元格为单位)
25
int pushSteps; // 推动着箱子走的步数
26
int levelOem; // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场
27
28
public string ErrorMsg { get { return errorMsg; } }
29
public string DebugMsg { get { return debugMsg; } }
30
public string[] Groups { get { return cfg.Groups; } }
31
public int Group { get { return cfg.Group; } set { cfg.Group = value; } }
32
public int Level { get { return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }
33
public int LeveLOem { get { return levelOem; } }
34
public int MaxLevel { get { return db.MaxLevel; } }
35
public string Steps { get { return cfg.Steps; } }
36
public Size LevelSize { get { return db.LevelSize; } }
37
public Size ClientSize { set { clientSize = value; } }
38
public Point ToPixel { set { toPixel = value; } }
39
public int MaxLevelSize { get { return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }
40
public int StepDelay { get { return cfg.StepDelay; } set { cfg.StepDelay = value; } }
41
public int ReplayDelay { get { return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }
42
public Action Active { get { return active; } set { active = value; } }
43
public byte Pen { get { return pen; } set { pen = value; } }
44
public bool HasError { get { return !string.IsNullOrEmpty(errorMsg); } }
45
public bool HasWorker { get { return db.HasWorker; } }
46
public bool CanUndo { get { return stack.Count != 0; } }
47
public bool CanReplay { get { return db.IsFinished && !CanUndo; } }
48
public bool IsSave { get { return cfg.IsSave; } set { cfg.IsSave = value; } }
49
public bool IsFinish { get { return db.Tasks == db.Boths; } }
50
public bool IsReplay { get { return isReplay; } set { isReplay = value; } }
51
public bool IsDesign { get { return active != Action.None; } }
52
53
public Env()
54
{
55
stack = new Stack<Step>();
56
cfg = new ConfigFile();
57
db = new DataFile();
58
Init();
59
}
60
61
/// <summary>
62
/// 状态栏信息
63
/// </summary>
64
public string StatusMessage
65
{
66
get
67
{
68
return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(
69
"{0} {1}/{2} {3} {4} {5} [{6}] {7}",
70
(active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',
71
Level + 1, MaxLevel, Pub.ToString(LevelSize),
72
IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),
73
IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),
74
IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ?
75
string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,
76
db.GroupName);
77
}
78
}
79
80
public void Dispose()
81
{
82
db.Dispose();
83
}
84
85
public void Init()
86
{
87
active = Action.None;
88
pen = Block.Land;
89
stack.Clear();
90
SetExceptionMessage(null);
91
}
92
93
void SetExceptionMessage(Exception ex)
94
{
95
errorMsg = Pub.GetMessage(ex, false);
96
debugMsg = Pub.GetMessage(ex, true);
97
}
98
99
/// <summary>
100
/// 计算当使用标准箱子尺寸时主窗体客户区的尺寸
101
/// </summary>
102
/// <param name="statusBarHeight">状态条的高度</param>
103
/// <returns>客户区的尺寸</returns>
104
public Size GetClientSize(int statusBarHeight)
105
{
106
int width = (Properties.Resources.PushBox24.Width / 8) * LevelSize.Width;
107
int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;
108
if (width < 240) width = 240;
109
if (height < 48) height = 48;
110
if (width > 1008) width = 1008;
111
if (height > 672) height = 672;
112
return new Size(width, height);
113
}
114
115
/// <summary>
116
/// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元
117
/// </summary>
118
public void SetBoxInfo()
119
{
120
if (HasError) return;
121
if (LevelSize.IsEmpty) return;
122
int rX = clientSize.Width / LevelSize.Width;
123
int rY = clientSize.Height / LevelSize.Height;
124
int r = Math.Min(rX, rY);
125
if (r >= 24) img = Properties.Resources.PushBox24;
126
else if (r >= 20) img = Properties.Resources.PushBox20;
127
else if (r >= 16) img = Properties.Resources.PushBox16;
128
else img = Properties.Resources.PushBox12;
129
boxSize = new Size(img.Height, img.Width / 8);
130
}
131
132
/// <summary>
133
/// 装入配置文件
134
/// </summary>
135
public void LoadConfig()
136
{
137
if (HasError) return;
138
try
139
{
140
cfg.LoadConfig();
141
}
142
catch (Exception ex)
143
{
144
SetExceptionMessage(ex);
145
}
146
}
147
148
/// <summary>
149
/// 保存组信息到配置文件
150
/// </summary>
151
/// <param name="groups">组信息</param>
152
public void SaveConfig(string[] groups)
153
{
154
if (HasError) return;
155
try
156
{
157
cfg.SaveConfig(groups);
158
}
159
catch (Exception ex)
160
{
161
SetExceptionMessage(ex);
162
}
163
}
164
165
/// <summary>
166
/// 保存当前选项及当前走法到配置文件
167
/// </summary>
168
public void SaveConfig()
169
{
170
if (HasError) return;
171
try
172
{
173
cfg.SaveConfig(stack.ToArray());
174
}
175
catch (Exception ex)
176
{
177
SetExceptionMessage(ex);
178
}
179
}
180
181
/// <summary>
182
/// 装入当前组信息
183
/// </summary>
184
public void LoadGroup()
185
{
186
if (HasError) return;
187
try
188
{
189
db.LoadGroup(Groups[Group]);
190
}
191
catch (Exception ex)
192
{
193
SetExceptionMessage(ex);
194
}
195
}
196
197
/// <summary>
198
/// 装入当前关信息
199
/// </summary>
200
public void LoadLevel()
201
{
202
active = Action.None;
203
if (HasError) return;
204
try
205
{
206
db.LoadLevel(Level);
207
worker = db.Worker;
208
stack.Clear();
209
pushSteps = 0;
210
isReplay = false;
211
}
212
catch (Exception ex)
213
{
214
SetExceptionMessage(ex);
215
}
216
}
217
218
/// <summary>
219
/// 新建一关
220
/// </summary>
221
/// <param name="isCopy">是否复制当前关</param>
222
/// <param name="size">新建关的尺寸</param>
223
public void NewLevel(bool isCopy, Size size)
224
{
225
if (HasError) return;
226
try
227
{
228
levelOem = Level;
229
Level = MaxLevel;
230
db.NewLevel(isCopy, size);
231
}
232
catch (Exception ex)
233
{
234
SetExceptionMessage(ex);
235
}
236
}
237
238
/// <summary>
239
/// 给出通关步骤
240
/// </summary>
241
/// <returns>通关步骤</returns>
242
public string GetSteps()
243
{
244
string steps = "";
245
if (!HasError)
246
{
247
try
248
{
249
steps = db.GetSteps(Level);
250
}
251
catch (Exception ex)
252
{
253
SetExceptionMessage(ex);
254
}
255
}
256
return steps;
257
}
258
259
/// <summary>
260
/// 记录通关步骤
261
/// </summary>
262
public void Record()
263
{
264
if (HasError) return;
265
try
266
{
267
db.SaveLevel(Level, stack.ToArray(), pushSteps);
268
}
269
catch (Exception ex)
270
{
271
SetExceptionMessage(ex);
272
}
273
}
274
275
/// <summary>
276
/// 保存设计数据
277
/// </summary>
278
public void SaveDesign()
279
{
280
if (HasError) return;
281
try
282
{
283
db.SaveDesign(active == Action.Create, Level);
284
}
285
catch (Exception ex)
286
{
287
SetExceptionMessage(ex);
288
}
289
}
290
291
/// <summary>
292
/// 删除最后一关
293
/// </summary>
294
public void DeleteLastLevel()
295
{
296
if (HasError) return;
297
try
298
{
299
db.DeleteLastLevel(Level);
300
}
301
catch (Exception ex)
302
{
303
SetExceptionMessage(ex);
304
}
305
}
306
307
/// <summary>
308
/// 更新主窗体客户区
309
/// </summary>
310
/// <param name="dc">画布</param>
311
/// <param name="rectangle">要在其中绘画的矩形</param>
312
public void Draw(Graphics dc, Rectangle rectangle)
313
{
314
if (HasError) return;
315
Rectangle box = PixelToBox(rectangle);
316
Rectangle box2 = new Rectangle(box.Left, box.Top, box.Width + 1, box.Height + 1);
317
for (int i = 1; i <= LevelSize.Height; i++)
318
{
319
for (int j = 1; j <= LevelSize.Width; j++)
320
{
321
if (!box2.Contains(j, i)) continue;
322
DrawBox(dc, j, i);
323
}
324
}
325
}
326
327
/// <summary>
328
/// 绘制一个单元格
329
/// </summary>
330
/// <param name="dc">画布</param>
331
/// <param name="x">单元格的横坐标</param>
332
/// <param name="y">单元格的纵坐标</param>
333
void DrawBox(Graphics dc, int x, int y)
334
{
335
DrawBox(dc, db.Map[y, x], (x - 1) * boxSize.Width, (y - 1) * boxSize.Height);
336
}
337
338
/// <summary>
339
/// 绘制一个单元格
340
/// </summary>
341
/// <param name="dc">画布</param>
342
/// <param name="idx">单元格的类型: 地 槽 墙 砖 箱子 工人</param>
343
/// <param name="x">单元格的横坐标</param>
344
/// <param name="y">单元格的纵坐标</param>
345
void DrawBox(Graphics dc, int idx, int x, int y)
346
{
347
dc.DrawImage(img, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel);
348
}
349
350
/// <summary>
351
/// 将单元格换算为像素
352
/// </summary>
353
/// <param name="box">单元格矩形</param>
354
/// <returns>像素矩形</returns>
355
Rectangle BoxToPixel(Rectangle box)
356
{
357
return new Rectangle((box.Left - 1) * boxSize.Width, (box.Top - 1) * boxSize.Height,
358
(box.Width + 1) * boxSize.Width, (box.Height + 1) * boxSize.Height);
359
}
360
361
/// <summary>
362
/// 将像素换算为单元格
363
/// </summary>
364
/// <param name="pixel">像素矩形</param>
365
/// <returns>单元格矩形</returns>
366
Rectangle PixelToBox(Rectangle pixel)
367
{
368
int x0 = pixel.Left / boxSize.Width + 1;
369
int y0 = pixel.Top / boxSize.Height + 1;
370
int x1 = (pixel.Right - 1) / boxSize.Width + 1;
371
int y1 = (pixel.Bottom - 1) / boxSize.Height + 1;
372
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
373
}
374
375
/// <summary>
376
/// 根据指定的对角顶点创建矩形
377
/// </summary>
378
/// <param name="a">顶点</param>
379
/// <param name="b">对角的顶点</param>
380
/// <returns>所需要的矩形</returns>
381
Rectangle GetRectangle(Point a, Point b)
382
{
383
return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
384
}
385
386
/// <summary>
387
/// 设计模式下,当鼠标点击时要采取的动作
388
/// </summary>
389
/// <param name="invalid">输出:要重绘的区域</param>
390
/// <returns>是否发生动作</returns>
391
public bool Design(out Rectangle invalid)
392
{
393
invalid = Rectangle.Empty;
394
Point to;
395
if (!ValidClick(out to)) return false;
396
db.UpdateCounts(to.X, to.Y, false);
397
Block.Update(ref db.Map[to.Y, to.X], pen);
398
db.UpdateCounts(to.X, to.Y, true);
399
if (pen == Block.Man0 && HasWorker) pen = Block.Box0;
400
invalid = BoxToPixel(GetRectangle(to, to));
401
return true;
402
}
403
404
/// <summary>
405
/// 工人往指定方向前进一步(可能推着箱子)
406
/// </summary>
407
/// <param name="dir">前进的方向</param>
408
/// <param name="isStop">“撤销”时是否停留</param>
409
/// <param name="invalid">输出:要重绘的区域</param>
410
/// <returns>是否成功</returns>
411
public bool StepIt(Direction dir, bool isStop, out Rectangle invalid)
412
{
413
invalid = Rectangle.Empty;
414
if (HasError) return false;
415
if (Direction.None == dir) return false;
416
Point p1 = worker; // 工人前进方向一步的位置
417
Point p2 = worker; // 箱子前进方向一步的位置
418
switch (dir)
419
{
420
case Direction.East: p1.X++; p2.X += 2; break;
421
case Direction.South: p1.Y++; p2.Y += 2; break;
422
case Direction.West: p1.X--; p2.X -= 2; break;
423
case Direction.North: p1.Y--; p2.Y -= 2; break;
424
}
425
byte b1 = db.Map[p1.Y, p1.X]; // 工人前进方向一步位置上的东西
426
bool isBox = Block.IsBox(b1); // 是否推着箱子前进
427
if (!isBox && !Block.IsBlank(b1)) return false; // 如果没有推着箱子且前方不是空地则失败
428
if (isBox && !Block.IsBlank(db.Map[p2.Y, p2.X])) return false; // 如果推着箱子且箱子前方不是空地则失败
429
invalid = BoxToPixel(GetRectangle(worker, isBox ? p2 : p1)); // 要重绘的区域
430
stack.Push(new Step(dir, isBox, isStop)); // 记录走法步骤
431
Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
432
Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入前方位置
433
if (isBox)
434
{
435
pushSteps++; // 更新推箱子步数
436
db.Boths += (db.Map[p2.Y, p2.X] - Block.Land) - (b1 - Block.Box0); // 更新已完成任务数
437
Block.BoxOut(ref db.Map[p1.Y, p1.X]); // 箱子离开当前位置
438
Block.BoxIn(ref db.Map[p2.Y, p2.X]); // 箱子进入前方位置
439
}
440
worker = p1; // 更新工人位置
441
return true; // 工人成功前进一步(可能推着条子)
442
}
443
444
/// <summary>
445
/// 工人后退一步(可能连带箱子一起后退)
446
/// </summary>
447
/// <param name="invalid">输出:要重绘的区域</param>
448
/// <returns>是否完成“撤消”</returns>
449
public bool Back(out Rectangle invalid)
450
{
451
invalid = Rectangle.Empty;
452
if (HasError) return true;
453
if (stack.Count == 0) return true;
454
Step step = stack.Pop(); // 当前步骤
455
Point p1 = worker; // 工人后退方向一步的位置
456
Point p2 = worker; // 箱子的当前位置
457
switch (step.Direct)
458
{
459
case Direction.East: p1.X--; p2.X++; break;
460
case Direction.South: p1.Y--; p2.Y++; break;
461
case Direction.West: p1.X++; p2.X--; break;
462
case Direction.North: p1.Y++; p2.Y--; break;
463
}
464
invalid = BoxToPixel(GetRectangle(p1, step.IsBox ? p2 : worker)); // 要重绘的区域
465
Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
466
Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入后退方向一步的位置
467
if (step.IsBox)
468
{
469
Block.BoxOut(ref db.Map[p2.Y, p2.X]); // 箱子离开当前位置
470
Block.BoxIn(ref db.Map[worker.Y, worker.X]); // 箱子进入工人原来的位置
471
db.Boths += (db.Map[worker.Y, worker.X] - Block.Box0) - (db.Map[p2.Y, p2.X] - Block.Land); // 更新已完成任务数
472
pushSteps--; // 更新推箱子步数
473
}
474
worker = p1; // 更新工人位置
475
return step.IsStop; // 是否完成“撤消”
476
}
477
478
/// <summary>
479
/// 寻找一条将工人移动到鼠标点击的位置的路线
480
/// </summary>
481
/// <returns>移动的路线</returns>
482
public Queue<Direction> GetMoveInfo()
483
{
484
Point to;
485
if (!CanTo(out to)) return null;
486
return FindPath.Seek(db.Map, worker, to);
487
}
488
489
/// <summary>
490
/// 给出将箱子推动到鼠标点击的位置所需的信息
491
/// </summary>
492
/// <param name="dir">输出:工人移动的方向</param>
493
/// <returns>工人移动的步数</returns>
494
public int GetPushInfo(out Direction dir)
495
{
496
dir = Direction.None;
497
if (HasError) return 0;
498
Point to; // 目的地
499
if (!CanTo(out to)) return 0; // 无效的目的地
500
if (to.Y != worker.Y && to.X != worker.X) return 0; // 目的地和工人不在同一条直线上
501
int z0 = (to.Y == worker.Y) ? worker.X : worker.Y;
502
int z9 = (to.Y == worker.Y) ? to.X : to.Y;
503
if (to.Y == worker.Y) dir = (z9 > z0) ? Direction.East : Direction.West;
504
else dir = (z9 > z0) ? Direction.South : Direction.North;
505
int i0 = Math.Min(z9, z0);
506
int i9 = Math.Max(z9, z0);
507
int steps = i9 - i0; // 目的地和工人之间的距离
508
int boxs = 0;
509
for (int i = i0 + 1; i < i9; i++)
510
{
511
byte bi = (to.Y == worker.Y) ? db.Map[worker.Y, i] : db.Map[i, worker.X];
512
if (Block.IsBox(bi)) boxs++; // 计算工人和目的地之间的箱子的个数
513
else if (!Block.IsBlank(bi)) boxs += 2; // “墙”和“砖”折算为两个箱子
514
}
515
if (boxs > 1) return 0; // 最多只能推着一个箱子前进
516
return steps - boxs; // 工人移动的步数
517
}
518
519
/// <summary>
520
/// 检查鼠标点击位置是否可达, 并将像素换算为单元格
521
/// </summary>
522
/// <param name="to">输出:换算后的位置</param>
523
/// <returns>是否可达</returns>
524
bool CanTo(out Point to)
525
{
526
if (!ValidClick(out to)) return false;
527
if (!Block.IsMan(db.Map[worker.Y, worker.X])) throw new Exception("内部错误:工人的位置上不是工人");
528
if (!Block.IsBlank(db.Map[to.Y, to.X])) return false; // 目的地必须是“地”或“槽”
529
if (to.Y == worker.Y && to.X == worker.X) return false; // 目的地不能是工人当前的位置
530
return true; // 目的地可达
531
}
532
533
/// <summary>
534
/// 检查鼠标点击位置是否有效, 并将像素换算为单元格
535
/// </summary>
536
/// <param name="to">输出:换算后的位置</param>
537
/// <returns>是否有效位置</returns>
538
bool ValidClick(out Point to)
539
{
540
to = Point.Empty;
541
if (HasError) return false;
542
to.Y = toPixel.Y / boxSize.Height + 1;
543
to.X = toPixel.X / boxSize.Width + 1;
544
if (toPixel.X >= boxSize.Width * LevelSize.Width || toPixel.Y >= boxSize.Height * LevelSize.Height)
545
return false; // 目的地超出当前关的有效范围
546
return true; // 目的地有效
547
}
548
}
549
}
550

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

上一篇:使用 C# 开发智能手机软件:推箱子(十)
下一篇:使用 C# 开发智能手机软件:推箱子(十二)
返回目录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述