见缝插针
见缝插针游戏的规则非常简单,给定一个旋转圆盘,每次按动按键,即可在圆盘上射入一根针,当两个针发生碰撞即挑战失败。胜利条件可以设置射出指定根数后胜利,也可以设置不限根数,以更多的根数作为挑战目标。
主要实现步骤
- 绘制圆盘
- 利用三角函数实现旋转
- 数组实现多根针
- 实现针的发射,失败判定
- 增加得分,关卡切换等设计
规制画布
画布的设置涉及长宽,颜色设置。
int canvas_width = 900;
int canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
圆盘绘制
setlinestyle(PS_SOLID, 5);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
circle(canvas_width / 2, canvas_height / 2, 50);//设置圆盘的坐标和半径
绘制线条
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
line(canvas_width / 2, canvas_height / 2, canvas_width / 2 + 50, canvas_height / 2);
实现旋转
旋转的实现改变的变量是角度。设置一个变量,代表角度,另设一变量代表角度的改变量,设置延时函数调整旋转时间。
#include<graphics.h>//调用图形库
#include<conio.h>
#include<stdio.h>
#include<math.h>
int main()
{
double canvas_width = 900;
double canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int radius = 50;
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
double PI = 3.1415926;
double lineLength = 100;
double xBegin;
double yBegin;
double xEnd;
double yEnd;
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
double angle = 0;
double rotate_speed = PI / 360;
while (1)
{
cleardevice();
angle += rotate_speed;
if (angle > 200 * PI) angle -= 200 * PI;
//计算坐标
xBegin = radius * cos(-angle) + canvas_width / 2;
yBegin = radius * sin(-angle) + canvas_height / 2;
xEnd = lineLength * cos(-angle) + canvas_width / 2;
yEnd = lineLength * sin(-angle) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
Sleep(10);
}
_getch();//设置一个读取字符函数,用于保持绘图窗口不关闭
closegraph();//关闭绘图窗口
return 0;
}
多根针的实现
前文可知,对于针这一个对象,我们主要改变的是针对应的角度。对于n根针的绘制,就需要同时求n个角度。使用数组存储角度。
#include<graphics.h>//调用图形库
#include<conio.h>
#include<stdio.h>
#include<math.h>
const double PI = 3.1415926;
int main()
{
double canvas_width = 900;
double canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int radius = 50;
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
double lineLength = 100;
double xBegin;
double yBegin;
double xEnd;
double yEnd;
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int needle_num = 10;
double needle_angles[20];
for (int i = 0; i < needle_num; i++)
{
needle_angles[i] = i * 2 * PI / needle_num;
}
double rotate_speed = PI / 360;
while (1)
{
cleardevice();
for (int i = 0; i < needle_num; i++)
{
needle_angles[i] += rotate_speed;
if (needle_angles[i] > 200 * PI) needle_angles[i] -= 200 * PI;
//计算坐标
xBegin = radius * cos(-needle_angles[i]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[i]) + canvas_height / 2;
xEnd = lineLength * cos(-needle_angles[i]) + canvas_width / 2;
yEnd = lineLength * sin(-needle_angles[i]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
Sleep(5);
}
_getch();//设置一个读取字符函数,用于保持绘图窗口不关闭
closegraph();//关闭绘图窗口
return 0;
}
此时已经实现了简单的动画效果
如果以动画为开发目标,还可以在现有代码上添加一点细节。如一个旋转的太阳小哥
如果按照上文给出代码,会发现图像绘制过程中有大量的闪烁,这种闪烁可以通过增加批量绘图函数解决额。
BeginBatchDraw();
FlushBatchDraw();
BeginBatchDraw();开始后任何绘图操作暂时搁置,直到FlushBatchDraw();将搁置的绘图操作全部完成。
上述两个函数分别放置在绘图循环开始时,和每一个绘图循环的最后,就可以实现批量绘制。
针的发射
前面实现的所有操作,实际上都是静态的,程序使用者或者说是玩家,无法对我们已经绘制的旋转⚪和针实现任何操作,接下来要完成的就是“插针”的实现,也就是交互的设计。
引入新的函数,用于交互
·检测按键
if (kbhit())//如果按键
·获得按键输入
char input = _getch();//获得用户按键
·判断是否为指定按键,以空格键为例
if (input == ' ')//如果为空格键
将代码整合
#include<graphics.h>//调用图形库
#include<conio.h>
#include<stdio.h>
#include<math.h>
const double PI = 3.1415926;
int main()
{
double canvas_width = 900;
double canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int radius = 50;
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
double needle_length = 100;
double xBegin;
double yBegin;
double xEnd;
double yEnd;
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int needle_num = 0;
double needle_angles[100];
double rotate_speed = PI / 360;
BeginBatchDraw();
while (1)
{
cleardevice();
line(0, canvas_height / 2, needle_length, canvas_height / 2);
for (int i = 0; i < needle_num; i++)
{
needle_angles[i] += rotate_speed;
if (needle_angles[i] > 200 * PI) needle_angles[i] -= 200 * PI;
//计算坐标
xBegin = radius * cos(-needle_angles[i]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[i]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[i]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[i]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
if (_kbhit())//如果按键
{
char input = _getch();//获得用户按键
if (input == ' ')//如果为空格键
{
//针的逻辑实现
needle_num++;
needle_angles[needle_num - 1] = PI;
xBegin = radius * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
}
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
circle(canvas_width / 2-20, canvas_height / 2-20, radius/10);//设置圆盘的坐标和半径
circle(canvas_width / 2+20, canvas_height / 2 - 20, radius / 10);//设置圆盘的坐标和半径
circle(canvas_width / 2 , canvas_height / 2 + 20, radius / 4);//设置圆盘的坐标和半径
FlushBatchDraw();
Sleep(5);
}
_getch();//设置一个读取字符函数,用于保持绘图窗口不关闭
closegraph();//关闭绘图窗口
return 0;
}
失败判定
通常用两个小针体积碰撞判定失败,在实现时,可以忽略针的形状,而采用角度判定失败。当两个针的角度差小于一定值时,认为发生了碰撞。
#include<graphics.h>//调用图形库
#include<conio.h>
#include<stdio.h>
#include<math.h>
const double PI = 3.1415926;
int main()
{
double canvas_width = 900;
double canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int radius = 50;
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
double needle_length = 100;
double xBegin;
double yBegin;
double xEnd;
double yEnd;
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int needle_num = 0;
double needle_angles[100];
double rotate_speed = PI / 360;
BeginBatchDraw();
while (1)
{
cleardevice();
line(0, canvas_height / 2, needle_length, canvas_height / 2);
for (int i = 0; i < needle_num; i++)
{
needle_angles[i] += rotate_speed;
if (needle_angles[i] > 2 * PI) needle_angles[i] -= 2 * PI;
//计算坐标
xBegin = radius * cos(-needle_angles[i]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[i]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[i]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[i]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
if (_kbhit() && rotate_speed != 0)//如果按键
{
char input = _getch();//获得用户按键
if (input == ' ')//如果为空格键
{
//针的逻辑实现
needle_num++;
needle_angles[needle_num - 1] = PI;
for (int i = 0; i <= needle_num-2; i++)
{
double fail_flag = needle_angles[needle_num - 1] - needle_angles[i];
if (fabs(fail_flag) < PI / 60)//当角度小于设定值,失败
{
rotate_speed = 0;
RECT r = { 0, 0, 600, 600 };
LOGFONT f;
gettextstyle(&f); // 获取当前字体设置
f.lfHeight = 78; // 设置字体高度为 48
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
drawtext(_T("you fail !!!!!"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//失败提示
FlushBatchDraw();
Sleep(10000);
}
}
xBegin = radius * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
}
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
circle(canvas_width / 2-20, canvas_height / 2-20, radius/10);//设置圆盘的坐标和半径
circle(canvas_width / 2+20, canvas_height / 2 - 20, radius / 10);//设置圆盘的坐标和半径
circle(canvas_width / 2 , canvas_height / 2 + 20, radius / 4);//设置圆盘的坐标和半径
FlushBatchDraw();
Sleep(5);
}
_getch();//设置一个读取字符函数,用于保持绘图窗口不关闭
closegraph();//关闭绘图窗口
return 0;
}
可以按照自己的喜好设置失败效果,我设置的如下
得分等优化措施
得分的展示
得分可以简单按照发射数量来判定,即发射一根,得分增加一个。
#include<graphics.h>//调用图形库
#include<conio.h>
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<string>
const double PI = 3.1415926;
using namespace std;
int main()
{
double canvas_width = 900;
double canvas_height = 600;
initgraph(canvas_width, canvas_height);//初始化绘图窗口,也就是设置画布的大小
setbkcolor(RGB(0, 0, 0));//设置背景颜色为黑色
cleardevice();//情况背景
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int radius = 50;
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
double needle_length = 100;
double xBegin;
double yBegin;
double xEnd;
double yEnd;
setlinestyle(PS_SOLID, 3);//设置线条样式
setlinecolor(0xFFFFFF);//设置圆盘颜色为白色
int needle_num = 0;
double needle_angles[100];
double rotate_speed = PI / 360;
int score = 0;
TCHAR s[20];
BeginBatchDraw();
while (1)
{
cleardevice();
line(0, canvas_height / 2, needle_length, canvas_height / 2);
for (int i = 0; i < needle_num; i++)
{
needle_angles[i] += rotate_speed;
if (needle_angles[i] > 2 * PI) needle_angles[i] -= 2 * PI;
//计算坐标
xBegin = radius * cos(-needle_angles[i]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[i]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[i]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[i]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
if (_kbhit() && rotate_speed != 0)//如果按键
{
char input = _getch();//获得用户按键
if (input == ' ')//如果为空格键
{
score++;
//针的逻辑实现
needle_num++;
needle_angles[needle_num - 1] = PI;
for (int i = 0; i <= needle_num-2; i++)
{
double fail_flag = needle_angles[needle_num - 1] - needle_angles[i];
if (fabs(fail_flag) < PI / 60)
{
rotate_speed = 0;
RECT r = { 0, 0, 600, 600 };
LOGFONT f;
gettextstyle(&f); // 获取当前字体设置
f.lfHeight = 78; // 设置字体高度为 48
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
drawtext(_T("you fail !!!!!"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
FlushBatchDraw();
Sleep(10000);
}
}
xBegin = radius * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yBegin = radius * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
xEnd = needle_length * cos(-needle_angles[needle_num - 1]) + canvas_width / 2;
yEnd = needle_length * sin(-needle_angles[needle_num - 1]) + canvas_height / 2;
line(xBegin, yBegin, xEnd, yEnd);
}
}
_stprintf_s(s, _T("%d"), score);
settextstyle(50, 0, _T("Times"));
settextcolor(RGB(100, 100, 100));
outtextxy(65, 200, s);
circle(canvas_width / 2, canvas_height / 2, radius);//设置圆盘的坐标和半径
circle(canvas_width / 2-20, canvas_height / 2-20, radius/10);//设置圆盘的坐标和半径
circle(canvas_width / 2+20, canvas_height / 2 - 20, radius / 10);//设置圆盘的坐标和半径
circle(canvas_width / 2 , canvas_height / 2 + 20, radius / 4);//设置圆盘的坐标和半径
FlushBatchDraw();
Sleep(5);
}
_getch();//设置一个读取字符函数,用于保持绘图窗口不关闭
closegraph();//关闭绘图窗口
return 0;
}
最终效果
在得分之外可以按照自己的喜好增加其他功能,如关卡,过场动画等。