如何将Editline用于游戏机控制台?


9

我想向我正在创建的C ++游戏中添加游戏机控制台。尽管渲染了控制台并解析了我擅长的命令,但是文本输入和编辑方面(例如,处理向左/向右键,退格键等)似乎要花很多精力,我宁愿花在更有趣的事情上。

我设法找到了一个名为Editline的库,该库可处理具有历史记录和自动完成功能的控制台式文本输入的精髓,但是我不确定如何将其与游戏接口-特别是所有示例我发现使用标准输入和输出句柄,readline()它本身是一个阻塞调用。我需要能够提供自己的输入并将控制台呈现为自己的输出,并且还需要确保游戏循环继续运行。

我应该怎么做呢?

Answers:


4

Editline库(和GNU readline)仅与终端功能一起使用。要将这些库与您的游戏机控制台一起使用,您首先需要实现一个终端模拟器(TTY)。

取而代之的是(因为这样做对于游戏来说是很疯狂的),我建议您实现光标移动并自行编辑。我曾经写过一个C ++类来为我自己的游戏机控制台处理这个问题。该类恰当地命名为EditLine。该类处理基本的光标移动,前进/后退字符/单词/行删除和历史记录。没什么,但是您可能会发现它很有用。文档稀疏或不存在,希望您能以任何方式解决它。不幸的是,没有自动完成。我使用此类在上层实现了这一点。

editline.h

#include <string>
#include <vector>

class EditLine {
public:
    EditLine();
    EditLine(const EditLine&);

    ~EditLine();

    // assignment operator
    EditLine& operator=(const EditLine&);

    // comparison operators
    bool operator==(const EditLine&) const;
    bool operator!=(const EditLine&) const;

    // edit commands
    void accept_line();
    void reject_line();
    void insert_char(int);
    void delete_char();
    void backward_delete_char();
    void delete_word();
    void backward_delete_word();
    void delete_line();

    // motion commands
    void beginning_of_line();
    void end_of_line();
    void forward_char();
    void backward_char();
    void forward_word();
    void backward_word();

    // history commands
    void next_history();
    void previous_history();

    // accessors
    int history_pos() const;
    inline size_t history_size() const;
    inline bool empty() const;
    inline const std::string& line() const;
    inline size_t length() const;
    inline const std::string::size_type& cursor_pos() const;

    // mutators
    void set_line(const std::string& s);
    void set_cursor_pos(std::string::size_type);
    void reset_line();

private:
    std::string line_;
    std::string::size_type cursor_pos_;
    std::vector< std::string > history_;
    std::vector< std::string >::iterator last_iter_;
};

inline size_t EditLine::history_size() const { return history_.size(); }
inline const std::string& EditLine::line() const { return line_; }
inline const std::string::size_type& EditLine::cursor_pos() const { return cursor_pos_; }
inline bool EditLine::empty() const { return line_.empty(); }
inline size_t EditLine::length() const { return line_.length(); }

editline.cpp

#include "editline.h"
#include "string_utils.h"
#include <string>

namespace {
    const std::string word_delims = " !@#$%^&*()+-={}[]:\"|;'\\<>?,./";
} // namespace

EditLine::EditLine()
    : line_(std::string()),
      cursor_pos_(0),
      last_iter_(history_.end())
{

}

EditLine::EditLine(const EditLine& rhs)
{
    *this = rhs;
}

EditLine::~EditLine()
{

}

EditLine& EditLine::operator=(const EditLine& rhs)
{
    line_ = rhs.line_;
    cursor_pos_ = rhs.cursor_pos_;
    history_ = rhs.history_;
    if (rhs.last_iter_ == rhs.history_.end())
        last_iter_ = history_.end();
    else {
        last_iter_ = history_.begin();
        std::advance(last_iter_, rhs.last_iter_ - rhs.history_.begin());
    }
    return *this;
}

void EditLine::set_line(const std::string& s)
{
    line_ = s;
    cursor_pos_ = line_.size();
}

void EditLine::set_cursor_pos(std::string::size_type pos)
{
    if (pos > line_.size())
        pos = line_.size();
    cursor_pos_ = pos;
}

void EditLine::reset_line()
{
    line_.clear();
    cursor_pos_ = 0;
}

bool EditLine::operator==(const EditLine& rhs) const
{
    return (line_         == rhs.line_       &&
            cursor_pos_   == rhs.cursor_pos_ &&
            history_      == rhs.history_    &&
            history_pos() == rhs.history_pos());
}

bool EditLine::operator!=(const EditLine& rhs) const
{
    return !operator==(rhs);
}

void EditLine::accept_line()
{
    if (!line_.empty())
        history_.push_back(line_);

    line_.clear();
    cursor_pos_ = 0;
    last_iter_ = history_.end();
}

void EditLine::reject_line()
{
    line_.clear();
    cursor_pos_ = 0;
    last_iter_ = history_.end();
}

void EditLine::insert_char(int c)
{
    line_.insert(cursor_pos_, 1, c);
    cursor_pos_++;
}

void EditLine::delete_char()
{
    line_.erase(cursor_pos_, 1);
}

void EditLine::backward_delete_char()
{
    if (cursor_pos_ > 0) {
        line_.erase(cursor_pos_ - 1, 1);
        cursor_pos_--;
    }
}

