基于c++的easyX写的小游戏,基于 【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、对图片去除了背景,虽然不是我写的