c++课设 _ 保卫萝卜

因为时间紧张也要准备四级和期末考试,所以原本的很多设想都没有实现,就简单实现了基本玩法,有很多的地方抠图都没有抠好,也有地方为了省事就直接输出了,给图为例,这篇博客写下主要为了记录不足。
![image.png](https://img-blog.csdnimg.cn/img_convert/f60ef97b0a6b99439b4e38c958fafa4b.png#clientId=ub1c9f3be-d6a0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=208&id=ufb90f503&margin=[object Object]&name=image.png&originHeight=830&originWidth=1192&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1147982&status=done&style=none&taskId=u2a2e9c77-7c71-4019-8a9a-49e97db9b87&title=&width=298)

虽然有很多地方不是很好,但也是我一行一行敲出来的,hhh。

主要类

map(地图)

整个地图被我用一个backmap[7][13]分割好了,每一块都是一块60*60的格子,backmap中的每个不同的值代表不同的图片。这里我用一个txt文件存储,想修改地图的时候就很方便,当然最初设置成这样是为了我想让玩家自己可以设置地图,和怪物的波数(当然最后没实现)。

class map
{
public:
	void upload(radish &tem);//读取
	void show(radish& tem);
private:
	IMAGE background,lujin,wo1,wo2;
	const int breadth = 60;
};

upload就是从txt读取文件,并且读取图片数据,其实就是构造函数了(我也不记得我当时干嘛这样搞)。
show 就是展示地图。
breadth这个值应该就是每个格子的宽度,虽然不记得我当时这样搞的目的。

radish

class radish//萝卜
{
public :
	friend class map;//好像没什么用
	radish(int a, int b) :x(a), y(b);
	radish() ;
	void injured();//受伤
	void show();//展示
private:
	int x, y,blood;//x和y坐标,blood血量
	IMAGE tupian1,tupian2;
};

现在回过头去看发现这个完全写在map里没必要另外写一个类
injured 这个函数没有参数,是因为我只实现了一种怪物,没有搞其他怪物,没有想到要实现类似BOSS那种怪物。

monst

class monst//基类
{
public:
	monst(int c) ;
	void attack(radish& a);
	void showblood();
	void injured(int a);
	void move();
	int showx();
	int showy();
	bool if_death();//是否死亡
	void huoqu(int &a, int &b);//获取这个怪物的坐标
	virtual void showimage() = 0;//虚函数
	int x, y;//坐标
private :
	const double mmax = 100;
	double blood;//血量
	int v;//速度
	bool st[7][13];//用来记录那些经过
	int fx, fy;//用来位移
	IMAGE tupian;
	bool is_death;
	int tt;//开始时间
	int stt;//位移时间
};

在这里的move的函数实现了怪物根据backmap来走路,就是说怪物会一直走路,不会横穿,当然其实我的实现方法不是很好,会有bug,(当然是根据时间进行位移的)。

void monst::move()
{
	stt = clock();
	if (stt - tt < 100)return;
	int dx[4] = { 0,1,-1,0 }, dy[4] = { 1,0,0,-1 };
	//cout << x << y << "kkk" << endl;
	//if (st[0][0])cout << "llllllll";
	//cout << zx << zy << endl;
	if (x % 60 == 0 && y % 60 == 0)
	{
		st[y / 60][x / 60] = false;
		if (y / 60 == zx && x / 60 == zy) is_death = true, bradish.injured();
		for (int i = 0; i < 4; i++)
		{
			int a = x / 60 + dy[i], b = y/60 + dx[i];
			if (a>=0&&b>=0&&a<7&&b<13&&st[b][a])fx = a - x / 60, fy = b - y / 60;
			//cout << a << b << "ll" << endl;
		}
		//cout << a << b <<"ll"<< endl;
	}
	x +=  fx, y +=  fy;
	tt = clock();
}

很明显我就类似于走迷宫的方法去实现,这种就会出现bug

我想了一下应该可以给路径的方块进行编号类似1,2,3,4.每次只走向大一的格子

monst1

class monst1 :public monst
{
public :
	void showimage();
	monst1(int c) :monst(c) { t = 0; };//实现了多态
private:
	int t;//时间
};

这个是由monst继承而来,monst定义了每个怪物的共有属性,而且在这个monst1的构造函数中定义了每个怪物的不同属性,类似于速度,这样就可以用monst的指针来实现多态。

weapon

class weapon//塔
{
public:
	weapon();
	void gr(gametime *l);
	bool fandeath();
	bool is_xiang(int t,int a, int b)
	bool jianche(monst *tem);
	virtual void showimage() = 0;
private:
	bool is_death;
	int gongjijuli;
protected:
	int x, y;
	int deng;//等级
	int t;//产生时间
	monst* gongji;//要攻击对象的地址
	int harm[3];
	int groop[2];
};

这个是塔的基类。

butter1

class bullet1 :public weapon//塔类型1
{
public:
	bullet1(int a, int b,gametime*m) :weapon()void showimage();
	~bullet1()private:
	IMAGE tt1[3],tt2[3];
	IMAGE guangshu1, guangshu2;
	IMAGE tu1, tu2;
	int worth;
	double jiaodu;//用来旋转
};

gametime

class gametime
{
public:
	friend bullet1;
	friend weapon;
	gametime()//void settime();//设置时间波数
	void generate(int lei, int size);
	void mianban(ExMessage);
	void check();//检测怪物是否死亡,并且展示怪物
	void showtime();
	void showmianban(int x, int y);
	void showwwmian();
	void showcaichan();//展示有多少钱
	void tisheng();//提升等级
	void jiancheshifou();
	void showhuopao();
private:
	int t;//时间;
	vector<monst*>qun;//怪物的地址
	vector <weapon*> ta;//塔的地址
	vector<PII> ba;
	vector<int> aa;
	bool zhankai;//是否展开
	int tzhankai;//展开时间
	int zkx, zky;
	IMAGE cha1, cha2;
	IMAGE tusheng1, tusheng2;
	IMAGE tu1, tu2;
	int caichan;
};

这个是这个课设最重要的类,基本所有的函数都在这个类里调用。

主要难点

怪物走路

前面已经说过了。

维护金币数

杀死一个怪物需要增长金币,建立一个塔需要消耗金币,塔要升级也要消耗金币,可以将monst和weapon设为友元类,将gametime当参数(只有一个game time)

鼠标操作

塔的建立和升级都要用到鼠标。,那么点击一个位置就要知道是升级的界面,还是建立的界面,并且还要设置一段时间后页面自动关闭。


void gametime::mianban(ExMessage a)
{
	int ddy = a.x / 60, ddx = (a.y - 100) / 60;
	
	if (backmap[ddx][ddy] != -1 && backmap[ddx][ddy] != 1 && backmap[ddx][ddy] != 2)this->showmianban(a.x, a.y);
}
void gametime::showmianban(int x, int y)
{
	if (zhankai)
	{
		int ddy = x / 60, ddx = (y - 100) / 60;
		int sxy = zkx / 60, sxx = (zky - 100) / 60;
		if (ddy == sxy + 1 && sxx == ddx&&backmap[sxx][sxy]!=0)
		{
			zhankai = false;
			tzhankai = clock();
		}
		else if (sxy == ddy && sxx ==ddx)
		{
			if (caichan >= 160)
			{
				weapon* tem = new bullet1(zkx / 60 * 60, (zky - 100) / 60 * 60+100,this);
				ta.push_back(tem);
				//system("pause");
			}
			zhankai = false;
		}
		else if (ddy == sxy && sxx == ddx + 1 && backmap[sxx][sxy] != 0)
		{
			//cout << "jjjjjjjjj" << endl;
			tisheng();
			zhankai = false;
		}
		else
		{
			tzhankai = clock();
			zhankai = true;
			zkx = x, zky = y;
		}
	}
	else
	{
		tzhankai = clock();
		zhankai = true;
		zkx = x, zky = y;
	}
}
void gametime::showwwmian()
{
	if (zhankai)
	{
		auto c = backmap[(zky - 100) / 60][zkx / 60];
		//cout << c << "c=多少" << endl;
		switch (c)
		{
		case 11:transparentimage2(zkx / 60 * 60, (zky - 100) / 60 * 60 - 60+100, &tusheng1, &tusheng2);
			transparentimage2(zkx / 60 * 60+60, (zky - 100) / 60 * 60+100, &cha1, &cha2); break;
		case 0: if (this->caichan >= 160)transparentimage(zkx / 60 * 60, (zky - 100) / 60 * 60+100, 60, 60, 70, 0, &tu1, &tu2); 
			  else transparentimage(zkx / 60 * 60, (zky - 100) / 60 * 60+100, 60, 60, 130, 0, &tu1, &tu2);  break;
		}
		int temt = clock();
		if (temt - tzhankai >= 3000)zhankai = false;
	}
	//transparentimage( 200,100, 60, 60, 130, 0, &tu1, &tu2); 
}

图片旋转

因为怪物会位移,塔的位置不同,所以火炮的方向必须要会旋转。

void bullet1::showimage()
{
	IMAGE ttem1,ttem2;
	if (jiaodu >= 6.28)jiaodu = 0;
	transparentimage(x+10, y+10, 60, 50, 345, 20, &tu1, &tu2);
	rotateimage(&ttem1, &tt1[deng], jiaodu, WHITE);
	rotateimage(&ttem2, &tt2[deng], jiaodu, BLACK);
	transparentimage2(x , y , &ttem2, &ttem1); 
	int tem1, tem2;
	if (gongji == NULL)
	{
		if (jiaodu == 0)return;
		if (jiaodu > 6.283 - jiaodu)
		{
			jiaodu += min(0.001, 6.283 - jiaodu);
			return;
		}
		else
		{
			jiaodu -= min(0.001, jiaodu - 0);
			return;
		}
		return;
	}
	
	gongji->huoqu(tem1, tem2);
	tem2 += 100;
	double len = sqrt((y - tem2 - 5) * 1.0 * (y - tem2 - 5) * 1.0 + (tem1 + 35 - x) * 1.0 * (tem1 + 35 - x));
	double ss = acos((tem1 + 35 - x) * 1.0 / len);
	//cout << tem2 << " " << tem1 << " " << y << " " << x << endl;
	if (abs(ss - jiaodu) < 0.002)
	{
		IMAGE hh,hh2;
		rotateimage(&hh, &guangshu1, jiaodu, WHITE);
		rotateimage(&hh2, &guangshu2, jiaodu, BLACK);
		double zzx= (tem1 + 95 + x)*1.0/2, zzy = (tem2 + 65 + y) * 1.0 / 2;
		double minx = min(tem1 + 65, x + 30), miny = min(tem2+35, y + 30);
		transparentimage(minx,miny,2*zzx-2*minx,2*zzy-2*miny,400-zzx+minx,400-zzy+miny,&hh2,&hh);
		jiaodu = ss;
		gongji->injured(harm[deng]);
		if (gongji->if_death())gongji = NULL;
	}
	else
	{
		if (ss > jiaodu )
		{
			jiaodu += 0.001;
		}
		else
		{
			jiaodu -= 0.001;
		}
	};
}

这里的细节就有很多,需要慢慢调。

主要流程

准备界面

void initialize()
{
	IMAGE img1,h1,h2;
	//initgraph(1280, 720, EW_SHOWCONSOLE);
	//setfillcolor(GREEN);
	loadimage(&img1,_T("backgrondtupian.jpeg"));
	loadimage(&h1, _T("开始游戏.png"));
	loadimage(&h2, _T("开始游戏2.png"));
	initgraph(780, 520, EW_SHOWCONSOLE);
	putimage(0, 0, &img1);
	setfillcolor(GREEN);
	MymciSendString(_T("open background.mp3 alias BackMusic"), NULL);
	MymciSendString("play BackMusic repeat", NULL);
	transparentimage2(-50, 200, &h1, &h2);
	ExMessage m;
	while (1) 
	{ 
		//fillroundrect(200, 550, 400, 650, 30, 30);
		if (peekmessage(&m, EM_MOUSE) && m.lbutton)
	    {
			if (m.x > 30 && m.x < 399 && m.y>362 && m.y < 446)
			{
				game();
				return;
			}
	    }
	}
	closegraph();
}

播放音乐

播放音乐用的mci可以实现很多操作,但我只用了循环播放音乐,更多细节可以看这篇博客

void MymciSendString(const char * szCommand , char* szbuffer)//const string szCommand)
{
	if (NULL == szbuffer)
	{
		if (0 != mciSendString(szCommand, NULL, 0, NULL))
		{
			printf("%s false!\n", szCommand);
		}
		else
		{
			printf("%s success!\n", szCommand);
		}
	}
	else
	{
		if (0 != mciSendString(szCommand, szbuffer, 1024, NULL))
		{
			printf("%s false!\n", szCommand);
		}
		else
		{
			printf("%s success!\n", szCommand);
		}
	}
}

游戏过程

void game()
{
	ExMessage m;
	//initialize();
	//game();
	//initgraph(860, 550, EW_SHOWCONSOLE);
	gametime l;
	map a;
	a.upload(bradish);
	initgraph(860, 550, EW_SHOWCONSOLE);
	//weapon* tem = new bullet1(240, 220, &l);
	while (jieshu)
	{
		BeginBatchDraw();
		a.show(bradish);
		thread zh(showwwwwtime, &l, &a);
		l.jiancheshifou();
		if (peekmessage(&m, EM_MOUSE) && m.lbutton)
		{
			cout << m.x << m.y << endl;
			l.mianban(m);
		}
		zh.join();
		l.showwwmian();
		EndBatchDraw();
		cleardevice();
	}
}

这里用了线程,没啥用虽然,就像试一下。

代优化的部分

  1. mci的只用来了播放音乐没有实现更多操作。
  2. 很多的代码都需要优化,很多地方都很蠢。
  3. 怪物的移动需要被优化,虽然想到方法。
  4. 玩家自己实现地图和怪物数量。

源文件地址

阿里云盘

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值