ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OpenGL-实战篇--Breakout--音效--54

2021-11-11 10:33:41  阅读:250  来源: 互联网

标签:实战篇 ball glm ResourceManager -- Breakout 音效 position GL


学习链接:中文--------英语原文

给游戏添加音乐!

参考上文链接导入相应的头文件和lib、DLL即可。
Game.cpp:

#include "ResourceManager.h"
#include "Game.h"
#include "SpriteRenderer.h"
#include "BallObject.h"
#include "ParticleGenerator.h"
#include "PostProcessor.h"
#include <irrklang/irrKlang.h>
using namespace irrklang;

SpriteRenderer* renderer;
GameObject* player;
BallObject* ball;
ParticleGenerator* particles;
PostProcessor* effects;
ISoundEngine* soundEngine = createIrrKlangDevice();
GLfloat shakeTime = 0.0f;

Game::Game(GLuint w, GLuint h) :state(GAME_ACTIVE), keys(), width(w), height(h) {}

Game::~Game()
{
	delete renderer;
	delete player;
	delete ball;
	delete particles;
	delete effects;
	soundEngine->drop();
}

void Game::Init()
{
	//load shader
	ResourceManager::LoadShader("Breakout/Shaders/Sprite.vs", "Breakout/Shaders/Sprite.fs", nullptr, "sprite");
	ResourceManager::LoadShader("Breakout/Shaders/Particle.vs", "Breakout/Shaders/Particle.fs", nullptr, "particle");
	ResourceManager::LoadShader("Breakout/Shaders/postprocessor.vs", "Breakout/Shaders/postprocessor.fs", nullptr, "postprocessor");
	//config shader
	glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(this->width), static_cast<GLfloat>(this->height), 0.0f, -1.0f, 1.0f);
	ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
	ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
	ResourceManager::GetShader("particle").Use().SetInteger("sprite", 0);
	ResourceManager::GetShader("particle").SetMatrix4("projection", projection);
	//load texture
	ResourceManager::LoadTexture("Breakout/Images/background.jpg", GL_FALSE, "background");
	ResourceManager::LoadTexture("Breakout/Images/block.png", GL_FALSE, "block");
	ResourceManager::LoadTexture("Breakout/Images/block_solid.png", GL_FALSE, "block_solid");
	ResourceManager::LoadTexture("Breakout/paddle.png", GL_TRUE, "paddle");//player
	ResourceManager::LoadTexture("Breakout/face.png", GL_TRUE, "ball");//ball
	ResourceManager::LoadTexture("Breakout/particle.png", GL_TRUE, "particle");//particle

	ResourceManager::LoadTexture("Breakout/tex_speed.png", GL_TRUE, "tex_speed");
	ResourceManager::LoadTexture("Breakout/tex_sticky.png", GL_TRUE, "tex_sticky");
	ResourceManager::LoadTexture("Breakout/tex_pass.png", GL_TRUE, "tex_pass");
	ResourceManager::LoadTexture("Breakout/tex_size.png", GL_TRUE, "tex_size");
	ResourceManager::LoadTexture("Breakout/tex_confuse.png", GL_TRUE, "tex_confuse");
	ResourceManager::LoadTexture("Breakout/tex_chaos.png", GL_TRUE, "tex_chaos");

	//setup render control
	renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
	particles = new ParticleGenerator(ResourceManager::GetShader("particle"), ResourceManager::GetTexture("particle"), 500);
	effects = new PostProcessor(ResourceManager::GetShader("postprocessor"), this->width, this->height);
	//load levels
	GameLevel one; one.Load("Breakout/Levels/1.lvl", this->width, this->height * 0.5);
	GameLevel two; two.Load("Breakout/Levels/2.lvl", this->width, this->height * 0.5);
	GameLevel three; three.Load("Breakout/Levels/3.lvl", this->width, this->height * 0.5);
	GameLevel four; four.Load("Breakout/Levels/4.lvl", this->width, this->height * 0.5);

	this->levels.push_back(one);
	this->levels.push_back(two);
	this->levels.push_back(three);
	this->levels.push_back(four);
	this->currentLevel = 0;

	//player init
	glm::vec2 playerPos = glm::vec2(this->width / 2 - PLAYER_SIZE.x / 2, this->height - PLAYER_SIZE.y);
	player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle"));

	//ball init    原点位置是左上角
	glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2 - BALL_RADIUS, -BALL_RADIUS * 2);
	ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, ResourceManager::GetTexture("ball"));

	//Audio
	soundEngine->play2D("Breakout/Audios/breakout.mp3", true);
}

