最近,小悦的生活像是一首繁忙的交响曲,每天忙得团团转,虽然她的日程安排得满满当当,但她并未感到充实。相反,她很少有时间陪伴家人,这让她感到有些遗憾。在周五的午后,小悦的哥哥突然打来电话,他的声音里充满了焦虑。

“小悦,我有个事情想拜托你。”哥哥的声音传来。

小悦不禁有些疑惑,哥哥有什么事情需要她帮忙呢?她忍不住问:“哥哥,有什么需要我帮忙的吗?”

哥哥解释说:“我最近要出差一段时间,大概一个星期左右。而我的妻子目前正在照顾住院的父母,没有时间照顾小明。我想请你帮忙照顾小明一段时间。”

小悦愣住了,没想到哥哥会把这个重任托付给她。这是一个不小的挑战,她有些担心自己无法胜任。但是,她知道她不能让自己的感情影响到哥哥的决定。于是她坚定地回答:“好的,哥哥,我会照顾好小明的。你放心去出差吧。”

电话那头的哥哥松了一口气,感激地说:“谢谢你,小悦。我相信你会照顾好小明的。我会尽快回来的。”

就这样,小悦接下了照顾小明的重任。虽然她很忙,但是她觉得为了哥哥和小明,她要付出更多的努力和时间。接下来的几天,小悦事无巨细地照顾着小明,给他做饭、洗衣、辅导作业,尽职尽责地扮演着一个姑姑的角色。

在周末的下午,小悦坐在电脑前,专注地处理着工作。她穿着舒适的衣裤,一头长发随意扎起,给人一种亲切自然的感觉。突然,门口传来了敲门声,小悦放下手中的工作,打开门一看,原来是她的侄儿小明。

小明是个活泼好动的孩子,二年级的他还保留着童年的纯真和好奇。他瞪大了眼睛,好奇地看着小悦的电脑屏幕:“姐姐,你在干什么呀?”小悦微微一笑,纠正道:“我是姑姑,不是姐姐。”小明尴尬地挠了挠头,赶紧改口:“姑姑,你在干什么呀?”

小悦笑着回答:“我在写程序呀。”

“程序?可以教我吗?”小明充满期待地问。

“当然可以,”小悦点点头,她向小明介绍了一下编程的基本知识和规则,然后开始手把手地教他如何编写简单的游戏程序。

小悦耐心地教导小明如何设计坦克模拟游戏。她先向他介绍了游戏的基本规则,然后手把手地教他如何用程序操作键盘上的十字键来控制屏幕中的坦克移动。在这个过程中,小悦始终保持着微笑,她温柔的话语和细心的指导让小明倍感亲切。

时间在不知不觉中流逝,小悦的心里也渐渐感到了疲惫。但每当看到小明在编程的世界中自由翱翔的时候,她的心里就充满了欣慰和自豪。小明也渐渐地开始明白了编程的奥秘,他的兴趣被极大地激发了出来,每天都充满了新的期待和挑战。尽管这段日子很累很忙,但小悦觉得很有意义。因为她知道这段时间对小明来说是宝贵的,而她也有幸参与其中,这让她感到无比的满足和幸福。


小悦使用面向对象的思想,设计了一个名为PlayerMovement的类,用于实时控制游戏中玩家的移动。该类包含一个directions列表,用于存储当前按下的方向键。Position属性表示当前玩家的位置,Direction属性表示当前玩家的方向。

算法实现1:

 1 public class PlayerMovement
