使用SDL2实现2d角色左右移动

目录

YUUSHA类

init() 

getSrcRect()和getDesRect()

move() 

Game类

render() 

handleEvent() 

gameLoop()

 main()

 完整代码

FSM类 

 State类

YUUSHA类

Game类

实现思路:写一个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));
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
你可以使用FFmpeg和SDL2库来实现带有音量柱的播放器。下面是一个简单的示例代码,可以帮助你开始: ```c #include <stdio.h> #include <SDL2/SDL.h> #include <libavformat/avformat.h> #include <libswresample/swresample.h> #define SAMPLE_RATE 44100 #define CHANNELS 2 #define BUFFER_SIZE 4096 int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: %s <input_file>\n", argv[0]); return -1; } const char *inputFile = argv[1]; // 初始化SDL if (SDL_Init(SDL_INIT_AUDIO) < 0) { printf("SDL initialization failed: %s\n", SDL_GetError()); return -1; } // 打开输入文件 AVFormatContext *formatContext = NULL; if (avformat_open_input(&formatContext, inputFile, NULL, NULL) != 0) { printf("Failed to open input file\n"); return -1; } // 查找音频流信息 if (avformat_find_stream_info(formatContext, NULL) < 0) { printf("Failed to find stream information\n"); return -1; } // 查找音频流索引 int audioStreamIndex = -1; for (int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { printf("Failed to find audio stream\n"); return -1; } // 获取音频流解码参数 AVCodecParameters *codecParams = formatContext->streams[audioStreamIndex]->codecpar; // 查找音频解码器 AVCodec *codec = avcodec_find_decoder(codecParams->codec_id); if (codec == NULL) { printf("Failed to find decoder\n"); return -1; } // 创建解码器上下文 AVCodecContext *codecContext = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codecContext, codecParams) != 0) { printf("Failed to copy decoder parameters to context\n"); return -1; } // 打开解码器 if (avcodec_open2(codecContext, codec, NULL) != 0) { printf("Failed to open decoder\n"); return -1; } // 创建音频重采样上下文 SwrContext *swrContext = swr_alloc(); av_opt_set_int(swrContext, "in_channel_layout", codecContext->channel_layout, 0); av_opt_set_int(swrContext, "out_channel_layout", codecContext->channel_layout, 0); av_opt_set_int(swrContext, "in_sample_rate", codecContext->sample_rate, 0); av_opt_set_int(swrContext, "out_sample_rate", SAMPLE_RATE, 0); av_opt_set_sample_fmt(swrContext, "in_sample_fmt", codecContext->sample_fmt, 0); av_opt_set_sample_fmt(swrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); swr_init(swrContext); // 设置SDL音频参数 SDL_AudioSpec audioSpec; audioSpec.freq = SAMPLE_RATE; audioSpec.format = AUDIO_S16SYS; audioSpec.channels = CHANNELS; audioSpec.silence = 0; audioSpec.samples = BUFFER_SIZE; audioSpec.callback = NULL; // TODO: 实现音频回调函数 audioSpec.userdata = codecContext; // 打开音频设备 if (SDL_OpenAudio(&audioSpec, NULL) < 0) { printf("Failed to open audio device: %s\n", SDL_GetError()); return -1; } // 开始播放 SDL_PauseAudio(0); // 解码并播放音频数据 AVPacket packet; av_init_packet(&packet); AVFrame *frame = av_frame_alloc(); uint8_t *buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE); while (av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == audioStreamIndex) { int ret = avcodec_send_packet(codecContext, &packet); if (ret < 0) { printf("Error sending a packet for decoding\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(codecContext, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { printf("Error during decoding\n"); break; } // 音频重采样 int dstNbSamples = av_rescale_rnd(swr_get_delay(swrContext, frame->sample_rate) + frame->nb_samples, SAMPLE_RATE, frame->sample_rate, AV_ROUND_UP); int nb = swr_convert(swrContext, &buffer, dstNbSamples, (const uint8_t **) frame->data, frame->nb_samples); int audioSize = nb * CHANNELS * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); // TODO: 更新音量柱 // 播放音频数据 SDL_QueueAudio(1, buffer, audioSize); } } av_packet_unref(&packet); } // 清理资源 av_frame_free(&frame); av_free(buffer); swr_free(&swrContext); avcodec_close(codecContext); avformat_close_input(&formatContext); SDL_CloseAudio(); SDL_Quit(); return 0; } ``` 这只是一个简单的示例代码,你可能需要根据你的需求进行修改和扩展。要实现音量柱功能,你可以在音频回调函数中计算音频样本的能量,并将其映射到柱状图上。希望这对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平面海螺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值