目录
实现思路:写一个Game类用来初始化SDL、创建窗口、渲染、和扫描键盘,处理游戏循环和物体碰撞(碰撞检测可能要单独写一个类,现在暂时不管),再写一个YUUSHA类用来储存角色信息(图片资源、自身位置、当前状态等)和角色行为(移动)。在Game类中创建一个YUUSHA类的实例,获取YUUSHA对象的getSrcRect方法和getDesRect方法的返回值,传入在Game类中SDL_RenderCopyEx函数,这样就将角色的信息与角色的画面呈现分离开来。
YUUSHA类
YUUSHA类中有一个FSM类的实例,用来表示角色状态(空闲、跑等状态),还有一个数组m_rects用来存储精灵表的每一帧图片。在init方法中设置m_rects的值和状态切换时要执行的代码。
init()
void YUUSHA::init() {
//成员变量声明
//vector<SDL_Rect> m_rects{};
//int m_numRow{ 11 };
//int m_numCol{ 7 };
//int m_widthSpr{ 50 };
//int m_heightSpr{ 37 };
//FSM* fsm{};
//vector<pair<int, int>> idle1{ {0, 0}, {0, 1}, {0, 2}, {0, 3} };
// vector<pair<int, int>> run{ {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6} };
//vector<pair<int, int>> m_curAct{ idle1 };
for (int i = 0; i < m_numRow; i++)
for (int j = 0; j < m_numCol; j++)
m_rects.push_back({ (j * m_widthSpr), (i * m_heightSpr), m_widthSpr, m_heightSpr });
fsm = new FSM(actions::idle1);
fsm->set(actions::idle1);
//当从idle1状态转到run状态时
fsm->on(actions::idle1, actions::run) = [&](const std::vector< std::string>& args) {
fsm->set(actions::run);
//将当前动作设置为表示run的那几帧图片的数组
m_curAct = run;
};
//当从run状态转到idle1状态时
fsm->on(actions::run, actions::idle1) = [&](const std::vector< std::string>& args) {
fsm->set(actions::idle1);
m_curAct = idle1;
};
}
getSrcRect()和getDesRect()
//成员变量声明
//int m_posX{ 0 };
//int m_posY{ 0 };
//int m_curPairIndex{ 0 };
//int m_curPair{ 0 };
//int m_count{ 0 };
//int m_duration{ 5 };
SDL_Rect YUUSHA::getDesRect()const {
//返回图片将要显示在窗口上的位置
return { getPosX(),getPosY(),m_widthSpr,m_heightSpr };
}
SDL_Rect YUUSHA::getSrcRect() {
//m_curAct的长度可能每次都不一样
//循环显示一个动作中的图片
if (m_curPairIndex >= getCurAct().size())
m_curPairIndex = 0;
if (!getCurAct().empty()) {
std::pair<int, int> curPair = std::move(getCurAct().at(m_curPairIndex));
//获取这组动作的某一帧在精灵表上的索引
m_curPair = curPair.second + curPair.first * m_numCol;
}
m_count++;
//m_duration用于控制图片播放的快慢
if (m_count > m_duration) {
//显示下一张图片
m_curPairIndex++;
m_count = 0;
}
//返回精灵表上的某一帧
return m_rects.at(m_curPair);
}
vector<pair<int, int>> YUUSHA::getCurAct()const {
return m_curAct;
}
move()
//成员变量声明
//int m_velX{ 0 };
//int m_velY{ 10 };
//int m_velocity{ 2 };
//int m_gravity{ 10 };
void YUUSHA::move(bool collisionX, bool collisionY, int posX, int posY) {
//如果水平方向没发生碰撞则移动,否则设置一个适当的位置
if (!collisionX) {
m_posX += m_velX;
}
else {
m_posX = posX;
}
//如果竖直方向没发生碰撞则移动,否则设置一个适当的位置
if (!collisionY) {
m_posY += m_velY;
}
else {
m_posY = posY;
}
}
void YUUSHA::actRun(const int&& direction) {
this->m_velX += direction * m_velocity;
}
Game类
render()
//成员变量声明
//SDL_Renderer* m_renderer{ nullptr };
//SDL_RendererFlip m_flipFlag{ SDL_FLIP_NONE };
//SDL_Texture* m_playerTexture{ nullptr };
//YUUSHA* player;
void Game::render() {
SDL_SetRenderDrawColor(m_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(m_renderer);
SDL_Rect srcrect{ std::move(player->getSrcRect()) };
SDL_Rect desrect{ std::move(player->getDesRect()) };
//最后一个参数m_flipFlag用来设置图片翻转
SDL_RenderCopyEx(m_renderer, m_playerTexture, &srcrect, &desrect, 0, 0, m_flipFlag);
//更新
SDL_RenderPresent(m_renderer);
}
handleEvent()
handleEvent方法负责跟角色控制有关的代码,检测A键跟D键的按下和释放。
YUUSHA类提供一个public的成员变量vector <function<bool(const State&)>> runVector{},
在handleEvent方法中用三个lambda表达式设置runVector前三个元素的值,分别表示向左跑、向右跑和空闲。
//成员变量声明
//SDL_Event m_event;
void Game::handleEvent() {
//按下按键并且不被当作重复按键
if (m_event.type == SDL_KEYDOWN && m_event.key.repeat == 0)
{
switch (m_event.key.keysym.scancode)
{
case SDL_SCANCODE_A:
player->runVector[0] = (
[&](const State&)->bool {
m_flipFlag = SDL_FLIP_HORIZONTAL;//水平翻转,即人物朝向左边
player->fsm->command(actions::run);
return true;
}
);
player->actRun(-1);//向左跑
break;
case SDL_SCANCODE_D:
player->runVector[1] = (
[&](const State&)->bool {
m_flipFlag = SDL_FLIP_NONE;//取消水平翻转,即人物朝向右边
player->fsm->command(actions::run);
return true;
}
);
player->actRun(1);//向右跑
break;
}
}
//按键释放
else if (m_event.type == SDL_KEYUP)
{
switch (m_event.key.keysym.scancode)
{
case SDL_SCANCODE_A:
player->runVector[0] = (
[&](const State&)->bool {
//什么也不干,只返回一个false
return false;
}
);
player->runVector[2] = (
[&](const State&)->bool {
//按键释放时转为空闲状态,不需要设置水平翻转,此时延用m_flipFlag的设置
return player->fsm->command(actions::idle1);
}
);
//左右抵消让角色停下来
player->actRun(1);//向右跑
break;
case SDL_SCANCODE_D:
player->runVector[1] = (
[&](const State&)->bool {
//什么也不干,只返回一个false
return false;
}
);
player->runVector[2] = (
[&](const State&)->bool {
return player->fsm->command(actions::idle1);
}
);
player->actRun(-1);//向左跑
break;
}
}
bool isIdle{ false };
if (!player->runVector.empty()) {
//调用runVector[0]()和runVector[1](),如果返回值都是false说明此时A键和D键都没有按下,则接着调用runVector[2](),展示空闲动画
isIdle = (!player->runVector[0]('null')) && (!player->runVector[1]('null'));
if (isIdle) {
player->runVector[2]('null');
}
}
}
因为runVector[0]执行在runVector[1]前面,而runVector[0]是向左跑,runVector[1]是向右跑,所以按下D键不松开,再按下A键会转头,但反过来则不会。
改成下面这样就好了。。。。。
//int running{ 0 };
void Game::handleEvent() {
//按下按键并且不被当作重复按键
if (m_event.type == SDL_KEYDOWN && m_event.key.repeat == 0){
switch (m_event.key.keysym.scancode){
case SDL_SCANCODE_A:
m_flipFlag = SDL_FLIP_HORIZONTAL;//水平翻转
player->fsm->command(actions::run);
player->running++;
player->actRun(-1);//向左跑
break;
case SDL_SCANCODE_D:
m_flipFlag = SDL_FLIP_NONE;//取消水平翻转
player->fsm->command(actions::run);
player->running++;
player->actRun(1);//向右跑
break;
}
}
//按键释放
else if (m_event.type == SDL_KEYUP){
switch (m_event.key.keysym.scancode){
case SDL_SCANCODE_A:
player->running--;
if (player->running == 1)
m_flipFlag = SDL_FLIP_NONE;//取消水平翻转
if (player->running == 0)
player->fsm->command(actions::idle1);
player->actRun(1);//向右跑
break;
case SDL_SCANCODE_D:
player->running--;
if (player->running == 1)
m_flipFlag = SDL_FLIP_HORIZONTAL;//水平翻转
if (player->running == 0)
player->fsm->command(actions::idle1);
player->actRun(-1);//向左跑
break;
}
}
}
gameLoop()
在gameLoop方法中调用handleEvent()和render()。帧数为60。
//成员变量声明
//int m_screenWidth{ 900 };
//int m_screenHeight{ 600 };
typedef std::chrono::high_resolution_clock Clock;
void Game::gameLoop() {
bool quit{ false };
while (!quit) {
//开始计时
double elapsedNano = 0;
auto t1 = Clock::now();
//轮询事件
while (SDL_PollEvent(&m_event)) {
if (m_event.type == SDL_QUIT)
quit = true;
else if (m_event.type == SDL_KEYDOWN) {
//按Esc键退出
if (m_event.key.keysym.sym == SDLK_ESCAPE)
quit = true;
}
handleEvent();
}
//碰撞检测
bool collisionY = player->getPosY() >= m_screenHeight - 37;
bool collisionX = (player->getPosX() >= m_screenWidth - 50) || (player->getPosX() <= 0);
int posX = m_screenWidth - 50 - 1;
int posY = m_screenHeight - 37 + 1;
player->move(collisionX, collisionY, posX, posY);
//渲染每帧画面
render();
//结束计时
auto t2 = Clock::now();
//计算间隔
elapsedNano = (double)(std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
if (elapsedNano > 0) {
//如果此时间间隔小于每帧所需要的时间,则延时
double diff = ((1000000000.f / 60.f) - elapsedNano) / 1000000.f;
if (diff > 0)
SDL_Delay((Uint32)diff);
}
}
}
main()
#include<iostream>
#include"Game.h"
int main(int argc, char* argv[]) {
Game* game = new Game();
game->gameLoop();
delete game;
game = nullptr;
return 0;
}
完整代码
FSM类
FSM.h
#pragma once
#include<deque>
#include<map>
#include<functional>
#include"State.h"
#include <iostream>
class FSM {
protected:
State m_curTrigger;
typedef std::pair<int, int> statePair;
typedef std::function<void(const std::vector<std::string>)> callback;
std::map<statePair, callback> m_callbacks;
typedef std::deque<State> states;
states m_states;
protected:
void replace(State& cur, const State& next);
public:
FSM(const State& start = 'null');
FSM(const int& start = 'null');
bool call(const State& from, const State& to)const;
callback& on(const State& from, const State& to);
void push(const State& state);
void pop();
void set(const State& state);
bool command(const State& trigger);
bool command(const State& trigger, const std::string& arg1);
bool command(const State& trigger, const std::string& arg1, const std::string& arg2);
bool operator()(const State& trigger);
bool operator()(const State& trigger, const std::string& arg1);
bool operator()(const State& trigger, const std::string& arg1, const std::string& arg2);
~FSM();
};
FSM.cpp
#include"FSM.h"
FSM::FSM(const State& start) :m_states(1), m_curTrigger{ State() } {
m_states[0] = start;
}
FSM::FSM(const int& start) :FSM{ State(start) } {
}
void FSM::replace(State& cur, const State& next) {
call(cur, 'quit');
cur = next;
call(cur, 'init');
}
bool FSM::call(const State& from, const State& to)const {
decltype(m_callbacks)::const_iterator found = m_callbacks.find(statePair(from, to));
if (found != m_callbacks.end()) {
found->second(to.m_args);
return true;
}
return false;
}
FSM::callback& FSM::on(const State& from, const State& to) {
return m_callbacks[statePair(from, to)];
}
void FSM::push(const State& state) {
if (m_states.size() && m_states.back() == state)
return;
call(m_states.back(), 'push');
m_states.push_back(state);
call(m_states.back(), 'init');
}
void FSM::pop() {
if (m_states.size()) {
call(m_states.back(), 'quit');
m_states.pop_back();
}
if (m_states.size())
call(m_states.back(), 'pop');
}
void FSM::set(const State& state) {
if (m_states.size())
replace(m_states.back(), state);
else
push(state);
}
bool FSM::command(const State& trigger) {
if (!m_states.size())
return false;
std::deque<states::reverse_iterator> aborted;
for (auto it = m_states.rbegin(); it != m_states.rend(); ++it) {
State& self = *it;
if (!call(self, trigger)) {
aborted.push_back(it);
continue;
}
for (auto ri = aborted.begin(), end = aborted.end(); ri != end; ++ri) {
call(**ri, 'quit');
m_states.erase(--(ri->base()));
}
m_curTrigger = trigger;
return true;
}
return false;
}
bool FSM::command(const State& trigger, const std::string& arg1) {
return command(trigger(arg1));
}
bool FSM::command(const State& trigger, const std::string& arg1, const std::string& arg2) {
return command(trigger(arg1, arg2));
}
bool FSM::operator()(const State& trigger) {
return command(trigger);
}
bool FSM::operator()(const State& trigger, const std::string& arg1) {
return command(trigger(arg1));
}
bool FSM::operator()(const State& trigger, const std::string& arg1, const std::string& arg2) {
return command(trigger(arg1, arg2));
}
FSM::~FSM() {
while (m_states.size())
pop();
}
State类
State.h
#pragma once
#include<vector>
#include<string>
#include <sstream>
class State
{
public:
State(const int& name = 'null');
State operator()()const;
State operator()(const std::string& s)const;
State operator()(const std::string& s1, const std::string& s2)const;
operator int()const;
bool operator>(const State& rhs)const;
bool operator==(const State& rhs)const;
//必须以全局函数(友元函数)的形式重载<<
friend std::ostream& operator<<(std::ostream& out, const State& r) {
if (r.m_name >= 256) {
out << char((r.m_name >> 24) & 0xff);
out << char((r.m_name >> 16) & 0xff);
out << char((r.m_name >> 8) & 0xff);
out << char((r.m_name >> 0) & 0xff);
}
else {
out << r.m_name;
}
out << "(";
std::string sep;
for (auto& arg : r.m_args) {
out << sep << arg;
sep = ',';
}
out << ")";
return out;
}
public:
int m_name{ 'null' };
typedef std::vector<std::string> args;
State::args m_args;
};
State.cpp
#include "State.h"
State::State(const int& name) :m_name{ name } {
}
State State::operator()()const {
State self = *this;
self.m_args = {};
return self;
}
State State::operator()(const std::string& s)const {
State self = *this;
self.m_args = { s };
return self;
}
State State::operator()(const std::string& s1, const std::string& s2)const {
State self = *this;
self.m_args = { s1,s2 };
return self;
}
State::operator int()const {
return m_name;
}
bool State::operator>(const State& rhs)const {
if (*this == rhs)
return false;
return this->m_name > rhs.m_name;
}
bool State::operator==(const State& rhs)const {
if (*this == rhs)
return true;
return this->m_name == rhs.m_name;
}
YUUSHA类
YUUSHA.h
#pragma once
#include<string>
#include<vector>
#include<map>
#include<deque>
#include<vector>
#include<functional>
#include<SDL.h>
#include"FSM.h"
using std::string;
using std::vector;
using std::pair;
using std::map;
using std::deque;
using std::vector;
using std::function;
namespace actions {
enum {
idle1,
crouch,
run,
jump,
mid,
fall,
slide,
grab,
climb,
idle2,
attack1,
attack2,
attack3,
hurt,
die,
jump2,
};
}
class YUUSHA
{
public:
YUUSHA();
~YUUSHA();
void init();
void actRun(const int&& direction);
private:
string m_IMGPath{ "assets/player.png" };
int m_numRow{ 11 };
int m_numCol{ 7 };
int m_widthSpr{ 50 };
int m_heightSpr{ 37 };
int m_posX{ 0 };
int m_posY{ 0 };
int m_velX{ 0 };
int m_velY{ 10 };
int m_velocity{ 2 };
int m_gravity{ 10 };
vector<pair<int, int>> idle1{ {0, 0}, {0, 1}, {0, 2}, {0, 3} };
vector<pair<int, int>> crouch{ {0, 4}, {0, 5}, {0, 6}, {1, 0} };
vector<pair<int, int>> run{ {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6} };
vector<pair<int, int>> jump{ {2, 0}, {2, 1}, {2, 2}, {2, 3} };
vector<pair<int, int>> mid{ {2, 4}, {2, 5}, {2, 6}, {3, 0} };
vector<pair<int, int>> fall{ {3, 1}, {3, 2} };
vector<pair<int, int>> slide{ {3, 3}, {3, 4}, {3, 5}, {3, 6}, {4, 0} };
vector<pair<int, int>> grab{ {4, 1}, {4, 2}, {4, 3}, {4, 4} };
vector<pair<int, int>> climb{ {4, 5}, {4, 6}, {5, 0}, {5, 1}, {5, 2} };
vector<pair<int, int>> idle2{ {5, 3}, {5, 4}, {5, 5}, {5, 6} };
vector<pair<int, int>> attack1{ {6, 0}, {6, 1}, {6, 2}, {6, 3}, {6, 4} };
vector<pair<int, int>> attack2{ {6, 5}, {6, 6}, {7, 0}, {7, 1}, {7, 2}, {7, 3} };
vector<pair<int, int>> attack3{ {7, 4}, {7, 5}, {7, 6}, {8, 0}, {8, 1}, {8, 2} };
vector<pair<int, int>> hurt{ {8, 3}, {8, 4}, {8, 5} };
vector<pair<int, int>> die{ {8, 6}, {9, 0}, {9, 1}, {9, 2}, {9, 3}, {9, 4}, {9, 5} };
vector<pair<int, int>> jump2{ {9, 6}, {10, 0}, {10, 1} };
vector<pair<int, int>> m_curAct{ idle1 };
vector<SDL_Rect> m_rects{};
int m_curPairIndex{ 0 };
int m_curPair{ 0 };
int m_count{ 0 };
int m_duration{ 5 };
public:
deque<function<void(void)>> runDeque{};
vector <function<bool(const State&)>> runVector{};
decltype(runVector)::iterator it{ runVector.begin() };
public:
const string getIMGPath()const;
const int getPosX()const;
const int getPosY()const;
const int getVelX()const;
const int getVelY()const;
SDL_Rect getSrcRect();
SDL_Rect getDesRect()const;
vector<pair<int, int>> getCurAct()const;
void move(bool collisionX, bool collisionY, int posX, int posY);
public:
FSM* fsm{};
};
YUUSHA.cpp
#include "YUUSHA.h"
#include <iostream>
YUUSHA::YUUSHA() {
init();
runVector.assign(3, ([&](const State&)->bool {return false; }));
}
YUUSHA::~YUUSHA() {
}
void YUUSHA::init() {
for (int i = 0; i < m_numRow; i++)
for (int j = 0; j < m_numCol; j++)
m_rects.push_back({ (j * m_widthSpr), (i * m_heightSpr), m_widthSpr, m_heightSpr });
fsm = new FSM(actions::idle1);
fsm->set(actions::idle1);
//当从idle1状态转到run状态时
fsm->on(actions::idle1, actions::run) = [&](const std::vector< std::string>& args) {
fsm->set(actions::run);
//将当前动作设置为表示run的那几帧图片的数组
m_curAct = run;
};
fsm->on(actions::run, actions::idle1) = [&](const std::vector< std::string>& args) {
fsm->set(actions::idle1);
m_curAct = idle1;
};
}
const string YUUSHA::getIMGPath()const {
return m_IMGPath;
}
const int YUUSHA::getPosX()const {
return m_posX;
}
const int YUUSHA::getPosY()const {
return m_posY;
}
const int YUUSHA::getVelX()const {
return m_velX;
}
const int YUUSHA::getVelY()const {
return m_velY;
}
SDL_Rect YUUSHA::getDesRect()const {
return { getPosX(),getPosY(),m_widthSpr,m_heightSpr };
}
SDL_Rect YUUSHA::getSrcRect() {
if (m_curPairIndex >= getCurAct().size())
m_curPairIndex = 0;
if (!getCurAct().empty()) {
std::pair<int, int> curPair = std::move(getCurAct().at(m_curPairIndex));
m_curPair = curPair.second + curPair.first * m_numCol;
}
m_count++;
if (m_count > m_duration) {
m_curPairIndex++;
m_count = 0;
}
return m_rects.at(m_curPair);
}
vector<pair<int, int>> YUUSHA::getCurAct()const {
return m_curAct;
}
void YUUSHA::move(bool collisionX, bool collisionY, int posX, int posY) {
//如果水平方向没发生碰撞则移动,否则设置一个适当的位置
if (!collisionX) {
m_posX += m_velX;
}
else {
m_posX = posX;
}
//如果竖直方向没发生碰撞则移动,否则设置一个适当的位置
if (!collisionY) {
m_posY += m_velY;
}
else {
m_posY = posY;
}
}
void YUUSHA::actRun(const int&& direction) {
this->m_velX += direction * m_velocity;
}
Game类
Game.h
#pragma once
#include<SDL.h>
#include <SDL_image.h>
#include<format>
#include<string>
#include<string_view>
#include<iostream>
#include <chrono>
#include"YUUSHA.h"
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::string_view;
class Game
{
public:
Game();
~Game();
bool init();
void close();
void gameLoop();
void handleEvent();
SDL_Texture* loadSpriteSheet(string_view path);
void render();
private:
SDL_Window* m_window{ nullptr };
SDL_Surface* m_surface{ nullptr };
SDL_Renderer* m_renderer{ nullptr };
SDL_RendererFlip m_flipFlag{ SDL_FLIP_NONE };
int m_screenWidth{ 900 };
int m_screenHeight{ 600 };
SDL_Event m_event;
SDL_Texture* m_playerTexture{ nullptr };
YUUSHA* player;
typedef std::chrono::high_resolution_clock Clock;
public:
const int getScreenWidth()const;
const int getScreenHeight()const;
};
Game.cpp
#include "Game.h"
Game::Game() {
init();
player = new YUUSHA();
m_playerTexture = loadSpriteSheet(player->getIMGPath());
}
Game::~Game() {
close();
}
bool Game::init() {
bool success = true;
if (SDL_Init(SDL_INIT_EVENTS) < 0) {
cout << format("SDL could not initialize! SDL Error: {}", string(SDL_GetError())) << endl;
success = false;
}
else {
//创建窗口
m_window = SDL_CreateWindow("Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, getScreenWidth(), getScreenHeight(), SDL_WINDOW_SHOWN);
if (m_window == nullptr) {
cout << format("Window could not be created! SDL error: {}", string(SDL_GetError())) << endl;
success = false;
}
else {
//创建渲染
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
if (m_renderer == nullptr) {
cout << format("Renderer could not be created! SDL Error: {}", string(SDL_GetError())) << endl;
success = false;
}
else {
SDL_SetRenderDrawColor(m_renderer, 0xff, 0xff, 0xff, 0xff);
//初始化SDL_image
int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
if (!(IMG_Init(imgFlags) & imgFlags)) {
cout << format("SDL_image could not initialize! SDL_image Error: {}", string(IMG_GetError())) << endl;
success = false;
}
}
//m_surface = SDL_GetWindowSurface(m_window);
}
}
return success;
}
void Game::close() {
SDL_DestroyRenderer(m_renderer);
SDL_DestroyWindow(m_window);
m_window = nullptr;
m_renderer = nullptr;
IMG_Quit();
SDL_Quit();
}
//游戏循环
void Game::gameLoop() {
bool quit{ false };
while (!quit) {
//开始计时
double elapsedNano = 0;
auto t1 = Clock::now();
//轮询事件
while (SDL_PollEvent(&m_event)) {
if (m_event.type == SDL_QUIT)
quit = true;
else if (m_event.type == SDL_KEYDOWN) {
//按Esc键退出
if (m_event.key.keysym.sym == SDLK_ESCAPE)
quit = true;
}
//角色控制
handleEvent();
}
//碰撞检测
bool collisionY = player->getPosY() >= m_screenHeight - 37;
bool collisionX = (player->getPosX() >= m_screenWidth - 50) || (player->getPosX() <= 0);
int posX = m_screenWidth - 50 - 1;
int posY = m_screenHeight - 37 + 1;
player->move(collisionX, collisionY, posX, posY);
//渲染每帧画面
render();
//结束计时
auto t2 = Clock::now();
//计算间隔
elapsedNano = (double)(std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
if (elapsedNano > 0) {
//如果此时间间隔小于每帧所需要的时间,则延时
double diff = ((1000000000.f / 60.f) - elapsedNano) / 1000000.f;
if (diff > 0)
SDL_Delay((Uint32)diff);
}
}
}
//控制事件
void Game::handleEvent() {
//按下按键并且不被当作重复按键
if (m_event.type == SDL_KEYDOWN && m_event.key.repeat == 0)
{
switch (m_event.key.keysym.scancode)
{
case SDL_SCANCODE_A:
player->runVector[0] = (
[&](const State&)->bool {
m_flipFlag = SDL_FLIP_HORIZONTAL;//水平翻转,即人物朝向左边
player->fsm->command(actions::run);
return true;
}
);
player->actRun(-1);//向左跑
break;
case SDL_SCANCODE_D:
player->runVector[1] = (
[&](const State&)->bool {
m_flipFlag = SDL_FLIP_NONE;//取消水平翻转,即人物朝向右边
player->fsm->command(actions::run);
return true;
}
);
player->actRun(1);//向右跑
break;
}
}
//按键释放
else if (m_event.type == SDL_KEYUP)
{
switch (m_event.key.keysym.scancode)
{
case SDL_SCANCODE_A:
player->runVector[0] = (
[&](const State&)->bool {
return false;
}
);
player->runVector[2] = (
[&](const State&)->bool {
return player->fsm->command(actions::idle1);
}
);
player->actRun(1);//向右跑
break;
case SDL_SCANCODE_D:
player->runVector[1] = (
[&](const State&)->bool {
return false;
}
);
player->runVector[2] = (
[&](const State&)->bool {
return player->fsm->command(actions::idle1);
}
);
player->actRun(-1);//向左跑
break;
}
}
bool isIdle{ false };
if (!player->runVector.empty()) {
isIdle = (!player->runVector[0]('null')) && (!player->runVector[1]('null'));
if (isIdle) {
player->runVector[2]('null');
}
}
}
//加载精灵表
SDL_Texture* Game::loadSpriteSheet(string_view path) {
SDL_Texture* texture = nullptr;
SDL_Surface* loadedSurface = IMG_Load(path.data());
if (loadedSurface == nullptr)
cout << format("Unable to load image {}! SDL_image Error: {}", path.data(), string(IMG_GetError())) << endl;
else {
SDL_SetColorKey(loadedSurface, true, SDL_MapRGB(loadedSurface->format, 0, 0xff, 0xff));
texture = SDL_CreateTextureFromSurface(m_renderer, loadedSurface);
if (texture == nullptr)
cout << format("Unable to create texture from {}! SDL Error: {}", path.data(), string(IMG_GetError())) << endl;
//创建完texture后就可以释放此surface了
SDL_FreeSurface(loadedSurface);
}
return texture;
}
//渲染
void Game::render() {
SDL_SetRenderDrawColor(m_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(m_renderer);
SDL_Rect srcrect{ std::move(player->getSrcRect()) };
SDL_Rect desrect{ std::move(player->getDesRect()) };
SDL_RenderCopyEx(m_renderer, m_playerTexture, &srcrect, &desrect, 0, 0, m_flipFlag);
SDL_RenderPresent(m_renderer);
}
const int Game::getScreenWidth()const {
return m_screenWidth;
}
const int Game::getScreenHeight()const {
return m_screenHeight;
}
fsm
可以将State类和FSM类写成模板类,放在同一个头文件fsm.h里
fsm.h
#pragma once
#include <deque>
#include <functional>
#include <map>
#include <sstream>
#include <string>
#include <vector>
using std::vector;
using std::string;
using std::function;
using std::deque;
using std::map;
using std::pair;
//to_string
template<typename T>
inline string to_string(const T& t) {
std::stringstream ss;
return ss << t ? ss.str() : string();
}
template<>
inline std::string to_string(const std::string& t) {
return t;
}
using args = vector<string>;
//State
template<typename T0=int,typename T1=int>
class State {
public:
State(const int& name = 'null');
~State() = default;
//仿函数
State<T0,T1> operator()()const;
State<T0, T1> operator()(const T0& t0)const;
State<T0, T1> operator()(const T0& t0, const T1& t1)const;
operator int()const;
bool operator<(const State<T0, T1>& other)const;
bool operator==(const State<T0, T1>& other)const;
public:
int m_name;
args m_args;
};
template<typename T0, typename T1>
State<T0, T1>::State(const int& name) :m_name(name) {
}
template<typename T0, typename T1>
State<T0, T1> State<T0, T1 >::operator()()const {
State<int> self = *this;
self.m_args = {};
return self;
}
template<typename T0, typename T1>
State<T0, T1> State<T0, T1>::operator()(const T0& t0)const {
State<int> self = *this;
self.m_args = { to_string(t0) };
return self;
}
template<typename T0,typename T1>
State<T0, T1> State<T0, T1>::operator()(const T0& t0,const T1& t1)const{
State<int> self = *this;
self.m_args = { to_string(t0),to_string(t1) };
return self;
}
template<typename T0, typename T1>
State<T0, T1>::operator int()const {
return m_name;
}
template<typename T0, typename T1>
bool State<T0, T1>::operator<(const State<T0, T1>& other)const {
return m_name < other.m_name;
}
template<typename T0, typename T1>
bool State<T0, T1>::operator==(const State<T0, T1 >& other)const {
return m_name == other.m_name;
}
//fsm
using callback = function<void(const args& args)>;
using states = deque<State<int, int>>;
template <typename T0=int,typename T1=int>
class fsm {
public:
fsm(const State<int, int>& start = 'null');
fsm(const int& start = 'null');
~fsm();
public:
typedef pair<int, int> bistate;
map<bistate, callback> m_callbacks;
states m_stateDeque;
State<int, int> m_curTrigger;
public:
callback& on(const State<int, int>& from, const State<int, int>& to);
bool call(const State<int, int>& from, const State<int, int>& to)const;
void set(const State<int, int>& state);
bool command(const State<int, int>& trigger);
bool command(const State<int, int>& trigger, const T0& arg0);
bool command(const State<int, int>& trigger, const T0& arg0, const T1& arg1);
//仿函数
bool operator()(const State<int, int>& trigger);
bool operator()(const State<int, int>& trigger, const T0& arg0);
bool operator()(const State<int, int>& trigger, const T0& arg0, const T1& arg1);
};
//构造
template<typename T0,typename T1>
fsm<T0, T1>::fsm(const State<int, int>& start) :m_stateDeque(1) {
m_stateDeque[0] = start;
call(m_stateDeque.back(), 'init');
}
template<typename T0, typename T1>
fsm<T0, T1>::fsm(const int& start) : fsm<T0, T1>::fsm(State<int, int>(start)) {
}
//析构
template<typename T0, typename T1>
fsm<T0, T1>::~fsm() {
while (m_stateDeque.size()) {
call(m_stateDeque.back(), 'quit');
m_stateDeque.pop_back();
if (m_stateDeque.size()) {
call(m_stateDeque.back(), 'back');
}
}
}
template<typename T0, typename T1>
callback& fsm<T0, T1>::on(const State<int, int>& from, const State<int, int>& to) {
return m_callbacks[bistate(from, to)];
}
template<typename T0, typename T1>
bool fsm<T0, T1>::call(const State<int, int>& from, const State<int, int>& to)const {
map<bistate, callback>::const_iterator found = m_callbacks.find(bistate(from, to));
if (found != m_callbacks.end()) {
found->second(to.m_args);
return true;
}
return false;
}
template<typename T0, typename T1>
void fsm<T0, T1>::set(const State<int, int>& state) {
if (m_stateDeque.size()) {
if (m_stateDeque.back() == state) {
return;
}
//replace
call(m_stateDeque.back(), 'quit');
m_stateDeque.back() = state;
call(m_stateDeque.back(), 'init');
}
else {
//push
call(m_stateDeque.back(), 'push');
m_stateDeque.push_back(state);
call(m_stateDeque.back(), 'init');
}
}
template<typename T0, typename T1>
bool fsm<T0, T1>::command(const State<int, int>& trigger) {
if (!m_stateDeque.size()) {
return false;
}
m_curTrigger = State<int, int>();
deque<states::reverse_iterator> aborted;
for (auto it = m_stateDeque.rbegin(); it != m_stateDeque.rend(); ++it) {
State<int, int>& self = *it;
if (!call(self, trigger)) {
aborted.push_back(it);
continue;
}
for (auto begin = aborted.begin(), end = aborted.end(); begin != end; ++begin) {
call(**begin, 'quit');
m_stateDeque.erase(--(begin->base()));
}
m_curTrigger = trigger;
return true;
}
return false;
}
template<typename T0, typename T1>
bool fsm<T0, T1>::command(const State<int, int>& trigger, const T0& arg0) {
return command(State<int, int>(arg0));
}
template<typename T0, typename T1>
bool fsm<T0, T1>::command(const State<int, int>& trigger, const T0& arg0, const T1& arg1) {
return command(State<int, int>(arg0, arg1));
}
template<typename T0, typename T1>
bool fsm<T0, T1>::operator()(const State<int, int>& trigger) {
return command(trigger);
}
template<typename T0, typename T1>
bool fsm<T0, T1>::operator()(const State<int, int>& trigger, const T0& arg0) {
return command(trigger(arg0));
}
template<typename T0, typename T1>
bool fsm<T0, T1>::operator()(const State<int, int>& trigger, const T0& arg0, const T1& arg1) {
return command(trigger(arg0, arg1));
}