Lỗi Game qua đường trên console

Em có làm thử một game cơ bản. Nhưng test tới đoạn này khi mà cho người di chuyển nó cứ giật giật. Sửa hoài mà không được :persevere:. Ai biết lỗi chỉ giúp em với. Em cảm ơn nhiều.

#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <time.h>
#include "console.h"

struct ToaDo
{
	int x, y;
}; 

struct HinhDang
{
	char a[3][3];
};
struct Human
{
	ToaDo toado;
	HinhDang hinhdang;
};


struct Xe
{
	ToaDo toado;
	HinhDang hinhdang;
};

//khởi taọ người
void KhoiTao(Human &X,Xe &C)
{
#define hdX X.hinhdang.a
#define hdC C.hinhdang.a

	X.toado.x = 40;
	X.toado.y = 23;
	C.toado.x = 1;
	C.toado.y = 19;
	hdX[0][0] = hdX[2][0] = hdX[1][2]= ' ';
	hdX[1][1] = hdX[0][2] = hdX[2][2] = '|';
	hdX[0][1] = hdX[2][1] = '-';
	hdX[1][0] = '@';
	hdC[0][0] = hdC[2][0] = hdC[0][2] = hdC[2][2] = 'O';
	hdC[1][0] = hdC[1][2] = '-';
	hdC[0][1] = '|'; hdC[2][1] = '>';
	hdC[1][1] = ' ';

}
//hiển thị người
void HienThi(Human X, Xe C)
{
	clrscr();

	for (int kDong = -1; kDong <= 1; kDong++)
		for (int kCot = -1; kCot <= 1; kCot++)
		{
			int x = kCot + C.toado.x;
			int y = kDong + C.toado.y;

			gotoXY(x, y);
			putchar(C.hinhdang.a[kCot + 1][kDong + 1]);	//a[0][0] --> a[2][2]
		}

	for (int kDong = -1; kDong <= 1; kDong++)
		for (int kCot = -1; kCot <= 1; kCot++)
		{
			int x = kCot + X.toado.x;
			int y = kDong + X.toado.y;

			gotoXY(x, y);
			putchar(X.hinhdang.a[kCot + 1][kDong + 1]);	//a[0][0] --> a[2][2]
		}
	
}


//điều khiển người di chuyển
void DieuKhienMan(Human &X)
{
	if (_kbhit())
	{
		int key = _getch();

		if (key == 'A' || key == 'a')
			X.toado.x --;

		else if (key == 'D' || key == 'd')
			X.toado.x ++;
	
		else if (key == 'W' || key == 'w')
			X.toado.y --;
	
		else if (key == 'S' || key == 's')
			X.toado.y ++;
	
	}
		
} 


void main()
{
	Human X; Xe C;
	KhoiTao(X,C);
	while(TRUE)
	{
		HienThi(X,C);
		DieuKhienMan(X);
		C.toado.x++;
		Sleep (200);
		
	}

}

p/s: Cái thư viện console em chỉ dùng mỗi hàm xóa màn hình clrscr() chứ không ảnh hưởng gì đâu ạ. Với lại em là người mới nên cũng chưa biết cách sử dụng giao diện của web mình. Mọi người thông cảm ạ.

1 Like

Vẽ đè lên vị trí cũ thì cần gì dùng đến clrscr().:smiling_imp:

2 Likes

Nếu không xóa thì khi di chuyển nó sẽ để lại hình cũ á bạn

giật thì phải chịu thôi em à. Nếu em muốn làm nó bớt giật thì 1 là phải đọc winapi phần double buffer gì đó rất mất công, mà kết quả đạt được cũng chỉ là bớt giật chứ ko loại hẳn :V

nếu em muốn làm game trên console thì em nghiên cứu thử pdcurses :V Nhưng cũng mất công, thà em làm game trên SDL2 hay SFML thì dễ hơn…

6 Likes

Dạ em cảm ơn. Tại đây là đồ án bắt buộc của em phải làm trên console :sleepy:

1 Like

