开始前,请确保你已经按照教程做了.

思路

其实也没别的,只是感觉有一个比较好用的思路推荐一下

EMMM

反正就是看到了一种随机算法,叫 柏林噪声 ,能生成比较自然的纹理,但我不用纹理,就稍微改了一下,让他能生成比较自然的地形:

int bj[120];
void dj__(){
	int bj_[126];
	for(int i=0;i<126;i++){
		bj_[i]=rand_(750,769);
	}
	for(int i=0;i<120;i++){
		bj[i]=0;
		for(int j=i;j<6+i;j++) bj[i]+=bj_[j];
		bj[i]/=6;
	}
}

代码(知道你们只看这里)

bits.h:

//这将是一个easyx的神人头文件
/*
1.函数
 1.qian() 把画布挪至最前方(最好必用)
 2.in() 输入
 3._out() 在画布上新建一行文字
 4.outph() 显示图片
 5.getph() 把图片名加上地址
 例(显示一张图片):
 string s="start.jpg";(一定要一个变量) 
 s=getph(s);(加上地址,默认是.exe地址+\ph\(就是把照片存到.exe所在文件夹下再建一个ph文件夹,在里面存照片)) 
 outph(0,0,s);(在0,0显示图片)
2.头文件
 包含所有头文件,最好不要改 
*/ 

#include <windows.h>
#include <easyx.h>
#include <conio.h>
#include <string>
#include <vector>
#include <map>
#include <bits/stdc++.h>
using namespace std;
int rand_(int l,int r); 
struct body{
	int x,y;
	int to_x,to_y;
	int xf,yf;
}b[3];
// 全局变量,记录每个按键的上一次状态
map<char, bool> keyPrevState;

