初学者RPG碰撞检测

时间:2018-10-12 12:55:00

标签: c# ascii console-application game-physics

再次,非常感谢您对我之前的问题的帮助。

我为娱乐和教育而制作的RPG取得了更大的进步。现在,我可以从.txt文件(彩色!)中显示地图了,因此我很高兴一切正常。

我接下来要实现的是碰撞检测系统。例如:如果我的玩家角色尝试踩到'〜'角色,则该程序不会移动玩家角色,因为'〜'是水,无法行走。

我为运动系统和加载地图提供的代码如下:

using System;
using System.IO;

namespace TextFileReaderTest
{
class Program
{
    public static int PosX;
    public static int PosY;
    static void DisplayMap()
    {

        string line;
        //Pass the file path and file name to the StreamReader constructor
        StreamReader sr = new StreamReader(@"D:\personal\tests\Tests\ascii map tools\map1.txt");

        //Read the first line of text
        line = sr.ReadLine();

        //Continue to read until you reach end of file
        while (line != null)
        {
            Char[] MapArray = line.ToCharArray();
            foreach (Char c in MapArray)
            {
                if (c == '/')
                {
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.Write('/');
                }
                else if (c == '^')
                {
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.Write('^');
                }
                else if (c == '|')
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.Write('|');
                }
                else if (c == '.')
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.Write('.');
                }
                else if (c == 'o')
                {
                    Console.ForegroundColor = ConsoleColor.DarkCyan;
                    Console.Write('o');
                }
                else if (c == '~')
                {
                    Console.ForegroundColor = ConsoleColor.DarkCyan;
                    Console.Write('~');
                }
                else
                {
                    Console.Write(c);
                    Console.ForegroundColor = System.ConsoleColor.White;
                }


            }
            //Read the next line
            line = sr.ReadLine();
        }

                //close the file
                sr.Close();

    }




    static void Main(string[] args)
    {
        Console.WindowWidth = 128;
        Console.WindowHeight = 32;
        DisplayMap();
        Console.SetCursorPosition(10, 10); //the cursor will be set at x = 10 and y = 10

        while (true)
        {
            ConsoleKeyInfo input = Console.ReadKey(true);
            PosX = Console.CursorLeft;
            PosY = Console.CursorTop;

            switch (input.KeyChar)
            {

                case 'w':
                    if (PosY >= 1)
                    {
                        Console.SetCursorPosition(PosX + 0, PosY - 1);
                        PosX = Console.CursorLeft;
                        PosY = Console.CursorTop;
                        //if (PosX == 11 && PosY == 11 )            //this is a portal
                        //{
                        //    Console.SetCursorPosition(20, 20);    // clear console, teleport player, load new map 
                        //}
                    }
                    break;

                case 'a':
                    if (PosX >= 1)
                    {
                        Console.SetCursorPosition(PosX - 1, PosY + 0);
                    }
                    break;

                case 's':
                    if (PosY < 31)
                    {
                        Console.SetCursorPosition(PosX + 0, PosY + 1);
                    }
                    break;

                case 'd':
                    if (PosX < 127)
                    {
                        Console.SetCursorPosition(PosX + 1, PosY + 0);
                    }
                    break;

            }
        }
    }
}

}

许多“ if else”语句是针对地图中的颜色的。请不要介意我的代码中有很多注释。每当有想法时,我通常会在可能实现的地方写评论。另外,它是其他程序员的解释,我认为这很有用。我仍然还是一个初学者,因此,如果您能解释您的解决方案,将不胜感激。

提前谢谢!

2 个答案:

答案 0 :(得分:2)

为特定事物提供特定类是一个好习惯。即Map类用于处理地图,Player类用于处理播放器,等等。