Game hồi trước mình làm không có bị giựt :slight_smile:

Đây là cách làm ↓ (ảnh chụp lại comment mình đã trả lời cho 1 bạn trên youtube)

6 Likes

thử thêm 2 file này vô project là ngon lành:

fastconrend.h

#pragma once

#include <cstdio>
#include <windows.h>

namespace fcr
{
enum ConsoleColor {
    Black,
    DarkBlue,
    DarkGreen,
    DarkCyan,
    DarkRed,
    DarkMagenta,
    DarkYellow,
    Gray,
    DarkGray,
    Blue,
    Green,
    Cyan,
    Red,
    Magenta,
    Yellow,
    White
};

struct Window {
    HANDLE hStdOut;
    CHAR_INFO* outBuf;
    COORD wdSize, bufCoord;
    SMALL_RECT srctWriteRect;
    WORD color;
    int iw, outBufSize;

    ~Window();
};

void init();
void settitle(const wchar_t* title);
void setsize(int w, int h);
void setfont(const wchar_t* fontName, int fontSizeX, int fontSizeY);

void gotoxy(int x, int y);
void clrscr();
int textcolor();
int backcolor();
void setcolor(int textcolor, int backcolor);
void settextcolor(int textcolor);
void setbackcolor(int backcolor);
void cursor(bool visible);

void putchar(char c);
void printf(const char* format, ...);
void putwchar(wchar_t wc);
void wprintf(const wchar_t* format, ...);

void writebuffer();
} // namespace fcr

fastconrend.cpp

#include "fastconrend.h"
#include <algorithm>

