ฉันจะใช้ Editline สำหรับคอนโซลในเกมได้อย่างไร


9

ฉันต้องการเพิ่มคอนโซลในเกมให้กับเกม C ++ ที่ฉันสร้าง แม้ว่าการแสดงผลคอนโซลและการแยกคำสั่งที่ฉันใช้ได้ แต่การป้อนข้อความและการแก้ไข (เช่นการจัดการปุ่มซ้าย / ขวา, แบ็กสเปซและอื่น ๆ ... ) ดูเหมือนจะใช้ความพยายามอย่างมาก .

ฉันจัดการเพื่อค้นหาไลบรารีชื่อEditlineที่จัดการป้อนข้อความสไตล์คอนโซลที่สมบูรณ์พร้อมประวัติและการเติมข้อมูลอัตโนมัติ แต่ฉันไม่แน่ใจว่าจะเชื่อมต่อสิ่งนี้กับเกมของฉันได้อย่างไรโดยเฉพาะตัวอย่างทั้งหมด ที่ฉันได้พบใช้จัดการอินพุตและเอาต์พุตมาตรฐานและreadline()ตัวเองเป็นสายการบล็อก ฉันต้องสามารถให้อินพุตของตัวเองและแสดงผลคอนโซลให้กับเอาต์พุตของตัวเองและฉันต้องแน่ใจว่าลูปเกมของฉันยังคงทำงานต่อไป

ฉันควรจะทำสิ่งนี้อย่างไร?

คำตอบ:


4

ไลบรารี Editline (และ readline GNU) ทำงานได้เฉพาะกับความสามารถของเทอร์มินัล หากต้องการใช้ไลบรารีเหล่านี้กับคอนโซลในเกมของคุณคุณต้องใช้เทอร์มินัลอีมูเลเตอร์ (TTY) ก่อน

แทน (เนื่องจากการทำเช่นนั้นสำหรับเกมจะบ้า) ฉันขอแนะนำให้คุณใช้การเคลื่อนไหวของเคอร์เซอร์และแก้ไขด้วยตัวเอง ฉันเคยเขียนคลาส C ++ เพื่อจัดการสิ่งนี้สำหรับคอนโซลในเกมของฉันเพื่อจุดประสงค์นี้ คลาสนี้มีชื่อว่า aptly 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

ดูเหมือนว่าปัญหาจะไม่มากเกินไป สิ่งแรกที่คุณต้องรู้คือมาตรฐานในและมาตรฐานเป็นเพียงกระแสข้อมูล - พวกเขาสามารถถูกแทนที่ด้วยของคุณเองถ้าต้องการ มีคำถามเกี่ยวกับเรื่องนี้เกี่ยวกับเรื่องนี้และ AFAIK มีเพียงสองวิธีเท่านั้นที่จะเข้าไปในโค้ดของ Editline และเปลี่ยนเป็นสตรีมของคุณหรือใช้cin.rdbuf()และcout.rdbuf()- ซึ่งมีให้โดยไลบรารีมาตรฐานและเอกสารสามารถพบได้ที่นี่ . โดยส่วนตัวแล้วฉันขอแนะนำให้ใช้ตัวหลัง - ไม่ใช่เรื่องแฮ็คและเกี่ยวข้องกับการเพิ่มโค้ด 2 บรรทัดลงในเกมของคุณเท่านั้น


ยกเว้นว่า libedit นั้นยังขึ้นอยู่กับ ncurses และต้องมีการใช้งานเทอร์มินัล (TTY) ที่สมบูรณ์ภายใต้ ...
Oskar N.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.