C#Monogame - 文件

时间:2015-08-22 18:29:44

标签: c# arrays io xna monogame

嘿,伙计我是Monogame的新手,所以我在线学习了一些教程。我正在构建一个2D平台游戏。但制作视频的人只是展示了如何创建地图和平铺类,并从他制作的数组中加载了关卡。我听说这是一个不好的做法,人们应该从文件加载他们的级别...我知道如何使用流阅读器加载文件,但由于那不是我的代码,我正在按照教程进行操作,我遇到了困难实施它。

所以这里是我对该关卡的代码:

private Map map;

// This is in my LoadContent() method
map.Generate(new int[,]
            {
                {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
                {2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2},
                {2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2},
                {2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2},
                {2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2},
                {2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
                {2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
                {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
                {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
            }, 64);

所以我正在加载这样的瓷砖:

// Tiles.cs class
public CollisionTiles(int i, Rectangle newRectangle)
        {
            texture = Content.Load<Texture2D>("Graphics/Tiles/tile_" + i);
            this.Rectangle = newRectangle;
        }

以上整数中的每一个都被添加到文件名的字符串中并直接加载并在屏幕上绘制(如下所示:map.Draw(spriteBatch);)。 64 int是实际切片绘制的大小。这就是我想要避免的,必须为每个级别编写这些数组,而我需要一种从文件加载级别的方法。在加载关卡的平铺时,平铺大小不应该忘记在.txt文件中。也许让它独自留在关卡文件的第一行或最后一行?这样,如果需要,每个级别可以具有不同大小的区块。

以下是我的Map.csTiles.cs课程。我没有直接将它们粘贴到SO上会导致问题太长......

那你怎么看待这个?什么样的解决方案最好?我认为加载类似于瓷砖的级别:"Levels/level_" + i也是我很好的方式,每次玩家完成一个关卡时我都会让游戏增加int i(我应该能够自己做) ,但我只想问你如何阅读文件。我会对我的代码进行任何建议的修改,并且我认为Split , 的文件内容是基于意见的,也可以使用空格,,但是因为那个&#39;在类中我必须使用{ }的数组。在文本文件中,我还应删除collisionTiles大括号。无论如何,我感谢你的任何反馈和代码示例与解释将是伟大的!

编辑: 那么我应该像这样将瓷砖添加到public int[,] LoadLevelData(string filename) { using (var streamReader = new StreamReader(filename)) { var serializer = new JsonSerializer(); Generate((int[,])serializer.Deserialize(streamReader, typeof(int[,])), 64); return (int[,])serializer.Deserialize(streamReader, typeof(int[,])); } } 吗?

gem install foo

2 个答案:

答案 0 :(得分:4)

  

那你怎么看待这个?什么样的解决方案最好?

此问题的最佳解决方案实际上取决于您计划编辑您的关卡的方式。

使用您当前的方法(在代码中存储关卡数据),您可以手动手动编辑关卡。这并不引人注目,但对于小型游戏来说它是可以管理的。

下一种方法是在文本文件中存储级别。前几天我answered a similar question对此进行了解释。许多游戏过去都使用过这种方法并取得了巨大的成功。

如果您想让事情变得非常简单,您可以获取现在获得的数据并使用JSON.NET对其进行反序列化。很酷的是,JSON中表示的数据看起来几乎与代码中显示的一样,可以由您喜欢的文本编辑器进行编辑。

package kb;

import com.example.kb.R;

import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.media.AudioManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputConnection;

public class SimpleIME extends InputMethodService implements
        OnKeyboardActionListener {

    private KeyboardView kv;
    private Keyboard keyboard;

    private boolean caps = false;

    @Override
    public View onCreateInputView() {
        kv = (KeyboardView) getLayoutInflater()
                .inflate(R.layout.keyboard, null);
        keyboard = new Keyboard(this, R.xml.qwerty);
        kv.setKeyboard(keyboard);
        kv.setOnKeyboardActionListener(this);
        return kv;
    }

    private void playClick(int keyCode) {
        AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
        switch (keyCode) {
        case 32:
            am.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
            break;
        case Keyboard.KEYCODE_DONE:
        case 10:
            am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
            break;
        case Keyboard.KEYCODE_DELETE:
            am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
            break;
        default:
            am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
        }
    }

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {

        InputConnection ic = getCurrentInputConnection();
        playClick(primaryCode);
        switch (primaryCode) {
        case Keyboard.KEYCODE_DELETE:
            ic.deleteSurroundingText(1, 0);
            break;
        case Keyboard.KEYCODE_SHIFT:
            caps = !caps;
            keyboard.setShifted(caps);
            kv.invalidateAllKeys();
            break;
        case Keyboard.KEYCODE_DONE:
            ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
                    KeyEvent.KEYCODE_ENTER));
            break;
        default:
            char code = (char) primaryCode;
            if (Character.isLetter(code) && caps) {
                code = Character.toUpperCase(code);
            }
            ic.commitText(String.valueOf(code), 1);
        }

    }

    @Override
    public void onPress(int primaryCode) {
    }

    @Override
    public void onRelease(int primaryCode) {
    }

    @Override
    public void onText(CharSequence text) {
    }

    @Override
    public void swipeDown() {
    }

    @Override
    public void swipeLeft() {
    }

    @Override
    public void swipeRight() 
    }

    @Override
    public void swipeUp() {
    }
}

加载方法也非常简单。

[
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
    [2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2],
    [2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2],
    [2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2],
    [2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2],
    [2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    [2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
]

要使用此方法,您可以将结果传递到 public int[,] LoadLevelData(string filename) { using (var streamReader = new StreamReader(filename)) { var serializer = new JsonSerializer(); return (int[,])serializer.Deserialize(streamReader, typeof(int[,])); } } 方法:

Generate

如果您总是使用64块大小,那么这样就可以了。但是,如果您愿意,也可以将块大小存储在JSON文件中。虽然这有点复杂,但对于这个答案来说可能太多了。 JSON.NET库有excellent documentation

由于您使用的是MonoGame,因此您可能希望使用TitleContainer.OpenStream而不是 // This is in my LoadContent() method var levelData = LoadLevelData(filename); map.Generate(levelData, 64); 来保持所有平台上的工作。或者,write a content reader可以MonoGame Pipeline tool,但这超出了此问题的范围。

您可能想要考虑的最后但并非最不重要的事情是使用第三方级编辑器软件,例如Tiled。在我的MonoGame.Extended图书馆或其他一些existing map loaders中有一个很棒的Tiled地图加载器可供选择。

无论你选择哪种方法,我的建议都是保持简单。每种方法都有优点和缺点,简单的解决方案通常是最容易上手的。一旦掌握了基础知识,您就可以升级到复杂的方法以获得额外的好处。

答案 1 :(得分:3)

加载级别和二进制数据,我想使用BinaryReaderBinaryWriter。对于数组,我首先写入大小,然后写入数据。像这样:

void Write(string fileName, int[,] data)
{
    using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileName)))
    {
        writer.Write(data.GetLength(0));
        writer.Write(data.GetLength(1));

        for (int x = 0; x < data.GetLength(0); x++)
        {
            for (int y = 0; y < data.GetLength(1); y++)
            {
                writer.Write(data[x, y]);
            }
        }
    }
}

int[,] Read(string fileName)
{
    using (BinaryReader reader = new BinaryReader(File.OpenRead(fileName)))
    {
        int width = reader.ReadInt32();
        int height = reader.ReadInt32();

        int[,] result = new int[width, height];

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                result[x, y] = reader.ReadInt32();
            }
        }

        return result;
    }
}