图片渲染知识 加载图片 查看EasyX文档,以加载test.jpg 为例
IMAGE img;
loadimage(&img, _T("test.jpg"));
渲染图片 putimage(100,200,&img);
//图片左上角坐标
动画基础 动画的数据逻辑 确保动画序列帧的能够间隔固定的时间进行切换,类比定时器的概念实现一个计数器
==使用static确保计数器只在第一个游戏帧被初始化为0 ==
通过if判断语句使得每五个游戏帧切换一个动画帧;随后考虑到动画帧序列播放结束后的行为,当动画的帧索引到达动画帧总数时,将索引重置为0,从而使得动画循环播放
定义const int PLAYER_ANIM_NUM = 6;
使动画循环播放,可书写下列代码
1 2 3 4 if (++counter % 5 == 0 ) index_current_anim++; index_current_anim = index_current_anim % PLAYER_ANIM_NUM;
动画的渲染 首先将每一张图片加载到程序中
定义IMAGE对象数组
IMAGE img_player_left[PLAYER_ANIM_NUM];
IMAGE img_player_right[PLAYER_ANIM_NUM];
图片命名十分有规律可使用循环来加载图片.在使用Unicode字符集的情况下,可以使用wstring来拼凑出文件的路径,再传递给loadimage函数,将图片传递到数组中
1 2 3 4 5 6 7 8 9 10 11 12 13 void LoadAnimation () { for (size_t i = 0 ; i < PLAYER_ANIM_NUM; i++) { std::wstring path = L"img/paimon_left_" + std::to_wstring (i) + L".png" ; loadimage (&img_paimon_left[i], path.c_str ()); } for (size_t i = 0 ; i < PLAYER_ANIM_NUM; i++) { std::wstring path = L"img/paimon_right_" + std::to_wstring (i) + L".png" ; loadimage (&img_paimon_right[i], path.c_str ()); } }
之前定义的动画帧索引此时边可以直接当做IMAGE数组的索引来使用
putimage(500, 500, &img_paimon_left[index_current_anim]);
处理带有透明度的图片素材 解决思路:类比putimage函数封装一个putimage_alpha函数,借助系统绘图函数比较轻巧的实现
1 2 3 4 5 6 7 8 9 #pragma comment(lib, "MSIMG32.LIB" ) inline void putimage_alpha (int x, int y, IMAGE* img) { int w = img->getwidth (); int h = img->getheight (); AlphaBlend (GetImageHDC (NULL ), x, y, w, h, GetImageHDC (img), 0 , 0 , w, h, { AC_SRC_OVER,0 ,255 ,AC_SRC_ALPHA }); }
将函数替换putimage_alpha(500, 500, &img_paimon_left[index_current_anim]);
键盘控制角色移动 常规思路 定义POINT player_pos = { 500, 500 };
用来存储玩家的位置,并将动画渲染的位置改为player_pos的坐标putimage_alpha(player_pos.x, player_pos.y, &img_paimon_left[index_current_anim]);
定义速度 int PLAYER_SPEED = 5;
在游戏主循环中通过函数判断键盘按下状态从而实现角色移动效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (msg.message == WM_KEYDOWN){ switch (msg.vkcode) { case 'W' : player_pos.y -= PLAYER_SPEED; break ; case 'S' : player_pos.y += PLAYER_SPEED; break ; case 'A' : player_pos.x -= PLAYER_SPEED; break ; case 'D' : player_pos.x += PLAYER_SPEED; break ; } }
问题
断续:由于当我们按下方向键时,WM_KEYDOWN消息进入事件队列,当保持按键按下一定时间后才会有接连不断的消息被触发
卡顿:keydown消息的产生与游戏主循环异步进行,且触发的频率与操作系统和硬件设备相关,导致有些游戏帧中时间处理部分对多个WM_KEYDOWN消息进行了处理,其余游戏帧中KEY_DOWN较少或几乎没有,导致角色在某些游戏帧中走得远有些走得近
实际需求:按键按下时,保证角色在某一个游戏帧中都能连续的移动;即玩家按键按下时,KEYD_DOWN消息触发,标志着角色开始移动,玩家按键抬起时,KEYUP消息触发,标志着角色结束移动
解放方法 解决思路:定义布尔变量标识玩家运动状态,不直接对玩家的位置数据进行操作而是改变布尔变量的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 bool is_move_up = false ;bool is_move_down = false ;bool is_move_left = false ;bool is_move_right = false ;if (msg.message == WM_KEYDOWN){ switch (msg.vkcode) { case 'W' : is_move_up = true ; break ; case 'S' : is_move_down = true ; break ; case 'A' : is_move_left = true ; break ; case 'D' : is_move_right = true ; break ; } } else if (msg.message == WM_KEYUP){ switch (msg.vkcode) { case 'W' : is_move_up = false ; break ; case 'S' : is_move_down = false ; break ; case 'A' : is_move_left = false ; break ; case 'D' : is_move_right = false ; break ; } } if (is_move_up) player_pos.y -= PLAYER_SPEED;if (is_move_down) player_pos.y += PLAYER_SPEED;if (is_move_left) player_pos.x -= PLAYER_SPEED;if (is_move_right) player_pos.x += PLAYER_SPEED;
优化动画数据逻辑 思路:由于代码相似,可使用类的思想进行封装,定义类用于封装动画相关的数据和逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Animation {public : Animation (LPCTSTR path, int num, int interval){ interval_ms = interval; TCHAR path_file[256 ]; for (size_t i = 0 ; i < num; i++) { _stprintf_s(path_file, path, i); IMAGE* frame = new IMAGE (); loadimage (frame, path_file); frame_list.push_back (frame); } } ~Animation (){ for (size_t i = 0 ; i < frame_list.size (); i++) { delete frame_list[i]; } } void Play (int x, int y, int delta) { timer += delta; if (timer >= interval_ms) { idx_frame = (idx_frame + 1 ) % frame_list.size (); timer = 0 ; } putimage_alpha (x, y, frame_list[idx_frame]); } private : int timer = 0 ; int idx_frame = 0 ; int interval_ms = 0 ; std::vector<IMAGE*> frame_list; }; Animation anim_left_player (_T("img/paimon_left_%d.png" ), 6 , 45 ) ;Animation anim_right_player (_T("img/paimon_right_%d.png" ), 6 , 45 ) ;