2024-10-24 21:50:06 +08:00

616 lines
9.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
1.导入素材
2.实现游戏场景界面初始化
3.实现游戏界面顶部工具栏
4.实现工具栏中的植物卡牌
*/
#include <graphics.h> // 引用图形库头文件
#include<stdlib.h>
#include<stdio.h>
#include"tools.h"
#include<time.h>
#define Wiw_width 900
#define Wiw_heigh 600
enum { WANDOU, XRK, JIANGUO,ZHIWU_COUNT };//枚举定义植物类型和数量
//将卡牌数量放到枚举最后一个,即可巧妙的自动表示数组长度(即卡牌数量)
IMAGE imgBg;//表示背景图片
IMAGE imgToor;//表示工具栏
IMAGE imgCards[ZHIWU_COUNT];//植物卡牌数组
IMAGE *imgplt[ZHIWU_COUNT][20];//每个植物有多个图片,形成动态效果
int percentX, percentY;//定义全局变量,当前选中植物所在坐标
int percent_plant;//0:没有选中 1选中第一种
struct plant
{
int type;//第几种植物0没有植物1第一种植物
int frameIndex;//序列帧序号
};
struct plant map[5][8];//地图上可以放五行八列的植物
struct sunshineBall
{
//阳光位置
int x, y;//飘落过程中的坐标位置x不变
int frameIndex;//阳光图片帧序号
int fixedY;//最终位置
bool used;//是否在使用
int timer;//计时器为了使阳光存在时间加长
};
//池
/*
预先准备10个阳光球
如果频繁的创建阳光球不方便管理
*/
struct sunshineBall balls[10];
//定义一个图片数组储存阳光
IMAGE imgSun[29];
int Suncount;//阳光的数量
bool fileExist(const char* Zwname)
{
FILE* fp = fopen(Zwname, "r");
if (fp == NULL)
{
return false;
}
else
{
fclose(fp);
return true;
}
}
void gameInit()
{
int j;
//加载背景图片
//把字符集修改为多字符集
loadimage(&imgBg, "res\\map0.jpg");
//加载工具栏图片
loadimage(&imgToor, "res\\bar5.png");
memset(imgplt, 0, sizeof(imgplt));
memset(map, 0, sizeof(map));
memset(balls, 0, sizeof(balls));
//加载多个植物卡牌图片
int i;
int suni;
//生成植物卡牌文件名
char Zwname[1024];
char Sunname[1024];
for (i = 0; i < ZHIWU_COUNT; i++)
{
//???
sprintf_s(Zwname, sizeof(Zwname), "res\\Cards\\card_%d.png", i + 1);
loadimage(&imgCards[i], Zwname);
for (j = 0; j < 20; j++)
{
sprintf_s(Zwname, sizeof(Zwname), "res\\Plants\\%d\\%d.png", i,j + 1);
//先判断文件是否存在
//通过文件是否能打开判断是否存在
if (fileExist(Zwname))
{
//开始设置的*imgplt是空指针直接加载会崩掉
//所以要分配内存
imgplt[i][j] = new IMAGE;//因为这个图形库函数是c++分配内存不能用malloc,用c++的方式来分配内存
//上方代码是c++内容,暂时看不懂
loadimage(imgplt[i][j], Zwname);
}
else
{
break;
}
}
}
percent_plant = 0;
//阳光池
//memset(balls, 0, sizeof(balls));
for (suni = 0; suni < 29; suni++)
{
sprintf_s(Sunname, sizeof(Sunname),"res\\sunshine\\%d.png", suni + 1);
//"C:\\Users\\URL\\source\\repos\\植物大战僵尸\\植物大战僵尸\\res\\sunshine\\%d.png"
loadimage(&imgSun[suni], Sunname);
}
//配置随机种子
srand(time(NULL));
//创建游戏图形化窗口
initgraph(Wiw_width, Wiw_heigh);
Suncount = 50;//初始阳光的数值
}
//渲染函数,把图片加载出来
void update_window()
{
//BeginBatchDraw函数用于启动批量绘图模式。
//在这种模式下,所有的绘图操作都不会立即显示在屏幕上,
//而是暂时存储在内存中直到调用FlushBatchDraw或EndBatchDraw函数时才会一次性渲染到屏幕上。
//这种方法可以有效减少绘图过程中的闪烁现象,
//特别是在动画或连续移动的图形中
BeginBatchDraw();//开始缓冲
int i, j, line, cloumn;
int index, planttype;
int iS;
int ballMax;
putimage(0, 0, &imgBg);
putimagePNG(250, 0, &imgToor);
for (i = 0; i < ZHIWU_COUNT; i++)
{
j = 340 + 64 * i;
putimage(j,6, &imgCards[i]);
}
//判断是否有植物
for (line = 0; line < 5; line++)
{
for (cloumn = 0; cloumn < 9; cloumn++)
{
if (map[line][cloumn].type > 0)
{
/*
line = (msg.y - 70) / 103;//第几行
column = (msg.x - 250) / 82;//第几列*/
index = map[line][cloumn].frameIndex;
planttype = map[line][cloumn].type - 1;
//可以微调渲染位置
putimagePNG(250 + cloumn * 82+2, 70 + line * 103+15, imgplt[planttype][index]);
}
}
}
//为了使拖动的植物图层在最上面
// 即拖动时被拖动植物覆盖已经种植的植物
// 所以将拖动中渲染放到最后
//渲染拖动中的植物
if (percent_plant > 0)
{
//为了方便定义一个变量img去获取长宽为了使鼠标指在渲染图片中央
IMAGE* img = imgplt[percent_plant - 1][0];
putimagePNG(percentX - img->getwidth() / 2, percentY - img->getheight() / 2, img);
}
ballMax = sizeof(balls) / sizeof(balls[0]);
for (iS = 0; iS < ballMax; iS++)
{
if (balls[iS].used)
{
IMAGE *img = &imgSun[balls[iS].frameIndex];
putimagePNG(balls[iS].x, balls[iS].y,img);
}
}
EndBatchDraw();//结束双缓冲
}
void collectSun(ExMessage* msg)
{
int i;
for (i = 0; i < sizeof(balls); i++)
{
if (msg->x == balls[i].x && msg->y == balls[i].y)
{
Suncount += 50;
}
}
}
void userClick()
{
//用户鼠标操作交互实现
//判断有没有消息
int index;
int line;
int column;
ExMessage msg;
static int status = 0;//定义静态变量,目的是确保选择后再拖动
if (peekmessage(&msg))
{
if (msg.message == WM_LBUTTONDOWN)
{
//判断是否在卡牌区域内操作
if (msg.x > 340 && msg.x < 340 + 64 * ZHIWU_COUNT && msg.y > 0 && msg.y < 97)
{
index = (msg.x - 340) / 64;
status = 1;
percent_plant = index + 1;
//printf("%d\n", index);
//验证鼠标选择植物卡牌是否准确
}
else
{
//如果不在卡牌区域内操作,执行以下代码(收集阳光,或铲子回收)
collectSun(&msg);//可以直接传递msg但传递一个指针程序消耗少
}
}
/*
图片可视化即是渲染,即在窗口某个坐标位置
出现图片,所以下面代码得用渲染函数处理
*/
else if (msg.message == WM_MOUSEMOVE && status == 1)
{
percentX = msg.x;
percentY = msg.y;
//拖动过程显示卡牌植物,不是卡牌,进行素材补充
//确定哪一种植物再次添加全局变量status
}
else if (msg.message == WM_LBUTTONUP)
{
line = (msg.y - 70) / 103;//第几行
column = (msg.x - 250) / 82;//第几列
//printf("%d%d\n", line+1, column+1);验证种植坐标是否准确
//种植位置确定,且确保一个位置只有一个植物
if (map[line][column].type == 0)
{
map[line][column].type = percent_plant;
map[line][column].frameIndex = 0;
}
percent_plant = 0;
status = 0 ;
}
}
}
//创建阳光
void createSun()
{
//控制阳光的刷新频率
int i;
static int count = 0;
static int fre = 1;//400
count++;
//每400+帧数刷新一次阳光
if (count >= 4)
{
count = 0;
fre = 120 + rand() % 200;
//从阳光池中取一个可以使用的
int ballMax = sizeof(balls) / sizeof(balls[0]);
//找到一个可以使用的阳光球的帧
for (i = 0; (i <= ballMax) && balls[i].used; i++);
if (i >= ballMax)
{
return;
}
balls[i].used = true;
balls[i].frameIndex = 0;
//阳光降落x范围260-930
balls[i].x = rand() % (900 - 260) + 260;
//以下代码实现第一个阳光球的x坐标小于345
static int test = 0;
if (test == 0)
{
balls[i].x = 852;//256
test = 1;
}
//经过以上测试只要第一个阳光球生成的x坐标<=345就可以运行下去
//balls[i].x = 345;
/*
经过找错找出
x坐标大于345出现报错问题
有阳光会不断在第一个x坐标位置下落一小段距离消失,
若将x的值小于等于0的阳光会正常下落但当落下来消失的那一刻会出现报错问题
*/
/*
问题在第一个阳光上
1.坐标范围会导致内存冲突什么的
2.第一个阳光会一直存在不消失
*/
balls[i].y = 60;//阳光的初始位置
//阳光的固定落点
balls[i].fixedY = 200 + (rand() % 4) * 90;
//balls[i].fixedY = rand() % (560 - 190) + 190;
balls[i].timer = 0;
}
}
//阳光位置在竖直向下移动,同时旋转
void moveSun()
{
int i;
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (i = 0; i < ballMax; i++)
{
if (balls[i].used)
{
//%取余是防止阳光球帧数超出图片数
balls[i].frameIndex = (balls[i].frameIndex + 1) % 29;
if (balls[i].timer == 0)
{
balls[i].y += 4;
}
if (balls[i].y >= balls[i].fixedY)
{
balls[i].timer++;
//balls[i].used = false;
if (balls[i].timer >= 100)
{
balls[i].used = false;
}
}
}
}
}
void updateGame()
{
int i;
int j;
int k;
int planttype;
int index;
/*for (k = 0; k< 100000000; k++)
{
;
}*/
//实现植物摇晃
for (i = 0; i < 5; i++)
{
for (j = 0; j < 9; j++)
{
if (map[i][j].type > 0)
{
map[i][j].frameIndex++;
planttype = map[i][j].type - 1;
index = map[i][j].frameIndex;
if (imgplt[planttype][index] == NULL)
{
map[i][j].frameIndex = 0;
}
}
}
}
//修改游戏数据,即是更新阳光球数据
createSun();
moveSun();
/*
以上两个函数内容有问题
*/
}
//初始菜单
void startUI()
{
IMAGE imgSTART,Menu1,Menu2;
loadimage(&imgSTART, "res\\menu.png");
loadimage(&Menu1, "res\\menu1.png");
loadimage(&Menu2, "res\\menu2.png");
//状态变量
int flag = 0;
while (1)
{
BeginBatchDraw();
putimage(0, 0, &imgSTART);
putimagePNG(460, 120, flag ? &Menu1 : &Menu2);
ExMessage msg;
if (peekmessage(&msg))
{
if (msg.message == WM_LBUTTONDOWN )
{
if (msg.x > 460 && msg.x < 460 + 300 && msg.y>75 && msg.y < 250)
{
flag = 1;
}
}
else if (msg.message == WM_LBUTTONUP&&flag==1)
{
//鼠标抬起后进入游戏
return;
}
}
EndBatchDraw();
}
}
int main()
{
gameInit();
startUI();
int timer = 0;
bool flag = true;
while (1)
{
userClick();
//植物摇摆太快直接用Sleep();会导致用户点击延迟
//getDelay();是工具包里的函数,返回值是距离上一次调用的时间差
//通过以下判断当时间差大于20ms再进行渲染
/*下面是实现延迟调用函数的方法
int getDelay() {
static unsigned long long lastTime = 0;
unsigned long long currentTime = GetTickCount();
if (lastTime == 0) {
lastTime = currentTime;
return 0;
}
else {
int ret = currentTime - lastTime;
lastTime = currentTime;
return ret;
}
}
*/
timer += getDelay();
if (timer > 60)
{
flag = true;
timer = 0;
}
if (flag)
{
flag = false;
update_window();
updateGame();
}
//每运行一次改变游戏内的相关数据,即改变植物图片形成动态效果
}
//system("pause");
return 0;
}
/*
问题
调试时点击进入游戏之后直接运行结束
第311行代码&& balls[i].used删掉后问题解决
即是执行313以下代码导致
moveSun();
*/