基于EasyX和Raylib的坚持100秒
Raylib 播放音频毫无压力, 相比之下 EasyX 需要借助 Windows API mciString 传入播放相关的命令, 感觉风格不统一, 不够优雅。
另一个问题是 clock()
和 CLOCKS_PER_SEC
在 Linux 下结果和 windows 相差了10倍, 需要手动处理下。
EasyX
// 根据《C和C++游戏趣味编程》第12章 坚持100秒 写出
#include <graphics.h>
#include <conio.h> // _kbhit()
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "EasyXPng.h"
// 引用 Windows Multimedia API
#pragma comment(lib, "Winmm.lib")
// global variables
#define WIDTH 560
#define HEIGHT 800
IMAGE im_bk;
IMAGE im_rocket;
IMAGE im_blowup;
IMAGE im_bullet;
void playMusicOnce(char filename[80])
{
char cmdString[50] = { 0 };
sprintf(cmdString, "open %s alias tmpmusic", filename);
mciSendString("close tmpmusic", NULL, 0, NULL); // 先关闭前一次的音乐
mciSendString(cmdString, NULL, 0, NULL); // 打开这次的音乐
mciSendString("play tmpmusic", NULL, 0, NULL); // 仅播放一次
}
// 火箭
class Rocket
{
public:
IMAGE im_rocket;
IMAGE im_blowup;
float x, y;
float width;
float height;
int liveSecond; // 存活了多长时间
int life; // 几条命
void draw()
{
if (life > 0)
{
putimagePng(x - width / 2, y - height / 2, &im_rocket);
}
else
{
putimagePng(x - width / 2, y - height / 2, &im_blowup);
}
// 窗口左上角显示 life 个火箭图标
for (int i = 0; i < life; i++)
{
// 注: 如果去掉 ofs, 看起来结果应该一样,但实际运行时子弹不会出现,推测是编译器优化导致结果差异。。
int ofs = 0;
putimagePng(ofs + i * width * 0.9, ofs, &im_rocket);
}
char s[20] = { 0 };
setbkmode(TRANSPARENT);
sprintf(s, "%d秒", liveSecond);
settextcolor(WHITE);
settextstyle(40, 0, _T("黑体"));
outtextxy(WIDTH * 0.85, 20, s);
}
void update(float mx, float my)
{
x = mx;
y = my;
}
void updateWhenLifeLost()
{
if (life > 0)
{
playMusicOnce("explode.mp3");
life--;
if (life == 0)
{
mciSendString("close bkmusic", NULL, 0, NULL);
}
}
}
};
// 子弹
class Bullet
{
public:
IMAGE image;
float x, y; // coordinate
float vx, vy; // velocity
float radius;
void draw()
{
putimagePng(x - radius, y - radius, &image);
}
void update()
{
x += vx;
y += vy;
if (x <= 0 || x >= WIDTH)
{
vx = -vx;
}
if (y <= 0 || y >= HEIGHT)
{
vy = -vy;
}
}
// 判断子弹是否和火箭碰撞
bool isCollideRocket(Rocket rocket) const
{
float distanec_x = fabs(rocket.x - x);
float distance_y = fabs(rocket.y - y);
if (distanec_x < rocket.width / 2 && distance_y < rocket.height / 2)
{
return true;
}
return false;
}
};
// 飞碟
class SmartUFO : public Bullet
{
public:
void updateVelForTarget(Rocket targetRocket)
{
float scalar = rand() / double(RAND_MAX) + 1;
if (targetRocket.x > x)
{
vx = scalar;
}
else if (targetRocket.x < x)
{
vx = -scalar;
}
if (targetRocket.y > y)
{
vy = scalar;
}
else if (targetRocket.y < y)
{
vy = -scalar;
}
}
};
int bulletNum = 0;
#define MaxBulletNum 20
Bullet bullet[MaxBulletNum];
Rocket rocket;
IMAGE im_UFO;
SmartUFO ufo;
void startup()
{
mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL); // 打开背景音乐
mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL); // 循环播放
loadimage(&im_bk, "background.png");
loadimage(&im_rocket, "rocket.png");
loadimage(&im_blowup, "blowup.png");
loadimage(&im_bullet, "bullet.png");
loadimage(&im_UFO, "ufo.png");
rocket.im_rocket = im_rocket;
rocket.im_blowup = im_blowup;
rocket.width = im_rocket.getwidth();
rocket.height = im_rocket.getheight();
rocket.life = 5;
ufo.x = WIDTH / 2;
ufo.y = 10;
ufo.image = im_UFO;
ufo.radius = im_UFO.getwidth() / 2;
ufo.updateVelForTarget(rocket);
initgraph(WIDTH, HEIGHT, SHOWCONSOLE);
BeginBatchDraw();
}
// 精确延时函数
void sleep(DWORD ms)
{
static DWORD oldtime = GetTickCount();
while (GetTickCount() - oldtime < ms)
{
Sleep(1);
}
oldtime = GetTickCount();
}
void show()
{
putimage(0, 0, &im_bk); // background image
for (int i = 0; i < bulletNum; i++)
{
bullet[i].draw();
}
rocket.draw();
ufo.draw();
FlushBatchDraw();
sleep(10);
}
void updateWithoutInput()
{
if (rocket.life <= 0)
{
return;
}
// 每隔2s,新生成一颗子弹
static int lastSecond = 0;
static int nowSecond = 0;
static clock_t start = clock();
clock_t now = clock();
nowSecond = (int(now - start) / CLOCKS_PER_SEC);
rocket.liveSecond = nowSecond;
if (nowSecond == lastSecond + 1)
{
// 时间过了1秒, 更新飞碟速度
ufo.updateVelForTarget(rocket);
// 更新飞碟位置和速度
ufo.update();
// 如果飞碟和火箭相撞, 则火箭减命, 飞碟回到左上角初始位置
if (ufo.isCollideRocket(rocket))
{
rocket.updateWhenLifeLost();
ufo.x = 5;
ufo.y = 5;
}
}
if (bulletNum < MaxBulletNum && nowSecond == lastSecond + 2)
{
// printf("bulletNum = %d, MaxBulletNum = %d\n", bulletNum, MaxBulletNum);
bullet[bulletNum].x = WIDTH / 2;
bullet[bulletNum].y = 10;
float angle = (rand() / double(RAND_MAX) - 0.5) * 0.9 * PI;
float scalar = 2 * rand() / double(RAND_MAX) + 2;
bullet[bulletNum].vx = scalar * sin(angle);
bullet[bulletNum].vy = scalar * cos(angle);
bullet[bulletNum].image = im_bullet;
bullet[bulletNum].radius = im_bullet.getwidth() / 2;
bulletNum++;
lastSecond = nowSecond;
}
for (int i = 0; i < bulletNum; i++)
{
bullet[i].update();
if (bullet[i].isCollideRocket(rocket))
{
rocket.updateWhenLifeLost();
bullet[i].x = 5; // 当前子弹移开, 防止重复碰撞
bullet[i].y = 5;
break;
}
}
}
void updateWithInput()
{
if (rocket.life <= 0)
{
return;
}
MOUSEMSG m;
while (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_MOUSEMOVE)
{
rocket.update(m.x, m.y);
}
}
}
int main()
{
startup();
while (1)
{
show();
updateWithoutInput();
updateWithInput();
}
closegraph();
return 0;
}
基于 Raylib
// 根据《C和C++游戏趣味编程》第12章 坚持100秒 写出
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#include "raylib_helper.hpp"
#include <time.h>
#include <math.h>
// global variables
#define WIDTH 560
#define HEIGHT 800
Texture2D bk_texture;
Texture2D rocket_texture;
Texture2D blowup_texture;
Texture2D bullet_texture;
Music bk_music; // 背景音乐
Music ep_music; // 爆炸声
void playMusicOnce(char filename[80])
{
StopMusicStream(ep_music);
ep_music.looping = false;
PlayMusicStream(ep_music);
}
class Rocket
{
public:
Texture2D rocket_texture;
Texture2D blowup_texture;
float x;
float y;
float width;
float height;
int liveSecond; // 存活了多长时间
int life; // 几条命
void draw()
{
if (life > 0)
{
DrawTexture(rocket_texture, x - width / 2, y - height / 2, WHITE);
}
else
{
DrawTexture(blowup_texture, x - width / 2, y - height / 2, WHITE);
}
// 窗口左上角显示 life 个火箭图标
for (int i = 0; i < life; i++)
{
int ofs = 0;
DrawTexture(rocket_texture, ofs + i * width * 0.9, ofs, WHITE);
}
char s[20] = { 0 };
sprintf(s, "%d s", liveSecond);
DrawText(s, WIDTH * 0.85, 20, 40, WHITE);
}
void update(Vector2 pos)
{
x = pos.x;
y = pos.y;
}
void updateWhenLifeLost()
{
if (life > 0)
{
life--;
playMusicOnce("explode.mp3");
if (life == 0)
{
StopMusicStream(bk_music);
}
}
}
};
class Bullet
{
public:
Texture2D texture;
float x, y; // coordinate
float vx, vy; // velocity
float radius;
void draw()
{
DrawTexture(texture, x - radius, y - radius, WHITE);
}
void update()
{
x += vx;
y += vy;
if (x <= 0 || x >= WIDTH)
{
vx = -vx;
}
if (y <= 0 || y >= HEIGHT)
{
vy = -vy;
}
}
// 判断子弹是否和火箭碰撞
bool isCollideRocket(Rocket rocket) const
{
float distanec_x = fabs(rocket.x - x);
float distance_y = fabs(rocket.y - y);
if (distanec_x < rocket.width / 2 && distance_y < rocket.height / 2)
{
return true;
}
return false;
}
};
// 飞碟
class SmartUFO : public Bullet
{
public:
void updateVelForTarget(Rocket targetRocket)
{
float scalar = rand() / double(RAND_MAX) + 1;
if (targetRocket.x > x)
{
vx = scalar;
}
else if (targetRocket.x < x)
{
vx = -scalar;
}
if (targetRocket.y > y)
{
vy = scalar;
}
else if (targetRocket.y < y)
{
vy = -scalar;
}
}
};
#define MaxBulletNum 20
Bullet bullet[MaxBulletNum];
int bulletNum = 0;
Rocket rocket;
Texture2D ufo_texture;
SmartUFO ufo;
void startup()
{
SetConfigFlags(FLAG_MSAA_4X_HINT); // NOTE: Try to enable MSAA 4X
InitWindow(WIDTH, HEIGHT, "persist 100 seconds");
SetTargetFPS(60);
InitAudioDevice(); // Initialize audio device
//bk_music = LoadMusicStream("game_music.mp3");
char* music_path = "game_music.mp3";
bk_music = LoadMusicStream(music_path);
bk_music.looping = true;
PlayMusicStream(bk_music);
ep_music = LoadMusicStream("explode.mp3");
bk_texture = LoadTexture("background.png");
rocket_texture = LoadTexture("rocket.png");
blowup_texture = LoadTexture("blowup.png");
bullet_texture = LoadTexture("bullet.png");
ufo_texture = LoadTexture("ufo.png");
rocket.rocket_texture = rocket_texture;
rocket.blowup_texture = blowup_texture;
rocket.width = rocket_texture.width;
rocket.height = rocket_texture.height;
rocket.life = 5;
rocket.x = WIDTH / 2;
rocket.y = HEIGHT / 2;
ufo.x = WIDTH / 2;
ufo.y = 10;
ufo.texture = ufo_texture;
ufo.radius = ufo_texture.width / 2;
ufo.updateVelForTarget(rocket);
}
void show()
{
BeginDrawing();
{
DrawTexture(bk_texture, WIDTH/2 - bk_texture.width/2, HEIGHT/2 - bk_texture.height/2, WHITE);
for (int i = 0; i < bulletNum; i++)
{
bullet[i].draw();
}
rocket.draw();
ufo.draw();
}
EndDrawing();
}
void updateWithoutInput()
{
if (rocket.life <= 0)
{
return;
}
// 每隔2s,新生成一颗子弹
static int lastSecond = 0;
static int nowSecond = 0;
static clock_t start = clock();
clock_t now = clock();
int unit = CLOCKS_PER_SEC;
#if __linux__
unit /= 10;
#endif
nowSecond = (int(now - start) / unit);
//printf("start = %d, now = %d, CLOCKS_PER_SEC = %d\n", (int)start, (int)now, CLOCKS_PER_SEC);K
rocket.liveSecond = nowSecond;
if (nowSecond == lastSecond + 1)
{
// 时间过了1秒, 更新飞碟速度
ufo.updateVelForTarget(rocket);
// 更新飞碟位置和速度
ufo.update();
// 如果飞碟和火箭相撞, 则火箭减命, 飞碟回到左上角初始位置
if (ufo.isCollideRocket(rocket))
{
rocket.updateWhenLifeLost();
ufo.x = 5;
ufo.y = 5;
}
}
if (bulletNum < MaxBulletNum && nowSecond == lastSecond + 2)
{
lastSecond = nowSecond;
bullet[bulletNum].x = WIDTH / 2;
bullet[bulletNum].y = 10;
float angle = (rand() / double(RAND_MAX) - 0.5) * 0.9 * PI;
float scalar = 2 * rand() / double(RAND_MAX) + 2;
bullet[bulletNum].vx = scalar * sin(angle);
bullet[bulletNum].vy = scalar * cos(angle);
bullet[bulletNum].texture = bullet_texture;
bullet[bulletNum].radius = bullet_texture.width / 2;
bulletNum++;
}
for (int i = 0; i < bulletNum; i++)
{
bullet[i].update();
if (bullet[i].isCollideRocket(rocket))
{
rocket.updateWhenLifeLost();
bullet[i].x = 5; // 当前子弹移开, 防止重复碰撞
bullet[i].y = 5;
break;
}
}
}
void updateWithInput()
{
if (rocket.life <= 0)
{
return;
}
Vector2 pos = GetMousePosition();
rocket.update(pos);
}
int main()
{
startup();
while (!WindowShouldClose())
{
UpdateMusicStream(bk_music);
UpdateMusicStream(ep_music);
updateWithoutInput();
updateWithInput();
show();
}
UnloadTexture(bk_texture);
UnloadTexture(rocket_texture);
UnloadTexture(blowup_texture);
UnloadTexture(bullet_texture);
CloseWindow();
return 0;
}
Greatness is never a given, it must be earned.