2 {
3 private List<Direction> directions = new List<Direction>();
4
5 public Tile Position { get; private set; }
6 public Direction Direction { get; private set; }
7
8 public PlayerMovement(int x, int y)
9 {
10 // 初始化玩家位置
11 this.Position = new Tile(x, y);
12 }
13
14 public void Update()
15 {
16 // 获取新的方向
17 this.directions = GetNewDirections(this.directions);
18 if (!this.directions.Any()) return;
19
20 var currentDirection = this.directions.Last();
21 if (this.Direction != currentDirection)
22 {
23 // 更新玩家方向
24 this.Direction = currentDirection;
25 }
26 else
27 {
28 // 获取新的位置
29 this.Position = GetNewPosition(this.Position, this.Direction);
30 }
31 }
32
33 private static List<Direction> GetNewDirections(IEnumerable<Direction> current)
34 {
35 var newState = current.ToList();
36
37 var currentDirections = GetCurrentDirections().ToList();
38
39 var directionsToRemove = newState.Except(currentDirections).ToList();
40 directionsToRemove.ForEach(d => newState.Remove(d));
41
42 var directionsToAdd = currentDirections.Except(newState).ToList();
43 directionsToAdd.ForEach(d => newState.Add(d));
44
45 return newState;
46 }
47
48 private static Tile GetNewPosition(Tile pos, Direction dir)
49 {
50 var newX = pos.X;
51 var newY = pos.Y;
52
53 switch (dir)
54 {
55 case Direction.Left:
56 newX--;
57 break;
58 case Direction.Right:
59 newX++;
60 break;
61 case Direction.Down:
62 newY--;
63 break;
64 case Direction.Up:
65 newY++;
66 break;
67 }
68
69 return new Tile(newX, newY);
70 }
71
72 private static IEnumerable<Direction> GetCurrentDirections()
73 {
74 // 通过yield return获取当前按键状态并返回对应的方向
75 if (Input.GetState(Direction.Right)) yield return Direction.Right;
76 if (Input.GetState(Direction.Left)) yield return Direction.Left;
77 if (Input.GetState(Direction.Down)) yield return Direction.Down;
78 if (Input.GetState(Direction.Up)) yield return Direction.Up;
79 }
80 }

通过测试用例解释运行过程:

 1       private PlayerMovement _player;
2
3 private void TestEquality(Direction direction, int x, int y)
4 {
5 _player.Update();
6
7 Assert.AreEqual(direction, _player.Direction);
8 Assert.AreEqual(new Tile(x, y), _player.Position);
9 }
10
11 [Test(Description = "Basic Test 1")]
12 public void BasicTest1()
13 {
14 _player = new PlayerMovement(0, 0);
15 Input.Clear();
16
17 Press(Direction.Down);
18
19 TestEquality(Direction.Down, 0, 0);
20 TestEquality(Direction.Down, 0, -1);
21 TestEquality(Direction.Down, 0, -2);
22
23 Press(Direction.Left);
24 Press(Direction.Right);
25
26 TestEquality(Direction.Left, 0, -2);
27 TestEquality(Direction.Left, -1, -2);
28
29 Release(Direction.Left);
30
31 TestEquality(Direction.Right, -1, -2);
32
33 Release(Direction.Right);
34
35 TestEquality(Direction.Down, -1, -2);
36 TestEquality(Direction.Down, -1, -3);
37
38 Release(Direction.Down);
39
40 TestEquality(Direction.Down, -1, -3);
41 }

首先,我们创建了一个名为_playerPlayerMovement对象,并将其初始位置设置为(0, 0)。然后,我们清除了输入状态。

接下来,我们按下了向下方向键。这将触发Input.GetState(Direction.Down)返回true,所以GetCurrentDirections函数将返回一个包含Direction.Down的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表只包含一个元素Direction.Down,所以它不会发生任何变化。

TestEquality方法中,我们首先调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -1)。然后,我们使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

接下来,我们再次调用_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们按下了向左和向右方向键。这将触发Input.GetState(Direction.Left)Input.GetState(Direction.Right)都返回true,所以GetCurrentDirections函数将返回一个包含Direction.LeftDirection.Right的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.Down,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Left,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

接下来,我们释放了向左方向键。这将触发Input.GetState(Direction.Left)返回false,所以GetCurrentDirections函数将返回一个不包含Direction.Left的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.DownDirection.Right,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Right,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们释放了向右方向键。这将触发Input.GetState(Direction.Right)返回false,所以GetCurrentDirections函数将返回一个不包含Direction.Right的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.Down,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

最后,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(-1, -3)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。


算法实现2:

 1 public class PlayerMovement
