源码解读:
GameLayer层级:在构造函数中:创建窗口和相机,对随机数种子初始化;
在OnAttach方法中:初始化关卡:加载资源初始化地图信息;设置字体;
在OnUpdate方法中:判断游戏状态;设置相机跟随;计算/刷新关卡,角色,粒子的变换、碰撞、颜色、生命周期等信息;接着根据刷新的信息,渲染关卡、角色、粒子
在OnImGuiRender方法中:判断状态渲染UI;
在OnEvent方法中:拦截窗口大小改变和鼠标点击事件,改变相机大小\根据状态判断是否重置游戏;
代码:
SandboxApp.cpp:
#include<YOTO.h>
//入口点
#include"YOTO/Core/EntryPoint.h"#include "imgui/imgui.h"
#include<stdio.h>
#include <glm/gtc/matrix_transform.hpp>
#include <Platform/OpenGL/OpenGLShader.h>
#include <glm/gtc/type_ptr.hpp>#include "Sandbox2D.h"
#include"GameLayer.h"
class Sandbox:public YOTO::Application
{
public:Sandbox(){//加入层级PushLayer(new GameLayer());//PushLayer(new Sandbox2D());}~Sandbox() {}private:};/// <summary>
/// 创建App
/// </summary>
/// <returns></returns>
YOTO::Application* YOTO::CreateApplication() {YT_PROFILE_FUNCTION();return new Sandbox();
}
GameLayer.h:
#pragma once#include "YOTO.h"#include "Level.h"
#include <imgui/imgui.h>class GameLayer : public YOTO::Layer
{
public:GameLayer();virtual ~GameLayer() = default;virtual void OnAttach() override;virtual void OnDetach() override;void OnUpdate(YOTO::Timestep ts) override;virtual void OnImGuiRender() override;void OnEvent(YOTO::Event& e) override;bool OnMouseButtonPressed(YOTO::MouseButtonPressedEvent& e);bool OnWindowResize(YOTO::WindowResizeEvent& e);
private:void CreateCamera(uint32_t width, uint32_t height);
private:YOTO::Scope<YOTO::OrthographicCamera> m_Camera;Level m_Level;ImFont* m_Font;float m_Time = 0.0f;bool m_Blink = false;enum class GameState{Play = 0, MainMenu = 1, GameOver = 2};GameState m_State = GameState::MainMenu;
};
GameLayer.cpp:
#include "GameLayer.h"#include <imgui/imgui.h>#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>GameLayer::GameLayer(): Layer("GameLayer")
{ //创建窗口auto& window = YOTO::Application::Get().GetWindow();//创建相机m_Camera赋值CreateCamera(window.GetWidth(), window.GetHeight());//初始化随机数系统,确定种子Random::Init();
}void GameLayer::OnAttach()
{//初始化关卡,加载资源、初始化关卡m_Level.Init();//设置UI字体ImGuiIO io = ImGui::GetIO();m_Font = io.Fonts->AddFontFromFileTTF("assets/OpenSans-Regular.ttf", 120.0f);
}void GameLayer::OnDetach()
{
}
/// <summary>
/// Update
/// </summary>
/// <param name="ts"></param>
void GameLayer::OnUpdate(YOTO::Timestep ts)
{//记录游戏开始总时间m_Time += ts;//控制闪烁if ((int)(m_Time * 10.0f) % 8 > 4)m_Blink = !m_Blink;//判断是否游戏结束if (m_Level.IsGameOver())//切换游戏状态m_State = GameState::GameOver;//获取角色位置,相机跟踪角色const auto& playerPos = m_Level.GetPlayer().GetPosition();m_Camera->SetPosition({ playerPos.x, playerPos.y, 0.0f });//如果游戏还在进行中switch (m_State){case GameState::Play:{//刷新关卡,角色,粒子m_Level.OnUpdate(ts);break;}}// RenderYOTO::RenderCommand::SetClearColor({ 0.0f, 0.0f, 0.0f, 1 });YOTO::RenderCommand::Clear();//设置相机,设置VP矩阵YOTO::Renderer2D::BeginScene(*m_Camera);//渲染m_Level.OnRender();YOTO::Renderer2D::EndScene();
}void GameLayer::OnImGuiRender()
{//ImGui::Begin("Settings");//m_Level.OnImGuiRender();//ImGui::End();//根据不同的状态渲染不同的UIswitch (m_State){case GameState::Play:{uint32_t playerScore = m_Level.GetPlayer().GetScore();std::string scoreStr = std::string("Score: ") + std::to_string(playerScore);ImGui::GetForegroundDrawList()->AddText(m_Font, 48.0f, ImGui::GetWindowPos(), 0xffffffff, scoreStr.c_str());break;}case GameState::MainMenu:{auto pos = ImGui::GetWindowPos();auto width = YOTO::Application::Get().GetWindow().GetWidth();auto height = YOTO::Application::Get().GetWindow().GetHeight();pos.x += width * 0.5f - 300.0f;pos.y += 50.0f;if (m_Blink)ImGui::GetForegroundDrawList()->AddText(m_Font, 120.0f, pos, 0xffffffff, "Click to Play!");break;}case GameState::GameOver:{auto pos = ImGui::GetWindowPos();auto width = YOTO::Application::Get().GetWindow().GetWidth();auto height = YOTO::Application::Get().GetWindow().GetHeight();pos.x += width * 0.5f - 300.0f;pos.y += 50.0f;if (m_Blink)ImGui::GetForegroundDrawList()->AddText(m_Font, 120.0f, pos, 0xffffffff, "Click to Play!");pos.x += 200.0f;pos.y += 150.0f;uint32_t playerScore = m_Level.GetPlayer().GetScore();std::string scoreStr = std::string("Score: ") + std::to_string(playerScore);ImGui::GetForegroundDrawList()->AddText(m_Font, 48.0f, pos, 0xffffffff, scoreStr.c_str());break;}}
}void GameLayer::OnEvent(YOTO::Event& e)
{//拦截窗口大小改变和鼠标点击YOTO::EventDispatcher dispatcher(e);dispatcher.Dispatch<YOTO::WindowResizeEvent>(YT_BIND_EVENT_FN(GameLayer::OnWindowResize));dispatcher.Dispatch<YOTO::MouseButtonPressedEvent>(YT_BIND_EVENT_FN(GameLayer::OnMouseButtonPressed));
}/// <summary>
/// 鼠标点击
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
bool GameLayer::OnMouseButtonPressed(YOTO::MouseButtonPressedEvent& e)
{//当游戏结束时候点击重置关卡if (m_State == GameState::GameOver)m_Level.Reset();m_State = GameState::Play;return false;
}/// <summary>
/// 窗口大小改变
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
bool GameLayer::OnWindowResize(YOTO::WindowResizeEvent& e)
{//创建相机,修改相机的宽高CreateCamera(e.GetWidth(), e.GetHeight());return false;
}
/// <summary>
/// 创建相机
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
void GameLayer::CreateCamera(uint32_t width, uint32_t height)
{//宽高比float aspectRatio = (float)width / (float)height;//宽度float camWidth = 8.0f;float bottom = -camWidth;float top = camWidth;float left = bottom * aspectRatio;float right = top * aspectRatio;//创建相机m_Camera = YOTO::CreateScope<YOTO::OrthographicCamera>(left, right, bottom, top);
}
Random.h:
#pragma once
#include<random>
//< random > :提供了随机数生成相关的类和函数。
//std::mt19937:Mersenne Twister 伪随机数生成器,可以生成高质量的随机数序列。
//std::random_device:用于生成真随机数的设备,通常是硬件随机数生成器。
//std::uniform_int_distribution:生成均匀分布的随机整数。
//std::numeric_limits<uint32_t>::max():返回 uint32_t 类型的最大值,用于归一化随机数。
class Random
{
public:static void Init() {s_RandomEngine.seed(std::random_device()());// 使用随机设备生成种子,以当前时间为种子}static float Float() {// 生成一个范围在[0,1]之间的随机浮点数return (float)s_Distribution(s_RandomEngine) / (float)std::numeric_limits<uint32_t>::max();// 通过均匀分布生成随机数,将其归一化到[0,1]之间}
private://Mersenne Twister 伪随机数生成器,可以生成高质量的随机数序列。static std::mt19937 s_RandomEngine;//生成均匀分布的随机整数。static std::uniform_int_distribution<std::mt19937::result_type> s_Distribution;
};
Random.cpp:
#include "Random.h"std::mt19937 Random::s_RandomEngine;
std::uniform_int_distribution<std::mt19937::result_type> Random::s_Distribution;
Level.h:
#pragma once
#include"YOTO.h"
#include"Player.h"/// <summary>
/// 每个三角刺的位置
/// </summary>
struct Pillar
{glm::vec3 TopPosition = { 0.0f, 10.0f, 0.0f };glm::vec2 TopScale = { 15.0f, 20.0f };glm::vec3 BottomPosition = { 10.0f, 10.0f, 0.0f };glm::vec2 BottomScale = { 15.0f, 20.0f };
};class Level
{public:void Init();void OnUpdate(YOTO::Timestep ts);void OnRender();void OnImGuiRender();bool IsGameOver()const { return m_GameOver; }void Reset();Player& GetPlayer() { return m_Player; }
private:void CreatePillar(int index, float offset);bool CollisionTest();void GameOver();
private:Player m_Player;bool m_GameOver;float m_PillarTarget = 30.0f;int m_PillarIndex = 0;glm::vec3 m_PillarHSV = { 0.0f,0.8f,0.8f };std::vector<Pillar> m_Pillars;YOTO::Ref<YOTO::Texture2D>m_TriangleTexture;
};
Level.cpp:
#include "Level.h"
#include<YOTO/Renderer/Texture.h>
#include"Random.h"
#include <glm/gtc/matrix_transform.hpp>
/// <summary>
/// 变换
/// </summary>
/// <param name="hsv"></param>
/// <returns></returns>
static glm::vec4 HSVtoRGB(const glm::vec3& hsv) {int H = (int)(hsv.x * 360.0f);double S = hsv.y;double V = hsv.z;double C = S * V;double X = C * (1 - abs(fmod(H / 60.0, 2) - 1));double m = V - C;double Rs, Gs, Bs;if (H >= 0 && H < 60) {Rs = C;Gs = X;Bs = 0;}else if (H >= 60 && H < 120) {Rs = X;Gs = C;Bs = 0;}else if (H >= 120 && H < 180) {Rs = 0;Gs = C;Bs = X;}else if (H >= 180 && H < 240) {Rs = 0;Gs = X;Bs = C;}else if (H >= 240 && H < 300) {Rs = X;Gs = 0;Bs = C;}else {Rs = C;Gs = 0;Bs = X;}return { (Rs + m), (Gs + m), (Bs + m), 1.0f };
}
/// <summary>
/// 判断是否在三角形内
/// </summary>
/// <param name="p">角色点</param>
/// <param name="p0">三角形点</param>
/// <param name="p1">三角形点</param>
/// <param name="p2">三角形点</param>
/// <returns></returns>
static bool PointInTri(const glm::vec2& p, glm::vec2& p0, const glm::vec2& p1, const glm::vec2& p2)
{float s = p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y;float t = p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y;if ((s < 0) != (t < 0))return false;float A = -p1.y * p2.x + p0.y * (p2.x - p1.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y;return A < 0 ?(s <= 0 && s + t >= A) :(s >= 0 && s + t <= A);
}
/// <summary>
/// 初始化关卡
/// </summary>
void Level::Init()
{//创建关卡中三角形的纹理m_TriangleTexture = YOTO::Texture2D::Create("assets/textures/Triangle.png");//加载角色资源,加载角色纹理m_Player.LoadAssets();//初始化三角刺的容器大小为5个m_Pillars.resize(5);//生成5个刺for (int i = 0; i < 5; i++)//索引为0-5,偏移量为0-50CreatePillar(i, i * 10.0f);
}
/// <summary>
/// Update
/// </summary>
/// <param name="ts"></param>
void Level::OnUpdate(YOTO::Timestep ts)
{//刷新角色位置,和粒子m_Player.OnUpdate(ts);//进行碰撞检测if (CollisionTest()) {//碰到就游戏结束GameOver();return;}//柱子的颜色m_PillarHSV.x += 0.1f * ts;if (m_PillarHSV.x > 1.0f) {m_PillarHSV.x = 0.0f;}//如果角色到达柱子位置if (m_Player.GetPosition().x > m_PillarTarget) {//生成柱子CreatePillar(m_PillarIndex, m_PillarTarget + 20.0f);//索引++且大于最大柱子数m_PillarIndex = ++m_PillarIndex % m_Pillars.size();//多了一个柱子所以target+10(每个柱子间隔10)m_PillarTarget += 10;}}void Level::OnRender()
{//获取角色位置const auto& playerPos = m_Player.GetPosition();//把HSV变换成RGBglm::vec4 color = HSVtoRGB(m_PillarHSV);// Background背景YOTO::Renderer2D::DrawQuad({ playerPos.x, 0.0f, -0.8f }, { 50.0f, 50.0f }, { 0.3f, 0.3f, 0.3f, 1.0f });//顶部和底部俩横着的YOTO::Renderer2D::DrawQuad({ playerPos.x, 34.0f }, { 50.0f, 50.0f }, color);YOTO::Renderer2D::DrawQuad({ playerPos.x, -34.0f }, { 50.0f, 50.0f }, color);//渲染柱子for (auto& pillar : m_Pillars){ //顶部柱子YOTO::Renderer2D::DrawRotatedQuad(pillar.TopPosition, pillar.TopScale, glm::radians(180.0f), m_TriangleTexture,1.0f, color);//底部柱子YOTO::Renderer2D::DrawRotatedQuad(pillar.BottomPosition, pillar.BottomScale, 0.0f, m_TriangleTexture,1.0f, color);}//渲染角色,粒子m_Player.OnRender();
}void Level::OnImGuiRender()
{m_Player.OnImGuiRender();
}
/// <summary>
/// 重置关卡
/// </summary>
void Level::Reset()
{m_GameOver = false;//重置角色位置和速度m_Player.Reset();//重置柱子目标和索引m_PillarTarget = 30.0f;m_PillarIndex = 0;//重置前五个柱子的位置for (int i = 0; i < 5; i++)CreatePillar(i, i * 10.0f);
}
/// <summary>
/// 生成刺的函数
/// </summary>
/// <param name="index">索引</param>
/// <param name="offset">偏移量</param>
void Level::CreatePillar(int index, float offset)
{//取出索引的柱子Pillar& pillar = m_Pillars[index];//设置水平位置pillar.TopPosition.x = offset;pillar.BottomPosition.x = offset;pillar.TopPosition.z = index*0.1-0.5f;pillar.BottomPosition.z = index * 0.1 - 0.5f+0.05f;//设置中心float center = Random::Float() * 35.0f - 17.5f;//设置缝隙float gap = 8.0f + Random::Float() * 0.5f;//设置垂直的位置pillar.TopPosition.y = 10.0f - ((10.0f - center) * 0.2f) + gap * 0.5f;pillar.BottomPosition.y = -10.0f - ((-10.0f - center) * 0.2f) - gap * 0.5f;
}
/// <summary>
/// 碰撞检测
/// </summary>
/// <returns></returns>
bool Level::CollisionTest()
{//如果超过活动范围,直接判定为碰到if (glm::abs(m_Player.GetPosition().y) > 8.5f)return true;//player的四个点的分布glm::vec4 playerVertices[4] = {{ -0.5f, -0.5f, 0.0f, 1.0f },{ 0.5f, -0.5f, 0.0f, 1.0f },{ 0.5f, 0.5f, 0.0f, 1.0f },{ -0.5f, 0.5f, 0.0f, 1.0f }};//player的位置const auto& pos = m_Player.GetPosition();//player的变换矩阵glm::vec4 playerTransformedVerts[4];for (int i = 0; i < 4; i++){playerTransformedVerts[i] = glm::translate(glm::mat4(1.0f), { pos.x, pos.y, 0.0f })* glm::rotate(glm::mat4(1.0f), glm::radians(m_Player.GetRotation()), { 0.0f, 0.0f, 1.0f })* glm::scale(glm::mat4(1.0f), { 1.0f, 1.3f, 1.0f })* playerVertices[i];}//柱子的点的分布// To match Triangle.png (each corner is 10% from the texture edge)glm::vec4 pillarVertices[3] = {{ -0.5f + 0.1f, -0.5f + 0.1f, 0.0f, 1.0f },{ 0.5f - 0.1f, -0.5f + 0.1f, 0.0f, 1.0f },{ 0.0f + 0.0f, 0.5f - 0.1f, 0.0f, 1.0f },};//判断每个柱子for (auto& p : m_Pillars){//每个点的位置glm::vec2 tri[3];// Top pillarsfor (int i = 0; i < 3; i++){//获取三角形点的位置tri[i] = glm::translate(glm::mat4(1.0f), { p.TopPosition.x, p.TopPosition.y, 0.0f })* glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), { 0.0f, 0.0f, 1.0f })* glm::scale(glm::mat4(1.0f), { p.TopScale.x, p.TopScale.y, 1.0f })* pillarVertices[i];}//判断palyer的位置是否在三角形内for (auto& vert : playerTransformedVerts){//判断是否在三角形内if (PointInTri({ vert.x, vert.y }, tri[0], tri[1], tri[2]))return true;}// Bottom pillars下方的三角形for (int i = 0; i < 3; i++){tri[i] = glm::translate(glm::mat4(1.0f), { p.BottomPosition.x, p.BottomPosition.y, 0.0f })* glm::scale(glm::mat4(1.0f), { p.BottomScale.x, p.BottomScale.y, 1.0f })* pillarVertices[i];}for (auto& vert : playerTransformedVerts){if (PointInTri({ vert.x, vert.y }, tri[0], tri[1], tri[2]))return true;}}return false;
}void Level::GameOver()
{m_GameOver = true;
}
Player.h:
#pragma once
#include"YOTO.h"
//#include "Color.h"
#include "Random.h"
#include "ParticleSystem.h"
class Player
{
public:Player();void LoadAssets();void OnUpdate(YOTO::Timestep ts);void OnRender();void OnImGuiRender();void Reset();//根据速度y获取旋转float GetRotation() { return m_Velocity.y * 4.0f - 90.0f; }const glm::vec2& GetPosition() const { return m_Position; }uint32_t GetScore() const { return (uint32_t)(m_Position.x + 10.0f) / 10.0f; }
private:glm::vec2 m_Position = { -10.0f, 0.0f };glm::vec2 m_Velocity = { 5.0f, 0.0f };float m_EnginePower = 0.5f;float m_Gravity = 0.4f;float m_Time = 0.0f;float m_SmokeEmitInterval = 0.4f;float m_SmokeNextEmitTime = m_SmokeEmitInterval;ParticleProps m_SmokeParticle, m_EngineParticle;ParticleSystem m_ParticleSystem;YOTO::Ref<YOTO::Texture2D> m_ShipTexture;
};
Player.cpp:
#include "Player.h"
#include<YOTO/Renderer/Texture.h>
#include <imgui/imgui.h>
#include <glm/gtc/matrix_transform.hpp>
Player::Player()
{// Smokem_SmokeParticle.Position = { 0.0f, 0.0f };m_SmokeParticle.Velocity = { -2.0f, 0.0f }, m_SmokeParticle.VelocityVariation = { 4.0f, 2.0f };m_SmokeParticle.SizeBegin = 0.35f, m_SmokeParticle.SizeEnd = 0.0f, m_SmokeParticle.SizeVariation = 0.15f;m_SmokeParticle.ColorBegin = { 0.8f, 0.8f, 0.8f, 1.0f };m_SmokeParticle.ColorEnd = { 0.6f, 0.6f, 0.6f, 1.0f };m_SmokeParticle.LifeTime = 4.0f;// Flamesm_EngineParticle.Position = { 0.0f, 0.0f };m_EngineParticle.Velocity = { -2.0f, 0.0f }, m_EngineParticle.VelocityVariation = { 3.0f, 1.0f };m_EngineParticle.SizeBegin = 0.5f, m_EngineParticle.SizeEnd = 0.0f, m_EngineParticle.SizeVariation = 0.3f;m_EngineParticle.ColorBegin = { 254 / 255.0f, 109 / 255.0f, 41 / 255.0f, 1.0f };m_EngineParticle.ColorEnd = { 254 / 255.0f, 212 / 255.0f, 123 / 255.0f , 1.0f };m_EngineParticle.LifeTime = 1.0f;
}void Player::LoadAssets()
{//加载角色的纹理m_ShipTexture = YOTO::Texture2D::Create("assets/textures/Ship.png");
} void Player::OnUpdate(YOTO::Timestep ts)
{m_Time += ts;//如果按下空格if (YOTO::Input::IsKeyPressed(YT_KEY_SPACE)){//速度的y增加动力m_Velocity.y += m_EnginePower;//如果速度小于0(向下),动力*2if (m_Velocity.y < 0.0f)m_Velocity.y += m_EnginePower * 2.0f;// Flames//排放物的点glm::vec2 emissionPoint = { 0.0f, -0.6f };//根据速度y获取旋转角度float rotation = glm::radians(GetRotation());//计算旋转后的位置glm::vec4 rotated = glm::rotate(glm::mat4(1.0f), rotation, { 0.0f, 0.0f, 1.0f }) * glm::vec4(emissionPoint, 0.0f, 1.0f);//赋值给m_EngineParticle动力粒子系统m_EngineParticle.Position = m_Position + glm::vec2{ rotated.x, rotated.y };//赋值给粒子系统速度m_EngineParticle.Velocity.y = -m_Velocity.y * 0.2f - 0.2f;//传入粒子系统的例子配置信息m_ParticleSystem.Emit(m_EngineParticle);}else{//如果没按,速度就减重力:v=atm_Velocity.y -= m_Gravity;}//速度y限制在-20到20之间m_Velocity.y = glm::clamp(m_Velocity.y, -20.0f, 20.0f);//更新位置m_Position += m_Velocity * (float)ts;// Particles 粒子,每隔一段时间产生一次白烟if (m_Time > m_SmokeNextEmitTime){//烟的位置=当前时间m_SmokeParticle.Position = m_Position;//配置烟的粒子信息m_ParticleSystem.Emit(m_SmokeParticle);//产生间隔m_SmokeNextEmitTime += m_SmokeEmitInterval;}//粒子系统刷新m_ParticleSystem.OnUpdate(ts);
}void Player::OnRender()
{//渲染例子m_ParticleSystem.OnRender();//渲染角色YOTO::Renderer2D::DrawRotatedQuad({ m_Position.x, m_Position.y, 0.5f }, { 1.0f, 1.3f }, glm::radians(GetRotation()), m_ShipTexture);
}void Player::OnImGuiRender()
{ImGui::DragFloat("Engine Power", &m_EnginePower, 0.1f);ImGui::DragFloat("Gravity", &m_Gravity, 0.1f);
}
/// <summary>
/// 重置角色
/// </summary>
void Player::Reset()
{//重置位置和速度m_Position = { -10.0f, 0.0f };m_Velocity = { 5.0f, 0.0f };
}
ParticleSystem.h:
#pragma once#include <YOTO.h>struct ParticleProps
{glm::vec2 Position;glm::vec2 Velocity, VelocityVariation;glm::vec4 ColorBegin, ColorEnd;float SizeBegin, SizeEnd, SizeVariation;float LifeTime = 1.0f;
};class ParticleSystem
{
public:ParticleSystem();void Emit(const ParticleProps& particleProps);void OnUpdate(YOTO::Timestep ts);void OnRender();
private:struct Particle{glm::vec2 Position;glm::vec2 Velocity;glm::vec4 ColorBegin, ColorEnd;float Rotation = 0.0f;float SizeBegin, SizeEnd;float LifeTime = 1.0f;float LifeRemaining = 0.0f;bool Active = false;};std::vector<Particle> m_ParticlePool;uint32_t m_PoolIndex = 999;
};
ParticleSystem.cpp:
#include "ParticleSystem.h"#include "Random.h"#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/compatibility.hpp>ParticleSystem::ParticleSystem()
{m_ParticlePool.resize(1000);
}/// <summary>
///发散粒子
/// </summary>
/// <param name="particleProps"></param>
void ParticleSystem::Emit(const ParticleProps& particleProps)
{//从粒子池中取出一个Particle& particle = m_ParticlePool[m_PoolIndex];//激活particle.Active = true;//设置位置particle.Position = particleProps.Position;//设置旋转随机数*2*πparticle.Rotation = Random::Float() * 2.0f * glm::pi<float>();// Velocity 设置速度particle.Velocity = particleProps.Velocity;particle.Velocity.x += particleProps.VelocityVariation.x * (Random::Float() - 0.5f);particle.Velocity.y += particleProps.VelocityVariation.y * (Random::Float() - 0.5f);// Color 设置颜色particle.ColorBegin = particleProps.ColorBegin;particle.ColorEnd = particleProps.ColorEnd;// Size 设置大小particle.SizeBegin = particleProps.SizeBegin + particleProps.SizeVariation * (Random::Float() - 0.5f);particle.SizeEnd = particleProps.SizeEnd;// Life 设置生命周期particle.LifeTime = particleProps.LifeTime;particle.LifeRemaining = particleProps.LifeTime;//索引减一后取模,保证大于0m_PoolIndex = --m_PoolIndex % m_ParticlePool.size();
}void ParticleSystem::OnUpdate(YOTO::Timestep ts)
{//更新池子的每个元素for (auto& particle : m_ParticlePool){//如果没激活,直接跳过if (!particle.Active)continue;//如果生命周期到头了,直接设置未激活if (particle.LifeRemaining <= 0.0f){particle.Active = false;continue;}//每次刷新生命周期减少particle.LifeRemaining -= ts;//位置更新particle.Position += particle.Velocity * (float)ts;//旋转更新(自动旋转)particle.Rotation += 0.01f * ts;}
}void ParticleSystem::OnRender()
{//取出粒子for (auto& particle : m_ParticlePool){//如果没有激活直接不处理if (!particle.Active)continue;//获取lifefloat life = particle.LifeRemaining / particle.LifeTime;//根据life过渡Color变换glm::vec4 color = glm::lerp(particle.ColorEnd, particle.ColorBegin, life);//根据life过渡透明度color.a = color.a * life;//根据life过渡大小float size = glm::lerp(particle.SizeEnd, particle.SizeBegin, life);//渲染粒子YOTO::Renderer2D::DrawRotatedQuad(particle.Position, { size, size }, particle.Rotation, color);}
}
测试:
cool!