void EditLine::delete_word()
{
    std::string::size_type pos;

    // check if cursor points on a word delim
    if (word_delims.find(line_[cursor_pos_]) != std::string::npos) {
        // cursor points on a word delim - remove everything from here to
        // right up to first delim after this word.
        pos = line_.find_first_not_of(word_delims, cursor_pos_+1);
        if (pos != std::string::npos)
            pos = line_.find_first_of(word_delims, pos+1);
    } else {
        // cursor is in the middle of a word - remove everything up to first
        // delim.
        pos = line_.find_first_of(word_delims, cursor_pos_+1);
    }

    if (pos != std::string::npos)
        // removes everything right of cursor up to 'pos'
        line_.replace(cursor_pos_, pos - cursor_pos_, "");
    else
        // removes everthing right of cursor
        line_.erase(cursor_pos_);
}

void EditLine::backward_delete_word()
{
    std::string::size_type pos;

    if (cursor_pos_ == 0) return;

    // check if char left of cursor is a word delim
    if (word_delims.find(line_[cursor_pos_-1]) != std::string::npos) {
        // left of cursor is a word delim - remove everything from left of
        // cursor up to next word delim before current word.
        pos = rfind_first_not_of(line_, word_delims, cursor_pos_-1);
        if (pos != std::string::npos)
            pos = rfind_first_of(line_, word_delims, pos);
    } else {
        // left of cursor is not a word delim - remove everything left of
        // cursor up to next word delim.
        pos = rfind_first_of(line_, word_delims, cursor_pos_-1);
    }

    if (pos != std::string::npos) {
        // removes from right of pos and up to cursor
        line_.erase(pos + 1, cursor_pos_ - pos - 1);
        cursor_pos_ = pos + 1;
    } else {
        // removes everything from beginning up to cursor
        line_.erase(0, cursor_pos_);
        cursor_pos_ = 0;
    }
}

void EditLine::delete_line()
{
    line_.erase(cursor_pos_);
    cursor_pos_ = line_.size();
}

void EditLine::beginning_of_line()
{
    cursor_pos_ = 0;
}

void EditLine::end_of_line()
{
    cursor_pos_ = line_.size();
}

void EditLine::forward_char()
{
    if (cursor_pos_ < line_.size())
        cursor_pos_++;
}

void EditLine::backward_char()
{
    if (cursor_pos_ > 0)
        cursor_pos_--;
}

void EditLine::forward_word()
{
    std::string::size_type pos;

    pos = line_.find_first_of(word_delims, cursor_pos_);

    if (pos != std::string::npos)
        cursor_pos_ = pos + 1;
    else
        cursor_pos_ = line_.size();
}

void EditLine::backward_word()
{
    if (cursor_pos_ <= 1) {
        cursor_pos_ = 0;
        return;
    }

    std::string::size_type pos, cursor_pos;

    cursor_pos = cursor_pos_;

    if (cursor_pos >= 2)
        cursor_pos -= 2;

    bool found = false;

    for (int i = (int) cursor_pos; i >= 0; --i) {
        if (word_delims.find(line_[i]) != std::string::npos) {
            pos = (std::string::size_type) i;
            found = true;
            break;
        }
    }

    if (found)
        cursor_pos_ = pos + 1;
    else
        cursor_pos_ = 0;
}

void EditLine::next_history()
{
    if (!history_.empty()) {
        if (last_iter_ == history_.end() || ++last_iter_ == history_.end())
            last_iter_ = history_.begin();
        line_ = *last_iter_;
        cursor_pos_ = line_.size();
    }
}

void EditLine::previous_history()
{
    if (!history_.empty()) {
        if (last_iter_ != history_.begin())
            --last_iter_;
        else
            last_iter_ = --history_.end();

        line_ = *last_iter_;
        cursor_pos_ = line_.size();
    }
}

int EditLine::history_pos() const
{
    if (last_iter_ == history_.end())
        return -1;
    return last_iter_ - history_.begin();
}

string_utils.h

std::string::size_type rfind_first_not_of(const std::string& s,
                                          const std::string& delims,
                                          std::string::size_type pos);

std::string::size_type rfind_first_of(const std::string& s,
                                      const std::string& delims,
                                      std::string::size_type pos);

string_utils.cpp

std::string::size_type rfind_first_not_of(const std::string& s,
        const std::string& delims, std::string::size_type pos)
{
    std::string::size_type p = pos;
    while (delims.find(s[p]) != std::string::npos) {
        if (p == 0) // not found
            return std::string::npos;
        --p;
    }
    return p;
}

std::string::size_type rfind_first_of(const std::string& s,
        const std::string& delims, std::string::size_type pos)
{
    std::string::size_type p = pos;
    while (delims.find(s[p]) == std::string::npos) {
        if (p == 0) // not found
            return std::string::npos;
        --p;
    }
    return p;
}

0

看起来并没有太大的问题。您需要了解的第一件事是标准输入和标准输出只是流-如果需要,可以用您自己的流替换它们。SO和AFAIK对此有一个疑问,仅有的两种方法是进入Editline代码并将其更改为使用您的流,或者使用cin.rdbuf()cout.rdbuf()-这些是由标准库提供的,可以在此处找到文档。就个人而言,我建议您使用后者-它不算老套,只需要在游戏中添加两行代码即可。


除了libedit还取决于ncurses并需要在其下执行完整的终端(TTY)实施...
Oskar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.