球球大作战 easyX 类复用

基于c++的easyX写的小游戏,基于 【C/C++游戏开发】球球大作战(附源码)!

C语言游戏教程:球球大作战(超基础)!

对他们的代码进行优化和修改,用java面向对象程序设计 类的思想。

代码

ball.h宏定义

// constants.h

#ifndef CONSTANTS_H  // 防止重复包含
#define CONSTANTS_H

// 宏定义
#define WIN_WIDTH 1024 //窗口宽
#define WIN_HEIGHT 640  //窗口高

#define MAP_WIDTH (WIN_WIDTH*3)
#define MAP_HEIGHT (WIN_HEIGHT*3)

#define RADIUS_BALL  16

//ball属性
#define NUM_FOOD 500
#define RADIUS_FOOD 4

#define Speed_DEFAULT 2

//enemy属性
#define NUM_ENEMY 45



#endif

Ball.h实体类 ball是抽象类,spire是用户操控的球,food 是食物,enemy是敌人。

#pragma once
#include "constants.h"
#include<iostream>
#include"Utilitys.h"
using namespace std;

extern IMAGE map;


//求两点之间距离的平方
int DisTance(POINT* b1, POINT* b2);
// 载入PNG图并去透明部分
void drawAlpha(IMAGE* picture, int  picture_x, int picture_y);
//缩放函数
void ZoomImage(IMAGE* P, IMAGE* Q, double ZoomRate);

class Ball {

public:
	POINT posi;
	int r;

	int speedX;
	int speedY;

	POINT direction;

	bool existsMap;//是否被吃掉

	virtual void init() = 0;//初始化


	virtual void showMe() = 0;//展示当前对象


	void moveForward() {
		//前进一步
		int tempX = posi.x + speedX * direction.x;
		if(tempX<= MAP_WIDTH - r  && tempX>=r)
			posi.x =tempX;

		int tempY= posi.y + speedY * direction.y;
		if (tempY <= MAP_HEIGHT-r && tempY >= r)
			posi.y=tempY;
	
	}//

	void updateRadius(int eatenR) {
		//吃掉了半径为r的球
		r += (eatenR >> 2);
	}
};



class Food :public Ball {
public:
	COLORREF color;
	void init() {
		color = RGB(rand() % 256, rand() % 256, rand() % 256);
		posi.x = rand() % MAP_WIDTH;
		posi.y = rand() % MAP_HEIGHT;
		r = RADIUS_FOOD;
		speedY = speedX = 0;
		existsMap = true;
		setfillcolor(color);
		solidcircle(posi.x, posi.y,r);
	}

	void showMe() {
		if (existsMap) {
			setfillcolor(color);
			solidcircle(posi.x, posi.y, r);
		}
		else
			init();
	}


};


class Spire :public Ball {
public:
	ExMessage msg = { 0 };//消息变量

	IMAGE ballIMG; // 定义一个IMAGE结构

	void init() {
		loadimage(&ballIMG, _T("E:/FinalWork/picture/ball(1).png"), RADIUS_BALL, RADIUS_BALL); // 加载图片
		int ballWidth = ballIMG.getwidth(); // 获取图片宽度
		int ballHeight =ballIMG.getheight(); // 获取图片高度

		existsMap = 1;

		r = RADIUS_BALL;

		// 计算图片的左上角坐标
		posi.x = (WIN_WIDTH - ballWidth) / 2;
		posi.y = (WIN_HEIGHT - ballHeight) / 2;
		

		direction.x = 0; direction.y = 0;
		
		speedX = Speed_DEFAULT; speedY = Speed_DEFAULT;

		drawAlpha(&ballIMG, posi.x, posi.y);
	}

	void showMe() {
		
		if (existsMap)
		{
			Resize(&ballIMG, r, r);
			loadimage(&ballIMG, _T("E:/FinalWork/picture/ball(1).png"), r, r);
			drawAlpha(&ballIMG, posi.x, posi.y);
			
		}
		else
			init();
	}


