实现效果

制作步骤

一、用Pr把视频导出为帧图片和mp3

1.打开Pr,按ctrl+i导入需要制成字符动画的原视频
导入.png
2.按ctrl+m导出为jpg图片,记得存进一个文件夹里,因为会导出大量图片,例如一个三分多钟六十帧的badApple就有一万三千多帧。
导出.png
3.按ctrl+m导出为mp3,后期程序播放音频会用到
等待导出完成。

二、用ascgen2把帧图制成字符文本

1.选择帧图所在文件夹,导入所有图片(安装包在最下面有)
批量制作字符文本1.png
批量制作字符文本2.png
批量制作字符文本3.png
2.修改字符文本的一些属性,或者默认也可
批量制作字符文本4.png
3.修改分辨率和导出路径,点击导出
批量制作字符文本5.png
等待导出完成

三、编写程序,读取并打印文本

代码部分其实很简单,逻辑上只要做读取文件,播放音乐,并每间隔$1/60$秒打印输出一帧的字符即可,而在开始播放前,需要做一些初始化处理,如隐藏光标,调整控制台大小等:

void init(){
    //隐藏光标
    CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); 
    SetConsoleTitle("字符badApple");          //控制台标题 
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SMALL_RECT rc = {0,0, MAXCOL, MAXROW};  // 重置窗口位置和大小
    SetConsoleWindowInfo(hOut,true ,&rc); 
}

完整代码

#include<iostream>
#include<windows.h>
#include<ctime>
#include<string>
#include<cstdio>
#include<mmsystem.h>
#pragma comment(lib, "Winmm.lib")
#define MAXCOL 150
#define MAXROW 66
#define ZERO SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),{0,0})

// 150: 150x66
//100:  100x44
using namespace std;

char map[200][200];
void init(){
    //隐藏光标
    CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); 
    SetConsoleTitle("字符badApple");          //控制台标题 
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SMALL_RECT rc = {0,0, MAXCOL, MAXROW};  // 重置窗口位置和大小
    SetConsoleWindowInfo(hOut,true ,&rc); 
}

string getName(int num){
    string fileSrcName = "badAppleTxt_150_60fps/ASCII-badApple60fps00000.txt";
    int tmp = num;
    int idx = fileSrcName.find('.')-1;

    while(num!=0){
        fileSrcName[idx--] = num%10+'0';
        num/=10;
    }
    return fileSrcName;
}
int main(){
    FILE *fp;
    char chs[200];
    int beginIdx = 4000;

    string fileName;
    int sum,x,y;
    cout << "请输入任意键开始" << endl;
    getchar();
    init();system("mode con cols=150 lines=68");
    //PlaySound("badApple.wav",NULL,SND_ASYNC);
    mciSendString("play badApple.mp3", NULL, 0, NULL);
    system("cls");
    float cd = 1000/60.0f;      //每秒60帧 
    clock_t beginClock = clock();
    float totalCd = 0.0f;
    for(int idx = 0;idx < 14000;idx+=1){
        while(1){
            if(clock()>=beginClock+(int)totalCd){
                totalCd+=cd;
                //读取
                fileName.clear();
                fileName = getName(idx);
                if ((fp = fopen(fileName.c_str(), "r")) != NULL) {      //打开一帧 
                    x = 0;
                    while (!feof(fp)) {             //只要该帧画面没读取完,就继续循环 
                        fgets(chs, MAXCOL + 10, fp);
                        chs[MAXCOL] = '\0';
                        puts(chs);
                        //strcpy(map[x], chs);
                        x++;
                    }
                    fclose(fp);
                }
                else {
                    goto End;
                };
                break;
            }
        }
        ZERO;
    }
    End:

    return 0;
}


但这种直接读一帧输出一帧的方式会受cpu性能影响,可能出现音画不同步的情况,所以还有另一种输出方案,就是先把所有帧全部读取进数组,然后再播放输出:
先缓存再播放版本代码

#include<iostream>
#include<windows.h>
#include<ctime>
#include<string>
#include<cstdio>
#include<mmsystem.h>
#pragma comment(lib, "Winmm.lib")
#define MAXCOL 150
#define MAXROW 66
#define ZERO SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0,0})

// 150: 150x66
//100:  100x44
using namespace std;

char map[14000][200][200];
void init(){
    //隐藏光标
    CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); 
    SetConsoleTitle("字符badApple");          //控制台标题 
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SMALL_RECT rc = {0,0, MAXCOL, MAXROW};  // 重置窗口位置和大小
    SetConsoleWindowInfo(hOut,true ,&rc); 
}

string getName(int num){
    string fileSrcName = "badAppleTxt_150_60fps/ASCII-badApple60fps00000.txt";
    int tmp = num;
    int idx = fileSrcName.find('.')-1;

    while(num!=0){
        fileSrcName[idx--] = num%10+'0';
        num/=10;
    }
    return fileSrcName;
}
int main(){
    FILE *fp;
    char chs[200];
    int beginIdx = 4000;

    string fileName;
    int sum,x,y;

    for(sum = 0;sum < 14000;sum++){
        fileName.clear();
        fileName = getName(sum);
        if((fp = fopen(fileName.c_str(),"r"))!=NULL){       //打开一帧 
            x = 0;y = 0;
            while(!feof(fp)){               //只要该帧画面没输出完,就继续循环 
                fgets(chs,MAXCOL+10,fp);
                chs[MAXCOL] = '\0';
                strcpy(map[sum][x],chs);
                x++;
            }
            fclose(fp);
        }
        else {
            break;
        };
    }
    //输出
    cout << "请输入任意键开始" << endl;
    getchar();
    init();system("mode con cols=150 lines=67");
    //PlaySound("badApple.wav",NULL,SND_ASYNC);
    mciSendString("play badApple.mp3", NULL, 0, NULL);
    system("cls");
    double cd = 1000/60.0;      //每秒60帧 
    clock_t beginClock = clock();
    double totalCd = 0.0;
    for(int tmp = 0;tmp < sum;tmp++){
        while(1){
            if(clock()>=beginClock+(int)totalCd){
                totalCd+=cd;
                for( x = 0;x < MAXROW;x++){
                    puts(map[tmp][x]);
                }
                break;
            }
        }
        ZERO;
    }


    return 0;
}

注意需要使用c++11标准,且要引入多媒体函数库 -lwinmm。
这里我使用的是devc,引入如图
Snipaste_2020-12-26_11-18-58.jpg

下载链接

BadApple.rar
ascgen2.exe

最后修改:2020 年 12 月 26 日
如果觉得我的文章对你有用,请随意赞赏