
童晶老师《C和C++游戏趣味编程》一书各个章节的案例代码,每章案例逐步利用学到的语法知识。
本章我们将编写贪吃蛇游戏,效果如图所示。键盘控制小蛇上下左右移动,吃到食物后长度加一;蛇头碰到自身或窗口边缘,游戏失败。
首先利用全局变量和函数的知识,设计了一个游戏开发框架;然后学习二维数组的知识,构造了地图和小蛇,实现了小蛇向四个方向移动;接着学习了静态变量的概念,进行了时间控制的改进;最后实现了失败判断与显示、吃食物增加长度的功能。
源码:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define BLOCK_SIZE 20 // 每个小格子的长宽大小
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 // 宽度上一共40个小格子
// 全局变量定义
int Blocks[HEIGH
...

童晶老师《C和C++游戏趣味编程》一书各个章节的案例代码,每章案例逐步利用学到的语法知识。
本章我们将实现一个见缝插针的游戏。按下空格键后发射一根针到圆盘上,所有针逆时针方向转动;如果新发射的针碰到已有的针,游戏结束。
首先进行了圆盘与针的绘制,利用三角函数实现了针的旋转;然后学习了一维数组的概念,并利用数组实现了多根针的效果;利用批量绘制函数改进了绘制效果;最后实现了针的发射与增加、游戏失败判断、得分与显示效果的改进。
源码:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int main()
{
const float Pi = 3.1415926; // Pi常量
int width = 800; // 画面宽度
int height = 600; // 画面高度
initgra
...

童晶老师《C 和 C++ 游戏趣味编程》一书各个章节的案例代码,每章案例逐步利用学到的语法知识。
本章我们将实现一个小球跳跃躲避方块的游戏。
首先学习字符的知识,按空格键控制小球起跳;然后学习矩形的绘制,利用逻辑运算符实现小球和方块的碰撞判断;接着学习整除、取余、类型转换,利用随机数增加游戏的趣味性;最后实现得分的计算显示、解决小球在空中起跳的问题。
源码:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main()
{
float width,height,gravity; // 游戏画面大小、重力加速度
float ball_x,ball_y,ball_vy,radius; // 小球圆心坐标、y方向速度、半径大小
float rect_left_x,rect_top_y,rect_width,rect_height,re
...
EasyX 自带的 putpixel 函数源自 Windows GDI 函数 SetPixel,由于要考虑裁剪区、缩放、原点坐标、坐标方向等等诸多因素,所以性能很低,在一些只要求速度的场合很不实用。这篇文章就教你写一个自己的画点函数。
总的思想,是通过直接操作显示缓冲区来避免额外的运算。
在 EasyX 绘图窗口的显示缓冲区中,每个点占用 4 个字节,用 DWORD 指针指向显示缓冲区就可以像一维数组一样访问了。然后就是将二维坐标 (x, y) 映射到一维数组中,很简单,y * 640 + x 就是。另外需要注意的是,显示缓冲区中颜色的保存和 COLORREF 相比,蓝色和红色是相反的,需要用 BGR 宏转换一下(BGR 宏执行两次就会还原为原值)。
然后,我们整理出画点和读点的函数:
(记得要获取显示缓冲区指针并保存为全局变量)
DWORD* g_pBuf;
// 在 main 函数中
g_pBuf = GetImageBuffer();
void
...
常见的屏幕分辨率很奇怪,为什么总用一些不零不整的数字?比如以前最常见的分辨率是 640 x 480,当初为什么这么设计?
因为这个分辨率运算速度比较快,人也相对比较好记。
大家知道,屏幕上的每一个点,都会对应内存中的一个位置。比如 640x480@256色,表示每个点占用一个字节,整个屏幕有 640 x 480 = 300k 个点,占用 300KB 内存。屏幕的每一个像素的保存,在内存中是线性存储结构。在屏幕上的坐标 (x, y) 画一个点,就相当于在对应内存偏移 y * 640 + x 的位置赋值。
PS:在这篇文章中有一段代码,可以实践一下通过内存赋值来直接画点:https://codebus.cn/yangw/fast-putpixel
每绘制一个点,都需要执行一次乘法和一次加法运算。所有的绘图操作都需要调用画点函数,因此提高画点的效率,将会提升所有绘图操作的速度。
现在我们将计算内存偏移量的算法修改一下:
d = y * 640 +
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
注:学习本节前,请自备 MSDN,以便查阅 Windows GDI 函数。
对于 Windows GDI 中的绘图函数,操作目标通常是 DC(Device Context),因此需要传入一个 DC 的句柄,数据类型为 HDC(Handle of Device Context)。用 GetImageHDC() 函数可以获取 EasyX 的绘图窗口或 IMAGE 对象的句柄,然后就可以使用 Windows GDI 函数进行绘图操作。看以下示例:
#include <graphics.h>
#include <conio.h>
int main()
{
// 初始化绘图窗口,并获取 HDC 句柄
initgraph(640, 480);
HDC hdc = GetImageHDC();
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
EasyX 库有一个获取窗口句柄的功能,很是强大,这里介绍一下。
【窗体句柄】
窗体句柄是 Windows 下窗口的标识,可以理解为窗口的 ID。Windows SDK 中的许多窗口操作函数都需要指明窗体句柄,也就是说,有了句柄,我们可以通过 Windows SDK 中的 API 实现许多高级的窗体控制。
【函数原型】
窗体句柄为 HWND 类型,通过 GetHWnd() 函数可以返回绘图窗体的句柄。其函数原型是:
HWND GetHWnd();
【使用句柄】
举个例子,设置窗体标题文字的 Windows API 为:
BOOL SetWindowText(HWND hWnd, LPCTSTR lpString);
参数:
hWnd: 要设置标题文字的窗口句柄
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
getimage / putimage / loadimage / saveimage 这一组命令和 IMAGE 对象可以实现图像处理的相关功能,下面逐个介绍。
(有点类似 tc 中的 imagesize)
[加载图片]
实现加载图片主要分三步:
- 定义 IMAGE 对象
- 读取图片至 IMAGE 对象
- 显示 IMAGE 对象到需要的位置
很简单,我们看一下完整的代码:
#include <graphics.h>
#include <conio.h>
int main()
{
initgraph(640, 480);
IMAGE img; // 定义 IMAGE 对象
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
课程要求:先复习下课本上对数组的讲解(随便一本教材都行)
一维数组
数组可以实现批量操作。比如,我们产生 10 个随机数,产生后先保存起来,然后输出最大的:
int n[10];
int i;
for (i = 0; i < 10; i++)
n[i] = rand() % 1000;
// 按生成的顺序,逆序输出
for (i = 9; i >= 0; i--)
printf("%d\n", n[i]);
// 找出最大的
int max = -1;
for (i = 0; i < 10; i++)
{
if (n[i] > max)
max = n[i];
}
printf("最大的数字是
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
========================
位运算和绘图有什么关系?先举个例子来个感性认识:使用XOR运算可以实现擦除图形后不破坏背景,这在时钟程序中绘制表针是很有用的。稍后我们会给出这样的例子。
一、位运算的运算法则
位运算主要分 4 种:NOT、AND、OR、XOR,位运算的运算对象是二进制数(十进制要转换为二进制,计算机会自动转换)。
运算法则如下:
- NOT
表示“取反”,将二进制位的 1 变 0、0 变 1。
C 语言用符号 ~ 表示。
如:
二进制: ~1101 = 0010
用十进制表示就是:~13 = 2
- AND
表示“并且”,只有两数的对应
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
最常用的一个捕获按键的函数:_getch()
前几课,都把这个函数当做“按任意键继续”来用,现在我们用变量保存这个按键:
char c = _getch();
然后再做判断即可。
不过程序执行到 _getch() 是会阻塞的,直到用户有按键才能继续执行。可游戏中总不能因为等待按键而停止游戏执行吧?所以,要有一个函数,判断是否有用户按键:_kbhit()
这个函数返回当前是否有用户按键,如果有,再用 _getch() 获取即可,这样是不会阻塞的。
即:
char c;
if (_kbhit())
c = _getch();
举一个简单的例子,如果有按键,就输出相关按键。否则,输出“.”。每隔 100 毫秒输出一次
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
所谓动画,其实是连续显示一系列图形而已。
结合到程序上,我们需要以下几个步骤:
- 绘制图像
- 延时
- 擦掉图像
循环以上即可实现动画。
举一个例子,我们实现一条直线从上往下移动:
#include <graphics.h>
#include <conio.h>
int main()
{
initgraph(640, 480);
for(int y = 0; y < 480; y++)
{
// 绘制绿色直线
setlinecolor(GREEN);
line(0, y, 639, y);
// 延时
Sleep(10);
// 绘制黑色直线(即擦掉之
...
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
[学习目标]
熟练使用循环、判断语句
[熟悉for语句]
这步需要自学,看看自己手边的书,是怎样讲for语句的,简单看看就行。
[范例]
例如,画10条直线的代码:
#include <graphics.h>
#include <conio.h>
int main()
{
initgraph(640, 480);
for(int y=100; y<200; y+=10)
line(100, y, 300, y);
_getch();
closegraph();
return 0;
}
换一下循环的范围和间隔,看看效果。
还可以用来画渐变色,例如:
...