从零开始用 Windows C++ 桌面程序制作方舟同人游戏(五)

一、角色信息的定义与绘制

继障碍物后,重要的就是要把敌人加进来。敌人加进来之后需要与其进行战斗。而战斗则与属性等数值挂钩。我们需要建立侧边栏,实时显示出角色的数值来监测战斗。因此,现在我们的任务是将 SideBar 类实现一部分。

再看一看 SideBar.h:

class SideBar
{
	std::vector<COperator> operators; // 当前干员
	COperator currentOperator; // 当前出战的干员
	int ingot; // 源石锭
	int cost; // 部署费用
};

注意 COperatorMCharacter 的不同。COperator 是只存在于 SideBar 中的数据,而 MCharacter 是需要实际被绘制在地图上的角色。当 MCharacter 与其他地图元素发生交互时(与敌人战斗,捡到宝物等等),就会将信息返回给 SideBar,由 SideBar 处理结果。

SideBar 的头文件:

#pragma once
#include <vector>
#include "Coperator.h"
class SideBar
{
	std::vector<COperator*> operators; // 当前干员
	COperator* currentOperator; // 当前出战的干员
	unsigned int ingot; // 源石锭
	unsigned int cost; // 部署费用
public:
	unsigned int getIngot() { return ingot; }
	unsigned int getCost() { return cost; }
	void setIngot(unsigned int i) { ingot = i; }
	void setCost(unsigned int c) { cost = c; }
};

现在添加一系列绘图函数与变量以将边栏绘制出来。

准备好斯卡蒂的头像 Skadi.bmp:

在这里插入图片描述

边栏的材质图片 SideBar.bmp(灰色纯色,测试材质 qwq):
在这里插入图片描述

创建句柄:

HBITMAP hSideBar; // 边栏图句柄
HBITMAP hAvatar; // 边栏干员头像

先在 Game_Init() 中加入边栏材质载入的内容:

hAvatar = (HBITMAP)LoadImage(NULL, L"res/Skadi.bmp", IMAGE_BITMAP, CELL_SIZE, CELL_SIZE, LR_LOADFROMFILE);
    hSideBar = (HBITMAP)LoadImage(NULL, L"res/SideBar.bmp", IMAGE_BITMAP, CELL_SIZE * 6, CELL_SIZE * 12, LR_LOADFROMFILE);

再新建一个 SideBar_Paint() 函数。

VOID SideBar_Paint(HWND hwnd); // 边栏信息的绘制

好的。到这里我发现,绘图的内容太多了。如果每个都一个一个打到 hdc 上去显示,很容易又导致闪烁问题出现。因此这里采用缓冲显示,先将所有内容绘制到缓冲区,再一次性打到 hdc 上。

创建缓冲 DC 和一个默认的位图:

HDC bufDC; // 缓冲DC
HBITMAP bufMap;
...
bufMap = CreateCompatibleBitmap(hdc, WINDOW_WIDTH, WINDOW_HEIGHT);
SelectObject(bufDC, bufMap);

将原来的所有与 hdc 相关的贴图操作都改为 bufDC,然后再在 Game_Paint() 中加一句:

BitBlt(hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufDC, 0, 0, SRCCOPY);

这次真的是彻底解决了闪烁问题……

愿人世间再无画面闪烁 qwq

那么可以安心写 SideBar_Paint() 的实现了:

VOID SideBar_Paint(HWND hwnd)
{
    SelectObject(mdc, hSideBar);
    BitBlt(bufDC, CELL_SIZE * 12, 0, CELL_SIZE * 6, CELL_SIZE * 12, mdc, 0, 0, SRCCOPY);
    SelectObject(mdc, hAvatar);
    BitBlt(bufDC, CELL_SIZE * 12 + CELL_SIZE / 2, CELL_SIZE / 2, CELL_SIZE, CELL_SIZE, mdc, 0, 0, SRCCOPY);
}

现在要绘制文字以显示信息。

但由于源石锭、部署费用这些东西以及文字说明其实都是固定不动的,为了节省资源将他们直接画在图片上,而不再使用 BitBlt().

从代码中大伙都知道了:边栏的大小为:6 倍单元格大小 x 12 倍单元格大小。图片尺寸我们设为 192x384.

SideBar.bmp:

在这里插入图片描述

现在,我们将游戏内的文字显示在这上面。

首先加一个表示字体大小的宏:

#define FONT_SIZE 24

Game_Init() 中创建字体:

// 全局变量
HFONT hFont; // 字体句柄
...
// Game_Init()函数
hFont = CreateFont(FONT_SIZE, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, L"微软雅黑");