void Game::Update(GLfloat dt)
{
	//update ball
	ball->Move(dt, this->width);

	//collision detection
	this->DoCollisions();

	//particle
	particles->Update(dt, *ball, 2, glm::vec2(ball->radius / 2));

	//update powerups
	this->UpdatePowerUps(dt);

	// Reduce shake time
	if (shakeTime > 0.0f)
	{
		shakeTime -= dt;
		if (shakeTime <= 0.0f)
			effects->shake = GL_FALSE;
	}

	//球是否接触底部边界?
	if (ball->position.y >= this->height)
	{
		this->ResetLevel();
		this->ResetPlayer();
	}
}

void Game::ProcessInput(GLfloat dt)
{
	if (this->state == GAME_ACTIVE)
	{
		GLfloat velocity = PLAYER_VELOCITY * dt;
		//移动挡板
		if (this->keys[GLFW_KEY_A])
		{
			if (player->position.x >= 0)
			{
				player->position.x -= velocity;

				if (ball->stuck)
					ball->position.x -= velocity;
			}
		}
		if (this->keys[GLFW_KEY_D])
		{
			if (player->position.x <= this->width - player->size.x)
			{
				player->position.x += velocity;
				if (ball->stuck)
					ball->position.x += velocity;
			}
		}
		if (this->keys[GLFW_KEY_SPACE])
			ball->stuck = GL_FALSE;
	}
}

void Game::Render()
{
	if (this->state == GAME_ACTIVE)
	{
		effects->BeginRender();

		//绘制背景
		renderer->DrawSprite(ResourceManager::GetTexture("background"), glm::vec2(0, 0), glm::vec2(this->width, this->height), 0.0f);

		//绘制关卡
		this->levels[this->currentLevel].Draw(*renderer);

		//绘制玩家挡板
		player->Draw(*renderer);

		//绘制道具
		for (PowerUp& powerup : this->powerups)
		{
			if (!powerup.destroyed)
				powerup.Draw(*renderer);
		}

		//绘制粒子
		particles->Draw();

		//绘制球
		ball->Draw(*renderer);

		effects->EndRender();

		effects->Render(glfwGetTime());
	}
}

Direction VectorDirection(glm::vec2 target)
{
	glm::vec2 compass[] =
	{
		glm::vec2(0.0f,1.0f),
		glm::vec2(1.0f,0.0f),
		glm::vec2(0.0f,-1.0f),
		glm::vec2(-1.0f,0.0f),
	};

	GLfloat max = 0.0f;
	GLuint bestMatch = -1;
	for (GLuint i = 0; i < 4; i++)
	{
		GLfloat dot = glm::dot(glm::normalize(target), compass[i]);
		if (dot > max)
		{
			max = dot;
			bestMatch = i;
		}
	}
	return (Direction)bestMatch;
}

//碰撞检测::AABB
GLboolean CheckCollision(GameObject& one, GameObject& two) // AABB - AABB collision
{
	// x轴方向碰撞?
	bool collisionX = one.position.x + one.size.x >= two.position.x &&
		two.position.x + two.size.x >= one.position.x;
	// y轴方向碰撞?
	bool collisionY = one.position.y + one.size.y >= two.position.y &&
		two.position.y + two.size.y >= one.position.y;
	// 只有两个轴向都有碰撞时才碰撞
	return collisionX && collisionY;
}

