preface
This article belongs to the C# zero foundation introduction hundred knowledge hundred examples series. This series of articles aims to provide a systematic learning path for children's shoes learning C# language. This series of articles will be presented in the form of [knowledge points] [exercises]. If you have any questions, you can find me through comments, private letters, etc. I will answer your questions one-on-one.
Series contents:
"C# zero Basics: a hundred cases of knowledge" Directory article portal
Effect display
1, Level design
First, we need to determine the meaning of the array value and its corresponding representation:
numerical value | meaning | Symbol |
---|---|---|
0 | Space | '' empty string |
1 | wall | ■ solid square |
2 | case | □ hollow square |
3 | Villain | Human Chinese characters |
4 | target | ☆ hollow five pointed star |
5 | complete | ★ solid five pointed star |
This is the basic definition. In the article using array instances: Introduction to C# zero Basics (30) array application - push box game - map initialization , I won't repeat it here.
2, Map initialization
Main logic:
- Define two two two-dimensional arrays to represent the level map, map with player (3), and targeMap without player (3)
- Traverse the map, convert the map meaning corresponding to its value into the corresponding graph, and print it
/// <summary> ///Push box ///Map value meaning: 0: open space, 1: wall, 2: box, 3: people, 4: goal, 5: complete /// </summary> class Sokoban { static void Main(string[] args) { // Level initialization InitMap(); // Refresh map UpdateMap(); Console.ReadLine(); } // This level map static int[,] map; // Target map static int[,] targeMap; //Player's initial coordinates static int y = 4, x = 5; /// <summary> ///Level initialization - level array, player position /// </summary> static void InitMap() { y = 4; x = 5; map = new int[8, 9] { {0,0,0,1,1,1,0,0,0}, {0,0,0,1,4,1,0,0,0}, {0,0,0,1,0,1,1,1,1}, {0,1,1,1,2,0,2,4,1}, {0,1,4,0,2,3,1,1,1}, {0,1,1,1,1,2,1,0,0}, {0,0,0,0,1,4,1,0,0}, {0,0,0,0,1,1,1,0,0} }; targeMap = new int[8, 9] { {0,0,0,1,1,1,0,0,0}, {0,0,0,1,4,1,0,0,0}, {0,0,0,1,0,1,1,1,1}, {0,1,1,1,2,0,2,4,1}, {0,1,4,0,2,0,1,1,1}, {0,1,1,1,1,2,1,0,0}, {0,0,0,0,1,4,1,0,0}, {0,0,0,0,1,1,1,0,0} }; } /// <summary> ///Refresh map display /// </summary> static void UpdateMap() { // Clear screen Console.Clear(); Console.WriteLine("-------- Sokoban --------"); Console.WriteLine(); // Traversal array -- print new map for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { if (map[i, j] == 0) { Console.Write(" "); } if (map[i, j] == 1) { Console.Write("■"); } if (map[i, j] == 2) { Console.Write("□"); } if (map[i, j] == 3) { Console.Write("people![Please add a picture description](https://img-blog.csdnimg.cn/6a07bce3885340c6aa0b038e46858af9.gif) "); } if (map[i, j] == 4) { Console.Write("☆"); } if (map[i, j] == 5) { Console.Write("★"); } } Console.WriteLine(); } Console.WriteLine(); Console.WriteLine("Press ↑←↓→ Control player movement"); Console.WriteLine("input G restart"); } }
3, Player movement
Implementation logic:
- The user controls the player's movement through "↑←↓→", so it is necessary to detect the keyboard input in the cycle, and then process the corresponding keyboard input logic
- You need to pay attention to the direction of the player's movement and the calculation logic of the array. For example, moving up is y-1; it needs to be understood here
- Perform array out of bounds verification, and do not process unsatisfied values to avoid program error.
Simulate player movement logic:
/// <summary> ///Moving function /// </summary> ///< param name = "direction" > moving direction < / param > static void Move(ConsoleKey direction) { // The player moves the position this time int ox = 0, oy = 0; // Offset calculation based on direction switch (direction) { case ConsoleKey.UpArrow: oy--; break; case ConsoleKey.DownArrow: oy++; break; case ConsoleKey.LeftArrow: ox--; break; case ConsoleKey.RightArrow: ox++; break; default: return; } // Player moves -- restore map map[y, x] = targeMap[y, x]; y += oy; x += ox; // Make cross-border judgment if (y < map.GetLength(0) && y > 0 && x < map.GetLength(1) && x > 0) { map[y, x] = 3; } }
Modify the Main function as follows to run the input to control the player's movement
static void Main(string[] args) { // Level initialization InitMap(); // Loop - > execute once after waiting for user input while (true) { // Refresh map UpdateMap(); // Receive user input operation ConsoleKeyInfo keyInfo = Console.ReadKey(); // Protagonist movement Move(keyInfo.Key); } Console.ReadLine(); }
Movement effect:
4, Players push boxes
There are two steps for players to push the box: * * 1. The player moves forward one step; 2. The box moves forward one step. * * these two steps have various conditions, such as the wall in front of the box can't move, the wall in front of the box can't move, and so on
The movement after sorting is as follows: (for convenience of description, A1 is taken as the next target point and A2 as the next two target points)
- A1 is an open space or target -- > can be moved
- A1 is a wall -- > cannot be moved
- A1 is the box or the box to the target point -- > can be moved -- > at this time, we need to see whether the box can be pushed, that is, A2 point
-A2 is an open space or target point -- > can be moved
-A2 is the wall, box or box reaching the target -- > cannot be moved
Note: when you can move, you also need to consider whether the current player's position is the target position. If it is the target position, you need to restore the target. If not, you can restore it to a space.
The mobile code is modified as follows:
/// <summary> ///Moving function /// </summary> ///< param name = "direction" > moving direction < / param > static void Move(ConsoleKey direction) { // x, y of the next position the player wants to move int nx1 = x, ny1 = y; // Move x, y of the next two positions in the same direction int nx2 = x, ny2 = y; // The player moves the position this time int ox = 0, oy = 0; // Offset calculation based on direction switch (direction) { case ConsoleKey.UpArrow: ny1 -= 1; ny2 -= 2; oy--; break; case ConsoleKey.DownArrow: ny1 += 1; ny2 += 2; oy++; break; case ConsoleKey.LeftArrow: nx1 -= 1; nx2 -= 2; ox--; break; case ConsoleKey.RightArrow: nx1 += 1; nx2 += 2; ox++; break; default: return; } // The player's next coordinate is the open space to move if (map[ny1, nx1] == 0 || map[ny1, nx1] == 4) { map[ny1, nx1] = 3; map[y, x] = 0; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } // The next coordinate of the player is the wall and will not be processed else if (map[ny1, nx1] == 1) { return; } // The player's next coordinate is the box that does not reach the target or the box that reaches the target else if (map[ny1, nx1] == 2 || map[ny1, nx1] == 5) { // The next target of the box is the open space if (map[ny2, nx2] == 0) { map[ny2, nx2] = 2; map[ny1, nx1] = 3; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } // The next target of the box is the target point else if (map[ny2, nx2] == 4) { map[ny2, nx2] = 5; map[ny1, nx1] = 3; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } else { // The next target of the box is a wall or an empty box or a box reaching the target -- no processing //if (map[ny2, nx2] == 1 || map[ny2, nx2] == 2 || map[ny2, nx2] == 5) } } }
5, Game over
The logic of judging the end of the game is relatively simple. When there are no boxes and target points in the map, the game is considered successful. The code is implemented as follows:
/// <summary> ///End of the game - there are no boxes and target points in the map /// </summary> /// <returns></returns> static bool IsGameOver() { for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { if (map[i, j] == 2 || map[i, j] == 4) { return false; } } } return true; }
After the game is over, you can reopen or go to the next level according to the user's operation (this paper is not implemented, in fact, the logic can't, just set the map to the next level)
Modify the Main function as follows:
static void Main(string[] args) { // Level initialization InitMap(); // Loop - > execute once after waiting for user input while (true) { // Refresh map UpdateMap(); // Game success if (IsGameOver()) { Console.WriteLine("======== Congratulations ========"); // Console.WriteLine("press any key to play again...); // todo... Next level // Console.ReadKey(); //break; } // Receive user input operation ConsoleKeyInfo keyInfo = Console.ReadKey(); // Play again if (keyInfo.Key == ConsoleKey.G) { // Level initialization InitMap(); // Refresh map UpdateMap(); } else { // Protagonist movement Move(keyInfo.Key); } } Console.ReadLine(); }
6, Source sharing
The above code can form a completed push box game. The combined code:
/// <summary> ///Push box /// </summary> class Sokoban { /// <summary> ///This level map ///Value interpretation - > 0: space; wall: 1 ■; box: 2 □; person: 3; goal: 4 ☆; completion: 5 ★ /// </summary> static int[,] map; // Target map static int[,] targeMap; //Player's initial coordinates static int y = 4, x = 5; static void Main(string[] args) { // Level initialization InitMap(); // Loop - > execute once after waiting for user input while (true) { // Refresh map UpdateMap(); // Game success if (IsGameOver()) { Console.WriteLine("======== Congratulations ========"); // Console.WriteLine("press any key to play again...); // todo... Next level // Console.ReadKey(); break; } // Receive user input operation ConsoleKeyInfo keyInfo = Console.ReadKey(); // Play again if (keyInfo.Key == ConsoleKey.G) { // Level initialization InitMap(); // Refresh map UpdateMap(); } else { // Protagonist movement Move(keyInfo.Key); } } Console.ReadLine(); } /// <summary> ///Level initialization - level array, player position /// </summary> static void InitMap() { y = 4; x = 5; map = new int[8, 9] { {0,0,0,1,1,1,0,0,0}, {0,0,0,1,4,1,0,0,0}, {0,0,0,1,0,1,1,1,1}, {0,1,1,1,2,0,2,4,1}, {0,1,4,0,2,3,1,1,1}, {0,1,1,1,1,2,1,0,0}, {0,0,0,0,1,4,1,0,0}, {0,0,0,0,1,1,1,0,0} }; targeMap = new int[8, 9] { {0,0,0,1,1,1,0,0,0}, {0,0,0,1,4,1,0,0,0}, {0,0,0,1,0,1,1,1,1}, {0,1,1,1,2,0,2,4,1}, {0,1,4,0,2,0,1,1,1}, {0,1,1,1,1,2,1,0,0}, {0,0,0,0,1,4,1,0,0}, {0,0,0,0,1,1,1,0,0} }; } /// <summary> ///Refresh map display /// </summary> static void UpdateMap() { // Clear screen Console.Clear(); Console.WriteLine("-------- Sokoban --------"); Console.WriteLine(); //Print new map for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { if (map[i, j] == 0) { Console.Write(" "); } if (map[i, j] == 1) { Console.Write("■"); } if (map[i, j] == 2) { Console.Write("□"); } if (map[i, j] == 3) { Console.Write("people"); } if (map[i, j] == 4) { Console.Write("☆"); } if (map[i, j] == 5) { Console.Write("★"); } } Console.WriteLine(); } Console.WriteLine(); Console.WriteLine("Press ↑←↓→ Control player movement"); Console.WriteLine("input G restart"); } /// <summary> ///Moving function /// </summary> ///< param name = "direction" > moving direction < / param > static void Move(ConsoleKey direction) { // x, y of the next position the player wants to move int nx1 = x, ny1 = y; // Move x, y of the next two positions in the same direction int nx2 = x, ny2 = y; // The player moves the position this time int ox = 0, oy = 0; // Offset calculation based on direction switch (direction) { case ConsoleKey.UpArrow: ny1 -= 1; ny2 -= 2; oy--; break; case ConsoleKey.DownArrow: ny1 += 1; ny2 += 2; oy++; break; case ConsoleKey.LeftArrow: nx1 -= 1; nx2 -= 2; ox--; break; case ConsoleKey.RightArrow: nx1 += 1; nx2 += 2; ox++; break; default: return; } // The player's next coordinate is the open space to move if (map[ny1, nx1] == 0 || map[ny1, nx1] == 4) { map[ny1, nx1] = 3; map[y, x] = 0; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } // The next coordinate of the player is the wall and will not be processed else if (map[ny1, nx1] == 1) { return; } // The player's next coordinate is the box that does not reach the target or the box that reaches the target else if (map[ny1, nx1] == 2 || map[ny1, nx1] == 5) { // The next target of the box is the open space if (map[ny2, nx2] == 0) { map[ny2, nx2] = 2; map[ny1, nx1] = 3; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } // The next target of the box is the target point else if (map[ny2, nx2] == 4) { map[ny2, nx2] = 5; map[ny1, nx1] = 3; // The player's current coordinates are the coordinates of the target point -- restore it back if (targeMap[y, x] == 4) map[y, x] = 4; else map[y, x] = 0; y += oy; x += ox; } else { // The next target of the box is a wall or an empty box or a box reaching the target -- no processing //if (map[ny2, nx2] == 1 || map[ny2, nx2] == 2 || map[ny2, nx2] == 5) } } } /// <summary> ///End of the game - there are no boxes and target points in the map /// </summary> /// <returns></returns> static bool IsGameOver() { for (int i = 0; i < map.GetLength(0); i++) { for (int j = 0; j < map.GetLength(1); j++) { if (map[i, j] == 2 || map[i, j] == 4) { return false; } } } return true; } }