	void updateDirection(Ball* balls = nullptr) {
		//获取消息,方向
		if (peekmessage(&msg)) {
			//按键按下
			if (msg.message == WM_KEYDOWN) {
				switch (msg.vkcode)
				{
				case VK_UP:
					direction.y = -1;
					break;
				case VK_DOWN:
					direction.y = 1;
					break;
				case VK_LEFT:
					direction.x = -1;
					break;
				case VK_RIGHT:
					direction.x = 1;
					break;
				default:
					break;
				}
			}
			else if (msg.message==WM_KEYUP){
				switch (msg.vkcode)
				{
				case VK_UP:
				case VK_DOWN:
					direction.y = 0;
					break;
				case VK_LEFT:
				case VK_RIGHT:
					direction.x = 0;
					break;
				default:
					break;
				}
			}
		}

	}

};

extern Spire user;//外部变量

class Enemy :public Ball {
public:
	COLORREF color;
	void init() {

		direction.x = 0; direction.y = 0;

		speedX = Speed_DEFAULT; speedY = Speed_DEFAULT;

		//初始化AI
		
		
		color = RGB(rand() % 256, rand() % 256, rand() % 256);  //rand()%256  随机取值 0-255
		existsMap= 1;
		posi.x = rand() % (MAP_WIDTH - (r + 1)) + (r + 1);  //AI产生的位置不会出现一个越界
		posi.y = rand() % (MAP_HEIGHT - (r + 1)) + (r + 1);
		r = (rand() % 10 + 5);
		
	}

	void showMe() {
		if (existsMap) {
			setfillcolor(color);
			solidcircle(posi.x, posi.y, r);
		}
		else
			init();
		
	}

	
};

// 载入PNG图并去透明部分
void drawAlpha(IMAGE* picture, int  picture_x, int picture_y) //x为载入图片的X坐标,y为Y坐标
{
	//SetWorkingImage(&map);
	// 变量初始化
	DWORD* dst = GetImageBuffer();    // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
	DWORD* draw = GetImageBuffer();
	DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
	int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
	int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
	
	//int graphWidth = getwidth();       //获取绘图区的宽度,EASYX自带
	//int graphHeight = getheight();     //获取绘图区的高度,EASYX自带
	int graphWidth = WIN_WIDTH;       //获取绘图区的宽度,EASYX自带
	int graphHeight = WIN_HEIGHT;     //获取绘图区的高度,EASYX自带

	int dstX = 0;    //在显存里像素的角标

	// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
	for (int iy = 0; iy < picture_height; iy++)
	{
		for (int ix = 0; ix < picture_width; ix++)
		{
			int srcX = ix + iy * picture_width; //在显存里像素的角标
			int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
			int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
			int sg = ((src[srcX] & 0xff00) >> 8);   //G
			int sb = src[srcX] & 0xff;              //B
			if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
			{
				dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
				int dr = ((dst[dstX] & 0xff0000) >> 16);
				int dg = ((dst[dstX] & 0xff00) >> 8);
				int db = dst[dstX] & 0xff;
				draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)  //公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr
					| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)         //αp=sa/255 , FP=sg , BP=dg
					| (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
			}
		}
	}
}


//求两点之间的距离的平方
int DisTance(POINT *b1, POINT *b2)
{
	return ((b1->x - b2->x) * (b1->x - b2->x)
		+ (b1->y - b2->y) * (b1->y - b2->y));
}

//基于双线性插值算法的图像缩放函数,有修改 https://tieba.baidu.com/p/3587996765
void ZoomImage(IMAGE* P, IMAGE* Q, double ZoomRate)
{
	//P保存后的图片,Q要缩放的图片,zoomrate缩放比例
	P->Resize((int)(Q->getwidth() * ZoomRate), (int)(Q->getheight() * ZoomRate));

	DWORD* M = GetImageBuffer(P);
	DWORD* N = GetImageBuffer(Q);

	{
		for (int i = 0; i < P->getheight(); i++)
			for (int j = 0; j < P->getwidth(); j++)

				M[j + i * P->getwidth()] = N[(int)(j / ZoomRate) + (int)(i / ZoomRate) * Q->getwidth()];
	}
}

 utilities.h 工具类,用于处理类和类交互,碰撞检测,存在检验这类的。