namespace fcr
{
////////////////////////////////
/// GLOBAL VARIABLE `window` ///
////////////////////////////////
Window window;

Window::~Window()
{
    cursor(true);
    setcolor(Gray, Black);
    free(window.outBuf);
}

void makeNewBuffer(int w, int h)
{
    free(window.outBuf);

    window.outBufSize = w * h;
    window.outBuf = (CHAR_INFO*)calloc(window.outBufSize, sizeof(CHAR_INFO));
    if (!window.outBuf)
    {
        perror("Error allocating output buffer\n");
        exit(1);
    }
    for (int i = 0; i < window.outBufSize; ++i)
        window.outBuf[i].Attributes = window.color;
}

void init()
{
    window.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (window.hStdOut == INVALID_HANDLE_VALUE)
    {
        perror("Error getting stdout handle\n");
        exit(1);
    }
    window.bufCoord.X = window.bufCoord.Y = 0;
    window.srctWriteRect.Top = 0;
    window.srctWriteRect.Left = 0;
    window.iw = 0;

    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if (!GetConsoleScreenBufferInfo(window.hStdOut, &csbi))
    {
        perror("Error `GetConsoleScreenBufferInfo`\n");
        exit(1);
    }

    window.outBuf = NULL;
    makeNewBuffer(csbi.dwSize.X, csbi.dwSize.Y);
    window.wdSize = csbi.dwSize;
    window.srctWriteRect.Bottom = window.wdSize.Y - 1;
    window.srctWriteRect.Right = window.wdSize.X - 1;
    SetConsoleWindowInfo(window.hStdOut, true, &window.srctWriteRect);
    window.color = csbi.wAttributes;
}

void settitle(const WCHAR* title)
{
    SetConsoleTitleW(title);
}

void setsize(int w, int h)
{
    char changeWindowSizeMsg[64];
    sprintf(changeWindowSizeMsg, "mode con: cols=%d lines=%d", w, h);
    system(changeWindowSizeMsg); //// system call /////

    makeNewBuffer(w, h);
    window.wdSize.X = w;
    window.wdSize.Y = h;
    window.srctWriteRect.Bottom = window.wdSize.Y - 1;
    window.srctWriteRect.Right = window.wdSize.X - 1;
    SetConsoleWindowInfo(window.hStdOut, true, &window.srctWriteRect);
}

void gotoxy(int x, int y)
{
    window.iw = x + y * window.wdSize.X;
    window.iw %= window.outBufSize; // prevent overflow
}

void clrscr()
{
    for (int i = 0; i < window.outBufSize; ++i)
    {
        window.outBuf[i].Attributes = window.color;
        window.outBuf[i].Char.AsciiChar = ' ';
    }
    gotoxy(0, 0);
}

int textcolor()
{
    return window.color & 0xF;
}

int backcolor()
{
    return (window.color >> 4) & 0xF;
}

void setcolor(int textColor, int backColor)
{
    window.color = (backColor << 4) + textColor;
}

void settextcolor(int textColor)
{
    setcolor(textColor, backcolor());
}

void setbackcolor(int backColor)
{
    setcolor(textcolor(), backColor);
}

void cursor(bool visible)
{
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(window.hStdOut, &cursorInfo);
    cursorInfo.bVisible = visible; // set the cursor visibility
    SetConsoleCursorInfo(window.hStdOut, &cursorInfo);
}

void putchar(char c)
{
    window.outBuf[window.iw].Char.AsciiChar = c;
    window.outBuf[window.iw].Attributes = window.color;
    ++window.iw;
    window.iw %= window.outBufSize; // prevent overflow
}

void printf(const char* fmt, ...)
{
    static char buff[1024];
    va_list args;
    va_start(args, fmt);
    vsprintf(buff, fmt, args);
    va_end(args);

    // Write buff to outBuf
    int wdX = window.wdSize.X;
    for (char* ptr = buff; *ptr; ++ptr)
    {
        if (*ptr == '\b')
            --window.iw;
        else if (*ptr == '\r')
            window.iw = window.iw / wdX * wdX;
        else if (*ptr == '\n')
            window.iw = (window.iw / wdX + 1) * wdX;
        else
            putchar(*ptr);
        window.iw %= window.outBufSize; // prevent overflow
    }
}

void putwchar(wchar_t wc)
{
    window.outBuf[window.iw].Char.UnicodeChar = wc;
    window.outBuf[window.iw].Attributes = window.color;
    ++window.iw;
    window.iw %= window.outBufSize; // prevent overflow
}

void wprintf(const wchar_t* fmt, ...)
{
    static wchar_t buff[1024];
    va_list args;
    va_start(args, fmt);
    vswprintf_s(buff, 1024, fmt, args);
    va_end(args);

    // Write buff to outBuf
    int wdX = window.wdSize.X;
    for (wchar_t* ptr = buff; *ptr; ++ptr)
    {
        if (*ptr == L'\b')
            --window.iw;
        else if (*ptr == L'\r')
            window.iw = window.iw / wdX * wdX;
        else if (*ptr == L'\n')
            window.iw = (window.iw / wdX + 1) * wdX;
        else
            putwchar(*ptr);
        window.iw %= window.outBufSize; // prevent overflow
    }
}

void writebuffer()
{
    WriteConsoleOutputW(window.hStdOut,  // screen buffer to write to
                        window.outBuf,   // buffer to copy from
                        window.wdSize,   // col-row size of chiBuffer
                        window.bufCoord, // top left src cell in chiBuffer
                        &window.srctWriteRect);
}

void setfont(const wchar_t* fontName, int fontSizeX, int fontSizeY)
{
    CONSOLE_FONT_INFOEX cfi;
    cfi.cbSize = sizeof cfi;
    cfi.nFont = 0;
    cfi.dwFontSize.X = fontSizeX;
    cfi.dwFontSize.Y = fontSizeY;
    cfi.FontFamily = FF_DONTCARE;
    cfi.FontWeight = FW_NORMAL;
    wcscpy(cfi.FaceName, fontName);
    if (!SetCurrentConsoleFontEx(window.hStdOut, false, &cfi))
    {
        perror("Cannot set font\n");
        exit(1);
    }
}

} // namespace fcr

(code copy xài thoải mái license cho đại là MIT vậy :V)