//碰撞检测:方形和圆形
Collision CheckCollision(BallObject& one, GameObject& two) // AABB - Circle collision
{
	//获取圆的中心
	glm::vec2 center(one.position + one.radius);
	//计算AABB的信息(中心,半边长)
	glm::vec2 aabb_half_extents(two.size.x / 2, two.size.y / 2);
	glm::vec2 aabb_center(two.position.x + aabb_half_extents.x, two.position.y + aabb_half_extents.y);
	//获取两个中心的差矢量
	glm::vec2 difference = center - aabb_center;
	glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
	//AABB_center 加上clampled这样就得到了碰撞箱上距离最近点closest
	glm::vec2 closest = aabb_center + clamped;
	//获得圆心center和最近点closest的矢量,并判断是否length<=radius
	difference = closest - center;
	if (glm::length(difference) < one.radius)
		return std::make_tuple(GL_TRUE, VectorDirection(difference), difference);
	else
		return std::make_tuple(GL_FALSE, UP, glm::vec2(0, 0));
}

void ActivatePowerUp(PowerUp& powerup)
{
	// 根据道具类型发动道具
	if (powerup.type == "speed")
	{
		ball->velocity *= 1.2;
	}
	else if (powerup.type == "sticky")
	{
		ball->sticky = GL_TRUE;
		player->color = glm::vec3(1.0f, 0.5f, 1.0f);
	}
	else if (powerup.type == "pass-through")
	{
		ball->passThrough = GL_TRUE;
		ball->color = glm::vec3(1.0f, 0.5f, 0.5f);
	}
	else if (powerup.type == "pad-size-increase")
	{
		player->size.x += 50;
	}
	else if (powerup.type == "confuse")
	{
		if (!effects->chaos)
			effects->confuse = GL_TRUE; // 只在chaos未激活时生效,chaos同理
	}
	else if (powerup.type == "chaos")
	{
		if (!effects->confuse)
			effects->chaos = GL_TRUE;
	}
}

void Game::DoCollisions()
{
	for (GameObject& box : this->levels[this->currentLevel].bricks)
	{
		if (!box.destroyed)
		{
			Collision collision = CheckCollision(*ball, box);
			if (std::get<0>(collision))
			{
				if (!box.isSoild)
				{
					box.destroyed = GL_TRUE;
					this->SpawnPowerUps(box);
					soundEngine->play2D("Breakout/Audios/bleep.mp3", GL_FALSE);
				}
				else
				{
					shakeTime = 0.05f;
					effects->shake = true;
					soundEngine->play2D("Breakout/Audios/solid.wav", GL_FALSE);
				}

				//碰撞处理
				Direction dir = std::get<1>(collision);
				glm::vec2 diff_vector = std::get<2>(collision);
				if (!(ball->passThrough && !box.isSoild))
				{
					if (dir == LEFT || dir == RIGHT)
					{
						ball->velocity.x = -ball->velocity.x;//反转水平速度
						//重定位
						GLfloat penetration = ball->radius - std::abs(diff_vector.x);
						if (dir == LEFT)
							ball->position.x += penetration;//球右移
						else
							ball->position.x -= penetration;//球左移
					}
					else//垂直方形碰撞
					{
						ball->velocity.y = -ball->velocity.y;//反转垂直速度
						//重定位
						GLfloat penetration = ball->radius - std::abs(diff_vector.y);
						if (dir == UP)
							ball->position.y -= penetration;//球上移
						else
							ball->position.y += penetration;//球下移						
					}
				}
			}

			for (PowerUp& powerup : this->powerups)
			{
				if (!powerup.destroyed)
				{
					if (powerup.position.y >= this->height)
					{
						powerup.destroyed = GL_TRUE;
					}
					if (CheckCollision(*player, powerup))
					{
						ActivatePowerUp(powerup);
						powerup.destroyed = GL_TRUE;
						powerup.activated = GL_TRUE;
						soundEngine->play2D("Breakout/Audios/powerup.wav", GL_FALSE);
					}
				}
			}

			//玩家--球碰撞
			Collision result = CheckCollision(*ball, *player);
			if (!ball->stuck && std::get<0>(result))
			{
				//检查碰到了挡板的哪个位置,并根据碰撞到哪个位置改变速度
				GLfloat centerBoard = player->position.x + player->size.x / 2;
				GLfloat distance = (ball->position.x + ball->radius) - centerBoard;
				GLfloat percentage = distance / (player->size.x / 2);
				//依据结果移动
				GLfloat strength = 2.0f;
				glm::vec2 oldVelocity = ball->velocity;
				ball->velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength;
				//ball->velocity.y = -ball->velocity.y;
				//解决粘板问题:保证最后的Y方向始终向上
				ball->velocity.y = -1 * abs(ball->velocity.y);
				//新的速度:新的归一化方向*原来速度的大小
				ball->velocity = glm::normalize(ball->velocity) * glm::length(oldVelocity);

				ball->stuck = ball->sticky;

				soundEngine->play2D("Breakout/Audios/bleep.wav", GL_FALSE);
			}
		}
	}
}

