[教程]字符动画制作
2023-06-13 09:16:13 时间
实现效果
https://www.bilibili.com/video/av75231006
bad apple!!
制作步骤
一、用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,引入如图
下载链接
相关文章
- pycharm编码设置为utf-8._python字符编码使用ascii编码对么
- 全面详解c语言使用cJSON解析JSON字符[通俗易懂]
- 剑指54-字符流中第一个不重复的字符
- mysql数据库去掉字符前/中/后的空格详解数据库
- 错误解决Oracle数据库中非法字符错误(oracle非法字符)
- 无重复字符的最长子串及最小覆盖子串(滑动窗口法)算法详解编程语言
- MySQL:获取字符串长度的技巧(mysql获取字符长度)
- 字符解析MSSQL中的转义字符(mssql转义)
- 如何将Oracle半角字符转换为全角字符(oracle半角转全角)
- 指定字符的数据行(mysql中删除不包含)
- Oracle中挖掘有趣数据的通配字符指南(oracle中通配字符)
- 串Oracle中如何提取字符串(oracle中取字符)
- 串Oracle优于字符串的数据库(oracle 不等于字符)
- Oracle一行的最长字符限制(oracle一行最长度)
- asp过滤非法字符函数
- java全角、半角字符的关系以及转换详解
- JavaScript实现查找字符串中第一个不重复的字符