自己怎么个人网站新公司注册流程及费用
- 作者: 五速梦信息网
- 时间: 2026年04月20日 05:02
当前位置: 首页 > news >正文
自己怎么个人网站,新公司注册流程及费用,北京平台网站建设找哪家,网站建设 上海珍岛目录 前言一、渲染引擎二、关闭事件三、梯形绘制四、轨道绘制五、边缘绘制六、草坪绘制七、前后移动八、左右移动九、曲线轨道十、课山坡轨道十一、循环轨道十二、背景展示十三、引入速度十四、物品绘制十五、课数字路障十六、分数展示十七、重新生成十八、… 目录 前言一、渲染引擎二、关闭事件三、梯形绘制四、轨道绘制五、边缘绘制六、草坪绘制七、前后移动八、左右移动九、曲线轨道十、课山坡轨道十一、循环轨道十二、背景展示十三、引入速度十四、物品绘制十五、课数字路障十六、分数展示十七、重新生成十八、跳跃功能十九、课音效播放二十、音乐播放二十一、音乐切换 前言
这一期我们一起学期3d赛车这个项目实战这个项目比之前的那几个难度高了很多所以做起来会比较复杂里面会涉及一些数学思想。这个项目做完了之后非常有意思还是值得一做的。
完整工程文件提取码为1111 蓝色字体可以点进去
一、渲染引擎
做这个项目我们要自己到网上下载一些库要配置环境。所以配置环境可以看我上一期扫雷蓝色字体可以点进去
#include SFML/Graphics.hpp
using namespace sf;const int WinWidth 1024;
const int WinHeight 768;int main() {
RenderWindow win(VideoMode(WinWidth, WinHeight), Racing);win.setFramerateLimit(60); // 设置帧率为 60 帧
while (win.isOpen()) {
}
return 0;
}运行之后我不得到一个不可以移动的窗口。
二、关闭事件
这样就可以关闭和移动窗口了。 while (win.isOpen()) {Event e;
while (win.pollEvent(e)) {
if (e.type Event::Closed) {win.close();}}}三、梯形绘制
这个梯形就代表每一块道路的图案 void DrawTrape(RenderWindow window, Color c, int x1, int y1, int w1, int x2, int y2, int w2) {ConvexShape polygon(4);//梯形的四个定点polygon.setFillColor©;//设置梯形填充的颜色polygon.setPoint(0, Vector2f(x1 - w1, y1));polygon.setPoint(1, Vector2f(x2 - w2, y2));polygon.setPoint(2, Vector2f(x2 w2, y2));polygon.setPoint(3, Vector2f(x1 w1, y1));window.draw(polygon);
}
win.clear();
DrawTrape(win, Color::White, WinWidth / 2, 500, 200, WinWidth / 2, 300, 100);win.display();四、轨道绘制
const int roadWidth 1800;
const int roadSegLength 180;
const int roadCount 1884;对于每个轨道用一个结构体来实现小写的 xyz 代表每个轨道的中心在 3D 世界中的坐标大写的 X 和 Y则是 3D 映射到 2D 时的屏幕坐标大写的 W 代表这条轨道映射到屏幕后它的宽度。 实现一个带参构造函数利用初始化列表进行初始化。 然后实现一个 project 函数代表 3D 到 2D 的映射camXcamYcamZ 代表的是 3D 相机的位置接下来我会讲一些数学的概念。听不懂没有关系只要你能够自己跟着把代码写出来并且通过修改每个参数运行后得到你想要的效果就可以了。不用太计较这里的数学计算。 首先当相机位置 camZ 确定时z 越大那么这条轨道就应该越小所以实现这么一个反比例函数计算缩放比例 scale。 利用这个缩放比例可以计算出 X 和 Y像这样。 最后计算 W也就是每个轨道的实际屏幕宽度和 scale 强相关所以作为系数乘上去。
struct Road {
float x, y, z;
float X, Y, W;
float scale;
Road(int _x, int _y, int _z): x(_x), y(_y), z(_z) {}
void project(int camX, int camY, int camZ) {scale 1.0 / (z - camZ);X (1 (x - camX) * scale) * WinWidth / 2;Y (1 - (y - camY) * scale) * WinHeight / 2;W scale * roadWidth * WinWidth / 2;}
};定义一个轨道的 vector每个轨道的 x 和 y 都是 0z 坐标按照 i 进行平铺。 vectorRoad roads;
for (int i 0; i roadCount; i) {
Road r(0, 0, (i1) * roadSegLength);roads.push_back®;}因为近大远小的关系所以屏幕上只显示 300 个轨道对于每个轨道调用 project 计算投影传入的是相机的位置其中 y 代表竖直方向设定为 1600。 获取当前轨道和前一个轨道分别为 now 和 pre利用这两个轨道可以计算出一个梯形并且设置好颜色稍微做点差异化产生相间的效果。运行看下效果。 for (int i 0; i 300; i) {Road now roads[i % roadCount];now.project(0, 1600, 0);
if (!i) {
continue;}Road pre roads[(i - 1) % roadCount];Color road i % 2 ? Color(105, 105, 105) :
DrawTrape(win, road, pre.X, pre.Y, pre.W, now.X, now.Y, now.W);}当然可以在 i 这里除上一个数使得两个颜色区分的时候不会那么密集。看下效果。这里如果你不理解怎么办呢你就多试几个数字试着试着感觉就有了。
Color road (i/3) % 2 ? Color(105, 105, 105) : Color(101, 101, 101);五、边缘绘制
Color edge (i/3) % 2 ? Color(0, 0, 0) : Color(255, 255, 255);DrawTrape(win, edge, pre.X, pre.Y, pre.W*1.3, now.X, now.Y, now.W*1.3);六、草坪绘制
Color grass i / 3 % 2 ? Color(16, 210, 16) : Color(0, 199, 0);DrawTrape(win, grass, pre.X, pre.Y, WinWidth, now.X, now.Y, WinWidth);七、前后移动 for (int i 0; i roadCount; i) {
Road r(0, 0, (i1) * roadSegLength);roads.push_back®;}
int cameraZ 0;
while (win.isOpen()) {
if (Keyboard::isKeyPressed(Keyboard::Up)) cameraZ roadSegLength;
if (Keyboard::isKeyPressed(Keyboard::Down)) cameraZ - roadSegLength;win.clear();
int roadIndex cameraZ / roadSegLength;for (int i roadIndex; i roadIndex 300; i) {Road now roads[i % roadCount];now.project(0, 1600, cameraZ );八、左右移动
除了前后移动左右也可以移动。同样是控制相机的坐标定义 cameraX 代表相机的 x坐标。
int cameraZ 0;
int cameraX 0;
分别按下左键和右键时更改 cameraX 的值。
if (Keyboard::isKeyPressed(Keyboard::Left)) cameraX - 100;if (Keyboard::isKeyPressed(Keyboard::Right)) cameraX 100; 然后在这里把 cameraX 代替原来 0 的值。
now.project(cameraX, 1600, cameraZ);运行发现露馅了。没事宽度乘10就好了。
DrawTrape(win, grass, pre.X, pre.Y, 10*WinWidth, now.X, now.Y, 10*WinWidth);九、曲线轨道
这节课我们想办法让这条路能够变成弯的看起来更加的真实在原来的路上加上一个曲线因子。
float scale, curve;Road(int _x, int _y, int _z, float _c): x(_x), y(_y), z(_z), curve(_c) {} 然后在实例化 Road 的时候当 i 在 0 到 300 之间曲线因子就是 0.5否则就是负的 0.5。构造的时候传参传进去。
float curve (i 0 i 300) ? 0.5 : -0.5;Road r(0, 0, (i1) * roadSegLength, curve);
然后定义一个 x 和 dx初始化都为 0这段代码怎么去理解呢这里的 now.curve 就理解成加速度那么 dx 就是速度而 x 就是位移这样这条路弯曲起来就看起来非常的连续了看下效果。
int roadIndex cameraZ / roadSegLength;
float x 0, dx 0;
for (int i roadIndex; i roadIndex 300; i) {Road now roads[i % roadCount];now.project(cameraX - x, 1600, cameraZ);x dx;dx now.curve;十、课山坡轨道
然后我希望这个轨道有一种跌宕起伏的感觉三角函数可以做到这一点所以每个轨道的中心的 y 坐标用一个三角函数 sin 来实现振幅 1600像这样 相机的位置从 1600 作为基准再加上起始轨道的 y 坐标像这样。
int cameraY 1600 roads[roadIndex].y;
int minY WinHeight; 这里的 1600 就可以改成这个 cameraY 了。所以这个 cameraY 的范围就根据正弦函数的值变成了 0 到 3200。
now.project(cameraX - x, cameraY, cameraZ);
这时候我们发现效果好像不对问题出在哪里呢 原因是这样的。 需要注意的是一旦出现跌宕起伏那么如果出现一个 Y 值最小的轨道后面比 Y 值大的轨道就不应该被绘制出来注意这里用的屏幕坐标系所以越往上值越小。
int roadIndex cameraZ / roadSegLength;float x 0, dx 0;
int cameraY 1600 roads[roadIndex].y;
int minY WinHeight;那么判断当前的 Y 是否比最小的 Y 小如果小那么记录这个最小值否则直接 continue代表这条轨道不用绘制来看看效果。
if (now.Y minY) {minY now.Y;else {
continue;}十一、循环轨道
然后我们希望整条路能够产生循环这样就可以用有限的 Road 对象来展现无限的路。 计算这条路的总长度 totalLength如果发现 cameraZ 的位置已经大于总长度了就减去总长度如果小于总长度就变成负数了那么加上总长度。 其实就是让 cameraZ 的值始终保持在 [0, totalLength) 之间。
int totalLength roadCount * roadSegLength;
while (cameraZ totalLength) cameraZ - totalLength;
while (cameraZ 0) cameraZ totalLength;now.project(cameraX - x, cameraY, cameraZ - (i roadCount ? totalLength : 0) );然后你会发现这里出现了一些断层这个不是很合理。 根据 PI 计算一下roadCount 让它是 3.14 的倍数就好了那就 3.14 乘上 600变成 1884 看下效果。
const int roadCount 1884;十二、背景展示
准备一张云的图片加载一个纹理并且生成一个精灵。宽度是窗口宽度高度是窗口高度的一半。 Texture bg;bg.loadFromFile(cloud.png);
Sprite s(bg, IntRect(0, 0, WinWidth, WinHeight/2));然后在每次绘制轨道的时候把它绘制出来。
if (now.Y minY) {minY now.Y;}
else {
continue;}
win.draw(s);十三、引入速度
引入一个赛车前进的速度speed 初始化为 0。
int cameraZ 0;
int cameraX 0;
int speed 100;当键盘按下 UP 和 DOWN 的时候不再修改 cameraZ 的值而是修改速度的值然后最终cameraZ 把这个速度累加上就好了。 这样一来往前往后修改的就是速度不再是位移。
if (Keyboard::isKeyPressed(Keyboard::Up)) { if (speed 1000) speed 1000; } if (Keyboard::isKeyPressed(Keyboard::Down)) { speed - 2; if (speed 100) speed 100; } cameraZ speed;
十四、物品绘制
接下来在这条路上间隔的绘制物品每个物品在图片的宽高是 450这个作为常量根据你图片的大小进行修改即可。
const int itemSize 450;Road 的结构体中定义一个精灵 spr。
struct Road {
float x, y, z;
float scale, curve;Sprite spr;实现一个 drawItem 的函数设置好纹理的矩形缩放比例以及位置位置的计算方式比较简单X 坐标和这条路保持一致Y 坐标减去 W。然后给它绘制出来。 s.setScale(W / itemSize, W / itemSize);s.setPosition(X, Y - W);win.draw(s);
}然后定义一个物品的纹理对象加载对应的图片生成一个精灵对象。 Texture item;item.loadFromFile(item.png);
Sprite sitem(item);把精灵对象通过传参传到 Road 对象中。
Road r(0, sin(i/30.0)*1600, (i 1) * roadSegLength, curve, sitem); 然后一个 for 循环注意从后往前绘制否则遮挡关系会不对。为了不产生密集恐惧症我们可以 % 上一个 20每 20 个轨道出现一个物品。
for (int i roadIndex 300; i roadIndex; –i) {
if (i % 20 0)roads[i % roadCount].drawItem(win);}Road r(0, 0, (i 1) * roadSegLength, curve, sitem);十五、课数字路障
我们把路上的这些物品显示成一些不同的数字有数字有符号。 首先定义这么一个字符串和这张图片中的每个图片一一对应。
const char charItem[] 1234567890*/-%;然后在 Road 中定义两个变量operatorIndex 代表符号在 charItem 中的下标numberIndex 代表数字 charItem 中的下标。
int operatorIndex;
int numberIndex; 接下来就可以开始初始化了为了数字不会太密集采用随机数的方式如果是 200 的倍数才出现物品。同样是通过随机的方式尽量不出现 0因为 除 0 和 模 0 都没意义。 operatorIndex 等于 -1 代表这个 Road 上没有东西。 Road(int _x, int _y, int _z, float _c, Sprite _spr): x(_x), y(_y), z(_z), curve(_c), spr(_spr){
if (rand() % 200 0) {operatorIndex (rand() % 5) 10;numberIndex rand() % 10;
if (numberIndex 9) {numberIndex 0;}}
else {operatorIndex -1;}}然后我们重载原先的 drawItem并且加入一个新的参数 index 代表的是 charItem 中的下标xPlacement 则表示 x 方向的偏移。然后 left 和 top 则代表的是在这张图上的对应数字或者符号的左上角坐标。
void drawItem(RenderWindow win, int index, int xPlacement) {Sprite s spr;
int left (index % 5) * itemSize;
int top (index / 5) * itemSize;s.setTextureRect(IntRect(left, top, itemSize, itemSize));s.setScale(W / itemSize, W / itemSize);s.setPosition(X xPlacement*W, Y - W);win.draw(s);}
void drawItem(RenderWindow win) {
if (operatorIndex -1) {
return;}
drawItem(win, operatorIndex, -1);
drawItem(win, numberIndex, 0);} 这里的取模就可以去掉了。
for (int i roadIndex 300; i roadIndex; –i) {roads[i % roadCount].drawItem(win);}十六、分数展示
我们在窗口的左上角展示一个分数并且还是这张图片。先定义一个分数 score尽量考虑到所有情况所以初始化 - 的 1234567890。 然后实现一个 DrawNumber 函数sItem 代表数字对应那个精灵x 和 y 代表显示在屏幕的左上角坐标。然后把 number 转换成字符串存储到 ch 中。 遍历这个字符串并且去 charItem 中找到对应的下标利用相同的绘制方式把它绘制在屏幕上。
void DrawNumber(RenderWindow win, Sprite sItem, int number, int x, int y) {
char ch[100] {\0};_itoa_s(number, ch, 10);
int len strlen(ch);
for (int i 0; i len; i) {Sprite s sItem;
int index -1;
for (int j 0; charItem[j]; j) {
if (charItem[j] ch[i]) {index j;
break;}}
int left (index % 5) * itemSize;
int top (index / 5) * itemSize;s.setTextureRect(IntRect(left, top, itemSize, itemSize));s.setScale(0.18, 0.18);s.setPosition(x 0.13 * itemSize * i, y);win.draw(s);}
} 然后调用即可。 s.setTextureRect(IntRect(0, 0, WinWidth, minY));win.draw(s);
DrawNumber(win, sitem, score, 10, 10);十七、重新生成
如果每次路过都把数字清空那么迟早有一天路上就没有数字了于是实现一个重新生成的接口这里做一点小改动这个 Road 一开始是通过随机计算的。 当然如果 generateItem 直接传一个 true那么必定产生数字。
Road(int _x, int _y, int _z, float _c, Sprite _spr): x(_x), y(_y), z(_z), curve(_c), spr(_spr){
generateItem(false);}
void generateItem(bool bAlwaysGen) {
if (bAlwaysGen || rand() % 200 0) {operatorIndex (rand() % 5) 10;numberIndex rand() % 10;
if (numberIndex 9) {numberIndex 0;}}
else {operatorIndex -1;}}所以当前这条路如果正好出屏幕外那么就把 i 1500 的轨道生成一个新的数字。
now.operatorIndex -1;
roads[(i 1500) % roadCount].generateItem(十八、跳跃功能
为了增加趣味性可以引入跳跃功能从而可以跳过这个数字。加入一个状态叫 isJumping 一开始是 false代表没有跳跃。 跳跃会改变 z 的坐标所以 z 代表实际偏移的 z dz 代表跳跃时的速度。
bool isJumping false;
float z 0, dz 0; 如果按下空格当非跳跃状态则切换到跳跃状态并且把起跳速度设置为 150。
if (Keyboard::isKeyPressed(Keyboard::Space)) {
if (isJumping false) {isJumping true;dz 150;}} 然后如果一直在跳跃中则减去 5这里就是在模拟自由落体这里的 5 就是加速度然后 z 累加 dz 的过程就是模拟的 位移 和 速度当 z 小于等于 0说明落地了isJumping 置为 false。
if (isJumping) {dz - 5;z dz;
if (z 0) {z 0;isJumping false;}} 最后当遇到数字的时候如果非跳跃状态才会生效否则没有任何效果。
if (now.Y WinHeight) {
if (!isJumping now.operatorIndex ! -1) {score caculateScore(score, now.operatorIndex, now.numberIndex);now.operatorIndex -1;roads[(i 1500) % roadCount].generateItem(true);}}
十九、课音效播放
接下来我们加入一些音效让游戏更加的带感。引入一个头文件。 初始化三个音效缓存分别代表 碰到数字、起跳、落地 音效。
buffer[1].loadFromFile(jump.mp3);
buffer[2].loadFromFile(falldown.mp3); 起跳可以这么写。
if (isJumping false) {sound.setBuffer(buffer[1]);sound.play();isJumping true;dz 150;}落地可以这么写。
if (z 0) {z 0;isJumping false;sound.setBuffer(buffer[2]);sound.play();}碰到数字可以这么写。
if (!isJumping now.operatorIndex ! -1) {score caculateScore(score, now.operatorIndex, now.numberIndex);now.operatorIndex -1;roads[(i 1500) % roadCount].generateItem(true);sound.setBuffer(buffer[0]);sound.play();}二十、音乐播放
再加上一个 bgm。 SoundBuffer buffer[5];Sound sound, bgm;buffer[0].loadFromFile(get.mp3);buffer[1].loadFromFile(jump.mp3); buffer[2].loadFromFile(falldown.mp3);buffer[3].loadFromFile(tianfuyue.mp3);buffer[4].loadFromFile(liumaishenjian.mp3); 在窗口 while 之前调用 play 接口。
bgm.setBuffer(buffer[3]);
bgm.setLoop(true);
bgm.play();二十一、音乐切换
音乐可以随着赛车的速度进行切换当开的快的时候播放 4 号 音乐否则播放 5 号音乐。
if (Keyboard::isKeyPressed(Keyboard::Up)) {speed 2;
if (speed 1000) speed 1000;
if (speed 500) {speed 502;bgm.setBuffer(buffer[4]);bgm.play();}}
if (Keyboard::isKeyPressed(Keyboard::Down)) {speed - 2;
if (speed 100) speed 100;
if (speed 500) {speed 498;bgm.setBuffer(buffer[3]);bgm.play();}}
相关文章
-
自己有域名要怎么制作网站网站里面的导航图标怎么做的
自己有域名要怎么制作网站网站里面的导航图标怎么做的
- 技术栈
- 2026年04月20日
-
自己有网站怎么优化营销型网站建设论文
自己有网站怎么优化营销型网站建设论文
- 技术栈
- 2026年04月20日
-
自己优化网站做贸易要看什么网站
自己优化网站做贸易要看什么网站
- 技术栈
- 2026年04月20日
-
自己怎么建个免费网站北京装饰公司十大排名榜
自己怎么建个免费网站北京装饰公司十大排名榜
- 技术栈
- 2026年04月20日
-
自己怎么建设购物网站网站质量
自己怎么建设购物网站网站质量
- 技术栈
- 2026年04月20日
-
自己怎么优化网站湛江网站建设优化建站
自己怎么优化网站湛江网站建设优化建站
- 技术栈
- 2026年04月20日
