实现效果
制作步骤
一、用Pr把视频导出为帧图片和mp3
1.打开Pr,按ctrl+i导入需要制成字符动画的原视频
2.按ctrl+m导出为jpg图片,记得存进一个文件夹里,因为会导出大量图片,例如一个三分多钟六十帧的badApple就有一万三千多帧。
3.按ctrl+m导出为mp3,后期程序播放音频会用到
等待导出完成。
二、用ascgen2把帧图制成字符文本
1.选择帧图所在文件夹,导入所有图片(安装包在最下面有)
2.修改字符文本的一些属性,或者默认也可
3.修改分辨率和导出路径,点击导出
等待导出完成
三、编写程序,读取并打印文本
代码部分其实很简单,逻辑上只要做读取文件,播放音乐,并每间隔$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,引入如图