2 {
3 public Tile Position { get; private set; } // 玩家的位置
4 public Direction Direction { get; private set; } // 玩家的方向
5
6 // 不同方向对应的坐标变化
7 private Dictionary<Direction, (int xDelta, int yDelta)> moves = new Dictionary<Direction, (int xDelta, int yDelta)>()
8 {
9 { Direction.Up, (0, 1) },
10 { Direction.Down, (0, -1) },
11 { Direction.Left, (-1, 0) },
12 { Direction.Right, (1, 0) }
13 };
14
15 // 方向的排序顺序
16 private Direction[] sortOrder = new[] {Direction.Up, Direction.Down, Direction.Left, Direction.Right};
17
18 private Stack<Direction> directions = new Stack<Direction>(); // 当前的方向列表
19
20 public PlayerMovement(int x, int y)
21 {
22 this.Position = new Tile(x, y); // 初始化玩家的位置
23 }
24
25 public void Update()
26 {
27 Console.WriteLine("Called update"); // 输出调试信息
28
29 // 获取当前按下的方向键
30 var pressedDirections = Enum.GetValues(typeof(Direction)).Cast<Direction>().Where(x => Input.GetState(x));
31
32 // 清除不再按下的方向键
33 while (directions.Count != 0 && !pressedDirections.Contains(directions.Peek())) {
34 directions.Pop();
35 }
36
37 if (pressedDirections.Count() == 0) {
38 return; // 如果没有按下任何方向键,则直接返回
39 }
40
41 // 获取新按下的方向键,并按照排序顺序进行排序
42 var newDirections = pressedDirections.Except(directions).OrderBy(x => Array.IndexOf(sortOrder, x)).ToArray();
43
44 // 将新的方向键加入到方向列表中
45 for (int i = newDirections.Length - 1; i >= 0; i -= 1) {
46 directions.Push(newDirections[i]);
47 }
48
49 Direction directionToMove = directions.Peek(); // 获取要移动的方向
50
51 if (directionToMove == this.Direction) {
52 // 如果要移动的方向与当前方向相同,则更新玩家的位置
53 (int xDelta, int yDelta) = moves[directionToMove];
54 this.Position = new Tile(this.Position.X + xDelta, this.Position.Y + yDelta);
55 }
56 else {
57 // 如果要移动的方向与当前方向不同,则更新玩家的方向
58 this.Direction = directionToMove;
59 }
60 }
61 }

算法2的运行步骤如下:

  1. 获取当前按下的方向键。
  2. 清除不再按下的方向键。
  3. 如果没有按下任何方向键,则直接返回。
  4. 获取新按下的方向键,并按照排序顺序进行排序。
  5. 将新的方向键加入到方向列表中。
  6. 获取要移动的方向。
  7. 如果要移动的方向与当前方向相同,则更新玩家的位置。
  8. 如果要移动的方向与当前方向不同,则更新玩家的方向。

优点:

  1. 实现简单,易于理解。
  2. 可以处理多个方向键同时按下的情况。

缺点:

  1. 在处理多个方向键同时按下的情况时,可能会出现按键顺序和预期不一致的情况。
  2. 对于复杂的移动逻辑,可能需要添加更多的代码来处理。

总结,这两个PlayerMovement类都是面向对象的,但是它们的实现方式略有不同:

1. 数据封装

算法2类使用属性来封装位置和方向,而算法1类也使用属性来封装位置和方向。两者的数据封装方式相同。

2. 代码组织

算法2类使用一个Update方法来更新玩家位置和方向,而算法1类也使用一个Update方法来更新玩家位置和方向。两者的代码组织方式相同。

3. 代码复用

算法2类使用了一个moves字典来存储方向和移动量的对应关系,而算法1类则没有使用字典,而是使用了GetCurrentDirections方法和GetNewPosition方法来计算新的方向和位置。两者的代码复用方式略有不同。

4. 可扩展性

算法2类使用了一个sortOrder数组来定义方向的排序,而算法1类则没有使用这种方式。算法1类使用了GetNewDirections方法,利用yield return特性来计算新的方向,这种方式更加灵活和可扩展。

它们都具有良好的数据封装和代码组织,以及可扩展性和代码复用性。其中,算法1类更加灵活和可扩展,适用于更加复杂的场景,例如:

  1. 虚拟现实和增强现实应用:在虚拟现实和增强现实应用中,用户通常需要通过手柄、控制器或手势来控制虚拟对象的移动。PlayerMovement算法可以用于处理用户输入的方向指令,并更新虚拟对象的位置和方向。

  2. 机器人控制:在机器人控制领域,PlayerMovement算法可以用于处理机器人的移动指令。例如,将方向键映射到机器人的移动方向,并根据按键的顺序来确定机器人的移动路径。

  3. 自动驾驶系统:在自动驾驶系统中,PlayerMovement算法可以用于处理车辆的移动指令。例如,将方向键映射到车辆的转向角度,并根据按键的顺序来确定车辆的转向路径。

  4. 智能家居系统:在智能家居系统中,PlayerMovement算法可以用于处理家居设备的移动指令。例如,将方向键映射到智能灯泡的亮度和颜色,并根据按键的顺序来调整灯泡的状态。


测试用例:

  1 using NUnit.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5
