游戏截图:

游戏截图1

游戏截图2

游戏介绍

我的职务:程序、美术、策划

此游戏为我与两名同班同学合作制作,为刚刚接触C++的第一款Win32面向对象游戏作品。我在项目中担任程序与美术,Yuuki担任音乐,GH担任测试与推广。

玩法简介

按空格起跳,躲避障碍

美术风格

因为框架优化奇差的问题,我必须选择像素风格。

但既然已经选用了像素,那就将像素简约的优点发扬光大。所以我选择舍弃了颜色,使用黑白风格。而且完全使用了不带任何渐变的边缘,防止产生模糊的感觉。

既然是我们自己做着玩r的游戏,我们在其中夹带了许多私货。但是既然夹带了私货那就得让别人能看出来。所以我在本来只有黑白两色的画面中添加了很亮眼的色彩,每个色彩都高度概括了我们夹带的私货。

音乐设计

既然选择了像素风格,那音乐必然得选用8bit。

Yukki将5首我们耳熟能详的8bit音乐缝合在了一起,写出了背景循环音乐


        

技术路线

通过枚举值储存游戏阶段,在Update函数中根据不同枚举值做出不同的反应

1
2
3
4
5
6
7
8
enum EGameState
{
eGS_Menu,
eGS_Help,
eGS_About,
eGS_Play,
eGS_Gameover,
};

类设计

  1. 玩家
    • 初始化时添加事件监听器,加载指定bmp贴图,并设置坐标以及碰撞箱大小;
    • 更新时重绘贴图以及碰撞箱,以及重力响应;
    • 空格按下时跳跃,给垂直方向初速度;
  2. 障碍物
    • 初始化时随机生成石头或者鸟,石头则初始坐标在地面,鸟则在天空。加载对应bmp贴图;
    • 刷新时每帧向左移动
    • 当玩家跳过去后加分
    • 当玩家撞到后游戏结束
    • 当自己出屏幕后销毁

按位读取BMP文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (m_bpp == 32)//32位纹理直接拷贝
{

for (uint32 i = 0; i < m_width * m_height; ++i)
{
m_data[i] = (colorStart[i * 4 + 0] << 0) | (colorStart[i * 4 + 1] << 8) |
(colorStart[i * 4 + 2] << 16) | (colorStart[i * 4 + 3] << 24);
}

}
else if (m_bpp == 24)//24位纹理逐位拷贝 //存储的时候是GBR的存储方法
{
for (uint32 i = 0; i < m_width * m_height; ++i)
{
m_data[i] = 0xff << 24 | (colorStart[i * 3 + 0] << 0) |
(colorStart[i * 3 + 1] << 8) |
(colorStart[i * 3 + 2] << 16);
}
}

背景滚动

通过图片大小归一化处理制作,也就是UV的方法进行循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GtGameDemo::BeiJingGunDong(GtBitmap* bg, float2 pos1, float2 pos2, float speed, float* t, float fElapsedTime)
//pos1为绘制区域左上角坐标,pos2为右下角,通过全局外部变量t来控制位置
{
int2 k(0, 0);
for (uint32 iy = (pos1.y * g_context.height); iy < (pos2.y * g_context.height); ++iy)
{
k.y++;
for (uint32 ix = (pos1.x * g_context.width); ix < (pos2.x * g_context.width); ++ix)
{
k.x++;
uint32 address = (iy * g_context.width + ix);
if (address > 0 && address < g_context.width * g_context.height)
{//这里的texXhetexY即为图片UV坐标
texX = k.x / ((float)bg->GetWidth() * mult.x);
texY = k.y / ((float)bg->GetHeight() * mult.y);
g_context.buffer[address] = bg->getColor(float2(texX + *t, texY));
}
}
}
*t += speed * fElapsedTime;
}

计分系统

我自己绘制了数字0-9的bmp图像,导入之后通过对分数求余计算分数的十位以及个位是什么数字,然后显示。

1
2
3
4
//对100求余再除以10,为十位上的数字
g_context.painter->DrawBitmap(number[(score % 100) / 10], pos.x, pos.y, size, size, 5, eBm_Direct, int2(1, 1));
//对10求余,即为个位上的数字
g_context.painter->DrawBitmap(number[score % 10], pos.x + size, pos.y, size, size, 5, eBm_Direct, int2(1, 1));

音频系统

考虑了PlaySound函数无法进行音频混响,所以选用了MCI音频系统

使用方法如下:

1
2
3
4
5
6
7
8
9
#include <windows.h>
#include <Mmsystem.h>
#pragma comment(lib, "winmm.lib")

int main()
{
mciSendString(TEXT("open Fail.wav alias fail"), NULL, 0, NULL);
return 0;
}

其中text部分为(操作 音频源 别称 循环/开始位置/结束位置)

比如

1
2
3
4
5
6
//打开音频Fail.wav,别称为fail
mciSendString(TEXT("open Fail.wav alias fail"), NULL, 0, NULL);
//将别称为click的音频从头开始播放
mciSendString(TEXT("play click from 0"), NULL, 0, NULL);
//将别称为bgm的音效从头播放,并且循环
mciSendString(TEXT("play bgm from 0 repeat"), NULL, 0, NULL);

Github链接

如果可以的话随手点开star一下岂不美哉#滑稽

Github链接