现在,要处理碰撞,我将有两个Map,一个用于存储和显示播放器图标等,还有一个用于瓷砖的参考Map(检查起来会更容易播放器在什么位置上并正在朝此方向移动。

Map应该有一个private char[][]字段来存储图块,以便可以快速调用所述图块。

将字符的当前位置存储在Player类的XY中的两个字段中,然后将 next 位置存储到另外两个位置中字段NewXNewY。在移动Player之前,请将当前坐标存储到PrevXPrevY中。

如果玩家不应该移动TileMap[Player.NewX][Player.NewY]上的图块,请不要更改Player.XPlayer.Y。否则,将Player.NewX存储到Player.X中,将Player.NewY存储到Player.Y中。

TL; DR:

Player类的示例:

public class Player{
    public int X, Y;
    private int NewX, NewY;
    private int PrevX, PrevY;

    public Player(){
        X = 10;
        Y = 10;
        NewX = 0;
        NewY = 0;
        PrevX = 0;
        PrevY = 0;
    }

    //Add other constructors to initialize the coordinates

    public void Update(){
        //Call this method in `Main()` in a loop.  Make sure to add a delay between each call!
        PrevX = X;
        PrevY = Y;

        ConsoleKeyInfo input = Console.ReadKey(true);
        switch(input.KeyChar){
            case 'w':
                NewX = X;
                NewY = Y - 1;
                break;
            case 'a':
                NewX = X - 1;
                NewY = Y;
                break;
            case 's':
                NewX = X;
                NewY = Y + 1;
                break;
            case 'd':
                NewX = X + 1;
                NewY = Y;
        }

        //Add code to restrict the player to the window

        if(RPG.ReferenceMap[NewX][NewY] == '~'){  //Water tile
            NewX = X;
            NewY = Y;
        }

        //Check for other tiles the player should not walk on

        X = NewX;
        Y = NewY;
    }
}

Map类的示例:

public class Map{
    public char[][] TileMap;
    public int Height, Width;

    public Map(){
        TileMap = new char[16][16];  //Default to a 16x16 map
        Height = 16;
        Width = 16;
    }
    public Map(string file){
        //Load the map like you did above, but store the chars read into the TileMap array
        //Give the 'Height' and 'Width' fields the height and width of the map
    }

    public void Update(){
        //Call this AFTER Player.Update()

        //Use the reference map to add the correct tile to the previous position
        //and then update the Player's icon
        TileMap[Player.PrevX][Player.PrevY] = RPG.ReferenceMap.TileMap[Player.PrevX][Player.PrevY];

        TileMap[Player.X][Player.Y] = 'O';  //Assuming that 'O' is your player icon
    }

    public void Draw(){
        //Use the "TileMap" field to refresh the map

        Console.Clear();  //Clear the console

        foreach(char[] array in TileMap){
            foreach(char tile in array){
                //Use the logic you used in your "foreach" loop to draw the tiles.
            }
        }
    }
}

在主类中应称为RPG的示例:

public class RPG{
    Player player;
    Map ReferenceMap, ActualMap;

    public static void Main(){
        player = new Player();  //Use the default constructor or another constructor
                                //to set the starting coords of the player

        ActualMap = new Map();  //Load the data into the map

        ReferenceMap = ActualMap;  //Since they are the same for now, just store
                                   //the data already there
        while(true){
            //Add a delay using Stopwatch, preferably 1/60th of a second
            Update();
        }
    }

    public void Update(){
        Player.Update();
        ActualMap.Update();

        ActualMap.Draw();
    }
}

答案 1 :(得分:1)

虽然我认为另一个答案比这个答案更好,并且更接近实际游戏开发,但对于初学者而言可能很难掌握。这样做的目的是为了更简单,并且理论上更少。

代码使用函数和参数以及switch语句,但应更接近OP的级别。代码中的注释应说明其作用。

示例文件:

^^^^^^^^^^^^^^^
|||||||||||||||

  ~~~          
 ~~~~~         
  ~~~          

...............
...............
...............
...............

代码:

using System;
using System.IO;

namespace TextFileReaderTest
{
    class Program
    {
        static char GetTerrainAt(string[] map, int x, int y)
        {
            return map[y][x];
        }

        static void PrintCharacter(int x, int y)
        {
            Console.SetCursorPosition(x, y);
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.Write('X');
        }

        static void PrintTerrainAt(string[] map, int x, int y)
        {
            char terrain = GetTerrainAt(map, x, y);

            switch (terrain)
            {
                case '/':
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    break;
                case '^':
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    break;
                case '|':
                    Console.ForegroundColor = ConsoleColor.Green;
                    break;
                case '.':
                    Console.ForegroundColor = ConsoleColor.Green;
                    break;
                case 'o':
                    Console.ForegroundColor = ConsoleColor.DarkCyan;
                    break;
                case '~':
                    Console.ForegroundColor = ConsoleColor.DarkCyan;
                    break;
                default:
                    Console.ForegroundColor = ConsoleColor.White;
                    break;
            }

            Console.SetCursorPosition(x, y);
            Console.Write(terrain);
        }

        static void Main()
        {
            // initialize (once)
            var map = File.ReadAllLines(@"D:\personal\tests\Tests\ascii map tools\map1.txt");

            var posX = 10;
            var posY = 10;

            Console.WindowWidth = map[0].Length; // length of first line, make sure all lines in the file are of same length
            Console.WindowHeight = map.Length; // number of lines
            Console.CursorVisible = false;

            // print whole map once
            for (int x = 0; x < Console.WindowWidth; x++)
            {
                for (int y = 0; y < Console.WindowHeight; y++)
                {
                    PrintTerrainAt(map, x, y);
                }
            }

            // print character starting pos
            PrintCharacter(posX, posY);

            // start
            while (true)
            {
                var input = Console.ReadKey(true);

                // next move for now: stay in the same place
                var nextX = posX;
                var nextY = posY;

                // find out where the next move will take us
                switch (input.KeyChar)
                {
                    case 'w':
                        nextY--;
                        break;

                    case 'a':
                        nextX--;
                        break;

                    case 's':
                        nextY++;
                        break;

                    case 'd':
                        nextX++;
                        break;
                }

                // make sure it's a legal move 
                if (nextY >= Console.WindowHeight || nextY < 0 || nextX >= Console.WindowWidth || nextX < 0)
                {
                    // illegal move, beep and continue the while loop from the top without moving the character
                    Console.Beep();
                    continue;
                }

                char terrainToMoveTo = GetTerrainAt(map, nextX, nextY);

                // this should probably be moved into a function "IsTerrainPassable(terrainToMoveTo)"
                if (terrainToMoveTo == '~')
                {
                    // illegal move, beep and continue the while loop from the top without moving the character
                    Console.Beep();
                    continue;
                }

                // okay, legal move, move our character:

                // clean up old position (if you comment this out, you will see a "snake")
                PrintTerrainAt(map, posX, posY);

                // move character
                posX = nextX;
                posY = nextY;

                // print character at new position
                PrintCharacter(posX, posY);
            }
        }
    }
}