file main chỉ cần chỉnh sửa chút xíu: putchar, clrscr, gotoxy thêm namespace fcr:: vào, trong main thì cần nhớ khởi tạo cho nó bằng cách gọi fcr::init();, rồi có thể gọi fcr::cursor(false); để ẩn dấu _ đi, fcr::setsize(80, 25); để set kích thước màn hình cho đúng 80x25 ký tự, thậm chí đổi font cũng được: fcr::setfont(L"Consolas", 8, 16);

à quên :V vì xài 1 buffer riêng (double buffer) nên mỗi lần vẽ thật ra chỉ là chép ký tựu vào mảng 2 chiều chứ ko phải in ra màn hình, muốn in ra màn hình toàn bộ buffer phụ thì phải gọi fcr::writebuffer();

#include "fastconrend.h"
#include <conio.h>
#include <cstdio>
#include <ctime>
#include <windows.h>

struct ToaDo {
    int x, y;
};
struct HinhDang {
    char a[3][3];
};
struct Human {
    ToaDo toado;
    HinhDang hinhdang;
};
struct Xe {
    ToaDo toado;
    HinhDang hinhdang;
};

// khởi taọ người
void KhoiTao(Human& X, Xe& C)
{
#define hdX X.hinhdang.a
#define hdC C.hinhdang.a

    X.toado.x = 40;
    X.toado.y = 23;
    C.toado.x = 1;
    C.toado.y = 19;
    hdX[0][0] = hdX[2][0] = hdX[1][2] = ' ';
    hdX[1][1] = hdX[0][2] = hdX[2][2] = '|';
    hdX[0][1] = hdX[2][1] = '-';
    hdX[1][0] = '@';
    hdC[0][0] = hdC[2][0] = hdC[0][2] = hdC[2][2] = 'O';
    hdC[1][0] = hdC[1][2] = '-';
    hdC[0][1] = '|';
    hdC[2][1] = '>';
    hdC[1][1] = ' ';
}

// hiển thị người
void HienThi(Human X, Xe C)
{
    fcr::clrscr();

    for (int kDong = -1; kDong <= 1; kDong++)
        for (int kCot = -1; kCot <= 1; kCot++)
        {
            int x = kCot + C.toado.x;
            int y = kDong + C.toado.y;

            fcr::gotoxy(x, y);
            fcr::putchar(C.hinhdang.a[kCot + 1][kDong + 1]);
        }

    for (int kDong = -1; kDong <= 1; kDong++)
        for (int kCot = -1; kCot <= 1; kCot++)
        {
            int x = kCot + X.toado.x;
            int y = kDong + X.toado.y;

            fcr::gotoxy(x, y);
            fcr::putchar(X.hinhdang.a[kCot + 1][kDong + 1]);
        }
}

//điều khiển người di chuyển
void DieuKhienMan(Human& X)
{
    if (_kbhit())
    {
        int key = _getch();
        if (key == 'A' || key == 'a')
            X.toado.x--;
        else if (key == 'D' || key == 'd')
            X.toado.x++;
        else if (key == 'W' || key == 'w')
            X.toado.y--;
        else if (key == 'S' || key == 's')
            X.toado.y++;
    }
}


int main()
{
    fcr::init();
    fcr::cursor(false);
    fcr::setsize(80, 25);
    Human X;
    Xe C;
    KhoiTao(X, C);
    while (true)
    {
        HienThi(X, C);
        DieuKhienMan(X);
        C.toado.x++;
        fcr::writebuffer();
        Sleep(16);
    }
}

cho Sleep(16) tức là khoảng 60fps nó chạy vù vù :V :V :V

2019-04-05_21-25-12

nếu tăng kích thước màn hình ~ 10k ký tự thì lâu lâu nó cũng giựt mà ít :frowning: Muốn trị hết thì qua pdcruses nó xài SDL giả lập console gì đó :V

8 Likes

Ý tưởng này hay quá để em thử. Cảm ơn anh.

Cái Sleep là để em chỉnh tốc độ của xe mà nếu xe đi nhanh quá thì không được á anh. Mà em cảm ơn anh nhiều lắm. Anh chỉ tận tình ghê ^^

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?