SideBar_Paint() 中加入字体绘制:

    wchar_t fName[20], fIngots[20], fCosts[20];
    wsprintf(fName, L"Skadi");
    wsprintf(fIngots, L"%d", sidebar.getIngot());
    wsprintf(fCosts, L"%d", sidebar.getCost());

    TextOut(bufDC, CELL_SIZE * 12 + CELL_SIZE * 1.75, CELL_SIZE, fName, wcslen(fName));

稍微像模像样一点的边栏出现了。

在这里插入图片描述

然而,SideBar 类中的 COperator 仍然没有得到使用。现在我们将文件和角色联系起来,创建出与角色挂钩的信息……

新建一个 Operators.hOperators.cpp 用于初始化各个干员。

// Operators.h
#pragma once
#include "COperator.h"

void Operator_Init(COperator& op, double hp, double ad, double dp, double mr, AttackType type, double hpg, double dpg, double adg);
void Operators_Init();

// Operators.cpp
#include "Operators.h"

void Operator_Init(COperator& op, double hp, double ad, double dp, double mr, AttackType type, double hpg, double dpg, double adg)
{
	op.SetHealthPoint(hp);
	op.SetAttackDamage(ad);
	op.SetDefensePoint(dp);
	op.SetMagicResistance(mr);
	op.SetAttackType(type);
	op.SetHealthPointGrow(hpg);
	op.SetDefensePointGrow(dpg);
	op.SetAttackDamageGrow(adg);
	op.SetHealthPointNow(Skadi.getHealthPoint());
}

// 主程序源文件
void Operators_Init()
{
	// Skadi:
	Operator_Init(Skadi, 1000.0, 100.0, 50.0, 0.0, AttackType::Physical, 400.0, 40.0, 50.0);
}

在主程序中 include 该文件,并在 Game_Init() 中调用 Operators_Init()

总感觉 Game_Init() 越来越臃肿了,背负的东西太多了捏……

设初始的干员堆只有斯卡蒂一人,由此初始化。

接着,我们实现 SideBar 中关于当前干员的操作。

添加一个友元函数 SideBar_Init():

void SideBar_Init()
{
    sidebar.ingot = 32;
    sidebar.cost = 10;
    sidebar.operators.push_back(&Skadi);
}

在主程序中调用这个函数。

对了,还要对每个角色加上角色名这一属性。在 COperator 类中添加 GetName()SetName() 函数,并在 SideBar 类中添加 GetCurrentOperator() 函数。具体内容就不一一展示了(比较简单了吧),其中角色名是 const wchar_t* 类型的。相应的修改一系列初始化函数,最后把 SideBar_Paint() 中的

wsprintf(fName, L"Skadi");

改为

wsprintf(fName, sidebar.GetCurrentOperator()->GetName());

就可以了!

二、干员切换功能

现在我们尝试增加切换干员的功能。

首先在 SideBar 类中添加一个函数用于切换干员:

// .h
bool ShiftOperator();

// .cpp
bool SideBar::ShiftOperator()
{
	if (operators.empty())
		return false;
	COperator* tmp = currentOperator;
	currentOperator = operators.back();
	operators.push_back(tmp);
	return true;
}

除此之外,为了让其在地图上的贴图也正确改变,还需要为 COperator 类添加一个变量用于指定贴图 id。

再次修改 SideBar_Init():

void SideBar_Init()
{
    sidebar.ingot = 32;
    sidebar.cost = 10;
    sidebar.currentOperator = &Skadi;
    gamePlayer.SetID(sidebar.currentOperator->GetID());
}

为 SideBar 类新建一个 ShiftOperator() 函数:

bool SideBar::ShiftOperator()
{
	if (operators.empty())
		return false;
	COperator* tmp = currentOperator;
	currentOperator = operators.back();
	operators.push_back(tmp);
	return true;
}

查 Windows 虚拟键表,将几个常用字母的宏定义定好:

// 虚拟键表宏定义
#define KEYBOARD_E 69
#define KEYBOARD_Q 81
#define KEYBOARD_L 76
#define KEYBOARD_S 83

再在消息处理程序中添加 KEYBOARD_E 的响应事件。

case KEYBOARD_E:
            sidebar.ShiftOperator(); // 边栏信息修改
	gamePlayer.SetID(sidebar.GetCurrentOperator()->GetID()); // 地图信息修改

            break;

函数完成,现在我们再加一个干员 Surtr,其素材:

103.bmp

在这里插入图片描述

Surtr.bmp

在这里插入图片描述

有点抽象,不过先凑合着测试啦哈哈

Operators_Init() 中:

// Surtr:
    Operator_Init(Surtr, 800.0, 70.0, 40.0, 15.0, AttackType::Magic, 350.0, 40.0, 48.0, L"Surtr", 103);

按 E 切换干员!

在这里插入图片描述

在这里插入图片描述

再将干员的四维(生命值,防御力,攻击力,法抗) 显示出来。这里略过了(位图绘制之前写过太多了 qwq)

效果:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值