void Game::ResetLevel()
{
	if (this->currentLevel == 0)
		this->levels[0].Load("Breakout/Levels/1.lvl", this->width, this->height * 0.5f);
	else if (this->currentLevel == 1)
		this->levels[1].Load("Breakout/Levels/2.lvl", this->width, this->height * 0.5f);
	else if (this->currentLevel == 2)
		this->levels[1].Load("Breakout/Levels/3.lvl", this->width, this->height * 0.5f);
	else if (this->currentLevel == 3)
		this->levels[1].Load("Breakout/Levels/4.lvl", this->width, this->height * 0.5f);
}

void Game::ResetPlayer()
{
	player->size = PLAYER_SIZE;
	player->position = glm::vec2(this->width / 2 - PLAYER_SIZE.x / 2, this->height - PLAYER_SIZE.y);
	ball->Reset(player->position + glm::vec2(PLAYER_SIZE.x / 2 - ball->radius, -(BALL_RADIUS * 2)), INITIAL_BALL_VELOCITY);
}

GLboolean ShouldSpawn(GLuint chance)
{
	GLuint random = rand() % chance;
	return random == 0;
}

void Game::SpawnPowerUps(GameObject& block)
{
	if (ShouldSpawn(10))
		this->powerups.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.position, ResourceManager::GetTexture("tex_speed")));
	if (ShouldSpawn(10))
		this->powerups.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.position, ResourceManager::GetTexture("tex_sticky")));
	if (ShouldSpawn(10))
		this->powerups.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.position, ResourceManager::GetTexture("tex_pass")));
	if (ShouldSpawn(10))
		this->powerups.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.position, ResourceManager::GetTexture("tex_size")));
	//if (ShouldSpawn(15))
		//this->powerups.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.position, ResourceManager::GetTexture("tex_confuse")));
	//if (ShouldSpawn(10))
		//this->powerups.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.position, ResourceManager::GetTexture("tex_chaos")));

}

GLboolean IsOtherPowerUpActive(std::vector<PowerUp>& powerUps, std::string type)
{
	for (const PowerUp& powerUp : powerUps)
	{
		if (powerUp.activated)
			if (powerUp.type == type)
				return GL_TRUE;
	}
	return GL_FALSE;
}

void Game::UpdatePowerUps(GLfloat dt)
{
	for (PowerUp& powerUp : this->powerups)
	{
		powerUp.position += powerUp.velocity * dt;
		if (powerUp.activated)
		{
			powerUp.duration -= dt;

			if (powerUp.duration <= 0.0f)
			{
				powerUp.activated = GL_FALSE;
				if (powerUp.type == "sticky")
				{
					if (!IsOtherPowerUpActive(this->powerups, "sticky"))
					{
						ball->sticky = GL_FALSE;
						player->color = glm::vec3(1.0f);
					}
				}
				else if (powerUp.type == "pass-through")
				{
					if (!IsOtherPowerUpActive(this->powerups, "pass-through"))
					{
						ball->passThrough = GL_FALSE;
						player->color = glm::vec3(1.0f);
					}
				}
				else if (powerUp.type == "pass-through")
				{
					if (!IsOtherPowerUpActive(this->powerups, "pass-through"))
					{
						ball->passThrough = GL_FALSE;
						player->color = glm::vec3(1.0f);
					}
				}

			}
		}
	}

	this->powerups.erase(std::remove_if(this->powerups.begin(), this->powerups.end(),
		[](const PowerUp& powerUp) { return powerUp.destroyed && !powerUp.activated; }
	), this->powerups.end());
}

标签:实战篇,ball,glm,ResourceManager,--,Breakout,音效,position,GL
来源: https://blog.csdn.net/weixin_41155760/article/details/121262438

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有