void qian_() {
    HWND hWnd = GetHWnd();
    SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

void chbkc(COLORREF W){
	setbkcolor(W);
	cleardevice();
}//清空画布 

void drso(int xx,int xy,int chx,int chy,COLORREF W){
	int yx=xx+chx,yy=xy+chy;
	setfillcolor(W);
	solidrectangle(xx,xy,yx,yy);
}//绘制填充矩形 

////
int bj[120];
void dj__(){
	int bj_[126];
	for(int i=0;i<126;i++){
		bj_[i]=rand_(750,769);
	}
	for(int i=0;i<120;i++){
		bj[i]=0;
		for(int j=i;j<6+i;j++) bj[i]+=bj_[j];
		bj[i]/=6;
	}
}
int dt[1200];
void rdt(){
	for(int i=0;i<120;i++){
		for(int j=10*i;j<10*(i+1);j++) dt[j]=bj[i];
	}
}
int dux[1000][120];
int dxbj=0;
void dx(bool x){
	if(x==0){
		if(dxbj==0) dxbj=999;
		else dxbj=dxbj-1;
	}
	if(x==1){
		if(dxbj==999) dxbj=0;
		else dxbj++;
	}
	if(dux[dxbj][0]==0){
		dj__();
		for(int i=0;i<120;i++) dux[dxbj][i]=bj[i];
	}
	else{
		for(int i=0;i<120;i++) bj[i]=dux[dxbj][i];
	}
	rdt();
}

////只有dx能用,给的参数是向左还是向右,0为左,dt[i]表示x=i时地的高度 

bool in(char x, bool flag = 1) {
    char e = x;
    if (flag) {
        e -= 32; // 转换为大写(假设x是小写)
    }
    return (GetAsyncKeyState(x) || GetAsyncKeyState(e));
}

void _out(string s, int x, int y,COLORREF c=WHITE) {
    setbkmode(TRANSPARENT);
    settextcolor(c);
    settextstyle(20, 0, _T("宋体"));
    TCHAR tstr[256];
    _stprintf(tstr, _T("%s"), s.c_str());
    outtextxy(x, y, tstr);
}
// 显示图片函数
// 参数: x - x坐标, y - y坐标, filename - 图片文件名
// 返回值: bool - 成功返回true,失败返回false
// 显示图片函数
void outph(int x, int y, const std::string& s) {
    // 加载图片
    IMAGE img;
    loadimage(&img, s.c_str());
    
    // 在指定位置显示图片
    putimage(x, y, &img);
}
string getph(const string& pl) {
    char buffer[MAX_PATH] = {0};
    GetModuleFileNameA(NULL, buffer, MAX_PATH);
    
    string path(buffer);
    size_t pos = path.find_last_of("\\/");
    if (pos != string::npos) {
        path = path.substr(0, pos) + "\\ph\\" + pl;
    } else {
        path = path + "\\ph\\" + pl;
    }
    
    return path;
}

// 侧身人形改进版 - 更明显的侧身效果
void drawman(int x, int y,bool fla) {
    int baseX = x - 10;
    int baseY = y - 30;
    
    // 1. 头 - 侧脸(椭圆)
    setfillcolor(RGB(255, 205, 148));
    solidrectangle(baseX + 10, baseY + 1, baseX + 15, baseY + 8);
    solidrectangle(baseX + 12, baseY + 8, baseX + 13, baseY + 9);
    setfillcolor(RGB(0, 0, 0));
    solidrectangle(baseX + 10, baseY + 1, baseX + 15, baseY + 2);
    if(fla==0) solidrectangle(baseX + 10, baseY + 4, baseX + 11, baseY + 5);
    else solidrectangle(baseX + 14, baseY + 4, baseX + 15, baseY + 5); 
    if(fla==1) solidrectangle(baseX + 10, baseY + 3, baseX + 11, baseY + 4);
    else solidrectangle(baseX + 14, baseY + 3, baseX + 15, baseY + 4); 
    
    setfillcolor(RGB(255, 205, 148));  // 肤色
    if(fla==1) solidrectangle(baseX + 10, baseY + 10, baseX + 12, baseY + 20);
    else solidrectangle(baseX + 14, baseY + 10, baseX + 16, baseY + 20);
    
    // 2. 身体 - 侧身(窄矩形)
    setfillcolor(RGB(220, 100, 100));  // 红色上衣
    solidrectangle(baseX + 11, baseY + 10, baseX + 15, baseY + 22);
    
    // 3. 腿 - 侧身(一条腿)
    setfillcolor(RGB(50, 50, 50));  // 黑色裤子
    solidrectangle(baseX + 12, baseY + 22, baseX + 14, baseY + 30);
    
    // 4. 前臂 - 在身体前方
    setfillcolor(RGB(255, 205, 148));  // 肤色
    if(fla==0) solidrectangle(baseX + 10, baseY + 10, baseX + 12, baseY + 20);
    else solidrectangle(baseX + 14, baseY + 10, baseX + 16, baseY + 20);
}

int rand_(int l, int r) {
    static unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    static std::mt19937 gen(seed);
    std::uniform_int_distribution<> dis(l, r);
    return dis(gen);
}

bool u_d_(body xx,body yy){
	if(xx.x==yy.x && xx.y==yy.y) return 1;
	return 0;
}

int jumpv=-20;
bool isjump=0;

bool go(int zy,int& x,int& y){ 
	if(zy==0){
		if(x-6<0){
			x-=6;
			x+=1200;
			if(isjump==0) y=dt[x];
			return 1;
		}
		x-=6;
		if(isjump==0) y=dt[x];
	}
	if(zy==1){
		if(x+6>=1200){
			x+=6;
			x-=1200;
			if(isjump==0) y=dt[x];
			return 1;
		}
		x+=6;
		if(isjump==0) y=dt[x];
	}
	return 0;
}

void jump(int& x,int& y){
	if(y>=dt[x]){
		isjump=0;
		jumpv=-20;
		y=dt[x];
		return ;
	}
	y+=jumpv;
	if(y>=dt[x]) y=dt[x]; 
	jumpv=min(jumpv+1,20);
} 

int n=3;
void start_csh(){
	cont:;
	for(int i=0;i<n;i++){
		con:;
		b[i].x=rand_(10,1190);
		b[i].y=rand_(10,790);
		b[i].to_x=rand_(10,1190);
		b[i].to_y=rand_(10,790);
		b[i].xf=b[i].to_x-b[i].x;
		b[i].yf=b[i].to_y-b[i].y;
		if(abs(b[i].xf)+abs(b[i].yf)>20) goto con;
	}
	if(u_d_(b[0],b[1]) || u_d_(b[0],b[2]) || u_d_(b[1],b[2])) goto cont;
}

main.cpp:

#include"bits.h"
using namespace std;

int main(){
	std::srand(static_cast<unsigned int>(
    std::chrono::system_clock::now().time_since_epoch().count()));
    
	initgraph(1200,800);
	qian_();
	string lo=getph("ling.png");
	outph(0,0,lo);
	start_csh();
//	for(int i=0;i<n;i++){
//		cout << b[i].x<<' '<<b[i].y<<endl;
//		cout << b[i].xf<<' '<<b[i].yf<<endl;
//	}
	dx(0);
	bool manf=1;
	int manx=350,many=0; 
	go(manf,manx,many);
	while(1){
		int uuu=1;
		
		BeginBatchDraw();
		
		if(in('a') || in(VK_LEFT,0)) manf=0;
		else if(in('d') || in(VK_RIGHT,0)) manf=1;
		else uuu=0;
		if((in('w') || in(VK_UP,0)) && !isjump){
			isjump=1;
			many--;
		}
		chbkc(RGB(160, 82, 45));
		for(int i=0;i<1200;i++) drso(i,0,1,dt[i],RGB(135, 206, 235));
		_out(to_string(dxbj),0,0,BLACK);
		bool wk=0; 
		if(uuu==1) wk=go(manf,manx,many);
		if(wk){
			if(manx<300) dx(1);
			else dx(0);
		}
		if(isjump){
			jump(manx,many);
		}
		drawman(manx,many,manf);
		
		EndBatchDraw();
		Sleep(15); 
	}
	return 0;
}

备注

1.此代码含有约20%的AI成分,但注释是自己写的,而且核心和50%多的函数是自己写的(防止有人说不是我写的)

2.请确保你已经按照

教程

照做,否则出错我不负任何责任

3.请确保你用C++14或以上版本,否则出错我不负任何责任

4.建议新建项目时创建一个文件夹

5.仅支持DEV

2 comments

  • @ 2026-1-24 9:16:40

    同学你好,

    首先,感谢你分享你的代码和思路。从你的代码中可以看出,你尝试实现了一个基于柏林噪声(Perlin Noise)的地形生成算法,并将其应用在一个简单的2D平台游戏中。这是一个非常有趣且具有挑战性的项目,涉及到随机算法、图形渲染和游戏逻辑等多个方面。

    不过,我注意到你的回复中提到“教程被ban了”,这可能意味着你无法访问原帖中的教程链接,导致在环境配置或理解上遇到了一些困难。作为信息学竞赛的教练,我的职责是引导你自主解决问题,而不是直接提供可运行的代码。因此,我将从以下几个方面为你提供思路和指导:

    1. 分析你的代码结构与核心逻辑

    你的项目主要分为两部分:

    • bits.h:包含了图形渲染、地形生成、人物绘制和物理模拟(移动、跳跃)的核心函数。
    • main.cpp:主循环,处理用户输入、更新游戏状态并渲染画面。

    核心算法分析

    • 你提到的“柏林噪声”实际上是一维的平滑随机地形生成。你的 dj__() 函数通过生成126个随机数,然后对每6个连续值取平均,得到120个地形高度值。这是一种滑动窗口平均滤波,可以有效平滑随机噪声,生成连续的地形。
    • dx() 函数管理一个环形缓冲区 (dux),用于存储和读取不同位置的地形数据,实现了“无限”地形的效果。

    2. 可能遇到的问题及调试思路

    由于教程链接失效,你可能会在以下方面遇到问题:

    a) 开发环境配置(EasyX图形库)

    • 问题:你的代码严重依赖 EasyX 图形库。如果未正确安装或配置,编译会失败。
    • 解决思路
      1. 确保你使用的是 Dev-C++ 并已安装 EasyX 库。你可以通过搜索引擎查找“Dev-C++ 安装 EasyX”的最新教程。
      2. 在 Dev-C++ 中,创建项目时,项目类型应选择“Windows Application”,而不是控制台程序。
      3. 检查 #include <easyx.h> 是否能正确找到头文件。可能需要手动指定库路径。

    b) 资源文件路径问题

    • 你的代码通过 getph() 函数构建图片路径,期望在可执行文件同目录下的 ph 文件夹中找到 ling.png
    • 解决思路
      1. 确保你的项目目录下确实存在 ph 文件夹,并且其中包含 ling.png 文件。
      2. 如果图片加载失败,游戏启动时背景会显示异常。你可以在 outph 调用后添加简单的错误检查(例如,检查图片尺寸是否为0),或者暂时注释掉背景图片的显示,用纯色背景代替,以隔离问题。

    c) 地形生成算法的理解与改进

    • 你的地形生成算法 (dj__()rdt()) 是一维的,且平滑窗口固定为6。这可能导致地形变化不够“自然”(柏林噪声通常是多维的,并利用梯度进行插值)。
    • 引导思考
      • 你能解释一下 dux 这个二维数组(dux[1000][120])的作用吗?它如何实现“无限”地形?
      • 当前算法的时间复杂度是多少?如果地形块更多(比如1200改为10000),性能会如何?
      • 如何调整平滑窗口的大小(代码中的6)来改变地形的“粗糙度”?

    3. 算法优化与扩展建议

    如果你想进一步优化或扩展此项目,可以考虑以下方向:

    1. 真正的柏林噪声

      • 研究经典的Perlin Noise算法。它通过定义网格点上的随机梯度向量,并在网格之间进行平滑插值(通常使用三次Hermite插值)来生成更自然的连续噪声。
      • 你可以尝试实现一个简化版的二维Perlin Noise来生成地形高度图。
    2. 物理引擎的完善

      • 当前的跳跃物理 (jump 函数) 模拟了简单的重力加速度。考虑添加水平速度衰减(摩擦力)更精确的碰撞检测(目前只是判断 y >= dt[x])以及斜坡处理
      • 如何检测人物是否站在地面上(isjump 的逻辑)?目前的逻辑在快速移动时可能不准确。
    3. 代码结构与可读性

      • 将游戏状态(如人物位置、速度、地形数据)封装到一个 Game 类中。
      • 使用枚举类型代替魔术数字(如 manf=0 表示向左)。
      • 将地形生成、渲染、物理更新等逻辑分离到不同的函数或模块中。

    4. 调试技巧

    如果程序运行出现崩溃、黑屏或逻辑错误,请尝试:

    1. 分模块测试:注释掉游戏主循环,先单独测试地形生成函数 dj__()rdt(),输出一些高度值看看是否符合预期。
    2. 简化渲染:先不用图片背景和人物精灵,只用矩形和线条绘制地形和人物,确保核心逻辑正确。
    3. 输出调试信息:在控制台输出关键变量(如 manx, many, isjump, jumpv)的值,观察其变化是否符合物理规律。

    总结

    你的项目已经具备了地形生成、人物移动、基础物理和图形渲染的雏形,这是一个很好的起点。遇到教程链接失效的问题,正是锻炼你独立查找资料、阅读文档和调试代码能力的机会。

    下一步行动建议

    1. 首先确保 EasyX 开发环境配置正确。
    2. 然后,分步运行和测试你的代码,从最简单的显示一个窗口开始,逐步添加地形、人物、控制逻辑。
    3. 如果遇到具体的编译错误或运行时问题,可以再来讨论,我会引导你分析错误信息并找到解决方案。

    请记住,在信息学竞赛和编程学习中,遇到问题并解决问题的过程本身就是最宝贵的经验。期待看到你完善后的作品!

    • 主包,你的教程被ban了,T-T

    • 1