6 namespace TopDownMovement
7 {
8 [TestFixture]
9 public class SolutionTest
10 {
11 private PlayerMovement _player;
12
13 private void TestEquality(Direction direction, int x, int y)
14 {
15 _player.Update();
16
17 Assert.AreEqual(direction, _player.Direction);
18 Assert.AreEqual(new Tile(x, y), _player.Position);
19 }
20
21 [Test(Description = "Basic Test 1")]
22 public void BasicTest1()
23 {
24 _player = new PlayerMovement(0, 0);
25 Input.Clear();
26
27 Press(Direction.Down);
28
29 TestEquality(Direction.Down, 0, 0);
30 TestEquality(Direction.Down, 0, -1);
31 TestEquality(Direction.Down, 0, -2);
32
33 Press(Direction.Left);
34 Press(Direction.Right);
35
36 TestEquality(Direction.Left, 0, -2);
37 TestEquality(Direction.Left, -1, -2);
38
39 Release(Direction.Left);
40
41 TestEquality(Direction.Right, -1, -2);
42
43 Release(Direction.Right);
44
45 TestEquality(Direction.Down, -1, -2);
46 TestEquality(Direction.Down, -1, -3);
47
48 Release(Direction.Down);
49
50 TestEquality(Direction.Down, -1, -3);
51 }
52
53 [Test(Description = "All keys at once")]
54 public void BasicTest2()
55 {
56 _player = new PlayerMovement(0, 0);
57 Input.Clear();
58
59 Press(Direction.Down);
60 Press(Direction.Left);
61 Press(Direction.Right);
62 Press(Direction.Up);
63
64 TestEquality(Direction.Up, 0, 0);
65 TestEquality(Direction.Up, 0, 1);
66
67 Release(Direction.Left);
68
69 TestEquality(Direction.Up, 0, 2);
70
71 Release(Direction.Up);
72
73 TestEquality(Direction.Down, 0, 2);
74
75 Release(Direction.Down);
76
77 TestEquality(Direction.Right, 0, 2);
78 TestEquality(Direction.Right, 1, 2);
79 TestEquality(Direction.Right, 2, 2);
80
81 Release(Direction.Right);
82
83 TestEquality(Direction.Right, 2, 2);
84 }
85
86 [Test(Description = "Random Test")]
87 public void RandomTest()
88 {
89 int x, y;
90 Random rand = new Random();
91
92 x = rand.Next(200) - 100;
93 y = rand.Next(200) - 100;
94
95 _player = new PlayerMovement(x, y);
96 Input.Clear();
97
98 Press(Direction.Down);
99 Press(Direction.Left);
100 Press(Direction.Right);
101 Press(Direction.Up);
102
103 TestEquality(Direction.Up, x, y);
104
105 for (int i = 0; i < rand.Next(20) + 1; i++)
106 {
107 y++;
108 TestEquality(Direction.Up, x, y);
109 }
110
111 Release(Direction.Left);
112
113 y++;
114 TestEquality(Direction.Up, x, y);
115
116 Release(Direction.Up);
117
118 TestEquality(Direction.Down, x, y);
119
120 Release(Direction.Down);
121
122 TestEquality(Direction.Right, x, y);
123 x++;
124 TestEquality(Direction.Right, x, y);
125 x++;
126 TestEquality(Direction.Right, x, y);
127
128 Release(Direction.Right);
129
130 TestEquality(Direction.Right, x, y);
131 }
132
133 private void Press(Direction dir) { Console.WriteLine("Pressed " + dir); Input.Press(dir); }
134 private void Release(Direction dir) { Console.WriteLine("Released " + dir); Input.Release(dir); }
135 }
136 }