#pragma once
#include"constants.h"
#include"Ball.h"

extern Spire user;
extern Food foods[NUM_FOOD];//食物
extern Enemy enemys[NUM_ENEMY];//敌人


//用于处理food user enemys交互
class Utilitys
{
public:
	//找到a最近的球
	static Ball* findMinDistaceBall(Ball* a) {
		//找到距离最小的食物或者敌人
		Ball* target = nullptr;
		int min_DISTANCE = MAP_WIDTH * MAP_WIDTH;//最大搜索距离

		//AI靠近AI
		for (int k = 0; k < NUM_ENEMY; k++)
		{
			if (a->r > enemys[k].r && enemys[k].existsMap == 1)
			{//如果能吃,且距离达到要求则更新距离,并保存下标
				int dis = DisTance(&a->posi, &enemys[k].posi);
				if (dis < min_DISTANCE)
				{
					min_DISTANCE = dis;
					target = &enemys[k];
				}
			}
		}

		//AI靠近玩家
		int dis = DisTance(&a->posi, &user.posi);
		if (dis < min_DISTANCE)
		{
			min_DISTANCE = dis;
			target = &user;
		}

		//AI靠近食物
		for (int k = 0; k < NUM_FOOD; k++)
		{
			if (a->r > enemys[k].r && foods[k].existsMap == 1)
			{//如果能吃,且距离达到要求则更新距离,并保存下标
				int dis = DisTance(&a->posi, &foods[k].posi);
				if (dis < min_DISTANCE)
				{
					min_DISTANCE = dis;
					target = &foods[k];
				}
			}
		}
		//如果找到目标,则去追逐
		return target;


	}

	static void updateEnemysDirection(Enemy* a ) {
		if (!a->existsMap)
			return;

		Ball* target = findMinDistaceBall(a);
		//改变方向,朝着食物去
		if (rand() % 2) {
			a->direction.x = (a->posi.x < target->posi.x) ? 1 : -1;
		}
		else {
			a->direction.y = (a->posi.y < target->posi.y) ? 1 : -1;
		}
	}

	//判断两个点是否碰撞
	static bool collision(Ball* a, Ball* b) {
		int dis = (a->r + b->r) * (a->r + b->r);
		return  DisTance(&a->posi, &b->posi) <= dis;
	}

	static void judgeCollosion(Ball* a, Ball* b) {
		//判断本球是否和其他敌人相撞, 是的话,更改球的状态
		if (!a->existsMap || !b->existsMap || !collision(a, b))
			return;

		if (a->r > b->r) {
			b->existsMap = false;
			a->updateRadius(b->r);
		}
		else
		{
			a->existsMap = false;
			b->updateRadius(a->r);
		}
	}

	//对于所有的球碰撞的检测算法
	static void judgeCollosionAll() {
		//user和ai
		for (int i = 0; i < NUM_ENEMY; i++) {
			judgeCollosion(&user, &enemys[i]);
		}

		//user和食物
		for (int i = 0; i < NUM_FOOD; i++) {
			judgeCollosion(&user, &foods[i]);
		}

		//AI和AI, 食物
		for (int i = 0; i < NUM_ENEMY; i++) {
			if (!enemys[i].existsMap)
				continue;
			//AI
			for (int j = i + 1; j < NUM_ENEMY; j++) {
				if (!enemys[i].existsMap)
					continue;
				judgeCollosion(&enemys[i], &enemys[j]);
			}
		
			//食物
			for (int j = 0; j < NUM_FOOD; j++) {
				judgeCollosion(&enemys[i], &foods[j]);
			}
		}

		
	}
};

 待解决的地方

1、碰撞检测,暴力遍历所有的ball,有搜索过更高明的检测算法,但是太复杂了,呜呜

2、easyx图片的伸缩,尝试过用数学方法,但效果不好,只能重新loadimge

3、碰撞检测的抽象,用ball数组会导致向上转型,数据冲突,只能一个个检测

4、不够丝滑

优点

1、对参考代码进行了抽象,易于复用和扩展

2、引入了图片,虽然很卡

3、对图片去除了背景,虽然不是我写的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值