【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动的更多相关文章

  1. 从 prototype.js 深入学习 javascript 的面向对象特性

    从 prototype.js 深入学习 javascript 的面向对象特性 js是一门很强大的语言,灵活,方便. 目前我接触到的语言当中,从语法角度上讲,只有 Ruby 比它更爽. 不过我接触的动态 ...

  2. 我的Java学习笔记-Java面向对象

    今天来学习Java的面向对象特性,由于与C#的面向对象类似,不需详细学习 一.Java继承 继承可以使用 extends 和 implements 这两个关键字来实现继承. extends:类的继承是 ...

  3. 【整理】HTML5游戏开发学习笔记(1)- 骰子游戏

    <HTML5游戏开发>,该书出版于2011年,似乎有些老,可对于我这样没有开发过游戏的人来说,却比较有吸引力,选择自己感兴趣的方向来学习html5,css3,相信会事半功倍.不过值得注意的 ...

  4. 在游戏中使用keybd_event的问题

    转自在游戏中使用keybd_event的问题 今天发现在游戏中,keybd_event不能使用,结果发现游戏是使用directinput实现读取键盘的,关键还是扫描码的问题,我抄了一段老外的代码,经测 ...

  5. 论游戏中Buff的实现 [转]

    论游戏中Buff的实现 分类: C/C++ 游戏开发2012-09-13 14:30 574人阅读 评论(6) 收藏 举报 c++游戏开发 源地址:http://blog.codingnow.com/ ...

  6. 游戏中的沉浸(Flow in Games)

    转自:https://www.jianshu.com/p/4c52067f6594 作者:陈星汉(JenovaChen) 本论文的主旨在于提供一种独特的方法论,用以指导游戏设计中的以玩家为中心的动态难 ...

  7. ChatGPT 设计游戏剧情 | 基于 AI 5 天创建一个农场游戏,完结篇!

    欢迎使用 AI 进行游戏开发! 在本系列中,我们将使用 AI 工具在 5 天内创建一个功能完备的农场游戏.到本系列结束时,您将了解到如何将多种 AI 工具整合到游戏开发流程中.本文将向您展示如何将 A ...

  8. JavaScript游戏中的面向对象的设计

    简介: 从程序角度考虑,许多 JavaScript 都基于循环和大量的 if/else 语句.在本文中,我们可了解一种更聪明的做法 — 在 JavaScript 游戏中使用面向对象来设计.本文将概述原 ...

  9. 如何在Cocos2D游戏中实现A*寻路算法(八)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  10. 如何在Cocos2D游戏中实现A*寻路算法(六)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

随机推荐

  1. C++面试八股文:知道std::unordered_set/std::unordered_map吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第27面: 面试官:知道std::unordered_set/std::unordered_map吗? 二师兄:知道.两者都是C++11引入的新容器, ...

  2. GGTalk 开源即时通讯系统源码剖析之:数据库设计

    自从<开源即时通讯GGTalk 8.0发布,增加Linux客户端,支持在统信UOS.银河麒麟上运行!>一文在博客园发布后,有园友联系我QQ,说能不能整理个更系统更详细地介绍GGTalk源码 ...

  3. 龙芯电脑编译redis (loongarch)

    1.获取源码 源码地址:https://redis.io/download/#redis-downloads 最新版本是7.2,这里用redis5测试,最后一个版本是5.0.14 wget https ...

  4. 安装Hadoop单节点伪分布式集群

    目录 安装Hadoop单节点伪分布式集群 系统准备 开启SSH 安装JDK 安装Hadoop 下载 准备启动 伪分布式模式安装 配置 配饰SSH免密登录本机 测试启动 单节点安装YARN 伪分布式集群 ...

  5. 实际上手体验maven面对冲突Jar包的加载规则

    一.问题背景 相信大家在日常的开发过程中都遇到过Jar包冲突的问题,emm,在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题.主要是一个完整的项目会不可避免的使用第三方的Jar ...

  6. 2023年CCPC河南省程序设计竞赛 mjh

    首先,很荣幸有机会参加此次ccpc,虽然成绩很一般... 这次ccpc一共过了两道签到题.比赛开始就找到了a题,考察字符串的回文判断,通过调用c++库函数过了.第二道签到题类似于数学题.通过类似于找规 ...

  7. Windows安装SSH服务器

    1.打开Win的设置并在设置中找到应用 2.在应用中依次选择应用和功能 可选功能 3.在可选功能中选择添加功能 (OpenSSH客户端默认已存在) 选中OpenSSH服务器后点击下方的安装 4.快捷键 ...

  8. Cloudflare 重定向配置

    最近把之前的一个网站域名换成另一个域名,想要添加一下重定向,避免流量流失(虽然本来就没流量).然后在 Cloudflare 配置时尝试多次都失败了,遇到各种 Your connection is no ...

  9. minipc安装与设置Ubuntu

    此文章是对刚刚在某宝买的minipc进行的Ubuntu server安装,以及部分应用过程 安装Ubuntu server22 参考一文搞懂Ubuntu Server 22.04.2安装 问题记录 开 ...

  10. flash 游戏分析 - 1

    游戏 我们就以<猎人的生存日记>(Orion Sandbox)这款游戏来分析. 下载链接 用FlashStart打开Orion Sandbox 1.swf 我们需要反复进入游戏,可以先打开 ...